summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--runtime/ChangeLog14
-rw-r--r--runtime/syscall.h132
-rw-r--r--runtime/task_finder.c317
-rw-r--r--runtime/task_finder_vma.c112
-rw-r--r--tapsets.cxx57
6 files changed, 637 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 6db66597..1fb69a93 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2008-06-23 David Smith <dsmith@redhat.com>
+
+ * tapsets.cxx (utrace_derived_probe_group::emit_probe_decl):
+ Handles UDPF_NONE value.
+ (utrace_derived_probe_group::emit_vm_callback_probe_decl): New
+ function.
+ (utrace_derived_probe_group::emit_module_decls): Calls
+ emit_vm_callback_probe_decl() to set up vm_callbacks.
+
2008-06-23 Stan Cox <scox@redhat.com>
* tapsets.cxx (enum line_t): Add RANGE and WILDCARD.
diff --git a/runtime/ChangeLog b/runtime/ChangeLog
index c30219ef..aa7058ed 100644
--- a/runtime/ChangeLog
+++ b/runtime/ChangeLog
@@ -1,5 +1,19 @@
2008-06-23 David Smith <dsmith@redhat.com>
+ * task_finder.c (__stp_tf_vm_cb): New function.
+ (stap_register_task_finder_target): Sets up syscall entry and
+ syscall exit handlers.
+ (__stp_find_file_based_vma): New function.
+ (__stp_utrace_task_finder_target_syscall_entry): New function.
+ Saves vma information off at syscall entry.
+ (__stp_target_call_vm_callback): New function.
+ (__stp_utrace_task_finder_target_syscall_exit): New function.
+ Handles changes to memory maps based on information saved at
+ syscall entry.
+ * syscall.h: New file containing syscall function.
+ * task_finder_vma.c: New file containing saved vma information
+ handling functions.
+
* regs.h: Removed trailing semicolons from macro definitions.
2008-06-17 David Smith <dsmith@redhat.com>
diff --git a/runtime/syscall.h b/runtime/syscall.h
new file mode 100644
index 00000000..3e37b550
--- /dev/null
+++ b/runtime/syscall.h
@@ -0,0 +1,132 @@
+/* syscall defines and inlines
+ * Copyright (C) 2008 Red Hat Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#ifndef _SYSCALL_H_ /* -*- linux-c -*- */
+#define _SYSCALL_H_
+
+#if defined(__i386__) || defined(CONFIG_IA32_EMULATION)
+#define __MMAP_SYSCALL_NO_IA32 192 /* mmap2 */
+#define __MPROTECT_SYSCALL_NO_IA32 125
+#define __MUNMAP_SYSCALL_NO_IA32 91
+#define __MREMAP_SYSCALL_NO_IA32 163
+# if !defined(CONFIG_IA32_EMULATION)
+#define MMAP_SYSCALL_NO(tsk) __MMAP_SYSCALL_NO_IA32
+#define MPROTECT_SYSCALL_NO(tsk) __MPROTECT_SYSCALL_NO_IA32
+#define MUNMAP_SYSCALL_NO(tsk) __MUNMAP_SYSCALL_NO_IA32
+#define MREMAP_SYSCALL_NO(tsk) __MREMAP_SYSCALL_NO_IA32
+# endif
+#endif
+
+#if defined(__x86_64__)
+#define __MMAP_SYSCALL_NO_X86_64 9
+#define __MPROTECT_SYSCALL_NO_X86_64 10
+#define __MUNMAP_SYSCALL_NO_X86_64 11
+#define __MREMAP_SYSCALL_NO_X86_64 25
+# if defined(CONFIG_IA32_EMULATION)
+#define MMAP_SYSCALL_NO(tsk) ((test_tsk_thread_flag((tsk), TIF_IA32)) \
+ ? __MMAP_SYSCALL_NO_IA32 \
+ : __MMAP_SYSCALL_NO_X86_64)
+#define MPROTECT_SYSCALL_NO(tsk) ((test_tsk_thread_flag((tsk), TIF_IA32)) \
+ ? __MPROTECT_SYSCALL_NO_IA32 \
+ : __MPROTECT_SYSCALL_NO_X86_64)
+#define MUNMAP_SYSCALL_NO(tsk) ((test_tsk_thread_flag((tsk), TIF_IA32)) \
+ ? __MUNMAP_SYSCALL_NO_IA32 \
+ : __MUNMAP_SYSCALL_NO_X86_64)
+#define MREMAP_SYSCALL_NO(tsk) ((test_tsk_thread_flag((tsk), TIF_IA32)) \
+ ? __MREMAP_SYSCALL_NO_IA32 \
+ : __MREMAP_SYSCALL_NO_X86_64)
+# else
+#define MMAP_SYSCALL_NO(tsk) __MMAP_SYSCALL_NO_X86_64
+#define MPROTECT_SYSCALL_NO(tsk) __MPROTECT_SYSCALL_NO_X86_64
+#define MUNMAP_SYSCALL_NO(tsk) __MUNMAP_SYSCALL_NO_X86_64
+#define MREMAP_SYSCALL_NO(tsk) __MREMAP_SYSCALL_NO_X86_64
+# endif
+#endif
+
+#if !defined(MMAP_SYSCALL_NO) || !defined(MPROTECT_SYSCALL_NO) \
+ || !defined(MUNMAP_SYSCALL_NO) || !defined(MREMAP_SYSCALL_NO)
+#error "Unimplemented architecture"
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+static inline unsigned long
+__stp_user_syscall_nr(struct pt_regs *regs)
+{
+#if defined(STAPCONF_X86_UNIREGS)
+ return regs->orig_ax;
+#elif defined(__x86_64__)
+ return regs->orig_rax;
+#elif defined (__i386__)
+ return regs->orig_eax;
+#endif
+}
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+static inline long *
+__stp_user_syscall_return_value(struct task_struct *task, struct pt_regs *regs)
+{
+#ifdef CONFIG_IA32_EMULATION
+// This code works, but isn't what we need. Since
+// __stp_user_syscall_arg() doesn't sign-extend, a value passed in as
+// an argument and then returned won't compare correctly anymore. So,
+// for now, disable this code.
+# if 0
+ if (test_tsk_thread_flag(task, TIF_IA32))
+ // Sign-extend the value so (int)-EFOO becomes (long)-EFOO
+ // and will match correctly in comparisons.
+ regs->ax = (long) (int) regs->ax;
+# endif
+#endif
+ return &regs->ax;
+}
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+static inline long *
+__stp_user_syscall_arg(struct task_struct *task, struct pt_regs *regs,
+ unsigned int n)
+{
+#if defined(CONFIG_X86_32)
+ if (n > 5) {
+ _stp_error("syscall arg > 5");
+ return NULL;
+ }
+ return &regs->bx + n;
+#elif defined(CONFIG_X86_64)
+#ifdef CONFIG_IA32_EMULATION
+ if (test_tsk_thread_flag(task, TIF_IA32))
+ switch (n) {
+ case 0: return &regs->bx;
+ case 1: return &regs->cx;
+ case 2: return &regs->dx;
+ case 3: return &regs->si;
+ case 4: return &regs->di;
+ case 5: return &regs->bp;
+ default:
+ _stp_error("syscall arg > 5");
+ return NULL;
+ }
+#endif /* CONFIG_IA32_EMULATION */
+ switch (n) {
+ case 0: return &regs->di;
+ case 1: return &regs->si;
+ case 2: return &regs->dx;
+ case 3: return &regs->r10;
+ case 4: return &regs->r8;
+ case 5: return &regs->r9;
+ default:
+ _stp_error("syscall arg > 5");
+ return NULL;
+ }
+#endif /* CONFIG_X86_32 */
+}
+#endif
+
+#endif /* _SYSCALL_H_ */
diff --git a/runtime/task_finder.c b/runtime/task_finder.c
index 7391f104..abb76e09 100644
--- a/runtime/task_finder.c
+++ b/runtime/task_finder.c
@@ -1,5 +1,7 @@
#include <linux/list.h>
#include <linux/binfmts.h>
+#include "syscall.h"
+#include "task_finder_vma.c"
static LIST_HEAD(__stp_task_finder_list);
@@ -35,6 +37,20 @@ typedef int (*stap_task_finder_vm_callback)(struct task_struct *tsk,
unsigned long vm_end,
unsigned long vm_pgoff);
+#ifdef DEBUG_TASK_FINDER_VMA
+int __stp_tf_vm_cb(struct task_struct *tsk,
+ int map_p, char *vm_path,
+ unsigned long vm_start,
+ unsigned long vm_end,
+ unsigned long vm_pgoff)
+{
+ _stp_dbug(__FUNCTION__, __LINE__,
+ "vm_cb: tsk %d:%d path %s, start 0x%08lx, end 0x%08lx, offset 0x%lx\n",
+ tsk->pid, map_p, vm_path, vm_start, vm_end, vm_pgoff);
+ return 0;
+}
+#endif
+
struct stap_task_finder_target {
/* private: */
struct list_head list; /* __stp_task_finder_list linkage */
@@ -59,6 +75,16 @@ static u32
__stp_utrace_task_finder_target_quiesce(struct utrace_attached_engine *engine,
struct task_struct *tsk);
+static u32
+__stp_utrace_task_finder_target_syscall_entry(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ struct pt_regs *regs);
+
+static u32
+__stp_utrace_task_finder_target_syscall_exit(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ struct pt_regs *regs);
+
static int
stap_register_task_finder_target(struct stap_task_finder_target *new_tgt)
{
@@ -82,6 +108,10 @@ stap_register_task_finder_target(struct stap_task_finder_target *new_tgt)
memset(&new_tgt->ops, 0, sizeof(new_tgt->ops));
new_tgt->ops.report_death = &__stp_utrace_task_finder_target_death;
new_tgt->ops.report_quiesce = &__stp_utrace_task_finder_target_quiesce;
+ new_tgt->ops.report_syscall_entry = \
+ &__stp_utrace_task_finder_target_syscall_entry;
+ new_tgt->ops.report_syscall_exit = \
+ &__stp_utrace_task_finder_target_syscall_exit;
// Search the list for an existing entry for pathname/pid.
list_for_each(node, &__stp_task_finder_list) {
@@ -224,9 +254,13 @@ __stp_get_mm_path(struct mm_struct *mm, char *buf, int buflen)
#define __STP_ATTACHED_TASK_BASE_EVENTS (UTRACE_EVENT(DEATH))
-#define __STP_ATTACHED_TASK_VM_EVENTS (__STP_ATTACHED_TASK_BASE_EVENTS \
- | UTRACE_ACTION_QUIESCE \
- | UTRACE_EVENT(QUIESCE))
+#define __STP_ATTACHED_TASK_VM_BASE_EVENTS (__STP_ATTACHED_TASK_BASE_EVENTS \
+ | UTRACE_EVENT(SYSCALL_ENTRY) \
+ | UTRACE_EVENT(SYSCALL_EXIT))
+
+#define __STP_ATTACHED_TASK_VM_EVENTS (__STP_ATTACHED_TASK_VM_BASE_EVENTS \
+ | UTRACE_ACTION_QUIESCE \
+ | UTRACE_EVENT(QUIESCE))
#define __STP_ATTACHED_TASK_EVENTS(tgt) \
((((tgt)->vm_callback) == NULL) ? __STP_ATTACHED_TASK_BASE_EVENTS \
@@ -509,7 +543,7 @@ __stp_utrace_task_finder_target_quiesce(struct utrace_attached_engine *engine,
struct stap_task_finder_target *tgt = engine->data;
// Turn off quiesce handling.
- utrace_set_flags(tsk, engine, __STP_ATTACHED_TASK_BASE_EVENTS);
+ utrace_set_flags(tsk, engine, __STP_ATTACHED_TASK_VM_BASE_EVENTS);
if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) {
debug_task_finder_detach();
@@ -577,6 +611,281 @@ utftq_out:
return (UTRACE_ACTION_NEWSTATE | UTRACE_ACTION_RESUME);
}
+
+struct vm_area_struct *
+__stp_find_file_based_vma(struct mm_struct *mm, unsigned long addr)
+{
+ struct vm_area_struct *vma = find_vma(mm, addr);
+
+ // I'm not positive why the checking for vm_start > addr is
+ // necessary, but it seems to be (sometimes find_vma() returns
+ // a vma that addr doesn't belong to).
+ if (vma && (vma->vm_file == NULL || vma->vm_start > addr))
+ vma = NULL;
+ return vma;
+}
+
+static u32
+__stp_utrace_task_finder_target_syscall_entry(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ struct pt_regs *regs)
+{
+ struct stap_task_finder_target *tgt = engine->data;
+ unsigned long syscall_no;
+ struct mm_struct *mm;
+ struct vm_area_struct *vma;
+ unsigned long *arg0_addr, arg0;
+ int rc;
+
+ if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) {
+ debug_task_finder_detach();
+ return UTRACE_ACTION_DETACH;
+ }
+
+ if (tgt == NULL || tgt->vm_callback == NULL)
+ return UTRACE_ACTION_RESUME;
+
+ // See if syscall is one we're interested in.
+ //
+ // FIXME: do we need to handle mremap()?
+ syscall_no = __stp_user_syscall_nr(regs);
+ if (syscall_no != MMAP_SYSCALL_NO(tsk)
+ && syscall_no != MPROTECT_SYSCALL_NO(tsk)
+ && syscall_no != MUNMAP_SYSCALL_NO(tsk))
+ return UTRACE_ACTION_RESUME;
+
+
+ // We need the first syscall argument to see what address
+ // we're operating on.
+ arg0_addr = __stp_user_syscall_arg(tsk, regs, 0);
+ if ((rc = __stp_get_user(arg0, arg0_addr)) != 0) {
+ _stp_error("couldn't read syscall arg 0 for pid %d: %d",
+ tsk->pid, rc);
+ }
+ else if (arg0 != (unsigned long)NULL) {
+ mm = get_task_mm(tsk);
+ if (mm) {
+ down_read(&mm->mmap_sem);
+
+ // If we can find a matching vma associated
+ // with a file, save off its details.
+ vma = __stp_find_file_based_vma(mm, arg0);
+ if (vma != NULL) {
+ __stp_tf_add_vma(tsk, arg0, vma);
+ }
+
+ up_read(&mm->mmap_sem);
+ mmput(mm);
+ }
+ }
+ return UTRACE_ACTION_RESUME;
+}
+
+static void
+__stp_target_call_vm_callback(struct stap_task_finder_target *tgt,
+ struct task_struct *tsk,
+ struct vm_area_struct *vma)
+{
+ char *mmpath_buf;
+ char *mmpath;
+ int rc;
+
+ // Allocate space for a path
+ mmpath_buf = _stp_kmalloc(PATH_MAX);
+ if (mmpath_buf == NULL) {
+ _stp_error("Unable to allocate space for path");
+ return;
+ }
+
+ // Grab the path associated with this vma.
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ mmpath = d_path(vma->vm_file->f_dentry, vma->vm_file->f_vfsmnt,
+ mmpath_buf, PATH_MAX);
+#else
+ mmpath = d_path(&(vma->vm_file->f_path), mmpath_buf, PATH_MAX);
+#endif
+ if (mmpath == NULL || IS_ERR(mmpath)) {
+ rc = -PTR_ERR(mmpath);
+ _stp_error("Unable to get path (error %d) for pid %d",
+ rc, (int)tsk->pid);
+ }
+ else {
+ rc = tgt->vm_callback(tsk, 1, mmpath, vma->vm_start,
+ vma->vm_end, vma->vm_pgoff);
+ if (rc != 0) {
+ _stp_error("vm callback for %d failed: %d",
+ (int)tsk->pid, rc);
+ }
+ }
+ _stp_kfree(mmpath_buf);
+}
+
+static u32
+__stp_utrace_task_finder_target_syscall_exit(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ struct pt_regs *regs)
+{
+ struct stap_task_finder_target *tgt = engine->data;
+ unsigned long syscall_no;
+ unsigned long *rv_addr, rv;
+ unsigned long *arg0_addr, arg0;
+ int rc;
+ struct mm_struct *mm;
+ struct vm_area_struct *vma;
+ struct __stp_tf_vma_entry *entry = NULL;
+
+ if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) {
+ debug_task_finder_detach();
+ return UTRACE_ACTION_DETACH;
+ }
+
+ if (tgt == NULL || tgt->vm_callback == NULL)
+ return UTRACE_ACTION_RESUME;
+
+ // See if syscall is one we're interested in.
+ //
+ // FIXME: do we need to handle mremap()?
+ syscall_no = __stp_user_syscall_nr(regs);
+ if (syscall_no != MMAP_SYSCALL_NO(tsk)
+ && syscall_no != MPROTECT_SYSCALL_NO(tsk)
+ && syscall_no != MUNMAP_SYSCALL_NO(tsk))
+ return UTRACE_ACTION_RESUME;
+
+ // Get return value
+ rv_addr = __stp_user_syscall_return_value(tsk, regs);
+ if ((rc = __stp_get_user(rv, rv_addr)) != 0) {
+ _stp_error("couldn't read syscall return value for pid %d: %d",
+ tsk->pid, rc);
+ return UTRACE_ACTION_RESUME;
+ }
+
+ // We need the first syscall argument to see what address we
+ // were operating on.
+ arg0_addr = __stp_user_syscall_arg(tsk, regs, 0);
+ if ((rc = __stp_get_user(arg0, arg0_addr)) != 0) {
+ _stp_error("couldn't read syscall arg 0 for pid %d: %d",
+ tsk->pid, rc);
+ return UTRACE_ACTION_RESUME;
+ }
+
+#ifdef DEBUG_TASK_FINDER_VMA
+ _stp_dbug(__FUNCTION__, __LINE__,
+ "tsk %d found %s(0x%lx), returned 0x%lx\n",
+ tsk->pid,
+ ((syscall_no == MMAP_SYSCALL_NO(tsk)) ? "mmap"
+ : ((syscall_no == MPROTECT_SYSCALL_NO(tsk)) ? "mprotect"
+ : ((syscall_no == MUNMAP_SYSCALL_NO(tsk)) ? "munmap"
+ : "UNKNOWN"))),
+ arg0, rv);
+#endif
+
+ // Try to find the vma info we might have saved.
+ if (arg0 != (unsigned long)NULL)
+ entry = __stp_tf_get_vma_entry(tsk, arg0);
+
+ // If entry is NULL, this means we didn't find a file based
+ // vma to store in the syscall_entry routine. This could mean
+ // we just created a new vma.
+ if (entry == NULL) {
+ mm = get_task_mm(tsk);
+ if (mm) {
+ down_read(&mm->mmap_sem);
+ vma = __stp_find_file_based_vma(mm, rv);
+ if (vma != NULL) {
+ __stp_target_call_vm_callback(tgt, tsk,
+ vma);
+ }
+ up_read(&mm->mmap_sem);
+ mmput(mm);
+ }
+ }
+ // If we found saved vma information, try to match it up with
+ // what currently exists.
+ else {
+#ifdef DEBUG_TASK_FINDER_VMA
+ _stp_dbug(__FUNCTION__, __LINE__,
+ "** found stored vma 0x%lx/0x%lx/0x%lx!\n",
+ entry->vm_start, entry->vm_end, entry->vm_pgoff);
+#endif
+ mm = get_task_mm(tsk);
+ if (mm) {
+ down_read(&mm->mmap_sem);
+ vma = __stp_find_file_based_vma(mm, entry->vm_start);
+
+ // We couldn't find the vma at all. The
+ // original vma was deleted.
+ if (vma == NULL) {
+ // FIXME: We'll need to figure out to
+ // retrieve the path of a deleted
+ // vma.
+ rc = tgt->vm_callback(tsk, 0, NULL,
+ entry->vm_start,
+ entry->vm_end,
+ entry->vm_pgoff);
+ if (rc != 0) {
+ _stp_error("vm callback for %d failed: %d",
+ (int)tsk->pid, rc);
+ }
+ }
+
+ // If nothing has changed, there is no
+ // need to call the callback.
+ else if (vma->vm_start == entry->vm_start
+ && vma->vm_end == entry->vm_end
+ && vma->vm_pgoff == entry->vm_pgoff) {
+ // do nothing
+ }
+
+ // The original vma has been changed. It is
+ // possible that calling mprotect (e.g.) split
+ // up an existing vma into 2 or 3 new vma's
+ // (assuming it protected a portion of the
+ // original vma at the beginning, middle, or
+ // end). Try to determine what happened.
+ else {
+ unsigned long tmp;
+
+ // First report that the original vma
+ // is gone.
+ //
+ // FIXME: We'll need to figure out to
+ // retrieve the path of a deleted
+ // vma.
+ rc = tgt->vm_callback(tsk, 0, NULL,
+ entry->vm_start,
+ entry->vm_end,
+ entry->vm_pgoff);
+ if (rc != 0) {
+ _stp_error("vm callback for %d failed: %d",
+ (int)tsk->pid, rc);
+ }
+
+ // Now find all the new vma's that
+ // made up the original vma's address
+ // space and call the callback on each
+ // new vma.
+ tmp = entry->vm_start;
+ while (((vma = __stp_find_file_based_vma(mm,
+ tmp))
+ != NULL)
+ && vma->vm_end <= entry->vm_end) {
+ __stp_target_call_vm_callback(tgt, tsk,
+ vma);
+ if (vma->vm_end >= entry->vm_end)
+ break;
+ tmp = vma->vm_end;
+ }
+ }
+ up_read(&mm->mmap_sem);
+ mmput(mm);
+ }
+
+ // Cleanup by deleting the saved vma info.
+ __stp_tf_remove_vma_entry(entry);
+ }
+ return UTRACE_ACTION_RESUME;
+}
+
struct utrace_engine_ops __stp_utrace_task_finder_ops = {
.report_clone = __stp_utrace_task_finder_report_clone,
.report_exec = __stp_utrace_task_finder_report_exec,
diff --git a/runtime/task_finder_vma.c b/runtime/task_finder_vma.c
new file mode 100644
index 00000000..c849f187
--- /dev/null
+++ b/runtime/task_finder_vma.c
@@ -0,0 +1,112 @@
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/mutex.h>
+
+// __stp_tf_vma_mutex protects the hash table.
+static DEFINE_MUTEX(__stp_tf_vma_mutex);
+
+#define __STP_TF_HASH_BITS 4
+#define __STP_TF_TABLE_SIZE (1 << __STP_TF_HASH_BITS)
+
+struct __stp_tf_vma_entry {
+ struct hlist_node hlist;
+
+ pid_t pid;
+ unsigned long addr;
+ unsigned long vm_start;
+ unsigned long vm_end;
+ unsigned long vm_pgoff;
+ // Is that enough? Should we store a dcookie for vm_file?
+};
+
+static struct hlist_head __stp_tf_vma_table[__STP_TF_TABLE_SIZE];
+
+static inline u32
+__stp_tf_vma_hash(struct task_struct *tsk, unsigned long addr)
+{
+#if (__SIZEOF_LONG__ == 8)
+ return (jhash_3words(tsk->pid, (u32)addr, (u32)(addr >> 32), 0)
+ & (__STP_TF_TABLE_SIZE - 1));
+#else
+ return (jhash_2words(tsk->pid, addr, 0) & (__STP_TF_TABLE_SIZE - 1));
+#endif
+}
+
+
+// Get vma_entry if the vma is present in the vma hash table.
+// Returns NULL if not present.
+static struct __stp_tf_vma_entry *
+__stp_tf_get_vma_entry(struct task_struct *tsk, unsigned long addr)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct __stp_tf_vma_entry *entry;
+
+ mutex_lock(&__stp_tf_vma_mutex);
+ head = &__stp_tf_vma_table[__stp_tf_vma_hash(tsk, addr)];
+ hlist_for_each_entry(entry, node, head, hlist) {
+ if (tsk->pid == entry->pid
+ && addr == entry->addr) {
+ mutex_unlock(&__stp_tf_vma_mutex);
+ return entry;
+ }
+ }
+ mutex_unlock(&__stp_tf_vma_mutex);
+ return NULL;
+}
+
+// Add the vma info to the vma hash table.
+static int
+__stp_tf_add_vma(struct task_struct *tsk, unsigned long addr,
+ struct vm_area_struct *vma)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct __stp_tf_vma_entry *entry;
+
+ mutex_lock(&__stp_tf_vma_mutex);
+ head = &__stp_tf_vma_table[__stp_tf_vma_hash(tsk, addr)];
+ hlist_for_each_entry(entry, node, head, hlist) {
+ if (tsk->pid == entry->pid
+ && addr == entry->addr) {
+ printk(KERN_NOTICE
+ "vma (pid: %d, vm_start: 0x%lx) present?\n",
+ tsk->pid, vma->vm_start);
+ mutex_unlock(&__stp_tf_vma_mutex);
+ return -EBUSY; /* Already there */
+ }
+ }
+
+ // Using kmalloc here to allocate an element. Could cause some
+ // memory fragmentation if overused.
+ entry = kmalloc(sizeof(struct __stp_tf_vma_entry), GFP_KERNEL);
+ if (!entry) {
+ mutex_unlock(&__stp_tf_vma_mutex);
+ return -ENOMEM;
+ }
+ entry->pid = tsk->pid;
+ entry->addr = addr;
+ entry->vm_start = vma->vm_start;
+ entry->vm_end = vma->vm_end;
+ entry->vm_pgoff = vma->vm_pgoff;
+ hlist_add_head(&entry->hlist, head);
+ mutex_unlock(&__stp_tf_vma_mutex);
+ return 0;
+}
+
+// Remove the vma entry from the vma hash table.
+static int
+__stp_tf_remove_vma_entry(struct __stp_tf_vma_entry *entry)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ int found = 0;
+
+ if (entry != NULL) {
+ mutex_lock(&__stp_tf_vma_mutex);
+ hlist_del(&entry->hlist);
+ kfree(entry);
+ mutex_unlock(&__stp_tf_vma_mutex);
+ }
+ return 0;
+}
diff --git a/tapsets.cxx b/tapsets.cxx
index 3f9291f2..5b66d186 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -5225,6 +5225,9 @@ private:
bool flags_seen[UDPF_NFLAGS];
void emit_probe_decl (systemtap_session& s, utrace_derived_probe *p);
+ void emit_vm_callback_probe_decl (systemtap_session& s, bool has_path,
+ string path, int64_t pid,
+ string vm_callback);
public:
utrace_derived_probe_group(): num_probes(0), flags_seen() { }
@@ -5484,6 +5487,11 @@ utrace_derived_probe_group::emit_probe_decl (systemtap_session& s,
s.op->line() << " .ops={ .report_syscall_exit=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death },";
s.op->line() << " .events=(UTRACE_EVENT(SYSCALL_EXIT)|UTRACE_EVENT(DEATH)),";
break;
+ case UDPF_NONE:
+ s.op->line() << " .flags=(UDPF_NONE),";
+ s.op->line() << " .ops={ },";
+ s.op->line() << " .events=0,";
+ break;
default:
throw semantic_error ("bad utrace probe flag");
break;
@@ -5494,6 +5502,40 @@ utrace_derived_probe_group::emit_probe_decl (systemtap_session& s,
void
+utrace_derived_probe_group::emit_vm_callback_probe_decl (systemtap_session& s,
+ bool has_path,
+ string path,
+ int64_t pid,
+ string vm_callback)
+{
+ s.op->newline() << "{";
+ s.op->line() << " .tgt={";
+
+ if (has_path)
+ {
+ s.op->line() << " .pathname=\"" << path << "\",";
+ s.op->line() << " .pid=0,";
+ }
+ else
+ {
+ s.op->line() << " .pathname=NULL,";
+ s.op->line() << " .pid=" << pid << ",";
+ }
+
+ s.op->line() << " .callback=NULL,";
+ s.op->line() << " .vm_callback=&" << vm_callback << ",";
+ s.op->line() << " },";
+ s.op->line() << " .pp=\"internal\",";
+ s.op->line() << " .ph=NULL,";
+ s.op->line() << " .flags=(UDPF_NONE),";
+ s.op->line() << " .ops={ NULL },";
+ s.op->line() << " .events=0,";
+ s.op->line() << " .engine_attached=0,";
+ s.op->line() << " },";
+}
+
+
+void
utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
{
if (probes_by_path.empty() && probes_by_pid.empty())
@@ -5737,6 +5779,14 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
for (p_b_path_iterator it = probes_by_path.begin();
it != probes_by_path.end(); it++)
{
+ // Emit a "fake" probe decl that is really a hook for to get
+ // our vm_callback called.
+ string path = it->first;
+ s.op->newline() << "#ifdef DEBUG_TASK_FINDER_VMA";
+ emit_vm_callback_probe_decl (s, true, path, (int64_t)0,
+ "__stp_tf_vm_cb");
+ s.op->newline() << "#endif";
+
for (unsigned i = 0; i < it->second.size(); i++)
{
utrace_derived_probe *p = it->second[i];
@@ -5751,6 +5801,13 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
for (p_b_pid_iterator it = probes_by_pid.begin();
it != probes_by_pid.end(); it++)
{
+ // Emit a "fake" probe decl that is really a hook for to get
+ // our vm_callback called.
+ s.op->newline() << "#ifdef DEBUG_TASK_FINDER_VMA";
+ emit_vm_callback_probe_decl (s, false, NULL, it->first,
+ "__stp_tf_vm_cb");
+ s.op->newline() << "#endif";
+
for (unsigned i = 0; i < it->second.size(); i++)
{
utrace_derived_probe *p = it->second[i];