diff options
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | runtime/ChangeLog | 14 | ||||
-rw-r--r-- | runtime/syscall.h | 132 | ||||
-rw-r--r-- | runtime/task_finder.c | 317 | ||||
-rw-r--r-- | runtime/task_finder_vma.c | 112 | ||||
-rw-r--r-- | tapsets.cxx | 57 |
6 files changed, 637 insertions, 4 deletions
@@ -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 ®s->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 ®s->bx + n; +#elif defined(CONFIG_X86_64) +#ifdef CONFIG_IA32_EMULATION + if (test_tsk_thread_flag(task, TIF_IA32)) + switch (n) { + case 0: return ®s->bx; + case 1: return ®s->cx; + case 2: return ®s->dx; + case 3: return ®s->si; + case 4: return ®s->di; + case 5: return ®s->bp; + default: + _stp_error("syscall arg > 5"); + return NULL; + } +#endif /* CONFIG_IA32_EMULATION */ + switch (n) { + case 0: return ®s->di; + case 1: return ®s->si; + case 2: return ®s->dx; + case 3: return ®s->r10; + case 4: return ®s->r8; + case 5: return ®s->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]; |