diff options
-rw-r--r-- | ChangeLog | 84 | ||||
-rw-r--r-- | NEWS | 37 | ||||
-rw-r--r-- | elaborate.cxx | 33 | ||||
-rw-r--r-- | hash.cxx | 16 | ||||
-rw-r--r-- | main.cxx | 25 | ||||
-rw-r--r-- | runtime/ChangeLog | 5 | ||||
-rw-r--r-- | runtime/loc2c-runtime.h | 7 | ||||
-rw-r--r-- | runtime/task_finder.c | 14 | ||||
-rw-r--r-- | runtime/task_finder_vma.c | 12 | ||||
-rw-r--r-- | tapset/ChangeLog | 261 | ||||
-rw-r--r-- | tapset/utrace.stp | 11 | ||||
-rw-r--r-- | tapsets.cxx | 1137 | ||||
-rw-r--r-- | translate.cxx | 11 | ||||
-rw-r--r-- | util.cxx | 81 | ||||
-rw-r--r-- | util.h | 2 |
15 files changed, 1061 insertions, 675 deletions
@@ -1,3 +1,48 @@ +2008-09-02 Frank Ch. Eigler <fche@elastic.org> + + * tapsets.cxx (uprobe..emit_module_init): Leave dying-uprobe + loop as early as possible. + +2008-09-02 Stan Cox <scox@redhat.com> + + * elaborate.cxx (add_global_var_display): Simplify token use. + +2008-09-02 Frank Ch. Eigler <fche@elastic.org> + + PR4225. + * tapsets.cxx (generated stap_uprobe_change): Fix major thinko that + falsely triggered a slew of uprobe_unregister's for each plain + register. + (uprobe_derived_probe_group::emit_module_init): Add code to generate + printk's for uprobe activities, if -DDEBUG_UPROBES. + +2008-09-02 Frank Ch. Eigler <fche@elastic.org> + + PR4225 merge. + * tapsets.cxx: Add prototype user-space probing support. Collateral + damage breaks symbol-table-only (dwarfless) probing, PR6864. + (setup_user, setup_kernel): Split & reorganized. + (class uprobe_derived_probe): Nearly all new code. + (base_query): Add "has_module" and "has_process" flags. + (dwarf_builder): Add "user_dw" map. + (dwarf_query::add_probe_point): Use ".dynamic" pseudo-reloc-base for + ET_DYN modules; ".absolute" for ET_EXEC. + (register_patterns): Register process(...) uprobe-based probes. + (task_finder_derived_probe_group::create_session_group): Let runtime + code assert CONFIG_UTRACE. + (base_query ctor, dwarf_builder::build, itrace_builder, + utrace_derived_probe): Use find_executable() to resolve FOO path in + process("FOO"). + (utrace_derived_probe ctor): Reverse-engineer probe point. + * main.cxx (main): Don't override $PATH etc. + * util.cxx (find_executable): Reorganize, simplify, canonicalize. + * util.h, hash.cxx: Corresponding changes. + +2008-09-02 Wenji Huang <wenji.huang@oracle.com> + + * tapsets.cxx (query_func_info): Disable prologue searching in + no-dwarf testing. + 2008-09-01 Stan Cox <scox@redhat.com> * elaborate.cxx (add_global_var_display): Also handle statistics. @@ -54,7 +99,7 @@ 2008-08-25 Frank Ch. Eigler <fche@elastic.org> * Makefile.am (AUTOMAKE_OPTIONS): Add "no-dist". Remove all - "make dist"-oriented targets and macros. + "make dist"-oriented targets and macros. (rpm): Rewrite. Allow parametrization with RPMBUILDFLAGS=. * Makefile.in: Regenerated. * systemtap.spec: Make configuration macros at top overridable. @@ -109,7 +154,7 @@ 2008-08-20 Dave Brolley <brolley@redhat.com> * stap-client: Ignore SIGHUP and SIGPIPE. - (initialization): Set b_specified.: + (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. @@ -250,6 +295,13 @@ 2008-08-11 Frank Ch. Eigler <fche@elastic.org> + * tapsets.cxx (translate_final_fetch_or_store): Reject some + unhandleable types such as floats. + (dwarf_var...visit_target_symbol): Tweak logic of $$var expansion + to quietly skip over any $context variables that cause exceptions. + +2008-08-11 Frank Ch. Eigler <fche@elastic.org> + * tapsets.cxx (dwarf_var_expanding...visit_target_symbol): Don't add a \n at the end of $$vars/$$parms/$$locals. @@ -406,7 +458,7 @@ 2008-08-04 Stan Cox <scox@redhat.com> - * tapsets.cxx (dwarf_var_expanding_copy_visitor::visit_target_symbol): + * tapsets.cxx (dwarf_var_expanding_copy_visitor::visit_target_symbol): Add support for $$vars, $$parms, and $$locals. * stapprobes.5.in: Likewise. * doc/langref.tex: Likewise. @@ -585,12 +637,12 @@ which may be unrelocatable. 2008-07-11 Frank Ch. Eigler <fche@elastic.org> - + * hash.cxx (find_hash): Mix in -d MODULE names. 2008-07-10 Frank Ch. Eigler <fche@elastic.org> - * main.cxx (main): If "-k" (save temp directory) was supplied, + * main.cxx (main): If "-k" (save temp directory) was supplied, disable caching. 2008-07-10 Frank Ch. Eigler <fche@elastic.org> @@ -877,14 +929,14 @@ * tapsets.cxx (dwarf_assert, dwfl_assert): Move to dwarf_wrappers.h. (iterate_over_srcfile_lines, has_single_line_record, - query_srcfile_line): Use dwarf_line_t wrapper. + query_srcfile_line): Use dwarf_line_t wrapper. (die_has_pc): Take a reference to a Dwarf_Die instead of a pointer. Clean up use of dwfl_assert. (query_cu): Check that statement raw address matches the beginning of a statement record. * elaborate.h: Include iosfwd instead of iostream. (literal_map_t, resolve_prologue_endings,): New typedef. - + 2008-06-10 Jim Keniston <jkenisto@us.ibm.com> @@ -918,7 +970,7 @@ (lexer::scan): Ditto. Interpret "$#" as the argc value in all cases. * parse.h: Corresponding decl changes. - + 2008-06-10 Frank Ch. Eigler <fche@elastic.org> PR 6470 @@ -951,8 +1003,8 @@ 2008-06-06 Stan Cox <scox@redhat.com> - * tapsets.cxx (dwflpp::iterate_over_srcfile_lines): - Add parameter line_type_relative. + * tapsets.cxx (dwflpp::iterate_over_srcfile_lines): + Add parameter line_type_relative. (enum line_t): New. (dwarf_query::line_type): New. (dwarf_query::parse_function_spec): Set line_type. @@ -1084,7 +1136,7 @@ * tapsets.cxx: Convert .funcname to funcname when adding it to our symbol table. Accept all weak symbols except those that map to sys_ni_syscall. - + 2008-05-23 Srinivasa DS <srinivasa@in.ibm.com> PR 6429: Inerim fix to avoid compilation error of systemtap module * runtime/transport/symbols.c: added definitions of struct @@ -1116,7 +1168,7 @@ * elaborate.cxx (dead_assignment_remover::visit_binary_expression): New. (dead_assignment_remover::visit_assignment): Allow rhs simplification. - + 2008-05-20 Tim Moore <timoore@redhat.com> * configure.ac: Check for tr1/unordered_map header. @@ -1179,7 +1231,7 @@ x86_64 remains in regs.c. * tapset/{i686,x86_64}/registers.stp: Moved register-lookup code from runtime/regs.c to here. - + 2008-05-12 Jim Keniston <jkenisto@us.ibm.com> (2008-05-06 in dwarfless branch) @@ -1629,7 +1681,7 @@ 2008-02-25 Frank Ch. Eigler <fche@elastic.org> PR5792. - * parse.cxx (eval_pp_conditional): Support wildcards in + * parse.cxx (eval_pp_conditional): Support wildcards in %( kernel_v/kernel_vr/arch ==/!= "*foo?" %) operands. * NEWS, stap.1.in: Document this. @@ -1661,7 +1713,7 @@ (register_standard_tapsets): Added new 'format' marker probe optional parameter. * stapprobes.5.in (parts): Documented new "format" probe - component. + component. 2008-02-19 Roland McGrath <roland@redhat.com> @@ -1930,7 +1982,7 @@ * configure: Regenerated. 2008-01-17 Srinivasa DS <srinivasa@in.ibm.com> - + PR 5483 * tapsets.cxx : Possible fix for making systemtap compatible with the elfutils-0.131 @@ -1,5 +1,30 @@ * What's new +- All user-space-related probes support $PATH-resolved executable + names, so + + probe process("ls").syscall {} + probe process("./a.out").syscall {} + + work now, instead of just + + probe process("/bin/ls").syscall {} + probe process("/my/directory/a.out").syscall {} + +- Prototype symbolic user-space probing support: + + # stap -e 'probe process("ls").function("*").call { + log (probefunc()." ".$$parms) + }' \ + -c 'ls -l' + + This requires: + - debugging information for the named program + - a version of utrace in the kernel that is compatible with the "uprobes" + kernel module prototype. This includes RHEL5 and older Fedora, but not + yet current lkml-track utrace; a "pass 4a"-time build failure means + your system cannot use this yet. + - Prototype systemtap client and compile server are now available. These allow you to compile a systemtap module on a host other than the one which it will be run, providing the client and server @@ -10,10 +35,10 @@ was specified. See stap-server(8) for more details. - Global variables which are written to but never read are now - automatically displayed when the session does a shutdown. For example: + automatically displayed when the session does a shutdown. For example: - global running_tasks - probe timer.profile {running_tasks[pid(),tid()] = execname()} + global running_tasks + probe timer.profile {running_tasks[pid(),tid()] = execname()} probe timer.ms(8000) {exit()} - A formatted string representation of the variables, parameters, or local @@ -109,7 +134,7 @@ printf ("%*d", four, two) // prints <space><space><space>2 - Preprocessor conditional expressions can now include wildcard style - matches on kernel versions. + matches on kernel versions. %( kernel_vr != "*xen" %? foo %: bar %) - Prototype support for user-space probing is showing some progress. @@ -303,12 +328,12 @@ handlers are invoked, as a safety improvement. - Add an optional numeric parameter for begin/end probe specifications, - to order their execution. + to order their execution. probe begin(10) { } /* comes after */ probe begin(-10) {} - Add an optional array size declaration, which is handy for very small or very large ones. - global little[5], big[20000] + global little[5], big[20000] - Include some example scripts along with the documentation. diff --git a/elaborate.cxx b/elaborate.cxx index cfbe1392..44b6e24f 100644 --- a/elaborate.cxx +++ b/elaborate.cxx @@ -1172,10 +1172,7 @@ void add_global_var_display (systemtap_session& s) block *b = new block; pl->components.push_back (c); - token* p_tok = new token; - p_tok->type = tok_identifier; - p_tok->content = "probe"; - p->tok = p_tok; + p->tok = l->tok; p->locations.push_back (pl); print_tok->type = tok_identifier; print_tok->content = "printf"; @@ -1211,13 +1208,9 @@ void add_global_var_display (systemtap_session& s) else // Array { int idx_count = l->index_types.size(); - token* idx_tok[idx_count]; symbol* idx_sym[idx_count]; vardecl* idx_v[idx_count]; // Create a foreach loop - token* fe_tok = new token; - fe_tok->type = tok_identifier; - fe_tok->content = "foreach"; foreach_loop* fe = new foreach_loop; fe->sort_direction = 0; fe->limit = NULL; @@ -1225,19 +1218,16 @@ void add_global_var_display (systemtap_session& s) // Create indices for the foreach loop for (int i=0; i < idx_count; i++) { - idx_tok[i] = new token; - idx_tok[i]->type = tok_identifier; char *idx_name; if (asprintf (&idx_name, "idx%d", i) < 0) return; - idx_tok[i]->content = idx_name; idx_sym[i] = new symbol; - idx_sym[i]->tok = idx_tok[i]; idx_sym[i]->name = idx_name; + idx_sym[i]->tok = l->tok; idx_v[i] = new vardecl; idx_v[i]->name = idx_name; - idx_v[i]->tok = idx_tok[i]; idx_v[i]->type = l->index_types[i]; + idx_v[i]->tok = l->tok; idx_sym[i]->referent = idx_v[i]; fe->indexes.push_back (idx_sym[i]); } @@ -1274,25 +1264,22 @@ void add_global_var_display (systemtap_session& s) if (l->type == pe_stats) { struct stat_op* so [5]; - token* so_tok [5]; - - for (unsigned sti = 0; sti < (sizeof(so_tok)/sizeof(token*)); sti++) - { - so_tok[sti] = new token; - so_tok[sti]->type = tok_identifier; - } const stat_component_type stypes[] = {sc_count, sc_min, sc_max, sc_sum, sc_average}; - for (unsigned si = 0; si < (sizeof(so_tok)/sizeof(token*)); si++) + for (unsigned si = 0; + si < (sizeof(so)/sizeof(struct stat_op*)); + si++) { so[si]= new stat_op; so[si]->ctype = stypes[si]; so[si]->type = pe_long; so[si]->stat = ai; - so[si]->tok = so_tok[si]; + so[si]->tok = l->tok; } ai->type = pe_stats; - for (unsigned si = 0; si < (sizeof(so_tok)/sizeof(token*)); si++) + for (unsigned si = 0; + si < (sizeof(so)/sizeof(struct stat_op*)); + si++) pf->args.push_back(so[si]); } else @@ -126,16 +126,14 @@ find_hash (systemtap_session& s, const string& script) h.add(s.runtime_path); // Hash compiler path, size, and mtime. We're just going to assume - // we'll be using gcc, which should be correct most of the time. - string gcc_path; - if (find_executable("gcc", gcc_path)) + // we'll be using gcc. XXX: getting kbuild to spit out out would be + // better. + string gcc_path = find_executable ("gcc"); + if (stat(gcc_path.c_str(), &st) == 0) { - if (stat(gcc_path.c_str(), &st) == 0) - { - h.add(gcc_path); - h.add(st.st_size); - h.add(st.st_mtime); - } + h.add(gcc_path); + h.add(st.st_size); + h.add(st.st_mtime); } // Hash the systemtap size and mtime. We could use VERSION/DATE, @@ -709,25 +709,11 @@ main (int argc, char * const argv []) int rc = 0; - // override PATH and LC_ALL - const char *path = "/bin:/sbin:/usr/bin:/usr/sbin"; - rc = setenv("PATH", path, 1) || setenv("LC_ALL", "C", 1); - if (rc) - { - const char* e = strerror (errno); - cerr << "setenv (\"PATH=" << path << "\" + \"LC_ALL=C\"): " - << e << endl; - } - - // Get rid of a few standard environment variables (which might - // cause us to do unintended things). - rc = unsetenv("IFS") || unsetenv("CDPATH") || unsetenv("ENV") - || unsetenv("BASH_ENV"); - if (rc) - { - const char* e = strerror (errno); - cerr << "unsetenv failed: " << e << endl; - } + // For PR1477, we used to override $PATH and $LC_ALL and other stuff + // here. We seem to use complete pathnames in + // buildrun.cxx/tapsets.cxx now, so this is not necessary. Further, + // it interferes with util.cxx:find_executable(), used for $PATH + // resolution. s.kernel_base_release.assign(s.kernel_release, 0, s.kernel_release.find('-')); @@ -1006,6 +992,7 @@ main (int argc, char * const argv []) if (rc || s.last_pass == 3 || pending_interrupts) goto cleanup; // PASS 4: COMPILATION + times (& tms_before); gettimeofday (&tv_before, NULL); rc = compile_pass (s); diff --git a/runtime/ChangeLog b/runtime/ChangeLog index b7e44a0f..e02c5f0b 100644 --- a/runtime/ChangeLog +++ b/runtime/ChangeLog @@ -1,3 +1,8 @@ +2008-09-01 Frank Ch. Eigler <fche@elastic.org> + + * task_finder.c: Move CONFIG_UTRACE assertion here. + * task_finder_vma.c (__stp_tf_add_vma): Make printk conditional. + 2008-08-29 David Smith <dsmith@redhat.com> * task_finder.c (__stp_utrace_attach_match_filename): Don't call diff --git a/runtime/loc2c-runtime.h b/runtime/loc2c-runtime.h index 215676ee..1247da51 100644 --- a/runtime/loc2c-runtime.h +++ b/runtime/loc2c-runtime.h @@ -113,16 +113,13 @@ kernel mode, it is not saved in the trap frame (struct pt_regs). The `esp' (and `xss') fields are valid only for a user-mode trap. For a kernel mode trap, the interrupted state's esp is actually an - address inside where the `struct pt_regs' on the kernel trap stack points. - - For now we assume all traps are from kprobes in kernel-mode code. - For extra paranoia, could do BUG_ON((regs->xcs & 3) == 3). */ + address inside where the `struct pt_regs' on the kernel trap stack points. */ #define dwarf_register_0(regs) regs->eax #define dwarf_register_1(regs) regs->ecx #define dwarf_register_2(regs) regs->edx #define dwarf_register_3(regs) regs->ebx -#define dwarf_register_4(regs) ((long) ®s->esp) +#define dwarf_register_4(regs) (user_mode(regs) ? regs->esp : (long)®s->esp) #define dwarf_register_5(regs) regs->ebp #define dwarf_register_6(regs) regs->esi #define dwarf_register_7(regs) regs->edi diff --git a/runtime/task_finder.c b/runtime/task_finder.c index f1c2c41b..9c0dd55b 100644 --- a/runtime/task_finder.c +++ b/runtime/task_finder.c @@ -1,5 +1,15 @@ +#ifndef TASK_FINDER_C +#define TASK_FINDER_C + +#if ! defined(CONFIG_UTRACE) +#error "Need CONFIG_UTRACE!" +#endif + +#include <linux/utrace.h> #include <linux/list.h> #include <linux/binfmts.h> +#include <linux/mount.h> + #include "syscall.h" #include "utrace_compatibility.h" #include "task_finder_vma.c" @@ -1110,6 +1120,7 @@ stap_start_task_finder(void) struct task_struct *grp, *tsk; char *mmpath_buf; + debug_task_finder_report(); mmpath_buf = _stp_kmalloc(PATH_MAX); if (mmpath_buf == NULL) { _stp_error("Unable to allocate space for path"); @@ -1217,3 +1228,6 @@ stap_stop_task_finder(void) debug_task_finder_report(); atomic_set(&__stp_task_finder_state, __STP_TF_STOPPED); } + + +#endif /* TASK_FINDER_C */ diff --git a/runtime/task_finder_vma.c b/runtime/task_finder_vma.c index 9d43e36c..8c60175e 100644 --- a/runtime/task_finder_vma.c +++ b/runtime/task_finder_vma.c @@ -133,11 +133,13 @@ __stp_tf_add_vma(struct task_struct *tsk, unsigned long 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 */ +#ifdef DEBUG_TASK_FINDER_VMA + printk(KERN_NOTICE + "vma (pid: %d, vm_start: 0x%lx) present?\n", + tsk->pid, vma->vm_start); +#endif + mutex_unlock(&__stp_tf_vma_mutex); + return -EBUSY; /* Already there */ } } diff --git a/tapset/ChangeLog b/tapset/ChangeLog index 61976ee0..39b6b93b 100644 --- a/tapset/ChangeLog +++ b/tapset/ChangeLog @@ -1,3 +1,8 @@ +2008-09-01 Frank Ch. Eigler <fche@elastic.org> + + PR4225 merge. + * utrace.stp: New file, for use by utrace $var expansions. + 2008-09-01 Zhaolei <zhaolei@cn.fujitsu.com> * nfs_proc.stp: Fix memory access error in nfs.proc.read_setup, nfs.proc.write_setup and nfs.proc.commit_setup. @@ -54,7 +59,7 @@ 2008-06-20 wcohen <wcohen@redhat.com> * dev.stp: New. - + 2008-06-19 Zhaolei <zhaolei@cn.fujitsu.com> * aux_syscalls.stp: Output unknown bits in _stp_lookup_or_str. @@ -175,9 +180,9 @@ PR 5231 * ioblock.stp (ioblock.end): Set bytes_done depending on kernel version. - + 2008-04-29 Frank Ch. Eigler <fche@elastic.org> - + PR 6466 * tcp.stp (tcp_sockstate_str, tcp_sockopt_str): Initialize number->string lookup tables here, instead of "probe begin(-1)" @@ -214,7 +219,7 @@ register unification 2008-02-28 Will Cohen <wcohen@redhat.com> - + PR433780 * errno.stp (returnstr): Handle unified i386/x86_64 reg names. @@ -224,7 +229,7 @@ on xen kernel. 2008-01-25 Will Cohen <wcohen@redhat.com> - + PR5554 * syscalls.stp (__is_user_regs): Modify to work with older kernels. @@ -282,7 +287,7 @@ (kexec_load): Ditto. (mbind): Ditto. (move_pages): Ditto. - + 2007-12-17 Zhaolei <zhaolei@cn.fujitsu.com> From Yang Zhiguo <yzgcsu@cn.fujitsu.com> @@ -324,13 +329,13 @@ 2007-11-12 Martin Hunt <hunt@redhat.com> - * logging.stp (print_char): Remove. Now implemented by + * logging.stp (print_char): Remove. Now implemented by stap. (log): Add a comment that it is deprecated. - + 2007-11-12 Martin Hunt <hunt@redhat.com> - - * syscalls2.stp (sys_remap_file_pages: : Change kernel + + * syscalls2.stp (sys_remap_file_pages: : Change kernel version check to >= 2.6.24. 2007-11-8 Zhaolei <zhaolei@cn.fujitsu.com> @@ -437,14 +442,14 @@ sa_restorer field for IA64. 2007-10-09 Martin Hunt <hunt@redhat.com> - + * syscalls2.stp (sys_signal): Call _sighandler_str(). * aux_syscalls.stp (_stp_sigset_str): Don't include SIG_0. (_sighandler_str): New. - + 2007-10-09 Zhaolei <zhaolei@cn.fujitsu.com> * queue_stats.stp (qsq_start): Add destruction for qs_utime. @@ -462,16 +467,16 @@ * syscalls2.stp (rt_sigaction, sigaction): Call _struct_sigaction_u(). - - * aux_syscalls.stp (_struct_timeval): Removed. No longer - necessary now that we have structure access in scripts. + + * aux_syscalls.stp (_struct_timeval): Removed. No longer + necessary now that we have structure access in scripts. (_struct_timespec): Ditto. (_struct_itimerval): Ditto. (_struct_timezone_u): Remove random CATCH_DEREF_FAULT() line. (_stp_sigset_str): New. (_struct_sigaction_u): New. - + 2007-10-04 Frank Ch. Eigler <fche@elastic.org> * inet.stp: New tapset for htonl and friends. @@ -493,7 +498,7 @@ 2007-10-01 Martin Hunt <hunt@redhat.com> - * aux_syscalls.stp (_str_lookup_str): Print unmatched + * aux_syscalls.stp (_str_lookup_str): Print unmatched values in hex. 2007-10-01 Zhaolei <zhaolei@cn.fujitsu.com> @@ -513,7 +518,7 @@ (_shmat_flags_str): Ditto. (_at_flag_str): Ditto. (get_mmap_args): Complete rewrite for safety and correctness. - + 2007-09-27 Wenji Huang <wenji.huang@oracle.com> * rpc.stp (clones_from_clnt, tasks_from_clnt): Fix for kernel >= 2.6.22. @@ -554,7 +559,7 @@ Added __page_index() to work around problem dereferencing unions in stap language. - + Made generic.fop.sendfile conditional on kernel <= 2.6.22. generic_file_sendfile() was removed in 2.6.23. @@ -619,7 +624,7 @@ 2007-09-24 Frank Ch. Eigler <fche@elastic.org> - * aux_syscalls.stp: Add missing CATCH_DEREF_FAULT()s. + * aux_syscalls.stp: Add missing CATCH_DEREF_FAULT()s. 2007-09-24 Frank Ch. Eigler <fche@elastic.org> @@ -772,21 +777,21 @@ * syscalls2.stp (sys_tee.return): Make optional 2007-08-23 Martin Hunt <hunt@redhat.com> - + * aux_syscalls.stp (_struct_compat_timeval_u): Fix array reference. (_struct_compat_timespec_u): Cast args to long. 2007-08-23 Martin Hunt <hunt@redhat.com> - + From Cai Fei <caifei@cn.fujitsu.com> * aux_syscalls.stp (_waitid_opt_str): New. (_wait4_opt_str): Just handle wait4 options. * syscalls2.stp (sys_waitid): Use _waitid_opt_str. - + 2007-08-23 Wenji Huang <wenji.huang@oracle.com> - * rpc.stp (sunrpc.svc.process, sunrpc.svc.recv): Modify evaluating + * rpc.stp (sunrpc.svc.process, sunrpc.svc.recv): Modify evaluating sv_name,sv_prog,sv_nrthreads in kernel>=2.6.19. (sunrpc.sched.delay,sunrpc.sched.delay): Fix typo. (sunrpc.*.return): Change name by adding "return". @@ -809,49 +814,49 @@ * syscalls.stp (compat_sys_epoll_ctl): New. (compat_sys_epoll_wait): New. (sys_epoll_pwait): New. - (compat_sys_epoll_pwait): New. + (compat_sys_epoll_pwait): New. (compat_sys_keyctl): New. (compat_sys_mq_open): New. - (compat_sys_futex): New. + (compat_sys_futex): New. (compat_sys_mq_timedsend): New. (compat_sys_mq_timedreceive): New. (compat_sys_mq_notify): New. (compat_sys_mq_getsetattr): New. (init_module): Make optional and quote args. (sys_eventfd): New. - + * syscalls2.stp (sys_splice): New. (sys_vmsplice): New. - (compat_sys_vmsplice): New. + (compat_sys_vmsplice): New. (sys_tee): New. - (sys_signalfd): New. - (compat_sys_signalfd): New. + (sys_signalfd): New. + (compat_sys_signalfd): New. (sys_timerfd): New. (compat_sys_timerfd): New. (old32_readdir): New. - + 2007-08-22 Martin Hunt <hunt@redhat.com> - + * aux_syscalls.stp (_utimensat_flag_str): New. (_dfd_str): New. (_struct_timeval2_u): Deleted. (_struct_timeval_u): Take a number of structs to decode. - (_struct_compat_timeval2_u): Deleted - (_struct_compat_timeval_u): Take a number of structs to decode. + (_struct_compat_timeval2_u): Deleted + (_struct_compat_timeval_u): Take a number of structs to decode. (_struct_timespec_u): Take a number of structs to decode. Recognize UTIME_NOW and UTIME_OMIT. (_struct_compat_timespec_u): Ditto. - + * syscalls.stp (compat_sys_old_getrlimit): Removed. (sys_migrate_pages): New. (sys_move_pages): New. * syscalls2.stp (compat_sys_sigprocmask): Removed. Calls sys_sigprocmask. - (compat_sys_sysinfo): New. + (compat_sys_sysinfo): New. (compat_sys_rt_sigtimedwait): New. (sys_utimensat, compat_sys_utimensat): New. - + * ppc64/syscalls.stp (compat_sys_sigpending): Removed. Calls sys_ func. (compat_sys_setrlimit): Ditto. @@ -860,11 +865,11 @@ (compat_sys_getrusage): Ditto. (compat_sys_wait4): Ditto. (compat_sys_sched_setaffinity): Ditto. - (compat_sys_sched_getaffinity): Ditto. - + (compat_sys_sched_getaffinity): Ditto. + 2007-08-22 Wenji Huang <wenji.huang@oracle.com> - * nfsd.stp (nfsd.proc?.*): Modify evaluating client_ip + * nfsd.stp (nfsd.proc?.*): Modify evaluating client_ip with function addr_from_rqst to make it compatible in kernel>=2.6.19 (nfsd.proc4.compound,nfsd.read,nfsd.write): Fix typo. (nfsd.unlink): Fix reference to variable iap. @@ -907,7 +912,7 @@ * nfs.stp (nfs.fop.aio_read, nfs.fop.aio_write): Modify evaluating count. (__iov_length): New function. (nfs.fop.sendfile, nfs.aop.set_page_dirty, nfs.aop.prepare_write, - nfs.aop.release_page): Fix typo. + nfs.aop.release_page): Fix typo. 2007-08-17 Martin Hunt <hunt@redhat.com> From Lai Jiangshan <laijs@cn.fujitsu.com> @@ -932,10 +937,10 @@ From zhaolei@cn.fujitsu.com Remove PF_UNIX. PF_LOCAL is correct. - -2007-08-16 Martin Hunt <hunt@redhat.com> - * aux_syscalls.stp (__get_argv): Add parameter to +2007-08-16 Martin Hunt <hunt@redhat.com> + + * aux_syscalls.stp (__get_argv): Add parameter to optionally skip the first argv (argv[0]). (__get_compat_argv): Ditto. @@ -943,8 +948,8 @@ For argstr use filename plus argv starting at [1]. (compat_execve): Ditto. * ppc64/syscalls.stp (sys32_exevve): Ditto. - - From Cai Fei <caifei@cn.fujitsu.com> + + From Cai Fei <caifei@cn.fujitsu.com> * syscalls.stp (sts_getpgid): Add pid arg. 2007-08-16 Wenji Huang <wenji.huang@oracle.com> @@ -965,7 +970,7 @@ * aux_syscalls.stp (get_mmap_args): Moved here. * syscalls.stp: Add sys32_alarm. * syscalls2.stp: Add sys32_uname. - + 2007-08-15 Frank Ch. Eigler <fche@elastic.org> * context.stp (cpuid, cpu): Use smp_processor_id(). @@ -1001,7 +1006,7 @@ * tapset/syscalls.stp (syscall.compat_execve, syscall.compat_execve.return): - * tapset/syscalls2.stp (syscall.compat_sys_semtimedop, + * tapset/syscalls2.stp (syscall.compat_sys_semtimedop, syscall.compat_sys_semtimedop.return): * tapset/i686/syscalls.stp (syscall.set_zone_reclaim, syscall.set_zone_reclaim.return): Make optional. @@ -1018,13 +1023,13 @@ 2007-07-31 Martin Hunt <hunt@redhat.com> * syscalls*: Move sys32_sysctl to arch dirs. - + 2007-07-31 Martin Hunt <hunt@redhat.com> * syscalls.stp (clock_nanosleep): Fix flags string. * syscalls2.stp: Fix typo. -2007-07-31 Martin Hunt <hunt@redhat.com> +2007-07-31 Martin Hunt <hunt@redhat.com> * */syscalls.stp: Added mmap functions. Continued moving common compatibility functions to @@ -1056,9 +1061,9 @@ (semctl): Make optional. (compat_sys_semctl): New. (semget): Make optional. - (semop): Make optional. - (semtimedop): Make optional. - (compat_sys_semtimedop): New. + (semop): Make optional. + (semtimedop): Make optional. + (compat_sys_semtimedop): New. (send): Make optional. (sendmsg): Make optional. (compat_sys_sendmsg): New. @@ -1078,7 +1083,7 @@ (sysctl): Add probe on sys32. 2007-07-31 Frank Ch. Eigler <fche@elastic.org> - + PR 4793 * ppc64/syscalls.stp: Remove duplicate sys_request_key{,.return} aliases. @@ -1109,7 +1114,7 @@ 2007-07-16 Martin Hunt <hunt@redhat.com> (compat_sys_io_submit): Fix typo. -2007-07-16 Martin Hunt <hunt@redhat.com> +2007-07-16 Martin Hunt <hunt@redhat.com> * syscalls.stp (compat_sys_fcntl64, compat_sys_fcntl): Add. (compat_sys_fstatfs64): Add. (compat_sys_getdents, compat_sys_getdents64): Add. @@ -1131,9 +1136,9 @@ (sys_select): Don't try to print fd_sets. (compat_sys_select): Add. (compat_sys_statfs64): Add. - + * ppc/syscalls.stp: Remove duplicated probes. - + * aux_syscalls.stp (_nfsctl_cmd_str): New. (_fd_set_u): Removed. Not used. @@ -1145,10 +1150,10 @@ 2007-07-10 Martin Hunt <hunt@redhat.com> - * aux_syscalls.stp (_struct_compat_timeval2_u): + * aux_syscalls.stp (_struct_compat_timeval2_u): Fixes. -2007-07-10 Martin Hunt <hunt@redhat.com> +2007-07-10 Martin Hunt <hunt@redhat.com> * syscall*.stp: Major reorganization work. * aux_syscalls.stp (_sigprocmask_how_str): Rewrite in C so real arch-dependent header files @@ -1158,7 +1163,7 @@ 2007-07-09 Martin Hunt <hunt@redhat.com> * syscalls2.stp (compat_sys_settimeofday): New. -2007-07-09 Martin Hunt <hunt@redhat.com> +2007-07-09 Martin Hunt <hunt@redhat.com> * syscalls2.stp (compat_sys_utimes): New. * ppc64/syscalls.stp (compat_sys_utimes): Removed. @@ -1185,21 +1190,21 @@ 2007-06-15 Martin Hunt <hunt@redhat.com> - * syscalls.stp: Add sys_fcntl64, sys_oabi_fstat64, + * syscalls.stp: Add sys_fcntl64, sys_oabi_fstat64, sys_oabi_lstat64. * syscalls2.stp: Fix sys_send. Add sys_sendfile64. * errno.stp: Add arm support. 2007-06-14 Mike Mason <mmlnx@us.ibm.com> - * socket.stp: removed extra assignment to "size" in + * socket.stp: removed extra assignment to "size" in socket.aio_write.return 2007-06-07 Martin Hunt <hunt@redhat.com> Fallout from PR 3331 fix. Sometimes glibc and the kernel disagree about signed vs unsigned. * aux_syscalls.stp: Rename __uid() to __int32(). - * syscalls.stp: Change syscall.chown, syscall.fchown, + * syscalls.stp: Change syscall.chown, syscall.fchown, syscall.lchown, and syscall.lseek to use __int32(). * syscalls2.stp: Change syscall.setregid, syscall.resgid, syscall.resuid, and syscall.setreuid to use __int32(). @@ -1214,16 +1219,16 @@ * syscalls2.stp: Make optional sys_nice, sys_rt_sigreturn, sys_signal, sys_sigprocmask, sys_utime (syscall.old_getrlimit): Moved into syscall.getrlimit. - + 2007-05-16 Will Cohen <wcohen@redhat.com> PR 4471 * syscall2.stp: Correct sys_pipe function args for ia64. - + 2007-05-16 Will Cohen <wcohen@redhat.com> PR 4510 * scheduler.stp: Correct function argument. - + 2007-05-09 Martin Hunt <hunt@redhat.com> * syscalls2.stp: Added compat_sys_pause to syscall.pause. @@ -1334,7 +1339,7 @@ (backtrace): Just use _stp_stack_snprint(). (print_stack): Use new _stp_print* functions. (probefunc): Use _stp_symbol_snprint. - + 2007-01-26 Josh Stone <joshua.i.stone@intel.com> * vfs.stp (ppos_pos): Change deref() to kread() so i686 passes. @@ -1373,14 +1378,14 @@ 2007-01-12 Mike Mason <mmlnx@us.ibm.com> * x86_64/syscalls.stp: added ? to request_key aliases - to prevent syscall.* failures on some kernels + to prevent syscall.* failures on some kernels 2006-12-29 Li Guanglei <guanglei@cn.ibm.com> From Gui Jian <guij@cn.ibm.com> - * ioscheduler.stp, nfsd.stp, nfs.stp, nfs_proc.stp, + * ioscheduler.stp, nfsd.stp, nfs.stp, nfs_proc.stp, rpc.stp, vfs.stp: patch for 2.6.19 kernel - + 2006-12-18 Frank Ch. Eigler <fche@elastic.org> * vfs.stp (ppos_pos): Protect contents with deref(), though @@ -1395,13 +1400,13 @@ From Gui Jian <guij@cn.ibm.com> * nfs.stp, nfs_proc.stp, rpc.stp: patch for 2.6.9 kernel - + 2006-11-29 Li Guanglei <guanglei@cn.ibm.com> From Gui Jian <guij@cn.ibm.com> - * scheduler.stp, vfs.stp: made idle_balance and + * scheduler.stp, vfs.stp: made idle_balance and buffer_migrate_page optinal. - + 2006-11-20 Frank Ch. Eigler <fche@elastic.org> * context.stp (caller_addr): Declare return type. @@ -1439,7 +1444,7 @@ (stack_used): New. (stack_unused): New. (called_addr): New. - (caller): New. + (caller): New. 2006-10-12 Li Guanglei <guanglei@cn.ibm.com> * ioscheduler.stp: bugfix to avoid refer to NULL pointer @@ -1467,7 +1472,7 @@ Use gettimeofday_ns as the base unit. 2006-09-19 Li Guanglei <guanglei@cn.ibm.com> - + From Gui Jian <guijian@cn.ibm.com> * rpc.stp: Some changes and more comments of RPC tapset @@ -1478,48 +1483,48 @@ * string.stp: Fix docs. 2006-09-12 Li Guanglei <guanglei@cn.ibm.com> - + From Li Xuepeng <xuepengl@cn.ibm.com> - * nfsd.stp, nfs_proc.stp, nfs.stp: + * nfsd.stp, nfs_proc.stp, nfs.stp: Change NFS file identifier. Change NFS version data type - from String to INT. Add version variable to nfs_open + from String to INT. Add version variable to nfs_open and nfs_release. - + 2006-09-11 Li Guanglei <guanglei@cn.ibm.com> - + From Gui Jian(guijian@cn.ibm.com> * rpc.stp: tapsets for RPC activities. - + 2006-08-30 Li Guanglei <guanglei@cn.ibm.com> - * signal.stp: some changes to arguments and comments + * signal.stp: some changes to arguments and comments of signal tapset - + 2006-08-25 Li Guanglei <guanglei@cn.ibm.com> From Li Xuepeng: * vfs.stp,nfsd.stp,nfs.stp: bug fixes and more error checking - + 2006-08-23 Li Guanglei <guanglei@cn.ibm.com> * vfs.stp: New tapset from Thomas Zanussi(trz@us.ibm.com) to probe vfs layer activities. * nfs.stp: New tapset from Li Xuepeng(xuepengl@cn.ibm.com) to probe nfs file operations and nfs address space operations on client side. - * nfs_proc.stp: New tapset from Li Xuepeng to probe some nfs RPC + * nfs_proc.stp: New tapset from Li Xuepeng to probe some nfs RPC procedure stub functions on client side. - * nfsd.stp: New tapset from Li Xuepeng to probe nfs server side - activities, including some RPC procedure stub functions, nfsd + * nfsd.stp: New tapset from Li Xuepeng to probe nfs server side + activities, including some RPC procedure stub functions, nfsd dispatch routine, and nfsd_* functions - + 2006-08-22 Li Guanglei <guanglei@cn.ibm.com> * signal.stp: More variables for signal.do_action and signal.procmask. New function of get_sigset(). Minor changes to naming of signal.pend and its comments - + 2006-08-21 Martin Hunt <hunt@redhat.com> * string.stp (substr): Rewrite. Make the 3rd parameter @@ -1528,30 +1533,30 @@ 2006-08-17 Josh Stone <joshua.i.stone@intel.com> * signal.stp: Changes on behalf of Manoj S Pattabhiraman: - 1. As per the suggestions, i have removed the argstr from the + 1. As per the suggestions, i have removed the argstr from the probe points. - 2. Added some checks to find whether the signals generated are + 2. Added some checks to find whether the signals generated are USER or Kernel Mode in signal_handle probe. 2006-08-17 Li Guanglei <guanglei@cn.ibm.com> * signal.stp: update signal tapsets based on the discussion with Josh Stone on mailinglist: - 1. Added "send2queue" and "name" variable for signal.send.part* - 2. Added signal.send.return probe alias + 1. Added "send2queue" and "name" variable for signal.send.part* + 2. Added signal.send.return probe alias 3. Added signal.checkperm and signal.checkperm.return probe alias 4. Commented out signal.handle_stop 5. Alias all signal syscalls to syscall tapsets. 2006-08-15 Thang Nguyen <thang.p.nguyen@intel.com> - * ioblock.stp: Added safety checks for __bio_ino() and - __bio_start_sect(). + * ioblock.stp: Added safety checks for __bio_ino() and + __bio_start_sect(). 2006-08-09 Thang Nguyen <thang.p.nguyen@intel.com> * ioblock.stp: Merged io.stp from Tom Zanussi (IBM) into existing - ioblock.stp. Removed/Renamed duplicate variables and probes. + ioblock.stp. Removed/Renamed duplicate variables and probes. 2006-08-09 Josh Stone <joshua.i.stone@intel.com> @@ -1582,31 +1587,31 @@ * return.stp: Removed. Use $return instead. 2006-07-17 Tom Zanussi <zanussi@us.ibm.com> - + * ioblock.stp: add null bi_bdev check to bio_devname() 2006-07-17 Li Guanglei <guanglei@cn.ibm.com> - - * syscalls.stp: make sys_acct, sys_add_key, sys_keyctl - and sys_modify_ldt optional on ppc64 since they are + + * syscalls.stp: make sys_acct, sys_add_key, sys_keyctl + and sys_modify_ldt optional on ppc64 since they are weak symbol. * ppc64/syscalls.stp: remove syscall.acct, syscall.add_key - and syscall.umask since they are already defined in + and syscall.umask since they are already defined in syscalls.stp - + 2006-07-04 Martin Hunt <hunt@redhat.com> * syscalls.stp, syscalls2.stp: Use user_string_quoted(). 2006-07-04 Li Guanglei <guanglei@cn.ibm.com> - - * syscalls.stp, syscalls2.stp, ppc64/syscalls.stp: - fix syscall.lstat, syscall.lstat64, syscall.newlstat. - * ppc64/syscalls.stp: fix syscall.compat_sys_statfs and + + * syscalls.stp, syscalls2.stp, ppc64/syscalls.stp: + fix syscall.lstat, syscall.lstat64, syscall.newlstat. + * ppc64/syscalls.stp: fix syscall.compat_sys_statfs and syscall.sys32_open 2006-07-04 Li Guanglei <guanglei@cn.ibm.com> - + * memory.stp: add addr_to_node() from Jose R. Santos (jrs@us.ibm.com) * syscalls.stp: typo for syscall.lstat @@ -1617,17 +1622,17 @@ on faults, just return <unknown>. (user_string2): New function. (user_string_warn): New function. - + 2006-06-26 Martin Hunt <hunt@redhat.com> * aux_syscalls.stp (_stp_ctime): New function. (_struct_utimbuf_u): New function. 2006-06-25 Li Guanglei <guanglei@cn.ibm.com> - - * syscalls.stp: Changes to AIO related syscalls(io_setup, + + * syscalls.stp: Changes to AIO related syscalls(io_setup, io_submit, io_getevents, io_cancel, io_destroy) - + 2006-06-23 Thang Nguyen <thang.p.nguyen@intel.com> * tcp.stp: Refined variables and added more @@ -1657,10 +1662,10 @@ 2006-06-09 Martin Hunt <hunt@redhat.com> - * syscalls2.stp: Fixed the uid and gid calls. + * syscalls2.stp: Fixed the uid and gid calls. 2006-06-09 Li Guanglei <guanglei@cn.ibm.com> - + * syscalls.stp: made sys_ftruncate64 optional since it doesn't exist on 2.6.16*/ppc64 @@ -1689,7 +1694,7 @@ * syscalls2.stp (sys_write): Use text_strn(). * syscalls.stp (sys_lseek): Fix. (sys_llseek): Fix. - + * string.stp (text_str): New. (text_strn): New. @@ -1702,22 +1707,22 @@ (add_key): Comment out. This syscall is added by a xen patch and may not be present. (tux): Ditto. - + * syscalls.stp (accept): Fix arg name. 2006-05-25 Martin Hunt <hunt@redhat.com> * syscalls.stp: Add sys_fcntl and sys_listen. - * syscalls2.stp: Commented out sys_socketcall because it only calls + * syscalls2.stp: Commented out sys_socketcall because it only calls other system calls which are already probed. 2006-05-25 Martin Hunt <hunt@redhat.com> - + * syscall*.stp: Change "0x%p" to "%p". 2006-05-25 Li Guanglei <guanglei@cn.ibm.com> - + * scheduler.stp: add prev_task and next_task variable. 2006-05-24 Josh Stone <joshua.i.stone@intel.com> @@ -1735,7 +1740,7 @@ * tapset/ioscheduler.stp: change . to -> operator to get the elevator name - + 2006-05-18 Li Guanglei <guanglei@cn.ibm.com> * tapset/LKET/*: tracing tapsets of LKET @@ -1747,7 +1752,7 @@ * tapset/tskschedule.stp: deleted, merge into scheduler.stp * tapset/scheduler.stp: incorporate tskschedule.stp - + 2006-05-17 Josh Stone <joshua.i.stone@intel.com> * process.stp: Rename signal.send to signal_send and process.complete @@ -1796,7 +1801,7 @@ 2006-05-14 Martin Hunt <hunt@redhat.com> * aux_syscalls.stp (_sys_open_flag_str): Rewrite in C so it - is complete and works on different archs. + is complete and works on different archs. 2006-05-09 Josh Stone <joshua.i.stone@intel.com> @@ -1843,18 +1848,18 @@ 2006-03-06 Martin Hunt <hunt@redhat.com> - * system.stp: New tapset. + * system.stp: New tapset. 2006-03-01 Martin Hunt <hunt@redhat.com> - * aux_syscalls.stp (_struct_sockaddr_u): Parse sockaddr. + * aux_syscalls.stp (_struct_sockaddr_u): Parse sockaddr. * syscalls.stp (bind): Call _struct_sockaddr_u(). * errno.stp (errno_str): Complete rewrite in C for speed and accuracy. (returnstr): New function for syscall tapet. - + 2006-02-22 Frank Ch. Eigler <fche@elastic.org> * timestamp.stp (get_cycles): New function. @@ -1885,9 +1890,9 @@ * aux_syscalls.stp (_struct_timeval_u): New function. (_struct_timeval): New function. (_struct_timespec_u): New function. - (_struct_timespec): New function. + (_struct_timespec): New function. (_struct_itimerval_u): New function: - (_struct_itimerval): New function: + (_struct_itimerval): New function: (_signal_name): New function. (_module_flags_str): Fixed. @@ -1895,13 +1900,13 @@ _struct_itimerval_u(). * syscalls.stp: Latest version. Many changes. - + 2006-02-08 Martin Hunt <hunt@redhat.com> * aux_syscalls.stp(_msync_flag_str): Fix. (_wait_opt_str): Fix. * syscalls.stp: Latest. - + 2006-02-07 Martin Hunt <hunt@redhat.com> * syscalls.stp: Latest. diff --git a/tapset/utrace.stp b/tapset/utrace.stp new file mode 100644 index 00000000..3831ca3c --- /dev/null +++ b/tapset/utrace.stp @@ -0,0 +1,11 @@ +/* utrace-only subset of register accessors */ + + +%{ +#include "syscall.h" +%} + + +function _utrace_syscall_nr:long () %{ + THIS->__retvalue = __stp_user_syscall_nr(CONTEXT->regs); /* pure */ +%} diff --git a/tapsets.cxx b/tapsets.cxx index aa6a611d..4fa53a88 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -675,13 +675,8 @@ struct dwflpp void get_module_dwarf(bool required = false, bool report = true) { - if (!module_dwarf && mod_info->dwarf_status != info_absent) - { - if (!sess.ignore_dwarf) - module_dwarf = dwfl_module_getdwarf(module, &module_bias); - mod_info->dwarf_status = (module_dwarf ? info_present : info_absent); - } - + module_dwarf = dwfl_module_getdwarf(module, &module_bias); + mod_info->dwarf_status = (module_dwarf ? info_present : info_absent); if (!module_dwarf && report) { string msg = "cannot find "; @@ -863,7 +858,7 @@ struct dwflpp bool t = (fnmatch(pattern.c_str(), cu_name.c_str(), 0) == 0 || fnmatch(prefixed_pattern.c_str(), cu_name.c_str(), 0) == 0); if (t && sess.verbose>3) - clog << "pattern '" << pattern << "' " + clog << "pattern '" << prefixed_pattern << "' " << "matches " << "CU '" << cu_name << "'" << "\n"; return t; @@ -884,42 +879,8 @@ struct dwflpp { } - // Called by dwfl_linux_kernel_report_offline(). We may not have - // dwarf info for the kernel and/or modules, so remember this - // module's pathname in case we need to extract elf info from it. - // (Currently, we get all the elf info we need via elfutils -- if the - // elf file exists -- so remembering the pathname isn't strictly needed. - // But we still need to handle the case where there's no vmlinux.) - - static systemtap_session* this_session; // XXX: used only due to elfutils shortcoming - - static int pathname_caching_callback(const char *name, const char *path) - { - module_info *mi = new module_info(name); - assert (this_session); - this_session->module_cache->cache[name] = mi; - - if (this_session->ignore_vmlinux && path && name == TOK_KERNEL) - { - // report_kernel() in elfutils found vmlinux, but pretend it didn't. - // Given a non-null path, returning 1 means keep reporting modules. - mi->dwarf_status = info_absent; - return 1; - } - else if (path) - { - mi->elf_path = path; - return 1; - } - - // No vmlinux. Here returning 0 to report_kernel() means go ahead - // and keep reporting modules. - assert(name == TOK_KERNEL); - mi->dwarf_status = info_absent; - return 0; - } - void setup(bool kernel, bool debuginfo_needed = true) + void setup_kernel(bool debuginfo_needed = true) { if (! sess.module_cache) sess.module_cache = new module_cache (); @@ -933,14 +894,6 @@ struct dwflpp static const char *debug_path = (debuginfo_env_arr ? debuginfo_env_arr : sess.kernel_release.c_str()); - static const Dwfl_Callbacks proc_callbacks = - { - dwfl_linux_proc_find_elf, - dwfl_standard_find_debuginfo, - NULL, - & debuginfo_path - }; - static const Dwfl_Callbacks kernel_callbacks = { dwfl_linux_kernel_find_elf, @@ -949,135 +902,110 @@ struct dwflpp & debuginfo_path }; - if (kernel) - { - dwfl = dwfl_begin (&kernel_callbacks); - if (!dwfl) - throw semantic_error ("cannot open dwfl"); - dwfl_report_begin (dwfl); - - int (*callback)(const char *name, const char *path); - if (sess.consult_symtab && !sess.module_cache->paths_collected) - { - callback = pathname_caching_callback; - sess.module_cache->paths_collected = true; - } - else - callback = NULL; - - // XXX: we should not need to set this static variable just - // for the callback. The following elfutils routine should - // take some void* parameter to pass context to the callback. - this_session = & sess; - int rc = dwfl_linux_kernel_report_offline (dwfl, - debug_path, - /* selection predicate */ - callback); - this_session = 0; - - if (debuginfo_needed) - dwfl_assert (string("missing kernel ") + - sess.kernel_release + - string(" ") + - sess.architecture + - string(" debuginfo"), - rc); - - // XXX: it would be nice if we could do a single - // ..._report_offline call for an entire systemtap script, so - // that a selection predicate would filter out modules outside - // the union of all the requested wildcards. But we build - // derived_probes one-by-one and we don't have lookahead. - - // XXX: a special case: if we have only kernel.* probe points, - // we shouldn't waste time looking for module debug-info (and - // vice versa). - - // NB: the result of an _offline call is the assignment of - // virtualized addresses to relocatable objects such as - // modules. These have to be converted to real addresses at - // run time. See the dwarf_derived_probe ctor and its caller. - } - else - { - dwfl = dwfl_begin (&proc_callbacks); - dwfl_report_begin (dwfl); - if (!dwfl) - throw semantic_error ("cannot open dwfl"); - - throw semantic_error ("user-space probes not yet implemented"); - // XXX: Find pids or processes, do userspace stuff. - } + dwfl = dwfl_begin (&kernel_callbacks); + if (!dwfl) + throw semantic_error ("cannot open dwfl"); + dwfl_report_begin (dwfl); + + int rc = dwfl_linux_kernel_report_offline (dwfl, + debug_path, + NULL); + + if (debuginfo_needed) + dwfl_assert (string("missing kernel ") + + sess.kernel_release + + string(" ") + + sess.architecture + + string(" debuginfo"), + rc); + + // XXX: it would be nice if we could do a single + // ..._report_offline call for an entire systemtap script, so + // that a selection predicate would filter out modules outside + // the union of all the requested wildcards. But we build + // derived_probes one-by-one and we don't have lookahead. + // PR 3498. + + // XXX: a special case: if we have only kernel.* probe points, + // we shouldn't waste time looking for module debug-info (and + // vice versa). + + // NB: the result of an _offline call is the assignment of + // virtualized addresses to relocatable objects such as + // modules. These have to be converted to real addresses at + // run time. See the dwarf_derived_probe ctor and its caller. dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL)); } + void setup_user(string module_name, bool debuginfo_needed = true) + { + // XXX: this is where the session -R parameter could come in + static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug:build"; + static char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH"); + static char *debuginfo_path = (debuginfo_env_arr ?: debuginfo_path_arr); + static const Dwfl_Callbacks user_callbacks = + { + NULL, /* dwfl_linux_kernel_find_elf, */ + dwfl_standard_find_debuginfo, + dwfl_offline_section_address, + & debuginfo_path + }; - // ----------------------------------------------------------------- + dwfl = dwfl_begin (&user_callbacks); + if (!dwfl) + throw semantic_error ("cannot open dwfl"); + dwfl_report_begin (dwfl); - static int module_caching_callback(Dwfl_Module * mod, - void **, - const char *name, - Dwarf_Addr addr, - void *param) - { - systemtap_session *sess = static_cast<systemtap_session*>(param); - module_cache_t *cache = sess->module_cache; - module_info *mi = NULL; + // XXX: should support buildid-based naming - assert (cache); + Dwfl_Module *mod = dwfl_report_offline (dwfl, + module_name.c_str(), + module_name.c_str(), + -1); + // XXX: save mod! - if (sess->ignore_vmlinux && name == TOK_KERNEL) - // This wouldn't be called for vmlinux if vmlinux weren't there. - return DWARF_CB_OK; + if (debuginfo_needed) + dwfl_assert (string("missing process ") + + module_name + + string(" ") + + sess.architecture + + string(" debuginfo"), + mod); - if (cache->paths_collected) - mi = cache->cache[name]; - if (!mi) - { - mi = new module_info(name); - cache->cache[name] = mi; - } - mi->mod = mod; - mi->addr = addr; - return DWARF_CB_OK; - } + // NB: the result of an _offline call is the assignment of + // virtualized addresses to relocatable objects such as + // modules. These have to be converted to real addresses at + // run time. See the dwarf_derived_probe ctor and its caller. - void cache_modules_dwarf() - { - if (!sess.module_cache->dwarf_collected) - { - ptrdiff_t off = 0; - do - { - if (pending_interrupts) return; - off = dwfl_getmodules (dwfl, module_caching_callback, - & sess, off); - } - while (off > 0); - dwfl_assert("dwfl_getmodules", off == 0); - sess.module_cache->dwarf_collected = true; - } + dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL)); } - void iterate_over_modules(int (* callback)(Dwfl_Module *, module_info *, + + + // ----------------------------------------------------------------- + + void iterate_over_modules(int (* callback)(Dwfl_Module *, void **, const char *, Dwarf_Addr, void *), - void * data) + dwarf_query *data) { - cache_modules_dwarf(); - - map<string, module_info*>::iterator i; - for (i = sess.module_cache->cache.begin(); i != sess.module_cache->cache.end(); i++) + ptrdiff_t off = 0; + do { if (pending_interrupts) return; - module_info *mi = i->second; - int rc = callback (mi->mod, mi, mi->name, mi->addr, data); - if (rc != DWARF_CB_OK) break; + off = dwfl_getmodules (dwfl, callback, data, off); } + while (off > 0); + dwfl_assert("dwfl_getmodules", off == 0); + + // PR6864 XXX: For dwarfless case (if .../vmlinux is missing), then the + // "kernel" module is not reported in the loop above. However, we + // may be able to make do with symbol table data. } + // Defined after dwarf_query void query_modules(dwarf_query *q); @@ -2322,10 +2250,6 @@ struct dwflpp }; -systemtap_session* dwflpp::this_session = 0; // XXX: used only due to elfutils shortcoming - - - enum function_spec_type @@ -2363,9 +2287,7 @@ struct dwarf_derived_probe: public derived_probe long maxactive_val; void printsig (std::ostream &o) const; - void join_group (systemtap_session& s); - void emit_probe_local_init(translator_output * o); // Pattern registration helpers. @@ -2379,6 +2301,40 @@ struct dwarf_derived_probe: public derived_probe }; +struct uprobe_derived_probe: public derived_probe +{ + bool return_p; + string module; // * => unrestricted + int pid; // 0 => unrestricted + string section; // empty => absolute address + Dwarf_Addr address; + // bool has_maxactive; + // long maxactive_val; + + uprobe_derived_probe (const string& function, + const string& filename, + int line, + const string& module, + int pid, + const string& section, + Dwarf_Addr dwfl_addr, + Dwarf_Addr addr, + dwarf_query & q, + Dwarf_Die* scope_die); + + // alternate constructor for process(PID).statement(ADDR).absolute + uprobe_derived_probe (probe *base, + probe_point *location, + int pid, + Dwarf_Addr addr, + bool return_p); + + void printsig (std::ostream &o) const; + void join_group (systemtap_session& s); +}; + + + struct dwarf_derived_probe_group: public derived_probe_group { private: @@ -2422,6 +2378,8 @@ struct base_query // Extracted parameters. bool has_kernel; + bool has_module; + bool has_process; string module_val; // has_kernel => module_val = "kernel" virtual void handle_query_module() = 0; @@ -2437,15 +2395,21 @@ base_query::base_query(systemtap_session & sess, : sess(sess), base_probe(base_probe), base_loc(base_loc), dw(dw), results(results) { - has_kernel = has_null_param(params, TOK_KERNEL); + has_kernel = has_null_param (params, TOK_KERNEL); if (has_kernel) module_val = "kernel"; + + has_module = get_string_param (params, TOK_MODULE, module_val); + if (has_module) + has_process = false; else { - bool has_module = get_string_param(params, TOK_MODULE, module_val); - assert (has_module); // no other options are possible by construction - (void) has_module; + has_process = get_string_param(params, TOK_PROCESS, module_val); + if (has_process) + module_val = find_executable (module_val); } + + assert (has_kernel || has_process || has_module); } bool @@ -2726,23 +2690,40 @@ dwflpp::iterate_over_functions (int (* callback)(Dwarf_Die * func, void * arg), struct dwarf_builder: public derived_probe_builder { dwflpp *kern_dw; + map <string,dwflpp*> user_dw; dwarf_builder(): kern_dw(0) {} - void build_no_more (systemtap_session &s) + + /* NB: not virtual, so can be called from dtor too: */ + void dwarf_build_no_more (bool verbose) { if (kern_dw) { - if (s.verbose > 3) - clog << "dwarf_builder releasing dwflpp" << endl; + if (verbose) + clog << "dwarf_builder releasing kernel dwflpp" << endl; delete kern_dw; kern_dw = 0; } + + for (map<string,dwflpp*>::iterator udi = user_dw.begin(); + udi != user_dw.end(); + udi ++) + { + if (verbose) + clog << "dwarf_builder releasing user dwflpp " << udi->first << endl; + delete udi->second; + } + user_dw.erase (user_dw.begin(), user_dw.end()); + } + + void build_no_more (systemtap_session &s) + { + dwarf_build_no_more (s.verbose > 3); } ~dwarf_builder() { - // XXX: in practice, NOTREACHED - delete kern_dw; + dwarf_build_no_more (false); } virtual void build(systemtap_session & sess, @@ -2924,6 +2905,10 @@ dwarf_query::handle_query_module() void dwarf_query::build_blacklist() { + // No blacklist for userspace. + if (has_process) + return; + // We build up the regexps in these strings // Add ^ anchors at the front; $ will be added just before regcomp. @@ -3132,9 +3117,11 @@ dwarf_query::parse_function_spec(string & spec) } +#if 0 // Forward declaration. -static int query_kernel_module (Dwfl_Module *, module_info *, const char *, +static int query_kernel_module (Dwfl_Module *, void **, const char *, Dwarf_Addr, void *); +#endif // XXX: pull this into dwflpp @@ -3160,6 +3147,9 @@ dwarf_query::blacklisted_p(const string& funcname, const string& section, Dwarf_Addr addr) { + if (has_process) + return false; // no blacklist for userspace + if (section.substr(0, 6) == string(".init.") || section.substr(0, 6) == string(".exit.") || section.substr(0, 9) == string(".devinit.") || @@ -3249,7 +3239,6 @@ dwarf_query::add_probe_point(const string& funcname, Dwarf_Die* scope_die, Dwarf_Addr addr) { - dwarf_derived_probe *probe = NULL; string reloc_section; // base section for relocation purposes Dwarf_Addr reloc_addr = addr; // relocated string blacklist_section; // linking section for blacklist purposes @@ -3265,7 +3254,7 @@ dwarf_query::add_probe_point(const string& funcname, } else if (dwfl_module_relocations (dw.module) > 0) { - // This is arelocatable module; libdwfl already knows its + // This is a relocatable module; libdwfl already knows its // sections, so we can relativize addr. int idx = dwfl_module_relocate_address (dw.module, &reloc_addr); const char* r_s = dwfl_module_relocation_info (dw.module, idx, NULL); @@ -3274,12 +3263,16 @@ dwarf_query::add_probe_point(const string& funcname, blacklist_section = reloc_section; if(reloc_section == "" && dwfl_module_relocations (dw.module) == 1) - blacklist_section = this->get_blacklist_section(addr); + { + blacklist_section = this->get_blacklist_section(addr); + reloc_section = ".dynamic"; + reloc_addr = addr; + } } else { blacklist_section = this->get_blacklist_section(addr); - reloc_section = ""; + reloc_section = ".absolute"; } if (sess.verbose > 1) @@ -3287,8 +3280,10 @@ dwarf_query::add_probe_point(const string& funcname, clog << "probe " << funcname << "@" << filename << ":" << line; if (string(module) == TOK_KERNEL) clog << " kernel"; - else + else if (has_module) clog << " module=" << module; + else if (has_process) + clog << " process=" << module; if (reloc_section != "") clog << " reloc=" << reloc_section; if (blacklist_section != "") clog << " section=" << blacklist_section; clog << " pc=0x" << hex << addr << dec; @@ -3308,9 +3303,20 @@ dwarf_query::add_probe_point(const string& funcname, if (! bad) { sess.unwindsym_modules.insert (module); - probe = new dwarf_derived_probe(funcname, filename, line, - module, reloc_section, addr, reloc_addr, *this, scope_die); - results.push_back(probe); + + if (has_process) + { + results.push_back (new uprobe_derived_probe(funcname, filename, line, + module, 0, reloc_section, addr, reloc_addr, + *this, scope_die)); + } + else + { + assert (has_kernel || has_module); + results.push_back (new dwarf_derived_probe(funcname, filename, line, + module, reloc_section, addr, reloc_addr, + *this, scope_die)); + } } } @@ -3447,7 +3453,8 @@ query_func_info (Dwarf_Addr entrypc, else { if (q->sess.prologue_searching - && !q->has_statement_str && !q->has_statement_num) // PR 2608 + && !q->has_statement_str && !q->has_statement_num + && !q->sess.ignore_vmlinux && !q->sess.ignore_dwarf) // PR 2608 { if (fi.prologue_end == 0) throw semantic_error("could not find prologue-end " @@ -3762,9 +3769,10 @@ query_cu (Dwarf_Die * cudie, void * arg) } +#if 0 static int query_kernel_module (Dwfl_Module *mod, - module_info *, + void **, const char *name, Dwarf_Addr, void *arg) @@ -3778,6 +3786,8 @@ query_kernel_module (Dwfl_Module *mod, } return DWARF_CB_OK; } +#endif + static void validate_module_elf (Dwfl_Module *mod, const char *name, base_query *q) @@ -3844,17 +3854,69 @@ validate_module_elf (Dwfl_Module *mod, const char *name, base_query *q) << "\n"; } + + +static Dwarf_Addr +lookup_symbol_address (Dwfl_Module *m, const char* wanted) +{ + int syments = dwfl_module_getsymtab(m); + assert(syments); + for (int i = 1; i < syments; ++i) + { + GElf_Sym sym; + const char *name = dwfl_module_getsym(m, i, &sym, NULL); + if (name != NULL && strcmp(name, wanted) == 0) + return sym.st_value; + } + + return 0; +} + + + static int query_module (Dwfl_Module *mod, - module_info *mi, + void **, const char *name, - Dwarf_Addr, + Dwarf_Addr addr, void *arg) { - base_query * q = static_cast<base_query *>(arg); + base_query *q = static_cast<base_query *>(arg); try { + module_info* mi = q->sess.module_cache->cache[name]; + if (mi == 0) + { + mi = q->sess.module_cache->cache[name] = new module_info(name); + + mi->mod = mod; + mi->addr = addr; + + const char* debug_filename = ""; + const char* main_filename = ""; + (void) dwfl_module_info (mod, NULL, NULL, + NULL, NULL, NULL, + & main_filename, + & debug_filename); + + if (q->sess.ignore_vmlinux && name == TOK_KERNEL) + { + // report_kernel() in elfutils found vmlinux, but pretend it didn't. + // Given a non-null path, returning 1 means keep reporting modules. + mi->dwarf_status = info_absent; + } + else if (debug_filename || main_filename) + { + mi->elf_path = debug_filename ?: main_filename; + } + else if (name == TOK_KERNEL) + { + mi->dwarf_status = info_absent; + } + } + // OK, enough of that module_info caching business. + q->dw.focus_on_module(mod, mi); // If we have enough information in the pattern to skip a module and @@ -3871,14 +3933,28 @@ query_module (Dwfl_Module *mod, if (mod) validate_module_elf(mod, name, q); else + assert(q->has_kernel); // and no vmlinux to examine + + if (q->sess.verbose>2) + cerr << "focused on module '" << q->dw.module_name << "'\n"; + + + // Collect a few kernel addresses. XXX: these belong better + // to the sess.module_info["kernel"] struct. + if (q->dw.module_name == TOK_KERNEL) { - assert(q->has_kernel); // and no vmlinux to examine - if (q->sess.verbose>2) - cerr << "focused on module '" << q->dw.module_name << "'\n"; + if (! q->sess.sym_kprobes_text_start) + q->sess.sym_kprobes_text_start = lookup_symbol_address (mod, "__kprobes_text_start"); + if (! q->sess.sym_kprobes_text_end) + q->sess.sym_kprobes_text_end = lookup_symbol_address (mod, "__kprobes_text_end"); + if (! q->sess.sym_stext) + q->sess.sym_stext = lookup_symbol_address (mod, "_stext"); } + // Finally, search the module for matches of the probe point. q->handle_query_module(); + // If we know that there will be no more matches, abort early. if (q->dw.module_name_final_match(q->module_val)) return DWARF_CB_ABORT; @@ -3895,20 +3971,7 @@ query_module (Dwfl_Module *mod, void dwflpp::query_modules(dwarf_query *q) { - string name = q->module_val; - if (name_has_wildcard(name)) - iterate_over_modules(&query_module, q); - else - { - cache_modules_dwarf(); - - map<string, module_info*>::iterator i = sess.module_cache->cache.find(name); - if (i != sess.module_cache->cache.end()) - { - module_info *mi = i->second; - query_module(mi->mod, mi, name.c_str(), mi->addr, q); - } - } + iterate_over_modules(&query_module, q); } struct var_expanding_copy_visitor: public deep_copy_visitor @@ -4488,7 +4551,7 @@ dwarf_derived_probe::printsig (ostream& o) const // function instances. This is distinct from the verbose/clog // output, since this part goes into the cache hash calculations. sole_location()->print (o); - o << " /* pc=0x" << hex << addr << dec << " */"; + o << " /* pc=" << section << "+0x" << hex << addr << dec << " */"; printsig_nested (o); } @@ -4506,7 +4569,7 @@ dwarf_derived_probe::join_group (systemtap_session& s) dwarf_derived_probe::dwarf_derived_probe(const string& funcname, const string& filename, int line, - // module & section speficy a relocation + // module & section specify a relocation // base for <addr>, unless section=="" // (equivalently module=="kernel") const string& module, @@ -4572,10 +4635,14 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname, // number any particular match of the wildcards. vector<probe_point::component*> comps; - comps.push_back - (module == TOK_KERNEL - ? new probe_point::component(TOK_KERNEL) - : new probe_point::component(TOK_MODULE, new literal_string(module))); + if (q.has_kernel) + comps.push_back (new probe_point::component(TOK_KERNEL)); + else if(q.has_module) + comps.push_back (new probe_point::component(TOK_MODULE, new literal_string(module))); + else if(q.has_process) + comps.push_back (new probe_point::component(TOK_PROCESS, new literal_string(module))); + else + assert (0); string fn_or_stmt; if (q.has_function_str || q.has_function_num) @@ -4588,9 +4655,9 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname, string retro_name = funcname; if (filename != "") { - retro_name += ("@" + string (filename)); + retro_name += ("@" + string (filename)); if (line > 0) - retro_name += (":" + lex_cast<string> (line)); + retro_name += (":" + lex_cast<string> (line)); } comps.push_back (new probe_point::component @@ -4670,7 +4737,7 @@ dwarf_derived_probe::register_patterns(match_node * root) register_function_and_statement_variants(root->bind_str(TOK_MODULE), dw); root->bind(TOK_KERNEL)->bind_num(TOK_STATEMENT)->bind(TOK_ABSOLUTE)->bind(dw); - // register_function_and_statement_variants(root->bind_str(TOK_PROCESS), dw); + register_function_and_statement_variants(root->bind_str(TOK_PROCESS), dw); } void @@ -4856,7 +4923,7 @@ dwarf_derived_probe_group::emit_module_init (systemtap_session& s) s.op->newline() << "struct stap_dwarf_kprobe *kp = & stap_dwarf_kprobes[i];"; s.op->newline() << "unsigned long relocated_addr = _stp_module_relocate (sdp->module, sdp->section, sdp->address);"; s.op->newline() << "if (relocated_addr == 0) continue;"; // quietly; assume module is absent - s.op->newline() << "probe_point = sdp->pp;"; + s.op->newline() << "probe_point = sdp->pp;"; // for error messages s.op->newline() << "if (sdp->return_p) {"; s.op->newline(1) << "kp->u.krp.kp.addr = (void *) relocated_addr;"; s.op->newline() << "if (sdp->maxactive_p) {"; @@ -4973,26 +5040,6 @@ dwarf_derived_probe_group::emit_module_exit (systemtap_session& s) } - -static Dwarf_Addr -lookup_symbol_address (Dwfl_Module *m, const char* wanted) -{ - int syments = dwfl_module_getsymtab(m); - assert(syments); - for (int i = 1; i < syments; ++i) - { - GElf_Sym sym; - const char *name = dwfl_module_getsym(m, i, &sym, NULL); - if (name != NULL && strcmp(name, wanted) == 0) - return sym.st_value; - } - - return 0; -} - - - - void dwarf_builder::build(systemtap_session & sess, probe * base, @@ -5004,39 +5051,49 @@ dwarf_builder::build(systemtap_session & sess, // XXX: but they should be per-session, as this builder object // may be reused if we try to cross-instrument multiple targets. - if (!kern_dw) + dwflpp* dw = 0; + + if (! sess.module_cache) + sess.module_cache = new module_cache (); + + string module_name; + if (has_null_param (parameters, TOK_KERNEL) + || get_param (parameters, TOK_MODULE, module_name)) { - kern_dw = new dwflpp(sess); - assert(kern_dw); - kern_dw->setup(true); + // kernel or kernel module target + if (! kern_dw) + { + kern_dw = new dwflpp(sess); + // XXX: PR 3498, PR 6864 + kern_dw->setup_kernel(true); + } + dw = kern_dw; } - - Dwfl_Module* km = 0; - kern_dw->iterate_over_modules(&query_kernel_module, &km); - if (km) + else if (get_param (parameters, TOK_PROCESS, module_name)) { - if (! sess.sym_kprobes_text_start) - sess.sym_kprobes_text_start = lookup_symbol_address (km, "__kprobes_text_start"); - if (! sess.sym_kprobes_text_end) - sess.sym_kprobes_text_end = lookup_symbol_address (km, "__kprobes_text_end"); - if (! sess.sym_stext) - sess.sym_stext = lookup_symbol_address (km, "_stext"); + module_name = find_executable (module_name); // canonicalize it - if (sess.verbose > 2) + // user-space target; we use one dwflpp instance per module name + // (= program or shared library) + if (user_dw.find(module_name) == user_dw.end()) { - clog << "control symbols:" - // abbreviate the names - they're for our debugging only anyway - << " kts: 0x" << hex << sess.sym_kprobes_text_start - << " kte: 0x" << sess.sym_kprobes_text_end - << " stext: 0x" << sess.sym_stext - << dec << endl; + dw = new dwflpp(sess); + // XXX: PR 3498 + dw->setup_user(module_name); + user_dw[module_name] = dw; } + else + dw = user_dw[module_name]; } - dwflpp* dw = kern_dw; + dwarf_query q(sess, base, location, *dw, parameters, finished_results); - if (q.has_absolute) + + // XXX: kernel.statement.absolute is a special case that requires no + // dwfl processing. This code should be in a separate builder. + + if (q.has_kernel && q.has_absolute) { // assert guru mode for absolute probes if (! q.base_probe->privileged) @@ -5056,7 +5113,6 @@ dwarf_builder::build(systemtap_session & sess, return; } - // dw->iterate_over_modules(&query_module, &q); dw->query_modules(&q); } @@ -5385,7 +5441,6 @@ module_info::get_symtab(dwarf_query *q) sess.sym_kprobes_text_end = sym_table->lookup_symbol_address("__kprobes_text_end"); sess.sym_stext = sym_table->lookup_symbol_address("_stext"); - bias = sym_table->lookup_symbol_address("_text"); } } } @@ -5445,21 +5500,7 @@ void task_finder_derived_probe_group::create_session_group (systemtap_session& s) { if (! s.task_finder_derived_probes) - { - s.task_finder_derived_probes = new task_finder_derived_probe_group(); - - // Make sure we've got the stuff we need early in the output code. - embeddedcode *ec = new embeddedcode; - ec->tok = NULL; - ec->code = string("#if ! defined(CONFIG_UTRACE)\n") - + string("#error \"Need CONFIG_UTRACE!\"\n") - + string("#endif\n") - + string("#include <linux/utrace.h>\n") - + string("#include <linux/mount.h>\n") - + string("#include \"task_finder.c\"\n"); - - s.embeds.push_back(ec); - } + s.task_finder_derived_probes = new task_finder_derived_probe_group(); } @@ -5471,8 +5512,7 @@ task_finder_derived_probe_group::emit_module_init (systemtap_session& s) s.op->newline() << "rc = stap_start_task_finder();"; s.op->newline() << "if (rc) {"; - s.op->indent(1); - s.op->newline() << "stap_stop_task_finder();"; + s.op->newline(1) << "stap_stop_task_finder();"; s.op->newline(-1) << "}"; } @@ -5556,52 +5596,23 @@ struct itrace_builder: public derived_probe_builder vector<derived_probe *> & finished_results) { string path; - int64_t pid; + int64_t pid = 0; int single_step; bool has_path = get_param (parameters, TOK_PROCESS, path); bool has_pid = get_param (parameters, TOK_PROCESS, pid); + // XXX: PR 6445 needs !has_path && !has_pid support assert (has_path || has_pid); single_step = 1; // If we have a path, we need to validate it. if (has_path) - { - string::size_type start_pos, end_pos; - string component; - - // Make sure it starts with '/'. - if (path[0] != '/') - throw semantic_error ("process path must start with a '/'", - location->tok); - - start_pos = 1; // get past the initial '/' - while ((end_pos = path.find('/', start_pos)) != string::npos) - { - component = path.substr(start_pos, end_pos - start_pos); - // Make sure it isn't empty. - if (component.size() == 0) - throw semantic_error ("process path component cannot be empty", - location->tok); - // Make sure it isn't relative. - else if (component == "." || component == "..") - throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok); - - start_pos = end_pos + 1; - } - component = path.substr(start_pos); - // Make sure it doesn't end with '/'. - if (component.size() == 0) - throw semantic_error ("process path cannot end with a '/'", location->tok); - // Make sure it isn't relative. - else if (component == "." || component == "..") - throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok); - } + path = find_executable (path); finished_results.push_back(new itrace_derived_probe(sess, base, location, has_path, path, pid, - single_step + single_step )); } }; @@ -5830,26 +5841,64 @@ struct utrace_var_expanding_copy_visitor: public var_expanding_copy_visitor string probe_name; enum utrace_derived_probe_flags flags; bool target_symbol_seen; - static bool syscall_function_added; void visit_target_symbol (target_symbol* e); }; -bool utrace_var_expanding_copy_visitor::syscall_function_added = false; - utrace_derived_probe::utrace_derived_probe (systemtap_session &s, probe* p, probe_point* l, bool hp, string &pn, int64_t pd, enum utrace_derived_probe_flags f): - derived_probe(p, l), has_path(hp), path(pn), pid(pd), flags(f), + derived_probe (p, new probe_point (*l) /* .components soon rewritten */ ), + has_path(hp), path(pn), pid(pd), flags(f), target_symbol_seen(false) { // Make a local-variable-expanded copy of the probe body utrace_var_expanding_copy_visitor v (s, name, flags); require <statement*> (&v, &(this->body), base->body); target_symbol_seen = v.target_symbol_seen; + + // Reset the sole element of the "locations" vector as a + // "reverse-engineered" form of the incoming (q.base_loc) probe + // point. This allows a user to see what program etc. + // number any particular match of the wildcards. + + vector<probe_point::component*> comps; + if (hp) + comps.push_back (new probe_point::component(TOK_PROCESS, new literal_string(path))); + else + comps.push_back (new probe_point::component(TOK_PROCESS, new literal_number(pid))); + switch (flags) + { + case UDPF_THREAD_BEGIN: + comps.push_back (new probe_point::component(TOK_THREAD)); + comps.push_back (new probe_point::component(TOK_BEGIN)); + break; + case UDPF_THREAD_END: + comps.push_back (new probe_point::component(TOK_THREAD)); + comps.push_back (new probe_point::component(TOK_END)); + break; + case UDPF_SYSCALL: + comps.push_back (new probe_point::component(TOK_SYSCALL)); + break; + case UDPF_SYSCALL_RETURN: + comps.push_back (new probe_point::component(TOK_SYSCALL)); + comps.push_back (new probe_point::component(TOK_RETURN)); + break; + case UDPF_BEGIN: + comps.push_back (new probe_point::component(TOK_BEGIN)); + break; + case UDPF_END: + comps.push_back (new probe_point::component(TOK_END)); + break; + default: + assert (0); + } + + // Overwrite it. + this->sole_location()->components = comps; } @@ -5905,27 +5954,11 @@ utrace_var_expanding_copy_visitor::visit_target_symbol (target_symbol* e) // Remember that we've seen a target variable. target_symbol_seen = true; - // Synthesize a function to grab the syscall . - if (! syscall_function_added) - { - functiondecl *fdecl = new functiondecl; - fdecl->tok = e->tok; - embeddedcode *ec = new embeddedcode; - ec->tok = e->tok; - ec->code = string("THIS->__retvalue = __stp_user_syscall_nr(CONTEXT->regs); /* pure */"); - - fdecl->name = string("_syscall_nr_get"); - fdecl->body = ec; - fdecl->type = pe_long; - sess.functions.push_back(fdecl); - syscall_function_added = true; - } - // We're going to substitute a synthesized '_syscall_nr_get' // function call for the '$syscall' reference. functioncall* n = new functioncall; n->tok = e->tok; - n->function = "_syscall_nr_get"; + n->function = "_utrace_syscall_nr"; n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session provide <functioncall*> (this, n); @@ -5976,51 +6009,19 @@ struct utrace_builder: public derived_probe_builder has_pid = true; pid = 0; } - // Validate pid. + else if (has_path) + { + path = find_executable (path); + sess.unwindsym_modules.insert (path); + } else if (has_pid) { - // We can't probe 'init' (pid 1). + // We can't probe 'init' (pid 1). XXX: where does this limitation come from? if (pid < 2) throw semantic_error ("process pid must be greater than 1", location->tok); - } - // If we have a regular path, we need to validate it. - else if (has_path) - { - string::size_type start_pos, end_pos; - string component; - - // XXX: these checks should be done in terms of filesystem - // operations. - - // Make sure it starts with '/'. - if (path[0] != '/') - throw semantic_error ("process path must start with a '/'", - location->tok); - start_pos = 1; // get past the initial '/' - while ((end_pos = path.find('/', start_pos)) != string::npos) - { - component = path.substr(start_pos, end_pos - start_pos); - // Make sure it isn't empty. - if (component.size() == 0) - throw semantic_error ("process path component cannot be empty", - location->tok); - // Make sure it isn't relative. - else if (component == "." || component == "..") - throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok); - - start_pos = end_pos + 1; - } - component = path.substr(start_pos); - // Make sure it doesn't end with '/'. - if (component.size() == 0) - throw semantic_error ("process path cannot end with a '/'", location->tok); - // Make sure it isn't relative. - else if (component == "." || component == "..") - throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok); - - sess.unwindsym_modules.insert (path); + // XXX: could we use /proc/$pid/exe in unwindsym_modules and elsewhere? } finished_results.push_back(new utrace_derived_probe(sess, base, location, @@ -6163,6 +6164,8 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline(); s.op->newline() << "/* ---- utrace probes ---- */"; + s.op->newline() << "#include \"task_finder.c\""; + s.op->newline() << "enum utrace_derived_probe_flags {"; s.op->indent(1); s.op->newline() << "UDPF_NONE,"; @@ -6350,7 +6353,7 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "break;"; s.op->indent(-1); } - + if (flags_seen[UDPF_SYSCALL] || flags_seen[UDPF_SYSCALL_RETURN]) { s.op->newline() << "case UDPF_SYSCALL:"; @@ -6474,15 +6477,6 @@ utrace_derived_probe_group::emit_module_exit (systemtap_session& s) // user-space probes // ------------------------------------------------------------------------ -struct uprobe_derived_probe: public derived_probe -{ - uint64_t process, address; - bool return_p; - uprobe_derived_probe (systemtap_session &s, probe* p, probe_point* l, - uint64_t, uint64_t, bool); - void join_group (systemtap_session& s); -}; - struct uprobe_derived_probe_group: public generic_dpg<uprobe_derived_probe> { @@ -6493,12 +6487,128 @@ public: }; -uprobe_derived_probe::uprobe_derived_probe (systemtap_session &s, - probe* p, probe_point* l, - uint64_t pp, uint64_t aa, bool rr): - derived_probe(p, l), process(pp), address(aa), return_p (rr) +uprobe_derived_probe::uprobe_derived_probe (const string& function, + const string& filename, + int line, + const string& module, + int pid, + const string& section, + Dwarf_Addr dwfl_addr, + Dwarf_Addr addr, + dwarf_query & q, + Dwarf_Die* scope_die /* may be null */): + derived_probe (q.base_probe, new probe_point (*q.base_loc) /* .components soon rewritten */ ), + return_p (q.has_return), module (module), pid (pid), section (section), address (addr) +{ + // We may receive probes on two types of ELF objects: ET_EXEC or ET_DYN. + // ET_EXEC ones need no further relocation on the addr(==dwfl_addr), whereas + // ET_DYN ones do (addr += run-time mmap base address). We tell these apart + // by the incoming section value (".absolute" vs. ".dynamic"). + + this->tok = q.base_probe->tok; + + // Make a target-variable-expanded copy of the probe body + if (!null_die(scope_die)) + { + dwarf_var_expanding_copy_visitor v (q, scope_die, dwfl_addr); // XXX: user-space deref's! + require <statement*> (&v, &(this->body), this->body); + + // If during target-variable-expanding the probe, we added a new block + // of code, add it to the start of the probe. + if (v.add_block) + this->body = new block(v.add_block, this->body); + + // If when target-variable-expanding the probe, we added a new + // probe, add it in a new file to the list of files to be processed. + if (v.add_probe) + { + stapfile *f = new stapfile; + f->probes.push_back(v.add_probe); + q.sess.files.push_back(f); + } + } + // else - null scope_die - $target variables will produce an error during translate phase + + // Reset the sole element of the "locations" vector as a + // "reverse-engineered" form of the incoming (q.base_loc) probe + // point. This allows a user to see what function / file / line + // number any particular match of the wildcards. + + vector<probe_point::component*> comps; + if(q.has_process) + comps.push_back (new probe_point::component(TOK_PROCESS, new literal_string(module))); + else + assert (0); + + string fn_or_stmt; + if (q.has_function_str || q.has_function_num) + fn_or_stmt = "function"; + else + fn_or_stmt = "statement"; + + if (q.has_function_str || q.has_statement_str) + { + string retro_name = function; + if (filename != "") + { + retro_name += ("@" + string (filename)); + if (line > 0) + retro_name += (":" + lex_cast<string> (line)); + } + comps.push_back + (new probe_point::component + (fn_or_stmt, new literal_string (retro_name))); + } + else if (q.has_function_num || q.has_statement_num) + { + Dwarf_Addr retro_addr; + if (q.has_function_num) + retro_addr = q.function_num_val; + else + retro_addr = q.statement_num_val; + comps.push_back (new probe_point::component + (fn_or_stmt, + new literal_number(retro_addr))); // XXX: should be hex if possible + + if (q.has_absolute) + comps.push_back (new probe_point::component (TOK_ABSOLUTE)); + } + + if (q.has_call) + comps.push_back (new probe_point::component(TOK_CALL)); + if (q.has_inline) + comps.push_back (new probe_point::component(TOK_INLINE)); + if (return_p) + comps.push_back (new probe_point::component(TOK_RETURN)); + /* + if (has_maxactive) + comps.push_back (new probe_point::component + (TOK_MAXACTIVE, new literal_number(maxactive_val))); + */ + + // Overwrite it. + this->sole_location()->components = comps; +} + + +uprobe_derived_probe::uprobe_derived_probe (probe *base, + probe_point *location, + int pid, + Dwarf_Addr addr, + bool has_return): + derived_probe (base, location), // location is not rewritten here + return_p (has_return), pid (pid), address (addr) { - s.need_uprobes = true; +} + + +void +uprobe_derived_probe::printsig (ostream& o) const +{ + // Same as dwarf_derived_probe. + sole_location()->print (o); + o << " /* pc=" << section << "+0x" << hex << address << dec << " */"; + printsig_nested (o); } @@ -6508,6 +6618,7 @@ uprobe_derived_probe::join_group (systemtap_session& s) if (! s.uprobe_derived_probes) s.uprobe_derived_probes = new uprobe_derived_probe_group (); s.uprobe_derived_probes->enroll (this); + task_finder_derived_probe_group::create_session_group (s); } @@ -6529,8 +6640,7 @@ struct uprobe_builder: public derived_probe_builder bool rr = has_null_param (parameters, TOK_RETURN); assert (b1 && b2); // by pattern_root construction - finished_results.push_back(new uprobe_derived_probe(sess, base, location, - process, address, rr)); + finished_results.push_back(new uprobe_derived_probe(base, location, process, address, rr)); } }; @@ -6541,29 +6651,52 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) if (probes.empty()) return; s.op->newline() << "/* ---- user probes ---- */"; + s.need_uprobes = true; // Ask buildrun.cxx to build extra module if needed + // If uprobes isn't in the kernel, pull it in from the runtime. s.op->newline() << "#if defined(CONFIG_UPROBES) || defined(CONFIG_UPROBES_MODULE)"; s.op->newline() << "#include <linux/uprobes.h>"; s.op->newline() << "#else"; s.op->newline() << "#include \"uprobes/uprobes.h\""; s.op->newline() << "#endif"; + s.op->newline() << "#include \"task_finder.c\""; + + s.op->newline() << "#ifndef MAXUPROBES"; + s.op->newline() << "#define MAXUPROBES 16"; // maximum possible armed uprobes per process() probe point + s.op->newline() << "#endif"; + s.op->newline() << "#define NUMUPROBES (MAXUPROBES*" << probes.size() << ")"; + // In .bss, the shared pool of uprobe/uretprobe structs. These are + // too big to embed in the initialized .data stap_uprobe_spec array. s.op->newline() << "struct stap_uprobe {"; s.op->newline(1) << "union { struct uprobe up; struct uretprobe urp; };"; - s.op->newline() << "unsigned registered_p:1;"; - s.op->newline() << "unsigned return_p:1;"; - s.op->newline() << "unsigned long process;"; + s.op->newline() << "int spec_index;"; // index into stap_uprobe_specs; <0 == free && unregistered + s.op->newline(-1) << "} stap_uprobes [NUMUPROBES];"; + s.op->newline() << "DEFINE_MUTEX(stap_uprobes_lock);"; // protects against concurrent registration/unregistration + + s.op->newline() << "struct stap_uprobe_spec {"; + s.op->newline(1) << "struct stap_task_finder_target finder;"; s.op->newline() << "unsigned long address;"; + s.op->newline() << "const char *pathname;"; s.op->newline() << "const char *pp;"; s.op->newline() << "void (*ph) (struct context*);"; - s.op->newline(-1) << "} stap_uprobes [] = {"; + s.op->newline() << "unsigned return_p:1;"; + s.op->newline(-1) << "} stap_uprobe_specs [] = {"; s.op->indent(1); for (unsigned i =0; i<probes.size(); i++) { uprobe_derived_probe* p = probes[i]; s.op->newline() << "{"; + s.op->line() << " .finder = {"; + if (p->pid != 0) + s.op->line() << " .pid=" << p->pid; + else if (p->section == ".absolute") + s.op->line() << " .pathname=" << lex_cast_qstring(p->module) << ", "; + // else ".dynamic" gets pathname=0, pid=0, activating task_finder "global tracing" + s.op->line() << "},"; + if (p->section != ".absolute") + s.op->line() << " .pathname=" << lex_cast_qstring(p->module) << ", "; s.op->line() << " .address=0x" << hex << p->address << dec << "UL,"; - s.op->line() << " .process=" << p->process << ","; s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ","; s.op->line() << " .ph=&" << p->name << ","; if (p->return_p) s.op->line() << " .return_p=1,"; @@ -6571,81 +6704,225 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) } s.op->newline(-1) << "};"; - s.op->newline(); - s.op->newline() << "void enter_uprobe_probe (struct uprobe *inst, struct pt_regs *regs) {"; + s.op->newline() << "static void enter_uprobe_probe (struct uprobe *inst, struct pt_regs *regs) {"; s.op->newline(1) << "struct stap_uprobe *sup = container_of(inst, struct stap_uprobe, up);"; + s.op->newline() << "struct stap_uprobe_spec *sups = &stap_uprobe_specs [sup->spec_index];"; common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING"); - s.op->newline() << "c->probe_point = sup->pp;"; + s.op->newline() << "if (sup->spec_index < 0 ||" + << "sup->spec_index >= " << probes.size() << ") return;"; // XXX: should not happen + s.op->newline() << "c->probe_point = sups->pp;"; s.op->newline() << "c->regs = regs;"; - s.op->newline() << "(*sup->ph) (c);"; + s.op->newline() << "(*sups->ph) (c);"; common_probe_entryfn_epilogue (s.op); s.op->newline(-1) << "}"; - s.op->newline() << "void enter_uretprobe_probe (struct uretprobe_instance *inst, struct pt_regs *regs) {"; + s.op->newline() << "static void enter_uretprobe_probe (struct uretprobe_instance *inst, struct pt_regs *regs) {"; s.op->newline(1) << "struct stap_uprobe *sup = container_of(inst->rp, struct stap_uprobe, urp);"; + s.op->newline() << "struct stap_uprobe_spec *sups = &stap_uprobe_specs [sup->spec_index];"; common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING"); - s.op->newline() << "c->probe_point = sup->pp;"; + s.op->newline() << "if (sup->spec_index < 0 ||" + << "sup->spec_index >= " << probes.size() << ") return;"; // XXX: should not happen + s.op->newline() << "c->probe_point = sups->pp;"; // XXX: kretprobes saves "c->pi = inst;" too s.op->newline() << "c->regs = regs;"; - s.op->newline() << "(*sup->ph) (c);"; + s.op->newline() << "(*sups->ph) (c);"; common_probe_entryfn_epilogue (s.op); s.op->newline(-1) << "}"; -} -void -uprobe_derived_probe_group::emit_module_init (systemtap_session& s) -{ - if (probes.empty()) return; - s.op->newline() << "/* ---- user probes ---- */"; - s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {"; + // NB: Because these utrace callbacks only occur before / after + // userspace instructions run, there is no concurrency control issue + // between active uprobe callbacks and these registration / + // unregistration pieces. + + // We protect the stap_uprobe->spec_index (which also serves as a + // free/busy flag) value with the outer protective probes_lock + // mutex, to protect it against concurrent registration / + // unregistration. XXX: This should become less naive and let the + // mutex only protect stap_uprobe slot allocation and let the uprobe + // calls occur outside the critical section. + + s.op->newline(); + s.op->newline() << "static int stap_uprobe_change (struct task_struct *tsk, int register_p, unsigned long relocation, struct stap_uprobe_spec *sups) {"; + s.op->newline(1) << "int spec_index = (sups - stap_uprobe_specs);"; + s.op->newline() << "int handled_p = 0;"; + s.op->newline() << "int rc = 0;"; + s.op->newline() << "int i;"; + s.op->newline() << "mutex_lock (& stap_uprobes_lock);"; + + s.op->newline() << "#ifdef DEBUG_UPROBES"; + s.op->newline() << "printk (KERN_WARNING \"uprobe idx %d change pid %d register_p %d reloc %p pp %s\\n\", spec_index, tsk->tgid, register_p, (void*) relocation, sups->pp);"; + s.op->newline() << "#endif"; + + s.op->newline() << "for (i=0; i<NUMUPROBES; i++) {"; // XXX: slow linear search s.op->newline(1) << "struct stap_uprobe *sup = & stap_uprobes[i];"; - s.op->newline() << "probe_point = sup->pp;"; - s.op->newline() << "if (sup->return_p) {"; - s.op->newline(1) << "sup->urp.u.pid = sup->process;"; - s.op->newline() << "sup->urp.u.vaddr = sup->address;"; + + // register new uprobe + s.op->newline() << "if (register_p && sup->spec_index < 0) {"; + s.op->newline(1) << "sup->spec_index = spec_index;"; + s.op->newline() << "if (sups->return_p) {"; + s.op->newline(1) << "sup->urp.u.pid = tsk->tgid;"; + s.op->newline() << "sup->urp.u.vaddr = relocation + sups->address;"; s.op->newline() << "sup->urp.handler = &enter_uretprobe_probe;"; + s.op->newline() << "#ifdef DEBUG_UPROBES"; + s.op->newline() << "printk (KERN_WARNING \"uretprobe register pid %d addr %p\\n\", sup->urp.u.pid, (void*) sup->urp.u.vaddr);"; + s.op->newline() << "#endif"; s.op->newline() << "rc = register_uretprobe (& sup->urp);"; s.op->newline(-1) << "} else {"; - s.op->newline(1) << "sup->up.pid = sup->process;"; - s.op->newline() << "sup->up.vaddr = sup->address;"; + s.op->newline(1) << "sup->up.pid = tsk->tgid;"; + s.op->newline() << "sup->up.vaddr = relocation + sups->address;"; s.op->newline() << "sup->up.handler = &enter_uprobe_probe;"; + s.op->newline() << "#ifdef DEBUG_UPROBES"; + s.op->newline() << "printk (KERN_WARNING \"uprobe register pid %d addr %p\\n\", sup->up.pid, (void*) sup->up.vaddr);"; + s.op->newline() << "#endif"; s.op->newline() << "rc = register_uprobe (& sup->up);"; s.op->newline(-1) << "}"; - s.op->newline() << "if (rc) {"; - s.op->newline(1) << "for (j=i-1; j>=0; j--) {"; // partial rollback - s.op->newline(1) << "struct stap_uprobe *sup2 = & stap_uprobes[j];"; - s.op->newline() << "if (sup2->return_p) unregister_uretprobe (²->urp);"; - s.op->newline() << "else unregister_uprobe (²->up);"; - // NB: we don't have to clear sup2->registered_p, since the module_exit code is - // not run for this early-abort case. + + s.op->newline() << "if (rc) {"; // failed to register + s.op->newline(1) << "printk (KERN_WARNING \"uprobe failed pid %d addr %p rc %d\\n\", tsk->tgid, (void*)(relocation + sups->address), rc);"; + s.op->newline() << "sup->spec_index = -1;"; + s.op->newline(-1) << "} else {"; + s.op->newline(1) << "handled_p = 1;"; // success s.op->newline(-1) << "}"; - s.op->newline() << "break;"; // don't attempt to register any more probes + s.op->newline() << "break;"; // exit free slot search whether or not handled_p + + // unregister old uprobe + s.op->newline(-1) << "} else if (!register_p && " + << "sup->spec_index == spec_index && " // a u[ret]probe set up for this probe point + << "((sups->return_p && sup->urp.u.pid == tsk->tgid && sup->urp.u.vaddr == relocation + sups->address) ||" // dying uretprobe + << "(!sups->return_p && sup->up.pid == tsk->tgid && sup->up.vaddr == relocation + sups->address))) {"; // dying uprobe + s.op->newline() << "if (sups->return_p) {"; + s.op->newline(1) << "#ifdef DEBUG_UPROBES"; + s.op->newline() << "printk (KERN_WARNING \"uretprobe unregister pid %d addr %p\\n\", sup->up.pid, (void*) sup->up.vaddr);"; + s.op->newline() << "#endif"; + s.op->newline() << "unregister_uretprobe (& sup->urp);"; + s.op->newline(-1) << "} else {"; + s.op->newline(1) << "#ifdef DEBUG_UPROBES"; + s.op->newline() << "printk (KERN_WARNING \"uprobe unregister pid %d addr %p\\n\", sup->urp.u.pid, (void*) sup->urp.u.vaddr);"; + s.op->newline() << "#endif"; + s.op->newline() << "unregister_uprobe (& sup->up);"; + s.op->newline(-1) << "}"; + s.op->newline(1) << "sup->spec_index = -1;"; + s.op->newline() << "handled_p = 1;"; + s.op->newline() << "break;"; // exit to-free slot search + s.op->newline(-1) << "}"; // if/else + + s.op->newline(-1) << "}"; // stap_uprobes[] loop + s.op->newline() << "mutex_unlock (& stap_uprobes_lock);"; + s.op->newline() << "if (! handled_p) {"; + s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {"; + s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);"; + s.op->newline() << "_stp_exit ();"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; + + s.op->newline() << "return 0;"; // XXX: or rc? s.op->newline(-1) << "}"; - s.op->newline() << "else sup->registered_p = 1;"; + s.op->assert_0_indent(); + + + // The task_finder_callback we use for ET_EXEC targets. + s.op->newline(); + s.op->newline() << "static int stap_uprobe_process_found (struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {"; + + s.op->newline(1) << "struct stap_uprobe_spec *sups = container_of(tgt, struct stap_uprobe_spec, finder);"; + s.op->newline() << "if (! process_p) return 0;"; + s.op->newline(0) << "return stap_uprobe_change (tsk, register_p, 0, sups);"; s.op->newline(-1) << "}"; + + // The task_finder_vm_callback we use for ET_DYN targets. + s.op->newline(); + s.op->newline() << "static int stap_uprobe_vmchange_found (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) {"; + s.op->newline(1) << "struct stap_uprobe_spec *sups = container_of(tgt, struct stap_uprobe_spec, finder);"; + // 1 - shared libraries' executable segments load from offset 0 - ld.so convention + s.op->newline() << "if (vm_pgoff != 0) return 0;"; + // 2 - the shared library we're interested in + s.op->newline() << "if (vm_path == NULL || strcmp (vm_path, sups->pathname)) return 0;"; + // 3 - probe address within the mapping limits; test should not fail + s.op->newline() << "if (vm_end <= vm_start + sups->address) return 0;"; + + s.op->newline() << "#ifdef DEBUG_TASK_FINDER_VMA"; + s.op->newline() << "printk (KERN_INFO \"vmchange pid %d map_p %d path %s vms %p vme %p vmp %p\\n\", tsk->tgid, map_p, vm_path, (void*) vm_start, (void*) vm_end, (void*) vm_pgoff);"; + s.op->newline() << "printk (KERN_INFO \"sups %p pp %s path %s address %p\\n\", sups, sups->pp, sups->pathname ?: \"\", (void*) sups->address);"; + s.op->newline() << "#endif"; + + s.op->newline(0) << "return stap_uprobe_change (tsk, map_p, vm_start, sups);"; + s.op->newline(-1) << "}"; + s.op->assert_0_indent(); + + + s.op->newline(); } void -uprobe_derived_probe_group::emit_module_exit (systemtap_session& s) +uprobe_derived_probe_group::emit_module_init (systemtap_session& s) { if (probes.empty()) return; s.op->newline() << "/* ---- user probes ---- */"; + s.op->newline() << "for (j=0; j<NUMUPROBES; j++) {"; + s.op->newline(1) << "struct stap_uprobe *sup = & stap_uprobes[j];"; + s.op->newline() << "sup->spec_index = -1;"; // free slot + s.op->newline(-1) << "}"; + s.op->newline() << "mutex_init (& stap_uprobes_lock);"; + s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {"; - s.op->newline(1) << "struct stap_uprobe *sup = & stap_uprobes[i];"; - s.op->newline() << "if (! sup->registered_p) continue;"; - // s.op->newline() << "atomic_add (sdp->u.krp.nmissed, & skipped_count);"; - // s.op->newline() << "atomic_add (sdp->u.krp.kp.nmissed, & skipped_count);"; - s.op->newline() << "if (sup->return_p) unregister_uretprobe (&sup->urp);"; - s.op->newline() << "else unregister_uprobe (&sup->up);"; - s.op->newline() << "sup->registered_p = 0;"; + s.op->newline(1) << "struct stap_uprobe_spec *sups = & stap_uprobe_specs[i];"; + s.op->newline() << "probe_point = sups->pp;"; // for error messages + s.op->newline() << "if (sups->finder.pathname) sups->finder.callback = & stap_uprobe_process_found;"; + s.op->newline() << "else if (sups->pathname) sups->finder.vm_callback = & stap_uprobe_vmchange_found;"; + s.op->newline() << "rc = stap_register_task_finder_target (& sups->finder);"; + + // NB: if (rc), there is no need (XXX: nor any way) to clean up any + // finders already registered, since mere registration does not + // cause any utrace or memory allocation actions. That happens only + // later, once the task finder engine starts running. So, for a + // partial initialization requiring unwind, we need do nothing. + s.op->newline() << "if (rc) break;"; + s.op->newline(-1) << "}"; } +void +uprobe_derived_probe_group::emit_module_exit (systemtap_session& s) +{ + if (probes.empty()) return; + s.op->newline() << "/* ---- user probes ---- */"; + + // NB: there is no stap_unregister_task_finder_target call; + // important stuff like utrace cleanups are done by + // __stp_task_finder_cleanup() + + s.op->newline() << "for (j=0; j<NUMUPROBES; j++) {"; + s.op->newline(1) << "struct stap_uprobe *sup = & stap_uprobes[j];"; + s.op->newline() << "struct stap_uprobe_spec *sups = &stap_uprobe_specs [sup->spec_index];"; + s.op->newline() << "if (sup->spec_index < 0) continue;"; // free slot + + s.op->newline() << "if (sups->return_p) {"; + s.op->newline(1) << "#ifdef DEBUG_UPROBES"; + s.op->newline() << "printk (KERN_WARNING \"uretprobe unregister2 index %d pid %d addr %p\\n\", sup->spec_index, sup->up.pid, (void*) sup->up.vaddr);"; + s.op->newline() << "#endif"; + s.op->newline() << "unregister_uretprobe (& sup->urp);"; + s.op->newline(-1) << "} else {"; + s.op->newline(1) << "#ifdef DEBUG_UPROBES"; + s.op->newline() << "printk (KERN_WARNING \"uprobe unregister2 index %d pid %d addr %p\\n\", sup->spec_index, sup->urp.u.pid, (void*) sup->urp.u.vaddr);"; + s.op->newline() << "#endif"; + s.op->newline() << "unregister_uprobe (& sup->up);"; + s.op->newline(-1) << "}"; + + s.op->newline() << "sup->spec_index = -1;"; + + // XXX: uprobe missed counts? + + s.op->newline(-1) << "}"; + + s.op->newline() << "mutex_destroy (& stap_uprobes_lock);"; +} + + // ------------------------------------------------------------------------ // timer derived probes @@ -8866,7 +9143,7 @@ register_standard_tapsets(systemtap_session & s) s.pattern_root->bind("perfmon")->bind_str("counter") ->bind(new perfmon_builder()); - // dwarf-based kernel/module parts + // dwarf-based kprobe/uprobe parts dwarf_derived_probe::register_patterns(s.pattern_root); // XXX: user-space starter set diff --git a/translate.cxx b/translate.cxx index 57dfe4c3..2fe33314 100644 --- a/translate.cxx +++ b/translate.cxx @@ -4429,7 +4429,13 @@ dump_unwindsyms (Dwfl_Module *m, else if (n > 0) { assert (secname != NULL); - // secname adequately set + // secname adequately set + + // NB: it may be an empty string for ET_DYN objects + // like shared libraries, as their relocation base + // is implicit. + if (secname[0] == '\0') + secname = ".dynamic"; } else { @@ -4507,7 +4513,7 @@ emit_symbol_data (systemtap_session& s) unwindsym_dump_context ctx = { s, kallsyms_out, 0, s.unwindsym_modules }; - // XXX: copied from tapsets.cxx, sadly + // XXX: copied from tapsets.cxx dwflpp::, sadly static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug:build"; static char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH"); @@ -4546,6 +4552,7 @@ emit_symbol_data (systemtap_session& s) // ---- step 2: process any user modules (files) listed + // XXX: see dwflpp::setup_user. static const Dwfl_Callbacks user_callbacks = { NULL, /* dwfl_linux_kernel_find_elf, */ @@ -154,50 +154,69 @@ tokenize(const string& str, vector<string>& tokens, } -// Find an executable by name in $PATH. -bool -find_executable(const char *name, string& retpath) +// Resolve an executable name to a canonical full path name, with the +// same policy as execvp(). A program name not containing a slash +// will be searched along the $PATH. + +string find_executable(const string& name) { - const char *p; - string path; - vector<string> dirs; - struct stat st1, st2; + string retpath; + + if (name.size() == 0) + return name; + + struct stat st; - if (*name == '/') + if (name.find('/') != string::npos) // slash in the path already? { retpath = name; - return true; + } + else // Nope, search $PATH. + { + char *path = getenv("PATH"); + if (path) + { + // Split PATH up. + vector<string> dirs; + tokenize(string(path), dirs, string(":")); + + // Search the path looking for the first executable of the right name. + for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); i++) + { + string fname = *i + "/" + name; + const char *f = fname.c_str(); + + // Look for a normal executable file. + if (access(f, X_OK) == 0 + && stat(f, &st) == 0 + && S_ISREG(st.st_mode)) + { + retpath = fname; + break; + } + } + } } - p = getenv("PATH"); - if (!p) - return false; - path = p; - // Split PATH up. - tokenize(path, dirs, string(":")); + // Could not find the program on the $PATH. We'll just fall back to + // the unqualified name, which our caller will probably fail with. + if (retpath == "") + retpath = name; - // Search the path looking for the first executable of the right name. - for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); i++) + // Canonicalize the path name. + char *cf = canonicalize_file_name (retpath.c_str()); + if (cf) { - string fname = *i + "/" + name; - const char *f = fname.c_str(); - - // Look for a normal executable file. - if (access(f, X_OK) == 0 - && lstat(f, &st1) == 0 - && stat(f, &st2) == 0 - && S_ISREG(st2.st_mode)) - { - // Found it! - retpath = fname; - return true; - } + retpath = string(cf); + free (cf); } - return false; + return retpath; } + + const string cmdstr_quoted(const string& cmd) { // original cmd : substr1 @@ -10,7 +10,7 @@ int copy_file(const char *src, const char *dest); int create_dir(const char *dir); void tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters); -bool find_executable(const char *name, std::string& retpath); +std::string find_executable(const std::string& name); const std::string cmdstr_quoted(const std::string& cmd); |