summaryrefslogtreecommitdiffstats
path: root/runtime/task_finder.c
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/task_finder.c')
-rw-r--r--runtime/task_finder.c601
1 files changed, 546 insertions, 55 deletions
diff --git a/runtime/task_finder.c b/runtime/task_finder.c
index e78caab6..71b11569 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);
@@ -11,10 +13,44 @@ struct stap_task_finder_target;
#define __STP_TF_STOPPED 3
atomic_t __stp_task_finder_state = ATOMIC_INIT(__STP_TF_STARTING);
+#ifdef DEBUG_TASK_FINDER
+atomic_t __stp_attach_count = ATOMIC_INIT (0);
+
+#define debug_task_finder_attach() (atomic_inc(&__stp_attach_count))
+#define debug_task_finder_detach() (atomic_dec(&__stp_attach_count))
+#define debug_task_finder_report() (_stp_dbug(__FUNCTION__, __LINE__, \
+ "attach count: %d\n", atomic_read(&__stp_attach_count)))
+#else
+#define debug_task_finder_attach() /* empty */
+#define debug_task_finder_detach() /* empty */
+#define debug_task_finder_report() /* empty */
+#endif
+
typedef int (*stap_task_finder_callback)(struct task_struct *tsk,
int register_p,
+ int process_p,
struct stap_task_finder_target *tgt);
+typedef int (*stap_task_finder_vm_callback)(struct task_struct *tsk,
+ int map_p, char *vm_path,
+ unsigned long vm_start,
+ 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 */
@@ -28,12 +64,27 @@ struct stap_task_finder_target {
const char *pathname;
pid_t pid;
stap_task_finder_callback callback;
+ stap_task_finder_vm_callback vm_callback;
};
static u32
__stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine,
struct task_struct *tsk);
+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)
{
@@ -56,6 +107,11 @@ stap_register_task_finder_target(struct stap_task_finder_target *new_tgt)
new_tgt->engine_attached = 0;
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) {
@@ -115,6 +171,7 @@ stap_utrace_detach_ops(struct utrace_engine_ops *ops)
}
else if (engine != NULL) {
utrace_detach(tsk, engine);
+ debug_task_finder_detach();
}
}
} while_each_thread(grp, tsk);
@@ -125,6 +182,7 @@ udo_err:
_stp_error("utrace_attach returned error %d on pid %d",
error, pid);
}
+ debug_task_finder_report();
}
static void
@@ -190,16 +248,28 @@ __stp_get_mm_path(struct mm_struct *mm, char *buf, int buflen)
return rc;
}
-#define __STP_UTRACE_TASK_FINDER_EVENTS (UTRACE_EVENT(CLONE) \
- | UTRACE_EVENT(EXEC) \
- | UTRACE_EVENT(DEATH))
+#define __STP_TASK_FINDER_EVENTS (UTRACE_EVENT(CLONE) \
+ | UTRACE_EVENT(EXEC) \
+ | UTRACE_EVENT(DEATH))
+
+#define __STP_ATTACHED_TASK_BASE_EVENTS (UTRACE_EVENT(DEATH))
+
+#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_UTRACE_ATTACHED_TASK_EVENTS (UTRACE_EVENT(DEATH))
+#define __STP_ATTACHED_TASK_EVENTS(tgt) \
+ ((((tgt)->vm_callback) == NULL) ? __STP_ATTACHED_TASK_BASE_EVENTS \
+ : __STP_ATTACHED_TASK_VM_EVENTS)
static int
-__stp_utrace_attach(struct task_struct *tsk,
- const struct utrace_engine_ops *ops, void *data,
- unsigned long event_flags)
+stap_utrace_attach(struct task_struct *tsk,
+ const struct utrace_engine_ops *ops, void *data,
+ unsigned long event_flags)
{
struct utrace_attached_engine *engine;
struct mm_struct *mm;
@@ -231,13 +301,15 @@ __stp_utrace_attach(struct task_struct *tsk,
}
else {
utrace_set_flags(tsk, engine, event_flags);
+ debug_task_finder_attach();
}
return rc;
}
static inline void
__stp_utrace_attach_match_filename(struct task_struct *tsk,
- const char * const filename)
+ const char * const filename,
+ int register_p, int process_p)
{
size_t filelen;
struct list_head *tgt_node;
@@ -270,72 +342,114 @@ __stp_utrace_attach_match_filename(struct task_struct *tsk,
continue;
if (cb_tgt->callback != NULL) {
- int rc = cb_tgt->callback(tsk, 1, cb_tgt);
+ int rc = cb_tgt->callback(tsk, register_p,
+ process_p, cb_tgt);
if (rc != 0) {
- _stp_error("exec callback for %d failed: %d",
+ _stp_error("callback for %d failed: %d",
(int)tsk->pid, rc);
break;
}
}
- // Set up thread death notification.
- rc = __stp_utrace_attach(tsk, &cb_tgt->ops, cb_tgt,
- __STP_UTRACE_ATTACHED_TASK_EVENTS);
- if (rc != 0 && rc != EPERM)
- break;
- cb_tgt->engine_attached = 1;
+ // Set up events we need for attached tasks.
+ if (register_p) {
+ rc = stap_utrace_attach(tsk, &cb_tgt->ops,
+ cb_tgt,
+ __STP_ATTACHED_TASK_EVENTS(cb_tgt));
+ if (rc != 0 && rc != EPERM)
+ break;
+ cb_tgt->engine_attached = 1;
+ }
+ else {
+ struct utrace_attached_engine *engine;
+ engine = utrace_attach(tsk,
+ UTRACE_ATTACH_MATCH_OPS,
+ &cb_tgt->ops, 0);
+ if (! IS_ERR(engine) && engine != NULL) {
+ utrace_detach(tsk, engine);
+ debug_task_finder_detach();
+ }
+ }
}
}
}
-static u32
-__stp_utrace_task_finder_report_clone(struct utrace_attached_engine *engine,
- struct task_struct *parent,
- unsigned long clone_flags,
- struct task_struct *child)
+// This function handles the details of getting a task's associated
+// pathname, and calling __stp_utrace_attach_match_filename() to
+// attach to it if we find the pathname "interesting". So, what's the
+// difference between path_tsk and match_tsk? Normally they are the
+// same, except in one case. In an UTRACE_EVENT(EXEC), we need to
+// detach engines from the newly exec'ed process (since its path has
+// changed). In this case, we have to match the path of the parent
+// (path_tsk) against the child (match_tsk).
+
+static void
+__stp_utrace_attach_match_tsk(struct task_struct *path_tsk,
+ struct task_struct *match_tsk, int register_p,
+ int process_p)
{
- int rc;
struct mm_struct *mm;
char *mmpath_buf;
char *mmpath;
- if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING)
- return UTRACE_ACTION_RESUME;
-
- // On clone, attach to the child.
- rc = __stp_utrace_attach(child, engine->ops, 0,
- __STP_UTRACE_TASK_FINDER_EVENTS);
- if (rc != 0 && rc != EPERM)
- return UTRACE_ACTION_RESUME;
+ if (path_tsk->pid <= 1 || match_tsk->pid <= 1)
+ return;
- /* Grab the path associated with this task. */
- mm = get_task_mm(child);
+ /* Grab the path associated with the path_tsk. */
+ mm = get_task_mm(path_tsk);
if (! mm) {
/* If the thread doesn't have a mm_struct, it is
* a kernel thread which we need to skip. */
- return UTRACE_ACTION_RESUME;
+ return;
}
// Allocate space for a path
mmpath_buf = _stp_kmalloc(PATH_MAX);
if (mmpath_buf == NULL) {
+ mmput(mm);
_stp_error("Unable to allocate space for path");
- return UTRACE_ACTION_RESUME;
+ return;
}
// Grab the path associated with the new task
mmpath = __stp_get_mm_path(mm, mmpath_buf, PATH_MAX);
mmput(mm); /* We're done with mm */
if (mmpath == NULL || IS_ERR(mmpath)) {
- rc = -PTR_ERR(mmpath);
+ int rc = -PTR_ERR(mmpath);
_stp_error("Unable to get path (error %d) for pid %d",
- rc, (int)child->pid);
+ rc, (int)path_tsk->pid);
}
else {
- __stp_utrace_attach_match_filename(child, mmpath);
+ __stp_utrace_attach_match_filename(match_tsk, mmpath,
+ register_p, process_p);
}
_stp_kfree(mmpath_buf);
+ return;
+}
+
+static u32
+__stp_utrace_task_finder_report_clone(struct utrace_attached_engine *engine,
+ struct task_struct *parent,
+ unsigned long clone_flags,
+ struct task_struct *child)
+{
+ int rc;
+ struct mm_struct *mm;
+ char *mmpath_buf;
+ char *mmpath;
+
+ if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING)
+ return UTRACE_ACTION_RESUME;
+
+ // On clone, attach to the child.
+ rc = stap_utrace_attach(child, engine->ops, 0,
+ __STP_TASK_FINDER_EVENTS);
+ if (rc != 0 && rc != EPERM)
+ return UTRACE_ACTION_RESUME;
+
+ __stp_utrace_attach_match_tsk(parent, child, 1,
+ (clone_flags & CLONE_THREAD) == 0);
return UTRACE_ACTION_RESUME;
}
@@ -353,11 +467,25 @@ __stp_utrace_task_finder_report_exec(struct utrace_attached_engine *engine,
if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING)
return UTRACE_ACTION_RESUME;
- // On exec, check bprm
- if (bprm->filename == NULL)
- return UTRACE_ACTION_RESUME;
+ // When exec'ing, we need to let callers detach from the
+ // parent thread (if necessary). For instance, assume
+ // '/bin/bash' clones and then execs '/bin/ls'. If the user
+ // was probing '/bin/bash', the cloned thread is still
+ // '/bin/bash' up until the exec.
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#define real_parent parent
+#endif
+ if (tsk != NULL && tsk->real_parent != NULL
+ && tsk->real_parent->pid > 1) {
+ // We'll hardcode this as a process end, but a thread
+ // *could* call exec (although they aren't supposed to).
+ __stp_utrace_attach_match_tsk(tsk->real_parent, tsk, 0, 1);
+ }
- __stp_utrace_attach_match_filename(tsk, bprm->filename);
+ // We assume that all exec's are exec'ing a new process. Note
+ // that we don't use bprm->filename, since that path can be
+ // relative.
+ __stp_utrace_attach_match_tsk(tsk, tsk, 1, 1);
return UTRACE_ACTION_RESUME;
}
@@ -366,6 +494,7 @@ static u32
stap_utrace_task_finder_report_death(struct utrace_attached_engine *engine,
struct task_struct *tsk)
{
+ debug_task_finder_detach();
return UTRACE_ACTION_DETACH;
}
@@ -376,6 +505,7 @@ __stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine,
struct stap_task_finder_target *tgt = engine->data;
if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) {
+ debug_task_finder_detach();
return UTRACE_ACTION_DETACH;
}
@@ -392,15 +522,368 @@ __stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine,
int rc;
// Call the callback
- rc = tgt->callback(tsk, 0, tgt);
+ rc = tgt->callback(tsk, 0,
+ (atomic_read(&tsk->signal->live) == 0),
+ tgt);
if (rc != 0) {
_stp_error("death callback for %d failed: %d",
(int)tsk->pid, rc);
}
}
+ debug_task_finder_detach();
return UTRACE_ACTION_DETACH;
}
+static u32
+__stp_utrace_task_finder_target_quiesce(struct utrace_attached_engine *engine,
+ struct task_struct *tsk)
+{
+ struct stap_task_finder_target *tgt = engine->data;
+
+ // Turn off quiesce handling.
+ 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();
+ return UTRACE_ACTION_DETACH;
+ }
+
+ if (tgt != NULL && tgt->vm_callback != NULL) {
+ struct mm_struct *mm;
+ char *mmpath_buf;
+ char *mmpath;
+ struct vm_area_struct *vma;
+ int rc;
+
+ /* Call the vm_callback for every vma associated with
+ * a file. */
+ mm = get_task_mm(tsk);
+ if (! mm)
+ goto utftq_out;
+
+ // Allocate space for a path
+ mmpath_buf = _stp_kmalloc(PATH_MAX);
+ if (mmpath_buf == NULL) {
+ mmput(mm);
+ _stp_error("Unable to allocate space for path");
+ goto utftq_out;
+ }
+
+ down_read(&mm->mmap_sem);
+ vma = mm->mmap;
+ while (vma) {
+ if (vma->vm_file) {
+#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) {
+ // Call the callback
+ 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);
+ }
+
+ }
+ else {
+ _stp_dbug(__FUNCTION__, __LINE__,
+ "no mmpath?\n");
+ }
+ }
+ vma = vma->vm_next;
+ }
+ up_read(&mm->mmap_sem);
+ mmput(mm); /* We're done with mm */
+ _stp_kfree(mmpath_buf);
+ }
+
+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,
@@ -429,8 +912,8 @@ stap_start_task_finder(void)
size_t mmpathlen;
struct list_head *tgt_node;
- rc = __stp_utrace_attach(tsk, &__stp_utrace_task_finder_ops, 0,
- __STP_UTRACE_TASK_FINDER_EVENTS);
+ rc = stap_utrace_attach(tsk, &__stp_utrace_task_finder_ops, 0,
+ __STP_TASK_FINDER_EVENTS);
if (rc == EPERM) {
/* Ignore EPERM errors, which mean this wasn't
* a thread we can attach to. */
@@ -482,21 +965,27 @@ stap_start_task_finder(void)
cb_tgt = list_entry(cb_node,
struct stap_task_finder_target,
callback_list);
- if (cb_tgt == NULL || cb_tgt->callback == NULL)
+ if (cb_tgt == NULL)
continue;
- // Call the callback.
- rc = cb_tgt->callback(tsk, 1, cb_tgt);
- if (rc != 0) {
- _stp_error("attach callback for %d failed: %d",
- (int)tsk->pid, rc);
- goto stf_err;
+ // Call the callback. Assume that if
+ // the thread is a thread group
+ // leader, it is a process.
+ if (cb_tgt->callback != NULL) {
+ rc = cb_tgt->callback(tsk, 1,
+ (tsk->pid == tsk->tgid),
+ cb_tgt);
+ if (rc != 0) {
+ _stp_error("attach callback for %d failed: %d",
+ (int)tsk->pid, rc);
+ goto stf_err;
+ }
}
- // Set up thread death notification.
- rc = __stp_utrace_attach(tsk, &cb_tgt->ops,
- cb_tgt,
- __STP_UTRACE_ATTACHED_TASK_EVENTS);
+ // Set up events we need for attached tasks.
+ rc = stap_utrace_attach(tsk, &cb_tgt->ops,
+ cb_tgt,
+ __STP_ATTACHED_TASK_EVENTS(cb_tgt));
if (rc != 0 && rc != EPERM)
goto stf_err;
cb_tgt->engine_attached = 1;
@@ -514,7 +1003,9 @@ static void
stap_stop_task_finder(void)
{
atomic_set(&__stp_task_finder_state, __STP_TF_STOPPING);
+ debug_task_finder_report();
stap_utrace_detach_ops(&__stp_utrace_task_finder_ops);
__stp_task_finder_cleanup();
+ debug_task_finder_report();
atomic_set(&__stp_task_finder_state, __STP_TF_STOPPED);
}