Initial commit. Client and net aer build
authorNikita Kalyazin <n.kalyazin@samsung.com>
Fri, 25 Jun 2010 06:55:43 +0000 (10:55 +0400)
committerNikita Kalyazin <n.kalyazin@samsung.com>
Fri, 25 Jun 2010 06:55:43 +0000 (10:55 +0400)
31 files changed:
driver/Kbuild [new file with mode: 0644]
driver/debug.h [new file with mode: 0644]
driver/def_handlers.h [new file with mode: 0644]
driver/device_driver.c [new file with mode: 0644]
driver/device_driver.h [new file with mode: 0644]
driver/ec.c [new file with mode: 0644]
driver/ec.h [new file with mode: 0644]
driver/ec_ioctl.h [new file with mode: 0644]
driver/events.h [new file with mode: 0644]
driver/inperfa_driver.sh [new file with mode: 0755]
driver/legacy.c [new file with mode: 0644]
driver/legacy.h [new file with mode: 0644]
driver/module.c [new file with mode: 0644]
driver/module.h [new file with mode: 0644]
driver/picl.h [new file with mode: 0644]
driver/probes.c [new file with mode: 0644]
driver/probes.h [new file with mode: 0644]
driver/probes_manager.c [new file with mode: 0644]
driver/probes_manager.h [new file with mode: 0644]
driver/storage.c [new file with mode: 0644]
driver/storage.h [new file with mode: 0644]
driver/us_proc_inst.c [new file with mode: 0644]
driver/us_proc_inst.h [new file with mode: 0644]
kprobe/Kbuild [new file with mode: 0644]
kprobe/Makefile [new file with mode: 0644]
kprobe/README [new file with mode: 0644]
kprobe/asm/kprobes.h [new file with mode: 0644]
kprobe/inperfa_kprobe.sh [new file with mode: 0755]
kprobe/kprobes.c [new file with mode: 0644]
kprobe/kprobes.h [new file with mode: 0644]
kprobe/kprobes_arch.c [new file with mode: 0644]

diff --git a/driver/Kbuild b/driver/Kbuild
new file mode 100644 (file)
index 0000000..ef58237
--- /dev/null
@@ -0,0 +1,21 @@
+include $(project_pwd)/common.mk
+
+program_name := $(driver_name)
+source_dirs := $(project_pwd)/$(src_path)/$(driver_path)
+include_dirs := $(project_pwd)/$(src_path)/$(common_path) \
+       $(project_pwd)/$(src_path)/$(probes_path)
+include_libs := 
+special_defines := EC_ARCH_$(target_cpu)
+ifeq ($(specific_target), chelsea)
+    special_defines += KERNEL_HAS_ISPAGEPRESENT
+endif
+special_compiling_flags := 
+special_linking_flags := 
+ifeq ($(memory_checker), yes)
+special_defines += MEMORY_CHECKER
+endif
+ifeq ($(thread_profiler), yes)
+special_defines += THREAD_PROFILER
+endif
+
+include $(project_pwd)/rules-m.mk
diff --git a/driver/debug.h b/driver/debug.h
new file mode 100644 (file)
index 0000000..75b8bb9
--- /dev/null
@@ -0,0 +1,41 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           debug.h
+//
+//      DESCRIPTION:
+//     Debug functions for application
+//     
+//      SEE ALSO:       N/A
+//      AUTHOR:         L.Komkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.02
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(__DEBUG_H__)
+#define __DEBUG_H__
+
+#include <linux/kernel.h>
+#include <linux/string.h>      // strrchr
+
+#ifdef __DEBUG
+#define DPRINTF(format, args...) do { \
+       char *f = __FILE__; \
+       char *n = strrchr(f, '/'); \
+       printk("DRIVER[%s:%u:%s] DEBUG: " format "\n" , (n) ? n+1 : f, __LINE__, __FUNCTION__, ##args); \
+    } while(0)
+#else
+#define DPRINTF(format, args...)
+#endif
+
+#define EPRINTF(format, args...) do { \
+       char *f = __FILE__; \
+       char *n = strrchr(f, '/'); \
+       printk("DRIVER[%s:%u:%s] ERROR: " format "\n" , (n) ? n+1 : f, __LINE__, __FUNCTION__, ##args); \
+    } while(0)
+
+
+#endif /* !defined(__DEBUG_H__) */
diff --git a/driver/def_handlers.h b/driver/def_handlers.h
new file mode 100644 (file)
index 0000000..b80d187
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _TNCHK_DEF_HANDLERS_H_
+#define _TNCHK_DEF_HANDLERS_H_
+
+/*void
+ujprobe_event_pre_handler (us_proc_ip_t *, struct pt_regs *);
+
+void
+ujprobe_event_handler (unsigned long, unsigned long, unsigned long, 
+                       unsigned long, unsigned long, unsigned long);
+
+int
+uretprobe_event_handler (struct kretprobe_instance *, struct pt_regs *, us_proc_ip_t *);*/
+
+#endif /* _TNCHK_DEF_HANDLERS_H_ */
diff --git a/driver/device_driver.c b/driver/device_driver.c
new file mode 100644 (file)
index 0000000..5a3af32
--- /dev/null
@@ -0,0 +1,570 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           device_driver.c
+//
+//      DESCRIPTION:
+//      This file is C source for SWAP driver.
+//
+//      SEE ALSO:       device_driver.h
+//      AUTHOR:         L.Komkov, S.Dianov, S.Grekhov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#include "module.h"
+#include "device_driver.h"     // device driver
+#include "CProfile.h"
+
+DECLARE_WAIT_QUEUE_HEAD (notification_waiters_queue);
+volatile unsigned notification_count;
+
+static int device_mmap (struct file *filp, struct vm_area_struct *vma);
+static int device_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int device_open(struct inode *, struct file *);
+static int device_release(struct inode *, struct file *);
+static ssize_t device_read(struct file *, char *, size_t, loff_t *);
+static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
+
+static int gl_nDeviceOpened = 0;
+static struct file_operations device_fops = {
+       .owner = THIS_MODULE,
+       .mmap = device_mmap,
+       .ioctl = device_ioctl,
+       .read = device_read,
+       .write = device_write,
+       .open = device_open,
+       .release = device_release
+};
+
+int device_init (void)
+{
+       int nReserved = 0;
+       int nRetVal = register_chrdev(device_major, device_name, &device_fops);
+       if (nRetVal < 0) {
+               EPRINTF("Cannot register character device! [%s, %d]", device_name, device_major);
+               nReserved = register_chrdev(0, device_name, &device_fops);
+               if(nReserved >= 0)
+                       EPRINTF("Please, create a new device node with major number [%d],\n\tand pass it as module parameter!", nReserved);
+               return -1;
+       } else if(nRetVal > 0) {
+               EPRINTF("Cannot register this device major number! [%d]\n\tTrying a new one. [%d]", device_major, nRetVal);
+               device_major = nRetVal;
+       }
+       return 0;
+}
+
+void device_down (void)
+{
+       unregister_chrdev(device_major, device_name);
+}
+
+void notify_user (event_id_t event_id)
+{
+       ec_info.events_counters[event_id] += 1;
+
+       if (EVENT_EC_PROBE_RECORD == event_id)
+       {
+               // EC_PROBE_RECORD events happen to often. To reduce overhead user
+               // space will be notified only once per each EVENTS_AGGREGATION_USEC
+               static uint64_t timestamp_usec = 0;
+
+               uint64_t current_usec;
+               uint64_t delta_usec;
+
+               struct timeval tv;
+
+               do_gettimeofday (&tv);
+               current_usec = 1000000ULL * (unsigned) tv.tv_sec + (unsigned) tv.tv_usec;
+
+               if (current_usec < timestamp_usec)
+               {
+                       // Note: time from do_gettimeofday() may go backward
+                       EPRINTF ("current_usec=%llu timestamp_usec=%llu", current_usec, timestamp_usec);
+               }
+               else
+               {
+                       delta_usec = current_usec - timestamp_usec;
+                       if (EVENTS_AGGREGATION_USEC > delta_usec)
+                       {
+                               // wait the time left
+#if defined(__DEBUG)
+                               unsigned UNUSED left_usec = EVENTS_AGGREGATION_USEC - delta_usec;
+#endif /* defined(__DEBUG) */
+                               return; // supress notification
+                       }
+               }
+               timestamp_usec = current_usec;  // remember new time for the future use
+       } else if (EVENT_EC_START_CONDITION_SEEN == event_id) {
+               return;         // supress notification
+       } else if (EVENT_EC_STOP_CONDITION_SEEN == event_id) {
+               return;         // supress notification
+       }
+
+       ++notification_count;
+       wake_up_interruptible (&notification_waiters_queue);
+}
+
+static int device_mmap (struct file *filp UNUSED, struct vm_area_struct *vma)
+{
+       if(!p_buffer) {
+               EPRINTF("Null pointer to buffer!");
+               return -1;
+       }
+       return remap_vmalloc_range (vma, p_buffer, 0);
+}
+
+static int device_open(struct inode *inode, struct file *file)
+{
+       /*if (gl_nDeviceOpened)
+               return -EBUSY;*/
+       gl_nDeviceOpened++;
+       // TODO
+       try_module_get(THIS_MODULE);
+       return 0;
+}
+static int device_release(struct inode *inode, struct file *file)
+{
+       gl_nDeviceOpened--;
+       module_put(THIS_MODULE);
+       return 0;
+}
+static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t * offset)
+{
+       EPRINTF("Operation <<read>> not supported!");
+       return -1;
+}
+static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
+{
+       EPRINTF("Operation <<write>> not supported!");
+       return -1;
+}
+static int device_ioctl (struct inode *inode UNUSED, struct file *file UNUSED, unsigned int cmd, unsigned long arg)
+{
+       unsigned long spinlock_flags = 0L;
+       int result = -1;
+//     DPRINTF("Command=%d", cmd);
+       switch (cmd)
+       {
+       case EC_IOCTL_SET_EC_MODE:
+               {
+                       ioctl_general_t param;
+                       unsigned long nIgnoredBytes = 0;
+                       memset(&param, '0', sizeof(ioctl_general_t));
+                       nIgnoredBytes = copy_from_user (&param, (void*)arg, sizeof(ioctl_general_t));
+                       if (nIgnoredBytes > 0) {
+                               result = -1;
+                               break;
+                       }
+                       if(SetECMode(param.m_unsignedLong) == -1) {
+                               result = -1;
+                               break;
+                       }
+                       result = 0;
+                       DPRINTF("Set EC Mode = %lu", param.m_unsignedLong);
+                       break;
+               }
+       case EC_IOCTL_GET_EC_MODE:
+               {
+                       ioctl_general_t param;
+                       unsigned long nIgnoredBytes = 0;
+                       memset(&param, '0', sizeof(ioctl_general_t));
+                       param.m_unsignedLong = GetECMode();
+                       nIgnoredBytes = copy_to_user ((void*)arg, &param, sizeof (ioctl_general_t));
+                       if (nIgnoredBytes > 0) {
+                               result = -1;
+                               break;
+                       }
+                       result = 0;
+//                     DPRINTF("Get EC Mode = %lu", param.m_unsignedLong);  // Frequent call
+                       break;
+               }
+       case EC_IOCTL_SET_BUFFER_SIZE:
+               {
+                       ioctl_general_t param;
+                       unsigned long nIgnoredBytes = 0;
+                       memset(&param, '0', sizeof(ioctl_general_t));
+                       nIgnoredBytes = copy_from_user (&param, (void*)arg, sizeof(ioctl_general_t));
+                       if (nIgnoredBytes > 0) {
+                               result = -1;
+                               break;
+                       }
+                       if (SetBufferSize(param.m_unsignedLong) == -1) {
+                               result = -1;
+                               break;
+                       }
+                       result = 0;
+                       DPRINTF("Set Buffer Size = %lu", param.m_unsignedLong);
+                       break;
+               }
+       case EC_IOCTL_GET_BUFFER_SIZE:
+               {
+                       ioctl_general_t param;
+                       unsigned long nIgnoredBytes = 0;
+                       memset(&param, '0', sizeof(ioctl_general_t));
+                       param.m_unsignedLong = GetBufferSize();
+                       nIgnoredBytes = copy_to_user ((void*)arg, &param, sizeof (ioctl_general_t));
+                       if (nIgnoredBytes > 0) {
+                               result = -1;
+                               break;
+                       }
+                       result = 0;
+                       DPRINTF("Get Buffer Size = %lu", param.m_unsignedLong);
+                       break;
+               }
+       case EC_IOCTL_RESET_BUFFER:
+               {
+                       if (ResetBuffer() == -1) {
+                               result = -1;
+                               break;
+                       }
+                       result = 0;
+                       DPRINTF("Reset Buffer");
+                       break;
+               }
+       case EC_IOCTL_GET_EC_INFO:
+               {
+                       if (copy_ec_info_to_user_space ((ec_info_t *) arg) != 0) {
+                               result = -1;
+                               break;
+                       }
+                       result = 0;
+//                     DPRINTF("Get Buffer Status"); // Frequent call
+                       break;
+               }
+       case EC_IOCTL_CONSUME_BUFFER:
+               {
+                       static ec_info_t ec_info_copy;
+                       int nIgnoredBytes = 0;
+#ifndef __DISABLE_RELAYFS
+                       struct rchan* pRelayChannel = NULL;
+                       struct rchan_buf *buf = NULL;
+                       unsigned int nNumOfSubbufs = 0;
+                       void* pConsume = NULL;
+                       unsigned int nPaddingLength = 0;
+                       unsigned int nSubbufSize = 0;
+                       unsigned int nDataSize = 0;
+                       unsigned int nEffectSize = 0;
+                       unsigned int nSubbufDiscardedCount = 0;
+#endif
+                       nIgnoredBytes = copy_from_user (&ec_info_copy, (ec_info_t *) arg, sizeof (ec_info_t));
+                       if(nIgnoredBytes > 0)
+                       {
+                               EPRINTF ("copy_from_user(%08X,%08X)=%d", (unsigned) arg, (unsigned) &ec_info_copy, nIgnoredBytes);
+                               result = -1;
+                               break;
+                       }
+
+                       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+                       if((ec_info_copy.m_nMode & MODEMASK_MULTIPLE_BUFFER) == 0) {
+                               // Original buffer
+                               if(ec_info.after_last < ec_info.first) {
+                                       ec_info.buffer_effect = ec_info.buffer_size;
+                               }
+                               ec_info.first = ec_info_copy.after_last;
+                               ec_info.trace_size = ec_info.trace_size - ec_info_copy.trace_size;
+                       } else {
+                               // Relay FS buffer
+#ifndef __DISABLE_RELAYFS
+                               pRelayChannel = GetRelayChannel();
+                               if(pRelayChannel == NULL) {
+                                       EPRINTF("Null pointer to relay channel!");
+                                       result = -1;
+                                       break;
+                               }
+                               buf = pRelayChannel->buf[0];
+                               nNumOfSubbufs = pRelayChannel->n_subbufs;
+
+                               nSubbufSize = pRelayChannel->subbuf_size;
+                               pConsume = buf->start + buf->subbufs_consumed % nNumOfSubbufs * nSubbufSize;
+                               memcpy(&nPaddingLength, pConsume, sizeof(unsigned int));
+                               memcpy(&nSubbufDiscardedCount, pConsume + sizeof(unsigned int), sizeof(unsigned int));
+                               nEffectSize = nSubbufSize - nPaddingLength;
+                               nDataSize = nEffectSize - RELAY_SUBBUF_HEADER_SIZE;
+                               relay_subbufs_consumed(pRelayChannel, 0, 1);
+                               ec_info.m_nBeginSubbufNum = buf->subbufs_consumed % nNumOfSubbufs;
+                               ec_info.m_nEndSubbufNum = buf->subbufs_produced % nNumOfSubbufs;
+                               ec_info.buffer_effect -= nEffectSize;
+                               ec_info.trace_size -= nDataSize;
+                               buf->dentry->d_inode->i_size = ec_info.trace_size;
+#endif
+                       }
+                       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+                       result = 0;
+//                     DPRINTF("Consume Buffer"); // Frequent call
+                       break;
+               }
+       case EC_IOCTL_ADD_PROBE:
+               {
+                       unsigned long addr = arg;
+                       result = add_probe(addr);
+
+                       break;
+               }
+       //@AGv: remove_probe expects probe address instead of name
+       /*case EC_IOCTL_REMOVE_PROBE:
+               {
+                       char *probe_name = (char *) arg;
+                       result = remove_probe (probe_name);
+
+                       break;
+               }*/
+       case EC_IOCTL_RESET_PROBES:
+               {
+                       result = reset_probes();
+
+                       break;
+               }
+       case EC_IOCTL_UPDATE_CONDS:
+               {
+                       int args_cnt, i;
+                       struct cond *c, *c_tmp, *p_cond;
+                       unsigned char *p_data;
+                       int err;
+                       result = 0;
+                       err = copy_from_user(&args_cnt, (void *)arg, sizeof(int));
+                       if (err) {
+                               result = -1;
+                               break;
+                       }
+                       /* first, delete all the conds */
+                       list_for_each_entry_safe(c, c_tmp, &cond_list.list, list) {
+                               list_del(&c->list);
+                               kfree(c);
+                       }
+                       /* second, add new conds */
+                       p_data = (unsigned char *)(arg + sizeof(int));
+                       for (i = 0; i < args_cnt; i++) {
+                               p_cond = kmalloc(sizeof(struct cond), GFP_KERNEL);
+                               memcpy(&p_cond->tmpl, p_data, sizeof(struct event_tmpl));
+                               p_cond->applied = 0;
+                               list_add(&(p_cond->list), &(cond_list.list));
+                               p_data += sizeof(struct event_tmpl);
+                       }
+                       break;
+               }
+       case EC_IOCTL_ATTACH:
+               result = ec_user_attach ();
+               DPRINTF("Attach Probes");
+               break;
+       case EC_IOCTL_ACTIVATE:
+               result = ec_user_activate ();
+               DPRINTF("Activate Probes");
+               break;
+       case EC_IOCTL_STOP_AND_DETACH:
+       {
+               unsigned long nIgnoredBytes = 0;
+               if(ec_user_stop() != 0) {
+                       result = -1;
+                       break;
+               }
+               nIgnoredBytes = copy_ec_info_to_user_space ((ec_info_t*)arg);
+               if(nIgnoredBytes > 0) {
+                       result = -1;
+                       break;
+               }
+               result = 0;
+               DPRINTF("Stop and Detach Probes");
+               break;
+       }
+       case EC_IOCTL_WAIT_NOTIFICATION:
+               {
+                       static ec_info_t ec_info_copy;
+
+                       ioctl_wait_notification_t ioctl_args;
+
+                       result = copy_from_user (&ioctl_args, (void *) arg, sizeof (ioctl_args));
+                       if (result)
+                       {
+                               result = -1;
+                               break;
+                       }
+
+                       result = wait_event_interruptible (notification_waiters_queue, ioctl_args.notification_count != notification_count);
+                       if (result)
+                       {
+                               result = -EINTR;        // woken by signal (ERESTARTSYS 512)
+                               break;
+                       }
+
+                       ioctl_args.notification_count = notification_count;
+
+                       result = copy_to_user ((void *) arg, &ioctl_args, sizeof (ioctl_args));
+                       if (result)
+                       {
+                               result = -1;
+                               break;
+                       }
+
+                       // FIXME: synchronization is necessary here (ec_info must be locked).
+                       // ENTER_CRITICAL_SECTION
+                       memcpy (&ec_info_copy, &ec_info, sizeof (ec_info_copy));
+                       // LEAVE_CRITICAL_SECTION
+
+                       result = copy_to_user ((void *) ioctl_args.p_ec_info, &ec_info_copy, sizeof (ec_info_t));
+                       if (result)
+                       {
+                               EPRINTF ("copy_to_user(%08X,%08X)=%d", (unsigned) ioctl_args.p_ec_info, (unsigned) &ec_info_copy, result);
+                               result = -1;
+                               break;
+                       }
+                       DPRINTF("Wake up");
+                       break;
+               }
+       case EC_IOCTL_INST_USR_SPACE_PROC:
+               {
+                       ioctl_inst_usr_space_proc_t ioctl_args;
+                       result = copy_from_user (&ioctl_args, (void *) arg, sizeof (ioctl_args));
+                       if (result)
+                       {
+                               result = -1;
+                               EPRINTF ("copy_from_user() failure");
+                       }
+                       else
+                       {
+                               result = set_us_proc_inst_info (&ioctl_args);
+                       }
+                       DPRINTF("Instrument User Space Procedures");
+                       break;
+               }
+       case EC_IOCTL_DEINST_USR_SPACE_PROC:
+               {
+                       release_us_proc_inst_info ();
+                       result = 0;
+                       DPRINTF("Deinstrument User Space Procedures");
+                       break;
+               }
+       case EC_IOCTL_US_EVENT:
+               {
+                       ioctl_us_event_t ioctl_args;
+                       result = copy_from_user (&ioctl_args, (void *) arg, sizeof (ioctl_args));
+                       if (result)
+                       {
+                               result = -1;
+                               EPRINTF ("copy_from_user() failure");
+                       }
+                       else
+                       {
+                               if(ioctl_args.len == 0){
+                                       result = -EINVAL;
+                                       EPRINTF ("invalid event length!");                                      
+                               }
+                               else {
+                                       char *buf = kmalloc(ioctl_args.len, GFP_KERNEL);
+                                       if(!buf){
+                                               result = -ENOMEM;
+                                               EPRINTF ("failed to alloc mem for event!");                                     
+                                       }
+                                       else {
+                                               result = copy_from_user (buf, (void *) ioctl_args.data, ioctl_args.len);
+                                               if (result){
+                                                       result = -1;
+                                                       EPRINTF ("failed to copy event from user space!");
+                                               }
+                                               else
+                                                       result = put_us_event(buf, ioctl_args.len);
+                                               kfree(buf);
+                                       }
+                               }
+                       }
+//                     DPRINTF("User Space Event"); // Frequent call
+                       break;
+               }
+               
+       case EC_IOCTL_SET_EVENT_MASK:
+               {
+                       int mask;
+                       result = copy_from_user (&mask, (void *) arg, sizeof (mask));
+                       if (result)
+                       {
+                               result = -EFAULT;
+                               break;
+                       }
+
+                       result = set_event_mask (mask);
+                       if (result)
+                       {
+                               break;
+                       }
+                       DPRINTF("Set Event Mask = %d", mask);
+                       break;
+               }
+
+       case EC_IOCTL_GET_EVENT_MASK:
+               {
+                       int mask = 0;
+                       result = get_event_mask(&mask);
+                       if (result)
+                       {
+                               result = -EFAULT;
+                       }
+                       result = copy_to_user ((void *) arg, &mask, sizeof (mask));
+                       if (result)
+                       {
+                               result = -EFAULT;
+                       }
+                       DPRINTF("Get Event Mask = %d", mask);
+                       break;
+               }
+
+       case EC_IOCTL_SET_PREDEF_UPROBES:
+               {
+                       ioctl_predef_uprobes_info_t data;
+                       result = copy_from_user (&data, (void *) arg, sizeof (data));
+                       if (result)
+                       {
+                               result = -EFAULT;
+                               break;
+                       }
+
+                       result = set_predef_uprobes (&data);
+                       if (result)
+                       {
+                               break;
+                       }
+                       DPRINTF("Set Predefined User Space Probes");
+                       break;
+               }
+               
+       case EC_IOCTL_GET_PREDEF_UPROBES:
+               {
+                       result = get_predef_uprobes((ioctl_predef_uprobes_info_t *)arg);
+                       if (result)
+                       {
+                               result = -EFAULT;
+                       }
+                       DPRINTF("Get Predefined User Space Probes");
+                       break;
+               }
+               
+       case EC_IOCTL_GET_PREDEF_UPROBES_SIZE:
+               {
+                       int size = 0;
+                       result = get_predef_uprobes_size(&size);
+                       if (result)
+                       {
+                               result = -EFAULT;
+                       }
+                       result = copy_to_user ((void *) arg, &size, sizeof (size));
+                       if (result)
+                       {
+                               result = -EFAULT;
+                       }
+                       DPRINTF("Get Size of Predefined User Space Probes");
+                       break;
+               }
+       
+       default:
+               EPRINTF ("Unknown driver command = %u", cmd);
+               result = -EINVAL;
+               break;
+       }
+
+       return result;
+}
diff --git a/driver/device_driver.h b/driver/device_driver.h
new file mode 100644 (file)
index 0000000..b966dd3
--- /dev/null
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           device_driver.h
+//
+//      DESCRIPTION:
+//
+//      SEE ALSO:       device_driver.c
+//      AUTHOR:         L.Komkov, S.Dianov, S.Grekhov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(device_driver_h)
+#define device_driver_h
+
+#include "event.h"             // event_id_t
+#include "ec_info.h"           // ec_info_t
+#include "ec_probe.h"          // probe_id_t
+
+#define DEFAULT_DEVICE_NAME "inperfa_drv"
+#define DEFAULT_DEVICE_MAJOR 250
+#define EVENTS_AGGREGATION_USEC (5 * 1000000UL)
+
+extern int device_init (void);
+extern void device_down (void);
+extern void notify_user (event_id_t event_id);
+
+#endif /* !defined(device_driver_h) */
diff --git a/driver/ec.c b/driver/ec.c
new file mode 100644 (file)
index 0000000..9de8aca
--- /dev/null
@@ -0,0 +1,429 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           ec.c
+//
+//      DESCRIPTION:
+//      This file is C++ source for SWAP driver.
+//
+//      SEE ALSO:       ec.h
+//      AUTHOR:         L.Komkov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#include "module.h"
+#include "ec.h"
+#include "CProfile.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+ec_info_t ec_info = {
+       .ec_state = EC_STATE_IDLE,
+       .m_nMode = 0L,
+       .buffer_size = EC_BUFFER_SIZE_DEFAULT,
+       .ignored_events_count = 0,
+       .m_nNumOfSubbuffers = 0,
+       .m_nSubbufSize = DEFAULT_SUBBUF_SIZE,
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+spinlock_t ec_spinlock = SPIN_LOCK_UNLOCKED;   // protects 'ec_info'
+
+ec_probe_info_t ec_probe_info = {
+       .probe_id = -1,
+       .probe_selected = 0,
+       .jprobe_active = 0,
+       .retprobe_active = 0,
+       .address = 0,
+};
+
+spinlock_t ec_probe_spinlock = SPIN_LOCK_UNLOCKED;     // protects 'ec_probe_info'
+
+ec_state_t GetECState(void) { return ec_info.ec_state; };
+
+void ResetECInfo(void) {
+       unsigned long spinlock_flags = 0L;
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.trace_size = 0;
+       ec_info.first = 0;
+       ec_info.after_last = 0;
+       ec_info.ignored_events_count = 0;
+       ec_info.saved_events_count = 0;
+       ec_info.discarded_events_count = 0;
+       ec_info.collision_count = 0;
+       ec_info.lost_events_count = 0;
+       ec_info.m_nBeginSubbufNum = 0;
+       ec_info.m_nEndSubbufNum = 0;
+       ec_info.m_nEndOffset = 0;
+       ec_info.m_nSubbufSavedEvents = 0;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+}
+
+void CleanECInfo(void) {
+       unsigned long spinlock_flags = 0L;
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.buffer_effect = 0;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+       ResetECInfo();
+
+}
+
+int IsECMode(unsigned long nMask) { return ((ec_info.m_nMode & nMask) != 0); }
+
+int IsMultipleBuffer() { return IsECMode(MODEMASK_MULTIPLE_BUFFER); }
+
+int IsContinuousRetrieval() { return IsECMode(MODEMASK_CONTINUOUS_RETRIEVAL); }
+
+int SetECMode(unsigned long nECMode) {
+       unsigned long spinlock_flags = 0L;
+
+       if((nECMode & MODEMASK_MULTIPLE_BUFFER) != 0) {
+               if(EnableMultipleBuffer() == -1) {
+                       EPRINTF("Cannot enable multiple buffer!");
+                       return -1;
+               }
+       } else {
+               if(DisableMultipleBuffer() == -1) {
+                       EPRINTF("Cannot disable multiple buffer!");
+                       return -1;
+               }
+       }
+       if((nECMode & MODEMASK_CONTINUOUS_RETRIEVAL) != 0) {
+               if(EnableContinuousRetrieval() == -1) {
+                       EPRINTF("Cannot enable continuous retrieval!");
+                       return -1;
+               }
+       } else {
+               if(DisableContinuousRetrieval() == -1) {
+                       EPRINTF("Cannot disable continuous retrieval!");
+                       return -1;
+               }
+       }
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.m_nMode = nECMode;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+       return 0;
+}
+
+unsigned long GetECMode(void) { return ec_info.m_nMode; }
+
+unsigned int GetNumOfSubbuffers(unsigned long nBufferSize)
+{
+       if(nBufferSize % ec_info.m_nSubbufSize > 0)
+               EPRINTF("The buffer size is not divisible by a subbuffer size!");
+       return nBufferSize / ec_info.m_nSubbufSize;
+};
+
+#if defined(__DEBUG)
+static UNUSED char * ec_state_name (ec_state_t ec_state)
+{
+       static char *ec_state_names[EC_STATE_TAG_COUNT] = { "IDLE", "ATTACHED", "ACTIVE", "STOPPED" };
+
+       if (((unsigned) ec_info.ec_state) < EC_STATE_TAG_COUNT)
+       {
+               return ec_state_names[ec_info.ec_state];
+       }
+       else
+       {
+               return "<unknown>";
+       }
+}
+#endif /* defined(__DEBUG) */
+
+
+/*
+    On user request user space EC may change state in the following order:
+        IDLE -> ATTACHED (on "attach")
+        IDLE | ATTACHED -> ACTIVE (on "activate")
+        ATTACHED | ACTIVE | STOPPED -> IDLE (on "stop"/"detach")
+*/
+int ec_user_attach (void)
+{
+       unsigned long spinlock_flags;
+       int result;
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);       // make other CPUs wait
+       if (EC_STATE_IDLE == ec_info.ec_state)
+       {
+               int tmp;
+               ec_info.ec_state = EC_STATE_ATTACHED;
+
+               /* save 'start' time */
+               struct timeval tv;
+               do_gettimeofday(&tv);
+               memcpy(&last_attach_time, &tv, sizeof(struct timeval));
+
+               /* unpause if paused */
+               paused = 0;
+
+               /* if there is at least one start condition in the list
+                  we are paused at the beginning */
+               struct cond *p_cond;
+               struct event_tmpl *p_tmpl;
+               list_for_each_entry(p_cond, &cond_list.list, list) {
+                       p_tmpl = &p_cond->tmpl;
+                       if (p_tmpl->type == ET_TYPE_START_COND) {
+                               paused = 1;
+                               break;
+                       }
+               }
+
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+
+               //first of all put event with event format
+               tmp = event_mask;
+               event_mask = 0;
+               pack_event_info(EVENT_FMT_PROBE_ID, RECORD_ENTRY, "x", tmp);
+               event_mask = tmp;               
+               
+               result = attach_selected_probes ();
+               if (result == 0)        // instrument user space process 
+                       result = inst_usr_space_proc ();
+               // FIXME: SAFETY CHECK
+               if (result)
+               {               // return to safe state
+                       detach_selected_probes ();
+
+                       spin_lock_irqsave (&ec_spinlock, spinlock_flags);       // make other CPUs wait
+                       ec_info.ec_state = EC_STATE_IDLE;
+                       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+               }
+               // FIXME: SAFETY CHECK
+
+               notify_user (EVENT_EC_STATE_CHANGE);
+
+       }
+       else
+       {
+
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+               result = -EINVAL;
+
+       }
+
+       return result;
+}
+
+int ec_user_activate (void)
+{
+       unsigned long spinlock_flags;
+       int result;
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);       // make other CPUs wait
+       if (EC_STATE_IDLE == ec_info.ec_state)
+       {
+               int tmp;
+               ec_info.ec_state = EC_STATE_ACTIVE;
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+               //first of all put event with event format
+               tmp = event_mask;
+               event_mask = 0;
+               pack_event_info(EVENT_FMT_PROBE_ID, RECORD_ENTRY, "x", tmp);
+               event_mask = tmp;               
+
+               result = attach_selected_probes ();
+               if (result == 0)        // instrument user space process 
+                       result = inst_usr_space_proc ();
+               // FIXME: SAFETY CHECK
+               if (result)
+               {               // return to safe state
+                       detach_selected_probes ();
+
+                       spin_lock_irqsave (&ec_spinlock, spinlock_flags);       // make other CPUs wait
+                       ec_info.ec_state = EC_STATE_IDLE;
+                       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+               }
+               // FIXME: SAFETY CHECK
+
+               notify_user (EVENT_EC_STATE_CHANGE);
+
+       }
+       else if (EC_STATE_ATTACHED == ec_info.ec_state)
+       {
+
+               ec_info.ec_state = EC_STATE_ACTIVE;
+               result = 0;
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+
+               notify_user (EVENT_EC_STATE_CHANGE);
+
+       }
+       else
+       {
+
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+               result = -EINVAL;
+       }
+
+       return result;
+}
+
+int ec_user_stop (void)
+{
+       unsigned long spinlock_flags;
+       int result = 0, ret = 0;
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);       // make other CPUs wait
+       if (EC_STATE_ATTACHED == ec_info.ec_state || EC_STATE_ACTIVE == ec_info.ec_state || EC_STATE_STOPPED == ec_info.ec_state)
+       {
+
+               ec_info.ec_state = EC_STATE_IDLE;
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+
+               ret = deinst_usr_space_proc ();
+               result = detach_selected_probes ();
+               if (result == 0)
+                       result = ret;
+
+               notify_user (EVENT_EC_STATE_CHANGE);
+
+       }
+       else
+       {
+
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+               result = -EINVAL;
+
+       }
+
+       return result;
+}
+
+/*
+    Kernel space EC may change state in the following order:
+        ATTACHED -> ACTIVE (when start condition is satisfied)
+        ACTIVE -> STOPPED (when stop condition is satisfied)
+*/
+int ec_kernel_activate (void)
+{
+       unsigned long spinlock_flags;
+       int result;
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);       // make other CPUs wait
+       if (EC_STATE_ATTACHED == ec_info.ec_state)
+       {
+               ec_info.ec_state = EC_STATE_ACTIVE;
+               result = 0;
+       }
+       else
+       {
+               result = -EINVAL;
+       }
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+
+       notify_user (EVENT_EC_STATE_CHANGE);
+
+       return result;
+}
+
+int ec_kernel_stop (void)
+{
+       unsigned long spinlock_flags;
+       int result;
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);       // make other CPUs wait
+       if (EC_STATE_ACTIVE == ec_info.ec_state)
+       {
+               ec_info.ec_state = EC_STATE_STOPPED;
+               result = 0;
+       }
+       else
+       {
+               result = -EINVAL;
+       }
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+
+       notify_user (EVENT_EC_STATE_CHANGE);
+
+       return result;
+}
+
+// Copies EC info to user space
+// Since "copy_to_user" may block, an intermediate copy of ec_info is used here
+int copy_ec_info_to_user_space (ec_info_t * p_user_ec_info)
+{
+       /*
+          WARNING: to avoid stack overflow the following data structure was made
+          static. As result, simultaneous users of this function will share it
+          and must use additional synchronization to avoid collisions.
+        */
+       // FIXME: synchronization is necessary here (ec_info_copy must be locked).
+       static ec_info_t ec_info_copy;
+       unsigned long spinlock_flags;
+       int result;
+
+       // ENTER_CRITICAL_SECTION
+       // lock semaphore here
+
+
+       // ENTER_CRITICAL_SECTION
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);       // make other CPUs wait
+
+       // copy
+       memcpy (&ec_info_copy, &ec_info, sizeof (ec_info_copy));
+
+       // LEAVE_CRITICAL_SECTION
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);  // open our data for other CPUs
+
+
+       result = copy_to_user (p_user_ec_info, &ec_info_copy, sizeof (ec_info_t));
+
+       // LEAVE_CRITICAL_SECTION
+       // unlock semaphore here
+
+       if (result)
+       {
+               EPRINTF ("copy_to_user(%08X,%08X)=%d", (unsigned) p_user_ec_info, (unsigned) &ec_info_copy, result);
+               result = -EFAULT;
+       }
+       return result;
+}
+
+int copy_ec_probe_info_to_user_space (ec_probe_info_t * p_user_ec_probe_info)
+{
+       /*
+          WARNING: to avoid stack overflow the following data structure was made
+          static. As result, simultaneous users of this function will share it
+          and must use additional synchronization to avoid collisions.
+        */
+       // FIXME: synchronization is necessary here (ec_info_copy must be locked).
+       ec_probe_info_t ec_probe_info_copy;
+       unsigned long spinlock_flags;
+       int result;
+
+       // ENTER_CRITICAL_SECTION
+       // lock semaphore here
+
+
+       // ENTER_CRITICAL_SECTION
+       spin_lock_irqsave (&ec_probe_spinlock, spinlock_flags); // make other CPUs wait
+
+       // copy
+       memcpy (&ec_probe_info_copy, &ec_probe_info, sizeof (ec_probe_info_copy));
+
+       // LEAVE_CRITICAL_SECTION
+       spin_unlock_irqrestore (&ec_probe_spinlock, spinlock_flags);    // open our data for other CPUs
+
+
+       result = copy_to_user (p_user_ec_probe_info, &ec_probe_info_copy, sizeof (ec_probe_info_t));
+
+       // LEAVE_CRITICAL_SECTION
+       // unlock semaphore here
+
+       if (result)
+       {
+               EPRINTF ("copy_to_user(%08X,%08X)=%d", (unsigned) p_user_ec_probe_info, (unsigned) &ec_probe_info_copy, result);
+               result = -EFAULT;
+       }
+       return result;
+}
diff --git a/driver/ec.h b/driver/ec.h
new file mode 100644 (file)
index 0000000..04f57ba
--- /dev/null
@@ -0,0 +1,51 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           ec.h
+//
+//      DESCRIPTION:
+//
+//      SEE ALSO:       ec.c
+//      AUTHOR:         L.Komkov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(__EC_H__)
+#define __EC_H__
+
+#include "ec_ioctl.h"
+#include "ec_info.h"
+#include "ec_module.h"
+#include "ec_probe.h"
+#include "picl.h"
+
+extern ec_info_t ec_info;
+extern ec_probe_info_t ec_probe_info;
+extern spinlock_t ec_spinlock;
+
+extern int ec_user_attach (void);
+extern int ec_user_activate (void);
+extern int ec_user_stop (void);
+extern int ec_kernel_activate (void);
+extern int ec_kernel_stop (void);
+
+extern int copy_ec_info_to_user_space (ec_info_t * p_user_ec_info);
+extern int copy_ec_probe_info_to_user_space (ec_probe_info_t * p_user_ec_probe_info);
+
+extern ec_state_t GetECState(void);
+extern void ResetECInfo(void);
+extern void CleanECInfo(void);
+extern int IsECMode(unsigned long nMask);
+extern int IsMultipleBuffer(void);
+extern int IsContinuousRetrieval(void);
+extern int SetECMode(unsigned long nECMode);
+extern unsigned long GetECMode(void);
+extern unsigned int GetNumOfSubbuffers(unsigned long nBufferSize);
+extern struct timeval last_attach_time;
+extern int paused;
+
+#endif /* !defined(__EC_H__) */
diff --git a/driver/ec_ioctl.h b/driver/ec_ioctl.h
new file mode 100644 (file)
index 0000000..17c13e4
--- /dev/null
@@ -0,0 +1,227 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           ec_module.h
+//
+//      DESCRIPTION:
+//
+//      SEE ALSO:       N/A
+//      AUTHOR:         L.Komkov, S.Dianov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.02
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(ec_ioctl_h)
+#define ec_ioctl_h
+
+#include "ec_info.h"           // ec_info_t
+#include "picl.h"
+
+typedef enum
+{
+       // modes
+       EC_IOCTL_SET_EC_MODE,
+       EC_IOCTL_GET_EC_MODE,
+
+       // buffer manipulation
+       EC_IOCTL_SET_BUFFER_SIZE,
+       EC_IOCTL_GET_BUFFER_SIZE,
+       EC_IOCTL_RESET_BUFFER,
+
+       // probes management
+       EC_IOCTL_SELECT_PROBE,
+       EC_IOCTL_DESELECT_PROBE,
+       EC_IOCTL_GET_PROBE_INFO,
+       EC_IOCTL_ADD_PROBE,
+       //EC_IOCTL_REMOVE_PROBE,
+       EC_IOCTL_RESET_PROBES,
+       EC_IOCTL_SET_COMPLEX_PROBES,
+
+       // tracing
+       EC_IOCTL_ATTACH,        // attaches selected probes
+       EC_IOCTL_ACTIVATE,      // START
+
+       // stop is common for both tracing and profiling
+       EC_IOCTL_STOP_AND_DETACH,       // STOP (and detach for TRACING)
+
+       // kernel to user notification delivery
+       EC_IOCTL_WAIT_NOTIFICATION,
+
+       // get ec_info
+       EC_IOCTL_GET_EC_INFO,
+       EC_IOCTL_GET_COMPLEX_STATUS,
+       EC_IOCTL_CONSUME_BUFFER,
+
+       // instrument user space process
+       EC_IOCTL_INST_USR_SPACE_PROC,
+       // deinstrument user space process
+       EC_IOCTL_DEINST_USR_SPACE_PROC,
+
+       // conditions
+       EC_IOCTL_UPDATE_CONDS,
+
+       //user space event      
+       EC_IOCTL_US_EVENT,
+
+       //event format
+       EC_IOCTL_SET_EVENT_MASK,
+       EC_IOCTL_GET_EVENT_MASK,
+
+       // pre-defined user space probes
+       EC_IOCTL_SET_PREDEF_UPROBES,    
+       EC_IOCTL_GET_PREDEF_UPROBES,
+       EC_IOCTL_GET_PREDEF_UPROBES_SIZE,
+
+} EC_IOCTL_CMD;
+
+typedef struct
+{
+       unsigned notification_count;
+       ec_info_t *p_ec_info;
+} ioctl_wait_notification_t;
+
+typedef struct
+{
+       int m_signedInt;
+       unsigned int m_unsignedInt;
+       long m_signedLong;
+       unsigned long m_unsignedLong;
+       char* m_ptrChar;
+} ioctl_general_t;
+
+typedef enum
+{
+       OPERATION_ANY,          // means do not check value
+       OPERATION_EQUAL,
+       OPERATION_NOT_EQUAL,
+       OPERATION_LESS,
+       OPERATION_GREATER
+} operation_t;
+
+typedef struct
+{
+       unsigned m_condition_always_false;
+       unsigned m_op_time;
+       unsigned m_op_pid;
+       unsigned m_op_tid;
+       unsigned m_op_probe_id;
+//      char     m_probe_name[SWAP_COMMON_STRING_SIZE];
+       unsigned m_op_record_type;
+       unsigned m_time_sec;
+       unsigned m_time_usec;
+       unsigned m_pid;
+       unsigned m_tid;
+       unsigned m_probe_id;
+       unsigned m_record_type;
+} condition_t;
+
+
+
+// condition matching any event
+#define CONDITION_ANY \
+{ \
+    .m_condition_always_false = 0, \
+        .m_op_time = OPERATION_ANY, \
+        .m_op_pid = OPERATION_ANY, \
+        .m_op_tid = OPERATION_ANY, \
+        .m_op_probe_id = OPERATION_ANY, \
+        .m_op_record_type = OPERATION_ANY, \
+} \
+
+// never matching condition
+#define CONDITION_FALSE \
+{ \
+    .m_condition_always_false = 1, \
+        .m_op_time = OPERATION_ANY, \
+        .m_op_pid = OPERATION_ANY, \
+        .m_op_tid = OPERATION_ANY, \
+        .m_op_probe_id = OPERATION_ANY, \
+        .m_op_record_type = OPERATION_ANY, \
+} \
+
+// default start condition - start immediately
+#define DEFAULT_START_CONDITION CONDITION_ANY
+// default stop condition - never stop
+#define DEFAULT_STOP_CONDITION CONDITION_FALSE
+
+
+typedef struct
+{
+       unsigned count;
+       int *p_pids;
+} ioctl_set_pids_to_ignore_t;
+
+
+typedef struct
+{
+       char *name;
+       unsigned long addr;
+       char type;
+       unsigned long size;
+       signed char reg;        // -1 - memory, 0..127 - register number  
+       long off;
+} ioctl_usr_space_vtp_t;
+
+typedef struct
+{
+       //char *name;
+       //unsigned name_len;
+       unsigned long addr;
+} ioctl_usr_space_ip_t;
+
+typedef struct
+{
+       char *path;
+       //unsigned path_len;
+       unsigned ips_count;
+       ioctl_usr_space_ip_t *p_ips;
+       unsigned vtps_count;
+       ioctl_usr_space_vtp_t *p_vtps;
+} ioctl_usr_space_lib_t;
+
+typedef struct
+{
+       char *path;
+       //unsigned path_len;
+       unsigned libs_count;
+       ioctl_usr_space_lib_t *p_libs;
+} ioctl_inst_usr_space_proc_t;
+
+typedef struct
+{
+       char *proc_name;
+       unsigned count;
+} ioctl_set_proc_to_ignore_t;
+
+typedef struct
+{
+       char *data;
+       unsigned len;
+} ioctl_us_event_t;
+// exclude entry events
+#define IOCTL_EMASK_ENTRY      0x01
+// exclude exit events
+#define IOCTL_EMASK_EXIT       0x02
+// timestamp
+#define IOCTL_EMASK_TIME       0x04
+// PID
+#define IOCTL_EMASK_PID                0x08
+// TID
+#define IOCTL_EMASK_TID        0x10
+// CPU
+#define IOCTL_EMASK_CPU        0x20
+// Args
+#define IOCTL_EMASK_ARGS       0x40
+
+typedef struct
+{
+       unsigned probes_count;
+       char *p_probes;
+} ioctl_predef_uprobes_info_t;
+
+
+#endif /* !defined(ec_ioctl_h) */
diff --git a/driver/events.h b/driver/events.h
new file mode 100644 (file)
index 0000000..030b391
--- /dev/null
@@ -0,0 +1,287 @@
+#ifndef EVENTS_H_
+#define EVENTS_H_
+
+#define UNUSED __attribute__((unused))
+
+#ifdef __KERNEL__
+#include <asm/uaccess.h>       // copy_from_user
+#include "debug.h"             // //DPRINTF
+#else
+#include <string.h>
+#include <stdarg.h>
+#endif
+#include "picl.h"
+#include "ec_ioctl.h"
+#include "ec_probe.h"
+
+static TYPE_ARGUMENT GetArgumentType (char ch)
+{
+       switch (ch)
+       {
+       case 'c':
+               return AT_CHAR;
+               break;
+       case 'h':
+               return AT_SHORT;
+               break;
+       case 'd':
+               return AT_INT;
+               break;
+       case 'x':
+               return AT_LONG;
+               break;
+       case 'p':
+               return AT_PTR;
+               break;
+       case 'f':
+               return AT_FLOAT;
+               break;
+       case 'w':
+               return AT_DOUBLE;
+               break;
+       case 's':
+               return AT_STRING;
+               break;
+       case 'a':
+               return AT_ARRAY;
+               break;
+       };
+       return AT_UNKNOWN;
+}
+
+static char *PackArguments (char *pBuffer, unsigned long nLen, const char *szFormat, va_list args)
+{
+       TYPE_ARGUMENT nArgType = AT_UNKNOWN;
+       const char *pChar = NULL;
+       char chCode = '\0';
+       char *pResult = pBuffer;
+       unsigned long nLengthOfDescriptor = 0, nFree = nLen;
+       unsigned long nSizeOfDescriptor = 0;
+
+       // Descriptor
+       nLengthOfDescriptor = strlen (szFormat) + 1;
+       nSizeOfDescriptor = ALIGN_VALUE(nLengthOfDescriptor);
+       if(nFree < nSizeOfDescriptor)
+               return NULL; // no space for descriptor 
+       memcpy (pResult, szFormat, nLengthOfDescriptor);
+       pResult += nSizeOfDescriptor;
+       nFree -= nSizeOfDescriptor;
+
+       for (pChar = szFormat; (chCode = *pChar) != '\0'; pChar++)
+       {
+               nArgType = GetArgumentType(chCode);
+               switch (nArgType)
+               {
+               case AT_CHAR:
+                       {
+                               int ch = va_arg (args, int);
+                               if(nFree < sizeof(ch))
+                                       return NULL; // no space for arg
+                               memcpy(pResult, &ch, sizeof (ch));
+                               pResult += sizeof (ch);
+                               nFree -= sizeof (ch);
+                       }
+                       break;
+               case AT_SHORT:
+                       {
+                               int nShort = va_arg (args, int);
+                               if(nFree < sizeof(nShort))
+                                       return NULL; // no space for arg
+                               memcpy(pResult, &nShort, sizeof (nShort));
+                               pResult += sizeof (nShort);
+                               nFree -= sizeof (nShort);
+                       }
+                       break;
+               case AT_INT:
+                       {
+                               int nInt = va_arg (args, int);
+                               if(nFree < sizeof(nInt))
+                                       return NULL; // no space for arg
+                               memcpy(pResult, &nInt, sizeof (nInt));
+                               pResult += sizeof (nInt);
+                               nFree -= sizeof (nInt);
+                       }
+                       break;
+               case AT_LONG:
+                       {
+                               long nLong = va_arg (args, long);
+                               if(nFree < sizeof(nLong))
+                                       return NULL; // no space for arg
+                               memcpy (pResult, &nLong, sizeof (nLong));
+                               pResult += sizeof (nLong);
+                               nFree -= sizeof (nLong);
+                       }
+                       break;
+               case AT_PTR:
+                       {
+                               void *p = va_arg (args, void *);
+                               if(nFree < sizeof(p))
+                                       return NULL; // no space for arg
+                               memcpy (pResult, &p, sizeof (p));
+                               pResult += sizeof (p);
+                               nFree -= sizeof (p);
+                       }
+                       break;
+               case AT_FLOAT:
+                       {
+                               double fValue = va_arg (args, double);
+                               if(nFree < sizeof(fValue))
+                                       return NULL; // no space for arg
+                               memcpy (pResult, &fValue, sizeof (fValue));
+                               pResult += sizeof (fValue);
+                               nFree -= sizeof (fValue);
+                       }
+                       break;
+               case AT_DOUBLE:
+                       {
+                               double fDouble = va_arg (args, double);
+                               if(nFree < sizeof(fDouble))
+                                       return NULL; // no space for arg
+                               memcpy (pResult, &fDouble, sizeof (fDouble));
+                               pResult += sizeof (fDouble);
+                               nFree -= sizeof (fDouble);
+                       }
+                       break;
+               case AT_STRING:
+                       {
+                               const char *s = va_arg (args, const char *);
+                               int nLengthOfString = 0, nSizeOfString;
+#ifdef __KERNEL__
+                               if((void *)s < (void *)TASK_SIZE) {
+                                       nLengthOfString = strlen_user (s);
+                                       if(nFree < nLengthOfString)
+                                               return NULL; // no space for arg
+                                       if(strncpy_from_user(pResult, s, nLengthOfString) != (nLengthOfString-1))
+                                               EPRINTF ("failed to copy string from user %p, bytes %d", s, nLengthOfString);
+                               }
+                               else
+#endif
+                               {
+                                       nLengthOfString = strlen (s) + 1;
+                                       if(nFree < nLengthOfString)
+                                               return NULL; // no space for arg
+                                       memcpy (pResult, s, nLengthOfString);
+                               }
+                               nSizeOfString = ALIGN_VALUE (nLengthOfString);
+                               if(nFree < nSizeOfString)
+                                       return NULL; // no space for arg
+                               pResult += nSizeOfString;
+                               nFree -= nSizeOfString;
+                       }
+                       break;
+               case AT_ARRAY:
+                       {
+                               int nLength = va_arg (args, int);
+                               void *p = NULL;
+                               int nSize = 0;
+                               nSize = ALIGN_VALUE (nLength);
+                               if(nFree < nSize)
+                                       return NULL; // no space for arg
+                               memcpy (pResult, &nLength, sizeof (int));
+                               pResult += sizeof (int);
+                               p = va_arg (args, void *);
+#ifdef __KERNEL__
+                               if(p < (void *)TASK_SIZE) {
+                                       if(copy_from_user(pResult, p, nLength)!= 0)
+                                               EPRINTF ("failed to copy array from user %p, bytes %d", p, nLength);
+                               }
+                               else
+#endif
+                                       memcpy (pResult, p, nLength);
+                               pResult += nSize;
+                               nFree -= nSize;
+                       }
+                       break;
+               default:
+                       break;
+               };
+       }
+       return pResult;
+}
+
+static UNUSED TYPEOF_EVENT_LENGTH VPackEvent(char *buf, unsigned long buf_len, int mask, TYPEOF_PROBE_ID probe_id, 
+                               TYPEOF_EVENT_TYPE record_type, TYPEOF_TIME *tv, TYPEOF_PROCESS_ID pid, 
+                               TYPEOF_THREAD_ID tid, TYPEOF_CPU_NUMBER cpu, const char *fmt, va_list args)
+{
+       char *cur = buf;
+       SWAP_TYPE_EVENT_HEADER *pEventHeader = (SWAP_TYPE_EVENT_HEADER *)buf;
+
+       if(buf_len < sizeof(SWAP_TYPE_EVENT_HEADER))
+               return 0; // no space for header
+       
+       pEventHeader->m_nLength = 0;
+       cur += sizeof(TYPEOF_EVENT_LENGTH);
+       pEventHeader->m_nType = record_type;
+       cur += sizeof(TYPEOF_EVENT_TYPE);
+       pEventHeader->m_nProbeID = probe_id;
+       cur += sizeof(TYPEOF_PROBE_ID);
+       //pEventHeader->m_time.tv_sec = tv->tv_sec;
+       //pEventHeader->m_time.tv_usec = tv->tv_usec;
+       if((probe_id == EVENT_FMT_PROBE_ID) || !(mask & IOCTL_EMASK_TIME)){             
+               memcpy(cur, tv, sizeof(TYPEOF_TIME));
+               cur += sizeof(TYPEOF_TIME);
+       }
+       //pEventHeader->m_nProcessID = pid;
+       if((probe_id == EVENT_FMT_PROBE_ID) || !(mask & IOCTL_EMASK_PID)){              
+               (*(TYPEOF_PROCESS_ID *)cur) = pid;
+               cur += sizeof(TYPEOF_PROCESS_ID);
+       }
+       //pEventHeader->m_nThreadID = tid;
+       if((probe_id == EVENT_FMT_PROBE_ID) || !(mask & IOCTL_EMASK_TID)){              
+               (*(TYPEOF_THREAD_ID *)cur) = tid;
+               cur += sizeof(TYPEOF_THREAD_ID);
+       }
+       //pEventHeader->m_nCPU = cpu;
+       if((probe_id == EVENT_FMT_PROBE_ID) || !(mask & IOCTL_EMASK_CPU)){              
+               (*(TYPEOF_CPU_NUMBER *)cur) = cpu;
+               cur += sizeof(TYPEOF_CPU_NUMBER);
+       }
+       // dyn lib event should have all args, it is for internal use and not visible to user
+       if((probe_id == EVENT_FMT_PROBE_ID) || (probe_id == DYN_LIB_PROBE_ID) || !(mask & IOCTL_EMASK_ARGS)){
+               (*(TYPEOF_NUMBER_OF_ARGS *)cur) = strlen(fmt);
+               cur += sizeof(TYPEOF_NUMBER_OF_ARGS);
+               cur = PackArguments(cur, buf_len-(cur-buf), fmt, args);
+               if(!cur) return 0; // no space for args
+       }
+       else {
+               // user space and dynamic kernel probes should have at least one argument
+               // to identify them 
+               if((probe_id == US_PROBE_ID) || (probe_id == VTP_PROBE_ID) || (probe_id == KS_PROBE_ID)){
+                       char fmt2[2];
+                       (*(TYPEOF_NUMBER_OF_ARGS *)cur) = 1;
+                       cur += sizeof(TYPEOF_NUMBER_OF_ARGS);
+                       // pack args using format string for the 1st arg only
+                       fmt2[0] = fmt[0]; fmt2[1] = '\0';                       
+                       cur = PackArguments(cur, buf_len-(cur-buf), fmt2, args);
+                       if(!cur) return 0; // no space for args                 
+               }
+               else {
+                       (*(TYPEOF_NUMBER_OF_ARGS *)cur) = 0;
+                       cur += sizeof(TYPEOF_NUMBER_OF_ARGS);
+               }
+       }
+       
+       pEventHeader->m_nLength = cur - buf + sizeof(TYPEOF_EVENT_LENGTH);
+       if(buf_len < pEventHeader->m_nLength)
+               return 0;// no space for back length
+       //memcpy(cur, &pEventHeader->m_nLength, sizeof(TYPEOF_EVENT_LENGTH));
+       *((TYPEOF_EVENT_LENGTH *)cur) = pEventHeader->m_nLength;
+
+       return pEventHeader->m_nLength;
+}
+
+/*static TYPEOF_EVENT_LENGTH PackEvent(char *buf, unsigned long buf_len, TYPEOF_PROBE_ID probe_id, 
+                               TYPEOF_EVENT_TYPE record_type, TYPEOF_TIME *tv, TYPEOF_PROCESS_ID pid, 
+                               TYPEOF_THREAD_ID tid, TYPEOF_CPU_NUMBER cpu, const char *fmt, ...)
+{
+       va_list args;
+       TYPEOF_EVENT_LENGTH len;
+       
+       va_start (args, fmt);
+       len = VPackEvent(buf, buf_len, probe_id, record_type, tv, pid, tid, cpu, fmt, args);
+       va_end (args);
+       
+       return len;
+}*/
+
+#endif /*EVENTS_H_*/
diff --git a/driver/inperfa_driver.sh b/driver/inperfa_driver.sh
new file mode 100755 (executable)
index 0000000..b8a401a
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+
+# name of the file with module
+MODULE_FILE=inperfa_driver
+
+# device name
+DEVICE=inperfa_drv
+DEFAULT_MAJOR=250
+
+# name of device visible in /proc/devices
+DEVICE_NAME=${DEVICE}
+
+# name of the device file for /dev/
+DEVICE_FILE=${DEVICE}
+
+KSYMS=kallsyms_lookup_name
+
+# ADDRESS for "kallsyms_lookup_name" function taken from /proc/kallsyms
+ADDRESS=0x`sed "/ kallsyms_lookup_name/ ! d" /proc/kallsyms | sed "s/ T kallsyms_lookup_name//"`
+
+if [ "${ADDRESS}" = "0x" ]; then
+    if [ "$1" = "" ]; then
+       echo "Enter kallsyms_lookup_name as parameter:"
+       echo "insmod.sh <kallsyms_lookup_name address>"
+       exit
+    else
+       ADDRESS=$1
+       echo "kallsyms_lookup_name address is ${ADDRESS}"
+    fi
+fi
+
+MAJOR=`sed "/${DEVICE_NAME}/ ! d" /proc/devices | sed "s/ ${DEVICE_NAME}//"`
+if [ "${MAJOR}" != "" ] ; then
+    echo "Inperfa Driver is already loaded!"
+    exit 1
+    rmmod ${MODULE_FILE}
+    MAJOR=`sed "/${DEVICE_NAME}/ ! d" /proc/devices | sed "s/ ${DEVICE_NAME}//"`
+    if [ "${MAJOR}" != "" ] ; then
+        echo "Error: Unable to unload driver module '${MODULE_FILE}'"
+        exit 1
+    fi
+fi
+
+if [ ! -c /dev/${DEVICE_FILE} ] ; then
+       echo "WARNING: Creating device node with major number [${DEFAULT_MAJOR}]!"
+       mknod /dev/${DEVICE_FILE} c ${DEFAULT_MAJOR} 0
+       if [ $? -ne 0 ]; then
+           echo "Error: Unable to create device node!"
+           exit
+       fi
+       chmod a+r /dev/${DEVICE_FILE}
+fi
+
+# load driver module
+echo "loading module '${MODULE_FILE}'"
+insmod ${MODULE_FILE}.ko fp_kallsyms_lookup_name=${ADDRESS} device_name=${DEVICE_NAME} device_major=${DEFAULT_MAJOR}
+if [ $? -ne 0 ]; then
+    echo "Error: Unable to load Swap Driver!"
+fi
+MAJOR=`sed "/${DEVICE_NAME}/ ! d" /proc/devices | sed "s/ ${DEVICE_NAME}//"`
+
diff --git a/driver/legacy.c b/driver/legacy.c
new file mode 100644 (file)
index 0000000..838151f
--- /dev/null
@@ -0,0 +1,114 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           legacy.c
+//
+//      DESCRIPTION:
+//      This file is C source for SWAP driver.
+//
+//      SEE ALSO:       legacy.h
+//      AUTHOR:         L.Komkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.02
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#include "module.h"
+#include "legacy.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
+signed long
+schedule_timeout_interruptible (signed long timeout)
+{
+       __set_current_state (TASK_INTERRUPTIBLE);
+       return schedule_timeout (timeout);
+}
+#endif /* kernel without schedule_timeout_interruptible */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+int
+remap_vmalloc_range (struct vm_area_struct *vma, void *addr, unsigned long pgoff)
+{
+       unsigned long uaddr = vma->vm_start;
+       unsigned long usize = vma->vm_end - vma->vm_start;
+       int ret = -EINVAL;
+
+       if ((PAGE_SIZE - 1) & (unsigned long) addr)
+       {
+               return -EINVAL;
+       }
+
+       if (pgoff)
+       {
+               return -EINVAL;
+       }
+
+       while (usize)
+       {
+               ret = remap_pfn_range (vma, uaddr, vmalloc_to_pfn (addr), PAGE_SIZE, PAGE_SHARED);
+               if (ret)
+               {
+                       break;
+               }
+               uaddr += PAGE_SIZE;
+               addr += PAGE_SIZE;
+               usize -= PAGE_SIZE;
+       };
+
+       return ret;
+}
+#endif /* kernel without remap_vmalloc_range() */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+/*
+    To be mappable to user space, pages of memory allocated via "vmalloc" must
+    be marked with "PG_reserved" flag. Memory allocated via "vmalloc_user"
+    doesn't need it.
+*/
+static void
+_reserve_pages (void *p, unsigned size)
+{
+       unsigned pages = (size + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+       while (pages)
+       {
+               SetPageReserved (vmalloc_to_page (p));
+
+               p += PAGE_SIZE;
+               --pages;
+       }
+}
+
+static void
+_unreserve_pages (void *p, unsigned size)
+{
+       unsigned pages = (size + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+       while (pages)
+       {
+               ClearPageReserved (vmalloc_to_page (p));
+
+               p += PAGE_SIZE;
+               --pages;
+       }
+}
+
+void *
+vmalloc_user (unsigned long size)
+{
+       void *p = __vmalloc (size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
+       if (p)
+       {
+               memset (p, 0, size);
+               _reserve_pages (p, size);
+       }
+       return p;
+}
+
+void
+vfree_user (void *address, unsigned long size)
+{
+       _unreserve_pages (address, size);
+       vfree (address);
+}
+#endif /* kernel without vmalloc_user() */
diff --git a/driver/legacy.h b/driver/legacy.h
new file mode 100644 (file)
index 0000000..5ef4585
--- /dev/null
@@ -0,0 +1,54 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           legacy.h
+//
+//      DESCRIPTION:
+//
+//      SEE ALSO:       legacy.c
+//      AUTHOR:         L.Komkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+/*
+    Support for legacy Linux kernel versions
+*/
+#if !defined(__LEGACY_H__)
+#define __LEGACY_H__
+
+#include <linux/mm.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
+extern signed long schedule_timeout_interruptible (signed long timeout);
+#endif /* kernel without schedule_timeout_interruptible */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+extern int remap_vmalloc_range (struct vm_area_struct *vma, void *addr, unsigned long pgoff);
+#endif /* kernel without remap_vmalloc_range() */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+extern void *vmalloc_user (unsigned long size);
+extern void vfree_user (void *address, unsigned long size);
+#define VFREE_USER(address, size) \
+       { \
+               if(address != NULL) { \
+                       vfree_user(address, size); \
+                       address = NULL; \
+               } \
+       }
+#else
+#define VFREE_USER(address, size) \
+       { \
+               if(address != NULL) { \
+                       vfree(address); \
+                       address = NULL; \
+               } \
+       }
+#endif /* kernel without vmalloc_user() */
+
+#endif /* !defined(__LEGACY_H__) */
diff --git a/driver/module.c b/driver/module.c
new file mode 100644 (file)
index 0000000..6c42250
--- /dev/null
@@ -0,0 +1,131 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           module.c
+//
+//      DESCRIPTION:
+//      This file is C source for SWAP driver.
+//
+//      SEE ALSO:       module.h
+//      AUTHOR:         L.Komkov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#include "module.h"
+
+int fp_kallsyms_lookup_name = 0;
+module_param (fp_kallsyms_lookup_name, int, 0);
+MODULE_PARM_DESC (fp_kallsyms_lookup_name, "address of 'kallsyms_lookup_name' function");
+
+char gl_szDefaultDeviceName[128] = DEFAULT_DEVICE_NAME;
+char* device_name = NULL;
+module_param (device_name, charp, 0);
+MODULE_PARM_DESC (device_name, "device name for '/proc/devices'");
+
+unsigned int device_major = 0;
+module_param (device_major, uint, 0);
+MODULE_PARM_DESC (device_major, "default device major number");
+
+fp_kallsyms_lookup_name_t lookup_name;
+
+#if (LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 16))
+void (*__real_put_task_struct) (struct task_struct * tsk);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11))
+#define SWAPDRV_PUT_TASK_STRUCT        "put_task_struct"
+void
+put_task_struct (struct task_struct *tsk)
+{
+       __real_put_task_struct (tsk);
+}
+#else
+#define SWAPDRV_PUT_TASK_STRUCT        "__put_task_struct"
+void
+__put_task_struct (struct task_struct *tsk)
+{
+       __real_put_task_struct (tsk);
+}
+#endif
+#else /*2.6.16 */
+void (*__real_put_task_struct) (struct rcu_head * rhp);
+#define SWAPDRV_PUT_TASK_STRUCT        "__put_task_struct_cb"
+void
+__put_task_struct_cb (struct rcu_head *rhp)
+{
+       __real_put_task_struct (rhp);
+}
+#endif
+/*void (*__real_put_task_struct)(struct task_struct *tsk);
+void __put_task_struct(struct task_struct *tsk)
+{
+       __real_put_task_struct(tsk);
+}*/
+
+#if defined(CONFIG_MIPS)
+void (*flush_cache_page) (struct vm_area_struct * vma, unsigned long page);
+#endif
+
+static int __init InitializeModule(void)
+{
+       if(!fp_kallsyms_lookup_name) {
+               EPRINTF("fp_kallsyms_lookup_name parameter undefined!");
+               return -1;
+       }
+       if(device_name == NULL) {
+               EPRINTF("Using default device name!");
+               device_name = gl_szDefaultDeviceName;
+       }
+       if(device_major == 0) {
+               EPRINTF("Using default device major number!");
+               device_major = DEFAULT_DEVICE_MAJOR;
+       }
+
+       lookup_name = (fp_kallsyms_lookup_name_t) fp_kallsyms_lookup_name;
+       __real_put_task_struct = (void *) lookup_name (SWAPDRV_PUT_TASK_STRUCT);
+       if (!__real_put_task_struct)
+       {
+               EPRINTF (SWAPDRV_PUT_TASK_STRUCT " is not found! Oops. Where is it?");
+               return -ESRCH;
+       }
+
+#if defined(CONFIG_MIPS)
+       flush_cache_page = (void *) fp ("r4k_flush_cache_page");
+       if (!flush_cache_page)
+       {
+               EPRINTF ("failed to resolve 'flush_cache_page'!\n");
+               return -ESRCH;
+       }
+#endif
+
+       if(probes_manager_init() < 0) {
+               EPRINTF ("Cannot initialize probe manager!");
+               return -1;
+       }
+       if(device_init() < 0) {
+               EPRINTF ("Cannot initialize device!");
+               return -1;
+       }
+
+       INIT_LIST_HEAD(&cond_list.list);
+
+       DPRINTF ("is successfully initialized.");
+       return 0;
+}
+
+static void __exit UninitializeModule (void)
+{
+       ec_user_stop ();
+       device_down ();
+       probes_manager_down ();
+       DPRINTF ("is successfully finished.");
+}
+
+module_init (InitializeModule);
+module_exit (UninitializeModule);
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Adwanced Software Group (SRC, Moscow)");
+MODULE_DESCRIPTION("InPerfA Device Driver");
+MODULE_VERSION("4:1.0");
diff --git a/driver/module.h b/driver/module.h
new file mode 100644 (file)
index 0000000..d4f9acb
--- /dev/null
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           module.h
+//
+//      DESCRIPTION:
+//
+//      SEE ALSO:       module.c
+//      AUTHOR:         L.Komkov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(__MODULE_H__)
+#define __MODULE_H__
+
+#define UNUSED __attribute__((unused))
+
+#include <linux/types.h>
+#include <asm/current.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+#include <asm/local.h>
+#include <asm/string.h>
+#include <asm/mman.h>
+
+#include <net/sock.h>
+#include <net/tcp.h>
+#include <net/inet_common.h>
+
+#include <linux/gfp.h>
+#include <linux/mm.h>
+#include <linux/page-flags.h>
+#include <linux/vmalloc.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/genhd.h>
+#include <linux/fs.h>
+#include <linux/bio.h>
+#include <linux/file.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/ipsec.h>
+#include <linux/sysctl.h>
+#include <linux/dcache.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/audit.h>
+#include <linux/namei.h>
+#include <linux/signal.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/jiffies.h>
+#include <linux/time.h>
+#include <linux/proc_fs.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14))
+#ifndef __DISABLE_RELAYFS
+#define __DISABLE_RELAYFS
+#warning "RELAY FS was disabled since not supported!"
+#endif // __DISABLE_RELAYFS
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) & LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17))
+#include <linux/debugfs.h>
+#include <linux/relayfs_fs.h>
+#else
+#include <linux/debugfs.h>
+#include <linux/relay.h>
+#endif
+
+#include "events.h"
+
+#include "debug.h"
+#include "ec.h"
+#include "event.h"
+#include "legacy.h"
+#include "storage.h"
+#include "us_proc_inst.h"
+#include "device_driver.h"
+#include "probes_manager.h"
+#include "probes.h"
+#include "stdfunc.h"
+
+typedef unsigned long (*fp_kallsyms_lookup_name_t) (const char *name);
+extern int fp_kallsyms_lookup_name;
+extern fp_kallsyms_lookup_name_t lookup_name;
+extern char *device_name;
+extern unsigned int device_major;
+
+#endif /* !defined(module_h) */
diff --git a/driver/picl.h b/driver/picl.h
new file mode 100644 (file)
index 0000000..fcc0699
--- /dev/null
@@ -0,0 +1,139 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           PICL.h
+//
+//      DESCRIPTION:
+//
+//      SEE ALSO:       PICL.cpp
+//      AUTHOR:         L.Komkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.02
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(PICL_h)
+#define PICL_h
+
+#ifdef INPERFA_HOST
+#include <sys/time.h> // struct timeval
+#else // INPERFA_TARGET
+#ifdef __KERNEL__
+#include <linux/time.h>                // struct timeval
+#else
+#include <sys/time.h> // struct timeval
+#endif
+#endif
+
+#define MAX_ARGS_DATA 200
+
+#define EVENT_MAX_SIZE 2048*5
+
+/* EvenRecord format
+ ______________________________________________________________________________________________________________
+| Len | RecType | EventType | Time | Pid | Tid | CPU | ArgsCount |  ArgTypes  |    ArgValues       | PreEventLen |
+ __4b_____4b________4b________8b_____4b____4b_____4b______4b________ArgCount___variable-length._______4b_____
+  
+*/
+
+typedef enum
+{
+       RECORD_ENTRY = 0,
+       RECORD_RET,
+       RECORD_PROFILE,
+
+       RECORD_TYPE_COUNT       // fictional record type used to count real record types
+} record_type_t;
+
+const char *ec_record_type_to_name (record_type_t record_type);
+int ec_record_type_by_name (record_type_t * p_record_type, const char *record_type_name);
+
+// old Picl.h
+
+#define _TRACE_TYPE        0
+#define _TRACE_STATS_TYPE  2
+
+typedef union tagArgValueType
+{
+       char m_char;
+       int m_int;
+       long m_long;
+       float m_float;
+       double m_double;
+       void *m_pointer;
+       char *m_string;
+} _ARG_VALUE_TYPE;
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <sys/types.h>
+#endif
+
+struct common_timeval {
+       u_int32_t tv_sec;
+       u_int32_t tv_usec;
+};
+
+typedef u_int32_t TYPEOF_EVENT_LENGTH;
+typedef u_int32_t TYPEOF_EVENT_TYPE;
+typedef u_int32_t TYPEOF_PROBE_ID;
+typedef struct common_timeval TYPEOF_TIME;
+typedef u_int32_t TYPEOF_PROCESS_ID;
+typedef u_int32_t TYPEOF_THREAD_ID;
+typedef u_int32_t TYPEOF_CPU_NUMBER;
+typedef u_int32_t TYPEOF_NUMBER_OF_ARGS;
+
+typedef struct tagEventHeader
+{
+       TYPEOF_EVENT_LENGTH m_nLength;
+       TYPEOF_EVENT_TYPE m_nType;
+       TYPEOF_PROBE_ID m_nProbeID;
+       TYPEOF_TIME m_time;
+       TYPEOF_PROCESS_ID m_nProcessID;
+       TYPEOF_THREAD_ID m_nThreadID;
+       TYPEOF_CPU_NUMBER m_nCPU;
+       TYPEOF_NUMBER_OF_ARGS m_nNumberOfArgs;
+} SWAP_TYPE_EVENT_HEADER;
+
+typedef struct tagEvent
+{
+       SWAP_TYPE_EVENT_HEADER *m_pHeader;
+       char *m_pDescriptor;
+       char *m_pData;
+} SWAP_TYPE_EVENT;
+
+/*
+       Argument Descriptors
+
+       c - char
+       h - short
+       d - int
+       x - long
+       p - pointer
+       f - float
+       w - double
+       s - string
+*/
+
+typedef enum
+{
+       AT_UNKNOWN,
+       AT_CHAR,
+       AT_SHORT,
+       AT_INT,
+       AT_LONG,
+       AT_PTR,
+       AT_FLOAT,
+       AT_DOUBLE,
+       AT_STRING,
+       AT_ARRAY
+} TYPE_ARGUMENT;
+
+#define ALIGN_VALUE(x) ((x) + (4 - ((x) % 4)))
+
+#endif /* !defined(PICL_h) */
diff --git a/driver/probes.c b/driver/probes.c
new file mode 100644 (file)
index 0000000..d2fdc21
--- /dev/null
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           probes.c
+//
+//      DESCRIPTION:
+//      This file is C source for SWAP driver.
+//
+//      SEE ALSO:       probes.h
+//      AUTHOR:         L.Komkov, S.Grekhov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#include "module.h"
+#include "probes.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+#define tcp_opt tcp_sock
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+#define kmem_cache_t struct kmem_cache
+#endif
+
+/*
+#define UNUSED __attribute__((unused))
+
+#define USER_PRIO(p)           ((p)-MAX_RT_PRIO)
+#define MAX_USER_PRIO          (USER_PRIO(MAX_PRIO))
+
+#define PRIO_BONUS_RATIO        25
+#define MAX_BONUS              (MAX_USER_PRIO * PRIO_BONUS_RATIO / 100)
+#define INTERACTIVE_DELTA        2
+
+#define PRIO_TO_NICE(prio)     ((prio) - MAX_RT_PRIO - 20)
+#define TASK_NICE(p)           PRIO_TO_NICE((p)->static_prio)
+
+#define SCALE(v1,v1_max,v2_max) (v1) * (v2_max) / (v1_max)
+
+#define DELTA(p) (SCALE(TASK_NICE(p), 40, MAX_BONUS) + INTERACTIVE_DELTA)
+
+#define TASK_INTERACTIVE(p) ((p)->prio <= (p)->static_prio - DELTA(p))
+*/
+
+const char *ec_probe_name[] = {
+       "ks_probe_id",
+       "us_probe_id",
+       "vtp_probe_id",
+       "dyn_lib_probe_id",
+       "event_fmt_probe_id",
+       "rq_profile",
+       "pid_rq_profile"
+};
+
+//TODO: the same function should be used from utils.cpp
+int name2index (unsigned *p_index, unsigned count, const char **names, const char *name)
+{
+       unsigned index;
+       for (index = 0; index < count; ++index) {
+               if (!strcmp (names[index], name)) {
+                       *p_index = index;
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
diff --git a/driver/probes.h b/driver/probes.h
new file mode 100644 (file)
index 0000000..1a077c4
--- /dev/null
@@ -0,0 +1,68 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           probes.h
+//
+//      DESCRIPTION:
+//
+//      SEE ALSO:       probes.c
+//      AUTHOR:         L.Komkov, S.Grekhov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef __PROBES_H__
+#define __PROBES_H__
+
+#include "ec_probe.h"
+#include "storage.h"
+#include "../kprobe/kprobes.h"
+
+#ifndef regs_return_value
+/* "regs_return_value" is ARCH-dependent. ARCH is passed via "EC_ARCH_*" */
+
+#ifdef EC_ARCH_arm
+/* ARCH == arm */
+#define regs_return_value(regs) ((regs)->ARM_r0)
+#endif /* def EC_ARCH_arm */
+
+#ifdef EC_ARCH_i386
+/* ARCH == i386 */
+//#include <linux/kprobes.h>
+#define regs_return_value(regs) ((regs)->ax)
+#endif /* def EC_ARCH_i386 */
+
+#ifdef EC_ARCH_mips
+/* ARCH == mips */
+#define regs_return_value(regs) ((regs)->regs[2])
+#endif /* def EC_ARCH_mips */
+
+#endif /* ndef regs_return_value */
+
+extern struct jprobe my_jprobe[];
+extern const char *ec_probe_name[];
+
+extern struct kretprobe my_kretprobe[];
+
+#define MY_JPROBE_ENTRY(handler_entry) { .entry = JPROBE_ENTRY(handler_entry) }
+
+/* Probe up to 20 instances concurrently. */
+#define MAXACTIVE 20
+
+#define MY_RETPROBE_HANDLER(handler_entry) { .handler = (handler_entry), .maxactive = MAXACTIVE }
+
+#define MY_UAPP(_ips_arr) { .path="", .m_f_dentry=NULL, \
+       .ips_count=sizeof(_ips_arr)/sizeof(us_proc_ip_t), .p_ips=_ips_arr, \
+       .vtps_count=0, .p_vtps=NULL, .loaded=0}
+#define MY_ULIB(_lib, _ips_arr) { .path=#_lib, .m_f_dentry=NULL, \
+       .ips_count=sizeof(_ips_arr)/sizeof(us_proc_ip_t), .p_ips=_ips_arr, \
+       .vtps_count=0, .p_vtps=NULL, .loaded=0}
+#define MY_UPROBE_ENTRY(_name, _entry_hand, _exit_hand) {.name = #_name, \
+       .jprobe.entry = JPROBE_ENTRY(_entry_hand), \
+       .retprobe.handler = (kretprobe_handler_t)_exit_hand}
+#define MY_UPROBE_ENTRY_EXT(_name, _pre_entry_hand, _entry_hand, _exit_hand) {.name = #_name, .jprobe.pre_entry = (kprobe_pre_entry_handler_t)_pre_entry_hand, .jprobe.entry = JPROBE_ENTRY(_entry_hand), .retprobe.handler = (kretprobe_handler_t)_exit_hand}
+
+#endif // __PROBES_H__
diff --git a/driver/probes_manager.c b/driver/probes_manager.c
new file mode 100644 (file)
index 0000000..57616d5
--- /dev/null
@@ -0,0 +1,405 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           probes_manager.c
+//
+//      DESCRIPTION:
+//      This file is C source for SWAP driver.
+//
+//      SEE ALSO:       probes_manager.h
+//      AUTHOR:         L.Komkov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#include <linux/percpu.h>
+#include "module.h"
+#include "probes_manager.h"
+
+#ifdef EC_ARCH_arm
+/* ARCH == arm */
+#include "../kprobe/kprobes.h"
+#endif /* def EC_ARCH_arm */
+
+#ifdef EC_ARCH_i386
+/* ARCH == i386 */
+//#include <linux/kprobes.h>
+#include "../kprobe/kprobes.h"
+#endif /* def EC_ARCH_i386 */
+
+#ifdef EC_ARCH_mips
+/* ARCH == mips */
+#include "../kprobe/kprobes.h"
+#endif /* def EC_ARCH_mips */
+
+unsigned long pf_addr;
+unsigned long exit_addr;
+kernel_probe_t *pf_probe = NULL;
+kernel_probe_t *exit_probe = NULL;
+unsigned int probes_flags = 0;
+
+#ifdef MEMORY_CHECKER
+EXPORT_SYMBOL_GPL(pf_addr);
+#endif
+
+int
+probes_manager_init (void)
+{
+#ifdef CONFIG_X86
+       pf_addr = lookup_name("handle_mm_fault");
+#else
+       pf_addr = lookup_name("do_page_fault");
+#endif
+       if (pf_addr == 0) {
+               EPRINTF("Cannot find address for page fault function!");
+               return -EINVAL;
+       }
+
+       exit_addr = lookup_name("do_exit");
+       if (exit_addr == 0) {
+               EPRINTF("Cannot find address for do_exit function!");
+               return -EINVAL;
+       }
+
+       return storage_init ();
+}
+
+void
+probes_manager_down (void)
+{
+       detach_selected_probes ();
+       storage_down ();
+}
+
+static int
+register_kernel_jprobe (kernel_probe_t * probe)
+{
+       int result;
+       if (((probe == pf_probe) && (us_proc_probes & US_PROC_PF_INSTLD)) ||
+           ((probe == exit_probe) && (us_proc_probes & US_PROC_EXIT_INSTLD)))
+       {
+               return 0;       // probe is already registered
+       }
+       result = register_jprobe (&probe->jprobe, 0);
+       if (result)
+       {
+               EPRINTF ("register_kernel_jprobe(0x%lx) failure %d", probe->addr, result);
+               return result;
+       }
+       return 0;
+}
+
+static int
+unregister_kernel_jprobe (kernel_probe_t * probe)
+{
+       if (((probe == pf_probe) && (us_proc_probes & US_PROC_PF_INSTLD)) ||
+           ((probe == exit_probe) && (us_proc_probes & US_PROC_EXIT_INSTLD)))
+       {
+               return 0;       // probe is necessary for user space instrumentation
+       }
+       unregister_jprobe (&probe->jprobe, 0);
+       return 0;
+}
+
+static int
+register_kernel_retprobe (kernel_probe_t * probe)
+{
+       int result;
+       if (((probe == pf_probe) && (us_proc_probes & US_PROC_PF_INSTLD)) ||
+           ((probe == exit_probe) && (us_proc_probes & US_PROC_EXIT_INSTLD))/* ||
+           ((probe == fork_probe) && (us_proc_probes & US_PROC_FORK_INSTLD)) || 
+           ((probe == ss_probe) && (us_proc_probes & US_PROC_SS_INSTLD))*/)
+       {
+               return 0;       // probe is already registered
+       }
+
+       result = register_kretprobe (&probe->retprobe, 0);
+       if (result)
+       {
+               EPRINTF ("register_kernel_retprobe(0x%lx) failure %d", probe->addr, result);
+               return result;
+       }
+       return 0;
+}
+
+static int
+unregister_kernel_retprobe (kernel_probe_t * probe)
+{
+       if (((probe == pf_probe) && (us_proc_probes & US_PROC_PF_INSTLD)) ||
+           ((probe == exit_probe) && (us_proc_probes & US_PROC_EXIT_INSTLD))/* ||
+           ((probe == fork_probe) && (us_proc_probes & US_PROC_FORK_INSTLD)) || 
+           ((probe == ss_probe) && (us_proc_probes & US_PROC_SS_INSTLD))*/)
+       {
+               return 0;       // probe is necessary for user space instrumentation
+       }
+       unregister_kretprobe (&probe->retprobe, 0);
+       return 0;
+}
+
+int
+register_kernel_probe (kernel_probe_t * probe)
+{
+       register_kernel_jprobe (probe);
+       register_kernel_retprobe (probe);
+       return 0;
+}
+
+int
+unregister_kernel_probe (kernel_probe_t * probe)
+{
+       unregister_kernel_jprobe (probe);
+       unregister_kernel_retprobe (probe);
+       return 0;
+}
+
+int
+attach_selected_probes (void)
+{
+       int result = 0;
+       int partial_result = 0;
+       kernel_probe_t *p;
+       struct hlist_node *node;
+
+       hlist_for_each_entry_rcu (p, node, &kernel_probes, hlist)
+       {
+               partial_result = register_kernel_probe (p);
+               if (partial_result)
+               {
+                       result = partial_result;
+                       detach_selected_probes ();      // return into safe state
+                       break;
+               }
+       }
+
+       return result;
+}
+
+int
+detach_selected_probes (void)
+{
+       kernel_probe_t *p;
+       struct hlist_node *node;
+
+       hlist_for_each_entry_rcu (p, node, &kernel_probes, hlist)
+               unregister_kernel_probe (p);
+
+       return 0;
+}
+
+int
+add_probe (unsigned long addr)
+{
+       int result = 0;
+       kernel_probe_t **pprobe = NULL;
+
+       if (EC_STATE_IDLE != ec_info.ec_state)
+       {
+               EPRINTF("Probes addition is allowed in IDLE state only.");
+               return -EINVAL;
+       }
+
+       if (addr == pf_addr) {
+               probes_flags |= PROBE_FLAG_PF_INSTLD;
+               if (us_proc_probes & US_PROC_PF_INSTLD)
+               {
+                       return 0;
+               }
+               pprobe = &pf_probe;
+       }
+       else if (addr == exit_addr) {
+               probes_flags |= PROBE_FLAG_EXIT_INSTLD;
+               if (us_proc_probes & US_PROC_EXIT_INSTLD)
+               {
+                       return 0;
+               }
+               pprobe = &exit_probe;
+       }
+
+       result = add_probe_to_list (addr, pprobe);
+       if (result) {
+               if (addr == pf_addr)
+                       probes_flags &= ~PROBE_FLAG_PF_INSTLD;
+               else if (addr == exit_addr)
+                       probes_flags &= ~PROBE_FLAG_EXIT_INSTLD;
+       }
+       return result;
+}
+
+int reset_probes()
+{
+       struct hlist_node *node, *tnode;
+       kernel_probe_t *p;
+
+       hlist_for_each_entry_safe (p, node, tnode, &kernel_probes, hlist) {
+               if (p->addr == pf_addr) {
+                       probes_flags &= ~PROBE_FLAG_PF_INSTLD;
+                       pf_probe = NULL;
+               } else if (p->addr == exit_addr) {
+                       probes_flags &= ~PROBE_FLAG_EXIT_INSTLD;
+                       exit_probe = NULL;
+               }
+               hlist_del(node);
+               kfree(p);
+       }
+
+       return 0;
+}
+
+int
+remove_probe (unsigned long addr)
+{
+       int result = 0;
+
+       if (EC_STATE_IDLE != ec_info.ec_state)
+       {
+               EPRINTF("Probes addition is allowed in IDLE state only.");
+               return -EINVAL;
+       }
+
+       if (addr == pf_addr) {
+               probes_flags &= ~PROBE_FLAG_PF_INSTLD;
+               if (us_proc_probes & US_PROC_PF_INSTLD)
+               {
+                       return 0;
+               }
+               pf_probe = NULL;
+       }
+       else if (addr == exit_addr) {
+               probes_flags &= ~PROBE_FLAG_EXIT_INSTLD;
+               if (us_proc_probes & US_PROC_EXIT_INSTLD)
+               {
+                       return 0;
+               }
+               exit_probe = NULL;
+       }
+
+       result = remove_probe_from_list (addr);
+
+       return result;
+}
+
+DEFINE_PER_CPU (kernel_probe_t *, gpKernProbe) = NULL;
+EXPORT_PER_CPU_SYMBOL_GPL(gpKernProbe);
+DEFINE_PER_CPU(struct pt_regs *, gpKernRegs) = NULL;
+EXPORT_PER_CPU_SYMBOL_GPL(gpKernRegs);
+
+unsigned long
+def_jprobe_event_pre_handler (kernel_probe_t * probe, struct pt_regs *regs)
+{
+       __get_cpu_var (gpKernProbe) = probe;
+       __get_cpu_var (gpKernRegs) = regs;
+
+       return 0;
+}
+
+void
+def_jprobe_event_handler (unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5, unsigned long arg6)
+{
+       //static int nCount;
+       kernel_probe_t *probe = __get_cpu_var(gpKernProbe);
+       int skip = 0;
+
+       if (pf_probe == probe)
+       {
+               if (!(probes_flags & PROBE_FLAG_PF_INSTLD))
+                       skip = 1;
+       }
+       else if (exit_probe == probe)
+       {
+               if (us_proc_probes & US_PROC_EXIT_INSTLD)
+                       do_exit_probe_pre_code ();
+               if (!(probes_flags & PROBE_FLAG_EXIT_INSTLD))
+                       skip = 1;
+       }
+
+       if (!skip)
+               pack_event_info (KS_PROBE_ID, RECORD_ENTRY, "pxxxxxx", probe->addr, arg1, arg2, arg3, arg4, arg5, arg6);
+       jprobe_return ();
+}
+
+int
+def_retprobe_event_handler (struct kretprobe_instance *pi, struct pt_regs *regs, kernel_probe_t * probe)
+{
+       int skip = 0;
+
+       if (pf_probe == probe)
+       {
+               if (us_proc_probes & US_PROC_PF_INSTLD)
+                       do_page_fault_ret_pre_code ();
+               if (!(probes_flags & PROBE_FLAG_PF_INSTLD))
+                       skip = 1;
+       }
+       else if (exit_probe == probe)
+       {
+               if (!(probes_flags & PROBE_FLAG_EXIT_INSTLD))
+                       skip = 1;
+       }
+
+       if (!skip)
+               pack_event_info (KS_PROBE_ID, RECORD_RET, "p", probe->addr);
+       return 0;
+}
+
+/* This is a callback that is called by module 'inperfa_handlers'
+ * in order to register user defined handlers */
+void install_user_handlers(void)
+{
+       kernel_probe_t *probe;
+       struct hlist_node *node;
+       unsigned long pre_handler_addr, jp_handler_addr, rp_handler_addr;
+
+       /* We must perform this lookup whenever this function is called
+        * because the addresses of find_*_handler functions may differ. */
+       // MCPP inperfa_handlers removed
+       unsigned long (*find_jp_handler)(unsigned long) =
+       // MCPP inperfa_handlers removed
+               (unsigned long (*)(unsigned long))lookup_name("find_jp_handler");
+       unsigned long (*find_rp_handler)(unsigned long) =
+                       (unsigned long (*)(unsigned long))lookup_name("find_rp_handler");
+       unsigned long (*find_pre_handler)(unsigned long) =
+                       (unsigned long (*)(unsigned long))lookup_name("find_pre_handler");
+       hlist_for_each_entry_rcu (probe, node, &kernel_probes, hlist) {
+               if(find_pre_handler)
+               {
+                       pre_handler_addr = find_pre_handler(probe->addr);
+                       if (find_pre_handler != 0) {
+                               DPRINTF("Added user pre handler for 0x%lx: 0x%lx",
+                                               probe->addr, find_pre_handler);
+                               probe->jprobe.pre_entry = (kprobe_pre_entry_handler_t)pre_handler_addr;
+                       }
+               }
+               jp_handler_addr = find_jp_handler(probe->addr);
+               if (jp_handler_addr != 0) {
+                       DPRINTF("Added user jp handler for 0x%lx: 0x%lx",
+                                       probe->addr, jp_handler_addr);
+                       probe->jprobe.entry = (kprobe_opcode_t *)jp_handler_addr;
+               }
+               rp_handler_addr = find_rp_handler(probe->addr);
+               if (rp_handler_addr != 0)
+                       probe->retprobe.handler = (kretprobe_handler_t)rp_handler_addr;
+       }
+}
+EXPORT_SYMBOL_GPL(install_user_handlers);
+
+void uninstall_user_handlers(void)
+{
+       kernel_probe_t *probe;
+       struct hlist_node *node;
+
+       hlist_for_each_entry_rcu (probe, node, &kernel_probes, hlist) {
+               DPRINTF("Removed user jp handler for 0x%lx", probe->addr);
+               probe->jprobe.pre_entry = (kprobe_pre_entry_handler_t)def_jprobe_event_pre_handler;
+               probe->jprobe.entry = (kprobe_opcode_t *)def_jprobe_event_handler;
+               probe->retprobe.handler = (kretprobe_handler_t)def_retprobe_event_handler;
+       }
+}
+EXPORT_SYMBOL_GPL(uninstall_user_handlers);
+
+int is_pf_installed_by_user(void)
+{
+       return (probes_flags & PROBE_FLAG_PF_INSTLD) ? 1: 0;
+}
+EXPORT_SYMBOL_GPL(is_pf_installed_by_user);
diff --git a/driver/probes_manager.h b/driver/probes_manager.h
new file mode 100644 (file)
index 0000000..7438f80
--- /dev/null
@@ -0,0 +1,64 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           probes_manager.h
+//
+//      DESCRIPTION:
+//
+//      SEE ALSO:       probes_manager.c
+//      AUTHOR:         L.Komkov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(__PROBES_MANAGER_H__)
+#define __PROBES_MANAGER_H__
+
+#include "ec_module.h"
+#include "ec_probe.h"
+#include "ec_module.h"
+#include "probes.h"
+
+typedef struct
+{
+       unsigned long addr;
+       struct jprobe jprobe;
+       struct kretprobe retprobe;
+       struct hlist_node hlist;
+} kernel_probe_t;
+
+extern int probes_manager_init (void);
+extern void probes_manager_down (void);
+
+extern int add_probe (unsigned long addr);
+extern int reset_probes(void);
+extern int remove_probe (unsigned long addr);
+
+extern int attach_selected_probes (void);
+extern int detach_selected_probes (void);
+
+extern int register_kernel_probe (kernel_probe_t * probe);
+extern int unregister_kernel_probe (kernel_probe_t * probe);
+
+extern unsigned long def_jprobe_event_pre_handler (kernel_probe_t * probe, struct pt_regs *regs);
+extern void def_jprobe_event_handler (unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5, unsigned long arg6);
+extern int def_retprobe_event_handler (struct kretprobe_instance *probe, struct pt_regs *regs, kernel_probe_t * p);
+void install_user_handlers(void);
+void uninstall_user_handlers(void);
+int is_pf_installed_by_user(void);
+
+extern unsigned long pf_addr;
+extern unsigned long exit_addr;
+extern kernel_probe_t *pf_probe;
+extern kernel_probe_t *exit_probe;
+extern unsigned int probes_flags;
+
+#define PROBE_FLAG_PF_INSTLD   0x1
+#define PROBE_FLAG_EXIT_INSTLD 0x2
+//#define PROBE_FLAG_SS_INSTLD 0x4
+//#define PROBE_FLAG_FORK_INSTLD       0x8
+
+#endif // !defined(__PROBES_MANAGER_H__)
diff --git a/driver/storage.c b/driver/storage.c
new file mode 100644 (file)
index 0000000..d44d8d2
--- /dev/null
@@ -0,0 +1,1914 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           storage.c
+//
+//      DESCRIPTION:
+//      This file is C source for SWAP.
+//
+//      SEE ALSO:       storage.h
+//      AUTHOR:         L.Komkov, S.Dianov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#include <linux/types.h>
+#include <linux/hash.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include "module.h"
+#include "storage.h"
+#include "CProfile.h"
+//#include "def_handlers.h"
+
+#define after_buffer ec_info.buffer_size
+
+
+char *p_buffer = NULL;
+inst_us_proc_t us_proc_info;
+struct hlist_head kernel_probes;
+int event_mask = 0L;
+struct cond cond_list;
+int paused = 0; /* a state after a stop condition (events are not collected) */
+struct timeval last_attach_time = {0, 0};
+
+#ifdef MEMORY_CHECKER
+#define M_BITS 6
+#define M_SIZE (1 << M_BITS)
+
+static struct hlist_head malloc_table[M_SIZE];
+//void *sys_call_table[];
+asmlinkage int (*orig_sys_mprotect)(unsigned long start, size_t len, unsigned long prot);
+
+typedef struct {
+    void * address;
+    size_t size;
+    struct hlist_node hlist;
+} mec_object_t;
+
+EXPORT_SYMBOL_GPL(us_proc_info);
+#endif
+
+unsigned copy_into_cyclic_buffer (char *buffer, unsigned dst_offset, char *src, unsigned size)
+{
+       unsigned nOffset = dst_offset;
+       char* pSource = src;
+       while (size--)
+               buffer[nOffset++] = *pSource++;
+       return nOffset;
+}
+
+unsigned copy_from_cyclic_buffer (char *dst, char *buffer, unsigned src_offset, unsigned size)
+{
+       unsigned nOffset = src_offset;
+       char* pDestination = dst;
+       while (size--)
+               *pDestination++ = buffer[nOffset++];
+       return nOffset;
+}
+
+int CheckBufferSize (unsigned int nSize)
+{
+       if (nSize < EC_BUFFER_SIZE_MIN) {
+               EPRINTF("Too small buffer size! [Size=%u KB]", nSize / 1024);
+               return -1;
+       }
+       if (nSize > EC_BUFFER_SIZE_MAX) {
+               EPRINTF("Too big buffer size! [Size=%u KB]", nSize / 1024);
+               return -1;
+       }
+       return 0;
+}
+
+int AllocateSingleBuffer(unsigned int nSize)
+{
+       unsigned long spinlock_flags = 0L;
+
+       unsigned int nSubbufferSize = ec_info.m_nSubbufSize;
+       unsigned int nNumOfSubbufers = GetNumOfSubbuffers(nSize);
+       unsigned long nAllocatedSize = nSubbufferSize * nNumOfSubbufers;
+
+       p_buffer = vmalloc_user(nAllocatedSize);
+       if(!p_buffer) {
+               EPRINTF("Memory allocation error! [Size=%lu KB]", nAllocatedSize / 1024);
+               return -1;
+       }
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.m_nNumOfSubbuffers = nNumOfSubbufers;
+       ec_info.buffer_effect = ec_info.buffer_size = nAllocatedSize;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+       return 0;
+}
+
+void FreeSingleBuffer (void)
+{
+       VFREE_USER(p_buffer, ec_info.buffer_size);
+       CleanECInfo();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+int EnableContinuousRetrieval() {
+       unsigned long spinlock_flags = 0L;
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.m_nMode |= MODEMASK_CONTINUOUS_RETRIEVAL;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+       return 0;
+}
+
+int DisableContinuousRetrieval() {
+       unsigned long spinlock_flags = 0L;
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.m_nMode &= ~MODEMASK_CONTINUOUS_RETRIEVAL;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+       return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef __DISABLE_RELAYFS
+
+struct rchan* gl_pRelayChannel = NULL;
+struct rchan* GetRelayChannel(void) { return gl_pRelayChannel; };
+
+struct dentry* gl_pdirRelay = NULL;
+struct dentry* GetRelayDir(void) { return gl_pdirRelay; };
+
+#ifdef __USE_PROCFS
+
+struct proc_dir_entry* alt_pde = NULL;
+
+static inline struct dentry *_dir_create (const char *dirname, struct dentry *parent, struct proc_dir_entry **p2pde)
+{
+    struct dentry *dir;
+    struct proc_dir_entry *pde;
+
+    pde = proc_mkdir (dirname, PDE (parent->d_inode));
+    if (pde == NULL)
+    {
+        dir = NULL;
+    }
+    else
+    {
+        mutex_lock (&parent->d_inode->i_mutex);
+        dir = lookup_one_len (dirname, parent, strlen (dirname));
+        mutex_unlock (&parent->d_inode->i_mutex);
+
+        if (IS_ERR (dir))
+        {
+            dir = NULL;
+            remove_proc_entry (dirname, PDE (parent->d_inode));
+        }
+
+        *p2pde = pde;
+    }
+
+    return dir;
+}
+
+static inline struct dentry *_get_proc_root (void)
+{
+    struct file_system_type *procfs_type;
+    struct super_block *procfs_sb;
+
+    procfs_type = get_fs_type ("proc");
+
+    if (!procfs_type || list_empty (&procfs_type->fs_supers))
+        return NULL;
+
+    procfs_sb = list_entry (procfs_type->fs_supers.next, \
+        struct super_block, s_instances);
+
+    return procfs_sb->s_root;
+
+}
+
+static struct dentry *create_buf (const char *filename, struct dentry *parent, int mode, struct rchan_buf *buf, int *is_global)
+{
+    struct proc_dir_entry *pde;
+    struct proc_dir_entry *parent_pde = NULL;
+    struct dentry *dentry;
+
+    if (parent)
+        parent_pde = PDE (parent->d_inode);
+    else
+        parent = _get_proc_root ();
+
+    pde = create_proc_entry (filename, S_IFREG|S_IRUSR, parent_pde);
+
+    if(unlikely(!pde))
+        return NULL;
+
+    pde->proc_fops = &relay_file_operations;
+
+    mutex_lock (&parent->d_inode->i_mutex);
+    dentry = lookup_one_len (filename, parent, strlen (filename));
+    mutex_unlock (&parent->d_inode->i_mutex);
+
+    if (IS_ERR(dentry)) {
+        remove_proc_entry (filename, parent_pde);
+       }
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18))
+       dentry->d_inode->i_private = buf;
+#else
+       dentry->d_inode->u.generic_ip = buf;
+#endif
+
+    return dentry;
+
+}
+
+static int remove_buf (struct dentry *dentry)
+{
+    if (dentry != NULL)
+    {
+        struct proc_dir_entry *pde = PDE (dentry->d_inode);
+        dput (dentry);
+        remove_proc_entry (pde->name, pde->parent);
+    }
+    
+    return 0;
+}
+
+#endif // __USE_PROCFS
+       /*
+          * subbuf_start - called on buffer-switch to a new sub-buffer
+          * @buf: the channel buffer containing the new sub-buffer
+          * @subbuf: the start of the new sub-buffer
+          * @prev_subbuf: the start of the previous sub-buffer
+          * @prev_padding: unused space at the end of previous sub-buffer
+          *
+          * The client should return 1 to continue logging, 0 to stop
+          * logging.
+          *
+          * NOTE: subbuf_start will also be invoked when the buffer is
+          *       created, so that the first sub-buffer can be initialized
+          *       if necessary.  In this case, prev_subbuf will be NULL.
+          *
+          * NOTE: the client can reserve bytes at the beginning of the new
+          *       sub-buffer by calling subbuf_start_reserve() in this callback.
+          */
+int RelayCallbackSubbufStart(struct rchan_buf *buf,
+                              void *subbuf,
+                              void *prev_subbuf,
+                              size_t prev_padding)
+{
+       struct rchan* pRelayChannel = NULL;
+       unsigned int nNumOfSubbufs = 0;
+
+       unsigned long spinlock_flags = 0L;
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+
+       subbuf_start_reserve(buf, RELAY_SUBBUF_HEADER_SIZE);
+       ec_info.buffer_effect += RELAY_SUBBUF_HEADER_SIZE;
+       ec_info.m_nEndOffset = RELAY_SUBBUF_HEADER_SIZE;
+
+       if(prev_subbuf == NULL) {
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+               return 1;
+       }
+       memcpy(prev_subbuf, &prev_padding, sizeof(unsigned int));
+       memcpy(prev_subbuf + sizeof(unsigned int), &ec_info.m_nSubbufSavedEvents, sizeof(unsigned int));
+       ec_info.m_nSubbufSavedEvents = 0;
+       pRelayChannel = GetRelayChannel();
+       if(pRelayChannel == NULL) {
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+               EPRINTF("Null pointer to relay channel!");
+               return 0;
+       }
+       nNumOfSubbufs = pRelayChannel->n_subbufs;
+       ec_info.m_nBeginSubbufNum = buf->subbufs_consumed % nNumOfSubbufs;
+       ec_info.m_nEndSubbufNum = buf->subbufs_produced % nNumOfSubbufs;
+       if(relay_buf_full(buf)) {
+               void* pConsume = NULL;
+               unsigned int nPaddingLength = 0;
+               unsigned int nSubbufSize = 0;
+               unsigned int nDataSize = 0;
+               unsigned int nEffectSize = 0;
+               unsigned int nSubbufDiscardedCount = 0;
+               nSubbufSize = pRelayChannel->subbuf_size;
+               pConsume = buf->start + buf->subbufs_consumed % nNumOfSubbufs * nSubbufSize;
+               memcpy(&nPaddingLength, pConsume, sizeof(unsigned int));
+               memcpy(&nSubbufDiscardedCount, pConsume + sizeof(unsigned int), sizeof(unsigned int));
+               nEffectSize = nSubbufSize - nPaddingLength;
+               nDataSize = nEffectSize - RELAY_SUBBUF_HEADER_SIZE;
+               ec_info.discarded_events_count += nSubbufDiscardedCount;
+               relay_subbufs_consumed(pRelayChannel, 0, 1);
+               ec_info.m_nBeginSubbufNum = buf->subbufs_consumed % nNumOfSubbufs;
+               ec_info.m_nEndSubbufNum = buf->subbufs_produced % nNumOfSubbufs;
+               ec_info.buffer_effect -= nEffectSize;
+               ec_info.trace_size -= nDataSize;
+               buf->dentry->d_inode->i_size = ec_info.trace_size;
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+               return 1; // Overwrite mode
+       }
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+       return 1;
+}
+       /*
+          * buf_mapped - relay buffer mmap notification
+          * @buf: the channel buffer
+          * @filp: relay file pointer
+          *
+          * Called when a relay file is successfully mmapped
+          */
+void RelayCallbackBufMapped(struct rchan_buf *buf,
+                            struct file *filp)
+{
+}
+
+       /*
+          * buf_unmapped - relay buffer unmap notification
+          * @buf: the channel buffer
+          * @filp: relay file pointer
+          *
+          * Called when a relay file is successfully unmapped
+          */
+void RelayCallbackBufUnmapped(struct rchan_buf *buf,
+                             struct file *filp)
+{
+}
+       /*
+          * create_buf_file - create file to represent a relay channel buffer
+          * @filename: the name of the file to create
+          * @parent: the parent of the file to create
+          * @mode: the mode of the file to create
+          * @buf: the channel buffer
+          * @is_global: outparam - set non-zero if the buffer should be global
+          *
+          * Called during relay_open(), once for each per-cpu buffer,
+          * to allow the client to create a file to be used to
+          * represent the corresponding channel buffer.  If the file is
+          * created outside of relay, the parent must also exist in
+          * that filesystem.
+          *
+          * The callback should return the dentry of the file created
+          * to represent the relay buffer.
+          *
+          * Setting the is_global outparam to a non-zero value will
+          * cause relay_open() to create a single global buffer rather
+          * than the default set of per-cpu buffers.
+          *
+          * See Documentation/filesystems/relayfs.txt for more info.
+          */
+struct dentry * RelayCallbackCreateBufFile(const char *filename,
+                                           struct dentry *parent,
+                                           int mode,
+                                           struct rchan_buf *buf,
+                                           int *is_global)
+{
+       *is_global = 1;
+#ifdef __USE_PROCFS
+       DPRINTF("\"%s\" is creating in procfs...!", filename);
+       return create_buf(filename, parent, mode, buf, is_global);
+#else
+       DPRINTF("\"%s\" is creating in debugfs...!", filename);
+       return debugfs_create_file(filename, (mode_t)mode, parent, buf, &relay_file_operations);
+#endif // __USE_PROCFS
+}
+       /*
+          * remove_buf_file - remove file representing a relay channel buffer
+          * @dentry: the dentry of the file to remove
+          *
+          * Called during relay_close(), once for each per-cpu buffer,
+          * to allow the client to remove a file used to represent a
+          * channel buffer.
+          *
+          * The callback should return 0 if successful, negative if not.
+          */
+int RelayCallbackRemoveBufFile(struct dentry *dentry)
+{
+#ifdef __USE_PROCFS
+       remove_buf(dentry);
+#else
+       debugfs_remove(dentry);
+#endif // __USE_PROCFS
+       return 0;
+}
+
+struct rchan_callbacks gl_RelayCallbacks = {
+       .subbuf_start = RelayCallbackSubbufStart,
+       .buf_mapped = RelayCallbackBufMapped,
+       .buf_unmapped = RelayCallbackBufUnmapped,
+       .create_buf_file = RelayCallbackCreateBufFile,
+       .remove_buf_file = RelayCallbackRemoveBufFile
+};
+#endif //__DISABLE_RELAYFS
+
+int AllocateMultipleBuffer(unsigned int nSize) {
+#ifndef __DISABLE_RELAYFS
+       unsigned long spinlock_flags = 0L;
+
+       unsigned int nSubbufferSize = ec_info.m_nSubbufSize;
+       unsigned int nNumOfSubbufers = GetNumOfSubbuffers(nSize);
+
+       gl_pRelayChannel = relay_open(DEFAULT_RELAY_BASE_FILENAME,
+                                       GetRelayDir(),
+                                       nSubbufferSize,
+                                       nNumOfSubbufers,
+                                       &gl_RelayCallbacks
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18))
+                                       ,NULL
+#endif
+                                       );
+       if(gl_pRelayChannel == NULL) {
+               EPRINTF("Cannot create relay buffer channel! [%d subbufers by %u Kb = %u Kb]",
+                       nNumOfSubbufers, nSubbufferSize / 1024, nSize / 1024);
+               return -1;
+       }
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.m_nNumOfSubbuffers = nNumOfSubbufers;
+       ec_info.buffer_effect = ec_info.buffer_size = nSubbufferSize * nNumOfSubbufers;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+       return 0;
+#else
+       EPRINTF("RelayFS not supported!");
+       return -1;
+#endif //__DISABLE_RELAYFS
+}
+
+void FreeMultipleBuffer(void) {
+#ifndef __DISABLE_RELAYFS
+       relay_close(gl_pRelayChannel);
+       CleanECInfo();
+#else
+       EPRINTF("RelayFS not supported!");
+#endif //__DISABLE_RELAYFS
+}
+
+int InitializeBuffer(unsigned int nSize) {
+       if(IsMultipleBuffer())
+               return AllocateMultipleBuffer(nSize);
+       return AllocateSingleBuffer(nSize);
+}
+
+int UninitializeBuffer(void) {
+       if(IsMultipleBuffer())
+               FreeMultipleBuffer();
+       FreeSingleBuffer();
+       return 0;
+}
+
+int EnableMultipleBuffer() {
+       unsigned long spinlock_flags = 0L;
+
+       if(IsMultipleBuffer())
+               return 0;
+
+       if(UninitializeBuffer() == -1)
+               EPRINTF("Cannot uninitialize buffer!");
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.m_nMode |= MODEMASK_MULTIPLE_BUFFER;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+       if(InitializeBuffer(GetBufferSize()) == -1) {
+               EPRINTF("Cannot initialize buffer!");
+               return -1;
+       }
+       return 0;
+}
+
+int DisableMultipleBuffer() {
+       unsigned long spinlock_flags = 0L;
+
+       if(!IsMultipleBuffer())
+               return 0;
+
+       if(UninitializeBuffer() == -1)
+               EPRINTF("Cannot uninitialize buffer!");
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.m_nMode &= ~MODEMASK_MULTIPLE_BUFFER;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+       if(InitializeBuffer(GetBufferSize()) == -1) {
+               EPRINTF("Cannot initialize buffer!");
+               return -1;
+       }
+       return 0;
+}
+
+unsigned int GetBufferSize(void) { return ec_info.buffer_size; };
+
+int SetBufferSize(unsigned int nSize) {
+       if (GetECState() != EC_STATE_IDLE) {
+               EPRINTF("Buffer changes are allowed in IDLE state only (%d)!", GetECState());
+               return -1;
+       }
+       if(GetBufferSize() == nSize)
+               return 0;
+       if(CheckBufferSize(nSize) == -1) {
+               EPRINTF("Invalid buffer size!");
+               return -1;
+       }
+       detach_selected_probes ();
+       if(UninitializeBuffer() == -1)
+               EPRINTF("Cannot uninitialize buffer!");
+       if(InitializeBuffer(nSize) == -1) {
+               EPRINTF("Cannot initialize buffer! [Size=%u KB]", nSize / 1024);
+               return -1;
+       }
+       return 0;
+}
+
+void ResetSingleBuffer(void) {
+}
+
+void ResetMultipleBuffer(void) {
+#ifndef __DISABLE_RELAYFS
+       relay_reset(gl_pRelayChannel);
+#else
+       EPRINTF("RelayFS not supported!");
+#endif //__DISABLE_RELAYFS
+}
+
+int ResetBuffer(void) {
+       unsigned long spinlock_flags = 0L;
+
+       if (GetECState() != EC_STATE_IDLE) {
+               EPRINTF("Buffer changes are allowed in IDLE state only!");
+               return -1;
+       }
+
+       if(IsMultipleBuffer())
+               ResetMultipleBuffer();
+       else
+               ResetSingleBuffer();
+
+       detach_selected_probes ();
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.buffer_effect = ec_info.buffer_size;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+       ResetECInfo();
+
+       return 0;
+}
+
+int WriteEventIntoSingleBuffer(char* pEvent, unsigned long nEventSize) {
+       unsigned long spinlock_flags = 0L;
+       int bCopied = 0;
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       if(!p_buffer) {
+               EPRINTF("Invalid pointer to buffer!");
+               ++ec_info.lost_events_count;
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+               return -1;
+       }
+       while (!bCopied) {
+               unsigned unused_space;
+               if(ec_info.saved_events_count == 0 || ec_info.after_last > ec_info.first) {
+                       unused_space = ec_info.buffer_size - ec_info.after_last;
+                       if(unused_space > nEventSize) {
+                               ec_info.after_last = copy_into_cyclic_buffer (p_buffer, ec_info.after_last, pEvent, nEventSize);
+                               ++ec_info.saved_events_count;
+                               bCopied = 1;
+                               ec_info.buffer_effect = ec_info.buffer_size;
+                               ec_info.trace_size = ec_info.after_last - ec_info.first;
+                       } else {
+                               bCopied = 0;
+                               ec_info.buffer_effect = ec_info.after_last;
+                               ec_info.after_last = 0;
+                       }
+               } else {
+                       unused_space = ec_info.first - ec_info.after_last;
+                       if(unused_space > nEventSize) {
+                               ec_info.after_last = copy_into_cyclic_buffer (p_buffer, ec_info.after_last, pEvent, nEventSize);
+                               ++ec_info.saved_events_count;
+                               bCopied = 1;
+                               ec_info.trace_size = ec_info.buffer_effect - ec_info.first + ec_info.after_last;
+                       } else {
+                               if(ec_info.first < ec_info.buffer_effect) {
+                                       TYPEOF_EVENT_LENGTH discard_len = 0;
+                                       copy_from_cyclic_buffer ((char *) &discard_len, p_buffer, ec_info.first, sizeof (TYPEOF_EVENT_LENGTH));
+                                       ec_info.first = ec_info.first + discard_len;
+                                       ++ec_info.discarded_events_count;
+                                       bCopied = 0;
+                                       ec_info.trace_size = ec_info.buffer_effect - ec_info.first + ec_info.after_last;
+                               } else {
+                                       bCopied = 0;
+                                       ec_info.buffer_effect = ec_info.buffer_size;
+                                       ec_info.first = 0;
+                               }
+                       }
+               }
+       }
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+       return 0;
+}
+
+int WriteEventIntoMultipleBuffer(char* pEvent, unsigned long nEventSize) {
+#ifndef __DISABLE_RELAYFS
+       unsigned long spinlock_flags = 0L;
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       __relay_write(GetRelayChannel(), pEvent, nEventSize);
+       ec_info.buffer_effect += nEventSize;
+       ec_info.trace_size += nEventSize;
+       ec_info.saved_events_count++;
+       ec_info.m_nEndOffset += nEventSize;
+       ec_info.m_nSubbufSavedEvents++;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+       return 0;
+#else
+       EPRINTF("RelayFS not supported!");
+       return -1;
+#endif //__DISABLE_RELAYFS
+}
+
+int WriteEventIntoBuffer(char* pEvent, unsigned long nEventSize) {
+
+       /*unsigned long i;
+       for(i = 0; i < nEventSize; i++)
+               printk("%02X ", pEvent[i]);
+       printk("\n");*/
+
+       if(IsMultipleBuffer())
+               return WriteEventIntoMultipleBuffer(pEvent, nEventSize);
+       return WriteEventIntoSingleBuffer(pEvent, nEventSize);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+int set_event_mask (int new_mask)
+{
+       unsigned long spinlock_flags = 0L;
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       event_mask = new_mask;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+       return 0;
+}
+
+int
+get_event_mask (int *mask)
+{
+       *mask = event_mask;
+       return 0;
+}
+
+static void
+generic_swap (void *a, void *b, int size)
+{
+       char t;
+       do {
+               t = *(char *) a;
+               *(char *) a++ = *(char *) b;
+               *(char *) b++ = t;
+       } while (--size > 0);
+}
+
+static void sort (void *base, size_t num, size_t size, int (*cmp) (const void *, const void *), void (*fswap) (void *, void *, int size))
+{
+       /* pre-scale counters for performance */
+       int i = (num / 2) * size, n = num * size, c, r;
+
+       /* heapify */
+       for (; i >= 0; i -= size)
+       {
+               for (r = i; r * 2 < n; r = c)
+               {
+                       c = r * 2;
+                       if (c < n - size && cmp (base + c, base + c + size) < 0)
+                               c += size;
+                       if (cmp (base + r, base + c) >= 0)
+                               break;
+                       fswap (base + r, base + c, size);
+               }
+       }
+
+       /* sort */
+       for (i = n - size; i >= 0; i -= size)
+       {
+               fswap (base, base + i, size);
+               for (r = 0; r * 2 < i; r = c)
+               {
+                       c = r * 2;
+                       if (c < i - size && cmp (base + c, base + c + size) < 0)
+                               c += size;
+                       if (cmp (base + r, base + c) >= 0)
+                               break;
+                       fswap (base + r, base + c, size);
+               }
+       }
+}
+
+static int addr_cmp (const void *a, const void *b)
+{
+       return *(unsigned long *) a > *(unsigned long *) b ? -1 : 1;
+}
+
+int set_us_proc_inst_info (ioctl_inst_usr_space_proc_t * inst_info)
+{
+       int i, k, j, l, is_app = 0;
+       long len;
+       us_proc_lib_t *d_lib, *pd_lib;
+       ioctl_usr_space_lib_t s_lib;
+       us_proc_ip_t *d_ip;
+       ioctl_usr_space_ip_t s_ip;
+       us_proc_vtp_t *mvtp;
+       ioctl_usr_space_vtp_t s_vtp;
+       struct nameidata nd;
+       char *ptr;
+
+       inst_us_proc_t *my_uprobes_info = (inst_us_proc_t *)lookup_name("my_uprobes_info");
+       inst_us_proc_t empty_uprobes_info =
+       {
+               .libs_count = 0,
+               .p_libs = NULL,
+       };
+       if (my_uprobes_info == 0)
+               my_uprobes_info = &empty_uprobes_info;
+
+       if (us_proc_info.path)
+               release_us_proc_inst_info ();
+
+       memset ((void *) &us_proc_info, 0, sizeof (us_proc_info));
+       len = strlen_user (inst_info->path);
+       us_proc_info.path = kmalloc (len, GFP_KERNEL);
+       DPRINTF ("us_proc_info.path=%p", us_proc_info.path);
+       if (!us_proc_info.path)
+               return -ENOMEM;
+       if (strncpy_from_user (us_proc_info.path, inst_info->path, len) != (len-1))
+       {
+               EPRINTF ("strncpy_from_user app path failed %p (%ld)", us_proc_info.path, len);
+               return -EFAULT;
+       }
+
+       if (path_lookup (us_proc_info.path, LOOKUP_FOLLOW, &nd) != 0)
+       {
+               EPRINTF ("failed to lookup dentry for path %s!", us_proc_info.path);
+               return -EFAULT;
+       }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+       us_proc_info.m_f_dentry = nd.dentry;
+       path_release (&nd);
+#else
+       us_proc_info.m_f_dentry = nd.path.dentry;
+       path_put (&nd.path);
+#endif
+
+       us_proc_info.libs_count = inst_info->libs_count;
+       us_proc_info.p_libs = kmalloc (us_proc_info.libs_count * sizeof (us_proc_lib_t), GFP_KERNEL);
+       DPRINTF ("us_proc_info.p_libs=%p/%u", us_proc_info.p_libs, us_proc_info.libs_count);
+       if (!us_proc_info.p_libs)
+               return -ENOMEM;
+       memset (us_proc_info.p_libs, 0, us_proc_info.libs_count * sizeof (us_proc_lib_t));
+       for (i = 0; i < us_proc_info.libs_count; i++)
+       {
+               d_lib = &us_proc_info.p_libs[i];
+               if (copy_from_user ((void *) &s_lib, &inst_info->p_libs[i], sizeof (ioctl_usr_space_lib_t)))
+               {
+                       EPRINTF ("copy_from_user lib failed %p", &inst_info->p_libs[i]);
+                       return -EFAULT;
+               }
+
+               len = strlen_user (s_lib.path);
+               d_lib->path = kmalloc(len, GFP_KERNEL);
+               DPRINTF ("d_lib[%i]->path=%p", i, d_lib->path);
+               if (!d_lib->path)
+               {
+                       return -ENOMEM;
+               }
+               if (strncpy_from_user (d_lib->path, s_lib.path, len) != (len-1))
+               {
+                       EPRINTF ("strncpy_from_user lib path failed %p (%ld)", d_lib->path, len);
+                       return -EFAULT;
+               }
+               if (path_lookup (d_lib->path, LOOKUP_FOLLOW, &nd) != 0)
+               {
+                       EPRINTF ("failed to lookup dentry for path %s!", d_lib->path);
+                       continue;
+               }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+               d_lib->m_f_dentry = nd.dentry;
+               path_release (&nd);
+#else
+               d_lib->m_f_dentry = nd.path.dentry;
+               path_put (&nd.path);
+#endif
+               
+               if(strcmp(inst_info->path, d_lib->path) == 0)
+                       is_app = 1;
+               else
+                       is_app = 0;
+
+               pd_lib = NULL;
+               ptr = strrchr(d_lib->path, '/');
+               if(ptr) ptr++;
+               else    ptr = d_lib->path;
+               for(l = 0; l < my_uprobes_info->libs_count; l++){
+                       //DPRINTF("LIB %s/%s", ptr, my_uprobes_info->p_libs[l].path);
+                       if((strcmp(ptr, my_uprobes_info->p_libs[l].path) == 0) ||
+                          (is_app && *(my_uprobes_info->p_libs[l].path) == '\0')){
+                               pd_lib = &my_uprobes_info->p_libs[l];
+                               //DPRINTF("predef lib %s", pd_lib->path);
+                               break;
+                       }
+               }
+               
+               if (s_lib.ips_count > 0)
+               {
+                       us_proc_info.unres_ips_count += s_lib.ips_count;
+                       d_lib->ips_count = s_lib.ips_count;
+                       d_lib->p_ips = vmalloc (d_lib->ips_count * sizeof (us_proc_ip_t));
+                       DPRINTF ("d_lib[%i]->p_ips=%p/%u [%s]", i, d_lib->p_ips, us_proc_info.unres_ips_count, d_lib->path);
+                       if (!d_lib->p_ips)
+                       {
+                               return -ENOMEM;
+                       }
+                       memset (d_lib->p_ips, 0, d_lib->ips_count * sizeof (us_proc_ip_t));
+                       for (k = 0; k < d_lib->ips_count; k++)
+                       {
+                               d_ip = &d_lib->p_ips[k];
+                               if (copy_from_user ((void *) &s_ip, &s_lib.p_ips[k], sizeof (ioctl_usr_space_ip_t)))
+                               {
+                                       EPRINTF ("copy_from_user ip failed %p", &s_lib.p_ips[k]);
+                                       return -EFAULT;
+                               }
+                               d_ip->offset = s_ip.addr;
+
+                                /* TNCHK */
+                                /*d_ip->jprobe.pre_entry               = ujprobe_event_pre_handler;
+                                d_ip->jprobe.entry             = ujprobe_event_handler;
+                                d_ip->retprobe.handler         = uretprobe_event_handler;*/
+                               /* TNCHK */
+                                
+                               if(pd_lib){
+                                       for(l = 0; l < pd_lib->ips_count; l++){
+                                               if(d_ip->offset == pd_lib->p_ips[l].offset){
+                                                       DPRINTF("predefined probe %s in %s at %lx", pd_lib->p_ips[l].name, pd_lib->path, pd_lib->p_ips[l].offset);
+                                                       d_ip->jprobe.pre_entry          = pd_lib->p_ips[l].jprobe.pre_entry;
+                                                       d_ip->jprobe.entry              = pd_lib->p_ips[l].jprobe.entry;
+                                                       d_ip->retprobe.handler  = pd_lib->p_ips[l].retprobe.handler;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+               if (s_lib.vtps_count > 0)
+               {
+                       unsigned long ucount = 1, pre_addr;
+                       // array containing elements like (addr, index)
+                       unsigned long *addrs = kmalloc (s_lib.vtps_count * 2 * sizeof (unsigned long), GFP_KERNEL);
+//                     DPRINTF ("addrs=%p/%u", addrs, s_lib.vtps_count);
+                       if (!addrs)
+                       {
+                               //note: storage will released next time or at clean-up moment
+                               return -ENOMEM;
+                       }
+                       memset (addrs, 0, s_lib.vtps_count * 2 * sizeof (unsigned long));
+                       // fill the array in
+                       for (k = 0; k < s_lib.vtps_count; k++)
+                       {
+                               if (copy_from_user ((void *) &s_vtp, &s_lib.p_vtps[k], sizeof (ioctl_usr_space_vtp_t)))
+                               {
+                                       //note: storage will released next time or at clean-up moment
+                                       EPRINTF ("copy_from_user VTP failed %p", &s_lib.p_vtps[k]);
+                                       kfree (addrs);
+                                       return -EFAULT;
+                               }
+                               addrs[2 * k] = s_vtp.addr;
+                               addrs[2 * k + 1] = k;
+                       }
+                       // sort by VTP addresses, i.e. make VTPs with the same addresses adjacent;
+                       // organize them into bundles
+                       sort (addrs, s_lib.vtps_count, 2 * sizeof (unsigned long), addr_cmp, generic_swap);
+
+                       // calc number of VTPs with unique addresses 
+                       for (k = 1, pre_addr = addrs[0]; k < s_lib.vtps_count; k++)
+                       {
+                               if (addrs[2 * k] != pre_addr)
+                                       ucount++;       // count different only
+                               pre_addr = addrs[2 * k];
+                       }
+                       us_proc_info.unres_vtps_count += ucount;
+                       d_lib->vtps_count = ucount;
+                       d_lib->p_vtps = kmalloc (ucount * sizeof (us_proc_vtp_t), GFP_KERNEL);
+                       DPRINTF ("d_lib[%i]->p_vtps=%p/%lu", i, d_lib->p_vtps, ucount); //, d_lib->path);
+                       if (!d_lib->p_vtps)
+                       {
+                               //note: storage will released next time or at clean-up moment
+                               kfree (addrs);
+                               return -ENOMEM;
+                       }
+                       memset (d_lib->p_vtps, 0, d_lib->vtps_count * sizeof (us_proc_vtp_t));
+                       // go through sorted VTPS.
+                       for (k = 0, j = 0, pre_addr = 0, mvtp = NULL; k < s_lib.vtps_count; k++)
+                       {
+                               us_proc_vtp_data_t *vtp_data;
+                               // copy VTP data
+                               if (copy_from_user ((void *) &s_vtp, &s_lib.p_vtps[addrs[2 * k + 1]], sizeof (ioctl_usr_space_vtp_t)))
+                               {
+                                       //note: storage will released next time or at clean-up moment
+                                       EPRINTF ("copy_from_user VTP failed %p", &s_lib.p_vtps[addrs[2 * k + 1]]);
+                                       kfree (addrs);
+                                       return -EFAULT;
+                               }
+                               // if this is the first VTP in bundle (master VTP)
+                               if (addrs[2 * k] != pre_addr)
+                               {
+                                       // data are in the array of master VTPs
+                                       mvtp = &d_lib->p_vtps[j++];
+                                       mvtp->addr = s_vtp.addr;
+                                       INIT_LIST_HEAD (&mvtp->list);
+                               }
+                               // data are in the list of slave VTPs
+                               vtp_data = kmalloc (sizeof (us_proc_vtp_data_t), GFP_KERNEL);
+                               if (!vtp_data)
+                               {
+                                       //note: storage will released next time or at clean-up moment
+                                       kfree (addrs);
+                                       return -ENOMEM;
+                               }
+
+                               /*len = strlen_user (s_vtp.name);
+                               vtp_data->name = kmalloc (len, GFP_KERNEL);
+                               if (!vtp_data->name)
+                               {
+                                       //note: storage will released next time or at clean-up moment
+                                       kfree (vtp_data);
+                                       kfree (addrs);
+                                       return -ENOMEM;
+                               }
+                               if (strncpy_from_user (vtp_data->name, s_vtp.name, len) != (len-1))
+                               {
+                                       //note: storage will released next time or at clean-up moment
+                                       EPRINTF ("strncpy_from_user VTP name failed %p (%ld)", vtp_data->name, len);
+                                       kfree (vtp_data->name);
+                                       kfree (vtp_data);
+                                       kfree (addrs);
+                                       return -EFAULT;
+                               }
+                               //vtp_data->name[len] = 0;*/
+                               vtp_data->type = s_vtp.type;
+                               vtp_data->size = s_vtp.size;
+                               vtp_data->reg = s_vtp.reg;
+                               vtp_data->off = s_vtp.off;
+                               list_add_tail_rcu (&vtp_data->list, &mvtp->list);
+                               pre_addr = addrs[2 * k];
+                       }
+                       kfree (addrs);
+               }
+       }
+
+       //us_proc_info.mapped_codelets = (kprobe_opcode_t *) - 1;
+
+       return 0;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)
+#define list_for_each_rcu(pos, head) __list_for_each_rcu(pos, head)
+#endif
+
+// release storage of user space process instrumentation info
+void release_us_proc_inst_info (void)
+{
+       int i, k;
+       us_proc_lib_t *d_lib;
+       char *path;
+       struct list_head *pos;  //, *tmp;
+
+       path = us_proc_info.path;
+       us_proc_info.path = 0;
+
+       // first make sure "d_lib" is not used any more and only
+       // then release storage
+       if (us_proc_info.p_libs)
+       {
+               int count1 = us_proc_info.libs_count;
+               us_proc_info.libs_count = 0;
+               for (i = 0; i < count1; i++)
+               {
+                       d_lib = &us_proc_info.p_libs[i];
+                       if (d_lib->p_ips)
+                       {
+                               // first make sure "d_lib->p_ips" is not used any more and only
+                               // then release storage
+                               //int count2 = d_lib->ips_count;
+                               d_lib->ips_count = 0;
+                               /*for (k = 0; k < count2; k++)
+                                       kfree ((void *) d_lib->p_ips[k].name);*/
+                               vfree ((void *) d_lib->p_ips);
+                       }
+                       if (d_lib->p_vtps)
+                       {
+                               // first make sure "d_lib->p_vtps" is not used any more and only
+                               // then release storage
+                               int count2 = d_lib->vtps_count;
+                               d_lib->vtps_count = 0;
+                               for (k = 0; k < count2; k++)
+                               {
+                                       //list_for_each_safe_rcu(pos, tmp, &d_lib->p_vtps[k].list) {
+                                       list_for_each_rcu (pos, &d_lib->p_vtps[k].list)
+                                       {
+                                               us_proc_vtp_data_t *vtp = list_entry (pos, us_proc_vtp_data_t, list);
+                                               list_del_rcu (pos);
+                                               //kfree (vtp->name);
+                                               kfree (vtp);
+                                       }
+                               }
+                               kfree ((void *) d_lib->p_vtps);
+                       }
+                       kfree ((void *) d_lib->path);
+                       //putname(d_lib->path);
+               }
+               kfree ((void *) us_proc_info.p_libs);
+               us_proc_info.p_libs = 0;
+       }
+       if (path)
+       {
+               kfree ((void *) path);
+               //putname(path);
+       }
+
+       us_proc_info.tgid = 0;
+}
+
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
+int storage_init (void)
+{
+       unsigned long spinlock_flags = 0L;
+
+       spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+       ec_info.m_nMode = 0; // MASK IS CLEAR (SINGLE NON_CONTINUOUS BUFFER)
+//     ec_info.m_nMode |= ECMODEMASK_MULTIPLE_BUFFER;
+       spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+#ifndef __DISABLE_RELAYFS
+
+#ifdef __USE_PROCFS
+       gl_pdirRelay = _dir_create (DEFAULT_RELAY_BASE_DIR, _get_proc_root(), &alt_pde);
+       if(gl_pdirRelay == NULL) {
+               EPRINTF("Cannot create procfs directory for relay buffer!");
+               return -1;
+       }
+#else
+       gl_pdirRelay = debugfs_create_dir(DEFAULT_RELAY_BASE_DIR, NULL);
+       if(gl_pdirRelay == NULL) {
+               EPRINTF("Cannot create directory for relay buffer!");
+               return -1;
+       }
+
+#endif // __USE_PROCFS
+
+#endif //__DISABLE_RELAYFS
+
+       if(InitializeBuffer(EC_BUFFER_SIZE_DEFAULT) == -1) {
+               EPRINTF("Cannot initialize buffer! [Size=%u KB]", EC_BUFFER_SIZE_DEFAULT / 1024 );
+               return -1;
+       }
+
+       INIT_HLIST_HEAD (&kernel_probes);
+
+#ifdef MEMORY_CHECKER
+       {
+       void **sys_call_table_ptr = (void **)lookup_name("sys_call_table");
+       if(!sys_call_table_ptr){
+               EPRINTF("Cannot find sys_call_table in kernel!");
+               return -1;
+       }
+       orig_sys_mprotect = sys_call_table_ptr[__NR_mprotect];
+
+       int i;
+       for(i = 0; i < M_SIZE; i++)
+       {
+           INIT_HLIST_HEAD(&malloc_table[i]);
+       }
+       }
+#endif
+
+       return 0;
+}
+
+/*
+    Shuts down "storage".
+    Assumes that all probes are already deactivated.
+*/
+void storage_down (void)
+{
+       if(UninitializeBuffer() == -1)
+               EPRINTF("Cannot uninitialize buffer!");
+
+#ifndef __DISABLE_RELAYFS
+
+#ifdef __USE_PROCFS
+//     remove_buf(gl_pdirRelay);
+#else
+       debugfs_remove(gl_pdirRelay);
+#endif // __USE_PROCFS
+
+#endif //__DISABLE_RELAYFS
+
+       release_us_proc_inst_info ();
+
+       if (ec_info.collision_count)
+               EPRINTF ("ec_info.collision_count=%d", ec_info.collision_count);
+       if (ec_info.lost_events_count)
+               EPRINTF ("ec_info.lost_events_count=%d", ec_info.lost_events_count);
+}
+
+static u_int32_t get_probe_func_addr(const char *fmt, va_list args)
+{
+       if (fmt[0] != 'p')
+               return 0;
+
+       return va_arg(args, u_int32_t);
+}
+
+/*
+    Note: Experiment shows that on x86 jprobe and retprobe are handled with
+    interrupts disabled. Thus collision is possible only due to hardware
+    exception happening inside our code or due to parallel execution on 2 or
+    more physical CPUs. Experiment shows that on x86 with single CPU collisions
+    do not happen.
+
+    mk: We have dual situation. SMP (non-UP) Spinlock will help against
+    cross-CPU collisions but will lock-up even single CPU in case of hardware
+    exception like pagefault.
+
+    Per-CPU counter will allow us to catch nesting for the same CPU.
+    "pack_event_info()" is a preemption-safe, thus we can use "__get_cpu_var()"
+    directly.
+
+    NOTE: real_per_cpu_in_use_count = per_cpu_in_use_count + 1
+*/
+static DEFINE_PER_CPU (local_t, per_cpu_in_use_count) = LOCAL_INIT (-1);
+
+void pack_event_info (probe_id_t probe_id, record_type_t record_type, const char *fmt, ...)
+{
+       unsigned long spinlock_flags = 0L;
+       static char buf[EVENT_MAX_SIZE] = "";
+       //char* probe_name = NULL;
+       TYPEOF_EVENT_LENGTH event_len = 0L;
+       TYPEOF_TIME tv = { 0, 0 };
+       TYPEOF_THREAD_ID current_pid = current->pid;
+       TYPEOF_PROCESS_ID current_tgid = current->tgid;
+       unsigned current_cpu = task_cpu(current);
+       va_list args;
+       unsigned long addr = 0;
+       struct cond *p_cond;
+       struct event_tmpl *p_tmpl;
+
+       memset(buf, 0, EVENT_MAX_SIZE);
+       do_gettimeofday (&tv);
+
+       if (probe_id == KS_PROBE_ID) {
+               va_start(args, fmt);
+               addr = get_probe_func_addr(fmt, args);
+               va_end(args);
+               if (!find_probe(addr))
+                       return;
+               if (((addr == pf_addr) && !(probes_flags & PROBE_FLAG_PF_INSTLD)) ||
+                       ((addr == exit_addr) && !(probes_flags & PROBE_FLAG_EXIT_INSTLD)))
+                       return;
+       }
+       if (probe_id == US_PROBE_ID) {
+               va_start(args, fmt);
+               addr = get_probe_func_addr(fmt, args);
+               va_end(args);
+       }
+
+       /* Checking for all the conditions
+        * except stop condition that we process after saving the event */
+       list_for_each_entry(p_cond, &cond_list.list, list) {
+               p_tmpl = &p_cond->tmpl;
+               switch (p_tmpl->type) {
+               case ET_TYPE_START_COND:
+                       if ((!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_ADDR) ||
+                                (addr == p_tmpl->addr)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_PID) ||
+                                (current_tgid == p_tmpl->pid)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_TID) ||
+                                (current_pid == p_tmpl->tid)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_CPU_NUM) ||
+                                (current_cpu == p_tmpl->cpu_num)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_BIN_NAME) ||
+                                (strcmp(current->comm, p_tmpl->bin_name) == 0)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_TIME) ||
+                                (tv.tv_sec > last_attach_time.tv_sec + p_tmpl->sec) ||
+                                (tv.tv_sec == last_attach_time.tv_sec + p_tmpl->sec &&
+                                 tv.tv_usec >= last_attach_time.tv_usec + p_tmpl->usec)) &&
+                               !p_cond->applied) {
+                               paused = 0;
+                               p_cond->applied = 1;
+                       }
+                       break;
+               case ET_TYPE_IGNORE_COND:
+                       /* if (probe_id == PROBE_SCHEDULE) */
+                       /*      break; */
+                       if ((!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_ADDR) ||
+                                (addr == p_tmpl->addr)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_PID) ||
+                                (current_tgid == p_tmpl->pid)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_TID) ||
+                                (current_pid == p_tmpl->tid)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_CPU_NUM) ||
+                                (current_cpu == p_tmpl->cpu_num)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_BIN_NAME) ||
+                                (strcmp(current->comm, p_tmpl->bin_name) == 0))) {
+                               ec_info.ignored_events_count++;
+                               return;
+                       }
+                       break;
+               }
+       }
+
+       if (paused && probe_id != EVENT_FMT_PROBE_ID) {
+               ec_info.ignored_events_count++;
+               return;
+       }
+
+       // We are in a preemption-safe context (because interrupts are disabled on
+       // local CPU), thus we can use "__get_cpu_var()" directly.
+       local_inc (&__get_cpu_var (per_cpu_in_use_count));
+       if (0 != local_read (&__get_cpu_var (per_cpu_in_use_count))) {
+               // nested entry for the same CPU means page fault or something
+               // similarly terrific in our code
+               local_dec (&__get_cpu_var (per_cpu_in_use_count));      // roll back
+               EPRINTF ("nested %s() for the same CPU", __FUNCTION__);
+
+               spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+               ++ec_info.collision_count;
+               ++ec_info.lost_events_count;
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+               return;         // bail out
+       }
+
+       /*
+          The same CPU can't get here second time thus we can safely use spinlock
+          to separate multiple CPUs accessing our data.
+        */
+       va_start (args, fmt);
+       event_len = VPackEvent(buf, sizeof(buf), event_mask, probe_id, record_type, &tv,
+                               current_tgid, current_pid, current_cpu, fmt, args);
+       va_end (args);
+
+       if(event_len == 0) {
+               EPRINTF ("ERROR: failed to pack event!");
+
+               spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+               ++ec_info.lost_events_count;
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+       } else if(WriteEventIntoBuffer(buf, event_len) == -1) {
+               EPRINTF("Cannot write event into buffer!");
+
+               spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+               ++ec_info.lost_events_count;
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+       }
+
+       local_dec (&__get_cpu_var (per_cpu_in_use_count));      // roll back
+
+       /* Check for stop condition.  We pause collecting the trace right after
+        * storing this event */
+       list_for_each_entry(p_cond, &cond_list.list, list) {
+               p_tmpl = &p_cond->tmpl;
+               switch (p_tmpl->type) {
+               case ET_TYPE_STOP_COND:
+                       if ((!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_ADDR) ||
+                                (addr == p_tmpl->addr)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_PID) ||
+                                (current_tgid == p_tmpl->pid)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_TID) ||
+                                (current_pid == p_tmpl->tid)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_CPU_NUM) ||
+                                (current_cpu == p_tmpl->cpu_num)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_BIN_NAME) ||
+                               (strcmp(current->comm, p_tmpl->bin_name) == 0)) &&
+                               (!ET_FIELD_ISSET(p_tmpl->flags, ET_MATCH_TIME) ||
+                                (tv.tv_sec > last_attach_time.tv_sec + p_tmpl->sec) ||
+                                (tv.tv_sec == last_attach_time.tv_sec + p_tmpl->sec &&
+                                 tv.tv_usec >= last_attach_time.tv_usec + p_tmpl->usec)) &&
+                               !p_cond->applied) {
+                               paused = 1;
+                               p_cond->applied = 1;
+                       }
+                       break;
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(pack_event_info);
+
+kernel_probe_t* find_probe (unsigned long addr)
+{
+       kernel_probe_t *p;
+       struct hlist_node *node;
+
+       //check if such probe does exist
+       hlist_for_each_entry_rcu (p, node, &kernel_probes, hlist)
+               if (p->addr == addr)
+                       break;
+
+       return node ? p : NULL;
+}
+
+int add_probe_to_list (unsigned long addr, kernel_probe_t ** pprobe)
+{
+       kernel_probe_t *new_probe;
+       unsigned long jp_handler_addr, rp_handler_addr, pre_handler_addr;
+       unsigned long (*find_jp_handler)(unsigned long) =
+                       (unsigned long (*)(unsigned long))lookup_name("find_jp_handler");
+       unsigned long (*find_rp_handler)(unsigned long) =
+                       (unsigned long (*)(unsigned long))lookup_name("find_rp_handler");
+       unsigned long (*find_pre_handler)(unsigned long) =
+                       (unsigned long (*)(unsigned long))lookup_name("find_pre_handler");
+
+       if (pprobe)
+               *pprobe = NULL;
+       //check if such probe does already exist
+       if (find_probe (addr)) {
+               /* It is not a problem if we have already registered
+                  this probe before */
+               return 0;
+       }
+
+       new_probe = kmalloc (sizeof (kernel_probe_t), GFP_KERNEL);
+       if (!new_probe)
+       {
+               EPRINTF ("no memory for new probe!");
+               return -ENOMEM;
+       }
+       memset (new_probe, 0, sizeof (kernel_probe_t));
+
+       new_probe->addr = addr;
+       new_probe->jprobe.kp.addr = new_probe->retprobe.kp.addr = (kprobe_opcode_t *)addr;
+       new_probe->jprobe.priv_arg = new_probe->retprobe.priv_arg = new_probe;
+
+       //new_probe->jprobe.pre_entry = (kprobe_pre_entry_handler_t) def_jprobe_event_pre_handler;
+       if (find_pre_handler == 0 ||
+               (pre_handler_addr = find_pre_handler(new_probe->addr)) == 0)
+               new_probe->jprobe.pre_entry = (kprobe_pre_entry_handler_t) def_jprobe_event_pre_handler;
+       else
+               new_probe->jprobe.pre_entry = (kprobe_pre_entry_handler_t)pre_handler_addr;
+
+       if (find_jp_handler == 0 ||
+               (jp_handler_addr = find_jp_handler(new_probe->addr)) == 0)
+               new_probe->jprobe.entry = (kprobe_opcode_t *) def_jprobe_event_handler;
+       else
+               new_probe->jprobe.entry = (kprobe_opcode_t *)jp_handler_addr;
+
+       if (find_rp_handler == 0 ||
+               (rp_handler_addr = find_rp_handler(new_probe->addr)) == 0)
+               new_probe->retprobe.handler =
+                       (kretprobe_handler_t) def_retprobe_event_handler;
+       else
+               new_probe->retprobe.handler = (kretprobe_handler_t)rp_handler_addr;
+
+       INIT_HLIST_NODE (&new_probe->hlist);
+       hlist_add_head_rcu (&new_probe->hlist, &kernel_probes);
+
+       if (pprobe)
+               *pprobe = new_probe;
+
+       return 0;
+}
+
+int remove_probe_from_list (unsigned long addr)
+{
+       kernel_probe_t *p;
+
+       //check if such probe does exist
+       p = find_probe (addr);
+       if (!p) {
+               /* We do not care about it. Nothing bad. */
+               return 0;
+       }
+
+       hlist_del_rcu (&p->hlist);
+
+       kfree (p);
+
+       return 0;
+}
+
+#ifdef MEMORY_CHECKER
+
+// active (unprotected) memory object
+static mec_object_t *curr_mec_obj;
+// protects 'curr_mec_obj'
+static spinlock_t mec_spinlock = SPIN_LOCK_UNLOCKED;
+
+void *mec_find_in_object_range(void *addr, int *rz)
+{
+       mec_object_t *obj = NULL;
+       struct hlist_node *node = NULL;
+       struct hlist_head *head = &malloc_table[0];//hash_ptr(addr, M_BITS)];
+       void *obj_zone_start, *obj_zone_end;
+       unsigned long spinlock_flags = 0;
+
+       *rz = 0;
+       //DPRINTF("mec_find_in_object_range %p", addr);
+       hlist_for_each_entry(obj, node, head, hlist)
+       {
+               // hit into another object
+               if((addr >= obj->address) && (addr < (obj->address + obj->size)))
+           {
+               //DPRINTF("mec_find_in_object_range found %lx, %u", obj->address, obj->size);
+                       return obj;
+           }
+               obj_zone_start = (void *)((unsigned long)(obj->address) & ~(PAGE_SIZE-1));
+               obj_zone_end = (char *)obj_zone_start + PAGE_ALIGN(obj->size);
+               // hit into red zone of another object
+               if(((addr >= obj_zone_start) && (addr < obj->address)) ||
+                  ((addr >= (obj->address+obj->size)) && (addr < obj_zone_end)))
+               {
+                       *rz = 1;
+                       //DPRINTF("mec_find_in_object_range %lx found in red zone of %lx, %u. [%p..%p]",
+                       //              addr, obj->address, obj->size, obj_zone_start, obj_zone_end);
+                       return obj;
+               }
+       }
+       spin_lock_irqsave (&mec_spinlock, spinlock_flags);
+       if(curr_mec_obj)
+       {
+               // hit into unallocated zone near active object but within max underflow/overflow zone
+               obj_zone_start = (void *)((unsigned long)(curr_mec_obj->address) & ~(PAGE_SIZE-1));
+               obj_zone_end = (char *)obj_zone_start + PAGE_ALIGN(curr_mec_obj->size);
+               spin_unlock_irqrestore (&mec_spinlock, spinlock_flags);
+               if((addr < obj_zone_start) && (addr >= (obj_zone_start - MEC_MAX_OVERFLOW_SIZE)))
+               {
+                       *rz = 1;
+                       //DPRINTF("mec_find_in_object_range %lx found in unalloc pre-zone of %lx, %u. [%p..%p]",
+                       //              addr, curr_mec_obj->address, curr_mec_obj->size, obj_zone_start, obj_zone_end);
+                       return curr_mec_obj;
+               }
+               if((addr >= obj_zone_end) && (addr < (obj_zone_end + MEC_MAX_OVERFLOW_SIZE)))
+               {
+                       *rz = 1;
+                       //DPRINTF("mec_find_in_object_range %lx found in unalloc post-zone of %lx, %u.  [%p..%p]",
+                       //              addr, curr_mec_obj->address, curr_mec_obj->size, obj_zone_start, obj_zone_end);
+                       return curr_mec_obj;
+               }
+       }
+       else
+               spin_unlock_irqrestore (&mec_spinlock, spinlock_flags);
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(mec_find_in_object_range);
+
+int mec_mprotect(unsigned long start, size_t len, unsigned long prot)
+{
+       //DPRINTF("mec_mprotect %lx, %u, %lx", start, len, prot);
+       return orig_sys_mprotect(start, len, prot);
+}
+
+void mec_change_active_object(void *obj)
+{
+       mec_object_t *new_obj = (mec_object_t *)obj;
+       unsigned long sz, addr, spinlock_flags = 0;
+
+       spin_lock_irqsave (&mec_spinlock, spinlock_flags);
+
+       if(curr_mec_obj != NULL)
+       {
+               // protect old object
+               //DPRINTF("mec_change_active_object protect old obj %lx, %u", curr_obj->address, curr_obj->size);
+               addr = (unsigned long)(curr_mec_obj->address) & ~(PAGE_SIZE-1);
+               sz = PAGE_ALIGN((unsigned long)(curr_mec_obj->address) + curr_mec_obj->size) - addr;
+               mec_mprotect(addr, sz, PROT_NONE);
+       }
+
+       // remove protection from new object
+       //DPRINTF("mec_change_active_object unprotect new obj %lx, %u", new_obj->address, new_obj->size);
+       addr = (unsigned long)(new_obj->address) & ~(PAGE_SIZE-1);
+       sz = PAGE_ALIGN((unsigned long)(new_obj->address) + new_obj->size) - addr;
+       mec_mprotect(addr, sz, PROT_READ|PROT_WRITE);
+
+       curr_mec_obj = new_obj;
+
+       spin_unlock_irqrestore (&mec_spinlock, spinlock_flags);
+}
+EXPORT_SYMBOL_GPL(mec_change_active_object);
+
+void *mec_find_object(void *addr)
+{
+       mec_object_t *obj = NULL;
+       struct hlist_node *node = NULL;
+       struct hlist_head *head = &malloc_table[0];//hash_ptr(addr, M_BITS)];
+
+       hlist_for_each_entry(obj, node, head, hlist)
+       {
+           if(obj->address == addr)
+                       return obj;
+       }
+
+       return NULL;
+}
+
+void *mec_add_object(void *addr, unsigned long size)
+{
+       mec_object_t *new;
+
+       if (mec_find_object(addr))
+               return NULL;
+
+       DPRINTF("Add object %p/%lu!", addr, size);
+       new = (mec_object_t *)kmalloc(sizeof(mec_object_t), GFP_KERNEL);
+       if(new != NULL)
+       {
+               new->address = addr;
+               new->size = size;
+               INIT_HLIST_NODE (&new->hlist);
+               hlist_add_head(&new->hlist, &malloc_table[0]);//hash_ptr(addr, M_BITS)]);
+       }
+
+       return new;
+}
+
+void mec_delete_object(void *addr)
+{
+       struct hlist_head *head = &malloc_table[0];//hash_ptr(addr, M_BITS)];
+       unsigned long spinlock_flags = 0;
+
+       mec_object_t *obj = (mec_object_t *)mec_find_object(addr);
+       if(!obj)
+               return;
+
+       DPRINTF("Delete object %p!", addr);
+       hlist_del(&obj->hlist);
+       if(hlist_empty(head))
+       {
+               spin_lock_irqsave (&mec_spinlock, spinlock_flags);
+               curr_mec_obj = NULL;
+               spin_unlock_irqrestore (&mec_spinlock, spinlock_flags);
+       }
+       kfree(obj);
+}
+
+void mec_remove_objects()
+{
+       mec_object_t *obj = NULL;
+       struct hlist_node *node = NULL, *tmp;
+       struct hlist_head *head = &malloc_table[0];//hash_ptr(addr, M_BITS)];
+       unsigned long spinlock_flags = 0;
+
+       spin_lock_irqsave (&mec_spinlock, spinlock_flags);
+       curr_mec_obj = NULL;
+       spin_unlock_irqrestore (&mec_spinlock, spinlock_flags);
+
+       hlist_for_each_entry_safe(obj, node, tmp, head, hlist)
+       {
+               hlist_del(&obj->hlist);
+               kfree(obj);
+       }
+}
+
+void mec_resize_object(void *hnd, unsigned long size)
+{
+       mec_object_t *obj = (mec_object_t *)hnd;
+
+       DPRINTF("Resize object %p/%u->%lu!", obj->address, obj->size, size);
+       obj->size = size;
+}
+
+void mec_post_event(char *data, unsigned long len)
+{
+       SWAP_TYPE_EVENT_HEADER *pEventHeader = (SWAP_TYPE_EVENT_HEADER *)data;
+       void **oaddr;
+       // point to the first arg
+       char *arg = (char *)&(pEventHeader->m_nNumberOfArgs) + sizeof(TYPEOF_NUMBER_OF_ARGS);
+       arg += ALIGN_VALUE(strlen(arg) + 1);
+       //func_addr, ret_addr, frame_ptr, new_addr, new_size, [old_addr]
+       oaddr = (void **)(arg + 3*sizeof(void *));
+       if((pEventHeader->m_nProbeID >= MEC_ALLOC_PROBE_ID_MIN) && (pEventHeader->m_nProbeID <= MEC_ALLOC_PROBE_ID_MAX))
+       {
+               if(pEventHeader->m_nType == RECORD_RET)
+               {
+                       unsigned long *osize = (unsigned long *)(arg + 4*sizeof(void *));
+                       if((pEventHeader->m_nProbeID == MEC_REALLOC_PROBE_ID) || (pEventHeader->m_nProbeID == MEC_MREMAP_PROBE_ID))
+                       {
+                               void **addr = (void *)(arg + 4*sizeof(void *) + sizeof(unsigned long));
+                               if(*oaddr == *addr)
+                               {
+                                       void *obj = mec_find_object(*addr);
+                                       if(obj)
+                                               mec_resize_object(obj, *osize);
+                                       else if(!mec_add_object(*oaddr, *osize)) // normally should not get here
+                                                       EPRINTF("Failed to add new object %p/%lu!", *oaddr, *osize);
+                               }
+                               else
+                               {
+                                       mec_delete_object(*addr); // delete old
+                                       if(!mec_add_object(*oaddr, *osize)) // add new
+                                               EPRINTF("Failed to add new object %p/%lu!", *oaddr, *osize);
+                               }
+                       }
+                       else if(!mec_add_object(*oaddr, *osize))
+                                       EPRINTF("Failed to add new object %p/%lu!", *oaddr, *osize);
+               }
+       }
+       else if((pEventHeader->m_nProbeID >= MEC_DEALLOC_PROBE_ID_MIN) && (pEventHeader->m_nProbeID <= MEC_DEALLOC_PROBE_ID_MAX))
+       {
+               if(pEventHeader->m_nType == RECORD_ENTRY)
+                       mec_delete_object(*oaddr);
+       }
+       pEventHeader->m_nProbeID = US_PROBE_ID;
+}
+#endif
+
+int put_us_event (char *data, unsigned long len)
+{
+       unsigned long spinlock_flags = 0L;
+
+       SWAP_TYPE_EVENT_HEADER *pEventHeader = (SWAP_TYPE_EVENT_HEADER *)data;  
+       char *cur = data + sizeof(TYPEOF_EVENT_LENGTH) + sizeof(TYPEOF_EVENT_TYPE) 
+                               + sizeof(TYPEOF_PROBE_ID);
+       TYPEOF_NUMBER_OF_ARGS nArgs = pEventHeader->m_nNumberOfArgs;
+       TYPEOF_PROBE_ID probe_id = pEventHeader->m_nProbeID;
+       //int i;
+       
+       /*if(probe_id == US_PROBE_ID){
+               printk("esrc %p/%d[", data, len);
+               for(i = 0; i < len; i++)
+                       printk("%02x ", data[i]);
+               printk("]\n");
+       }*/
+       
+       // set pid/tid/cpu/time i
+       //pEventHeader->m_time.tv_sec = tv.tv_sec;
+       //pEventHeader->m_time.tv_usec = tv.tv_usec;
+
+#ifdef MEMORY_CHECKER
+       //TODO: move this part to special MEC event posting routine, new IOCTL is needed
+       if((probe_id >= MEC_PROBE_ID_MIN) && (probe_id <= MEC_PROBE_ID_MAX))
+               mec_post_event(data, len);
+#endif
+
+       if((probe_id == EVENT_FMT_PROBE_ID) || !(event_mask & IOCTL_EMASK_TIME)){
+               TYPEOF_TIME tv = { 0, 0 };
+               do_gettimeofday (&tv);
+               memcpy(cur, &tv, sizeof(TYPEOF_TIME));
+               cur += sizeof(TYPEOF_TIME);
+       }
+       //pEventHeader->m_nProcessID = current_tgid;
+       if((probe_id == EVENT_FMT_PROBE_ID) || !(event_mask & IOCTL_EMASK_PID)){                
+               //TYPEOF_PROCESS_ID current_tgid = current->tgid;
+               (*(TYPEOF_PROCESS_ID *)cur) = current->tgid;
+               cur += sizeof(TYPEOF_PROCESS_ID);
+       }
+       //pEventHeader->m_nThreadID = current_pid;
+       if((probe_id == EVENT_FMT_PROBE_ID) || !(event_mask & IOCTL_EMASK_TID)){                
+               //TYPEOF_THREAD_ID current_pid = current->pid;
+               (*(TYPEOF_THREAD_ID *)cur) = current->pid;
+               cur += sizeof(TYPEOF_THREAD_ID);
+       }
+       //pEventHeader->m_nCPU = current_cpu;
+       if((probe_id == EVENT_FMT_PROBE_ID) || !(event_mask & IOCTL_EMASK_CPU)){                
+               //TYPEOF_CPU_NUMBER current_cpu = task_cpu(current);
+               (*(TYPEOF_CPU_NUMBER *)cur) = task_cpu(current);
+               cur += sizeof(TYPEOF_CPU_NUMBER);
+       }
+       //printk("%d %x", probe_id, event_mask);
+       // dyn lib event should have all args, it is for internal use and not visible to user
+       if((probe_id == EVENT_FMT_PROBE_ID) || (probe_id == DYN_LIB_PROBE_ID) || !(event_mask & IOCTL_EMASK_ARGS)){
+               // move only if any of prev fields has been skipped 
+               if(event_mask & (IOCTL_EMASK_TIME|IOCTL_EMASK_PID|IOCTL_EMASK_TID|IOCTL_EMASK_CPU)){
+                       memmove(cur, data+sizeof(SWAP_TYPE_EVENT_HEADER)-sizeof(TYPEOF_NUMBER_OF_ARGS), 
+                                       len-sizeof(SWAP_TYPE_EVENT_HEADER)+sizeof(TYPEOF_NUMBER_OF_ARGS)
+                                       -sizeof(TYPEOF_EVENT_LENGTH));
+               }
+               cur += len-sizeof(SWAP_TYPE_EVENT_HEADER)+sizeof(TYPEOF_NUMBER_OF_ARGS)
+                               -sizeof(TYPEOF_EVENT_LENGTH);
+       }
+       else{
+               // user space probes should have at least one argument to identify them 
+               if((probe_id == US_PROBE_ID) || (probe_id == VTP_PROBE_ID)){
+                       char *pArg1;
+                       (*(TYPEOF_NUMBER_OF_ARGS *)cur) = 1;
+                       cur += sizeof(TYPEOF_NUMBER_OF_ARGS);
+                       // pack args using format string for the 1st arg only
+                       memset(cur, 0, ALIGN_VALUE(2));
+                       cur[0] = 'p'; cur[1] = '\0';
+                       cur += ALIGN_VALUE(2);
+                       pArg1 = data + sizeof(SWAP_TYPE_EVENT_HEADER)+ALIGN_VALUE(nArgs+1);
+                       memmove(cur, pArg1, sizeof(unsigned long));
+                       cur += sizeof(unsigned long);
+               }
+               else {
+                       (*(TYPEOF_NUMBER_OF_ARGS *)cur) = 0;
+                       cur += sizeof(TYPEOF_NUMBER_OF_ARGS);
+               }
+       }       
+       pEventHeader->m_nLength = cur - data + sizeof(TYPEOF_EVENT_LENGTH);
+       *((TYPEOF_EVENT_LENGTH *)cur) = pEventHeader->m_nLength;
+       len = pEventHeader->m_nLength;
+       
+       /*if(probe_id == US_PROBE_ID){
+               printk("edst[");
+               for(i = 0; i < len; i++)
+                       printk("%02x ", data[i]);
+               printk("]\n");
+       }*/
+       
+       // We are in a preemption-safe context (because interrupts are disabled on
+       // local CPU), thus we can use "__get_cpu_var()" directly.
+       local_inc (&__get_cpu_var (per_cpu_in_use_count));
+       if (0 != local_read (&__get_cpu_var (per_cpu_in_use_count)))
+       {
+               // nested entry for the same CPU means page fault or something
+               // similarly terrific in our code
+               local_dec (&__get_cpu_var (per_cpu_in_use_count));      // roll back
+               EPRINTF ("nested %s() for the same CPU", __FUNCTION__);
+
+               spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+               ++ec_info.collision_count;
+               ++ec_info.lost_events_count;
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+
+               return -1;              // bail out
+       }
+
+       /*
+          The same CPU can't get here second time thus we can safely use spinlock
+          to separate multiple CPUs accessing our data.
+        */
+       if(WriteEventIntoBuffer(data, len) == -1) {
+               EPRINTF("Cannot write event into buffer!");
+
+               spin_lock_irqsave (&ec_spinlock, spinlock_flags);
+               ++ec_info.lost_events_count;
+               spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
+       }
+
+       local_dec (&__get_cpu_var (per_cpu_in_use_count));      // roll back
+       
+       return 0;
+}
+
+int set_predef_uprobes (ioctl_predef_uprobes_info_t *data)
+{
+       int i, k, size = 0, probe_size, result, j;
+       char *buf, *sep1, *sep2;
+
+       inst_us_proc_t *my_uprobes_info = (inst_us_proc_t *)lookup_name("my_uprobes_info");
+       inst_us_proc_t empty_uprobes_info =
+       {
+               .libs_count = 0,
+               .p_libs = NULL,
+       };
+       if (my_uprobes_info == 0)
+               my_uprobes_info = &empty_uprobes_info;
+
+       for(j = 0; j < data->probes_count; j++){
+               probe_size = strlen_user(data->p_probes+size);
+               buf = kmalloc(probe_size, GFP_KERNEL);
+               if(!buf){
+                       EPRINTF("failed to alloc mem!");
+                       return -EFAULT;
+               }               
+               result = strncpy_from_user(buf, data->p_probes+size, probe_size);
+               if (result != (probe_size-1))
+               {
+                       EPRINTF("failed to copy from user!");
+                       kfree(buf);
+                       return -EFAULT;
+               }
+               //DPRINTF("%s", buf);
+               sep1 = strchr(buf, ':');
+               if(!sep1){
+                       EPRINTF("skipping invalid predefined uprobe string '%s'!", buf);
+                       kfree(buf);
+                       size += probe_size;
+                       continue;
+               }               
+               sep2 = strchr(sep1+1, ':');
+               if(!sep2 || (sep2 == sep1) || (sep2+2 == buf+probe_size)){
+                       EPRINTF("skipping invalid predefined uprobe string '%s'!", buf);
+                       kfree(buf);
+                       size += probe_size;
+                       continue;
+               }               
+               for(i = 0; i < my_uprobes_info->libs_count; i++){
+                       if(strncmp(buf, my_uprobes_info->p_libs[i].path, sep1-buf) != 0)
+                               continue;
+                       for(k = 0; k < my_uprobes_info->p_libs[i].ips_count; k++){
+                               if(strncmp(sep1+1, my_uprobes_info->p_libs[i].p_ips[k].name, sep2-sep1-1) != 0)
+                                       continue;                               
+                               my_uprobes_info->p_libs[i].p_ips[k].offset = simple_strtoul(sep2+1, NULL, 16);
+                       }
+               }
+               kfree(buf);
+               size += probe_size;
+       }
+       return 0;
+}
+
+int get_predef_uprobes_size(int *size)
+{
+       int i, k;
+
+       inst_us_proc_t *my_uprobes_info = (inst_us_proc_t *)lookup_name("my_uprobes_info");
+       inst_us_proc_t empty_uprobes_info =
+       {
+               .libs_count = 0,
+               .p_libs = NULL,
+       };
+
+       if (my_uprobes_info == 0)
+               my_uprobes_info = &empty_uprobes_info;
+
+       *size = 0;
+       for(i = 0; i < my_uprobes_info->libs_count; i++){
+               int lib_size = strlen(my_uprobes_info->p_libs[i].path);
+               for(k = 0; k < my_uprobes_info->p_libs[i].ips_count; k++){
+                       // libc.so.6:printf:
+                       *size += lib_size + 1 + strlen(my_uprobes_info->p_libs[i].p_ips[k].name) + 2;
+               }
+       }
+       return 0;
+}
+
+int get_predef_uprobes(ioctl_predef_uprobes_info_t *udata)
+{
+       ioctl_predef_uprobes_info_t data;
+       int i, k, size, lib_size, func_size, result;
+       unsigned count = 0;
+       char sep[] = ":";
+
+       inst_us_proc_t *my_uprobes_info = (inst_us_proc_t *)lookup_name("my_uprobes_info");
+       inst_us_proc_t empty_uprobes_info =
+       {
+               .libs_count = 0,
+               .p_libs = NULL,
+       };
+       if (my_uprobes_info == 0)
+               my_uprobes_info = &empty_uprobes_info;
+
+       // get addr of array
+       if (copy_from_user ((void *)&data, udata, sizeof (data)))
+       {
+               EPRINTF("failed to copy from user!");
+               return -EFAULT;
+       }
+               
+       size = 0;
+       for(i = 0; i < my_uprobes_info->libs_count; i++){
+               lib_size = strlen(my_uprobes_info->p_libs[i].path);
+               for(k = 0; k < my_uprobes_info->p_libs[i].ips_count; k++){
+                       // libname
+                       result = copy_to_user ((void *)(data.p_probes+size), my_uprobes_info->p_libs[i].path, lib_size);
+                       if (result)
+                       {
+                               EPRINTF("failed to copy to user!");
+                               return -EFAULT;
+                       }
+                       size += lib_size;
+                       // ":"
+                       result = copy_to_user ((void *)(data.p_probes+size), sep, 1);
+                       if (result)
+                       {
+                               EPRINTF("failed to copy to user!");
+                               return -EFAULT;
+                       }
+                       size++;
+                       // probename
+                       //DPRINTF("'%s'", my_uprobes_info->p_libs[i].p_ips[k].name);
+                       func_size = strlen(my_uprobes_info->p_libs[i].p_ips[k].name);
+                       result = copy_to_user ((void *)(data.p_probes+size), my_uprobes_info->p_libs[i].p_ips[k].name, func_size);
+                       if (result)
+                       {
+                               EPRINTF("failed to copy to user!");
+                               return -EFAULT;
+                       }
+                       size += func_size;
+                       // ":\0"
+                       result = copy_to_user ((void *)(data.p_probes+size), sep, 2);
+                       if (result)
+                       {
+                               EPRINTF("failed to copy to user!");
+                               return -EFAULT;
+                       }
+                       size += 2;
+                       count++;
+               }
+       }
+
+       // set probes_count
+       result = copy_to_user ((void *)&(udata->probes_count), &count, sizeof(count));
+       if (result)
+       {
+               EPRINTF("failed to copy to user!");
+               return -EFAULT;
+       }
+       return 0;
+}
diff --git a/driver/storage.h b/driver/storage.h
new file mode 100644 (file)
index 0000000..9dbf65f
--- /dev/null
@@ -0,0 +1,208 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           storage.h
+//
+//      DESCRIPTION:
+//      This file is C source for SWAP.
+//
+//      SEE ALSO:       storage.c
+//      AUTHOR:         L.Komkov, S.Dianov, A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.02.15
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(__STORAGE_H__)
+#define __STORAGE_H__
+
+#include "picl.h"
+#include "ec_ioctl.h"
+#include "ec_probe.h"
+#include "ec_module.h"
+#include "probes_manager.h"
+#include "probes.h"
+#include "event_tmpl.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef __DISABLE_RELAYFS
+extern struct rchan* GetRelayChannel(void);
+extern struct dentry* GetRelayDir(void);
+#endif //__DISABLE_RELAYFS
+
+extern int EnableMultipleBuffer(void);
+extern int DisableMultipleBuffer(void);
+extern int EnableContinuousRetrieval(void);
+extern int DisableContinuousRetrieval(void);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+extern unsigned int GetBufferSize(void);
+extern int SetBufferSize(unsigned int nSize);
+extern int ResetBuffer(void);
+
+//extern spinlock_t buffer_spinlock;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+    Functions "storage_init()" and "storage_down()" are for initialization and
+    shutdown respectively.
+*/
+extern int storage_init (void);
+extern void storage_down (void);
+
+/*
+    Function "pack_event_info()" saves information about event into buffer. It
+    is used in 'probes' to pack and save event data.
+*/
+extern void pack_event_info (probe_id_t probe_id, record_type_t record_type, const char *fmt, ...);
+
+/*
+    Function "set_us_proc_inst()" saves instrumentation info for user space process.
+*/
+extern int set_us_proc_inst_info (ioctl_inst_usr_space_proc_t * inst_info);
+
+/*
+    Function "release_us_proc_inst_info()" destroys instrumentation info for user space process.
+*/
+extern void release_us_proc_inst_info (void);
+
+/*
+    Adds non-predefined kernel probe to the list.
+*/
+extern int add_probe_to_list (unsigned long addr, kernel_probe_t ** pprobe);
+
+/*
+    Removes non-predefined kernel probe from the list.
+*/
+extern int remove_probe_from_list (unsigned long addr);
+
+/*
+    Searches non-predefined kernel probe in the list.
+*/
+extern kernel_probe_t *find_probe (unsigned long addr);
+
+/*
+    Copies event from user space to buffer and updates its pid/tid/cpu/time.
+*/
+extern int put_us_event (char *data, unsigned long len);
+
+/*
+    Sets event mask.
+*/
+extern int set_event_mask (int new_mask);
+
+/*
+    Gets event mask.
+*/
+extern int get_event_mask (int *mask);
+
+/*
+    Sets predefined user space probes info.
+*/
+extern int set_predef_uprobes (ioctl_predef_uprobes_info_t *data);
+/*
+    Gets predefined user space probes info length.
+*/
+extern int get_predef_uprobes_size(int *size);
+/*
+    Gets predefined user space probes info.
+*/
+extern int get_predef_uprobes(ioctl_predef_uprobes_info_t *data);
+
+#ifdef MEMORY_CHECKER
+extern void mec_remove_objects(void);
+extern void *mec_find_in_object_range(void *addr, int *rz);
+extern void mec_change_active_object(void *obj);
+extern unsigned long pf_addr;
+// maximum overflow/underflow size
+// access into unallocated process space near active object,
+// but within this gap will be treated as overflow/underflow
+#define MEC_MAX_OVERFLOW_SIZE  (1024)
+#endif
+
+// internal bookkeeping of storage
+extern char *p_buffer;
+
+// list of selected non-predefined kernel probes
+extern struct hlist_head kernel_probes;
+
+// event mask
+extern int event_mask;
+
+typedef struct
+{
+       char *name;
+       int installed;
+       struct jprobe jprobe;
+       struct kretprobe retprobe;
+       unsigned long offset;
+} us_proc_ip_t;
+
+typedef struct
+{
+       int installed;
+       struct jprobe jprobe;
+       unsigned long addr;
+       struct list_head list;
+} us_proc_vtp_t;
+
+typedef struct
+{
+       //char *name;
+       char type;
+       unsigned long size;
+       signed char reg;        // -1 - memory, 0..127 - register number  
+       long off;
+       struct list_head list;
+} us_proc_vtp_data_t;
+
+typedef struct dentry *STRUCT_DENTRY_PTR;
+
+typedef struct
+{
+       char *path;
+       STRUCT_DENTRY_PTR m_f_dentry;
+       unsigned ips_count;
+       us_proc_ip_t *p_ips;
+       unsigned vtps_count;
+       us_proc_vtp_t *p_vtps;
+       int loaded;
+} us_proc_lib_t;
+
+typedef struct
+{
+       char *path;
+       STRUCT_DENTRY_PTR m_f_dentry;
+       pid_t tgid;
+       unsigned unres_ips_count;
+       unsigned unres_vtps_count;
+       //kprobe_opcode_t *mapped_codelets;
+       unsigned libs_count;
+       us_proc_lib_t *p_libs;
+} inst_us_proc_t;
+
+struct cond {
+       /* cond data itself */
+       struct event_tmpl tmpl;
+       /* linked list */
+       struct list_head list;
+       /* has been applied (for start and stop conditions) */
+       int applied;
+};
+
+extern struct cond cond_list;
+
+/* macros for testing flags */
+#define ET_FIELD_CLR(flags, field) (flags &= ~field)
+#define ET_FIELD_SET(flags, field) (flags |= field)
+#define ET_FIELD_ISSET(flags, field) ((flags & field) != 0)
+
+extern inst_us_proc_t us_proc_info;
+
+
+#endif /* !defined(__STORAGE_H__) */
diff --git a/driver/us_proc_inst.c b/driver/us_proc_inst.c
new file mode 100644 (file)
index 0000000..21a2165
--- /dev/null
@@ -0,0 +1,853 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           us_proc_inst.c
+//
+//      DESCRIPTION:
+//      This file is C source for SWAP driver.
+//
+//      SEE ALSO:       us_proc_inst.h
+//      AUTHOR:         A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.06.02
+//      VERSION:        1.0
+//      REVISION DATE:  2008.12.02
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+#include "module.h"
+#include "us_proc_inst.h"
+
+static int register_usprobe (struct task_struct *task, struct mm_struct *mm, us_proc_ip_t * ip, int atomic, kprobe_opcode_t * islot);
+static int unregister_usprobe (struct task_struct *task, us_proc_ip_t * ip, int atomic);
+
+int us_proc_probes;
+
+static int
+find_task_by_path (const char *path, struct task_struct **p_task, struct list_head *tids)
+{
+       int found = 0;
+       struct task_struct *task;
+       struct vm_area_struct *vma;
+       struct mm_struct *mm;
+       struct nameidata nd;
+       //fp_kallsyms_lookup_name_t fp;
+
+       *p_task = 0;
+
+       /* find corresponding dir entry, this is also check for valid path */
+       // TODO: test - try to instrument process with non-existing path
+       // TODO: test - try to instrument process  with existing path and delete file just after start
+       if (path_lookup (path, LOOKUP_FOLLOW, &nd) != 0)
+       {
+               EPRINTF ("failed to lookup dentry for path %s!", path);
+               return -EINVAL;
+       }
+
+       rcu_read_lock ();
+       for_each_process (task) {
+               mm = get_task_mm (task);
+               if (!mm)
+                       continue;
+               down_read (&mm->mmap_sem);
+               vma = mm->mmap;
+               while (vma) {
+                       if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+                               if (vma->vm_file->f_dentry == nd.dentry) {
+#else
+                               if (vma->vm_file->f_dentry == nd.path.dentry) {
+#endif
+                                       if (!*p_task) {
+                                               *p_task = task;
+                                               get_task_struct (task);
+                                       }
+                                       //break;
+                               }
+                       }
+                       vma = vma->vm_next;
+               }
+               up_read (&mm->mmap_sem);
+               mmput (mm);
+               if (found)
+                       break;
+       }
+       rcu_read_unlock ();
+
+       if (*p_task)
+               DPRINTF ("found pid %d for %s.", (*p_task)->pid, path);
+       else
+               DPRINTF ("pid for %s not found!", path);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+       path_release (&nd);
+#else
+       path_put (&nd.path);
+#endif
+       return 0;
+}
+
+#if defined(CONFIG_MIPS)
+#      define ARCH_REG_VAL(regs, idx)  regs->regs[idx]
+#elif defined(CONFIG_ARM)
+#      define ARCH_REG_VAL(regs, idx)  regs->uregs[idx]
+#else
+#      define ARCH_REG_VAL(regs, idx)  0
+#      warning ARCH_REG_VAL is not implemented for this architecture. FBI will work improperly or even crash!!!
+#endif // ARCH
+
+DEFINE_PER_CPU (us_proc_vtp_t *, gpVtp) = NULL;
+//EXPORT_PER_CPU_SYMBOL_GPL(gpVtp);
+DEFINE_PER_CPU (struct pt_regs *, gpCurVtpRegs) = NULL;
+//EXPORT_PER_CPU_SYMBOL_GPL(gpCurVtpRegs);
+
+static void
+us_vtp_event_pre_handler (us_proc_vtp_t * vtp, struct pt_regs *regs)
+{
+       __get_cpu_var(gpVtp) = vtp;
+       __get_cpu_var(gpCurVtpRegs) = regs;
+}
+
+static void
+us_vtp_event_handler (unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5, unsigned long arg6)
+{
+       //static int nCount;
+       us_proc_vtp_t *vtp = __get_cpu_var(gpVtp);
+#if !defined(CONFIG_X86)
+       struct pt_regs *regs = __get_cpu_var(gpCurVtpRegs);
+#endif
+       char fmt[3];
+       unsigned long vaddr;
+       long ival;
+       char cval, *sval;
+       //float fval;
+       //struct list_head *pos, *tmp;
+       us_proc_vtp_data_t *vtp_data;
+
+       fmt[0] = 'p';
+       fmt[2] = 0;
+
+       list_for_each_entry_rcu (vtp_data, &vtp->list, list)
+       {
+//             DPRINTF ("[%d]proc %s(%d): %lx", nCount++, current->comm, current->pid, vtp->addr);
+               fmt[1] = vtp_data->type;
+               if (vtp_data->reg == -1)
+                       vaddr = vtp_data->off;
+               else
+                       vaddr = ARCH_REG_VAL (regs, vtp_data->reg) + vtp_data->off;
+//             DPRINTF ("VTP type '%c'", vtp_data->type);
+               switch (vtp_data->type)
+               {
+               case 'd':
+               case 'x':
+               case 'p':
+                       if (read_proc_vm_atomic (current, vaddr, &ival, sizeof (ival)) < sizeof (ival))
+                               EPRINTF ("failed to read vm of proc %s/%u addr %lu!", current->comm, current->pid, vaddr);
+                       else
+                               pack_event_info (VTP_PROBE_ID, RECORD_ENTRY, fmt, vtp->jprobe.kp.addr, ival);
+                       break;
+               case 'f':
+                       if (read_proc_vm_atomic (current, vaddr, &ival, sizeof (ival)) < sizeof (ival))
+                               EPRINTF ("failed to read vm of proc %s/%u addr %lu!", current->comm, current->pid, vaddr);
+                       else
+                               pack_event_info (VTP_PROBE_ID, RECORD_ENTRY, fmt, vtp->jprobe.kp.addr, ival);
+                       break;
+               case 'c':
+                       if (read_proc_vm_atomic (current, vaddr, &cval, sizeof (cval)) < sizeof (cval))
+                               EPRINTF ("failed to read vm of proc %s/%u addr %lu!", current->comm, current->pid, vaddr);
+                       else
+                               pack_event_info (VTP_PROBE_ID, RECORD_ENTRY, fmt, vtp->jprobe.kp.addr, cval);
+                       break;
+               case 's':
+                       if (current->active_mm)
+                       {
+                               struct page *page;
+                               struct vm_area_struct *vma;
+                               void *maddr;
+                               int len;
+                               if (get_user_pages_atomic (current, current->active_mm, vaddr, 1, 0, 1, &page, &vma) <= 0)
+                               {
+                                       EPRINTF ("get_user_pages_atomic failed for proc %s/%u addr %lu!", current->comm, current->pid, vaddr);
+                                       break;
+                               }
+                               maddr = kmap_atomic (page, KM_USER0);
+                               len = strlen (maddr + (vaddr & ~PAGE_MASK));
+                               sval = kmalloc (len + 1, GFP_KERNEL);
+                               if (!sval)
+                                       EPRINTF ("failed to alloc memory for string in proc %s/%u addr %lu!", current->comm, current->pid, vaddr);
+                               else
+                               {
+                                       copy_from_user_page (vma, page, vaddr, sval, maddr + (vaddr & ~PAGE_MASK), len + 1);
+                                       pack_event_info (VTP_PROBE_ID, RECORD_ENTRY, fmt, vtp->jprobe.kp.addr, sval);
+                                       kfree (sval);
+                               }
+                               kunmap_atomic (maddr, KM_USER0);
+                               page_cache_release (page);
+                       }
+                       else
+                               EPRINTF ("task %s/%u has no mm!", current->comm, current->pid);
+                       break;
+               default:
+                       EPRINTF ("unknown variable type '%c'", vtp_data->type);
+               }
+       }
+       uprobe_return ();
+}
+
+static int
+install_mapped_ips (struct task_struct *task, int atomic)
+{
+       struct vm_area_struct *vma;
+       int i, k, err, retry;
+       unsigned long addr;//, slot_idx;
+       //char lib_path[256];
+       //static unsigned npcdep;
+       unsigned int old_ips_count, old_vtps_count;
+       struct mm_struct *mm;
+       struct ip_node {
+               struct list_head        plist;
+               us_proc_ip_t *          ip;
+       } * nip, *tnip;
+       LIST_HEAD(iplist);
+       struct vtp_node {
+               struct list_head        plist;
+               us_proc_vtp_t *         vtp;            
+       } * nvtp, *tnvtp;
+       LIST_HEAD(vtplist);
+               
+_restart:
+       mm = atomic ? task->active_mm : get_task_mm (task);
+       if (!mm){
+//             DPRINTF ("proc %d has no mm", task->pid);
+               return us_proc_info.unres_ips_count + us_proc_info.unres_vtps_count;
+       }
+       
+       old_ips_count = us_proc_info.unres_ips_count;
+       old_vtps_count = us_proc_info.unres_vtps_count;
+       if(!atomic) 
+               down_read (&mm->mmap_sem);
+       vma = mm->mmap;
+       while (vma)
+       {
+               // skip non-text section
+               if (!(vma->vm_flags & VM_EXEC) || !vma->vm_file || 
+                       !(vma->vm_flags & (VM_WRITE | VM_MAYWRITE)) || 
+                       !(vma->vm_flags & (VM_READ | VM_MAYREAD)))
+               {
+                       vma = vma->vm_next;
+                       continue;
+               }
+               //DPRINTF("check mapped lib %s at %lx.", 
+               //              d_path(vma->vm_file->f_dentry, vma->vm_file->f_vfsmnt, lib_path, sizeof(lib_path)), vma->vm_start);
+               //slot_idx = 0;
+               for (i = 0; i < us_proc_info.libs_count; i++)
+               {                               
+                       //DPRINTF("lookup lib %s.", us_proc_info.p_libs[i].path);
+                       //TODO: test - try to instrument non-existing libs
+                       if (vma->vm_file->f_dentry == us_proc_info.p_libs[i].m_f_dentry)
+                       {
+                               //if (strcmp(d_path(vma->vm_file->m_f_dentry, vma->vm_file->f_vfsmnt, lib_path, sizeof(lib_path)),
+                               //                 us_proc_info.p_libs[i].path) == 0){
+                               //DPRINTF("found lib %s.", us_proc_info.p_libs[i].path);
+                               //unsigned oi_count = us_proc_info.unres_ips_count;
+                               //unsigned ov_count = us_proc_info.unres_vtps_count;
+                               for (k = 0; k < us_proc_info.p_libs[i].ips_count; k++/*, slot_idx++*/)
+                               {
+                                       if (!us_proc_info.p_libs[i].p_ips[k].installed)
+                                       {
+                                               addr = us_proc_info.p_libs[i].p_ips[k].offset;
+                                               if (!(vma->vm_flags & VM_EXECUTABLE))
+                                                       addr += vma->vm_start;
+                                               //DPRINTF("check sym %s at %lx.", us_proc_info.p_libs[i].p_ips[k].name, addr);
+                                               if (page_present (mm, addr))
+                                               {
+                                                       //DPRINTF ("pid %d, %s sym is present at %lx/%lx.", task->pid, us_proc_info.p_libs[i].path, us_proc_info.p_libs[i].p_ips[k].offset, addr);
+                                                       //if (!us_proc_info.p_libs[i].p_ips[k].installed)
+                                                       {
+                                                               us_proc_info.unres_ips_count--;
+                                                               us_proc_info.p_libs[i].p_ips[k].installed = 1;
+                                                               DPRINTF ("pid %d, %s sym is loaded at %lx/%lx.", task->pid, us_proc_info.p_libs[i].path, us_proc_info.p_libs[i].p_ips[k].offset, addr);
+                                                               nip = kmalloc(sizeof(struct ip_node), GFP_KERNEL);
+                                                               if(!nip){
+                                                                       EPRINTF ("failed to allocate list item for IP!");
+                                                                       continue;
+                                                               }
+                                                               us_proc_info.p_libs[i].p_ips[k].jprobe.kp.addr = (kprobe_opcode_t *) addr;
+                                                               us_proc_info.p_libs[i].p_ips[k].retprobe.kp.addr = (kprobe_opcode_t *) addr;
+                                                               INIT_LIST_HEAD (&nip->plist);
+                                                               nip->ip = &us_proc_info.p_libs[i].p_ips[k];
+                                                               list_add_tail (&nip->plist, &iplist);
+                                                       }
+                                               }
+                                       }
+                               }
+                               for (k = 0; k < us_proc_info.p_libs[i].vtps_count; k++/*, slot_idx++*/)
+                               {
+                                       if (us_proc_info.p_libs[i].p_vtps[k].installed)
+                                       {
+                                               /*DPRINTF("VTPs at %lx are already installed.", 
+                                                  us_proc_info.p_libs[i].p_ips[k].offset); */
+                                       }
+                                       else
+                                       {
+                                               addr = us_proc_info.p_libs[i].p_vtps[k].addr;
+                                               if (!(vma->vm_flags & VM_EXECUTABLE))
+                                                       addr += vma->vm_start;
+                                               //DPRINTF("check VTPs at %lx.", addr);
+                                               if (page_present (mm, addr))
+                                               {
+                                                       //us_proc_vtp_data_t *vtp_data;
+                                                       //DPRINTF("VTPs is loaded at %lx.", addr);
+                                                       us_proc_info.unres_vtps_count--;
+                                                       us_proc_info.p_libs[i].p_vtps[k].installed = 1;
+                                                       /*list_for_each_entry_rcu (vtp_data, &us_proc_info.p_libs[i].p_vtps[k].list, list)
+                                                       {
+                                                               DPRINTF ("VTP %s is loaded at %lx.", vtp_data->name, addr);
+                                                       }*/
+                                                       us_proc_info.p_libs[i].p_vtps[k].jprobe.kp.tgid = us_proc_info.tgid;
+                                                       us_proc_info.p_libs[i].p_vtps[k].jprobe.kp.addr = (kprobe_opcode_t *) addr;
+                                                       us_proc_info.p_libs[i].p_vtps[k].jprobe.entry = (kprobe_opcode_t *) us_vtp_event_handler;
+                                                       us_proc_info.p_libs[i].p_vtps[k].jprobe.pre_entry = (kprobe_pre_entry_handler_t) us_vtp_event_pre_handler;
+                                                       us_proc_info.p_libs[i].p_vtps[k].jprobe.priv_arg = &us_proc_info.p_libs[i].p_vtps[k];
+                                                       nvtp = kmalloc(sizeof(struct vtp_node), GFP_KERNEL);
+                                                       if(!nvtp){
+                                                               EPRINTF ("failed to allocate list item for VTP!");
+                                                               continue;
+                                                       }
+                                                       INIT_LIST_HEAD (&nvtp->plist);
+                                                       nvtp->vtp = &us_proc_info.p_libs[i].p_vtps[k];
+                                                       list_add_tail (&nvtp->plist, &vtplist);
+                                               }
+                                       }
+                               }
+                               if(!(vma->vm_flags & VM_EXECUTABLE) && !us_proc_info.p_libs[i].loaded
+                                  /*((oi_count != us_proc_info.unres_ips_count) || (ov_count != us_proc_info.unres_vtps_count))*/){
+                                       char *p;
+//                                     DPRINTF ("post dyn lib event %s", us_proc_info.p_libs[i].path);
+                                       // if we installed something, post library info for those IPs
+                                       p = strrchr(us_proc_info.p_libs[i].path, '/');
+                                       if(!p)
+                                               p = us_proc_info.p_libs[i].path;
+                                       else
+                                               p++;
+                                       us_proc_info.p_libs[i].loaded = 1;
+                                       pack_event_info (DYN_LIB_PROBE_ID, RECORD_ENTRY, "spd", 
+                                                       p, vma->vm_start, vma->vm_end-vma->vm_start);
+                               }
+                       }
+               }
+               vma = vma->vm_next;
+       }
+       if(!atomic){    
+               up_read (&mm->mmap_sem);
+               mmput (mm);
+       }
+       
+       if(!list_empty(&iplist) || !list_empty(&vtplist)){
+//             DPRINTF ("Unres IPs/VTPs %d/%d -> %d/%d.", old_ips_count, old_vtps_count,
+//                             us_proc_info.unres_ips_count, us_proc_info.unres_vtps_count);
+       }
+
+       retry = 0;
+       list_for_each_entry_safe(nip, tnip, &iplist, plist) {
+//             DPRINTF ("Install %p/%d IP at %lx.", task, task->pid, nip->ip->offset);
+               if((PAGE_SIZE-(nip->ip->offset % PAGE_SIZE)) < MAX_INSN_SIZE){
+                       retry = 1;
+//                     DPRINTF ("Possibly 1st insn of IP at %lx lies on 2 pages.",  nip->ip->offset);
+               }
+               err = register_usprobe (task, mm, nip->ip, atomic, 0);
+               if (err != 0)
+                       EPRINTF ("failed to install IP at %lx/%p. Error %d!", nip->ip->offset, nip->ip->jprobe.kp.addr, err);
+               list_del(&nip->plist);
+               kfree(nip);
+       }
+       list_for_each_entry_safe(nvtp, tnvtp, &vtplist, plist) {
+//             DPRINTF ("Install VTP at %p.", nvtp->vtp->jprobe.kp.addr);
+               if((PAGE_SIZE-(nvtp->vtp->addr % PAGE_SIZE)) < MAX_INSN_SIZE){
+                       retry = 1;
+//                     DPRINTF ("Possibly 1st insn of VTP %lx lies on 2 pages.", nvtp->vtp->addr);
+               }
+               err = register_ujprobe (task, mm, &nvtp->vtp->jprobe, atomic);
+               if (err)
+                       EPRINTF ("failed to install VTP at %p. Error %d!", nvtp->vtp->jprobe.kp.addr, err);
+               list_del(&nvtp->plist);
+               kfree(nvtp);
+       }
+                       
+       if(retry) goto _restart;
+               
+       return us_proc_info.unres_ips_count + us_proc_info.unres_vtps_count;
+}
+
+static int
+uninstall_mapped_ips (struct task_struct *task, int atomic)
+{
+       int i, k, err;
+
+       for (i = 0; i < us_proc_info.libs_count; i++)
+       {
+//             DPRINTF ("clear lib %s.", us_proc_info.p_libs[i].path);
+               for (k = 0; k < us_proc_info.p_libs[i].ips_count; k++)
+               {
+                       if (us_proc_info.p_libs[i].p_ips[k].installed)
+                       {
+//                             DPRINTF ("remove IP at %p.", us_proc_info.p_libs[i].p_ips[k].jprobe.kp.addr);
+                               err = unregister_usprobe (task, &us_proc_info.p_libs[i].p_ips[k], atomic);
+                               if (err != 0)
+                               {
+                                       EPRINTF ("failed to uninstall IP at %p. Error %d!", us_proc_info.p_libs[i].p_ips[k].jprobe.kp.addr, err);
+                                       continue;
+                               }
+                               us_proc_info.unres_ips_count++;
+                               us_proc_info.p_libs[i].p_ips[k].installed = 0;
+                       }
+               }
+               for (k = 0; k < us_proc_info.p_libs[i].vtps_count; k++)
+               {
+                       if (us_proc_info.p_libs[i].p_vtps[k].installed)
+                       {
+                               //us_proc_vtp_data_t *vtp_data;
+                               /*list_for_each_entry_rcu (vtp_data, &us_proc_info.p_libs[i].p_vtps[k].list, list)
+                               {
+                                       DPRINTF ("remove VTP %s.", vtp_data->name);
+                               }*/
+                               unregister_ujprobe (task, &us_proc_info.p_libs[i].p_vtps[k].jprobe, atomic);
+                               us_proc_info.unres_vtps_count++;
+                               us_proc_info.p_libs[i].p_vtps[k].installed = 0;
+                       }
+               }
+       }
+//     DPRINTF ("Ures IPs  %d.", us_proc_info.unres_ips_count);
+//     DPRINTF ("Ures VTPs %d.", us_proc_info.unres_vtps_count);
+
+       return 0;
+}
+
+void
+send_sig_jprobe_event_handler (int sig, struct siginfo *info, struct task_struct *t, struct sigpending *signals)
+{
+       int iRet, del = 0;
+       struct task_struct *task;
+
+       //DPRINTF("send_signal[%d] %d proc %d", nCount++, sig, t->pid);
+
+       //DPRINTF("%s(%d) send_signal %d for target proc %s(%d)", current->comm, current->pid, sig, t->comm, t->pid);
+       if (sig != SIGKILL)
+               return;
+
+       if (t->tgid != us_proc_info.tgid)
+               return;
+
+       del = 1;
+       // look for another process with the same tgid 
+       rcu_read_lock ();
+       for_each_process (task)
+       {
+               if ((task->pid != t->pid) && (task->tgid == us_proc_info.tgid))
+               {
+                       del = 0;
+                       break;
+               }
+       }
+       rcu_read_unlock ();
+       if (del)
+       {
+//             DPRINTF ("%s(%d) send_signal SIGKILL for the last target proc %s(%d)", current->comm, current->pid, t->comm, t->pid);
+               iRet = uninstall_mapped_ips (t, 1);
+               if (iRet != 0)
+                       EPRINTF ("failed to uninstall IPs (%d)!", iRet);
+       }
+}
+
+static int
+uninstall_kernel_probe (unsigned long addr, int uflag, int kflag, kernel_probe_t ** pprobe)
+{
+       kernel_probe_t *probe = NULL;
+       int iRet = 0;
+
+       if (probes_flags & kflag) {
+               probe = find_probe(addr);
+               if (probe) {
+                       iRet = remove_probe_from_list (addr);
+                       if (iRet)
+                               EPRINTF ("remove_probe_from_list(0x%lx) result=%d!", addr, iRet);
+                       if (pprobe)
+                               *pprobe = NULL;
+               }
+               probes_flags &= ~kflag;
+       }
+
+       if (us_proc_probes & uflag) {
+               if (!(probes_flags & uflag)) {
+                       if (probe) {
+                               iRet = unregister_kernel_probe(probe);
+                               if (iRet) {
+                                       EPRINTF ("unregister_kernel_probe(0x%lx) result=%d!",
+                                                        addr, iRet);
+                                       return iRet;
+                               }
+                       }
+               }
+               us_proc_probes &= ~uflag;
+       }
+
+       return iRet;
+}
+
+int
+deinst_usr_space_proc (void)
+{
+       int iRet = 0, found = 0;
+       struct task_struct *task = 0;
+
+       iRet = uninstall_kernel_probe (pf_addr, US_PROC_PF_INSTLD,
+                                                                  0, &pf_probe);
+       if (iRet)
+               EPRINTF ("uninstall_kernel_probe(do_page_fault) result=%d!", iRet);
+
+       iRet = uninstall_kernel_probe (exit_addr, US_PROC_EXIT_INSTLD,
+                                                                  0, &exit_probe);
+       if (iRet)
+               EPRINTF ("uninstall_kernel_probe(do_exit) result=%d!", iRet);
+
+       if (us_proc_info.tgid == 0)
+               return 0;
+
+       rcu_read_lock ();
+       for_each_process (task)
+       {
+               if (task->tgid == us_proc_info.tgid)
+               {
+                       found = 1;
+                       get_task_struct (task);
+                       break;
+               }
+       }
+       rcu_read_unlock ();
+       if (found)
+       {
+               // uninstall IPs
+               iRet = uninstall_mapped_ips (task, 0);
+               if (iRet != 0)
+                       EPRINTF ("failed to uninstall IPs %d!", iRet);
+               put_task_struct (task);
+               //unregister_all_uprobes(task, 1);
+               us_proc_info.tgid = 0;
+       }
+
+       return iRet;
+}
+
+static int
+install_kernel_probe (unsigned long addr, int uflag, int kflag, kernel_probe_t ** pprobe)
+{
+       kernel_probe_t *probe = NULL;
+       int iRet = 0;
+
+       DPRINTF("us_proc_probes = 0x%x, uflag = 0x%x, "
+                       "probes_flags = 0x%x, kflag = 0x%x",
+                       us_proc_probes, uflag, probes_flags, kflag);
+
+       if (!(probes_flags & kflag)) {
+               iRet = add_probe_to_list (addr, &probe);
+               if (iRet) {
+                       EPRINTF ("add_probe_to_list(0x%lx) result=%d!", addr, iRet);
+                       return iRet;
+               }
+               probes_flags |= kflag;
+       }
+
+       if (!(us_proc_probes & uflag)) {
+               if (!(probes_flags & uflag)) {
+                       iRet = register_kernel_probe (probe);
+                       if (iRet) {
+                               EPRINTF ("register_kernel_probe(0x%lx) result=%d!", addr, iRet);
+                               return iRet;
+                       }
+               }
+               us_proc_probes |= uflag;
+       }
+
+       if (pprobe)
+               *pprobe = probe;
+
+       return 0;
+}
+
+int
+inst_usr_space_proc (void)
+{
+       int iRet, i;
+       struct task_struct *task = 0;
+       //struct mm_struct *mm;
+
+       if (!us_proc_info.path)
+               return 0;
+
+       for (i = 0; i < us_proc_info.libs_count; i++)
+               us_proc_info.p_libs[i].loaded = 0;
+       /* check whether process is already running
+        * 1) if process is running - look for the libraries in the process maps 
+        * 1.1) check if page for symbol does exist
+        * 1.1.1) if page exists - instrument it 
+        * 1.1.2) if page does not exist - make sure that do_page_fault handler is installed
+        * 2) if process is not running - make sure that do_page_fault handler is installed
+        * */
+       //iRet = 
+       find_task_by_path (us_proc_info.path, &task, NULL);
+       //if (iRet != 0)
+       //      return iRet;
+
+       if (task)
+       {
+               us_proc_info.tgid = task->pid;
+               /*mm = get_task_mm (task);
+               if (!mm)
+               {
+                       EPRINTF ("task %d has no mm!", task->pid);
+                       return -1;
+               }
+               down_read (&mm->mmap_sem);*/
+               install_mapped_ips (task, 0);
+               //up_read (&mm->mmap_sem);
+               //mmput (mm);
+               put_task_struct (task);
+       }
+
+       // enable 'do_page_fault' probe to detect when they will be loaded
+       iRet = install_kernel_probe (pf_addr, US_PROC_PF_INSTLD, 0, &pf_probe);
+       if (iRet)
+       {
+               EPRINTF ("install_kernel_probe(do_page_fault) result=%d!", iRet);
+               return iRet;
+       }
+       // enable 'do_exit' probe to detect when user proc exits in order to remove user space probes
+       iRet = install_kernel_probe (exit_addr, US_PROC_EXIT_INSTLD, 0, &exit_probe);
+       if (iRet)
+       {
+               EPRINTF ("install_kernel_probe(do_exit) result=%d!", iRet);
+               return iRet;
+       }
+       
+       return 0;
+}
+
+void
+do_page_fault_ret_pre_code (void)
+{
+       struct mm_struct *mm;
+       struct vm_area_struct *vma = 0;
+
+       if (!us_proc_info.path)
+               return;
+
+       //DPRINTF("do_page_fault from proc %d-%d-%d", current->pid, us_proc_info.tgid, us_proc_info.unres_ips_count);
+       if ((us_proc_info.unres_ips_count + us_proc_info.unres_vtps_count) == 0)
+       {
+               //DPRINTF("do_page_fault: there no unresolved IPs");
+               return;
+       }
+
+       if (us_proc_info.tgid == 0)
+       {
+               //DPRINTF("do_page_fault check proc %d", current->pid);
+               // check whether 
+               /*if( path_lookup(us_proc_info.path, LOOKUP_FOLLOW, &nd) != 0 ) {
+                  EPRINTF("failed to lookup dentry for path %s!", us_proc_info.path);
+                  return; 
+                  } */
+               mm = get_task_mm (current);//current->active_mm;
+               if (mm)
+               {
+                       down_read (&mm->mmap_sem);
+                       //BUG_ON(down_read_trylock(&mm->mmap_sem) == 0);
+                       vma = mm->mmap;
+                       while (vma)
+                       {
+                               if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file)
+                               {
+                                       //DPRINTF("do_page_fault: dentry %p-%p.", vma->vm_file->m_f_dentry, nd.dentry);
+                                       //DPRINTF("do_page_fault: expath %s.", 
+                                       //              d_path(vma->vm_file->m_f_dentry, vma->vm_file->f_vfsmnt, expath, sizeof(expath)));                                      
+                                       if (vma->vm_file->f_dentry == us_proc_info.m_f_dentry)
+                                       {
+                                               //if (strcmp(d_path(vma->vm_file->m_f_dentry, vma->vm_file->f_vfsmnt, expath, sizeof(expath)),
+                                               //                 us_proc_info.path) == 0){
+                                               //DPRINTF("do_page_fault: found!");
+                                               //orig_mm = mm;
+                                               break;
+                                       }
+                               }
+                               vma = vma->vm_next;
+                       }
+                       up_read (&mm->mmap_sem);
+                       mmput (mm);
+               } else {
+//                     DPRINTF ("proc %s/%d has no mm", current->comm, current->pid);
+               }
+               //path_release(&nd);
+               if (vma)
+               {
+                       DPRINTF ("do_page_fault found target proc %s(%d)", current->comm, current->pid);
+                       us_proc_info.tgid = current->pid;
+               }
+       }
+       if (us_proc_info.tgid == current->tgid)
+       {
+               //DPRINTF("do_page_fault from target proc %d", us_proc_info.tgid);
+               if (install_mapped_ips (current, 1) == 0)
+               {
+                       /*iRet = unregister_one_probe(pf_probe_id);
+                          if (iRet)
+                          EPRINTF("unregister_one_probe(do_page_fault) result=%d!", iRet); */
+               }
+       }
+
+       //DPRINTF("do_page_fault from proc %d-%d exit", current->pid, us_proc_info.pid);
+}
+EXPORT_SYMBOL_GPL(do_page_fault_ret_pre_code);
+
+void
+do_exit_probe_pre_code (void)
+{
+       int iRet, del = 0;
+       struct task_struct *task;
+
+       //DPRINTF("do_exit from proc %s-%d", current->comm, current->pid);
+
+       if (current->tgid != us_proc_info.tgid)
+               return;
+
+       //DPRINTF("exit target proc %s-%d", current->comm, current->pid);
+       del = 1;
+       // look for another process with the same tgid 
+       rcu_read_lock ();
+       for_each_process (task)
+       {
+               //if(task->tgid == us_proc_info.tgid)
+               //      DPRINTF("check proc %s-%d", task->comm, task->pid);
+               if ((task->pid != current->pid) && (task->tgid == us_proc_info.tgid))
+               {
+                       del = 0;
+                       break;
+               }
+       }
+       rcu_read_unlock ();
+       if (del)
+       {
+//             DPRINTF ("do_exit from the last target proc %s-%d", current->comm, current->pid);
+               iRet = uninstall_mapped_ips (current, 1);
+               if (iRet != 0)
+                       EPRINTF ("failed to uninstall IPs (%d)!", iRet);
+               unregister_all_uprobes(current, 1);
+               us_proc_info.tgid = 0;
+       }
+
+#ifdef MEMORY_CHECKER
+       mec_remove_objects();
+#endif
+}
+EXPORT_SYMBOL_GPL(do_exit_probe_pre_code);
+
+DEFINE_PER_CPU (us_proc_ip_t *, gpCurIp) = NULL;
+EXPORT_PER_CPU_SYMBOL_GPL(gpCurIp);
+DEFINE_PER_CPU(struct pt_regs *, gpUserRegs) = NULL;
+EXPORT_PER_CPU_SYMBOL_GPL(gpUserRegs);
+
+static unsigned long
+ujprobe_event_pre_handler (us_proc_ip_t * ip, struct pt_regs *regs)
+{
+       __get_cpu_var (gpCurIp) = ip;
+       __get_cpu_var (gpUserRegs) = regs;
+       return 0;
+}
+
+void
+ujprobe_event_handler (unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5, unsigned long arg6)
+{
+       us_proc_ip_t *ip = __get_cpu_var (gpCurIp);
+
+       //static int nCount;
+
+       //DPRINTF("[%d]proc %s(%d): ENTRY %s(%#lx)", nCount++, current->comm, current->pid, ip->name, arg1);
+#ifdef MEMORY_CHECKER
+       struct pt_regs *regs = __get_cpu_var(gpUserRegs);
+#if defined(CONFIG_ARM)
+       pack_event_info (US_PROBE_ID, RECORD_ENTRY, "ppppppp", ip->jprobe.kp.addr, regs->ARM_lr, regs->ARM_fp, arg1, arg2, arg3, arg4, arg5, arg6);
+#else
+#warning not implemented for this arch!
+#endif
+#else
+       pack_event_info (US_PROBE_ID, RECORD_ENTRY, "ppppppp", ip->jprobe.kp.addr, arg1, arg2, arg3, arg4, arg5, arg6);
+#endif
+       uprobe_return ();
+}
+
+int
+uretprobe_event_handler (struct kretprobe_instance *probe, struct pt_regs *regs, us_proc_ip_t * ip)
+{
+       //static int nCount;
+       
+       int retval = regs_return_value(regs);
+       //DPRINTF("[%d]proc %s(%d): RETURN %s(%d)", nCount++, current->comm, current->pid, ip->name, retval);   
+       pack_event_info (US_PROBE_ID, RECORD_RET, "pd", ip->retprobe.kp.addr, retval);
+       return 0;
+}
+
+static int
+register_usprobe (struct task_struct *task, struct mm_struct *mm, us_proc_ip_t * ip, int atomic, kprobe_opcode_t * islot)
+{
+       int ret = 0;
+
+       ip->jprobe.kp.tgid = task->tgid;
+       //ip->jprobe.kp.addr = (kprobe_opcode_t *) addr;
+       if(!ip->jprobe.entry)
+               ip->jprobe.entry = (kprobe_opcode_t *) ujprobe_event_handler;
+       if(!ip->jprobe.pre_entry)
+               ip->jprobe.pre_entry = (kprobe_pre_entry_handler_t) ujprobe_event_pre_handler;
+       ip->jprobe.priv_arg = ip;
+       ret = register_ujprobe (task, mm, &ip->jprobe, atomic);
+       if (ret)
+       {
+               EPRINTF ("register_ujprobe() failure %d", ret);
+               return ret;
+       }
+       
+       //DPRINTF("IP %s boostable = %d", ip->name, ip->jprobe.kp.ainsn.boostable);
+#if 0//defined(CONFIG_X86)
+       // _start is an entry point of the program, so when control reaches it
+       // return address is not saved on stack and can not be hijacked,
+       // so we can not set retprobe on it
+       if(strcmp(ip->name, "_start")!=0)
+#endif
+       {
+               ip->retprobe.kp.tgid = task->tgid;
+               //ip->retprobe.kp.addr = (kprobe_opcode_t *) addr;
+               if(!ip->retprobe.handler)
+                       ip->retprobe.handler = (kretprobe_handler_t) uretprobe_event_handler;
+               ip->retprobe.priv_arg = ip;
+               ret = register_uretprobe (task, mm, &ip->retprobe, atomic);
+               if (ret)
+               {
+                       EPRINTF ("register_uretprobe() failure %d", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int
+unregister_usprobe (struct task_struct *task, us_proc_ip_t * ip, int atomic)
+{
+       unregister_ujprobe (task, &ip->jprobe, atomic);
+#if 0//defined(CONFIG_X86)
+       // _start is an entry point of the program, so when control reaches it
+       // return address is not saved on stack and can not be hijacked,
+       // so we can not set retprobe on it
+       if(strcmp(ip->name, "_start")!=0)
+#endif
+               unregister_uretprobe (task, &ip->retprobe, atomic);
+
+       return 0;
+}
diff --git a/driver/us_proc_inst.h b/driver/us_proc_inst.h
new file mode 100644 (file)
index 0000000..d94dd4c
--- /dev/null
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////////
+//
+//      FILE:           us_proc_inst.h
+//
+//      DESCRIPTION:
+//
+//      SEE ALSO:       us_proc_inst.c
+//      AUTHOR:         A.Gerenkov
+//      COMPANY NAME:   Samsung Research Center in Moscow
+//      DEPT NAME:      Advanced Software Group 
+//      CREATED:        2008.06.02
+//      VERSION:        1.0
+//      REVISION DATE: 2008.12.03
+//
+////////////////////////////////////////////////////////////////////////////////////
+
+/*
+    Functions in "us_proc_inst.h" file .
+*/
+
+#if !defined(__US_PROC_INST_H__)
+#define __US_PROC_INST_H__
+
+#include <linux/signal.h>      // struct sigpending
+
+/*
+    Instruments or schedules pending instrumentation of user space process.
+*/
+extern int inst_usr_space_proc (void);
+extern int deinst_usr_space_proc (void);
+
+/*
+    Detects when IPs are really loaded into phy mem and installs probes.
+*/
+extern void do_page_fault_ret_pre_code (void);
+
+/*
+    Detects when target process exits and removes IPs.
+*/
+extern void do_exit_probe_pre_code (void);
+
+/*
+    Detects when target process is killed and removes IPs.
+*/
+//extern void send_sig_jprobe_event_handler (int sig, struct siginfo *info, struct task_struct *t, struct sigpending *signals);
+
+extern int us_proc_probes;
+
+#define US_PROC_PF_INSTLD      0x1
+#define US_PROC_EXIT_INSTLD    0x2
+//#define US_PROC_SS_INSTLD    0x4
+//#define US_PROC_FORK_INSTLD  0x8
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#endif /* !defined(__US_PROC_INST_H__) */
diff --git a/kprobe/Kbuild b/kprobe/Kbuild
new file mode 100644 (file)
index 0000000..8b87086
--- /dev/null
@@ -0,0 +1,14 @@
+include $(project_pwd)/common.mk
+
+program_name := $(kprobe_name)
+source_dirs := $(project_pwd)/$(src_path)/$(kprobe_path)
+include_dirs := 
+include_libs := 
+special_defines := CONFIG_KPROBES
+ifeq ($(specific_target), chelsea)
+    special_defines += KERNEL_HAS_ISPAGEPRESENT
+endif
+special_compiling_flags := 
+special_linking_flags := 
+
+include $(project_pwd)/rules-m.mk
diff --git a/kprobe/Makefile b/kprobe/Makefile
new file mode 100644 (file)
index 0000000..0da2ef7
--- /dev/null
@@ -0,0 +1,16 @@
+include $(project_pwd)/common.mk
+
+help:
+       @$(MAKE) ARCH=$(target_cpu) CROSS_COMPILE=$(target_compiler_prefix) -C $(target_kernel_src) \
+               M=$(project_pwd)/$(src_path)/$(kprobe_path) \
+               project_pwd=$(project_pwd) help
+
+build:
+       @$(MAKE) ARCH=$(target_cpu) CROSS_COMPILE=$(target_compiler_prefix) -C $(target_kernel_src) \
+               M=$(project_pwd)/$(src_path)/$(kprobe_path) \
+               project_pwd=$(project_pwd) modules
+
+clean:
+       @$(MAKE) ARCH=$(target_cpu) CROSS_COMPILE=$(target_compiler_prefix) -C $(target_kernel_src) \
+               M=$(project_pwd)/$(src_path)/$(kprobe_path) \
+               project_pwd=$(project_pwd) clean
diff --git a/kprobe/README b/kprobe/README
new file mode 100644 (file)
index 0000000..6f25067
--- /dev/null
@@ -0,0 +1,36 @@
+########################################
+## ELP2 KProbe Dynamically Loaded Module
+########################################
+
+1. Overview
+       ELP2 KProbe Module provides dynamic enabling of KProbe facilities on the current platform,
+       eliminating the necessity of according Linux kernel patches.
+       Supports the following architectures:
+               a) ARM (OMAP 5912 OSK, MV320-LCD)
+               b) MIPS (DTV x260, DMB2 AMD)
+       Runs on Linux kernel 2.6.10 - 2.6.21.
+
+2. Installation
+       a) Extract files from installation archive "elp2-kprobe-dynamic.tgz"
+               into desired location.
+       b) Find the following files:
+               asm/kprobe.h    - architecture specific header file
+               kprobes_arch.c  - architecture specific source code
+               kprobe.h        - header file
+               kprobes.c       - source code
+               build_arm       - example of how to compile the module for OMAP 5912 OSK
+               build_cosmos    - example of how to compile the module for MV320-LCD
+               build_dtv_x260  - example of how to compile the module for DTV x260
+               build_mips      - example of how to compile the module for DMB2 AMD
+               README          - this file
+       c) Execute the following command to compile the module:
+               make ARCH=[arm, mips] CROSS_COMPILE=[/opt/...] KERNELDIR=[/opt/...]
+       d) Copy the output file "elp_kprobes.ko" to the target filesystem.
+
+3. Usage
+       a) Find out the address [XXX] of function "kallsyms_lookup_name":
+               cat /proc/kallsyms > kallsyms.txt
+       b) Execute the pointed command to load the module into memory:
+               insmod elp_kprobes.ko ksyms=0x[XXX]
+
+       Now, KProbe facilities are enabled to requestors.
diff --git a/kprobe/asm/kprobes.h b/kprobe/asm/kprobes.h
new file mode 100644 (file)
index 0000000..2caa623
--- /dev/null
@@ -0,0 +1,128 @@
+// src_asm_kprobes.h
+#ifndef _SRC_ASM_KPROBES_H
+#define _SRC_ASM_KPROBES_H
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#include <linux/config.h>
+#endif
+
+//#define _DEBUG
+//#define KPROBES_RET_PROBE_TRAMP
+//#define KPROBES_PROFILE      
+
+
+#ifdef _DEBUG
+extern int gSilent;
+#define DBPRINTF(format, args...) do { \
+               if( !gSilent ){ \
+                       char *f = __FILE__; \
+                       char *n = strrchr(f, '/'); \
+                       printk("%s : %u : %s : " format "\n" , (n) ? n+1 : f, __LINE__, __FUNCTION__, ##args); \
+               } \
+       } while(0)
+#else
+#define DBPRINTF(format, args...)
+#endif
+
+#if defined(CONFIG_MIPS)
+typedef unsigned long kprobe_opcode_t;
+# define BREAKPOINT_INSTRUCTION        0x0000000d
+# ifndef KPROBES_RET_PROBE_TRAMP
+#  define UNDEF_INSTRUCTION            0x0000004d
+# endif
+#elif defined(CONFIG_ARM)
+typedef unsigned long kprobe_opcode_t;
+# ifdef CONFIG_CPU_S3C2443
+#  define BREAKPOINT_INSTRUCTION       0xe1200070
+# else
+#  define BREAKPOINT_INSTRUCTION       0xffffffff
+# endif
+# ifndef KPROBES_RET_PROBE_TRAMP
+#  ifdef CONFIG_CPU_S3C2443
+#   define UNDEF_INSTRUCTION           0xe1200071
+#  else
+#   define UNDEF_INSTRUCTION           0xfffffffe
+#  endif
+# endif
+#elif defined(CONFIG_X86)
+typedef u8 kprobe_opcode_t;
+# define BREAKPOINT_INSTRUCTION        0xcc
+# define RELATIVEJUMP_INSTRUCTION 0xe9
+/*# define UNDEF_INSTRUCTION   0xff
+# warning UNDEF_INSTRUCTION is not defined for x86 arch!!!*/
+#else
+# error BREAKPOINT_INSTRUCTION is not defined for this arch!!!
+# error UNDEF_INSTRUCTION is not defined for this arch!!!
+#endif // ARCH
+
+#if defined(CONFIG_X86)
+# define MAX_INSN_SIZE 16
+# define MAX_STACK_SIZE 64
+# define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \
+       (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) \
+       ? (MAX_STACK_SIZE) \
+       : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+# define EREG(rg)              e##rg
+# define XREG(rg)              x##rg
+# define ORIG_EAX_REG  orig_eax
+#else
+# define EREG(rg)              rg
+# define XREG(rg)              rg
+# define ORIG_EAX_REG  orig_ax
+#endif
+#else//non x86
+# define MAX_INSN_SIZE 1
+#endif
+
+#define flush_insn_slot(p)     do { } while (0)
+
+#define kprobe_lookup_name(name, addr)
+
+/* Architecture specific copy of original instruction */
+struct arch_specific_insn {
+       /* copy of the original instruction */
+       kprobe_opcode_t *insn;
+       /*
+        * If this flag is not 0, this kprobe can be boost when its
+        * post_handler and break_handler is not set.
+        */
+       int boostable;
+};     
+
+#define JPROBE_ENTRY(pentry)   (kprobe_opcode_t *)pentry
+
+struct prev_kprobe {
+       struct kprobe *kp;
+       unsigned long status;
+#if defined(CONFIG_X86)
+       unsigned long old_eflags;
+       unsigned long saved_eflags;
+#endif
+};
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+       unsigned long kprobe_status;
+       struct prev_kprobe prev_kprobe;
+#if defined(CONFIG_X86)
+       struct pt_regs jprobe_saved_regs;
+       unsigned long kprobe_old_eflags;
+       unsigned long kprobe_saved_eflags;
+       unsigned long *jprobe_saved_esp;
+       kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE];
+#endif
+};
+
+void kretprobe_trampoline (void);
+
+extern int kprobe_handler (struct pt_regs *regs);
+extern int page_present (struct mm_struct *mm, unsigned long addr);
+
+#if defined(CONFIG_X86)
+extern int kprobe_exceptions_notify (struct notifier_block *self, unsigned long val, void *data);
+#endif // CONFIG_X86
+
+#endif                         /* _SRC_ASM_KPROBES_H */
diff --git a/kprobe/inperfa_kprobe.sh b/kprobe/inperfa_kprobe.sh
new file mode 100755 (executable)
index 0000000..d37d72f
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+SWAP_KPROBE=inperfa_kprobe
+KSYMS=kallsyms_lookup_name
+
+# ADDRESS for "kallsyms_lookup_name" function taken from /proc/kallsyms
+ADDRESS=0x`sed "/ kallsyms_lookup_name/ ! d" /proc/kallsyms | sed "s/ T kallsyms_lookup_name//"`
+
+if [ "${ADDRESS}" = "0x" ]; then
+    if [ "$1" = "" ]; then
+       echo "Enter kallsyms_lookup_name as parameter:"
+       echo "inperfa_kprobe.sh <kallsyms_lookup_name address>"
+       exit 1
+    else
+       ADDRESS=$1
+       echo "kallsyms_lookup_name address is ${ADDRESS}"
+    fi
+fi
+
+# Check for running module in /proc/modules
+RUNNING=`sed "/${SWAP_KPROBE}/ ! d" /proc/modules`
+
+if [ "${RUNNING}" = "" ]; then
+    insmod ${SWAP_KPROBE}.ko ksyms=${ADDRESS}
+    if [ $? -ne 0 ]; then
+         echo "Error: unable to load Inperfa KProbe!"
+    fi
+else
+       echo "Inperfa Kprobe is already running!"
+       exit 1
+fi
diff --git a/kprobe/kprobes.c b/kprobe/kprobes.c
new file mode 100644 (file)
index 0000000..7e5a524
--- /dev/null
@@ -0,0 +1,1744 @@
+// src_kprobes.c
+
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#include <linux/config.h>
+#endif
+
+#include <asm/types.h>
+
+#include <linux/hash.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleloader.h>
+#include <linux/kallsyms.h>
+//#include <linux/freezer.h>
+#include <linux/seq_file.h>
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+#include <asm-generic/sections.h>
+#include <asm/cacheflush.h>
+#include <asm/errno.h>
+#include <linux/spinlock.h>
+#include <linux/version.h>
+#include <linux/highmem.h>     // kmap_atomic, kunmap_atomic, copy_from_user_page, copy_to_user_page
+#include <linux/pagemap.h>     // page_cache_release
+#include <linux/vmalloc.h>     // vmalloc, vfree
+#if defined(CONFIG_X86)
+#include <linux/kdebug.h>      // register_die_notifier, unregister_die_notifier
+#endif
+#include <linux/hugetlb.h>     // follow_hugetlb_page, is_vm_hugetlb_page
+
+#include "kprobes.h"
+
+//#define arch_remove_kprobe(p)        do { } while (0)
+
+#ifdef _DEBUG
+extern int nCount;
+#endif
+
+/*
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
+
+int src_register_die_notifier(struct notifier_block *nb)
+{
+       int err = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&die_notifier_lock, flags);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
+       err = atomic_notifier_chain_register(&panic_notifier_list, nb);
+#else
+       err = notifier_chain_register(&panic_notifier_list, nb);
+#endif
+       spin_unlock_irqrestore(&die_notifier_lock, flags);
+
+       return err;
+}
+*/
+/**
+ * hlist_replace_rcu - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * The @old entry will be replaced with the @new entry atomically.
+ */
+static inline void
+src_hlist_replace_rcu (struct hlist_node *old, struct hlist_node *new)
+{
+       struct hlist_node *next = old->next;
+
+       new->next = next;
+       new->pprev = old->pprev;
+       smp_wmb ();
+       if (next)
+               new->next->pprev = &new->next;
+       if (new->pprev)
+               *new->pprev = new;
+       old->pprev = LIST_POISON2;
+}
+
+#define KPROBE_HASH_BITS 6
+#define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)
+
+
+/*
+ * Some oddball architectures like 64bit powerpc have function descriptors
+ * so this must be overridable.
+ */
+#ifndef kprobe_lookup_name
+#define kprobe_lookup_name(name, addr) \
+       addr = ((kprobe_opcode_t *)(kallsyms_lookup_name(name)))
+#endif
+
+static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE];
+static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
+static struct hlist_head uprobe_insn_slot_table[KPROBE_TABLE_SIZE];
+static atomic_t kprobe_count;
+
+//DEFINE_MUTEX(kprobe_mutex);           /* Protects kprobe_table */
+DEFINE_SPINLOCK (kretprobe_lock);      /* Protects kretprobe_inst_table */
+static DEFINE_PER_CPU (struct kprobe *, kprobe_instance) = NULL;
+unsigned long handled_exceptions;
+
+/* We have preemption disabled.. so it is safe to use __ versions */
+static inline void
+set_kprobe_instance (struct kprobe *kp)
+{
+       __get_cpu_var (kprobe_instance) = kp;
+}
+
+static inline void
+reset_kprobe_instance (void)
+{
+       __get_cpu_var (kprobe_instance) = NULL;
+}
+
+/*
+ * This routine is called either:
+ *     - under the kprobe_mutex - during kprobe_[un]register()
+ *                             OR
+ *     - with preemption disabled - from arch/xxx/kernel/kprobes.c
+ */
+struct kprobe __kprobes *
+get_kprobe (void *addr, int tgid, struct task_struct *ctask)
+{
+       struct hlist_head *head;
+       struct hlist_node *node;
+       struct kprobe *p, *retVal = NULL;
+       int ret = 0, uprobe_found;
+       struct page *page = 0, *tpage = 0;
+       struct vm_area_struct *vma = 0;
+       struct task_struct *task = 0;
+       void *paddr = 0;
+
+
+       if (ctask && ctask->active_mm)
+       {
+               ret = get_user_pages_atomic (ctask, ctask->active_mm, (unsigned long) addr, 1, 0, 0, &tpage, NULL);
+               if (ret <= 0)
+                       DBPRINTF ("get_user_pages for task %d at %p failed!", current->pid, addr);
+               else
+               {
+                       paddr = page_address (tpage);
+                       page_cache_release (tpage);
+               }
+       }
+       //else
+       //      DBPRINTF("task %d has no mm!", ctask->pid);
+
+       //TODO: test - two processes invokes instrumented function
+       head = &kprobe_table[hash_ptr (addr, KPROBE_HASH_BITS)];
+       hlist_for_each_entry_rcu (p, node, head, hlist)
+       {
+               //if looking for kernel probe and this is kernel probe with the same addr OR
+               //if looking for the user space probe and this is user space probe probe with the same addr and pid
+               DBPRINTF ("get_kprobe[%d]: check probe at %p/%p, task %d/%d", nCount, addr, p->addr, tgid, p->tgid);
+               if (p->addr == addr)
+               {
+                       uprobe_found = 0;
+                       if (tgid == p->tgid)
+                               uprobe_found = 1;
+                       if (!tgid || uprobe_found)
+                       {
+                               retVal = p;
+                               if (tgid)
+                                       DBPRINTF ("get_kprobe[%d]: found user space probe at %p for task %d", nCount, p->addr, p->tgid);
+                               else
+                                       DBPRINTF ("get_kprobe[%d]: found kernel probe at %p", nCount, p->addr);
+                               break;
+                       }
+               }
+               else if (tgid != p->tgid)
+               {
+                       // if looking for the user space probe and this is user space probe 
+                       // with another addr and pid but with the same offset whithin the page
+                       // it could be that it is the same probe (with address from other user space)
+                       // we should handle it as usual probe but without notification to user 
+                       if (paddr && tgid && (((unsigned long) addr & ~PAGE_MASK) == ((unsigned long) p->addr & ~PAGE_MASK))
+                           && p->tgid)
+                       {
+                               DBPRINTF ("get_kprobe[%d]: found user space probe at %p in task %d. possibly for addr %p in task %d", nCount, p->addr, p->tgid, addr, tgid);
+                               // this probe has the same offset in the page
+                               // look in the probes for the other pids                                
+                               // get page for user space probe addr
+                               rcu_read_lock ();
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
+                               task = find_task_by_pid (p->tgid);
+#else //lif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+                               task = pid_task(find_pid_ns(p->tgid, &init_pid_ns), PIDTYPE_PID);
+#endif
+                               if (task)
+                                       get_task_struct (task);
+                               rcu_read_unlock ();
+                               if (!task)
+                               {
+                                       DBPRINTF ("task for pid %d not found! Dead probe?", p->tgid);
+                                       continue;
+                               }
+                               if (task->active_mm)
+                               {
+                                       if (page_present (task->active_mm, (unsigned long) p->addr))
+                                       {
+                                               ret = get_user_pages_atomic (task, task->active_mm, (unsigned long) p->addr, 1, 0, 0, &page, &vma);
+                                               if (ret <= 0)
+                                                       DBPRINTF ("get_user_pages for task %d at %p failed!", p->tgid, p->addr);
+                                       }
+                                       else
+                                               ret = -1;
+                               }
+                               else
+                               {
+                                       DBPRINTF ("task %d has no mm!", task->pid);
+                                       ret = -1;
+                               }
+                               put_task_struct (task);
+                               if (ret <= 0)
+                                       continue;
+                               if (paddr == page_address (page))
+                               {
+                                       retVal = p;     // we found the probe in other process address space
+                                       DBPRINTF ("get_kprobe[%d]: found user space probe at %p in task %d for addr %p in task %d", nCount, p->addr, p->tgid, addr, tgid);
+                                       panic ("user space probe from another process");
+                               }
+                               page_cache_release (page);
+                               if (retVal)
+                                       break;
+                       }
+               }
+       }
+
+       DBPRINTF ("get_kprobe[%d]: probe %p", nCount, retVal);
+       return retVal;
+}
+
+struct kprobe __kprobes *
+get_kprobe_by_insn_slot (void *addr, int tgid, struct task_struct *ctask)
+{
+       struct hlist_head *head;
+       struct hlist_node *node;
+       struct kprobe *p, *retVal = NULL;
+       int uprobe_found;
+
+       //TODO: test - two processes invokes instrumented function
+       head = &uprobe_insn_slot_table[hash_ptr (addr, KPROBE_HASH_BITS)];
+       hlist_for_each_entry_rcu (p, node, head, is_hlist)
+       {
+               //if looking for kernel probe and this is kernel probe with the same addr OR
+               //if looking for the user space probe and this is user space probe probe with the same addr and pid
+               DBPRINTF ("get_kprobe[%d]: check probe at %p/%p, task %d/%d", nCount, addr, p->ainsn.insn, tgid, p->tgid);
+               if (p->ainsn.insn == addr)
+               {
+                       uprobe_found = 0;
+                       if (tgid == p->tgid)
+                               uprobe_found = 1;
+                       if (!tgid || uprobe_found)
+                       {
+                               retVal = p;
+                               if (tgid)
+                                       DBPRINTF ("get_kprobe[%d]: found user space probe at %p for task %d", nCount, p->addr, p->tgid);
+                               else
+                                       DBPRINTF ("get_kprobe[%d]: found kernel probe at %p", nCount, p->addr);
+                               break;
+                       }
+               }
+       }
+
+       DBPRINTF ("get_kprobe[%d]: probe %p", nCount, retVal);
+       return retVal;
+}
+
+/*
+ * Aggregate handlers for multiple kprobes support - these handlers
+ * take care of invoking the individual kprobe handlers on p->list
+ */
+static int __kprobes
+aggr_pre_handler (struct kprobe *p, struct pt_regs *regs       /*, 
+                                                                  struct vm_area_struct **vma, 
+                                                                  struct page **page, unsigned long **kaddr */ )
+{
+       struct kprobe *kp;
+       int ret;
+
+       list_for_each_entry_rcu (kp, &p->list, list)
+       {
+               if (kp->pre_handler)
+               {
+                       set_kprobe_instance (kp);
+                       ret = kp->pre_handler (kp, regs);
+                       if (ret)
+                               return ret;
+               }
+               reset_kprobe_instance ();
+       }
+       return 0;
+}
+
+static void __kprobes
+aggr_post_handler (struct kprobe *p, struct pt_regs *regs, unsigned long flags)
+{
+       struct kprobe *kp;
+
+       list_for_each_entry_rcu (kp, &p->list, list)
+       {
+               if (kp->post_handler)
+               {
+                       set_kprobe_instance (kp);
+                       kp->post_handler (kp, regs, flags);
+                       reset_kprobe_instance ();
+               }
+       }
+       return;
+}
+
+#if 1
+static int __kprobes
+aggr_fault_handler (struct kprobe *p, struct pt_regs *regs, int trapnr)
+{
+       struct kprobe *cur = __get_cpu_var (kprobe_instance);
+
+       /*
+        * if we faulted "during" the execution of a user specified
+        * probe handler, invoke just that probe's fault handler
+        */
+       if (cur && cur->fault_handler)
+       {
+               if (cur->fault_handler (cur, regs, trapnr))
+                       return 1;
+       }
+       return 0;
+}
+#endif
+
+static int __kprobes
+aggr_break_handler (struct kprobe *p, struct pt_regs *regs     /*, 
+                                                                  struct vm_area_struct **vma, 
+                                                                  struct page **page, unsigned long **kaddr */ )
+{
+       struct kprobe *cur = __get_cpu_var (kprobe_instance);
+       int ret = 0;
+       DBPRINTF ("cur = 0x%p\n", cur);
+       if (cur)
+               DBPRINTF ("cur = 0x%p cur->break_handler = 0x%p\n", cur, cur->break_handler);
+
+       if (cur && cur->break_handler)
+       {
+               if (cur->break_handler (cur, regs /*, vma, page, kaddr */ ))
+                       ret = 1;
+       }
+       reset_kprobe_instance ();
+       return ret;
+}
+
+/* Walks the list and increments nmissed count for multiprobe case */
+void __kprobes
+kprobes_inc_nmissed_count (struct kprobe *p)
+{
+       struct kprobe *kp;
+       if (p->pre_handler != aggr_pre_handler)
+       {
+               p->nmissed++;
+       }
+       else
+       {
+               list_for_each_entry_rcu (kp, &p->list, list) kp->nmissed++;
+       }
+       return;
+}
+
+/* Called with kretprobe_lock held */
+struct kretprobe_instance __kprobes *
+get_free_rp_inst (struct kretprobe *rp)
+{
+       struct hlist_node *node;
+       struct kretprobe_instance *ri;
+       hlist_for_each_entry (ri, node, &rp->free_instances, uflist) 
+               return ri;
+       return NULL;
+}
+
+/* Called with kretprobe_lock held */
+static struct kretprobe_instance __kprobes *
+get_used_rp_inst (struct kretprobe *rp)
+{
+       struct hlist_node *node;
+       struct kretprobe_instance *ri;
+       hlist_for_each_entry (ri, node, &rp->used_instances, uflist) return ri;
+       return NULL;
+}
+
+/* Called with kretprobe_lock held */
+void __kprobes
+add_rp_inst (struct kretprobe_instance *ri)
+{
+       /*
+        * Remove rp inst off the free list -
+        * Add it back when probed function returns
+        */
+       hlist_del (&ri->uflist);
+
+       /* Add rp inst onto table */
+       INIT_HLIST_NODE (&ri->hlist);
+       hlist_add_head (&ri->hlist, &kretprobe_inst_table[hash_ptr (ri->task, KPROBE_HASH_BITS)]);
+
+       /* Also add this rp inst to the used list. */
+       INIT_HLIST_NODE (&ri->uflist);
+       hlist_add_head (&ri->uflist, &ri->rp->used_instances);
+}
+
+/* Called with kretprobe_lock held */
+void __kprobes
+recycle_rp_inst (struct kretprobe_instance *ri, struct hlist_head *head)
+{
+       /* remove rp inst off the rprobe_inst_table */
+       hlist_del (&ri->hlist);
+       if (ri->rp)
+       {
+               /* remove rp inst off the used list */
+               hlist_del (&ri->uflist);
+               /* put rp inst back onto the free list */
+               INIT_HLIST_NODE (&ri->uflist);
+               hlist_add_head (&ri->uflist, &ri->rp->free_instances);
+       }
+       else
+               /* Unregistering */
+               hlist_add_head (&ri->hlist, head);
+}
+
+struct hlist_head __kprobes *
+kretprobe_inst_table_head (struct task_struct *tsk)
+{
+       return &kretprobe_inst_table[hash_ptr (tsk, KPROBE_HASH_BITS)];
+}
+
+/*
+ * This function is called from finish_task_switch when task tk becomes dead,
+ * so that we can recycle any function-return probe instances associated
+ * with this task. These left over instances represent probed functions
+ * that have been called but will never return.
+ */
+/*void __kprobes kprobe_flush_task(struct task_struct *tk)
+{
+       struct kretprobe_instance *ri;
+       struct hlist_head *head, empty_rp;
+       struct hlist_node *node, *tmp;
+       unsigned long flags = 0;
+
+       INIT_HLIST_HEAD(&empty_rp);
+       spin_lock_irqsave(&kretprobe_lock, flags);
+       head = kretprobe_inst_table_head(tk);
+       hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+               if (ri->task == tk)
+                       recycle_rp_inst(ri, &empty_rp);
+       }
+       spin_unlock_irqrestore(&kretprobe_lock, flags);
+
+       hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
+               hlist_del(&ri->hlist);
+               kfree(ri);
+       }
+}*/
+
+static inline void
+free_rp_inst (struct kretprobe *rp)
+{
+       struct kretprobe_instance *ri;
+       while ((ri = get_free_rp_inst (rp)) != NULL)
+       {
+               hlist_del (&ri->uflist);
+               kfree (ri);
+       }
+}
+
+/*
+ * Keep all fields in the kprobe consistent
+ */
+static inline void
+copy_kprobe (struct kprobe *old_p, struct kprobe *p)
+{
+       memcpy (&p->opcode, &old_p->opcode, sizeof (kprobe_opcode_t));
+       memcpy (&p->ainsn, &old_p->ainsn, sizeof (struct arch_specific_insn));
+       p->tgid = old_p->tgid;
+       p->ss_addr = old_p->ss_addr;
+       //p->spid = old_p->spid;
+}
+
+/*
+* Add the new probe to old_p->list. Fail if this is the
+* second jprobe at the address - two jprobes can't coexist
+*/
+static int __kprobes
+add_new_kprobe (struct kprobe *old_p, struct kprobe *p)
+{
+       if (p->break_handler)
+       {
+               if (old_p->break_handler)
+                       return -EEXIST;
+               list_add_tail_rcu (&p->list, &old_p->list);
+               old_p->break_handler = aggr_break_handler;
+       }
+       else
+               list_add_rcu (&p->list, &old_p->list);
+       if (p->post_handler && !old_p->post_handler)
+               old_p->post_handler = aggr_post_handler;
+       return 0;
+}
+
+/*
+ * Fill in the required fields of the "manager kprobe". Replace the
+ * earlier kprobe in the hlist with the manager kprobe
+ */
+static inline void
+add_aggr_kprobe (struct kprobe *ap, struct kprobe *p)
+{
+       copy_kprobe (p, ap);
+       flush_insn_slot (ap);
+       ap->addr = p->addr;
+       ap->pre_handler = aggr_pre_handler;
+       ap->fault_handler = aggr_fault_handler;
+       if (p->post_handler)
+               ap->post_handler = aggr_post_handler;
+       if (p->break_handler)
+               ap->break_handler = aggr_break_handler;
+
+       INIT_LIST_HEAD (&ap->list);
+       list_add_rcu (&p->list, &ap->list);
+
+       src_hlist_replace_rcu (&p->hlist, &ap->hlist);
+}
+
+/*
+ * This is the second or subsequent kprobe at the address - handle
+ * the intricacies
+ */
+static int __kprobes
+register_aggr_kprobe (struct kprobe *old_p, struct kprobe *p)
+{
+       int ret = 0;
+       struct kprobe *ap;
+       DBPRINTF ("start\n");
+
+       DBPRINTF ("p = %p old_p = %p \n", p, old_p);
+       if (old_p->pre_handler == aggr_pre_handler)
+       {
+               DBPRINTF ("aggr_pre_handler \n");
+
+               copy_kprobe (old_p, p);
+               ret = add_new_kprobe (old_p, p);
+       }
+       else
+       {
+               DBPRINTF ("kzalloc\n");
+
+#ifdef kzalloc
+               ap = kzalloc (sizeof (struct kprobe), GFP_KERNEL);
+#else
+               ap = kmalloc (sizeof (struct kprobe), GFP_KERNEL);
+               if (ap)
+                       memset (ap, 0, sizeof (struct kprobe));
+#endif
+               if (!ap)
+                       return -ENOMEM;
+               add_aggr_kprobe (ap, old_p);
+               copy_kprobe (ap, p);
+               DBPRINTF ("ap = %p p = %p old_p = %p \n", ap, p, old_p);
+               ret = add_new_kprobe (ap, p);
+       }
+       return ret;
+}
+
+static int __kprobes
+__register_kprobe (struct kprobe *p, unsigned long called_from, int atomic)
+{
+       struct kprobe *old_p;
+//      struct module *probed_mod;
+       int ret = 0;
+       /*
+        * If we have a symbol_name argument look it up,
+        * and add it to the address.  That way the addr
+        * field can either be global or relative to a symbol.
+        */
+       if (p->symbol_name)
+       {
+               if (p->addr)
+                       return -EINVAL;
+               kprobe_lookup_name (p->symbol_name, p->addr);
+       }
+
+       if (!p->addr)
+               return -EINVAL;
+       DBPRINTF ("p->addr = 0x%p\n", p->addr);
+       p->addr = (kprobe_opcode_t *) (((char *) p->addr) + p->offset);
+       DBPRINTF ("p->addr = 0x%p p = 0x%p\n", p->addr, p);
+
+/*     if ((!kernel_text_address((unsigned long) p->addr)) ||
+               in_kprobes_functions((unsigned long) p->addr))
+               return -EINVAL;*/
+
+#ifdef KPROBES_PROFILE
+       p->start_tm.tv_sec = p->start_tm.tv_usec = 0;
+       p->hnd_tm_sum.tv_sec = p->hnd_tm_sum.tv_usec = 0;
+       p->count = 0;
+#endif
+       p->mod_refcounted = 0;
+       //p->proc_prio = 0;
+       //p->proc_sched = 0;    
+       //p->spid = -1;
+       //p->irq = 0;
+       //p->task_flags = 0;
+/*
+       // Check are we probing a module
+       if ((probed_mod = module_text_address((unsigned long) p->addr))) {
+               struct module *calling_mod = module_text_address(called_from);
+               // We must allow modules to probe themself and
+               // in this case avoid incrementing the module refcount,
+               // so as to allow unloading of self probing modules.
+               //
+               if (calling_mod && (calling_mod != probed_mod)) {
+                       if (unlikely(!try_module_get(probed_mod)))
+                               return -EINVAL;
+                       p->mod_refcounted = 1;
+               } else
+                       probed_mod = NULL;
+       }
+*/
+       p->nmissed = 0;
+//      mutex_lock(&kprobe_mutex);
+       old_p = get_kprobe (p->addr, 0, NULL);
+       if (old_p)
+       {
+               ret = register_aggr_kprobe (old_p, p);
+               if (!ret)
+                       atomic_inc (&kprobe_count);
+               goto out;
+       }
+
+       if ((ret = arch_prepare_kprobe (p)) != 0)
+               goto out;
+
+       DBPRINTF ("before out ret = 0x%x\n", ret);
+
+       INIT_HLIST_NODE (&p->hlist);
+       hlist_add_head_rcu (&p->hlist, &kprobe_table[hash_ptr (p->addr, KPROBE_HASH_BITS)]);
+
+/*     if (atomic_add_return(1, &kprobe_count) == \
+                               (ARCH_INACTIVE_KPROBE_COUNT + 1))
+               register_page_fault_notifier(&kprobe_page_fault_nb);*/
+
+       arch_arm_kprobe (p);
+
+      out:
+//      mutex_unlock(&kprobe_mutex);
+/*
+       if (ret && probed_mod)
+               module_put(probed_mod);
+*/
+       DBPRINTF ("out ret = 0x%x\n", ret);
+
+       return ret;
+}
+
+static int __kprobes
+__register_uprobe (struct kprobe *p, struct task_struct *task, int atomic, unsigned long called_from)
+{
+       int ret = 0;
+       struct kprobe *old_p;
+
+       if (!p->addr)
+               return -EINVAL;
+
+       DBPRINTF ("p->addr = 0x%p p = 0x%p\n", p->addr, p);
+
+       p->mod_refcounted = 0;
+       p->nmissed = 0;
+#ifdef KPROBES_PROFILE
+       p->start_tm.tv_sec = p->start_tm.tv_usec = 0;
+       p->hnd_tm_sum.tv_sec = p->hnd_tm_sum.tv_usec = 0;
+       p->count = 0;
+#endif
+
+       // get the first item
+       old_p = get_kprobe (p->addr, p->tgid, NULL);
+       if (old_p)
+       {
+               ret = register_aggr_kprobe (old_p, p);
+               if (!ret)
+                       atomic_inc (&kprobe_count);
+               goto out;
+       }
+       if ((ret = arch_prepare_uprobe (p, task, atomic)) != 0)
+       {
+               goto out;
+       }
+
+       DBPRINTF ("before out ret = 0x%x\n", ret);
+
+       INIT_HLIST_NODE (&p->hlist);
+       hlist_add_head_rcu (&p->hlist, &kprobe_table[hash_ptr (p->addr, KPROBE_HASH_BITS)]);
+
+       INIT_HLIST_NODE (&p->is_hlist);
+       hlist_add_head_rcu (&p->is_hlist, &uprobe_insn_slot_table[hash_ptr (p->ainsn.insn, KPROBE_HASH_BITS)]);
+
+       arch_arm_uprobe (p, task);
+out:
+       DBPRINTF ("out ret = 0x%x\n", ret);
+
+       return ret;
+}
+
+void __kprobes
+unregister_uprobe (struct kprobe *p, struct task_struct *task, int atomic)
+{
+       unregister_kprobe (p, task, atomic);
+}
+
+int __kprobes
+register_kprobe (struct kprobe *p, int atomic)
+{
+       return __register_kprobe (p, (unsigned long) __builtin_return_address (0), atomic);
+}
+
+void __kprobes
+unregister_kprobe (struct kprobe *p, struct task_struct *task, int atomic)
+{
+//      struct module *mod;
+       struct kprobe *old_p, *list_p;
+       int cleanup_p, pid = 0;
+
+//      mutex_lock(&kprobe_mutex);
+
+       pid = p->tgid;
+
+       old_p = get_kprobe (p->addr, pid, NULL);
+       DBPRINTF ("unregister_kprobe p=%p old_p=%p", p, old_p);
+       if (unlikely (!old_p))
+       {
+//              mutex_unlock(&kprobe_mutex);
+               return;
+       }
+       if (p != old_p)
+       {
+               list_for_each_entry_rcu (list_p, &old_p->list, list) 
+                       if (list_p == p)
+                               /* kprobe p is a valid probe */
+                               goto valid_p;
+//              mutex_unlock(&kprobe_mutex);
+               return;
+       }
+valid_p:
+       DBPRINTF ("unregister_kprobe valid_p");
+       if ((old_p == p) || ((old_p->pre_handler == aggr_pre_handler) && 
+               (p->list.next == &old_p->list) && (p->list.prev == &old_p->list)))
+       {
+               /* Only probe on the hash list */
+               DBPRINTF ("unregister_kprobe disarm pid=%d", pid);
+               if (pid)
+                       arch_disarm_uprobe (p, task);//vma, page, kaddr);
+               else
+                       arch_disarm_kprobe (p);
+               hlist_del_rcu (&old_p->hlist);
+               cleanup_p = 1;
+       }
+       else
+       {
+               list_del_rcu (&p->list);
+               cleanup_p = 0;
+       }
+       DBPRINTF ("unregister_kprobe cleanup_p=%d", cleanup_p);
+//      mutex_unlock(&kprobe_mutex);
+
+//      synchronize_sched();
+/*
+       if (p->mod_refcounted &&
+           (mod = module_text_address((unsigned long)p->addr)))
+               module_put(mod);
+*/
+       if (cleanup_p)
+       {
+               if (p != old_p)
+               {
+                       list_del_rcu (&p->list);
+                       kfree (old_p);
+               }
+               arch_remove_kprobe (p, task);
+       }
+       else
+       {
+///             mutex_lock(&kprobe_mutex);
+               if (p->break_handler)
+                       old_p->break_handler = NULL;
+               if (p->post_handler)
+               {
+                       list_for_each_entry_rcu (list_p, &old_p->list, list)
+                       {
+                               if (list_p->post_handler)
+                               {
+                                       cleanup_p = 2;
+                                       break;
+                               }
+                       }
+                       if (cleanup_p == 0)
+                               old_p->post_handler = NULL;
+               }
+//              mutex_unlock(&kprobe_mutex);
+       }
+
+       /* Call unregister_page_fault_notifier()
+        * if no probes are active
+        */
+//      mutex_lock(&kprobe_mutex);
+/*     if (atomic_add_return(-1, &kprobe_count) == \
+                               ARCH_INACTIVE_KPROBE_COUNT)
+               unregister_page_fault_notifier(&kprobe_page_fault_nb);*/
+//      mutex_unlock(&kprobe_mutex);
+       return;
+}
+
+int __kprobes
+register_ujprobe (struct task_struct *task, struct mm_struct *mm, struct jprobe *jp, int atomic)
+{
+#ifdef _DEBUG
+       gSilent = 0;
+#endif
+       /* Todo: Verify probepoint is a function entry point */
+       jp->kp.pre_handler = setjmp_pre_handler;
+       jp->kp.break_handler = longjmp_break_handler;
+       
+       int ret = __register_uprobe (&jp->kp, task, atomic,
+                                   (unsigned long) __builtin_return_address (0));
+
+#ifdef _DEBUG
+       gSilent = 1;
+#endif
+       return ret;
+}
+
+void __kprobes
+unregister_ujprobe (struct task_struct *task, struct jprobe *jp, int atomic)
+{
+       unregister_uprobe (&jp->kp, task, atomic);
+}
+
+int __kprobes
+register_jprobe (struct jprobe *jp, int atomic)
+{
+       /* Todo: Verify probepoint is a function entry point */
+       jp->kp.pre_handler = setjmp_pre_handler;
+       jp->kp.break_handler = longjmp_break_handler;
+
+       return __register_kprobe (&jp->kp, (unsigned long) __builtin_return_address (0), atomic);
+}
+
+void __kprobes
+unregister_jprobe (struct jprobe *jp, int atomic)
+{
+       unregister_kprobe (&jp->kp, 0, atomic);
+}
+
+/*
+ * This kprobe pre_handler is registered with every kretprobe. When probe
+ * hits it will set up the return probe.
+ */
+static int __kprobes
+pre_handler_kretprobe (struct kprobe *p, struct pt_regs *regs  /*, struct vm_area_struct **vma, 
+                                                                  struct page **page, unsigned long **kaddr */ )
+{
+       struct kretprobe *rp = container_of (p, struct kretprobe, kp);
+       unsigned long flags = 0;
+       DBPRINTF ("START\n");
+
+       /*TODO: consider to only swap the RA after the last pre_handler fired */
+       spin_lock_irqsave (&kretprobe_lock, flags);
+       if (!rp->disarm)
+               __arch_prepare_kretprobe (rp, regs);
+       spin_unlock_irqrestore (&kretprobe_lock, flags);
+       DBPRINTF ("END\n");
+       return 0;
+}
+
+struct kretprobe *sched_rp;
+
+int __kprobes
+register_kretprobe (struct kretprobe *rp, int atomic)
+{
+       int ret = 0;
+       struct kretprobe_instance *inst;
+       int i;
+       DBPRINTF ("START");
+
+       rp->kp.pre_handler = pre_handler_kretprobe;
+       rp->kp.post_handler = NULL;
+       rp->kp.fault_handler = NULL;
+       rp->kp.break_handler = NULL;
+
+       rp->disarm = 0;
+
+       /* Pre-allocate memory for max kretprobe instances */
+       if(rp->kp.addr == sched_addr)
+               rp->maxactive = 1000;//max (100, 2 * NR_CPUS);
+       else if (rp->maxactive <= 0)
+       {
+#if 1//def CONFIG_PREEMPT
+               rp->maxactive = max (10, 2 * NR_CPUS);
+#else
+               rp->maxactive = NR_CPUS;
+#endif
+       }
+       INIT_HLIST_HEAD (&rp->used_instances);
+       INIT_HLIST_HEAD (&rp->free_instances);
+       for (i = 0; i < rp->maxactive; i++)
+       {
+               inst = kmalloc (sizeof (struct kretprobe_instance), GFP_KERNEL);
+               if (inst == NULL)
+               {
+                       free_rp_inst (rp);
+                       return -ENOMEM;
+               }
+               INIT_HLIST_NODE (&inst->uflist);
+               hlist_add_head (&inst->uflist, &rp->free_instances);
+       }
+
+       DBPRINTF ("addr=%p, *addr=[%lx %lx %lx]", rp->kp.addr, (unsigned long) (*(rp->kp.addr)), (unsigned long) (*(rp->kp.addr + 1)), (unsigned long) (*(rp->kp.addr + 2)));
+       rp->nmissed = 0;
+       /* Establish function entry probe point */
+       if ((ret = __register_kprobe (&rp->kp, (unsigned long) __builtin_return_address (0), atomic)) != 0)
+               free_rp_inst (rp);
+
+       DBPRINTF ("addr=%p, *addr=[%lx %lx %lx]", rp->kp.addr, (unsigned long) (*(rp->kp.addr)), (unsigned long) (*(rp->kp.addr + 1)), (unsigned long) (*(rp->kp.addr + 2)));
+       if(rp->kp.addr == sched_addr)
+               sched_rp = rp;
+
+       return ret;
+}
+
+void __kprobes
+unregister_kretprobe (struct kretprobe *rp, int atomic)
+{
+       unsigned long flags;
+       struct kretprobe_instance *ri;
+
+       //printk("addr=%p, *addr=[%lx %lx %lx]\n", rp->kp.addr, 
+       //               *(rp->kp.addr), *(rp->kp.addr+1), *(rp->kp.addr+2));
+       unregister_kprobe (&rp->kp, 0, atomic);
+
+       if(rp->kp.addr == sched_addr)
+               sched_rp = NULL;
+               
+       //printk("addr=%p, *addr=[%lx %lx %lx]\n", rp->kp.addr, 
+       //               *(rp->kp.addr), *(rp->kp.addr+1), *(rp->kp.addr+2));
+       /* No race here */
+       spin_lock_irqsave (&kretprobe_lock, flags);
+       while ((ri = get_used_rp_inst (rp)) != NULL)
+       {
+               ri->rp = NULL;
+               hlist_del (&ri->uflist);
+       }
+       spin_unlock_irqrestore (&kretprobe_lock, flags);
+       free_rp_inst (rp);
+}
+
+int __kprobes
+register_uretprobe (struct task_struct *task, struct mm_struct *mm, struct kretprobe *rp, int atomic)
+{
+       int ret = 0;
+       struct kretprobe_instance *inst;
+       /*struct page *pages[2] = {0, 0};
+          struct vm_area_struct *vmas[2] = {0, 0};
+          unsigned long *kaddrs[2] = {0, 0}; */
+       int i;
+#ifdef _DEBUG
+       gSilent = 0;
+#endif
+
+       DBPRINTF ("START\n");
+
+       rp->kp.pre_handler = pre_handler_kretprobe;
+       rp->kp.post_handler = NULL;
+       rp->kp.fault_handler = NULL;
+       rp->kp.break_handler = NULL;
+
+       rp->disarm = 0;
+
+       /* Pre-allocate memory for max kretprobe instances */
+       if (rp->maxactive <= 0)
+       {
+#if 1//def CONFIG_PREEMPT
+               rp->maxactive = max (10, 2 * NR_CPUS);
+#else
+               rp->maxactive = NR_CPUS;
+#endif
+       }
+       INIT_HLIST_HEAD (&rp->used_instances);
+       INIT_HLIST_HEAD (&rp->free_instances);
+       for (i = 0; i < rp->maxactive; i++)
+       {
+               inst = kmalloc (sizeof (struct kretprobe_instance), GFP_KERNEL);
+               if (inst == NULL)
+               {
+                       free_rp_inst (rp);
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               INIT_HLIST_NODE (&inst->uflist);
+               hlist_add_head (&inst->uflist, &rp->free_instances);
+       }
+
+       rp->nmissed = 0;
+#if 0
+       if (atomic)
+               ret = get_user_pages_atomic (task, mm, (unsigned long) rp->kp.addr, 1, 1, 1, &pages[0], &vmas[0]);
+       else
+               ret = get_user_pages (task, mm, (unsigned long) rp->kp.addr, 1, 1, 1, &pages[0], &vmas[0]);
+       if (ret <= 0)
+       {
+               DBPRINTF ("get_user_pages for %p failed!", rp->kp.addr);
+               ret = -EFAULT;
+               goto out;
+       }
+       if (atomic)
+               kaddrs[0] = kmap_atomic (pages[0], KM_USER0) + ((unsigned long) rp->kp.addr & ~PAGE_MASK);
+       else
+               kaddrs[0] = kmap (pages[0]) + ((unsigned long) rp->kp.addr & ~PAGE_MASK);
+       // if 2nd instruction is on the 2nd page
+       if ((((unsigned long) (rp->kp.addr + 1)) & ~PAGE_MASK) == 0)
+       {
+               if (atomic)
+                       ret = get_user_pages_atomic (task, mm, (unsigned long) (rp->kp.addr + 1), 1, 1, 1, &pages[1], &vmas[1]);
+               else
+                       ret = get_user_pages (task, mm, (unsigned long) (rp->kp.addr + 1), 1, 1, 1, &pages[1], &vmas[1]);
+               if (ret <= 0)
+               {
+                       DBPRINTF ("get_user_pages for %p failed!", rp->kp.addr + 1);
+                       ret = -EFAULT;
+                       goto out;
+               }
+               if (atomic)
+                       kaddrs[1] = kmap_atomic (pages[1], KM_USER1) + ((unsigned long) (rp->kp.addr + 1) & ~PAGE_MASK);
+               else
+                       kaddrs[1] = kmap (pages[1]) + ((unsigned long) (rp->kp.addr + 1) & ~PAGE_MASK);
+       }
+       else
+       {
+               // 2nd instruction is on the 1st page too
+               vmas[1] = vmas[0];
+               pages[1] = pages[0];
+               kaddrs[1] = kaddrs[0] + 1;
+       }
+#endif
+       /* Establish function exit probe point */
+       if ((ret = arch_prepare_uretprobe (rp, task/*vmas, pages, kaddrs */ )) != 0)
+               goto out;
+       /* Establish function entry probe point */
+       if ((ret = __register_uprobe (&rp->kp, task, atomic,
+                                        (unsigned long) __builtin_return_address (0))) != 0)
+       {
+               free_rp_inst (rp);
+               goto out;
+       }
+         
+       arch_arm_uretprobe (rp, task);//vmas[1], pages[1], kaddrs[1]);
+#if 0
+       if (atomic)
+               set_page_dirty (pages[1]);
+       else
+               set_page_dirty_lock (pages[1]);
+#endif
+      out:
+#if 0
+       if (pages[0])
+       {
+               if (kaddrs[0])
+               {
+                       if (atomic)
+                               kunmap_atomic (kaddrs[0] - ((unsigned long) rp->kp.addr & ~PAGE_MASK), KM_USER0);
+                       else
+                               kunmap (pages[0]);
+               }
+               page_cache_release (pages[0]);
+       }
+       if ((pages[0] != pages[1]))
+       {
+               if (pages[1])
+               {
+                       if (kaddrs[1])
+                       {
+                               if (atomic)
+                                       kunmap_atomic (kaddrs[1] - ((unsigned long) (rp->kp.addr + 1) & ~PAGE_MASK), KM_USER1);
+                               else
+                                       kunmap (pages[1]);
+                       }
+                       page_cache_release (pages[1]);
+               }
+       }
+       /*else if( (pages[0] != pages[2]) ){
+          if(pages[2]){
+          if(kaddrs[2]) {
+          if (atomic) kunmap_atomic(kaddrs[2], KM_USER1);
+          else        kunmap(pages[2]);
+          }
+          page_cache_release(pages[2]);
+          }
+          } */
+#endif
+
+#ifdef _DEBUG
+       gSilent = 1;
+#endif
+       return ret;
+}
+
+static struct kretprobe *__kprobes
+clone_kretprobe (struct kretprobe *rp)
+{
+       struct kprobe *old_p;
+       struct kretprobe *clone = NULL;
+       int ret;
+
+       clone = kmalloc (sizeof (struct kretprobe), GFP_KERNEL);
+       if (!clone)
+       {
+               DBPRINTF ("failed to alloc memory for clone probe %p!", rp->kp.addr);
+               return NULL;
+       }
+       memcpy (clone, rp, sizeof (struct kretprobe));
+       clone->kp.pre_handler = pre_handler_kretprobe;
+       clone->kp.post_handler = NULL;
+       clone->kp.fault_handler = NULL;
+       clone->kp.break_handler = NULL;
+       old_p = get_kprobe (rp->kp.addr, rp->kp.tgid, NULL);
+       if (old_p)
+       {
+               ret = register_aggr_kprobe (old_p, &clone->kp);
+               if (ret)
+               {
+                       kfree (clone);
+                       return NULL;
+               }
+               atomic_inc (&kprobe_count);
+       }
+
+       return clone;
+}
+
+void __kprobes
+unregister_uretprobe (struct task_struct *task, struct kretprobe *rp, int atomic)
+{
+       //int ret = 0;
+       unsigned long flags;
+       struct kretprobe_instance *ri;
+       struct kretprobe *rp2 = NULL;
+       /*struct mm_struct *mm;
+          struct page *pages[2] = {0, 0};
+          struct vm_area_struct *vmas[2] = {0, 0};
+          unsigned long *kaddrs[2] = {0, 0}; */
+
+#ifdef _DEBUG
+       gSilent = 0;
+#endif
+#if 0
+       mm = atomic ? task->active_mm : get_task_mm (task);
+       if (!mm)
+       {
+               DBPRINTF ("task %u has no mm!", task->pid);
+#ifdef _DEBUG
+               gSilent = 1;
+#endif
+               return;
+       }
+       if (atomic)
+               ret = get_user_pages_atomic (task, mm, (unsigned long) rp->kp.addr, 1, 1, 1, &pages[0], &vmas[0]);
+       else
+       {
+               down_read (&mm->mmap_sem);
+               ret = get_user_pages (task, mm, (unsigned long) rp->kp.addr, 1, 1, 1, &pages[0], &vmas[0]);
+       }
+       if (ret <= 0)
+       {
+               DBPRINTF ("get_user_pages for %p failed!", rp->kp.addr);
+               goto out;
+       }
+       if (atomic)
+               kaddrs[0] = kmap_atomic (pages[0], KM_USER0) + ((unsigned long) rp->kp.addr & ~PAGE_MASK);
+       else
+               kaddrs[0] = kmap (pages[0]) + ((unsigned long) rp->kp.addr & ~PAGE_MASK);
+       if ((((unsigned long) (rp->kp.addr + 1)) & ~PAGE_MASK) == 0)
+       {
+               if (atomic)
+                       ret = get_user_pages_atomic (task, mm, (unsigned long) (rp->kp.addr + 1), 1, 1, 1, &pages[1], &vmas[1]);
+               else
+                       ret = get_user_pages (task, mm, (unsigned long) (rp->kp.addr + 1), 1, 1, 1, &pages[1], &vmas[1]);
+               if (ret <= 0)
+               {
+                       DBPRINTF ("get_user_pages for %p failed!", rp->kp.addr + 1);
+                       goto out;
+               }
+               if (atomic)
+                       kaddrs[1] = kmap_atomic (pages[1], KM_USER1) + ((unsigned long) (rp->kp.addr + 1) & ~PAGE_MASK);
+               else
+                       kaddrs[1] = kmap (pages[1]) + ((unsigned long) (rp->kp.addr + 1) & ~PAGE_MASK);
+       }
+       else
+       {
+               vmas[1] = vmas[0];
+               pages[1] = pages[0];
+               kaddrs[1] = kaddrs[0] + 1;
+       }
+
+       /* No race here */
+       DBPRINTF ("unregister_uretprobe1 addr %p [%lx %lx]", rp->kp.addr, *kaddrs[0], *kaddrs[1]);
+#endif
+       spin_lock_irqsave (&kretprobe_lock, flags);
+       if (hlist_empty (&rp->used_instances))
+       {
+               // if there are no used retprobe instances (i.e. function is not entered) - disarm retprobe
+               arch_disarm_uretprobe (rp, task);//vmas[1], pages[1], kaddrs[1]);
+#if 0
+               if (atomic)
+                       set_page_dirty (pages[1]);
+               else
+                       set_page_dirty_lock (pages[1]);
+#endif
+       }
+       else
+       {
+               rp2 = clone_kretprobe (rp);
+               if (!rp2)
+                       DBPRINTF ("unregister_uretprobe addr %p: failed to clone retprobe!", rp->kp.addr);
+               else
+               {
+                       DBPRINTF ("initiating deferred retprobe deletion addr %p", rp->kp.addr);
+                       printk ("initiating deferred retprobe deletion addr %p\n", rp->kp.addr);
+                       rp2->disarm = 1;
+               }
+       }
+
+       while ((ri = get_used_rp_inst (rp)) != NULL)
+       {
+               ri->rp = NULL;
+               ri->rp2 = rp2;
+               hlist_del (&ri->uflist);
+       }
+       spin_unlock_irqrestore (&kretprobe_lock, flags);
+       free_rp_inst (rp);
+
+       unregister_uprobe (&rp->kp, task, atomic);
+       //DBPRINTF("unregister_uretprobe3 addr %p [%lx %lx]", 
+       //              rp->kp.addr, *kaddrs[0], *kaddrs[1]);
+#if 0
+      out:
+       if (pages[0])
+       {
+               if (kaddrs[0])
+               {
+                       if (atomic)
+                               kunmap_atomic (kaddrs[0] - ((unsigned long) rp->kp.addr & ~PAGE_MASK), KM_USER0);
+                       else
+                               kunmap (pages[0]);
+               }
+               page_cache_release (pages[0]);
+       }
+       if (pages[1] && (pages[0] != pages[1]))
+       {
+               if (kaddrs[1])
+               {
+                       if (atomic)
+                               kunmap_atomic (kaddrs[1] - ((unsigned long) (rp->kp.addr + 1) & ~PAGE_MASK), KM_USER1);
+                       else
+                               kunmap (pages[1]);
+               }
+               page_cache_release (pages[1]);
+       }
+       if (!atomic)
+       {
+               up_read (&mm->mmap_sem);
+               mmput (mm);
+       }
+#endif
+#ifdef _DEBUG
+       gSilent = 1;
+#endif
+}
+
+void __kprobes
+unregister_all_uprobes (struct task_struct *task, int atomic)
+{
+       struct hlist_head *head;
+       struct hlist_node *node, *tnode;
+       struct kprobe *p;
+       int i;
+
+       for(i = 0; i < KPROBE_TABLE_SIZE; i++){
+               head = &kprobe_table[i];
+               hlist_for_each_entry_safe (p, node, tnode, head, hlist){                        
+                       if(p->tgid == task->tgid){
+                               printk("unregister_all_uprobes: delete uprobe at %pf for %s/%d\n", p->addr, task->comm, task->pid);
+                               unregister_uprobe (p, task, atomic);
+                       }
+               }
+       }
+       purge_garbage_uslots(task, atomic);
+}
+
+#if 0
+int
+access_process_vm (struct task_struct *tsk, unsigned long addr, void *buf, int len, int write)
+{
+       struct mm_struct *mm;
+       struct vm_area_struct *vma;
+       struct page *page;
+       void *old_buf = buf;
+
+       mm = get_task_mm (tsk);
+       if (!mm)
+               return 0;
+
+       down_read (&mm->mmap_sem);
+       /* ignore errors, just check how much was sucessfully transfered */
+       while (len)
+       {
+               int bytes, ret, offset;
+               void *maddr;
+
+               ret = get_user_pages (tsk, mm, addr, 1, write, 1, &page, &vma);
+               if (ret <= 0)
+                       break;
+
+               bytes = len;
+               offset = addr & (PAGE_SIZE - 1);
+               if (bytes > PAGE_SIZE - offset)
+                       bytes = PAGE_SIZE - offset;
+
+               maddr = kmap (page);    //, KM_USER0);
+               if (write)
+               {
+                       copy_to_user_page (vma, page, addr, maddr + offset, buf, bytes);
+                       set_page_dirty_lock (page);
+               }
+               else
+               {
+                       copy_from_user_page (vma, page, addr, buf, maddr + offset, bytes);
+               }
+               kunmap (page);  //, KM_USER0);
+               page_cache_release (page);
+               len -= bytes;
+               buf += bytes;
+               addr += bytes;
+       }
+       up_read (&mm->mmap_sem);
+       mmput (mm);
+
+       return buf - old_buf;
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+const char *(*__real_kallsyms_lookup) (unsigned long addr, unsigned long *symbolsize, unsigned long *offset, char **modname, char *namebuf);
+const char *
+kallsyms_lookup (unsigned long addr, unsigned long *symbolsize, unsigned long *offset, char **modname, char *namebuf)
+{
+       return __real_kallsyms_lookup (addr, symbolsize, offset, modname, namebuf);
+}
+
+static void __kprobes
+report_probe (struct seq_file *pi, struct kprobe *p, const char *sym, int offset, char *modname)
+{
+       char *kprobe_type;
+
+       if (p->pre_handler == pre_handler_kretprobe)
+               if (p->tgid)
+                       kprobe_type = "ur";
+               else
+                       kprobe_type = "r";
+       else if (p->pre_handler == setjmp_pre_handler)
+               if (p->tgid)
+                       kprobe_type = "uj";
+               else
+                       kprobe_type = "j";
+       else if (p->tgid)
+               kprobe_type = "u";
+       else
+               kprobe_type = "k";
+       if (sym)
+               seq_printf (pi, "%p  %s  %s+0x%x  %s\n", p->addr, kprobe_type, sym, offset, (modname ? modname : " "));
+       else
+               seq_printf (pi, "%p  %s  %p\n", p->addr, kprobe_type, p->addr);
+}
+
+static void __kprobes *
+kprobe_seq_start (struct seq_file *f, loff_t * pos)
+{
+       return (*pos < KPROBE_TABLE_SIZE) ? pos : NULL;
+}
+
+static void __kprobes *
+kprobe_seq_next (struct seq_file *f, void *v, loff_t * pos)
+{
+       (*pos)++;
+       if (*pos >= KPROBE_TABLE_SIZE)
+               return NULL;
+       return pos;
+}
+
+static void __kprobes
+kprobe_seq_stop (struct seq_file *f, void *v)
+{
+       /* Nothing to do */
+}
+
+struct us_proc_ip
+{
+       char *name;
+       int installed;
+       struct jprobe jprobe;
+       struct kretprobe retprobe;
+       unsigned long offset;
+};
+
+static int __kprobes
+show_kprobe_addr (struct seq_file *pi, void *v)
+{
+       struct hlist_head *head;
+       struct hlist_node *node;
+       struct kprobe *p, *kp;
+       const char *sym = NULL;
+       unsigned int i = *(loff_t *) v;
+       unsigned long size, offset = 0;
+       char *modname, namebuf[128];
+
+       head = &kprobe_table[i];
+       preempt_disable ();
+       hlist_for_each_entry_rcu (p, node, head, hlist)
+       {
+               /*if(p->pid){
+                  struct us_proc_ip *up = NULL;
+                  if (p->pre_handler == pre_handler_kretprobe){
+                  struct kretprobe *rp = container_of(p, struct kretprobe, kp);
+                  up = container_of(rp, struct us_proc_ip, retprobe);
+                  }
+                  else {//if (p->pre_handler == setjmp_pre_handler){
+                  struct jprobe *jp = container_of(p, struct jprobe, kp);
+                  up = container_of(jp, struct us_proc_ip, jprobe);
+                  }
+                  if(up){
+                  sym = up->name;
+                  printk("show_kprobe_addr: %s\n", sym);
+                  }
+                  }
+                  else */
+               sym = kallsyms_lookup ((unsigned long) p->addr, &size, &offset, &modname, namebuf);
+               if (p->pre_handler == aggr_pre_handler)
+               {
+                       list_for_each_entry_rcu (kp, &p->list, list) report_probe (pi, kp, sym, offset, modname);
+               }
+               else
+                       report_probe (pi, p, sym, offset, modname);
+       }
+       //seq_printf (pi, "handled exceptions %lu\n", handled_exceptions);
+       preempt_enable ();
+       return 0;
+}
+
+static struct seq_operations kprobes_seq_ops = {
+       .start = kprobe_seq_start,
+       .next = kprobe_seq_next,
+       .stop = kprobe_seq_stop,
+       .show = show_kprobe_addr
+};
+
+static int __kprobes
+kprobes_open (struct inode *inode, struct file *filp)
+{
+       return seq_open (filp, &kprobes_seq_ops);
+}
+
+static struct file_operations debugfs_kprobes_operations = {
+       .open = kprobes_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = seq_release,
+};
+
+#ifdef KPROBES_PROFILE
+extern unsigned long nCount;
+extern struct timeval probe_enter_diff_sum;
+static void __kprobes *
+kprobe_prof_seq_start (struct seq_file *f, loff_t * pos)
+{
+       return (*pos < KPROBE_TABLE_SIZE) ? pos : NULL;
+}
+
+static void __kprobes *
+kprobe_prof_seq_next (struct seq_file *f, void *v, loff_t * pos)
+{
+       (*pos)++;
+       if (*pos >= KPROBE_TABLE_SIZE)
+               return NULL;
+       return pos;
+}
+
+static void __kprobes
+kprobe_prof_seq_stop (struct seq_file *f, void *v)
+{
+}
+
+static void __kprobes
+report_probe_prof (struct seq_file *pi, struct kprobe *p, const char *sym, int offset, char *modname)
+{
+       char *kprobe_type;
+
+       if (p->pre_handler == pre_handler_kretprobe)
+               if (p->pid)
+                       kprobe_type = "ur";
+               else
+                       kprobe_type = "r";
+       else if (p->pre_handler == setjmp_pre_handler)
+               if (p->pid)
+                       kprobe_type = "uj";
+               else
+                       kprobe_type = "j";
+       else if (p->pid)
+               kprobe_type = "u";
+       else
+               kprobe_type = "k";
+
+       if (sym)
+               seq_printf (pi, "%p  %s  %s+0x%x  %s %lu.%06ld\n", p->addr, kprobe_type,
+                           sym, offset, (modname ? modname : " "), p->count ? p->hnd_tm_sum.tv_sec / p->count : 0, p->count ? p->hnd_tm_sum.tv_usec / p->count : 0);
+       else
+
+               seq_printf (pi, "%p  %s  %p %lu.%06ld\n", p->addr, kprobe_type, p->addr, p->count ? p->hnd_tm_sum.tv_sec / p->count : 0, p->count ? p->hnd_tm_sum.tv_usec / p->count : 0);
+}
+
+static int __kprobes
+show_kprobe_prof (struct seq_file *pi, void *v)
+{
+       struct hlist_head *head;
+       struct hlist_node *node;
+       struct kprobe *p;       //, *kp;
+       const char *sym = NULL;
+       unsigned int i = *(loff_t *) v;
+       unsigned long size, offset = 0;
+       char *modname, namebuf[128];
+       static struct timeval utv, ktv;
+       static unsigned long ucount, kcount;
+
+       head = &kprobe_table[i];
+       preempt_disable ();
+       hlist_for_each_entry_rcu (p, node, head, hlist)
+       {
+               sym = kallsyms_lookup ((unsigned long) p->addr, &size, &offset, &modname, namebuf);
+               /*if (p->pre_handler == aggr_pre_handler) {
+                  list_for_each_entry_rcu(kp, &p->list, list)
+                  report_probe_prof(pi, kp, sym, offset, modname);
+                  } else */
+               report_probe_prof (pi, p, sym, offset, modname);
+               if (p->count)
+               {
+                       if (p->pid)
+                       {
+                               set_normalized_timeval (&utv, utv.tv_sec + p->hnd_tm_sum.tv_sec, utv.tv_usec + p->hnd_tm_sum.tv_usec);
+                               ucount += p->count;
+                       }
+                       else
+                       {
+                               //seq_printf(pi, "kernel probe handling %lu %lu.%06ld\n", 
+                               //              p->count, p->hnd_tm_sum.tv_sec, p->hnd_tm_sum.tv_usec); 
+                               //seq_printf(pi, "kernel probe handling2 %lu %lu.%06ld\n", 
+                               //              kcount, ktv.tv_sec, ktv.tv_usec);       
+                               set_normalized_timeval (&ktv, ktv.tv_sec + p->hnd_tm_sum.tv_sec, ktv.tv_usec + p->hnd_tm_sum.tv_usec);
+                               kcount += p->count;
+                               //seq_printf(pi, "kernel probe handling3 %lu %lu.%06ld\n", 
+                               //              kcount, ktv.tv_sec, ktv.tv_usec);       
+                       }
+               }
+       }
+       if (i == (KPROBE_TABLE_SIZE - 1))
+       {
+               seq_printf (pi, "Average kernel probe handling %lu.%06ld\n", kcount ? ktv.tv_sec / kcount : 0, kcount ? ktv.tv_usec / kcount : 0);
+               seq_printf (pi, "Average user probe handling %lu.%06ld\n", ucount ? utv.tv_sec / ucount : 0, ucount ? utv.tv_usec / ucount : 0);
+               seq_printf (pi, "Average probe period %lu.%06ld\n", nCount ? probe_enter_diff_sum.tv_sec / nCount : 0, nCount ? probe_enter_diff_sum.tv_usec / nCount : 0);
+               utv.tv_sec = utv.tv_usec = ktv.tv_sec = ktv.tv_usec = 0;
+               ucount = kcount = 0;
+       }
+       preempt_enable ();
+       return 0;
+}
+
+static struct seq_operations kprobes_prof_seq_ops = {
+       .start = kprobe_prof_seq_start,
+       .next = kprobe_prof_seq_next,
+       .stop = kprobe_prof_seq_stop,
+       .show = show_kprobe_prof
+};
+
+static int __kprobes
+kprobes_prof_open (struct inode *inode, struct file *filp)
+{
+       return seq_open (filp, &kprobes_prof_seq_ops);
+}
+
+static struct file_operations debugfs_kprobes_prof_operations = {
+       .open = kprobes_prof_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = seq_release,
+};
+#endif
+
+int __kprobes debugfs_kprobe_init (void);
+static struct dentry *dbg_dir, *dbg_file;
+#ifdef KPROBES_PROFILE
+static struct dentry *dbg_file_prof;
+#endif
+
+int __kprobes
+debugfs_kprobe_init (void)
+{
+       //struct dentry *dir, *file;
+
+       dbg_dir = debugfs_create_dir ("kprobes", NULL);
+       if (!dbg_dir)
+               return -ENOMEM;
+
+       dbg_file = debugfs_create_file ("list", 0444, dbg_dir, 0, &debugfs_kprobes_operations);
+       if (!dbg_file)
+       {
+               debugfs_remove (dbg_dir);
+               dbg_dir = NULL;
+               return -ENOMEM;
+       }
+
+#ifdef KPROBES_PROFILE
+       dbg_file_prof = debugfs_create_file ("prof", 0444, dbg_dir, 0, &debugfs_kprobes_prof_operations);
+       if (!dbg_file_prof)
+       {
+               debugfs_remove (dbg_file);
+               debugfs_remove (dbg_dir);
+               dbg_dir = NULL;
+               return -ENOMEM;
+       }
+#endif
+       return 0;
+}
+
+//late_initcall(debugfs_kprobe_init);
+extern unsigned long (*kallsyms_search) (const char *name);
+#endif /* CONFIG_DEBUG_FS */
+
+#if defined(CONFIG_X86)
+static struct notifier_block kprobe_exceptions_nb = {
+       .notifier_call = kprobe_exceptions_notify,
+       .priority = INT_MAX
+};
+#endif
+
+static int __init
+init_kprobes (void)
+{
+       int i, err = 0;
+
+       /* FIXME allocate the probe table, currently defined statically */
+       /* initialize all list heads */
+       for (i = 0; i < KPROBE_TABLE_SIZE; i++)
+       {
+               INIT_HLIST_HEAD (&kprobe_table[i]);
+               INIT_HLIST_HEAD (&kretprobe_inst_table[i]);
+               INIT_HLIST_HEAD (&uprobe_insn_slot_table[i]);
+       }
+       atomic_set (&kprobe_count, 0);
+
+       err = arch_init_kprobes ();
+
+       DBPRINTF ("init_kprobes: arch_init_kprobes - %d", err);
+#if defined(CONFIG_X86)
+       if (!err)
+               err = register_die_notifier (&kprobe_exceptions_nb);
+       DBPRINTF ("init_kprobes: register_die_notifier - %d", err);
+#endif // CONFIG_X86
+
+#ifdef CONFIG_DEBUG_FS
+       if (!err)
+       {
+               __real_kallsyms_lookup = (void *) kallsyms_search ("kallsyms_lookup");
+               if (!__real_kallsyms_lookup)
+               {
+                       DBPRINTF ("kallsyms_lookup is not found! Oops. Where is the kernel?");
+                       return -ESRCH;
+               }
+               err = debugfs_kprobe_init ();
+               DBPRINTF ("init_kprobes: debugfs_kprobe_init - %d", err);
+       }
+#endif /* CONFIG_DEBUG_FS */
+
+       return err;
+}
+
+static void __exit
+exit_kprobes (void)
+{
+#ifdef CONFIG_DEBUG_FS
+#ifdef KPROBES_PROFILE
+       if (dbg_file_prof)
+               debugfs_remove (dbg_file_prof);
+#endif
+       if (dbg_file)
+               debugfs_remove (dbg_file);
+       if (dbg_dir)
+               debugfs_remove (dbg_dir);
+#endif /* CONFIG_DEBUG_FS */
+
+#if defined(CONFIG_X86)
+       unregister_die_notifier (&kprobe_exceptions_nb);
+#endif // CONFIG_X86
+       arch_exit_kprobes ();
+}
+
+module_init (init_kprobes);
+module_exit (exit_kprobes);
+
+EXPORT_SYMBOL_GPL (register_kprobe);
+EXPORT_SYMBOL_GPL (unregister_kprobe);
+EXPORT_SYMBOL_GPL (register_jprobe);
+EXPORT_SYMBOL_GPL (unregister_jprobe);
+EXPORT_SYMBOL_GPL (register_ujprobe);
+EXPORT_SYMBOL_GPL (unregister_ujprobe);
+EXPORT_SYMBOL_GPL (jprobe_return);
+EXPORT_SYMBOL_GPL (uprobe_return);
+EXPORT_SYMBOL_GPL (register_kretprobe);
+EXPORT_SYMBOL_GPL (unregister_kretprobe);
+EXPORT_SYMBOL_GPL (register_uretprobe);
+EXPORT_SYMBOL_GPL (unregister_uretprobe);
+EXPORT_SYMBOL_GPL (unregister_all_uprobes);
+//EXPORT_SYMBOL_GPL (access_process_vm_atomic);
+#if LINUX_VERSION_CODE != KERNEL_VERSION(2,6,23)
+EXPORT_SYMBOL_GPL (access_process_vm);
+#endif
+#ifdef KERNEL_HAS_ISPAGEPRESENT
+EXPORT_SYMBOL_GPL (is_page_present);
+#else
+EXPORT_SYMBOL_GPL (page_present);
+#endif
+//EXPORT_SYMBOL_GPL(get_user_pages_atomic);
diff --git a/kprobe/kprobes.h b/kprobe/kprobes.h
new file mode 100644 (file)
index 0000000..8ca0ca1
--- /dev/null
@@ -0,0 +1,267 @@
+// src_kprobes.h
+#ifndef _SRC_KPROBES_H
+#define _SRC_KPROBES_H
+
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/smp.h>
+#include <linux/percpu.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+//#include <linux/mutex.h>
+#include <linux/sched.h>
+
+#include "asm/kprobes.h"
+
+/* kprobe_status settings */
+#define KPROBE_HIT_ACTIVE      0x00000001
+#define KPROBE_HIT_SS          0x00000002
+#define KPROBE_REENTER         0x00000004
+#define KPROBE_HIT_SSDONE      0x00000008
+
+/* Attach to insert probes on any functions which should be ignored*/
+#define __kprobes      __attribute__((__section__(".kprobes.text")))
+
+struct kprobe;
+struct pt_regs;
+struct kretprobe;
+struct kretprobe_instance;
+typedef int (*kprobe_pre_handler_t) (struct kprobe *, struct pt_regs * /*, struct vm_area_struct **, 
+                                                                          struct page **, unsigned long ** */ );
+typedef int (*kprobe_break_handler_t) (struct kprobe *, struct pt_regs *       /*, struct vm_area_struct **, 
+                                                                                  struct page **, unsigned long ** */ );
+typedef void (*kprobe_post_handler_t) (struct kprobe *, struct pt_regs *, unsigned long flags);
+typedef int (*kprobe_fault_handler_t) (struct kprobe *, struct pt_regs *, int trapnr);
+typedef int (*kretprobe_handler_t) (struct kretprobe_instance *, struct pt_regs *, void *);
+
+struct kprobe
+{
+       struct hlist_node hlist;
+
+       /*list of probes to search by instruction slot*/
+       struct hlist_node is_hlist;
+
+       /* list of kprobes for multi-handler support */
+       struct list_head list;
+
+       /* Indicates that the corresponding module has been ref counted */
+       unsigned int mod_refcounted;
+
+       /*count the number of times this probe was temporarily disarmed */
+       unsigned long nmissed;
+
+       /* location of the probe point */
+       kprobe_opcode_t *addr;
+
+       /* Allow user to indicate symbol name of the probe point */
+       char *symbol_name;
+
+       /* Offset into the symbol */
+       unsigned int offset;
+
+       /* Called before addr is executed. */
+       kprobe_pre_handler_t pre_handler;
+
+       /* Called after addr is executed, unless... */
+       kprobe_post_handler_t post_handler;
+
+       /* ... called if executing addr causes a fault (eg. page fault).
+        * Return 1 if it handled fault, otherwise kernel will see it. */
+       kprobe_fault_handler_t fault_handler;
+
+       /* ... called if breakpoint trap occurs in probe handler.
+        * Return 1 if it handled break, otherwise kernel will see it. */
+       kprobe_break_handler_t break_handler;
+
+       /* Saved opcode (which has been replaced with breakpoint) */
+       kprobe_opcode_t opcode;
+
+       /* copy of the original instruction */
+       struct arch_specific_insn ainsn;
+       // TGID to which probe belongs
+       pid_t tgid;
+       // override single-step target address,
+       // may be used to redirect control-flow to arbitrary address after probe point
+       // without invocation of original instruction;
+       // useful for functions replacement
+       // if jprobe.entry should return address of function or NULL
+       // if original function should be called
+       // not supported for X86, not tested for MIPS
+       kprobe_opcode_t *ss_addr;
+#ifdef _DEBUG
+       unsigned long entry_count;
+       unsigned long step_count;
+       unsigned long exit_count;
+       unsigned long lr;
+#endif
+#ifdef KPROBES_PROFILE
+       struct timeval start_tm;
+       struct timeval hnd_tm_sum;
+       unsigned long count;
+#endif
+};
+
+typedef unsigned long (*kprobe_pre_entry_handler_t) (void *priv_arg, struct pt_regs * regs);
+
+/*
+ * Special probe type that uses setjmp-longjmp type tricks to resume
+ * execution at a specified entry with a matching prototype corresponding
+ * to the probed function - a trick to enable arguments to become
+ * accessible seamlessly by probe handling logic.
+ * Note:
+ * Because of the way compilers allocate stack space for local variables
+ * etc upfront, regardless of sub-scopes within a function, this mirroring
+ * principle currently works only for probes placed on function entry points.
+ */
+struct jprobe
+{
+       struct kprobe kp;
+       kprobe_opcode_t *entry; /* probe handling code to jump to */
+       kprobe_pre_entry_handler_t pre_entry;   /*handler whichw willb bec called before 'entry' */
+       void *priv_arg;
+};
+
+struct jprobe_instance
+{
+       struct hlist_node uflist;       /* either on free list or used list */
+       struct hlist_node hlist;
+       struct jprobe *jp;
+       struct task_struct *task;
+};
+
+DECLARE_PER_CPU (struct kprobe *, current_kprobe);
+DECLARE_PER_CPU (struct kprobe_ctlblk, kprobe_ctlblk);
+
+extern void __arch_prepare_kretprobe (struct kretprobe *rp, struct pt_regs *regs);
+
+/*
+ * Function-return probe -
+ * Note:
+ * User needs to provide a handler function, and initialize maxactive.
+ * maxactive - The maximum number of instances of the probed function that
+ * can be active concurrently.
+ * nmissed - tracks the number of times the probed function's return was
+ * ignored, due to maxactive being too low.
+ *
+ */
+struct kretprobe
+{
+       struct kprobe kp;
+       kretprobe_handler_t handler;
+       void *priv_arg;
+       int maxactive;
+       int nmissed;
+       int disarm;
+       struct hlist_head free_instances;
+       struct hlist_head used_instances;
+};
+
+struct kretprobe_instance
+{
+       struct hlist_node uflist;       /* either on free list or used list */
+       struct hlist_node hlist;
+       struct kretprobe *rp;
+       kprobe_opcode_t *ret_addr;
+       struct kretprobe *rp2;
+       struct task_struct *task;
+};
+
+extern spinlock_t kretprobe_lock;
+extern struct mutex kprobe_mutex;
+extern int arch_prepare_kprobe (struct kprobe *p);
+extern int arch_prepare_uprobe (struct kprobe *p, struct task_struct *task, int atomic);
+extern int arch_prepare_kretprobe (struct kretprobe *p);
+extern int arch_prepare_uretprobe (struct kretprobe *p, struct task_struct *task);
+extern void arch_arm_kprobe (struct kprobe *p);
+extern void arch_arm_kretprobe (struct kretprobe *p);
+extern void arch_arm_uprobe (struct kprobe *p, struct task_struct *tsk);
+extern void arch_arm_uretprobe (struct kretprobe *p, struct task_struct *tsk);
+extern void arch_disarm_kprobe (struct kprobe *p);
+extern void arch_disarm_kretprobe (struct kretprobe *p);
+extern void arch_disarm_uprobe (struct kprobe *p, struct task_struct *tsk);
+extern void arch_disarm_uretprobe (struct kretprobe *p, struct task_struct *tsk);
+extern int arch_init_kprobes (void);
+extern void arch_exit_kprobes (void);
+extern void show_registers (struct pt_regs *regs);
+extern void kprobes_inc_nmissed_count (struct kprobe *p);
+
+/* Get the kprobe at this addr (if any) - called with preemption disabled */
+struct kprobe *get_kprobe (void *addr, int pid, struct task_struct *ctask);
+struct kprobe *get_kprobe_by_insn_slot (void *addr, int tgid, struct task_struct *ctask);
+struct hlist_head *kretprobe_inst_table_head (struct task_struct *tsk);
+
+/* kprobe_running() will just return the current_kprobe on this CPU */
+static inline struct kprobe *
+kprobe_running (void)
+{
+       return (__get_cpu_var (current_kprobe));
+}
+
+static inline void
+reset_current_kprobe (void)
+{
+       //__get_cpu_var (current_kprobe)->spid = -1;
+       __get_cpu_var (current_kprobe) = NULL;
+}
+
+static inline struct kprobe_ctlblk *
+get_kprobe_ctlblk (void)
+{
+       return (&__get_cpu_var (kprobe_ctlblk));
+}
+
+int register_kprobe (struct kprobe *p, int atomic);
+void unregister_kprobe (struct kprobe *p, struct task_struct *task, int atomic);
+int setjmp_pre_handler (struct kprobe *, struct pt_regs *);
+int longjmp_break_handler (struct kprobe *, struct pt_regs *);
+int register_jprobe (struct jprobe *p, int atomic);
+void unregister_jprobe (struct jprobe *p, int atomic);
+int register_ujprobe (struct task_struct *task, struct mm_struct *mm, struct jprobe *jp, int atomic);
+void unregister_ujprobe (struct task_struct *task, struct jprobe *jp, int atomic);
+void unregister_uprobe (struct kprobe *p, struct task_struct *task, int atomic);
+void jprobe_return (void);
+void uprobe_return (void);
+
+int register_kretprobe (struct kretprobe *rp, int atomic);
+void unregister_kretprobe (struct kretprobe *rp, int atomic);
+int register_uretprobe (struct task_struct *task, struct mm_struct *mm, struct kretprobe *rp, int atomic);
+void unregister_uretprobe (struct task_struct *task, struct kretprobe *rp, int atomic);
+
+void unregister_all_uprobes (struct task_struct *task, int atomic);
+
+struct kretprobe_instance *get_free_rp_inst (struct kretprobe *rp);
+void add_rp_inst (struct kretprobe_instance *ri);
+//void kprobe_flush_task(struct task_struct *tk);
+void recycle_rp_inst (struct kretprobe_instance *ri, struct hlist_head *head);
+
+//void arch_copy_kprobe(struct kprobe *p);
+void arch_remove_kprobe (struct kprobe *p, struct task_struct *task);
+void kretprobe_trampoline_holder (void);
+int __kprobes trampoline_probe_handler (struct kprobe *p, struct pt_regs *regs);
+#ifdef KPROBES_PROFILE
+int __kprobes pre_handler_kretprobe (struct kprobe *p, struct pt_regs *regs, struct vm_area_struct **vma, struct page **page, unsigned long **kaddr);
+void set_normalized_timeval (struct timeval *tv, time_t sec, suseconds_t usec);
+#endif
+
+kprobe_opcode_t *get_insn_slot (struct task_struct *task, int atomic);
+void free_insn_slot (struct hlist_head *page_list, struct task_struct *task, kprobe_opcode_t *slot, int dirty);
+
+//int access_process_vm_atomic(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
+#define access_process_vm_atomic       access_process_vm
+#define read_proc_vm_atomic(tsk, addr, buf, len)       access_process_vm_atomic(tsk, addr, buf, len, 0)
+#define write_proc_vm_atomic(tsk, addr, buf, len)      access_process_vm_atomic(tsk, addr, buf, len, 1)
+int page_present (struct mm_struct *mm, unsigned long addr);
+/*int get_user_pages_atomic(struct task_struct *tsk, struct mm_struct *mm,
+                               unsigned long start, int len, int write, int force,
+                               struct page **pages, struct vm_area_struct **vmas);*/
+#define get_user_pages_atomic  get_user_pages
+#ifdef KERNEL_HAS_ISPAGEPRESENT
+#define page_present                   is_page_present
+#else
+int page_present (struct mm_struct *mm, unsigned long addr);
+#endif
+void purge_garbage_uslots(struct task_struct *task, int atomic);
+#endif /* _SRC_KPROBES_H */
+
+extern kprobe_opcode_t *sched_addr;
+extern kprobe_opcode_t *fork_addr;
diff --git a/kprobe/kprobes_arch.c b/kprobe/kprobes_arch.c
new file mode 100644 (file)
index 0000000..46c2e19
--- /dev/null
@@ -0,0 +1,3337 @@
+#include <linux/version.h>     // LINUX_VERSION_CODE, KERNEL_VERSION()
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#include <linux/config.h>
+#endif
+#include <linux/ptrace.h>
+#include <linux/spinlock.h>
+#include <linux/preempt.h>
+#include <linux/module.h>
+#include <linux/highmem.h>     // kmap_atomic, kunmap_atomic, copy_from_user_page, copy_to_user_page
+#include <linux/pagemap.h>     // page_cache_release
+#include <asm/system.h>
+#include <asm/cacheflush.h>
+#include <linux/kallsyms.h>
+#include <linux/vmalloc.h>
+#include <linux/syscalls.h>
+#include <linux/security.h>
+#include <linux/mount.h>
+#include <linux/mman.h>
+#include <linux/personality.h>
+#include <linux/hugetlb.h>
+#include <linux/file.h>
+#include <linux/mempolicy.h>
+#if defined(CONFIG_X86)
+#include <linux/kdebug.h>
+#include <linux/moduleloader.h>
+#include <linux/freezer.h>
+#include <linux/hardirq.h>
+#endif
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 19))
+#include <linux/freezer.h>
+#endif
+
+#include "kprobes.h"
+
+#if defined(CONFIG_X86)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+#  define TF_MASK      X86_EFLAGS_TF
+#  define IF_MASK      X86_EFLAGS_IF
+# endif
+# define UPROBES_TRAMP_LEN                             (MAX_INSN_SIZE+sizeof(kprobe_opcode_t))
+# define UPROBES_TRAMP_INSN_IDX                        0
+# define UPROBES_TRAMP_RET_BREAK_IDX   MAX_INSN_SIZE
+# define KPROBES_TRAMP_LEN                             MAX_INSN_SIZE
+# define KPROBES_TRAMP_INSN_IDX                        0
+#elif defined(CONFIG_ARM) 
+# define UPROBES_TRAMP_LEN                             8
+# define UPROBES_TRAMP_INSN_IDX                        2
+# define UPROBES_TRAMP_SS_BREAK_IDX            4
+# define UPROBES_TRAMP_RET_BREAK_IDX   5
+# define KPROBES_TRAMP_LEN                             8
+# define KPROBES_TRAMP_INSN_IDX                        UPROBES_TRAMP_INSN_IDX
+# define KPROBES_TRAMP_SS_BREAK_IDX            UPROBES_TRAMP_SS_BREAK_IDX
+# define KPROBES_TRAMP_RET_BREAK_IDX   UPROBES_TRAMP_RET_BREAK_IDX
+#elif defined(CONFIG_MIPS) 
+# define UPROBES_TRAMP_LEN                             3
+# define UPROBES_TRAMP_INSN_IDX                        0
+# define UPROBES_TRAMP_SS_BREAK_IDX            1
+# define UPROBES_TRAMP_RET_BREAK_IDX   2
+# define KPROBES_TRAMP_LEN                             UPROBES_TRAMP_LEN
+# define KPROBES_TRAMP_INSN_IDX                        UPROBES_TRAMP_INSN_IDX
+# define KPROBES_TRAMP_SS_BREAK_IDX            UPROBES_TRAMP_SS_BREAK_IDX
+# define KPROBES_TRAMP_RET_BREAK_IDX   UPROBES_TRAMP_RET_BREAK_IDX
+#endif //CONFIG_MIPS
+
+DEFINE_PER_CPU (struct kprobe *, current_kprobe) = NULL;
+DEFINE_PER_CPU (struct kprobe_ctlblk, kprobe_ctlblk);
+
+/* kprobe_status settings */
+#define KPROBE_HIT_ACTIVE      0x00000001
+#define KPROBE_HIT_SS          0x00000002
+
+#define INVALID_VALUE 0xFFFFFFFF
+#define INVALID_POINTER (void*)INVALID_VALUE
+
+static int ksyms = INVALID_VALUE;
+module_param (ksyms, int, 0);
+
+extern unsigned long handled_exceptions;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12))
+#define synchronize_sched      synchronize_kernel
+#endif
+
+void jprobe_return_end (void);
+void uprobe_return_end (void);
+
+#if defined(CONFIG_X86)
+/*fastcall*/ void *__kprobes trampoline_probe_handler_x86 (struct pt_regs *regs);
+#endif
+
+static inline void
+kretprobe_assert (struct kretprobe_instance *ri, unsigned long orig_ret_address, unsigned long trampoline_address)
+{
+       if (!orig_ret_address || (orig_ret_address == trampoline_address))
+               panic ("kretprobe BUG!: Processing kretprobe %p @ %p\n", ri->rp, ri->rp->kp.addr);
+}
+
+#define HIWORD(x) (((x) & 0xFFFF0000) >> 16)
+#define LOWORD(x) ((x) & 0x0000FFFF)
+
+unsigned int gl_nNumberOfInstructions = 0;
+unsigned int gl_nCodeSize = 0;
+
+unsigned int arrTrapsTemplate[] = {
+#if defined(CONFIG_MIPS)
+               0x3c010000,             // lui  a1                              [0]
+               0x24210000,             // addiu a1, a1                         [1]
+               0x00200008,             // jr a1                                [2]
+               0x00000000,             // nop
+               0xffffffff              // end
+#elif defined(CONFIG_ARM)
+               0xe1a0c00d,             // mov          ip, sp
+               0xe92dd800,             // stmdb    sp!, {fp, ip, lr, pc}
+               0xe24cb004,             // sub          fp, ip, #4      ; 0x4
+               0x00000000,             // b                                    [3]
+               0xe3500000,             // cmp          r0, #0  ; 0x0   
+               0xe89da800,             // ldmia        sp, {fp, sp, pc}
+               0x00000000,             // nop
+               0xffffffff              // end
+#endif // ARCH
+};
+
+unsigned long nCount;
+
+kprobe_opcode_t *sched_addr;
+kprobe_opcode_t *fork_addr;
+
+#if defined(CONFIG_MIPS)
+#define REG_HI_INDEX 0
+#define REG_LO_INDEX 1
+#define NOTIFIER_CALL_CHAIN_INDEX 0
+
+#elif defined(CONFIG_ARM)
+#define NOTIFIER_CALL_CHAIN_INDEX 3
+//#define NOTIFIER_CALL_CHAIN_INDEX1 6
+//#define NOTIFIER_CALL_CHAIN_INDEX2 11
+
+static unsigned int
+arch_construct_brunch (unsigned int base, unsigned int addr, int link)
+{
+       kprobe_opcode_t insn;
+       unsigned int bpi = (unsigned int) base - (unsigned int) addr - 8;
+       insn = bpi >> 2;
+       DBPRINTF ("base=%x addr=%x base-addr-8=%x\n", base, addr, bpi);
+       if (abs (insn & 0xffffff) > 0xffffff)
+       {
+               DBPRINTF ("ERROR: kprobe address out of range\n");
+               BUG ();
+       }
+       insn = insn & 0xffffff;
+       insn = insn | ((link != 0) ? 0xeb000000 : 0xea000000);
+       DBPRINTF ("insn=%lX\n", insn);
+       return (unsigned int) insn;
+}
+#endif // ARCH
+
+unsigned int *arrTrapsOriginal = NULL;
+
+#ifndef KERNEL_HAS_ISPAGEPRESENT
+int
+page_present (struct mm_struct *mm, unsigned long addr)
+{
+       pgd_t *pgd;
+       pmd_t *pmd;
+       pte_t *pte;
+       int ret = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+       pud_t *pud;
+#endif
+
+       //printk("page_present\n");
+       //BUG_ON(down_read_trylock(&mm->mmap_sem) == 0);
+       down_read (&mm->mmap_sem);
+       spin_lock (&(mm->page_table_lock));
+       pgd = pgd_offset (mm, addr);
+       //printk("pgd %p\n", pgd);
+       if ((pgd != NULL) && pgd_present (*pgd))
+       {
+               //printk("pgd_present\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+               pud = pud_offset (pgd, addr);
+               //printk("pud %p\n", pud);
+               if ((pud != NULL) && pud_present (*pud))
+               {
+                       pmd = pmd_offset (pud, addr);
+#else
+               {
+                       pmd = pmd_offset (pgd, addr);
+#endif
+                       //printk("pmd %p\n", pmd);
+                       if ((pmd != NULL) && pmd_present (*pmd))
+                       {
+                               //spinlock_t *ptl;
+                               //printk("pmd_present\n");
+                               pte = pte_offset_map (pmd, addr);
+                               //pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
+                               //printk("pte %p/%lx\n", pte, addr);
+                               if ((pte != NULL) && pte_present (*pte))
+                               {
+                                       ret = 1;
+                                       //printk("pte_present\n");
+                               }
+                               pte_unmap (pte);
+                               //pte_unmap_unlock(pte, ptl);
+                       }
+               }
+       }
+       spin_unlock (&(mm->page_table_lock));
+       up_read (&mm->mmap_sem);
+       //printk("page_present %d\n", ret);
+       return ret;
+}
+#endif
+
+#if defined(CONFIG_MIPS)
+#define MIPS_INSN_OPCODE_MASK  0xFC000000
+#define MIPS_INSN_RS_MASK              0x03E00000
+#define MIPS_INSN_RT_MASK              0x001F0000
+//#define MIPS_INSN_UN_MASK             0x0000FFC0
+#define MIPS_INSN_FUNC_MASK            0x0000003F
+#define MIPS_INSN_OPCODE(insn) (insn & MIPS_INSN_OPCODE_MASK)
+#define MIPS_INSN_RS(insn)             (insn & MIPS_INSN_RS_MASK)
+#define MIPS_INSN_RT(insn)             (insn & MIPS_INSN_RT_MASK)
+#define MIPS_INSN_FUNC(insn)   (insn & MIPS_INSN_FUNC_MASK)
+// opcodes 31..26
+#define MIPS_BEQ_OPCODE                0x10000000
+#define MIPS_BNE_OPCODE                0x14000000
+#define MIPS_BLEZ_OPCODE       0x18000000
+#define MIPS_BGTZ_OPCODE       0x1C000000
+#define MIPS_BEQL_OPCODE       0x50000000
+#define MIPS_BNEL_OPCODE       0x54000000
+#define MIPS_BLEZL_OPCODE      0x58000000
+#define MIPS_BGTZL_OPCODE      0x5C000000
+#define MIPS_REGIMM_OPCODE     0x04000000
+#define MIPS_SPECIAL_OPCODE    0x00000000
+#define MIPS_COP1_OPCODE       0x44000000
+#define MIPS_COP2_OPCODE       0x48000000
+#define MIPS_J_OPCODE          0x08000000
+#define MIPS_JAL_OPCODE                0x0C000000
+#define MIPS_JALX_OPCODE       0x74000000
+// rs 25..21
+#define MIPS_BC_RS                     0x01000000
+// rt 20..16
+#define MIPS_BLTZ_RT           0x00000000
+#define MIPS_BGEZ_RT           0x00010000
+#define MIPS_BLTZL_RT          0x00020000
+#define MIPS_BGEZL_RT          0x00030000
+#define MIPS_BLTZAL_RT         0x00100000
+#define MIPS_BGEZAL_RT         0x00110000
+#define MIPS_BLTZALL_RT                0x00120000
+#define MIPS_BGEZALL_RT                0x00130000
+// unnamed 15..6
+// function 5..0
+#define MIPS_JR_FUNC           0x00000008
+#define MIPS_JALR_FUNC         0x00000009
+#define MIPS_BREAK_FUNC                0x0000000D
+#define MIPS_SYSCALL_FUNC      0x0000000C
+
+#elif defined(CONFIG_ARM)
+// undefined
+#define MASK_ARM_INSN_UNDEF            0x0FF00000
+#define PTRN_ARM_INSN_UNDEF            0x03000000
+// architecturally undefined
+#define MASK_ARM_INSN_AUNDEF   0x0FF000F0
+#define PTRN_ARM_INSN_AUNDEF   0x07F000F0
+// branches
+#define MASK_ARM_INSN_B                        0x0E000000
+#define PTRN_ARM_INSN_B                        0x0A000000
+#define MASK_ARM_INSN_BL               0x0E000000
+#define PTRN_ARM_INSN_BL               0x0B000000
+#define MASK_ARM_INSN_BLX1             0xFF000000
+#define PTRN_ARM_INSN_BLX1             0xFA000000
+#define MASK_ARM_INSN_BLX2             0x0FF000F0
+#define PTRN_ARM_INSN_BLX2             0x01200030
+#define MASK_ARM_INSN_BX               0x0FF000F0
+#define PTRN_ARM_INSN_BX               0x01200010
+#define MASK_ARM_INSN_BXJ              0x0FF000F0
+#define PTRN_ARM_INSN_BXJ              0x01200020
+// software interrupts
+#define MASK_ARM_INSN_SWI              0x0F000000
+#define PTRN_ARM_INSN_SWI              0x0F000000
+// break
+#define MASK_ARM_INSN_BREAK            0xFFF000F0
+#define PTRN_ARM_INSN_BREAK            0xE1200070
+// Data processing immediate shift
+#define MASK_ARM_INSN_DPIS             0x0E000010
+#define PTRN_ARM_INSN_DPIS             0x00000000
+// Data processing register shift
+#define MASK_ARM_INSN_DPRS             0x0E000090
+#define PTRN_ARM_INSN_DPRS             0x00000010
+// Data processing immediate
+#define MASK_ARM_INSN_DPI              0x0E000000
+#define PTRN_ARM_INSN_DPI              0x02000000
+// Load immediate offset
+#define MASK_ARM_INSN_LIO              0x0E100000
+#define PTRN_ARM_INSN_LIO              0x04100000
+// Store immediate offset
+#define MASK_ARM_INSN_SIO              MASK_ARM_INSN_LIO
+#define PTRN_ARM_INSN_SIO              0x04000000
+// Load register offset
+#define MASK_ARM_INSN_LRO              0x0E100010
+#define PTRN_ARM_INSN_LRO              0x06100000
+// Store register offset
+#define MASK_ARM_INSN_SRO              MASK_ARM_INSN_LRO
+#define PTRN_ARM_INSN_SRO              0x06000000
+// Load multiple
+#define MASK_ARM_INSN_LM               0x0E100000
+#define PTRN_ARM_INSN_LM               0x08100000
+// Store multiple
+#define MASK_ARM_INSN_SM               MASK_ARM_INSN_LM
+#define PTRN_ARM_INSN_SM               0x08000000
+// Coprocessor load/store and double register transfers
+#define MASK_ARM_INSN_CLS              0x0E000000
+#define PTRN_ARM_INSN_CLS              0x0C000000
+// Coprocessor register transfers
+#define MASK_ARM_INSN_CRT              0x0F000010
+#define PTRN_ARM_INSN_CRT              0x0E000010
+
+#define ARM_INSN_MATCH(name, insn)     ((insn & MASK_ARM_INSN_##name) == PTRN_ARM_INSN_##name)
+
+#define ARM_INSN_REG_RN(insn)                  ((insn & 0x000F0000)>>16)
+#define ARM_INSN_REG_SET_RN(insn, nreg)        {insn &= ~0x000F0000; insn |= nreg<<16;}
+#define ARM_INSN_REG_RD(insn)                  ((insn & 0x0000F000)>>12)
+#define ARM_INSN_REG_SET_RD(insn, nreg)        {insn &= ~0x0000F000; insn |= nreg<<12;}
+#define ARM_INSN_REG_RS(insn)                  ((insn & 0x00000F00)>>8)
+#define ARM_INSN_REG_SET_RS(insn, nreg)        {insn &= ~0x00000F00; insn |= nreg<<8;}
+#define ARM_INSN_REG_RM(insn)                  (insn & 0x0000000F)
+#define ARM_INSN_REG_SET_RM(insn, nreg)        {insn &= ~0x0000000F; insn |= nreg;}
+#define ARM_INSN_REG_MR(insn, nreg)            (insn & (1 << nreg))
+#define ARM_INSN_REG_SET_MR(insn, nreg)        {insn |= (1 << nreg);}
+#define ARM_INSN_REG_CLEAR_MR(insn, nreg)      {insn &= ~(1 << nreg);}
+
+#elif defined(CONFIG_X86)
+//#    warning Branch instruction patterns are not defined for x86 arch!!!
+#endif
+
+#if defined(CONFIG_X86)
+/* insert a jmp code */
+static __always_inline void
+set_jmp_op (void *from, void *to)
+{
+       struct __arch_jmp_op
+       {
+               char op;
+               long raddr;
+       } __attribute__ ((packed)) * jop;
+       jop = (struct __arch_jmp_op *) from;
+       jop->raddr = (long) (to) - ((long) (from) + 5);
+       jop->op = RELATIVEJUMP_INSTRUCTION;
+}
+
+static void
+set_user_jmp_op (void *from, void *to)
+{
+       struct __arch_jmp_op
+       {
+               char op;
+               long raddr;
+       } __attribute__ ((packed)) jop;
+       //jop = (struct __arch_jmp_op *) from;
+       jop.raddr = (long) (to) - ((long) (from) + 5);
+       jop.op = RELATIVEJUMP_INSTRUCTION;
+       if (!write_proc_vm_atomic (current, (unsigned long)from, &jop, sizeof(jop)))
+               panic ("failed to write jump opcode to user space %p!\n", from);        
+}
+
+/*
+ * returns non-zero if opcodes can be boosted.
+ */
+static __always_inline int
+can_boost (kprobe_opcode_t * opcodes)
+{
+#define W(row,b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,ba,bb,bc,bd,be,bf)               \
+       (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) |   \
+         (b4##UL << 0x4)|(b5##UL << 0x5)|(b6##UL << 0x6)|(b7##UL << 0x7) |   \
+         (b8##UL << 0x8)|(b9##UL << 0x9)|(ba##UL << 0xa)|(bb##UL << 0xb) |   \
+         (bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf))    \
+        << (row % 32))
+       /*
+        * Undefined/reserved opcodes, conditional jump, Opcode Extension
+        * Groups, and some special opcodes can not be boost.
+        */
+       static const unsigned long twobyte_is_boostable[256 / 32] = {
+               /*      0 1 2 3 4 5 6 7 8 9 a b c d e f         */
+               /*      -------------------------------         */
+               W (0x00, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0) |      /* 00 */
+                       W (0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),       /* 10 */
+               W (0x20, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) |      /* 20 */
+                       W (0x30, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),       /* 30 */
+               W (0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) |      /* 40 */
+                       W (0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),       /* 50 */
+               W (0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1) |      /* 60 */
+                       W (0x70, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1),       /* 70 */
+               W (0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) |      /* 80 */
+                       W (0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),       /* 90 */
+               W (0xa0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1) |      /* a0 */
+                       W (0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1),       /* b0 */
+               W (0xc0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) |      /* c0 */
+                       W (0xd0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1),       /* d0 */
+               W (0xe0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1) |      /* e0 */
+                       W (0xf0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0)        /* f0 */
+                       /*      -------------------------------         */
+                       /*      0 1 2 3 4 5 6 7 8 9 a b c d e f         */
+       };
+#undef W
+       kprobe_opcode_t opcode;
+       kprobe_opcode_t *orig_opcodes = opcodes;
+      retry:
+       if (opcodes - orig_opcodes > MAX_INSN_SIZE - 1)
+               return 0;
+       opcode = *(opcodes++);
+
+       /* 2nd-byte opcode */
+       if (opcode == 0x0f)
+       {
+               if (opcodes - orig_opcodes > MAX_INSN_SIZE - 1)
+                       return 0;
+               return test_bit (*opcodes, twobyte_is_boostable);
+       }
+
+       switch (opcode & 0xf0)
+       {
+       case 0x60:
+               if (0x63 < opcode && opcode < 0x67)
+                       goto retry;     /* prefixes */
+               /* can't boost Address-size override and bound */
+               return (opcode != 0x62 && opcode != 0x67);
+       case 0x70:
+               return 0;       /* can't boost conditional jump */
+       case 0xc0:
+               /* can't boost software-interruptions */
+               return (0xc1 < opcode && opcode < 0xcc) || opcode == 0xcf;
+       case 0xd0:
+               /* can boost AA* and XLAT */
+               return (opcode == 0xd4 || opcode == 0xd5 || opcode == 0xd7);
+       case 0xe0:
+               /* can boost in/out and absolute jmps */
+               return ((opcode & 0x04) || opcode == 0xea);
+       case 0xf0:
+               if ((opcode & 0x0c) == 0 && opcode != 0xf1)
+                       goto retry;     /* lock/rep(ne) prefix */
+               /* clear and set flags can be boost */
+               return (opcode == 0xf5 || (0xf7 < opcode && opcode < 0xfe));
+       default:
+               if (opcode == 0x26 || opcode == 0x36 || opcode == 0x3e)
+                       goto retry;     /* prefixes */
+               /* can't boost CS override and call */
+               return (opcode != 0x2e && opcode != 0x9a);
+       }
+}
+
+/*
+ * returns non-zero if opcode modifies the interrupt flag.
+ */
+static int __kprobes
+is_IF_modifier (kprobe_opcode_t opcode)
+{
+       switch (opcode)
+       {
+       case 0xfa:              /* cli */
+       case 0xfb:              /* sti */
+       case 0xcf:              /* iret/iretd */
+       case 0x9d:              /* popf/popfd */
+               return 1;
+       }
+       return 0;
+}
+#endif
+
+static int
+arch_check_insn (struct arch_specific_insn *ainsn)
+{
+       int ret = 0;
+
+#if defined(CONFIG_MIPS)
+       switch (MIPS_INSN_OPCODE (ainsn->insn[0]))
+       {
+       case MIPS_BEQ_OPCODE:   //B, BEQ   
+       case MIPS_BEQL_OPCODE:  //BEQL    
+       case MIPS_BNE_OPCODE:   //BNE      
+       case MIPS_BNEL_OPCODE:  //BNEL    
+       case MIPS_BGTZ_OPCODE:  //BGTZ    
+       case MIPS_BGTZL_OPCODE: //BGTZL
+       case MIPS_BLEZ_OPCODE:  //BLEZ    
+       case MIPS_BLEZL_OPCODE: //BLEZL  
+       case MIPS_J_OPCODE:     //J  
+       case MIPS_JAL_OPCODE:   //JAL
+               DBPRINTF ("arch_check_insn: opcode");
+               ret = -EFAULT;
+               break;
+       case MIPS_REGIMM_OPCODE:
+               //BAL, BGEZ, BGEZAL, BGEZALL, BGEZL, BLTZ, BLTZAL, BLTZALL, BLTZL
+               switch (MIPS_INSN_RT (ainsn->insn[0]))
+               {
+               case MIPS_BLTZ_RT:
+               case MIPS_BGEZ_RT:
+               case MIPS_BLTZL_RT:
+               case MIPS_BGEZL_RT:
+               case MIPS_BLTZAL_RT:
+               case MIPS_BGEZAL_RT:
+               case MIPS_BLTZALL_RT:
+               case MIPS_BGEZALL_RT:
+                       DBPRINTF ("arch_check_insn: REGIMM opcode\n");
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+               //BC1F, BC1FL, BC1T, BC1TL
+       case MIPS_COP1_OPCODE:
+               //BC2F, BC2FL, BC2T, BC2TL
+       case MIPS_COP2_OPCODE:
+               if (MIPS_INSN_RS (ainsn->insn[0]) == MIPS_BC_RS)
+               {
+                       DBPRINTF ("arch_check_insn: COP1 opcode\n");
+                       ret = -EFAULT;
+               }
+               break;
+       case MIPS_SPECIAL_OPCODE:
+               //BREAK, JALR, JALR.HB, JR, JR.HB
+               switch (MIPS_INSN_FUNC (ainsn->insn[0]))
+               {
+               case MIPS_JR_FUNC:
+               case MIPS_JALR_FUNC:
+               case MIPS_BREAK_FUNC:
+               case MIPS_SYSCALL_FUNC:
+                       DBPRINTF ("arch_check_insn: SPECIAL opcode\n");
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+       }
+#elif defined(CONFIG_ARM)
+       // check instructions that can change PC by nature 
+       if (ARM_INSN_MATCH (UNDEF, ainsn->insn[0]) ||
+           ARM_INSN_MATCH (AUNDEF, ainsn->insn[0]) ||
+           ARM_INSN_MATCH (SWI, ainsn->insn[0]) ||
+           ARM_INSN_MATCH (BREAK, ainsn->insn[0]) ||
+           ARM_INSN_MATCH (B, ainsn->insn[0]) ||
+           ARM_INSN_MATCH (BL, ainsn->insn[0]) ||
+           ARM_INSN_MATCH (BLX1, ainsn->insn[0]) || 
+           ARM_INSN_MATCH (BLX2, ainsn->insn[0]) || 
+           ARM_INSN_MATCH (BX, ainsn->insn[0]) || 
+           ARM_INSN_MATCH (BXJ, ainsn->insn[0]))
+       {
+               DBPRINTF ("arch_check_insn: %lx\n", ainsn->insn[0]);
+               ret = -EFAULT;
+       }
+#ifndef CONFIG_CPU_V7
+       // check instructions that can write result to PC
+       else if ((ARM_INSN_MATCH (DPIS, ainsn->insn[0]) ||
+                         ARM_INSN_MATCH (DPRS, ainsn->insn[0]) ||
+                 ARM_INSN_MATCH (DPI, ainsn->insn[0]) || 
+                 ARM_INSN_MATCH (LIO, ainsn->insn[0]) || 
+                 ARM_INSN_MATCH (LRO, ainsn->insn[0])) && 
+                (ARM_INSN_REG_RD (ainsn->insn[0]) == 15))
+       {
+               DBPRINTF ("arch_check_insn: %lx\n", ainsn->insn[0]);
+               ret = -EFAULT;
+       }
+#endif // CONFIG_CPU_V7
+       // check special instruction loads store multiple registers
+       else if ((ARM_INSN_MATCH (LM, ainsn->insn[0]) || ARM_INSN_MATCH (SM, ainsn->insn[0])) &&
+           // store pc or load to pc
+           (ARM_INSN_REG_MR (ainsn->insn[0], 15) ||
+            // store/load with pc update
+            ((ARM_INSN_REG_RN (ainsn->insn[0]) == 15) && (ainsn->insn[0] & 0x200000))))
+       {
+               DBPRINTF ("arch_check_insn: %lx\n", ainsn->insn[0]);
+               ret = -EFAULT;
+       }
+#elif defined(CONFIG_X86)
+//#    warning arch_check_insn is not implemented for x86 arch!!!
+#endif
+
+       return ret;
+}
+
+/*
+ * kprobe->ainsn.insn points to the copy of the instruction to be
+ * single-stepped. x86_64, POWER4 and above have no-exec support and
+ * stepping on the instruction on a vmalloced/kmalloced/data page
+ * is a recipe for disaster
+ */
+#define INSNS_PER_PAGE (PAGE_SIZE/(MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
+
+struct kprobe_insn_page
+{
+       struct hlist_node hlist;
+       kprobe_opcode_t *insns; /* Page of instruction slots */
+       char *slot_used;//[INSNS_PER_PAGE];     
+       int nused;
+       int ngarbage;
+       int tgid;
+};
+
+enum kprobe_slot_state
+{
+       SLOT_CLEAN = 0,
+       SLOT_DIRTY = 1,
+       SLOT_USED = 2,
+};
+
+static struct hlist_head kprobe_insn_pages;
+static int kprobe_garbage_slots;
+static struct hlist_head uprobe_insn_pages;
+static int uprobe_garbage_slots;
+static int collect_garbage_slots (struct hlist_head *page_list, struct task_struct *task);
+
+void gen_insn_execbuf (void);
+void pc_dep_insn_execbuf (void);
+void gen_insn_execbuf_holder (void);
+void pc_dep_insn_execbuf_holder (void);
+
+void
+gen_insn_execbuf_holder (void)
+{
+       asm volatile (".global gen_insn_execbuf\n" 
+                               "gen_insn_execbuf:\n" 
+#if defined(CONFIG_ARM)
+                               "nop\n" 
+                               "nop\n" 
+                               "nop\n" // original instruction
+                       "nop\n" 
+                               "ldr    pc, [pc, #4]\n" //ssbreak 
+                               "nop\n" //retbreak
+                               "nop\n" 
+                               "nop\n"); //stored PC-4(next insn addr)
+#elif defined(CONFIG_MIPS)
+                               "nop\n" // original instruction
+                               "nop\n" //ssbreak 
+                               "nop\n");//retbreak
+#else
+                               "nop\n");//retbreak
+#endif
+}
+
+#if defined(CONFIG_ARM)
+/*void
+pc_dep_uinsn_execbuf_holder (void)
+{
+       asm volatile (".global pc_dep_uinsn_execbuf\n" 
+                               "pc_dep_uinsn_execbuf:\n"
+                       "str    r0, [pc, #20]\n" 
+                               "ldr    r0, [pc, #12]\n" 
+                               "nop\n" // instruction with replaced PC
+                       "ldr    r0, [pc, #8]\n"
+                               "nop\n" // ssbreak
+                       "nop\n" // retbreak
+                       "nop\n" // stored PC
+                               "nop\n");// stored Rx
+}*/
+/*
+ * 0. push Rx on stack
+ * 1. load address to Rx
+ * 2. do insn using Rx
+ * 3. pop Rx from stack
+ * 4. BREAK1
+ * 5. BREAK2
+ * 6. stored PC
+ * 7. stored PC-4(next insn addr)
+ */
+void
+pc_dep_insn_execbuf_holder (void)
+{
+       asm volatile (".global pc_dep_insn_execbuf\n" 
+                               "pc_dep_insn_execbuf:\n"
+                       "str    r0, [sp, #-4]\n" 
+                               "ldr    r0, [pc, #12]\n" 
+                               "nop\n" // instruction with replaced PC
+                       "ldr    r0, [sp, #-4]\n"
+                               "ldr    pc, [pc, #4]\n" //ssbreak
+                       "nop\n" // retbreak
+                               "nop\n" // stored PC
+                               "nop\n");// stored PC-4 (next insn addr)
+}
+
+static int
+prep_pc_dep_insn_execbuf (kprobe_opcode_t * insns, kprobe_opcode_t insn, int uregs)
+{
+       int i;
+
+       if (uregs & 0x10)
+       {
+               int reg_mask = 0x1;
+               //search in reg list
+               for (i = 0; i < 13; i++, reg_mask <<= 1)
+               {
+                       if (!(insn & reg_mask))
+                               break;
+               }
+       }
+       else
+       {
+               for (i = 0; i < 13; i++)
+               {
+                       //              DBPRINTF("prep_pc_dep_insn_execbuf: check R%d/%d, changing regs %x in %x", 
+                       //                              i, ARM_INSN_REG_RN(insn), uregs, insn);
+                       if ((uregs & 0x1) && (ARM_INSN_REG_RN (insn) == i))
+                               continue;
+                       if ((uregs & 0x2) && (ARM_INSN_REG_RD (insn) == i))
+                               continue;
+                       if ((uregs & 0x4) && (ARM_INSN_REG_RS (insn) == i))
+                               continue;
+                       if ((uregs & 0x8) && (ARM_INSN_REG_RM (insn) == i))
+                               continue;
+                       break;
+               }
+       }
+       if (i == 13)
+       {
+               DBPRINTF ("there are no free register %x in insn %lx!", uregs, insn);
+               return -EINVAL;
+       }
+       DBPRINTF ("prep_pc_dep_insn_execbuf: using R%d, changing regs %x", i, uregs);
+
+       // set register to save
+       ARM_INSN_REG_SET_RD (insns[0], i);
+       // set register to load address to
+       ARM_INSN_REG_SET_RD (insns[1], i);
+       // set instruction to execute and patch it 
+       if (uregs & 0x10)
+       {
+               ARM_INSN_REG_CLEAR_MR (insn, 15);
+               ARM_INSN_REG_SET_MR (insn, i);
+       }
+       else
+       {
+               if ((uregs & 0x1) && (ARM_INSN_REG_RN (insn) == 15))
+                       ARM_INSN_REG_SET_RN (insn, i);
+               if ((uregs & 0x2) && (ARM_INSN_REG_RD (insn) == 15))
+                       ARM_INSN_REG_SET_RD (insn, i);
+               if ((uregs & 0x4) && (ARM_INSN_REG_RS (insn) == 15))
+                       ARM_INSN_REG_SET_RS (insn, i);
+               if ((uregs & 0x8) && (ARM_INSN_REG_RM (insn) == 15))
+                       ARM_INSN_REG_SET_RM (insn, i);
+       }
+       insns[UPROBES_TRAMP_INSN_IDX] = insn;
+       // set register to restore
+       ARM_INSN_REG_SET_RD (insns[3], i);
+       return 0;
+}
+#endif//ARM
+
+int
+arch_prepare_kprobe (struct kprobe *p)
+{
+#if !defined(CONFIG_X86)
+       kprobe_opcode_t insns[KPROBES_TRAMP_LEN];
+#endif
+#if defined(CONFIG_ARM)
+       int uregs, pc_dep;
+#endif
+
+       int ret = 0;
+#if !defined(CONFIG_X86)
+       if ((unsigned long) p->addr & 0x01)
+       {
+               DBPRINTF ("Attempt to register kprobe at an unaligned address\n");
+               ret = -EINVAL;
+       }
+#endif
+       /* XXX: Might be a good idea to check if p->addr is a valid
+        * kernel address as well... */
+
+       if (!ret)
+       {
+               kprobe_opcode_t insn[MAX_INSN_SIZE];
+               struct arch_specific_insn ainsn;
+               /* insn: must be on special executable page on i386. */
+               p->ainsn.insn = get_insn_slot (NULL, 0);
+               if (!p->ainsn.insn)
+                       return -ENOMEM;
+               memcpy (insn, p->addr, MAX_INSN_SIZE * sizeof (kprobe_opcode_t));
+               ainsn.insn = insn;
+               ret = arch_check_insn (&ainsn);
+               if (!ret)
+               {
+                       p->opcode = *p->addr;
+#if defined(CONFIG_ARM)
+                       p->ainsn.boostable = 1;
+                       uregs = pc_dep = 0;
+                       // Rn, Rm ,Rd
+                       if (ARM_INSN_MATCH (DPIS, insn[0]) || ARM_INSN_MATCH (LRO, insn[0]) || 
+                               ARM_INSN_MATCH (SRO, insn[0]))
+                       {
+
+                               uregs = 0xb;
+                               if ((ARM_INSN_REG_RN (insn[0]) == 15) || (ARM_INSN_REG_RM (insn[0]) == 15) || 
+                                       (ARM_INSN_MATCH (SRO, insn[0]) && (ARM_INSN_REG_RD (insn[0]) == 15)))
+                               {
+
+                                       DBPRINTF ("Unboostable insn %lx, DPIS/LRO/SRO\n", insn[0]);
+                                       pc_dep = 1;
+                               }
+                       }
+                       // Rn ,Rd
+                       else if (ARM_INSN_MATCH (DPI, insn[0]) || ARM_INSN_MATCH (LIO, insn[0]) || 
+                                        ARM_INSN_MATCH (SIO, insn[0]))
+                       {
+
+                               uregs = 0x3;
+                               if ((ARM_INSN_REG_RN (insn[0]) == 15) || (ARM_INSN_MATCH (SIO, insn[0]) && 
+                                       (ARM_INSN_REG_RD (insn[0]) == 15)))
+                               {
+
+                                       pc_dep = 1;
+                                       DBPRINTF ("Unboostable insn %lx/%p/%d, DPI/LIO/SIO\n", insn[0], p, p->ainsn.boostable);
+                               }
+                       }
+                       // Rn, Rm, Rs                                   
+                       else if (ARM_INSN_MATCH (DPRS, insn[0]))
+                       {
+
+                               uregs = 0xd;
+                               if ((ARM_INSN_REG_RN (insn[0]) == 15) || (ARM_INSN_REG_RM (insn[0]) == 15) || 
+                                       (ARM_INSN_REG_RS (insn[0]) == 15))
+                               {
+
+                                       pc_dep = 1;
+                                       DBPRINTF ("Unboostable insn %lx, DPRS\n", insn[0]);
+                               }
+                       }
+                       // register list
+                       else if (ARM_INSN_MATCH (SM, insn[0]))
+                       {
+
+                               uregs = 0x10;
+                               if (ARM_INSN_REG_MR (insn[0], 15))
+                               {
+
+                                       DBPRINTF ("Unboostable insn %lx, SM\n", insn[0]);
+                                       pc_dep = 1;
+                               }
+                       }
+                       // check instructions that can write result to SP andu uses PC
+                       if (pc_dep  && (ARM_INSN_REG_RD (ainsn.insn[0]) == 13))
+                       {
+                               static int count;
+                               count++;
+                               //printk ("insn writes result to SP and uses PC: %lx/%d\n", ainsn.insn[0], count);
+                               free_insn_slot (&kprobe_insn_pages, NULL, p->ainsn.insn, 0);
+                               ret = -EFAULT;
+                       }
+                       else {
+                               if (uregs && pc_dep)
+                               {
+                                       memcpy (insns, pc_dep_insn_execbuf, sizeof (insns));
+                                       if (prep_pc_dep_insn_execbuf (insns, insn[0], uregs) != 0)
+                                       {
+                                               DBPRINTF ("failed to prepare exec buffer for insn %lx!", insn[0]);
+                                               free_insn_slot (&kprobe_insn_pages, NULL, p->ainsn.insn, 0);
+                                               return -EINVAL;
+                                       }
+                                       //insns[KPROBES_TRAMP_SS_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+                                       insns[6] = (kprobe_opcode_t) (p->addr + 2);
+                               }
+                               else
+                               {
+                                       memcpy (insns, gen_insn_execbuf, sizeof (insns));
+                                       insns[KPROBES_TRAMP_INSN_IDX] = insn[0];
+                               }                       
+                               //insns[KPROBES_TRAMP_RET_BREAK_IDX] = UNDEF_INSTRUCTION;
+                               insns[7] = (kprobe_opcode_t) (p->addr + 1);
+                               DBPRINTF ("arch_prepare_kprobe: insn %lx", insn[0]);
+                               DBPRINTF ("arch_prepare_kprobe: to %p - %lx %lx %lx %lx %lx %lx %lx %lx %lx", 
+                                               p->ainsn.insn, insns[0], insns[1], insns[2], insns[3], insns[4], 
+                                               insns[5], insns[6], insns[7], insns[8]);
+                               memcpy (p->ainsn.insn, insns, sizeof(insns));
+                       }
+#elif defined(CONFIG_MIPS)
+                       p->ainsn.boostable = 0;
+                       memcpy (insns, gen_insn_execbuf, sizeof (insns));
+                       insns[KPROBES_TRAMP_INSN_IDX] = insn[0];
+                       insns[KPROBES_TRAMP_SS_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+                       insns[KPROBES_TRAMP_RET_BREAK_IDX] = UNDEF_INSTRUCTION;
+                       DBPRINTF ("arch_prepare_kprobe: insn %lx", insn[0]);
+                       DBPRINTF ("arch_prepare_kprobe: to %p - %lx %lx %lx", 
+                                       p->ainsn.insn, insns[0], insns[1], insns[2]);
+                       memcpy (p->ainsn.insn, insns, sizeof(insns));
+#elif defined(CONFIG_X86)
+                       if (can_boost (p->addr))
+                               p->ainsn.boostable = 0;
+                       else
+                               p->ainsn.boostable = -1;
+                       memcpy (p->ainsn.insn, insn, MAX_INSN_SIZE * sizeof (kprobe_opcode_t));
+#endif
+               }
+               else
+               {
+                       free_insn_slot (&kprobe_insn_pages, NULL, p->ainsn.insn, 0);
+               }
+       }
+
+       return ret;
+}
+
+int
+arch_prepare_kretprobe (struct kretprobe *p)
+{
+       int ret = 0;
+#if 0
+       if ((unsigned long) p->kp.addr & 0x01)
+       {
+               DBPRINTF ("Attempt to register kprobe at an unaligned address\n");
+               ret = -EINVAL;
+       }
+
+       /* XXX: Might be a good idea to check if p->addr is a valid
+        * kernel address as well... */
+
+       if (!ret)
+       {
+               kprobe_opcode_t insn;
+               struct arch_specific_insn ainsn;
+               memcpy (&insn, p->kp.addr, MAX_INSN_SIZE * sizeof (kprobe_opcode_t));
+               ainsn.insn = &insn;
+               ret = arch_check_insn (&ainsn);
+               if (!ret)
+               {
+                       p->kp.opcode = *p->kp.addr;
+#if defined(CONFIG_X86)
+                       memcpy (p->kp.ainsn.insn, p->kp.addr, MAX_INSN_SIZE * sizeof (kprobe_opcode_t));
+#endif
+               }
+       }
+#endif
+       return ret;
+}
+
+int
+arch_prepare_uprobe (struct kprobe *p, struct task_struct *task, int atomic)
+{
+       int ret = 0;
+       kprobe_opcode_t insns[UPROBES_TRAMP_LEN];
+#if defined(CONFIG_ARM)
+       int uregs, pc_dep;
+#endif
+
+#if !defined(CONFIG_X86)
+       if ((unsigned long) p->addr & 0x01)
+       {
+               DBPRINTF ("Attempt to register kprobe at an unaligned address");
+               ret = -EINVAL;
+       }
+#endif
+
+       if (!ret)
+       {
+               kprobe_opcode_t insn[MAX_INSN_SIZE];
+               struct arch_specific_insn ainsn;
+               
+               if (!read_proc_vm_atomic (task, (unsigned long) p->addr, &insn, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
+                       panic ("failed to read memory %p!\n", p->addr);
+               ainsn.insn = insn;
+               ret = arch_check_insn (&ainsn);
+               if (!ret)
+               {
+                       p->opcode = insn[0];
+                       p->ainsn.insn = get_insn_slot(task, atomic);
+                       if (!p->ainsn.insn)
+                               return -ENOMEM;
+#if defined(CONFIG_ARM)
+                       p->ainsn.boostable = 1;
+                       uregs = pc_dep = 0;
+                       // Rn, Rm ,Rd
+                       if (ARM_INSN_MATCH (DPIS, insn[0]) || ARM_INSN_MATCH (LRO, insn[0]) || 
+                               ARM_INSN_MATCH (SRO, insn[0]))
+                       {
+
+                               uregs = 0xb;
+                               if ((ARM_INSN_REG_RN (insn[0]) == 15) || (ARM_INSN_REG_RM (insn[0]) == 15) || 
+                                       (ARM_INSN_MATCH (SRO, insn[0]) && (ARM_INSN_REG_RD (insn[0]) == 15)))
+                               {
+
+                                       DBPRINTF ("Unboostable insn %lx, DPIS/LRO/SRO\n", insn[0]);
+                                       pc_dep = 1;
+                               }
+                       }
+                       // Rn ,Rd
+                       else if (ARM_INSN_MATCH (DPI, insn[0]) || ARM_INSN_MATCH (LIO, insn[0]) || 
+                                        ARM_INSN_MATCH (SIO, insn[0]))
+                       {
+
+                               uregs = 0x3;
+                               if ((ARM_INSN_REG_RN (insn[0]) == 15) || (ARM_INSN_MATCH (SIO, insn[0]) && 
+                                       (ARM_INSN_REG_RD (insn[0]) == 15)))
+                               {
+
+                                       pc_dep = 1;
+                                       DBPRINTF ("Unboostable insn %lx/%p/%d, DPI/LIO/SIO\n", insn[0], p, p->ainsn.boostable);
+                               }
+                       }
+                       // Rn, Rm, Rs                                   
+                       else if (ARM_INSN_MATCH (DPRS, insn[0]))
+                       {
+
+                               uregs = 0xd;
+                               if ((ARM_INSN_REG_RN (insn[0]) == 15) || (ARM_INSN_REG_RM (insn[0]) == 15) || 
+                                       (ARM_INSN_REG_RS (insn[0]) == 15))
+                               {
+
+                                       pc_dep = 1;
+                                       DBPRINTF ("Unboostable insn %lx, DPRS\n", insn[0]);
+                               }
+                       }
+                       // register list
+                       else if (ARM_INSN_MATCH (SM, insn[0]))
+                       {
+
+                               uregs = 0x10;
+                               if (ARM_INSN_REG_MR (insn[0], 15))
+                               {
+
+                                       DBPRINTF ("Unboostable insn %lx, SM\n", insn[0]);
+                                       pc_dep = 1;
+                               }
+                       }
+                       // check instructions that can write result to SP andu uses PC
+                       if (pc_dep  && (ARM_INSN_REG_RD (ainsn.insn[0]) == 13))
+                       {
+                               static int count;
+                               count++;
+                               //printk ("insn writes result to SP and uses PC: %lx/%d\n", ainsn.insn[0], count);
+                               free_insn_slot (&uprobe_insn_pages, task, p->ainsn.insn, 0);
+                               ret = -EFAULT;
+                       }
+                       else {
+                               if (uregs && pc_dep)
+                               {
+                                       memcpy (insns, pc_dep_insn_execbuf, sizeof (insns));
+                                       if (prep_pc_dep_insn_execbuf (insns, insn[0], uregs) != 0)
+                                       {
+                                               DBPRINTF ("failed to prepare exec buffer for insn %lx!", insn[0]);
+                                               free_insn_slot (&uprobe_insn_pages, task, p->ainsn.insn, 0);
+                                               return -EINVAL;
+                                       }
+                                       //insns[UPROBES_TRAMP_SS_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+                                       insns[6] = (kprobe_opcode_t) (p->addr + 2);
+                               }
+                               else
+                               {
+                                       memcpy (insns, gen_insn_execbuf, sizeof (insns));
+                                       insns[UPROBES_TRAMP_INSN_IDX] = insn[0];
+                               }                       
+                               insns[UPROBES_TRAMP_RET_BREAK_IDX] = UNDEF_INSTRUCTION;
+                               insns[7] = (kprobe_opcode_t) (p->addr + 1);
+                               DBPRINTF ("arch_prepare_uprobe: to %p - %lx %lx %lx %lx %lx %lx %lx %lx %lx", 
+                                               p->ainsn.insn, insns[0], insns[1], insns[2], insns[3], insns[4], 
+                                               insns[5], insns[6], insns[7], insns[8]);
+                       }
+#elif defined(CONFIG_MIPS)
+                       p->ainsn.boostable = 0;
+                       memcpy (insns, gen_insn_execbuf, sizeof (insns));
+                       insns[UPROBES_TRAMP_INSN_IDX] = insn[0];
+                       insns[UPROBES_TRAMP_SS_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+                       insns[UPROBES_TRAMP_RET_BREAK_IDX] = UNDEF_INSTRUCTION;
+                       DBPRINTF ("arch_prepare_uprobe: insn %lx", insn[0]);
+                       DBPRINTF ("arch_prepare_uprobe: to %p - %lx %lx %lx", 
+                                       p->ainsn.insn, insns[0], insns[1], insns[2]);
+#elif defined(CONFIG_X86)
+                       if (can_boost (insn))
+                               p->ainsn.boostable = 0;
+                       else
+                               p->ainsn.boostable = -1;
+                       memcpy (&insns[UPROBES_TRAMP_INSN_IDX], insn, MAX_INSN_SIZE*sizeof(kprobe_opcode_t));
+                       insns[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+                       /*printk ("arch_prepare_uprobe: to %p - %02x %02x %02x %02x %02x %02x %02x %02x "
+                                                                                                  "%02x %02x %02x %02x %02x %02x %02x %02x %02x", p->ainsn.insn 
+                                                                                               , insns[0], insns[1], insns[2], insns[3]
+                                                                                               , insns[4], insns[5], insns[6], insns[7]
+                                                                                               , insns[8], insns[9], insns[10], insns[11]
+                                                                                               , insns[12], insns[13], insns[14], insns[15], insns[16]);*/
+#endif
+                       if (!write_proc_vm_atomic (task, (unsigned long) p->ainsn.insn, insns, sizeof (insns)))
+                       {
+                               panic("failed to write memory %p!\n", p->ainsn.insn);
+                               DBPRINTF ("failed to write insn slot to process memory: insn %p, addr %p, probe %p!", insn, p->ainsn.insn, p->addr);
+                               /*printk ("failed to write insn slot to process memory: %p/%d insn %lx, addr %p, probe %p!\n", 
+                                               task, task->pid, insn, p->ainsn.insn, p->addr);*/
+                               free_insn_slot (&uprobe_insn_pages, task, p->ainsn.insn, 0);
+                               return -EINVAL;
+                       }
+                       /*if(!read_proc_vm_atomic(task, (unsigned long)p->ainsn.insn, insns, 3*MAX_INSN_SIZE*sizeof(kprobe_opcode_t)))
+                          panic("failed to read memory %p!\n", p->addr);
+                          printk("arch_prepare_uprobe: from %p - %lx %lx %lx\n", p->ainsn.insn, insns[0], insns[1], insns[2]); */
+               }
+       }
+
+       return ret;
+}
+
+int
+arch_prepare_uretprobe (struct kretprobe *p, struct task_struct *task)//, struct vm_area_struct **vma, struct page **page, unsigned long **kaddr)
+{
+       int ret = 0;
+#if 0
+       if ((unsigned long) p->kp.addr & 0x01)
+       {
+               DBPRINTF ("Attempt to register kprobe at an unaligned address\n");
+               ret = -EINVAL;
+       }
+#if defined(CONFIG_X86)
+#warning arch_prepare_uretprobe is not implemented for this arch!!!
+#endif
+#endif
+       return ret;
+}
+
+void
+arch_remove_kprobe (struct kprobe *p, struct task_struct *task)
+{
+       //mutex_lock(&kprobe_mutex);
+       if(p->tgid)
+               free_insn_slot (&uprobe_insn_pages, task, p->ainsn.insn, (p->ainsn.boostable == 1));
+       else
+               free_insn_slot (&kprobe_insn_pages, NULL, p->ainsn.insn, (p->ainsn.boostable == 1));
+       //mutex_unlock(&kprobe_mutex)
+}
+
+static unsigned long alloc_user_pages(struct task_struct *task, unsigned long len, 
+                                                                        unsigned long prot, unsigned long flags, int atomic)
+{
+#if 1
+       long ret = 0;
+       struct task_struct *otask = current;
+       struct mm_struct *mm;
+       
+       mm = atomic ? task->active_mm : get_task_mm (task);
+       if (mm){
+               if(!atomic)
+                       down_write (&mm->mmap_sem);
+               // FIXME: its seems to be bad decision to replace 'current' pointer temporarily 
+               current_thread_info()->task = task;
+               ret = (unsigned long)do_mmap_pgoff(0, 0, len, prot, flags, 0);
+               current_thread_info()->task = otask;
+               //printk ("mmap proc %p/%d %p/%d (%ld/%lx)\n", task, task->pid, current, current->pid, ret, ret);
+               if(!atomic){
+                       up_write (&mm->mmap_sem);
+                       mmput(mm);
+               }
+               /*if(ret < 0){
+                       printk ("failed to mmap page in proc %d (%ld)", task->pid, ret);
+                       ret = 0;
+               }*/
+       }
+       else
+               printk ("proc %d has no mm", task->pid);
+       return (unsigned long)ret;
+#else
+       struct file * file = 0;
+       unsigned long addr = 0, pgoff = 0;      
+       struct mm_struct * mm = task->mm;
+       struct vm_area_struct * vma, * prev;
+       struct inode *inode;
+       unsigned int vm_flags;
+       int correct_wcount = 0;
+       int error;
+       struct rb_node ** rb_link, * rb_parent;
+       int accountable = 1;
+       unsigned long charged = 0, reqprot = prot;
+
+    if (file) {
+        if (is_file_hugepages(file))
+            accountable = 0;
+
+        if (!file->f_op || !file->f_op->mmap)
+            return -ENODEV;
+
+        if ((prot & PROT_EXEC) &&
+            (file->f_vfsmnt->mnt_flags & MNT_NOEXEC))
+            return -EPERM;
+    }
+    /*
+     * Does the application expect PROT_READ to imply PROT_EXEC?
+     *
+     * (the exception is when the underlying filesystem is noexec
+     *  mounted, in which case we dont add PROT_EXEC.)
+     */
+    if ((prot & PROT_READ) && (task->personality & READ_IMPLIES_EXEC))
+        if (!(file && (file->f_vfsmnt->mnt_flags & MNT_NOEXEC)))
+            prot |= PROT_EXEC;
+
+    if (!len)
+        return -EINVAL;
+
+    /* Careful about overflows.. */
+    len = PAGE_ALIGN(len);
+    if (!len || len > TASK_SIZE)
+        return -ENOMEM;
+
+    /* offset overflow? */
+    if ((pgoff + (len >> PAGE_SHIFT)) < pgoff)
+       return -EOVERFLOW;
+
+    /* Too many mappings? */
+    if (mm->map_count > sysctl_max_map_count)
+        return -ENOMEM;
+
+    /* Obtain the address to map to. we verify (or select) it and ensure
+     * that it represents a valid section of the address space.
+     */
+    addr = get_unmapped_area(file, addr, len, pgoff, flags);
+    if (addr & ~PAGE_MASK)
+        return addr;
+
+    /* Do simple checking here so the lower-level routines won't have
+     * to. we assume access permissions have been handled by the open
+     * of the memory object, so we don't do any here.
+     */
+    vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) |
+                mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
+
+    if (flags & MAP_LOCKED) {
+        if (!can_do_mlock())
+            return -EPERM;
+        vm_flags |= VM_LOCKED;
+    }
+    /* mlock MCL_FUTURE? */
+    if (vm_flags & VM_LOCKED) {
+        unsigned long locked, lock_limit;
+        locked = len >> PAGE_SHIFT;
+        locked += mm->locked_vm;
+        lock_limit = task->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
+        lock_limit >>= PAGE_SHIFT;
+        if (locked > lock_limit && !capable(CAP_IPC_LOCK))
+            return -EAGAIN;
+    }
+
+    inode = file ? file->f_dentry->d_inode : NULL;
+
+    if (file) {
+        switch (flags & MAP_TYPE) {
+        case MAP_SHARED:
+            if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE))
+                return -EACCES;
+
+            /*
+             * Make sure we don't allow writing to an append-only
+             * file..
+             */
+            if (IS_APPEND(inode) && (file->f_mode & FMODE_WRITE))
+                return -EACCES;
+
+            /*
+             * Make sure there are no mandatory locks on the file.
+             */
+            if (locks_verify_locked(inode))
+                return -EAGAIN;
+
+            vm_flags |= VM_SHARED | VM_MAYSHARE;
+            if (!(file->f_mode & FMODE_WRITE))
+                vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
+
+            /* fall through */
+        case MAP_PRIVATE:
+            if (!(file->f_mode & FMODE_READ))
+                return -EACCES;
+            break;
+
+        default:
+            return -EINVAL;
+        }
+    } else {
+        switch (flags & MAP_TYPE) {
+        case MAP_SHARED:
+            vm_flags |= VM_SHARED | VM_MAYSHARE;
+            break;
+        case MAP_PRIVATE:
+            /*
+             * Set pgoff according to addr for anon_vma.
+             */
+            pgoff = addr >> PAGE_SHIFT;
+            break;
+        default:
+            return -EINVAL;
+        }
+    }
+
+
+    error = security_file_mmap(file, reqprot, prot, flags);
+    if (error)
+        return error;
+            
+    /* Clear old maps */
+    error = -ENOMEM;
+munmap_back:
+        vma = find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent);
+        if (vma && vma->vm_start < addr + len) {
+            if (do_munmap(mm, addr, len))
+                return -ENOMEM;
+            goto munmap_back;
+        }
+
+        /* Check against address space limit. */
+        if (!may_expand_vm(mm, len >> PAGE_SHIFT))
+            return -ENOMEM;
+
+        if (accountable && (!(flags & MAP_NORESERVE) ||
+                        sysctl_overcommit_memory == OVERCOMMIT_NEVER)) {
+            if (vm_flags & VM_SHARED) {
+                /* Check memory availability in shmem_file_setup? */
+                vm_flags |= VM_ACCOUNT;
+            } else if (vm_flags & VM_WRITE) {
+                /*
+                 * Private writable mapping: check memory availability
+                 */
+                charged = len >> PAGE_SHIFT;
+                if (security_vm_enough_memory(charged))
+                    return -ENOMEM;
+                vm_flags |= VM_ACCOUNT;
+            }
+        }
+
+        /*
+         * Can we just expand an old private anonymous mapping?
+         * The VM_SHARED test is necessary because shmem_zero_setup
+         * will create the file object for a shared anonymous map below.
+         */
+        if (!file && !(vm_flags & VM_SHARED) &&
+            vma_merge(mm, prev, addr, addr + len, vm_flags,
+                                    NULL, NULL, pgoff, NULL))
+            goto out;
+
+        /*
+         * Determine the object being mapped and call the appropriate
+         * specific mapper. the address has already been validated, but
+         * not unmapped, but the maps are removed from the list.
+         */
+        vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+        if (!vma) {
+            error = -ENOMEM;
+            goto unacct_error;
+        }
+        memset(vma, 0, sizeof(*vma));
+
+        vma->vm_mm = mm;
+        vma->vm_start = addr;
+        vma->vm_end = addr + len;
+        vma->vm_flags = vm_flags;
+        vma->vm_page_prot = protection_map[vm_flags & 0x0f];
+        vma->vm_pgoff = pgoff;
+
+        if (file) {
+            error = -EINVAL;
+            if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
+                goto free_vma;
+            if (vm_flags & VM_DENYWRITE) {
+                error = deny_write_access(file);
+                if (error)
+                        goto free_vma;
+                correct_wcount = 1;
+            }
+            vma->vm_file = file;
+            get_file(file);
+            error = file->f_op->mmap(file, vma);
+            if (error)
+                goto unmap_and_free_vma;
+        } else if (vm_flags & VM_SHARED) {
+            error = shmem_zero_setup(vma);
+            if (error)
+                goto free_vma;
+        }
+
+        /* We set VM_ACCOUNT in a shared mapping's vm_flags, to inform
+         * shmem_zero_setup (perhaps called through /dev/zero's ->mmap)
+         * that memory reservation must be checked; but that reservation
+         * belongs to shared memory object, not to vma: so now clear it.
+         */
+        if ((vm_flags & (VM_SHARED|VM_ACCOUNT)) == (VM_SHARED|VM_ACCOUNT))
+            vma->vm_flags &= ~VM_ACCOUNT;
+
+        /* Can addr have changed??
+         *
+         * Answer: Yes, several device drivers can do it in their
+         *         f_op->mmap method. -DaveM
+         */
+        addr = vma->vm_start;
+        pgoff = vma->vm_pgoff;
+        vm_flags = vma->vm_flags;
+
+        if (!file || !vma_merge(mm, prev, addr, vma->vm_end,
+                    vma->vm_flags, NULL, file, pgoff, vma_policy(vma))) {
+            file = vma->vm_file;
+            vma_link(mm, vma, prev, rb_link, rb_parent);
+            if (correct_wcount)
+                atomic_inc(&inode->i_writecount);
+        } else {
+            if (file) {
+                if (correct_wcount)
+                    atomic_inc(&inode->i_writecount);
+                fput(file);
+            }
+            mpol_free(vma_policy(vma));
+            kmem_cache_free(vm_area_cachep, vma);
+        }
+out:    
+        mm->total_vm += len >> PAGE_SHIFT;
+        vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
+        if (vm_flags & VM_LOCKED) {
+            mm->locked_vm += len >> PAGE_SHIFT;
+            make_pages_present(addr, addr + len);
+        }
+        if (flags & MAP_POPULATE) {
+            up_write(&mm->mmap_sem);
+            sys_remap_file_pages(addr, len, 0,
+                                    pgoff, flags & MAP_NONBLOCK);
+            down_write(&mm->mmap_sem);
+        }
+        return addr;
+
+unmap_and_free_vma:
+        if (correct_wcount)
+            atomic_inc(&inode->i_writecount);
+        vma->vm_file = NULL;
+        fput(file);
+
+        /* Undo any partial mapping done by a device driver. */
+        unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
+        charged = 0;
+free_vma:
+        kmem_cache_free(vm_area_cachep, vma);
+unacct_error:
+        if (charged)
+            vm_unacct_memory(charged);
+        return error;
+#endif
+}
+
+static int __kprobes
+check_safety (void)
+{
+       int ret = 0;    
+#if defined(CONFIG_PREEMPT) && defined(CONFIG_PM)
+       ret = freeze_processes ();
+       if (ret == 0)
+       {
+               struct task_struct *p, *q;
+               do_each_thread (p, q)
+               {
+                       if (p != current && p->state == TASK_RUNNING && p->pid != 0)
+                       {
+                               printk ("Check failed: %s is running\n", p->comm);
+                               ret = -1;
+                               goto loop_end;
+                       }
+               }
+               while_each_thread (p, q);
+       }
+loop_end:
+       thaw_processes ();
+#else
+       synchronize_sched ();
+#endif
+       return ret;
+}
+
+/**
+ * get_us_insn_slot() - Find a slot on an executable page for an instruction.
+ * We allocate an executable page if there's no room on existing ones.
+ */
+kprobe_opcode_t __kprobes *
+get_insn_slot (struct task_struct *task, int atomic)
+{
+       struct kprobe_insn_page *kip;
+       struct hlist_node *pos;
+       struct hlist_head *page_list = task ? &uprobe_insn_pages : &kprobe_insn_pages;
+       unsigned slots_per_page = INSNS_PER_PAGE, slot_size = MAX_INSN_SIZE;
+
+       if(task) {
+               slots_per_page = INSNS_PER_PAGE/UPROBES_TRAMP_LEN;
+               slot_size = UPROBES_TRAMP_LEN;
+       }
+       else {
+               slots_per_page = INSNS_PER_PAGE/KPROBES_TRAMP_LEN;
+               slot_size = KPROBES_TRAMP_LEN;          
+       }
+       
+retry:
+       hlist_for_each_entry (kip, pos, page_list, hlist)
+       {
+               if (kip->nused < slots_per_page)
+               {
+                       int i;
+                       for (i = 0; i < slots_per_page; i++)
+                       {
+                               if (kip->slot_used[i] == SLOT_CLEAN)
+                               {
+                                       if(!task || (kip->tgid == task->tgid)){
+                                               kip->slot_used[i] = SLOT_USED;
+                                               kip->nused++;
+                                               return kip->insns + (i * slot_size);
+                                       }
+                               }
+                       }
+                       /* Surprise!  No unused slots.  Fix kip->nused. */
+                       kip->nused = slots_per_page;
+               }
+       }
+
+       /* If there are any garbage slots, collect it and try again. */
+       if(task) {
+               if (uprobe_garbage_slots && collect_garbage_slots(page_list, task) == 0)
+                       goto retry;
+       }
+       else {
+               if (kprobe_garbage_slots && collect_garbage_slots(page_list, task) == 0)
+                       goto retry;             
+       }
+
+       /* All out of space.  Need to allocate a new page. Use slot 0. */
+       kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_KERNEL);
+       if (!kip)
+               return NULL;
+
+       kip->slot_used = kmalloc(sizeof(char)*slots_per_page, GFP_KERNEL);
+       if (!kip->slot_used){
+               kfree(kip);
+               return NULL;
+       }
+
+       if(task) {
+               kip->insns = (kprobe_opcode_t *)alloc_user_pages(task, PAGE_SIZE, 
+                                               PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, atomic);
+       }
+       else {
+#if defined(CONFIG_X86)
+               kip->insns = module_alloc (PAGE_SIZE);
+#else
+               kip->insns = kmalloc(PAGE_SIZE, GFP_KERNEL);
+#endif
+       }
+       if (!kip->insns)
+       {
+               kfree (kip->slot_used);
+               kfree (kip);
+               return NULL;
+       }       
+       INIT_HLIST_NODE (&kip->hlist);
+       hlist_add_head (&kip->hlist, page_list);
+       memset(kip->slot_used, SLOT_CLEAN, slots_per_page);
+       kip->slot_used[0] = SLOT_USED;
+       kip->nused = 1;
+       kip->ngarbage = 0;
+       kip->tgid = task ? task->tgid : 0;
+       return kip->insns;
+}
+
+/* Return 1 if all garbages are collected, otherwise 0. */
+static int __kprobes
+collect_one_slot (struct hlist_head *page_list, struct task_struct *task, 
+                                       struct kprobe_insn_page *kip, int idx)
+{
+       struct mm_struct *mm;
+
+       kip->slot_used[idx] = SLOT_CLEAN;
+       kip->nused--;
+       DBPRINTF("collect_one_slot: nused=%d", kip->nused);
+       if (kip->nused == 0)
+       {
+               /*
+                * Page is no longer in use.  Free it unless
+                * it's the last one.  We keep the last one
+                * so as not to have to set it up again the
+                * next time somebody inserts a probe.
+                */
+               hlist_del (&kip->hlist);
+               if (!task && hlist_empty (page_list))
+               {
+                       INIT_HLIST_NODE (&kip->hlist);
+                       hlist_add_head (&kip->hlist, page_list);
+               }
+               else
+               {
+                       if(task){
+                               //printk("collect_one_slot %p/%d\n", task, task->pid);
+                               mm = get_task_mm (task);
+                               if (mm){                        
+                                       down_write (&mm->mmap_sem);
+                                       do_munmap(mm, (unsigned long)(kip->insns), PAGE_SIZE);
+                                       up_write (&mm->mmap_sem);
+                                       mmput(mm);
+                               }
+                               kip->tgid = 0;
+                       }
+                       else {
+#if defined(CONFIG_X86)                        
+                               module_free (NULL, kip->insns);
+#else
+                               vfree(kip->insns);
+#endif
+                       }
+                       kfree (kip->slot_used);
+                       kfree (kip);
+               }
+               return 1;
+       }
+       return 0;
+}
+
+static int __kprobes
+collect_garbage_slots (struct hlist_head *page_list, struct task_struct *task)
+{
+       struct kprobe_insn_page *kip;
+       struct hlist_node *pos, *next;
+       unsigned slots_per_page = INSNS_PER_PAGE;
+
+       /* Ensure no-one is preepmted on the garbages */
+       if (!task && check_safety() != 0)
+               return -EAGAIN;
+
+       if(task)
+               slots_per_page = INSNS_PER_PAGE/UPROBES_TRAMP_LEN;
+       else
+               slots_per_page = INSNS_PER_PAGE/KPROBES_TRAMP_LEN;
+       
+       hlist_for_each_entry_safe (kip, pos, next, page_list, hlist)
+       {
+               int i;
+               if ((task && (kip->tgid != task->tgid)) || (kip->ngarbage == 0))
+                       continue;
+               kip->ngarbage = 0;      /* we will collect all garbages */
+               for (i = 0; i < slots_per_page; i++)
+               {
+                       if (kip->slot_used[i] == SLOT_DIRTY && collect_one_slot (page_list, task, kip, i))
+                               break;
+               }
+       }
+       if(task)        uprobe_garbage_slots = 0;
+       else            kprobe_garbage_slots = 0;
+       return 0;
+}
+
+void purge_garbage_uslots(struct task_struct *task, int atomic)
+{
+       if(collect_garbage_slots(&uprobe_insn_pages, task))
+               panic("failed to collect garbage slotsfo for task %s/%d/%d", task->comm, task->tgid, task->pid);
+}
+
+void __kprobes
+free_insn_slot (struct hlist_head *page_list, struct task_struct *task, kprobe_opcode_t *slot, int dirty)
+{
+       struct kprobe_insn_page *kip;
+       struct hlist_node *pos;
+       unsigned slots_per_page = INSNS_PER_PAGE, slot_size = MAX_INSN_SIZE;
+
+       if(task) {      
+               slots_per_page = INSNS_PER_PAGE/UPROBES_TRAMP_LEN;
+               slot_size = UPROBES_TRAMP_LEN;
+       }
+       else {
+               slots_per_page = INSNS_PER_PAGE/KPROBES_TRAMP_LEN;
+               slot_size = KPROBES_TRAMP_LEN;
+       }
+       
+       DBPRINTF("free_insn_slot: dirty %d, %p/%d", dirty, task, task?task->pid:0);
+       hlist_for_each_entry (kip, pos, page_list, hlist)
+       {
+               DBPRINTF("free_insn_slot: kip->insns=%p slot=%p", kip->insns, slot);
+               if ((kip->insns <= slot) && (slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)))
+               {
+                       int i = (slot - kip->insns) / slot_size;
+                       if (dirty)
+                       {
+                               kip->slot_used[i] = SLOT_DIRTY;
+                               kip->ngarbage++;
+                       }
+                       else
+                       {
+                               collect_one_slot (page_list, task, kip, i);
+                       }
+                       break;
+               }
+       }
+
+       if (dirty){
+               if(task){
+                       if(++uprobe_garbage_slots > slots_per_page)
+                               collect_garbage_slots (page_list, task);
+               }
+               else if(++kprobe_garbage_slots > slots_per_page)
+                       collect_garbage_slots (page_list, task);
+       }
+}
+
+static void
+prepare_singlestep (struct kprobe *p, struct pt_regs *regs)
+{
+#if defined(CONFIG_X86)
+       if(p->ss_addr)
+       {
+               regs->EREG (ip) = (unsigned long)p->ss_addr;
+               p->ss_addr = NULL;
+       }
+       else
+       {
+               regs->EREG (flags) |= TF_MASK;
+               regs->EREG (flags) &= ~IF_MASK;
+               /*single step inline if the instruction is an int3 */
+               if (p->opcode == BREAKPOINT_INSTRUCTION){
+                       regs->EREG (ip) = (unsigned long) p->addr;
+                       //printk("break_insn!!!\n");
+               }
+               else
+                       regs->EREG (ip) = (unsigned long) p->ainsn.insn;
+       }
+       //printk("singlestep %p/%lx\n", p->addr, p->ainsn.insn);
+#elif defined(CONFIG_ARM)
+       if(p->ss_addr)
+       {
+               regs->uregs[15] = (unsigned long) p->ss_addr;
+               p->ss_addr = NULL;
+       }
+       else
+               regs->uregs[15] = (unsigned long) p->ainsn.insn;
+       //DBPRINTF("prepare_singlestep: %p/%p/%d\n", p, p->addr, p->ainsn.boostable);
+#elif defined(CONFIG_MIPS)
+       if(p->ss_addr)
+       {
+               regs->cp0_epc = (unsigned long) p->ss_addr;
+               p->ss_addr = NULL;
+       }
+       else
+               regs->cp0_epc = (unsigned long) p->ainsn.insn;
+#endif
+       //if(p->tgid)
+               //printk("prepare_singlestep: %p/%d to %lx\n", p->addr, p->ainsn.boostable, regs->EREG (ip));
+       //printk("SS[%lx] to %lx/%lx/%lx\n", p->addr, regs->uregs[15], p->ss_addr, p);
+}
+
+static void __kprobes
+save_previous_kprobe (struct kprobe_ctlblk *kcb, struct kprobe *cur_p)
+{
+       if (kcb->prev_kprobe.kp != NULL)
+       {
+               panic ("no space to save new probe[%lu]: task = %d/%s, prev %d/%p, current %d/%p, new %d/%p,",
+                               nCount, current->pid, current->comm, kcb->prev_kprobe.kp->tgid, kcb->prev_kprobe.kp->addr, 
+                               kprobe_running()->tgid, kprobe_running()->addr, cur_p->tgid, cur_p->addr);
+       }
+#if defined(CONFIG_X86)
+       kcb->prev_kprobe.old_eflags = kcb->kprobe_old_eflags;
+       kcb->prev_kprobe.saved_eflags = kcb->kprobe_saved_eflags;
+#endif
+       kcb->prev_kprobe.kp = kprobe_running ();
+       kcb->prev_kprobe.status = kcb->kprobe_status;
+}
+
+static void __kprobes
+restore_previous_kprobe (struct kprobe_ctlblk *kcb)
+{
+       __get_cpu_var (current_kprobe) = kcb->prev_kprobe.kp;
+       kcb->kprobe_status = kcb->prev_kprobe.status;
+       kcb->prev_kprobe.kp = NULL;
+       kcb->prev_kprobe.status = 0;
+#if defined(CONFIG_X86)
+       kcb->kprobe_old_eflags = kcb->prev_kprobe.old_eflags;
+       kcb->kprobe_saved_eflags = kcb->prev_kprobe.saved_eflags;
+#endif
+}
+
+static void __kprobes
+set_current_kprobe (struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
+{
+       __get_cpu_var (current_kprobe) = p;
+       DBPRINTF ("set_current_kprobe[%lu]: p=%p addr=%p\n", nCount, p, p->addr);
+#if defined(CONFIG_X86)
+       kcb->kprobe_saved_eflags = kcb->kprobe_old_eflags = (regs->EREG (flags) & (TF_MASK | IF_MASK));
+       if (is_IF_modifier (p->opcode))
+               kcb->kprobe_saved_eflags &= ~IF_MASK;
+#endif
+}
+
+#define REENTER
+
+#ifdef _DEBUG
+int gSilent = 1;
+#endif
+
+#if defined(CONFIG_X86)
+int
+kprobe_handler (struct pt_regs *regs)
+{
+       struct kprobe *p = 0;
+       int ret = 0, pid = 0, retprobe = 0, reenter = 0;
+       kprobe_opcode_t *addr = NULL;
+       struct kprobe_ctlblk *kcb;      
+       
+       nCount++;
+
+       /* We're in an interrupt, but this is clear and BUG()-safe. */
+#ifdef _DEBUG
+       gSilent = 1;
+#endif
+
+       addr = (kprobe_opcode_t *) (regs->EREG (ip) - sizeof (kprobe_opcode_t));
+       DBPRINTF ("KPROBE[%lu]: regs->eip = 0x%lx addr = 0x%p\n", nCount, regs->EREG (ip), addr);
+       
+       preempt_disable ();
+       
+       kcb = get_kprobe_ctlblk ();
+
+       if (user_mode_vm(regs))
+       {
+#ifdef _DEBUG
+               gSilent = 0;
+#endif
+               //printk("exception[%lu] from user mode %s/%u/%u addr %p.\n", nCount, current->comm, current->pid, current->tgid, addr);
+               pid = current->tgid;
+       }
+       
+       /* Check we're not actually recursing */
+       if (kprobe_running ())
+       {
+               DBPRINTF ("lock???");
+               p = get_kprobe (addr, pid, current);
+               if (p)
+               {
+                       DBPRINTF ("reenter p = %p", p);
+                       if(!pid){
+                               if (kcb->kprobe_status == KPROBE_HIT_SS && *p->ainsn.insn == BREAKPOINT_INSTRUCTION)
+                               {
+                                       regs->EREG (flags) &= ~TF_MASK;
+                                       regs->EREG (flags) |= kcb->kprobe_saved_eflags;
+                                       goto no_kprobe;
+                               }
+                       }
+                       else {
+                               //#warning BREAKPOINT_INSTRUCTION user mode handling is missed!!! 
+                       }
+                               
+                       /* We have reentered the kprobe_handler(), since
+                        * another probe was hit while within the handler.
+                        * We here save the original kprobes variables and
+                        * just single step on the instruction of the new probe
+                        * without calling any user handlers.
+                        */
+                       save_previous_kprobe (kcb, p);
+                       set_current_kprobe (p, regs, kcb);
+                       kprobes_inc_nmissed_count (p);
+                       prepare_singlestep (p, regs);
+                       kcb->kprobe_status = KPROBE_REENTER;
+#ifdef _DEBUG
+                       gSilent = 1;
+#endif
+                       return 1;
+               }
+               else
+               {
+                       if(!pid){
+                               if (*addr != BREAKPOINT_INSTRUCTION)
+                               {
+                                       /* The breakpoint instruction was removed by
+                                        * another cpu right after we hit, no further
+                                        * handling of this interrupt is appropriate
+                                        */
+                                       regs->EREG (ip) -= sizeof (kprobe_opcode_t);
+                                       ret = 1;
+                                       goto no_kprobe;
+                               }
+                       }
+                       else {
+                               //#warning BREAKPOINT_INSTRUCTION user mode handling is missed!!! 
+                               //we can reenter probe upon uretprobe exception   
+                               DBPRINTF ("check for UNDEF_INSTRUCTION %p\n", addr);
+                               // UNDEF_INSTRUCTION from user space
+                               p = get_kprobe_by_insn_slot (addr-UPROBES_TRAMP_RET_BREAK_IDX, pid, current);
+                               if (p) {
+                                       save_previous_kprobe (kcb, p);
+                                       kcb->kprobe_status = KPROBE_REENTER;
+                                       reenter = 1;
+                                       retprobe = 1;
+                                       DBPRINTF ("uretprobe %p\n", addr);
+                               }
+                       }
+                       if(!p){
+                               p = __get_cpu_var (current_kprobe);
+                               if(p->tgid)
+                                       panic("after uhandler");
+                               DBPRINTF ("kprobe_running !!! p = 0x%p p->break_handler = 0x%p", p, p->break_handler);
+                               if (p->break_handler && p->break_handler (p, regs))
+                               {
+                                       DBPRINTF ("kprobe_running !!! goto ss");
+                                       goto ss_probe;
+                               }
+                               DBPRINTF ("kprobe_running !!! goto no");
+                               DBPRINTF ("no_kprobe");
+                               goto no_kprobe;
+                       }
+               }
+       }
+
+       DBPRINTF ("get_kprobe %p", addr);
+       if (!p)
+               p = get_kprobe (addr, pid, current);
+       if (!p)
+       {
+               if(!pid){
+                       if (*addr != BREAKPOINT_INSTRUCTION)
+                       {
+                               /*
+                                * The breakpoint instruction was removed right
+                                * after we hit it.  Another cpu has removed
+                                * either a probepoint or a debugger breakpoint
+                                * at this address.  In either case, no further
+                                * handling of this interrupt is appropriate.
+                                * Back up over the (now missing) int3 and run
+                                * the original instruction.
+                                */
+                               regs->EREG (ip) -= sizeof (kprobe_opcode_t);
+                               ret = 1;
+                       }
+               }
+               else {
+                       //#warning BREAKPOINT_INSTRUCTION user mode handling is missed!!! 
+                       DBPRINTF ("search UNDEF_INSTRUCTION %p\n", addr);
+                       // UNDEF_INSTRUCTION from user space
+                       p = get_kprobe_by_insn_slot (addr-UPROBES_TRAMP_RET_BREAK_IDX, pid, current);
+                       if (!p) {
+                               // Not one of ours: let kernel handle it
+                               DBPRINTF ("no_kprobe");
+                               //printk("no_kprobe2 ret = %d\n", ret);
+                               goto no_kprobe;
+                       }
+                       retprobe = 1;
+                       DBPRINTF ("uretprobe %p\n", addr);
+               }
+               if(!p) {
+                       /* Not one of ours: let kernel handle it */
+                       DBPRINTF ("no_kprobe");
+                       goto no_kprobe;
+               }
+       }
+       set_current_kprobe (p, regs, kcb);
+       if(!reenter)
+               kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+       if (retprobe)           //(einsn == UNDEF_INSTRUCTION)
+               ret = trampoline_probe_handler (p, regs);
+       else if (p->pre_handler)
+               ret = p->pre_handler (p, regs);
+
+       if (ret)
+       {
+               if (ret == 2) { // we have alreadyc called the handler, so just single step the instruction
+                       DBPRINTF ("p->pre_handler[%lu] 2", nCount);
+                       goto ss_probe;
+               }
+               DBPRINTF ("p->pre_handler[%lu] 1", nCount);
+               /* handler has already set things up, so skip ss setup */
+#ifdef _DEBUG
+               gSilent = 1;
+#endif
+               return 1;
+       }
+       DBPRINTF ("p->pre_handler[%lu] 0", nCount);
+
+ss_probe:
+       DBPRINTF ("p = %p\n", p);
+       DBPRINTF ("p->opcode = 0x%lx *p->addr = 0x%lx p->addr = 0x%p\n", (unsigned long) p->opcode, p->tgid ? 0 : (unsigned long) (*p->addr), p->addr);
+
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
+       if (p->ainsn.boostable == 1 && !p->post_handler)
+       {
+               /* Boost up -- we can execute copied instructions directly */
+               reset_current_kprobe ();
+               regs->EREG (ip) = (unsigned long) p->ainsn.insn;
+               preempt_enable_no_resched ();
+#ifdef _DEBUG
+               gSilent = 1;
+#endif
+               return 1;
+       }
+#endif // !CONFIG_PREEMPT
+       prepare_singlestep (p, regs);
+       kcb->kprobe_status = KPROBE_HIT_SS;
+#ifdef _DEBUG
+       gSilent = 1;
+#endif
+       return 1;
+
+no_kprobe:
+
+       preempt_enable_no_resched ();
+#ifdef _DEBUG
+       gSilent = 1;
+#endif
+       return ret;
+}
+
+#else
+int
+kprobe_handler (struct pt_regs *regs)
+{
+       struct kprobe *p = 0;
+       int ret = 0, pid = 0, retprobe = 0, reenter = 0;
+       kprobe_opcode_t *addr = NULL, *ssaddr = 0;
+       struct kprobe_ctlblk *kcb;
+       
+       nCount++;
+       /* We're in an interrupt, but this is clear and BUG()-safe. */
+#ifdef _DEBUG
+       gSilent = 1;
+#endif
+#if defined(CONFIG_MIPS)
+       addr = (kprobe_opcode_t *) regs->cp0_epc;
+       DBPRINTF ("regs->regs[ 31 ] = 0x%lx\n", regs->regs[31]);
+#elif defined(CONFIG_ARM)
+       addr = (kprobe_opcode_t *) (regs->uregs[15] - 4);
+       DBPRINTF ("KPROBE[%lu]: regs->uregs[15] = 0x%lx addr = 0x%p\n", nCount, regs->uregs[15], addr);
+       regs->uregs[15] -= 4;
+       //DBPRINTF("regs->uregs[14] = 0x%lx\n", regs->uregs[14]);
+#else
+#error implement how to get exception address for this arch!!!
+#endif // ARCH
+
+       preempt_disable ();
+
+       kcb = get_kprobe_ctlblk ();
+
+       if (user_mode (regs))
+       {
+#ifdef _DEBUG
+               gSilent = 0;
+#endif
+               //DBPRINTF("exception[%lu] from user mode %s/%u addr %p (%lx).", nCount, current->comm, current->pid, addr, regs->uregs[14]);
+               pid = current->tgid;
+       }
+
+       /* Check we're not actually recursing */
+       if (kprobe_running ())
+       {
+               DBPRINTF ("lock???");
+               p = get_kprobe (addr, pid, current);
+               if (p)
+               {
+                       if(!pid && (addr == (kprobe_opcode_t *)kretprobe_trampoline)){
+                               save_previous_kprobe (kcb, p);
+                               kcb->kprobe_status = KPROBE_REENTER;
+                               reenter = 1;
+                       }
+                       else {
+                               /* We have reentered the kprobe_handler(), since
+                                * another probe was hit while within the handler.
+                                * We here save the original kprobes variables and
+                                * just single step on the instruction of the new probe
+                                * without calling any user handlers.
+                                */
+                               if(!p->ainsn.boostable){
+                                       save_previous_kprobe (kcb, p);
+                                       set_current_kprobe (p, regs, kcb);
+                               }
+                               kprobes_inc_nmissed_count (p);
+                               prepare_singlestep (p, regs);
+                               if(!p->ainsn.boostable)
+                                       kcb->kprobe_status = KPROBE_REENTER;
+                               preempt_enable_no_resched ();
+                               return 1;
+                       }
+               }
+               else
+               {
+                       if(pid) { //we can reenter probe upon uretprobe exception   
+                               DBPRINTF ("check for UNDEF_INSTRUCTION %p\n", addr);
+                               // UNDEF_INSTRUCTION from user space
+                               p = get_kprobe_by_insn_slot (addr-UPROBES_TRAMP_RET_BREAK_IDX, pid, current);
+                               if (p) {
+                                       save_previous_kprobe (kcb, p);
+                                       kcb->kprobe_status = KPROBE_REENTER;
+                                       reenter = 1;
+                                       retprobe = 1;
+                                       DBPRINTF ("uretprobe %p\n", addr);
+                               }
+                       }
+                       if(!p) {
+                               p = __get_cpu_var (current_kprobe);
+#ifdef _DEBUG
+                               if (p->tgid) gSilent = 0;
+#endif
+                               DBPRINTF ("kprobe_running !!! p = 0x%p p->break_handler = 0x%p", p, p->break_handler);
+                               /*if (p->break_handler && p->break_handler(p, regs)) {
+                                  DBPRINTF("kprobe_running !!! goto ss");
+                                  goto ss_probe;
+                                  } */                 
+                               DBPRINTF ("unknown uprobe at %p cur at %p/%p\n", addr, p->addr, p->ainsn.insn);
+                               if(pid)
+                                       ssaddr = p->ainsn.insn + UPROBES_TRAMP_SS_BREAK_IDX;
+                               else
+                                       ssaddr = p->ainsn.insn + KPROBES_TRAMP_SS_BREAK_IDX;                            
+                               if (addr == ssaddr)
+                               {
+#if defined(CONFIG_ARM)
+                                       regs->uregs[15] = (unsigned long) (p->addr + 1);
+                                       DBPRINTF ("finish step at %p cur at %p/%p, redirect to %lx\n", addr, p->addr, p->ainsn.insn, regs->uregs[15]);
+#elif defined(CONFIG_MIPS)
+                                       regs->cp0_epc = (unsigned long) (p->addr + 1);
+                                       DBPRINTF ("finish step at %p cur at %p/%p, redirect to %lx\n", addr, p->addr, p->ainsn.insn, regs->cp0_epc);
+#else
+#warning uprobe single step is not implemented for this arch!!!
+#endif
+                                       if (kcb->kprobe_status == KPROBE_REENTER) {
+                                               restore_previous_kprobe (kcb);
+                                       }
+                                       else {
+                                               reset_current_kprobe ();
+                                       }
+                               }
+                               DBPRINTF ("kprobe_running !!! goto no");
+                               ret = 1;
+                               /* If it's not ours, can't be delete race, (we hold lock). */
+                               DBPRINTF ("no_kprobe");
+                               goto no_kprobe;
+                       }
+               }
+       }
+
+       //if(einsn != UNDEF_INSTRUCTION) {
+       DBPRINTF ("get_kprobe %p-%d", addr, pid);
+       if (!p)
+               p = get_kprobe (addr, pid, current);
+       if (!p)
+       {
+               if(pid) {
+                       DBPRINTF ("search UNDEF_INSTRUCTION %p\n", addr);
+                       // UNDEF_INSTRUCTION from user space
+                       p = get_kprobe_by_insn_slot (addr-UPROBES_TRAMP_RET_BREAK_IDX, pid, current);
+                       if (!p) {
+                               /* Not one of ours: let kernel handle it */
+                               DBPRINTF ("no_kprobe");
+                               //printk("no_kprobe2 ret = %d\n", ret);
+                               goto no_kprobe;
+                       }
+                       retprobe = 1;
+                       DBPRINTF ("uretprobe %p\n", addr);
+               }
+               else {
+                       /* Not one of ours: let kernel handle it */
+                       DBPRINTF ("no_kprobe");
+                       //printk("no_kprobe2 ret = %d\n", ret);
+                       goto no_kprobe;
+               }
+       }
+#ifdef _DEBUG
+       if (p->tgid) gSilent = 0;
+#endif
+       
+       set_current_kprobe (p, regs, kcb);
+       if(!reenter)
+               kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+       if (retprobe)           //(einsn == UNDEF_INSTRUCTION)
+               ret = trampoline_probe_handler (p, regs);
+       else if (p->pre_handler)
+       {
+               ret = p->pre_handler (p, regs);
+               if(!p->ainsn.boostable)
+                       kcb->kprobe_status = KPROBE_HIT_SS;
+               else if(p->pre_handler != trampoline_probe_handler)
+                       reset_current_kprobe ();                        
+       }
+
+       if (ret)
+       {
+               DBPRINTF ("p->pre_handler[%lu] 1", nCount);
+               /* handler has already set things up, so skip ss setup */
+               return 1;
+       }
+       DBPRINTF ("p->pre_handler 0");
+
+no_kprobe:
+       preempt_enable_no_resched ();
+#ifdef _DEBUG
+       gSilent = 1;
+#endif
+       return ret;
+}
+#endif
+
+extern struct kretprobe *sched_rp;
+
+static void patch_suspended_task_ret_addr(struct task_struct *p, struct kretprobe *rp)
+{
+       struct kretprobe_instance *ri = NULL;
+       struct hlist_node *node, *tmp; 
+       struct hlist_head *head;
+       unsigned long flags;
+       int found = 0;
+       
+       spin_lock_irqsave (&kretprobe_lock, flags); 
+       head = kretprobe_inst_table_head (p);
+       hlist_for_each_entry_safe (ri, node, tmp, head, hlist){
+               if ((ri->rp == rp) && (p == ri->task)){
+                       found = 1;
+                       break; 
+               }
+       }
+       spin_unlock_irqrestore (&kretprobe_lock, flags); 
+
+#ifdef CONFIG_ARM
+
+#ifndef task_thread_info
+#define task_thread_info(task) (task)->thread_info
+#endif // task_thread_info
+
+       if (found){
+               // update PC
+               if(thread_saved_pc(p) != (unsigned long)&kretprobe_trampoline){
+                       ri->ret_addr = (kprobe_opcode_t *)thread_saved_pc(p);
+                       task_thread_info(p)->cpu_context.pc = (unsigned long) &kretprobe_trampoline;
+               }
+               return; 
+       }
+       
+       if ((ri = get_free_rp_inst(rp)) != NULL)
+       {
+               ri->rp = rp; 
+               ri->rp2 = NULL; 
+               ri->task = p;
+               ri->ret_addr = (kprobe_opcode_t *)thread_saved_pc(p);
+               task_thread_info(p)->cpu_context.pc = (unsigned long) &kretprobe_trampoline;
+               add_rp_inst (ri);
+//             printk("change2 saved pc %p->%p for %d/%d/%p\n", ri->ret_addr, &kretprobe_trampoline, p->tgid, p->pid, p);
+       }
+       else{
+               printk("no ri for %d\n", p->pid);
+               BUG();                          
+       }
+#endif // CONFIG_ARM
+}
+
+typedef kprobe_opcode_t (*entry_point_t) (unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
+int
+setjmp_pre_handler (struct kprobe *p, struct pt_regs *regs)
+{
+       struct jprobe *jp = container_of (p, struct jprobe, kp);
+       kprobe_pre_entry_handler_t pre_entry;
+       entry_point_t entry;
+       
+#if defined(CONFIG_X86)
+       unsigned long addr, args[6];    
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk ();
+
+       DBPRINTF ("setjmp_pre_handler %p:%d", p->addr, p->tgid);
+       pre_entry = (kprobe_pre_entry_handler_t) jp->pre_entry;
+       entry = (entry_point_t) jp->entry;
+       if(p->tgid) {
+               regs->EREG (flags) &= ~IF_MASK;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+               trace_hardirqs_off ();
+#endif
+               if (p->tgid == current->tgid)
+               {
+                       // read first 6 args from stack
+                       if (!read_proc_vm_atomic (current, regs->EREG(sp)+4, args, sizeof(args)))
+                               panic ("failed to read user space func arguments %lx!\n", regs->EREG(sp)+4);
+                       if (pre_entry)
+                               p->ss_addr = pre_entry (jp->priv_arg, regs);
+                       if (entry)
+                               entry (args[0], args[1], args[2], args[3], args[4], args[5]);
+               }
+               else
+                       uprobe_return ();
+               
+               return 2;
+       }
+       else {
+               kcb->jprobe_saved_regs = *regs;
+               kcb->jprobe_saved_esp = &regs->EREG (sp);
+               addr = (unsigned long) (kcb->jprobe_saved_esp);
+       
+               /*
+                * TBD: As Linus pointed out, gcc assumes that the callee
+                * owns the argument space and could overwrite it, e.g.
+                * tailcall optimization. So, to be absolutely safe
+                * we also save and restore enough stack bytes to cover
+                * the argument area.
+                */
+               memcpy (kcb->jprobes_stack, (kprobe_opcode_t *) addr, MIN_STACK_SIZE (addr));
+               regs->EREG (flags) &= ~IF_MASK;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+               trace_hardirqs_off ();
+#endif
+               if (pre_entry)
+                       p->ss_addr = pre_entry (jp->priv_arg, regs);
+               regs->EREG (ip) = (unsigned long) (jp->entry);
+       }
+       
+       return 1;
+#else //!CONFIG_X86
+# ifdef REENTER
+       p = __get_cpu_var (current_kprobe);
+# endif
+
+       DBPRINTF ("pjp = 0x%p jp->entry = 0x%p", jp, jp->entry);
+       entry = (entry_point_t) jp->entry;
+       pre_entry = (kprobe_pre_entry_handler_t) jp->pre_entry;
+       //if(!entry)
+       //      DIE("entry NULL", regs)
+       DBPRINTF ("entry = 0x%p jp->entry = 0x%p", entry, jp->entry);
+
+       //call handler for all kernel probes and user space ones which belong to current tgid
+       if (!p->tgid || (p->tgid == current->tgid))
+       {               
+               if(!p->tgid && (p->addr == sched_addr) && sched_rp){
+                       struct task_struct *p, *g;
+                       rcu_read_lock();
+                       //swapper task
+                       if(current != &init_task)
+                               patch_suspended_task_ret_addr(&init_task, sched_rp);
+                       // other tasks
+                       do_each_thread(g, p){
+                               if(p == current)
+                                       continue;                                                                       
+                               patch_suspended_task_ret_addr(p, sched_rp);
+                       } while_each_thread(g, p);
+                       rcu_read_unlock();
+               }
+               if (pre_entry)
+                       p->ss_addr = pre_entry (jp->priv_arg, regs);
+               if (entry){
+# if defined(CONFIG_MIPS)
+                       entry (regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7], regs->regs[8], regs->regs[9]);
+# elif defined(CONFIG_ARM)
+                       entry (regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3, regs->ARM_r4, regs->ARM_r5);
+# endif        // ARCH
+               }
+               else {
+                       if (p->tgid)
+                               uprobe_return ();
+                       else
+                               jprobe_return ();
+               }
+       }
+       else if (p->tgid)
+               uprobe_return ();
+
+       prepare_singlestep (p, regs);
+# ifdef _DEBUG
+       p->step_count++;
+# endif
+       
+       return 1;       
+#endif //!CONFIG_X86
+}
+
+void __kprobes
+jprobe_return (void)
+{
+#if defined(CONFIG_X86)
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk ();
+
+       asm volatile("       xchgl   %%ebx,%%esp     \n"
+                       "       int3                    \n"
+                       "       .globl jprobe_return_end        \n"
+                               "       jprobe_return_end:      \n"
+                               "       nop                     \n"::"b" (kcb->jprobe_saved_esp):"memory");
+#else
+       preempt_enable_no_resched();
+#endif
+}
+
+void __kprobes
+uprobe_return (void)
+{
+#if defined(CONFIG_X86)
+       /*struct kprobe_ctlblk *kcb = get_kprobe_ctlblk ();
+
+       asm volatile("       xchgl   %%ebx,%%esp     \n"
+                       "       int3                    \n"
+                       "       .globl jprobe_return_end        \n"
+                               "       jprobe_return_end:      \n"
+                               "       nop                     \n"::"b" (kcb->jprobe_saved_esp):"memory");*/
+#else
+       preempt_enable_no_resched ();
+#endif
+}
+
+#if defined(CONFIG_X86)
+/*
+ * Called after single-stepping.  p->addr is the address of the
+ * instruction whose first byte has been replaced by the "int 3"
+ * instruction.  To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction.  The address of this
+ * copy is p->ainsn.insn.
+ *
+ * This function prepares to return from the post-single-step
+ * interrupt.  We have to fix up the stack as follows:
+ *
+ * 0) Except in the case of absolute or indirect jump or call instructions,
+ * the new eip is relative to the copied instruction.  We need to make
+ * it relative to the original instruction.
+ *
+ * 1) If the single-stepped instruction was pushfl, then the TF and IF
+ * flags are set in the just-pushed eflags, and may need to be cleared.
+ *
+ * 2) If the single-stepped instruction was a call, the return address
+ * that is atop the stack is the address following the copied instruction.
+ * We need to make it the address following the original instruction.
+ *
+ * This function also checks instruction size for preparing direct execution.
+ */
+static void __kprobes
+resume_execution (struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
+{
+       unsigned long *tos, tos_dword = 0;
+       unsigned long copy_eip = (unsigned long) p->ainsn.insn;
+       unsigned long orig_eip = (unsigned long) p->addr;
+       kprobe_opcode_t insns[2];
+
+       regs->EREG (flags) &= ~TF_MASK;
+
+       if(p->tgid){
+               tos = (unsigned long *) &tos_dword;
+               if (!read_proc_vm_atomic (current, regs->EREG (sp), &tos_dword, sizeof(tos_dword)))
+                       panic ("failed to read dword from top of the user space stack %lx!\n", regs->EREG (sp));
+               if (!read_proc_vm_atomic (current, (unsigned long)p->ainsn.insn, insns, 2*sizeof(kprobe_opcode_t)))
+                       panic ("failed to read first 2 opcodes of instruction copy from user space %p!\n", p->ainsn.insn);
+       }
+       else {
+               tos = (unsigned long *) &regs->EREG (sp);
+               insns[0] = p->ainsn.insn[0];
+               insns[1] = p->ainsn.insn[1];
+       }
+       
+       switch (insns[0])
+       {
+       case 0x9c:              /* pushfl */
+               *tos &= ~(TF_MASK | IF_MASK);
+               *tos |= kcb->kprobe_old_eflags;
+               break;
+       case 0xc2:              /* iret/ret/lret */
+       case 0xc3:
+       case 0xca:
+       case 0xcb:
+       case 0xcf:
+       case 0xea:              /* jmp absolute -- eip is correct */
+               /* eip is already adjusted, no more changes required */
+               p->ainsn.boostable = 1;
+               goto no_change;
+       case 0xe8:              /* call relative - Fix return addr */
+               *tos = orig_eip + (*tos - copy_eip);
+               break;
+       case 0x9a:              /* call absolute -- same as call absolute, indirect */
+               *tos = orig_eip + (*tos - copy_eip);
+               if(p->tgid){
+                       if (!write_proc_vm_atomic (current, regs->EREG (sp), &tos_dword, sizeof(tos_dword)))
+                               panic ("failed to write dword to top of the user space stack %lx!\n", regs->EREG (sp));
+               }
+               goto no_change;
+       case 0xff:
+               if ((insns[1] & 0x30) == 0x10)
+               {
+                       /*
+                        * call absolute, indirect
+                        * Fix return addr; eip is correct.
+                        * But this is not boostable
+                        */
+                       *tos = orig_eip + (*tos - copy_eip);
+                       if(p->tgid){
+                               if (!write_proc_vm_atomic (current, regs->EREG (sp), &tos_dword, sizeof(tos_dword)))
+                                       panic ("failed to write dword to top of the user space stack %lx!\n", regs->EREG (sp));
+                       }
+                       goto no_change;
+               }
+               else if (((insns[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */
+                            ((insns[1] & 0x31) == 0x21))
+               {               /* jmp far, absolute indirect */
+                       /* eip is correct. And this is boostable */
+                       p->ainsn.boostable = 1;
+                       goto no_change;
+               }
+       default:
+               break;
+       }
+
+       if(p->tgid){
+               if (!write_proc_vm_atomic (current, regs->EREG (sp), &tos_dword, sizeof(tos_dword)))
+                       panic ("failed to write dword to top of the user space stack %lx!\n", regs->EREG (sp));
+       }
+
+       if (p->ainsn.boostable == 0)
+       {
+               if ((regs->EREG (ip) > copy_eip) && (regs->EREG (ip) - copy_eip) + 5 < MAX_INSN_SIZE)
+               {
+                       /*
+                        * These instructions can be executed directly if it
+                        * jumps back to correct address.
+                        */                     
+                       if(p->tgid)
+                               set_user_jmp_op ((void *) regs->EREG (ip), (void *) orig_eip + (regs->EREG (ip) - copy_eip));                           
+                       else
+                               set_jmp_op ((void *) regs->EREG (ip), (void *) orig_eip + (regs->EREG (ip) - copy_eip));
+                       p->ainsn.boostable = 1;
+               }
+               else
+               {
+                       p->ainsn.boostable = -1;
+               }
+       }
+
+       regs->EREG (ip) = orig_eip + (regs->EREG (ip) - copy_eip);
+
+no_change:
+       return;
+}
+
+/*
+ * Interrupts are disabled on entry as trap1 is an interrupt gate and they
+ * remain disabled thoroughout this function.
+ */
+static int __kprobes
+post_kprobe_handler (struct pt_regs *regs)
+{
+       struct kprobe *cur = kprobe_running ();
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk ();
+
+       if (!cur)
+               return 0;
+       if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler)
+       {
+               kcb->kprobe_status = KPROBE_HIT_SSDONE;
+               cur->post_handler (cur, regs, 0);
+       }
+       
+       resume_execution (cur, regs, kcb);
+       regs->EREG (flags) |= kcb->kprobe_saved_eflags;
+#ifndef CONFIG_X86
+       trace_hardirqs_fixup_flags (regs->EREG (flags));
+#endif // CONFIG_X86
+       /*Restore back the original saved kprobes variables and continue. */
+       if (kcb->kprobe_status == KPROBE_REENTER)
+       {
+               restore_previous_kprobe (kcb);
+               goto out;
+       }
+       reset_current_kprobe ();
+out:
+       preempt_enable_no_resched ();
+
+       /*
+        * if somebody else is singlestepping across a probe point, eflags
+        * will have TF set, in which case, continue the remaining processing
+        * of do_debug, as if this is not a probe hit.
+        */
+       if (regs->EREG (flags) & TF_MASK)
+               return 0;
+
+       return 1;
+}
+
+static int __kprobes
+kprobe_fault_handler (struct pt_regs *regs, int trapnr)
+{
+       struct kprobe *cur = kprobe_running ();
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk ();
+
+       switch (kcb->kprobe_status)
+       {
+       case KPROBE_HIT_SS:
+       case KPROBE_REENTER:
+               /*
+                * We are here because the instruction being single
+                * stepped caused a page fault. We reset the current
+                * kprobe and the eip points back to the probe address
+                * and allow the page fault handler to continue as a
+                * normal page fault.
+                */
+               regs->EREG (ip) = (unsigned long) cur->addr;
+               regs->EREG (flags) |= kcb->kprobe_old_eflags;
+               if (kcb->kprobe_status == KPROBE_REENTER)
+                       restore_previous_kprobe (kcb);
+               else
+                       reset_current_kprobe ();
+               preempt_enable_no_resched ();
+               break;
+       case KPROBE_HIT_ACTIVE:
+       case KPROBE_HIT_SSDONE:
+               /*
+                * We increment the nmissed count for accounting,
+                * we can also use npre/npostfault count for accouting
+                * these specific fault cases.
+                */
+               kprobes_inc_nmissed_count (cur);
+
+               /*
+                * We come here because instructions in the pre/post
+                * handler caused the page_fault, this could happen
+                * if handler tries to access user space by
+                * copy_from_user(), get_user() etc. Let the
+                * user-specified handler try to fix it first.
+                */
+               if (cur->fault_handler && cur->fault_handler (cur, regs, trapnr))
+                       return 1;
+
+               /*
+                * In case the user-specified fault handler returned
+                * zero, try to fix up.
+                */
+               if (fixup_exception (regs))
+                       return 1;
+
+               /*
+                * fixup_exception() could not handle it,
+                * Let do_page_fault() fix it.
+                */
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+int
+kprobe_exceptions_notify (struct notifier_block *self, unsigned long val, void *data)
+{
+       struct die_args *args = (struct die_args *) data;
+       int ret = NOTIFY_DONE;
+
+       DBPRINTF ("val = %ld, data = 0x%X", val, (unsigned int) data);
+
+       /*if (args->regs && user_mode_vm (args->regs))
+               return ret;*/
+
+       DBPRINTF ("switch (val) %lu %d %d", val, DIE_INT3, DIE_TRAP);
+       switch (val)
+       {
+//#ifdef CONFIG_KPROBES
+//      case DIE_INT3:
+//#else
+       case DIE_TRAP:
+//#endif
+               DBPRINTF ("before kprobe_handler ret=%d %p", ret, args->regs);
+               if (kprobe_handler (args->regs))
+                       ret = NOTIFY_STOP;
+               DBPRINTF ("after kprobe_handler ret=%d %p", ret, args->regs);
+               break;
+       case DIE_DEBUG:
+               if (post_kprobe_handler (args->regs))
+                       ret = NOTIFY_STOP;
+               break;
+       case DIE_GPF:
+               // kprobe_running() needs smp_processor_id()
+               preempt_disable ();
+               if (kprobe_running () && kprobe_fault_handler (args->regs, args->trapnr))
+                       ret = NOTIFY_STOP;
+               preempt_enable ();
+               break;
+       default:
+               break;
+       }
+       DBPRINTF ("ret=%d", ret);
+       if(ret == NOTIFY_STOP)
+               handled_exceptions++;
+       
+       return ret;
+}
+#endif // CONFIG_X86
+
+int
+longjmp_break_handler (struct kprobe *p, struct pt_regs *regs)
+{
+#if defined(CONFIG_X86)
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk ();
+       u8 *addr = (u8 *) (regs->EREG (ip) - 1);
+       unsigned long stack_addr = (unsigned long) (kcb->jprobe_saved_esp);
+       struct jprobe *jp = container_of (p, struct jprobe, kp);
+
+       DBPRINTF ("p = %p\n", p);
+
+       if ((addr > (u8 *) jprobe_return) && (addr < (u8 *) jprobe_return_end))
+       {
+               if ((unsigned long *)(&regs->EREG(sp)) != kcb->jprobe_saved_esp)
+               {
+                       struct pt_regs *saved_regs = &kcb->jprobe_saved_regs;
+                       printk ("current esp %p does not match saved esp %p\n", &regs->EREG (sp), kcb->jprobe_saved_esp);
+                       printk ("Saved registers for jprobe %p\n", jp);
+                       show_registers (saved_regs);
+                       printk ("Current registers\n");
+                       show_registers (regs);
+                       panic("BUG");
+                       //BUG ();                       
+               }
+               *regs = kcb->jprobe_saved_regs;
+               memcpy ((kprobe_opcode_t *) stack_addr, kcb->jprobes_stack, MIN_STACK_SIZE (stack_addr));
+               preempt_enable_no_resched ();
+               return 1;
+       }
+#else //non x86
+       DBPRINTF ("p = %p\n", p);
+       //DBPRINTF("p->opcode = 0x%lx *p->addr = 0x%lx p->addr = 0x%p\n", p->opcode, p->pid?*kaddr[0]:*p->addr, p->pid?kaddr[0]:p->addr);
+# ifndef REENTER
+       //kprobe_opcode_t insn = BREAKPOINT_INSTRUCTION;
+       kprobe_opcode_t insns[2];
+
+       if (p->pid)
+       {
+               insns[0] = BREAKPOINT_INSTRUCTION;
+               insns[1] = p->opcode;
+               //p->opcode = *p->addr;
+               if (read_proc_vm_atomic (current, (unsigned long) (p->addr), &(p->opcode), sizeof (p->opcode)) < sizeof (p->opcode))
+               {
+                       printk ("ERROR[%lu]: failed to read vm of proc %s/%u addr %p.", nCount, current->comm, current->pid, p->addr);
+                       return -1;
+               }
+               //*p->addr = BREAKPOINT_INSTRUCTION;
+               //*(p->addr+1) = p->opcode;             
+               if (write_proc_vm_atomic (current, (unsigned long) (p->addr), insns, sizeof (insns)) < sizeof (insns))
+               {
+                       printk ("ERROR[%lu]: failed to write vm of proc %s/%u addr %p.", nCount, current->comm, current->pid, p->addr);
+                       return -1;
+               }
+       }
+       else
+       {
+               DBPRINTF ("p->opcode = 0x%lx *p->addr = 0x%lx p->addr = 0x%p\n", p->opcode, *p->addr, p->addr);
+               *(p->addr + 1) = p->opcode;
+               p->opcode = *p->addr;
+               *p->addr = BREAKPOINT_INSTRUCTION;
+               flush_icache_range ((unsigned int) p->addr, (unsigned int) (((unsigned int) p->addr) + (sizeof (kprobe_opcode_t) * 2)));
+       }
+
+       reset_current_kprobe ();
+# endif        //!reenter
+#endif //non x86
+
+       return 0;
+}
+
+void __kprobes
+arch_arm_kprobe (struct kprobe *p)
+{
+#if defined(CONFIG_X86)
+       text_poke (p->addr, ((unsigned char[])
+                            {BREAKPOINT_INSTRUCTION}), 1);
+#else
+       *p->addr = BREAKPOINT_INSTRUCTION;
+       flush_icache_range ((unsigned long) p->addr, (unsigned long) p->addr + sizeof (kprobe_opcode_t));
+#endif
+}
+
+void __kprobes
+arch_disarm_kprobe (struct kprobe *p)
+{
+#if defined(CONFIG_X86)
+       text_poke (p->addr, &p->opcode, 1);
+#else
+       *p->addr = p->opcode;
+       flush_icache_range ((unsigned long) p->addr, (unsigned long) p->addr + sizeof (kprobe_opcode_t));
+#endif
+}
+
+void __kprobes
+arch_arm_uprobe (struct kprobe *p, struct task_struct *tsk)
+{
+       kprobe_opcode_t insn = BREAKPOINT_INSTRUCTION;
+
+       if (!write_proc_vm_atomic (tsk, (unsigned long) p->addr, &insn, sizeof (insn)))
+               panic ("failed to write memory %p!\n", p->addr);
+}
+
+void __kprobes
+arch_arm_uretprobe (struct kretprobe *p, struct task_struct *tsk)
+{
+}
+
+void __kprobes
+arch_disarm_uprobe (struct kprobe *p, struct task_struct *tsk)
+{
+       if (!write_proc_vm_atomic (tsk, (unsigned long) p->addr, &p->opcode, sizeof (p->opcode)))
+               panic ("failed to write memory %p!\n", p->addr);
+}
+
+void __kprobes
+arch_disarm_uretprobe (struct kretprobe *p, struct task_struct *tsk)//, struct vm_area_struct *vma, struct page *page, unsigned long *kaddr)
+{
+}
+
+#if defined(CONFIG_X86)
+/*fastcall*/ void *__kprobes trampoline_probe_handler_x86 (struct pt_regs *regs)
+{
+       return (void *)trampoline_probe_handler(NULL, regs);
+}
+#endif
+
+/*
+ * Function return probe trampoline:
+ *     - init_kprobes() establishes a probepoint here
+ *     - When the probed function returns, this probe
+ *             causes the handlers to fire
+ */
+void
+kretprobe_trampoline_holder (void)
+{
+       asm volatile (".global kretprobe_trampoline\n" 
+                         "kretprobe_trampoline:\n"
+#if defined(CONFIG_MIPS)
+                     "nop\n" 
+                         "nop\n");
+#elif defined(CONFIG_ARM)
+                     "nop\n" 
+                         "nop\n" 
+                         "mov pc, r14\n");
+#elif defined(CONFIG_X86)
+                     " pushf\n"
+                     /* skip cs, eip, orig_eax */
+                     " subl $12, %esp\n"
+                     " pushl %fs\n"
+                     " pushl %ds\n"
+                     " pushl %es\n"
+                     " pushl %eax\n" 
+                     " pushl %ebp\n" 
+                     " pushl %edi\n" 
+                     " pushl %esi\n" 
+                     " pushl %edx\n" 
+                     " pushl %ecx\n" 
+                     " pushl %ebx\n" 
+                     " movl %esp, %eax\n" 
+                     " call trampoline_probe_handler_x86\n"
+                     /* move eflags to cs */
+                     " movl 52(%esp), %edx\n" 
+                     " movl %edx, 48(%esp)\n"
+                     /* save true return address on eflags */
+                     " movl %eax, 52(%esp)\n" 
+                     " popl %ebx\n" ""
+                     " popl %ecx\n" 
+                     " popl %edx\n" 
+                     " popl %esi\n" 
+                     " popl %edi\n" 
+                     " popl %ebp\n" 
+                     " popl %eax\n"
+                     /* skip eip, orig_eax, es, ds, fs */
+                     " addl $20, %esp\n" 
+                     " popf\n" 
+                         "     ret\n");
+#else
+#      error kretprobe_trampoline_holder is not implemented for this arch!!!
+#endif                         // ARCH
+}
+
+/*
+ * Called when the probe at kretprobe trampoline is hit
+ */
+int __kprobes trampoline_probe_handler (struct kprobe *p, struct pt_regs *regs)
+{
+       struct kretprobe_instance *ri = NULL; 
+       struct hlist_head *head, empty_rp; 
+       struct hlist_node *node, *tmp; 
+       unsigned long flags, orig_ret_address = 0;
+       unsigned long trampoline_address = (unsigned long) &kretprobe_trampoline;
+       struct kretprobe *crp = NULL; 
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk ();
+       
+       DBPRINTF ("start");
+
+       if (p && p->tgid){
+               // in case of user space retprobe trampoline is at the Nth instruction of US tramp 
+               trampoline_address = (unsigned long)(p->ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
+       }
+
+       INIT_HLIST_HEAD (&empty_rp); 
+       spin_lock_irqsave (&kretprobe_lock, flags); 
+       head = kretprobe_inst_table_head (current);
+#if defined(CONFIG_X86)
+       if(!p){ // X86 kernel space
+               DBPRINTF ("regs %p", regs);
+               /* fixup registers */
+               regs->XREG (cs) = __KERNEL_CS | get_kernel_rpl (); 
+               regs->EREG (ip) = trampoline_address; 
+               regs->ORIG_EAX_REG = 0xffffffff;
+       }
+#endif
+       /*
+        * It is possible to have multiple instances associated with a given
+        * task either because an multiple functions in the call path
+        * have a return probe installed on them, and/or more then one 
+        * return probe was registered for a target function.
+        *
+        * We can handle this because:
+        *     - instances are always inserted at the head of the list
+        *     - when multiple return probes are registered for the same
+        *       function, the first instance's ret_addr will point to the
+        *       real return address, and all the rest will point to
+        *       kretprobe_trampoline
+        */
+       hlist_for_each_entry_safe (ri, node, tmp, head, hlist)
+       {
+               if (ri->task != current)
+                       /* another task is sharing our hash bucket */
+                       continue; 
+               if (ri->rp && ri->rp->handler){
+#if defined(CONFIG_X86)
+                       if(!p){ // X86 kernel space
+                               __get_cpu_var (current_kprobe) = &ri->rp->kp; 
+                               get_kprobe_ctlblk ()->kprobe_status = KPROBE_HIT_ACTIVE;
+                       }
+#endif
+                       ri->rp->handler (ri, regs, ri->rp->priv_arg);
+#if defined(CONFIG_X86)
+                       if(!p) // X86 kernel space
+                               __get_cpu_var (current_kprobe) = NULL;
+#endif
+               }
+
+               orig_ret_address = (unsigned long) ri->ret_addr; 
+               recycle_rp_inst (ri, &empty_rp); 
+               if (orig_ret_address != trampoline_address)
+                       /*
+                        * This is the real return address. Any other
+                        * instances associated with this task are for
+                        * other calls deeper on the call stack
+                        */
+                       break;
+       }
+       kretprobe_assert (ri, orig_ret_address, trampoline_address);
+       //BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));
+       if (trampoline_address != (unsigned long) &kretprobe_trampoline){
+               if (ri->rp2) BUG_ON (ri->rp2->kp.tgid == 0);
+               if (ri->rp) BUG_ON (ri->rp->kp.tgid == 0);
+               else if (ri->rp2) BUG_ON (ri->rp2->kp.tgid == 0);
+       }
+       if ((ri->rp && ri->rp->kp.tgid) || (ri->rp2 && ri->rp2->kp.tgid)) 
+               BUG_ON (trampoline_address == (unsigned long) &kretprobe_trampoline);
+#if defined(CONFIG_MIPS)
+       regs->regs[31] = orig_ret_address;
+       DBPRINTF ("regs->cp0_epc = 0x%lx", regs->cp0_epc); 
+       if (trampoline_address != (unsigned long) &kretprobe_trampoline) 
+               regs->cp0_epc = orig_ret_address;
+       else
+               regs->cp0_epc = regs->cp0_epc + 4; 
+       DBPRINTF ("regs->cp0_epc = 0x%lx", regs->cp0_epc); 
+       DBPRINTF ("regs->cp0_status = 0x%lx", regs->cp0_status);
+#elif defined(CONFIG_ARM)
+       regs->uregs[14] = orig_ret_address; 
+       DBPRINTF ("regs->uregs[14] = 0x%lx\n", regs->uregs[14]);
+       DBPRINTF ("regs->uregs[15] = 0x%lx\n", regs->uregs[15]); 
+       if (trampoline_address != (unsigned long) &kretprobe_trampoline) 
+               regs->uregs[15] = orig_ret_address;
+       else
+               regs->uregs[15] += 4;
+       DBPRINTF ("regs->uregs[15] = 0x%lx\n", regs->uregs[15]);
+#elif defined(CONFIG_X86)
+       if(p){ // X86 user space
+               regs->EREG(ip) = orig_ret_address; 
+               //printk (" uretprobe regs->eip = 0x%lx\n", regs->EREG(ip));
+       }
+#endif // ARCH
+
+       if(p){ // ARM, MIPS, X86 user space
+               if (kcb->kprobe_status == KPROBE_REENTER)
+                       restore_previous_kprobe (kcb);
+               else
+                       reset_current_kprobe ();
+                       
+               //TODO: test - enter function, delete us retprobe, exit function 
+               // for user space retprobes only - deferred deletion
+               if (trampoline_address != (unsigned long) &kretprobe_trampoline)
+               {
+                       // if we are not at the end of the list and current retprobe should be disarmed 
+                       if (node && ri->rp2)
+                       {
+                               crp = ri->rp2;
+                               /*sprintf(die_msg, "deferred disarm p->addr = %p [%lx %lx %lx]\n", 
+                                crp->kp.addr, *kaddrs[0], *kaddrs[1], *kaddrs[2]);
+                                DIE(die_msg, regs); */
+                               // look for other instances for the same retprobe
+                               hlist_for_each_entry_continue (ri, node, hlist)
+                               {
+                                       if (ri->task != current) 
+                                               continue;       /* another task is sharing our hash bucket */
+                                       if (ri->rp2 == crp)     //if instance belong to the same retprobe
+                                               break;
+                               }
+                               if (!node)
+                               {       // if there are no more instances for this retprobe
+                                       // delete retprobe
+                                       DBPRINTF ("defered retprobe deletion p->addr = %p", crp->kp.addr);
+                                       unregister_uprobe (&crp->kp, current, 1);
+                                       kfree (crp);
+                               }
+                       }
+               }
+       }
+       
+       spin_unlock_irqrestore (&kretprobe_lock, flags); 
+       hlist_for_each_entry_safe (ri, node, tmp, &empty_rp, hlist)
+       {
+               hlist_del (&ri->hlist); 
+               kfree (ri);
+       }
+#if defined(CONFIG_X86)
+       if(!p) // X86 kernel space
+               return (int)orig_ret_address;
+#endif
+       preempt_enable_no_resched ();
+       /*
+        * By returning a non-zero value, we are telling
+        * kprobe_handler() that we don't want the post_handler
+        * to run (and have re-enabled preemption)
+        */
+       return 1;
+}
+
+/* Called with kretprobe_lock held */
+void __kprobes __arch_prepare_kretprobe (struct kretprobe *rp, struct pt_regs *regs)
+{
+       struct kretprobe_instance *ri;
+
+       DBPRINTF ("start\n");
+       //TODO: test - remove retprobe after func entry but before its exit
+       if ((ri = get_free_rp_inst (rp)) != NULL)
+       {
+               ri->rp = rp; 
+               ri->rp2 = NULL; 
+               ri->task = current;
+#if defined(CONFIG_MIPS)
+               ri->ret_addr = (kprobe_opcode_t *) regs->regs[31];
+               if (rp->kp.tgid)
+                       regs->regs[31] = (unsigned long) (rp->kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
+               else    /* Replace the return addr with trampoline addr */
+                       regs->regs[31] = (unsigned long) &kretprobe_trampoline;
+#elif defined(CONFIG_ARM)
+               ri->ret_addr = (kprobe_opcode_t *) regs->uregs[14];
+               if (rp->kp.tgid)
+                       regs->uregs[14] = (unsigned long) (rp->kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
+               else    /* Replace the return addr with trampoline addr */
+                       regs->uregs[14] = (unsigned long) &kretprobe_trampoline; 
+               DBPRINTF ("ret addr set to %p->%lx\n", ri->ret_addr, regs->uregs[14]);
+#elif defined(CONFIG_X86)
+               /* Replace the return addr with trampoline addr */
+               if (rp->kp.tgid){
+                       unsigned long ra = (unsigned long) (rp->kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);/*, stack[6];
+                       if (!read_proc_vm_atomic (current, regs->EREG(sp), stack, sizeof(stack)))
+                               panic ("failed to read user space func stack %lx!\n", regs->EREG(sp));
+                       printk("stack: %lx %lx %lx %lx %lx %lx\n", stack[0], stack[1], stack[2], stack[3], stack[4], stack[5]);*/
+                       if (!read_proc_vm_atomic (current, regs->EREG(sp), &(ri->ret_addr), sizeof(ri->ret_addr)))
+                               panic ("failed to read user space func ra %lx!\n", regs->EREG(sp));
+                       if (!write_proc_vm_atomic (current, regs->EREG(sp), &ra, sizeof(ra)))
+                               panic ("failed to write user space func ra %lx!\n", regs->EREG(sp));
+                       //printk("__arch_prepare_kretprobe: ra %lx %p->%lx\n",regs->EREG(sp), ri->ret_addr, ra);
+               }
+               else {
+                       unsigned long *sara = (unsigned long *)&regs->EREG(sp);
+                       ri->ret_addr = (kprobe_opcode_t *)*sara;
+                       *sara = (unsigned long)&kretprobe_trampoline;
+                       DBPRINTF ("ra loc %p, origr_ra %p new ra %lx\n", sara, ri->ret_addr, *sara);
+               }               
+#else
+#error  __arch_prepare_kretprobe is not implemented for this arch!!!
+#endif // ARCH
+               add_rp_inst (ri);
+       }
+       else {
+               DBPRINTF ("WARNING: missed retprobe %p\n", rp->kp.addr);
+               rp->nmissed++;
+       }
+}
+
+#if !defined(CONFIG_X86)
+static struct kprobe trampoline_p =
+{
+               .addr = (kprobe_opcode_t *) & kretprobe_trampoline,
+               .pre_handler = trampoline_probe_handler
+};
+#endif
+
+/*static void do_exit_probe_handler (void)
+{
+       printk("do_exit_probe_handler\n");
+       unregister_all_uprobes(current, 1);
+       jprobe_return();
+}
+
+static struct jprobe do_exit_p =
+{
+               .entry = (kprobe_pre_entry_handler_t)do_exit_probe_handler
+};*/
+
+//--------------------- Declaration of module dependencies ------------------------//
+#define DECLARE_MOD_FUNC_DEP(name, ret, ...) ret(*__ref_##name)(__VA_ARGS__)
+#define DECLARE_MOD_CB_DEP(name, ret, ...) ret(*name)(__VA_ARGS__)
+// persistent deps
+DECLARE_MOD_CB_DEP(kallsyms_search, unsigned long, const char *name);
+DECLARE_MOD_FUNC_DEP(access_process_vm, int, struct task_struct * tsk, unsigned long addr, void *buf, int len, int write);
+// deps controled by config macros
+#ifdef KERNEL_HAS_ISPAGEPRESENT
+DECLARE_MOD_FUNC_DEP(is_page_present, int, struct mm_struct * mm, unsigned long address);
+#endif
+#if defined(CONFIG_PREEMPT) && defined(CONFIG_PM)
+DECLARE_MOD_FUNC_DEP(freeze_processes, int, void);
+DECLARE_MOD_FUNC_DEP(thaw_processes, void, void);
+#endif
+// deps controled by arch type
+#if defined(CONFIG_MIPS)
+DECLARE_MOD_CB_DEP(flush_icache_range, void, unsigned long __user start, unsigned long __user end);
+DECLARE_MOD_CB_DEP(flush_icache_page, void, struct vm_area_struct * vma, struct page * page);
+DECLARE_MOD_CB_DEP(flush_cache_page, void, struct vm_area_struct * vma, unsigned long page);
+#elif defined(CONFIG_X86)
+DECLARE_MOD_FUNC_DEP(module_alloc, void *, unsigned long size);
+DECLARE_MOD_FUNC_DEP(module_free, void, struct module *mod, void *module_region);
+DECLARE_MOD_FUNC_DEP(fixup_exception, int, struct pt_regs * regs);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26))
+DECLARE_MOD_FUNC_DEP(text_poke, void, void *addr, unsigned char *opcode, int len);
+#else
+DECLARE_MOD_FUNC_DEP(text_poke, void *, void *addr, const void *opcode, size_t len);
+#endif
+DECLARE_MOD_FUNC_DEP(show_registers, void, struct pt_regs * regs);
+#elif defined(CONFIG_ARM)
+#if defined(CONFIG_CPU_CACHE_VIPT) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18))
+//DECLARE_MOD_FUNC_DEP(flush_ptrace_access, void, struct vm_area_struct * vma, struct page * page, unsigned long uaddr, void *kaddr, unsigned long len, int write);
+#endif
+#endif
+// deps controled by kernel version
+#if (LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 16))
+DECLARE_MOD_FUNC_DEP(put_task_struct, void, struct task_struct *tsk);
+#else //2.6.16
+DECLARE_MOD_FUNC_DEP(put_task_struct, void, struct rcu_head * rhp);
+#endif
+
+//----------------- Implementation of module dependencies wrappers -----------------//
+#define DECLARE_MOD_DEP_WRAPPER(name, ret, ...) ret name(__VA_ARGS__)
+#define IMP_MOD_DEP_WRAPPER(name, ...) \
+{ \
+       return __ref_##name(__VA_ARGS__); \
+}
+/*#define IMP_MOD_DEP_WRAPPER_NORET(name, ...) \
+{ \
+       return __ref_##name(__VA_ARGS__); \
+}*/
+// persistent deps
+DECLARE_MOD_DEP_WRAPPER(access_process_vm, int, struct task_struct *tsk, unsigned long addr, void *buf, int len, int write)
+IMP_MOD_DEP_WRAPPER(access_process_vm, tsk, addr, buf, len, write)
+// deps controled by config macros
+#ifdef KERNEL_HAS_ISPAGEPRESENT
+int is_page_present (struct mm_struct *mm, unsigned long address)
+{
+       int ret; 
+         
+       spin_lock (&(mm->page_table_lock)); 
+       ret = __ref_is_page_present (mm, address); 
+       spin_unlock (&(mm->page_table_lock)); 
+       return ret;
+}
+#endif
+
+#if defined(CONFIG_PREEMPT) && defined(CONFIG_PM)
+DECLARE_MOD_DEP_WRAPPER(freeze_processes, int, void)
+IMP_MOD_DEP_WRAPPER(freeze_processes)
+DECLARE_MOD_DEP_WRAPPER(thaw_processes, void, void)
+IMP_MOD_DEP_WRAPPER(thaw_processes)
+#endif
+
+// deps controled by arch type
+#if defined(CONFIG_MIPS)
+#elif defined(CONFIG_X86)
+DECLARE_MOD_DEP_WRAPPER(module_alloc, void *, unsigned long size)
+IMP_MOD_DEP_WRAPPER(module_alloc, size)
+DECLARE_MOD_DEP_WRAPPER(module_free, void, struct module *mod, void *module_region)
+IMP_MOD_DEP_WRAPPER(module_free, mod, module_region)
+DECLARE_MOD_DEP_WRAPPER(fixup_exception, int, struct pt_regs * regs)
+IMP_MOD_DEP_WRAPPER(fixup_exception, regs)
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26))
+DECLARE_MOD_DEP_WRAPPER(text_poke, void, void *addr, unsigned char *opcode, int len)
+#else
+DECLARE_MOD_DEP_WRAPPER(text_poke, void *, void *addr, const void *opcode, size_t len)
+#endif
+IMP_MOD_DEP_WRAPPER(text_poke, addr, opcode, len)
+DECLARE_MOD_DEP_WRAPPER(show_registers, void, struct pt_regs * regs)
+IMP_MOD_DEP_WRAPPER(show_registers, regs)
+#elif defined(CONFIG_ARM)
+#endif
+// deps controled by kernel version
+#if (LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 16))
+//DECLARE_MOD_FUNC_DEP(put_task_struct, void, struct task_struct *tsk);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11))
+DECLARE_MOD_DEP_WRAPPER(put_task_struct, void, struct task_struct *tsk)
+IMP_MOD_DEP_WRAPPER(put_task_struct, tsk)
+#else // >= 2.6.11 and != 2.6.16.x
+DECLARE_MOD_DEP_WRAPPER(__put_task_struct, void, struct task_struct *tsk)
+IMP_MOD_DEP_WRAPPER(put_task_struct, tsk)
+#endif
+#else //2.6.16
+DECLARE_MOD_DEP_WRAPPER(__put_task_struct_cb, void, struct rcu_head *rhp)
+IMP_MOD_DEP_WRAPPER(put_task_struct, rhp)
+#endif
+
+//---------------------- Module dependencies initialization --------------------//
+#define INIT_MOD_DEP_VAR(dep, name) \
+{ \
+       __ref_##dep = (void *) kallsyms_search (#name); \
+       if (!__ref_##dep) \
+       { \
+                 DBPRINTF (#name " is not found! Oops. Where is it?"); \
+                 return -ESRCH; \
+       } \
+}
+
+#define INIT_MOD_DEP_CB(dep, name) \
+{ \
+       dep = (void *) kallsyms_search (#name); \
+       if (!dep) \
+       { \
+                 DBPRINTF (#name " is not found! Oops. Where is it?"); \
+                 return -ESRCH; \
+       } \
+}
+
+int __init arch_init_kprobes (void)
+{
+#if !defined(CONFIG_X86)
+       unsigned int xDoBp; unsigned int xKProbeHandler;
+#endif
+#if defined(CONFIG_MIPS)
+       unsigned int xRegHi; unsigned int xRegLo;
+#endif // ARCH
+       int ret = 0;
+       
+       // Prepare to lookup names
+       kallsyms_search = (void *) ksyms; 
+       DBPRINTF ("kallsyms=0x%08x\n", ksyms);
+       sched_addr = (kprobe_opcode_t *)kallsyms_search("__switch_to");//"schedule");
+       fork_addr = (kprobe_opcode_t *)kallsyms_search("do_fork");
+       INIT_MOD_DEP_VAR(access_process_vm, access_process_vm);
+#ifdef KERNEL_HAS_ISPAGEPRESENT
+       INIT_MOD_DEP_VAR(is_page_present, is_page_present);
+#endif
+#if (LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 16))
+# if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11))
+       INIT_MOD_DEP_VAR(put_task_struct, put_task_struct);
+# else
+       INIT_MOD_DEP_VAR(put_task_struct, __put_task_struct);
+# endif
+#else /*2.6.16 */
+       INIT_MOD_DEP_VAR(put_task_struct, __put_task_struct_cb);
+#endif
+#if defined(CONFIG_MIPS)
+       INIT_MOD_DEP_CB(flush_icache_range, r4k_flush_icache_range);
+       INIT_MOD_DEP_CB(flush_icache_page, r4k_flush_icache_page);
+       INIT_MOD_DEP_CB(flush_cache_page, r4k_flush_cache_page);
+#elif defined(CONFIG_X86)
+       INIT_MOD_DEP_VAR(module_alloc, module_alloc);
+       INIT_MOD_DEP_VAR(module_free, module_free);
+       INIT_MOD_DEP_VAR(fixup_exception, fixup_exception);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
+# error this kernel version has no text_poke function which is necessaryf for x86 ach!!!
+#else
+       INIT_MOD_DEP_VAR(text_poke, text_poke);
+#endif
+       INIT_MOD_DEP_VAR(show_registers, show_registers);
+#if defined(CONFIG_PREEMPT) && defined(CONFIG_PM)
+       INIT_MOD_DEP_VAR(freeze_processes, freeze_processes);
+       INIT_MOD_DEP_VAR(thaw_processes, thaw_processes);
+#endif
+       return ret;
+#endif // CONFIG_X86   
+       
+#if !defined(CONFIG_X86)
+       // Get instruction addresses
+# if defined(CONFIG_MIPS)
+       xDoBp = (unsigned int) kallsyms_search ("do_bp");
+# elif defined(CONFIG_ARM)
+       xDoBp = (unsigned int) kallsyms_search ("do_undefinstr");
+# endif        // ARCH
+       xKProbeHandler = (unsigned int) &kprobe_handler;
+       gl_nNumberOfInstructions = sizeof (arrTrapsTemplate) / sizeof (arrTrapsTemplate[0]);
+       gl_nCodeSize = gl_nNumberOfInstructions * sizeof (unsigned int); 
+       DBPRINTF ("nNumberOfInstructions = %d\n", gl_nNumberOfInstructions);
+       // Save original code
+       arrTrapsOriginal = kmalloc (gl_nCodeSize /* + sizeof(unsigned int) */ , GFP_KERNEL);
+       if (!arrTrapsOriginal)
+       {
+               DBPRINTF ("Unable to allocate space for original code of <do_bp>!\n"); 
+               return -1;
+       }
+       memcpy (arrTrapsOriginal, (void *) xDoBp, gl_nCodeSize);
+       // Fill in template
+#if defined(CONFIG_MIPS)
+       xRegHi = HIWORD (xKProbeHandler);
+       xRegLo = LOWORD (xKProbeHandler); 
+       if (xRegLo >= 0x8000) 
+       xRegHi += 0x0001; 
+       arrTrapsTemplate[REG_HI_INDEX] |= xRegHi; 
+       arrTrapsTemplate[REG_LO_INDEX] |= xRegLo;
+#elif defined(CONFIG_ARM)
+       arrTrapsTemplate[NOTIFIER_CALL_CHAIN_INDEX] = arch_construct_brunch (xKProbeHandler, xDoBp + NOTIFIER_CALL_CHAIN_INDEX * 4, 1);
+       //arrTrapsTemplate[NOTIFIER_CALL_CHAIN_INDEX1] = arch_construct_brunch(xKProbeHandler,
+       //      xDoBp + NOTIFIER_CALL_CHAIN_INDEX1 * 4, 1);
+       //arrTrapsTemplate[NOTIFIER_CALL_CHAIN_INDEX2] = arch_construct_brunch((unsigned int)arrTrapsOriginal,
+       //      xDoBp + NOTIFIER_CALL_CHAIN_INDEX2 * 4, 1);
+       //arrTrapsOriginal[gl_nNumberOfInstructions] = arch_construct_brunch(xDoBp + gl_nNumberOfInstructions * 4, 
+       //      (unsigned int)(arrTrapsOriginal + gl_nNumberOfInstructions), 1);        
+#endif // ARCH
+       /*for(i = 0; i < gl_nNumberOfInstructions+1; i++)
+       {
+       printk("%08x\n", arrTrapsOriginal[i]);
+       } */
+       /*do_exit_p.kp.addr = (kprobe_opcode_t *)kallsyms_search ("do_exit");
+       if (!do_exit_p.kp.addr)
+       {
+               DBPRINTF ("do_exit is not found! Oops. Where is it?");
+               return -ESRCH;
+       }
+       if((ret = register_jprobe (&do_exit_p, 0)) != 0)
+               return ret;*/
+
+       // Insert new code
+       memcpy ((void *) xDoBp, arrTrapsTemplate, gl_nCodeSize); 
+       flush_icache_range (xDoBp, xDoBp + gl_nCodeSize); 
+       if((ret = register_kprobe (&trampoline_p, 0)) != 0){
+               //unregister_jprobe(&do_exit_p, 0);
+               return ret;
+       }
+       
+       return ret;     
+#endif
+}
+
+void __exit arch_exit_kprobes (void)
+{
+#if !defined(CONFIG_X86)
+       unsigned int xDoBp;
+       // Get instruction address
+#if defined(CONFIG_MIPS)
+       xDoBp = (unsigned int) kallsyms_search ("do_bp");
+#elif defined(CONFIG_ARM)
+       xDoBp = (unsigned int) kallsyms_search ("do_undefinstr");
+#endif // ARCH
+       //unregister_jprobe(&do_exit_p, 0);
+       // Replace back the original code
+       memcpy ((void *) xDoBp, arrTrapsOriginal, gl_nCodeSize); 
+       flush_icache_range (xDoBp, xDoBp + gl_nCodeSize); 
+       kfree (arrTrapsOriginal); 
+       arrTrapsOriginal = NULL;
+#endif
+}
+
+MODULE_LICENSE ("Dual BSD/GPL");