diff options
-rw-r--r-- | ChangeLog | 51 | ||||
-rw-r--r-- | runtime/ChangeLog | 29 | ||||
-rw-r--r-- | runtime/syscall.h | 22 | ||||
-rw-r--r-- | runtime/task_finder.c | 199 | ||||
-rw-r--r-- | runtime/utrace_compatibility.h | 57 | ||||
-rwxr-xr-x | stap-client | 94 | ||||
-rwxr-xr-x | stap-server | 17 | ||||
-rwxr-xr-x | stap-serverd | 6 | ||||
-rw-r--r-- | tapsets.cxx | 80 |
9 files changed, 447 insertions, 108 deletions
@@ -1,3 +1,54 @@ +2008-08-21 David Smith <dsmith@redhat.com> + + * tapsets.cxx (itrace_derived_probe_group::emit_module_decls): + Updated task finder callback function signature. + (utrace_derived_probe_group::emit_module_decls): Ditto. + +2008-08-20 David Smith <dsmith@redhat.com> + + * tapsets.cxx (utrace_derived_probe_group::emit_probe_decl): + Supports original and new utrace interfaces. + (utrace_derived_probe_group::emit_module_decls): Ditto. + +2008-08-20 Dave Brolley <brolley@redhat.com> + + * stap-client: Ignore SIGHUP and SIGPIPE. + (initialization): Set b_specified.: + (parse_options): Handle the -b option. Quote $stap_arg. Use process_m. + (process_m): New function. + (process_o): Set stdout_redirection to simply the filename. + (process_response): Obtain the exit code from stap on the server side. + Copy the module to the current directory if -m was specified. + Call stream_output. + (stream_output): It's back. + (maybe_call_staprun): Print status messages for -v. Leave v_level + unchanged. Pass -o option to staprun. Wait until the staprun job + disappears. + (terminate): Redirect message to stderr. + (interrupt): Only kill staprun if it's still running. Call cleanup + if exiting. + (ignore_signal): New function. + * stap-server (receive_request): Quote the job specifier. + (send_response): Likewise. + (read_data_file): Use read to check the first line. Use cat the read + the entire file. + * stap-serverd (listen): Quote job specifier. + (terminate): Likewise. + +2008-08-19 David Smith <dsmith@redhat.com> + + PR 6841 + * tapsets.cxx (utrace_derived_probe_group::emit_probe_decl): + Workaround utrace bug by quiescing threads before attaching a + utrace syscall engine to them. + (utrace_derived_probe_group::emit_module_decls): Emit quiesce + handler. + +2008-08-18 David Smith <dsmith@redhat.com> + + * tapsets.cxx (register_standard_tapsets): Add missing + 'process.syscall' and 'process.syscall.return' bindings. + 2008-08-16 Mark Wielaard <mjw@redhat.com> * configure.ac (build_elfutils): Mention possible distro diff --git a/runtime/ChangeLog b/runtime/ChangeLog index 58678de5..3e1e400a 100644 --- a/runtime/ChangeLog +++ b/runtime/ChangeLog @@ -1,3 +1,32 @@ +2008-08-21 David Smith <dsmith@redhat.com> + + * task_finder.c (__stp_tf_vm_cb): Added task finder target + parameter. + (__stp_utrace_attach_match_filename): Updated task finder callback + call. + (__stp_utrace_task_finder_target_death): Ditto. + (__stp_utrace_task_finder_target_quiesce): Ditto. + (__stp_target_call_vm_callback): Ditto. + (__stp_utrace_task_finder_target_syscall_exit): Ditto. + (stap_start_task_finder): Ditto. + +2008-08-20 David Smith <dsmith@redhat.com> + + * task_finder.c: Supports original and new utrace interfaces. + * utrace_compatibility.h (utrace_attach_task): Compatibility layer + for original utrace interface. + + * task_finder.c (__stp_utrace_task_finder_target_syscall_entry): + Handles mmap2 (as well as mmap). + (__stp_utrace_task_finder_target_syscall_exit): Ditto. + * syscall.h: Added defines for mmap and mmap2. + +2008-08-19 David Smith <dsmith@redhat.com> + + PR 6841 + * task_finder.c (__stp_utrace_task_finder_target_quiesce): + Quiesces thread before turning on syscall tracing. + 2008-08-14 Frank Ch. Eigler <fche@elastic.org> PR 6842. diff --git a/runtime/syscall.h b/runtime/syscall.h index 3d1034e6..24e93463 100644 --- a/runtime/syscall.h +++ b/runtime/syscall.h @@ -11,12 +11,14 @@ #define _SYSCALL_H_ #if defined(__i386__) || defined(CONFIG_IA32_EMULATION) -#define __MMAP_SYSCALL_NO_IA32 192 /* mmap2 */ +#define __MMAP_SYSCALL_NO_IA32 90 +#define __MMAP2_SYSCALL_NO_IA32 192 #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 MMAP2_SYSCALL_NO(tsk) __MMAP2_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 @@ -25,13 +27,19 @@ #if defined(__x86_64__) #define __MMAP_SYSCALL_NO_X86_64 9 +/* x86_64 doesn't have a mmap2 system call. So, we'll use a number + * that doesn't map to a real system call. */ +#define __MMAP2_SYSCALL_NO_X86_64 ((unsigned long)-1) #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 \ +#define MMAP_SYSCALL_NO(tsk) ((test_tsk_thread_flag((tsk), TIF_IA32)) \ + ? __MMAP_SYSCALL_NO_IA32 \ : __MMAP_SYSCALL_NO_X86_64) +#define MMAP2_SYSCALL_NO(tsk) ((test_tsk_thread_flag((tsk), TIF_IA32)) \ + ? __MMAP2_SYSCALL_NO_IA32 \ + : __MMAP2_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) @@ -51,13 +59,17 @@ #if defined(__powerpc__) #define MMAP_SYSCALL_NO(tsk) 90 +/* MMAP2 only exists on a 32-bit kernel. On a 64-bit kernel, we'll + * never see mmap2 (but that's OK). */ +#define MMAP2_SYSCALL_NO(tsk) 192 #define MPROTECT_SYSCALL_NO(tsk) 125 #define MUNMAP_SYSCALL_NO(tsk) 91 #define MREMAP_SYSCALL_NO(tsk) 163 #endif -#if !defined(MMAP_SYSCALL_NO) || !defined(MPROTECT_SYSCALL_NO) \ - || !defined(MUNMAP_SYSCALL_NO) || !defined(MREMAP_SYSCALL_NO) +#if !defined(MMAP_SYSCALL_NO) || !defined(MMAP2_SYSCALL_NO) \ + || !defined(MPROTECT_SYSCALL_NO) || !defined(MUNMAP_SYSCALL_NO) \ + || !defined(MREMAP_SYSCALL_NO) #error "Unimplemented architecture" #endif diff --git a/runtime/task_finder.c b/runtime/task_finder.c index 59b83b76..146ce06f 100644 --- a/runtime/task_finder.c +++ b/runtime/task_finder.c @@ -11,6 +11,7 @@ #include <linux/mount.h> #include "syscall.h" +#include "utrace_compatibility.h" #include "task_finder_vma.c" static LIST_HEAD(__stp_task_finder_list); @@ -36,19 +37,21 @@ atomic_t __stp_attach_count = ATOMIC_INIT (0); #define debug_task_finder_report() /* empty */ #endif -typedef int (*stap_task_finder_callback)(struct task_struct *tsk, +typedef int (*stap_task_finder_callback)(struct stap_task_finder_target *tgt, + struct task_struct *tsk, int register_p, - int process_p, - struct stap_task_finder_target *tgt); + int process_p); -typedef int (*stap_task_finder_vm_callback)(struct task_struct *tsk, +typedef int (*stap_task_finder_vm_callback)(struct stap_task_finder_target *tgt, + 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 __stp_tf_vm_cb(struct stap_task_finder_target *tgt, + struct task_struct *tsk, int map_p, char *vm_path, unsigned long vm_start, unsigned long vm_end, @@ -86,23 +89,54 @@ struct stap_task_finder_target { stap_task_finder_vm_callback vm_callback; }; +#ifdef UTRACE_ORIG_VERSION static u32 __stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine, struct task_struct *tsk); +#else +static u32 +__stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine, + struct task_struct *tsk, + bool group_dead, int signal); +#endif +#ifdef UTRACE_ORIG_VERSION static u32 __stp_utrace_task_finder_target_quiesce(struct utrace_attached_engine *engine, struct task_struct *tsk); +#else +static u32 +__stp_utrace_task_finder_target_quiesce(enum utrace_resume_action action, + struct utrace_attached_engine *engine, + struct task_struct *tsk, + unsigned long event); +#endif +#ifdef UTRACE_ORIG_VERSION static u32 __stp_utrace_task_finder_target_syscall_entry(struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs); +#else +static u32 +__stp_utrace_task_finder_target_syscall_entry(enum utrace_resume_action action, + struct utrace_attached_engine *engine, + struct task_struct *tsk, + struct pt_regs *regs); +#endif +#ifdef UTRACE_ORIG_VERSION static u32 __stp_utrace_task_finder_target_syscall_exit(struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs); +#else +static u32 +__stp_utrace_task_finder_target_syscall_exit(enum utrace_resume_action action, + struct utrace_attached_engine *engine, + struct task_struct *tsk, + struct pt_regs *regs); +#endif static int stap_register_task_finder_target(struct stap_task_finder_target *new_tgt) @@ -186,11 +220,11 @@ stap_utrace_detach(struct task_struct *tsk, // we'd miss detaching from it if we were checking to see if // it had an mm. - engine = utrace_attach(tsk, UTRACE_ATTACH_MATCH_OPS, ops, 0); + engine = utrace_attach_task(tsk, UTRACE_ATTACH_MATCH_OPS, ops, 0); if (IS_ERR(engine)) { rc = -PTR_ERR(engine); if (rc != ENOENT) { - _stp_error("utrace_attach returned error %d on pid %d", + _stp_error("utrace_attach_task returned error %d on pid %d", rc, tsk->pid); } else { @@ -203,7 +237,7 @@ stap_utrace_detach(struct task_struct *tsk, rc = EFAULT; } else { - rc = utrace_detach(tsk, engine); + rc = utrace_control(tsk, engine, UTRACE_DETACH); switch (rc) { case 0: /* success */ debug_task_finder_detach(); @@ -327,8 +361,8 @@ __stp_get_mm_path(struct mm_struct *mm, char *buf, int buflen) | UTRACE_EVENT(SYSCALL_ENTRY) \ | UTRACE_EVENT(SYSCALL_EXIT)) -#define __STP_ATTACHED_TASK_VM_EVENTS (__STP_ATTACHED_TASK_VM_BASE_EVENTS \ - | UTRACE_ACTION_QUIESCE \ +#define __STP_ATTACHED_TASK_VM_EVENTS (__STP_ATTACHED_TASK_BASE_EVENTS \ + | UTRACE_STOP \ | UTRACE_EVENT(QUIESCE)) #define __STP_ATTACHED_TASK_EVENTS(tgt) \ @@ -354,7 +388,7 @@ stap_utrace_attach(struct task_struct *tsk, return EPERM; mmput(mm); - engine = utrace_attach(tsk, UTRACE_ATTACH_CREATE, ops, data); + engine = utrace_attach_task(tsk, UTRACE_ATTACH_CREATE, ops, data); if (IS_ERR(engine)) { int error = -PTR_ERR(engine); if (error != ENOENT) { @@ -369,11 +403,11 @@ stap_utrace_attach(struct task_struct *tsk, rc = EFAULT; } else { - rc = utrace_set_flags(tsk, engine, event_flags); + rc = utrace_set_events(tsk, engine, event_flags); if (rc == 0) debug_task_finder_attach(); else - _stp_error("utrace_set_flags returned error %d on pid %d", + _stp_error("utrace_set_events returned error %d on pid %d", rc, (int)tsk->pid); } return rc; @@ -421,8 +455,9 @@ __stp_utrace_attach_match_filename(struct task_struct *tsk, continue; if (cb_tgt->callback != NULL) { - int rc = cb_tgt->callback(tsk, register_p, - process_p, cb_tgt); + int rc = cb_tgt->callback(cb_tgt, tsk, + register_p, + process_p); if (rc != 0) { _stp_error("callback for %d failed: %d", (int)tsk->pid, rc); @@ -505,11 +540,20 @@ __stp_utrace_attach_match_tsk(struct task_struct *path_tsk, return; } +#ifdef UTRACE_ORIG_VERSION 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) +#else +static u32 +__stp_utrace_task_finder_report_clone(enum utrace_resume_action action, + struct utrace_attached_engine *engine, + struct task_struct *parent, + unsigned long clone_flags, + struct task_struct *child) +#endif { int rc; struct mm_struct *mm; @@ -517,24 +561,34 @@ __stp_utrace_task_finder_report_clone(struct utrace_attached_engine *engine, char *mmpath; if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) - return UTRACE_ACTION_RESUME; + return UTRACE_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; + return UTRACE_RESUME; __stp_utrace_attach_match_tsk(parent, child, 1, (clone_flags & CLONE_THREAD) == 0); - return UTRACE_ACTION_RESUME; + return UTRACE_RESUME; } +#ifdef UTRACE_ORIG_VERSION static u32 __stp_utrace_task_finder_report_exec(struct utrace_attached_engine *engine, struct task_struct *tsk, const struct linux_binprm *bprm, struct pt_regs *regs) +#else +static u32 +__stp_utrace_task_finder_report_exec(enum utrace_resume_action action, + struct utrace_attached_engine *engine, + struct task_struct *tsk, + const struct linux_binfmt *fmt, + const struct linux_binprm *bprm, + struct pt_regs *regs) +#endif { size_t filelen; struct list_head *tgt_node; @@ -542,7 +596,7 @@ __stp_utrace_task_finder_report_exec(struct utrace_attached_engine *engine, int found_node = 0; if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) - return UTRACE_ACTION_RESUME; + return UTRACE_RESUME; // When exec'ing, we need to let callers detach from the // parent thread (if necessary). For instance, assume @@ -565,26 +619,40 @@ __stp_utrace_task_finder_report_exec(struct utrace_attached_engine *engine, // relative. __stp_utrace_attach_match_tsk(tsk, tsk, 1, 1); - return UTRACE_ACTION_RESUME; + return UTRACE_RESUME; } +#ifdef UTRACE_ORIG_VERSION static u32 stap_utrace_task_finder_report_death(struct utrace_attached_engine *engine, struct task_struct *tsk) +#else +static u32 +stap_utrace_task_finder_report_death(struct utrace_attached_engine *engine, + struct task_struct *tsk, + bool group_dead, int signal) +#endif { debug_task_finder_detach(); - return UTRACE_ACTION_DETACH; + return UTRACE_DETACH; } +#ifdef UTRACE_ORIG_VERSION static u32 __stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine, struct task_struct *tsk) +#else +static u32 +__stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine, + struct task_struct *tsk, + bool group_dead, int signal) +#endif { 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; + return UTRACE_DETACH; } // The first implementation of this added a @@ -600,30 +668,42 @@ __stp_utrace_task_finder_target_death(struct utrace_attached_engine *engine, int rc; // Call the callback - rc = tgt->callback(tsk, 0, - (tsk->signal == NULL) || (atomic_read(&tsk->signal->live) == 0), - tgt); + rc = tgt->callback(tgt, tsk, 0, + ((tsk->signal == NULL) + || (atomic_read(&tsk->signal->live) == 0))); if (rc != 0) { _stp_error("death callback for %d failed: %d", (int)tsk->pid, rc); } } debug_task_finder_detach(); - return UTRACE_ACTION_DETACH; + return UTRACE_DETACH; } +#ifdef UTRACE_ORIG_VERSION static u32 __stp_utrace_task_finder_target_quiesce(struct utrace_attached_engine *engine, struct task_struct *tsk) +#else +static u32 +__stp_utrace_task_finder_target_quiesce(enum utrace_resume_action action, + struct utrace_attached_engine *engine, + struct task_struct *tsk, + unsigned long event) +#endif { struct stap_task_finder_target *tgt = engine->data; + int rc; - // Turn off quiesce handling. - utrace_set_flags(tsk, engine, __STP_ATTACHED_TASK_VM_BASE_EVENTS); + // Turn off quiesce handling (and turn on syscall handling). + rc = utrace_set_events(tsk, engine, __STP_ATTACHED_TASK_VM_BASE_EVENTS); + if (rc != 0) + _stp_error("utrace_set_events returned error %d on pid %d", + rc, (int)tsk->pid); if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) { debug_task_finder_detach(); - return UTRACE_ACTION_DETACH; + return UTRACE_DETACH; } if (tgt != NULL && tgt->vm_callback != NULL) { @@ -661,7 +741,8 @@ __stp_utrace_task_finder_target_quiesce(struct utrace_attached_engine *engine, #endif if (mmpath) { // Call the callback - rc = tgt->vm_callback(tsk, 1, mmpath, + rc = tgt->vm_callback(tgt, tsk, 1, + mmpath, vma->vm_start, vma->vm_end, (vma->vm_pgoff @@ -685,7 +766,7 @@ __stp_utrace_task_finder_target_quiesce(struct utrace_attached_engine *engine, } utftq_out: - return (UTRACE_ACTION_NEWSTATE | UTRACE_ACTION_RESUME); + return UTRACE_RESUME; } @@ -702,10 +783,18 @@ __stp_find_file_based_vma(struct mm_struct *mm, unsigned long addr) return vma; } +#ifdef UTRACE_ORIG_VERSION static u32 __stp_utrace_task_finder_target_syscall_entry(struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs) +#else +static u32 +__stp_utrace_task_finder_target_syscall_entry(enum utrace_resume_action action, + struct utrace_attached_engine *engine, + struct task_struct *tsk, + struct pt_regs *regs) +#endif { struct stap_task_finder_target *tgt = engine->data; unsigned long syscall_no; @@ -716,20 +805,21 @@ __stp_utrace_task_finder_target_syscall_entry(struct utrace_attached_engine *eng if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) { debug_task_finder_detach(); - return UTRACE_ACTION_DETACH; + return UTRACE_DETACH; } if (tgt == NULL || tgt->vm_callback == NULL) - return UTRACE_ACTION_RESUME; + return UTRACE_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 != MMAP2_SYSCALL_NO(tsk) && syscall_no != MPROTECT_SYSCALL_NO(tsk) && syscall_no != MUNMAP_SYSCALL_NO(tsk)) - return UTRACE_ACTION_RESUME; + return UTRACE_RESUME; // We need the first syscall argument to see what address @@ -755,7 +845,7 @@ __stp_utrace_task_finder_target_syscall_entry(struct utrace_attached_engine *eng mmput(mm); } } - return UTRACE_ACTION_RESUME; + return UTRACE_RESUME; } static void @@ -787,7 +877,7 @@ __stp_target_call_vm_callback(struct stap_task_finder_target *tgt, rc, (int)tsk->pid); } else { - rc = tgt->vm_callback(tsk, 1, mmpath, vma->vm_start, + rc = tgt->vm_callback(tgt, tsk, 1, mmpath, vma->vm_start, vma->vm_end, (vma->vm_pgoff << PAGE_SHIFT)); if (rc != 0) { @@ -798,10 +888,18 @@ __stp_target_call_vm_callback(struct stap_task_finder_target *tgt, _stp_kfree(mmpath_buf); } +#ifdef UTRACE_ORIG_VERSION static u32 __stp_utrace_task_finder_target_syscall_exit(struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs) +#else +static u32 +__stp_utrace_task_finder_target_syscall_exit(enum utrace_resume_action action, + struct utrace_attached_engine *engine, + struct task_struct *tsk, + struct pt_regs *regs) +#endif { struct stap_task_finder_target *tgt = engine->data; unsigned long syscall_no; @@ -814,27 +912,28 @@ __stp_utrace_task_finder_target_syscall_exit(struct utrace_attached_engine *engi if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) { debug_task_finder_detach(); - return UTRACE_ACTION_DETACH; + return UTRACE_DETACH; } if (tgt == NULL || tgt->vm_callback == NULL) - return UTRACE_ACTION_RESUME; + return UTRACE_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 != MMAP2_SYSCALL_NO(tsk) && syscall_no != MPROTECT_SYSCALL_NO(tsk) && syscall_no != MUNMAP_SYSCALL_NO(tsk)) - return UTRACE_ACTION_RESUME; + return UTRACE_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; + return UTRACE_RESUME; } // We need the first syscall argument to see what address we @@ -843,7 +942,7 @@ __stp_utrace_task_finder_target_syscall_exit(struct utrace_attached_engine *engi 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; + return UTRACE_RESUME; } #ifdef DEBUG_TASK_FINDER_VMA @@ -851,9 +950,10 @@ __stp_utrace_task_finder_target_syscall_exit(struct utrace_attached_engine *engi "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"))), + : ((syscall_no == MMAP2_SYSCALL_NO(tsk)) ? "mmap2" + : ((syscall_no == MPROTECT_SYSCALL_NO(tsk)) ? "mprotect" + : ((syscall_no == MUNMAP_SYSCALL_NO(tsk)) ? "munmap" + : "UNKNOWN")))), arg0, rv); #endif @@ -896,7 +996,7 @@ __stp_utrace_task_finder_target_syscall_exit(struct utrace_attached_engine *engi // FIXME: We'll need to figure out to // retrieve the path of a deleted // vma. - rc = tgt->vm_callback(tsk, 0, NULL, + rc = tgt->vm_callback(tgt, tsk, 0, NULL, entry->vm_start, entry->vm_end, (entry->vm_pgoff @@ -930,7 +1030,7 @@ __stp_utrace_task_finder_target_syscall_exit(struct utrace_attached_engine *engi // FIXME: We'll need to figure out to // retrieve the path of a deleted // vma. - rc = tgt->vm_callback(tsk, 0, NULL, + rc = tgt->vm_callback(tgt, tsk, 0, NULL, entry->vm_start, entry->vm_end, (entry->vm_pgoff @@ -963,7 +1063,7 @@ __stp_utrace_task_finder_target_syscall_exit(struct utrace_attached_engine *engi // Cleanup by deleting the saved vma info. __stp_tf_remove_vma_entry(entry); } - return UTRACE_ACTION_RESUME; + return UTRACE_RESUME; } struct utrace_engine_ops __stp_utrace_task_finder_ops = { @@ -1064,9 +1164,8 @@ stap_start_task_finder(void) // 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); + rc = cb_tgt->callback(cb_tgt, tsk, 1, + (tsk->pid == tsk->tgid)); if (rc != 0) { _stp_error("attach callback for %d failed: %d", (int)tsk->pid, rc); diff --git a/runtime/utrace_compatibility.h b/runtime/utrace_compatibility.h new file mode 100644 index 00000000..80037015 --- /dev/null +++ b/runtime/utrace_compatibility.h @@ -0,0 +1,57 @@ +/* + * utrace compatibility 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 _UTRACE_COMPATIBILITY_H_ +#define _UTRACE_COMPATIBILITY_H_ + +#include <linux/utrace.h> + +#ifdef UTRACE_ACTION_RESUME + +/* + * If UTRACE_ACTION_RESUME is defined after including utrace.h, we've + * got the original version of utrace. So that utrace clients can be + * written using the new interface (mostly), provide a (very thin) + * compatibility layer that hides the differences. + */ + +#define UTRACE_ORIG_VERSION + +#define UTRACE_RESUME UTRACE_ACTION_RESUME +#define UTRACE_DETACH UTRACE_ACTION_DETACH +#define UTRACE_STOP UTRACE_ACTION_QUIESCE + +static inline struct utrace_attached_engine * +utrace_attach_task(struct task_struct *target, int flags, + const struct utrace_engine_ops *ops, void *data) +{ + return utrace_attach(target, flags, ops, data); +} + +static inline int __must_check +utrace_control(struct task_struct *target, + struct utrace_attached_engine *engine, + unsigned long action) +{ + if (action == UTRACE_DETACH) + return utrace_detach(target, engine); + return -EINVAL; +} + +static inline int __must_check +utrace_set_events(struct task_struct *target, + struct utrace_attached_engine *engine, + unsigned long eventmask) +{ + return utrace_set_flags(target, engine, eventmask); +} +#endif + +#endif /* _UTRACE_COMPATIBILITY_H_ */ diff --git a/stap-client b/stap-client index bced5e5f..3fa397a7 100755 --- a/stap-client +++ b/stap-client @@ -19,6 +19,7 @@ # Catch ctrl-c and other termination signals trap 'terminate' SIGTERM trap 'interrupt' SIGINT +trap 'ignore_signal' SIGHUP SIGPIPE #----------------------------------------------------------------------------- # Helper functions. @@ -41,6 +42,7 @@ function initialization { p_phase=5 v_level=0 keep_temps=0 + b_specified=0 # Create a temporary directory to package things in # Do this before parsing the command line so that there is a place @@ -110,6 +112,9 @@ function parse_options { # We are at the start of an option. Look at the first character. case $first_char in + b) + b_specified=1 + ;; c) get_arg $first_token "$2" process_c "$stap_arg" @@ -135,7 +140,7 @@ function parse_options { ;; m) get_arg $first_token $2 - cmdline2="${cmdline2}m $stap_arg" + process_m $stap_arg ;; o) get_arg $first_token $2 @@ -147,7 +152,7 @@ function parse_options { ;; r) get_arg $first_token $2 - cmdline2="${cmdline2}r $stap_arg" + cmdline2="${cmdline2}r '$stap_arg'" ;; R) get_arg $first_token $2 @@ -155,14 +160,14 @@ function parse_options { ;; s) get_arg $first_token $2 - cmdline2="${cmdline2}s $stap_arg" + cmdline2="${cmdline2}s '$stap_arg'" ;; v) v_level=$(($v_level + 1)) ;; x) get_arg $first_token $2 - cmdline2="${cmdline2}x $stap_arg" + cmdline2="${cmdline2}x '$stap_arg'" ;; *) # An unknown or unimportant flag. Ignore it, but pass it on to the server. @@ -258,11 +263,19 @@ function process_I { cmdline2="${cmdline2}I 'tapsets/$local_name'" } +# function: process_m ARGUMENT +# +# Process the -m flag. +function process_m { + m_name="$1" + cmdline2="${cmdline2}m '$1'" +} + # function: process_o ARGUMENT # # Process the -o flag. function process_o { - stdout_redirection="> $1" + stdout_redirection="$1" cmdline2="${cmdline2}o '$1'" } @@ -531,13 +544,30 @@ function disconnect_from_server { # # Write the stdout and stderr from the server to stdout and stderr respectively. function process_response { + # Pick up the results of running stap on the server. + rc=`cat rc` + + # Copy the module to the current directory, if -m was specified + if test "X$m_name" != "X"; then + if test -f $tmpdir_stap/$m_name.ko; then + cp $tmpdir_stap/$m_name.ko $wd + else + stream_output + fatal "module $tmpdir_stap/$m_name.ko does not exist" + fi + fi + # Output stdout and stderr as directed + stream_output +} + +# function: stream_output +# +# Output stdout and stderr as directed +function stream_output { cd $tmpdir_server cat stderr >&2 - eval cat stdout $stdout_redirection - - # Pick up the results of running stap on the server. - rc=`cat rc` + cat stdout } # function: maybe_call_staprun @@ -566,6 +596,8 @@ function maybe_call_staprun { fi if test $p_phase = 5; then + test $v_level -gt 0 && echo "Pass 5: starting run." >&2 + # We have a module. Try to run it # If a -c command was specified, pass it along. if test "X$c_cmd" != "X"; then @@ -574,21 +606,37 @@ function maybe_call_staprun { # The -v level will be one less than what was specified # for us. - for ((--v_level; $v_level > 0; --v_level)) + for ((vl=$((v_level - 1)); $vl > 0; --vl)) do staprun_opts="$staprun_opts -v" done + # if -o was specified, pass it along + if test "X$stdout_redirection" != "X"; then + staprun_opts="$staprun_opts -o $stdout_redirection" + fi + # Run it in the background and wait for it. This # way any signals send to us can be caught. + if test $v_level -ge 2; then + echo "running `which staprun` $staprun_opts $tmpdir_stap/`ls $tmpdir_stap | grep '.ko$'`" >&2 + fi PATH=`staprun_PATH` eval staprun "$staprun_opts" \ - $tmpdir_stap/`ls $tmpdir_stap | grep '.ko$'` & + $tmpdir_stap/`ls $tmpdir_stap | grep '.ko$'` staprun_running=1 - wait %?staprun + wait '%?staprun' > /dev/null 2>&1 rc=$? staprun_running=0 # 127 from wait means that the job was already finished. test $rc=127 && rc=0 + + # Wait until the job actually disappears so that its output is complete. + while jobs '%?staprun' >/dev/null 2>&1 + do + sleep 1 + done + + test $v_level -gt 0 && echo "Pass 5: run completed in 0usr/0sys/0real ms." >&2 fi fi } @@ -640,11 +688,11 @@ function cleanup { # Terminate gracefully. function terminate { # Clean up - echo "$0: terminated by signal" + echo "$0: terminated by signal" >&2 cleanup # Kill any running staprun job - kill -s SIGTERM %?staprun 2>/dev/null + kill -s SIGTERM '%?staprun' 2>/dev/null exit 1 } @@ -654,11 +702,21 @@ function terminate { # Pass an interrupt (ctrl-C) to staprun function interrupt { # Pass the signal on to any running staprun job - kill -s SIGINT %?staprun 2>/dev/null + if test $staprun_running = 1; then + kill -s SIGINT '%?staprun' 2>/dev/null + return + fi + + # If staprun was not running, then exit. + cleanup + exit 1 +} - # If staprun was running, then just interrupt it. Otherwise - # we exit. - test $staprun_running = 0 && exit 1 +# function: ignore_signal +# +# Called in order to ignore a signal +function ignore_signal { + : } #----------------------------------------------------------------------------- diff --git a/stap-server b/stap-server index 12f93757..b82bc695 100755 --- a/stap-server +++ b/stap-server @@ -69,7 +69,7 @@ function receive_request { nc -l $port < /dev/zero > $tar_client & # Wait for the transfer to complete. - wait %nc + wait '%nc' >/dev/null 2>&1 } # function: unpack_request @@ -159,25 +159,18 @@ function read_data_file { # Verify the first line of the file. read <&3 - line=$REPLY + line="$REPLY" data=`expr "$line" : "$1: \\\(.*\\\)"` if test "X$data" = "X"; then fatal "ERROR: Data in file $1 is incorrect" return fi - # Add any additional lines of data - while read <&3 - do - data="$data -$REPLY" - done - # Close the file exec 3<&- - # Echo the result - echo "$data" + # Now read the entire file. + cat $1 | sed "s/$1: //" } # function: parse_options [ STAP-OPTIONS ] @@ -397,7 +390,7 @@ function send_response { # in order to workaround a nc bug. It closes the connection # early if stdin from the other side is not provided. nc -l $port < $tar_server > /dev/null & - wait %nc + wait '%nc' >/dev/null 2>&1 } # function: fatal [ MESSAGE ] diff --git a/stap-serverd b/stap-serverd index a6611255..6970217d 100755 --- a/stap-serverd +++ b/stap-serverd @@ -78,7 +78,7 @@ function listen { advertise_presence first=0 fi - wait %nc + wait '%nc' >/dev/null 2>&1 rc=$? if test $rc = 127 -o $rc = 0; then break; # success @@ -110,11 +110,11 @@ function terminate { # Kill the running 'avahi-publish-service' job kill -s SIGTERM %avahi-publish-service 2> /dev/null - wait %avahi-publish-service 2> /dev/null + wait '%avahi-publish-service' >/dev/null 2>&1 # Kill any running 'nc -l' job. kill -s SIGTERM "%nc -l" 2> /dev/null - wait "%nc - l" 2> /dev/null + wait "%nc - l" >/dev/null 2>&1 # Clean up cd `dirname $tmpdir` diff --git a/tapsets.cxx b/tapsets.cxx index 8f0b38b4..3d4d0dc9 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -5702,7 +5702,7 @@ itrace_derived_probe_group::emit_module_decls (systemtap_session& s) // Output task finder callback routine that gets called for all // itrace probe types. - s.op->newline() << "static int _stp_itrace_probe_cb(struct task_struct *tsk, int register_p, int process_p, struct stap_task_finder_target *tgt) {"; + s.op->newline() << "static int _stp_itrace_probe_cb(struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {"; s.op->indent(1); s.op->newline() << "int rc = 0;"; s.op->newline() << "struct stap_itrace_probe *p = container_of(tgt, struct stap_itrace_probe, tgt);"; @@ -6084,12 +6084,12 @@ utrace_derived_probe_group::emit_probe_decl (systemtap_session& s, case UDPF_BEGIN: // process begin s.op->line() << " .flags=(UDPF_BEGIN),"; s.op->line() << " .ops={ .report_quiesce=stap_utrace_probe_quiesce },"; - s.op->line() << " .events=(UTRACE_ACTION_QUIESCE|UTRACE_EVENT(QUIESCE)),"; + s.op->line() << " .events=(UTRACE_STOP|UTRACE_EVENT(QUIESCE)),"; break; case UDPF_THREAD_BEGIN: // thread begin s.op->line() << " .flags=(UDPF_THREAD_BEGIN),"; s.op->line() << " .ops={ .report_quiesce=stap_utrace_probe_quiesce },"; - s.op->line() << " .events=(UTRACE_ACTION_QUIESCE|UTRACE_EVENT(QUIESCE)),"; + s.op->line() << " .events=(UTRACE_STOP|UTRACE_EVENT(QUIESCE)),"; break; // Notice we're not setting up a .ops/.report_death handler for @@ -6104,17 +6104,19 @@ utrace_derived_probe_group::emit_probe_decl (systemtap_session& s, // For UDPF_SYSCALL/UDPF_SYSCALL_RETURN probes, the .report_death // handler isn't strictly necessary. However, it helps to keep - // our attaches/detaches symmetrical. + // our attaches/detaches symmetrical. Notice we're using quiesce + // as a workaround for bug 6841. case UDPF_SYSCALL: s.op->line() << " .flags=(UDPF_SYSCALL),"; - s.op->line() << " .ops={ .report_syscall_entry=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death },"; - s.op->line() << " .events=(UTRACE_EVENT(SYSCALL_ENTRY)|UTRACE_EVENT(DEATH)),"; + s.op->line() << " .ops={ .report_syscall_entry=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death, .report_quiesce=stap_utrace_probe_syscall_quiesce },"; + s.op->line() << " .events=(UTRACE_STOP|UTRACE_EVENT(QUIESCE)|UTRACE_EVENT(DEATH)),"; break; case UDPF_SYSCALL_RETURN: s.op->line() << " .flags=(UDPF_SYSCALL_RETURN),"; - 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)),"; + s.op->line() << " .ops={ .report_syscall_exit=stap_utrace_probe_syscall, .report_death=stap_utrace_task_finder_report_death, .report_quiesce=stap_utrace_probe_syscall_quiesce },"; + s.op->line() << " .events=(UTRACE_STOP|UTRACE_EVENT(QUIESCE)|UTRACE_EVENT(DEATH)),"; break; + case UDPF_NONE: s.op->line() << " .flags=(UDPF_NONE),"; s.op->line() << " .ops={ },"; @@ -6200,7 +6202,11 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) // Output handler function for UDPF_BEGIN and UDPF_THREAD_BEGIN if (flags_seen[UDPF_BEGIN] || flags_seen[UDPF_THREAD_BEGIN]) { + s.op->newline() << "#ifdef UTRACE_ORIG_VERSION"; s.op->newline() << "static u32 stap_utrace_probe_quiesce(struct utrace_attached_engine *engine, struct task_struct *tsk) {"; + s.op->newline() << "#else"; + s.op->newline() << "static u32 stap_utrace_probe_quiesce(enum utrace_resume_action action, struct utrace_attached_engine *engine, struct task_struct *tsk, unsigned long event) {"; + s.op->newline() << "#endif"; s.op->indent(1); s.op->newline() << "struct stap_utrace_probe *p = (struct stap_utrace_probe *)engine->data;"; @@ -6211,10 +6217,9 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "(*p->ph) (c);"; common_probe_entryfn_epilogue (s.op); - // UTRACE_ACTION_NEWSTATE not needed here to clear quiesce since - // we're detaching - utrace automatically restarts the thread. + // we're detaching, so utrace automatically restarts the thread. s.op->newline() << "debug_task_finder_detach();"; - s.op->newline() << "return UTRACE_ACTION_DETACH;"; + s.op->newline() << "return UTRACE_DETACH;"; s.op->newline(-1) << "}"; } @@ -6238,7 +6243,39 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) // Output handler function for SYSCALL_ENTRY and SYSCALL_EXIT events if (flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN]) { + s.op->newline() << "#ifdef UTRACE_ORIG_VERSION"; + s.op->newline() << "static u32 stap_utrace_probe_syscall_quiesce(struct utrace_attached_engine *engine, struct task_struct *tsk) {"; + s.op->newline() << "#else"; + s.op->newline() << "static u32 stap_utrace_probe_syscall_quiesce(enum utrace_resume_action action, struct utrace_attached_engine *engine, struct task_struct *tsk, unsigned long event) {"; + s.op->newline() << "#endif"; + s.op->indent(1); + s.op->newline() << "struct stap_utrace_probe *p = (struct stap_utrace_probe *)engine->data;"; + s.op->newline() << "int rc = 0;"; + + // Turn off quiesce handling and turn on either syscall entry + // or exit events. + s.op->newline() << "if (p->flags == UDPF_SYSCALL)"; + s.op->indent(1); + s.op->newline() << "rc = utrace_set_events(tsk, engine, UTRACE_EVENT(SYSCALL_ENTRY)|UTRACE_EVENT(DEATH));"; + s.op->indent(-1); + s.op->newline() << "else if (p->flags == UDPF_SYSCALL_RETURN)"; + s.op->indent(1); + s.op->newline() << "rc = utrace_set_events(tsk, engine, UTRACE_EVENT(SYSCALL_EXIT)|UTRACE_EVENT(DEATH));"; + s.op->indent(-1); + s.op->newline() << "if (rc != 0)"; + s.op->indent(1); + s.op->newline() << "_stp_error(\"utrace_set_events returned error %d on pid %d\", rc, (int)tsk->pid);"; + s.op->indent(-1); + + s.op->newline() << "return UTRACE_RESUME;"; + s.op->newline(-1) << "}"; + + s.op->newline() << "#ifdef UTRACE_ORIG_VERSION"; s.op->newline() << "static u32 stap_utrace_probe_syscall(struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs) {"; + s.op->newline() << "#else"; + s.op->newline() << "static u32 stap_utrace_probe_syscall(enum utrace_resume_action action, struct utrace_attached_engine *engine, struct task_struct *tsk, struct pt_regs *regs) {"; + s.op->newline() << "#endif"; + s.op->indent(1); s.op->newline() << "struct stap_utrace_probe *p = (struct stap_utrace_probe *)engine->data;"; @@ -6250,13 +6287,13 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "(*p->ph) (c);"; common_probe_entryfn_epilogue (s.op); - s.op->newline() << "return UTRACE_ACTION_RESUME;"; + s.op->newline() << "return UTRACE_RESUME;"; s.op->newline(-1) << "}"; } // Output task_finder callback routine that gets called for all // utrace probe types. - s.op->newline() << "static int _stp_utrace_probe_cb(struct task_struct *tsk, int register_p, int process_p, struct stap_task_finder_target *tgt) {"; + s.op->newline() << "static int _stp_utrace_probe_cb(struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {"; s.op->indent(1); s.op->newline() << "int rc = 0;"; s.op->newline() << "struct stap_utrace_probe *p = container_of(tgt, struct stap_utrace_probe, tgt);"; @@ -9143,21 +9180,24 @@ register_standard_tapsets(systemtap_session & s) ->bind(new utrace_builder ()); s.pattern_root->bind(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_END) ->bind(new utrace_builder ()); - - // itrace user-space probes - s.pattern_root->bind_str(TOK_PROCESS)->bind("itrace") - ->bind(new itrace_builder ()); - s.pattern_root->bind_num(TOK_PROCESS)->bind("itrace") - ->bind(new itrace_builder ()); - s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_SYSCALL) ->bind(new utrace_builder ()); s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_SYSCALL) ->bind(new utrace_builder ()); + s.pattern_root->bind(TOK_PROCESS)->bind(TOK_SYSCALL) + ->bind(new utrace_builder ()); s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_SYSCALL)->bind(TOK_RETURN) ->bind(new utrace_builder ()); s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_SYSCALL)->bind(TOK_RETURN) ->bind(new utrace_builder ()); + s.pattern_root->bind(TOK_PROCESS)->bind(TOK_SYSCALL)->bind(TOK_RETURN) + ->bind(new utrace_builder ()); + + // itrace user-space probes + s.pattern_root->bind_str(TOK_PROCESS)->bind("itrace") + ->bind(new itrace_builder ()); + s.pattern_root->bind_num(TOK_PROCESS)->bind("itrace") + ->bind(new itrace_builder ()); // marker-based parts s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_MARK) |