diff options
-rwxr-xr-x | dtrace.in | 19 | ||||
-rw-r--r-- | includes/sys/sdt.h | 2 | ||||
-rw-r--r-- | tapset-utrace.cxx | 102 | ||||
-rw-r--r-- | tapsets.cxx | 179 | ||||
-rw-r--r-- | testsuite/lib/systemtap.exp | 4 | ||||
-rw-r--r-- | testsuite/systemtap.base/dtrace.exp | 72 | ||||
-rw-r--r-- | testsuite/systemtap.base/sdt_misc.exp | 101 | ||||
-rw-r--r-- | util.cxx | 4 | ||||
-rw-r--r-- | util.h | 3 |
9 files changed, 344 insertions, 142 deletions
@@ -46,7 +46,7 @@ class provider: self.f = open(provider) self.h = open(header,mode='w') self.h.write("/* Generated by the Systemtap dtrace wrapper */\n") - # self.h.write("\n#define STAP_HAS_SEMAPHORES 1\n\n") + self.h.write("\n#define STAP_HAS_SEMAPHORES 1\n\n") self.h.write("\n#include <sys/sdt.h>\n\n") in_comment = False typedefs = "" @@ -107,7 +107,7 @@ class provider: i += 1 self.h.write ('/* %s (%s) */\n' % (this_probe_canon,args_string)) # XXX Enable this when .so semaphores work properly - self.h.write ('#define %s_ENABLED() 1 /*%s_semaphore*/\n' % (this_probe_canon,this_probe)) + self.h.write ('#define %s_ENABLED() %s_semaphore\n' % (this_probe_canon,this_probe)) # NB: unsigned short is fixed in ABI self.h.write ("__extension__ extern unsigned short %s_semaphore __attribute__ ((unused)) __attribute__ ((section (\".probes\")));\n" % (this_probe)) self.h.write (define_str + ") \\\n") @@ -120,13 +120,15 @@ class provider: def usage (): - print "Usage " + sys.argv[0] + " [--help] [-h | -G] -s File.d [-o File]" + print "Usage " + sys.argv[0] + " [--help] [-h | -G] [-C [-I<Path>]] -s File.d [-o <File>]" def help (): usage() print "Where -h builds a systemtap header file from the .d file" + print " -C when used with -h, also run cpp preprocessor" print " -o specifies an explicit output file name," - print " The default for -G is file.o and -h is file.h" + print " the default for -G is file.o and -h is file.h" + print " -I when running cpp pass through this -I include Path" print " -s specifies the name of the .d input file" print " -G builds a stub file.o from file.d," print " which is required by some packages that use dtrace." @@ -159,6 +161,8 @@ keep_temps = False use_cpp = False h_ext = '.h' filename = "" +s_filename = "" +includes = [] while (i < len (sys.argv)): if (sys.argv[i] == "-o"): i += 1 @@ -170,6 +174,8 @@ while (i < len (sys.argv)): use_cpp = True elif (sys.argv[i] == "-h"): build_header = True + elif (sys.argv[i].startswith("-I")): + includes.append(sys.argv[i]) elif (sys.argv[i] == "-G"): build_source = True elif (sys.argv[i] == "-k"): @@ -185,9 +191,10 @@ if (build_header == False and build_source == False): if (s_filename != "" and use_cpp): (d,fn) = mkstemp(suffix=".d") - retcode = call(["cpp", s_filename, fn]) + args = ['cpp'] + includes + [s_filename, fn] + retcode = call(args) if (retcode != 0): - print "\"cpp s_filename\" failed" + print "\"cpp includes s_filename\" failed" usage() sys.exit(1) s_filename = fn diff --git a/includes/sys/sdt.h b/includes/sys/sdt.h index 3847c497..d85d8e2e 100644 --- a/includes/sys/sdt.h +++ b/includes/sys/sdt.h @@ -40,7 +40,7 @@ #define STAP_PROBE_DATA(probe, guard, arg) \ STAP_PROBE_DATA_(#probe,guard,arg) -#if defined STAP_HAS_SEMAPHORES && defined EXPERIMENTAL_UTRACE_SDT +#if defined STAP_HAS_SEMAPHORES && ! defined EXPERIMENTAL_KPROBE_SDT #define STAP_SEMAPHORE(probe) \ if (__builtin_expect ( probe ## _semaphore , 0)) #else diff --git a/tapset-utrace.cxx b/tapset-utrace.cxx index 795d88b7..bd668a2b 100644 --- a/tapset-utrace.cxx +++ b/tapset-utrace.cxx @@ -29,6 +29,7 @@ static const string TOK_END("end"); static const string TOK_THREAD("thread"); static const string TOK_SYSCALL("syscall"); static const string TOK_RETURN("return"); +static const string TOK_LIBRARY("library"); // ------------------------------------------------------------------------ @@ -52,13 +53,15 @@ struct utrace_derived_probe: public derived_probe { bool has_path; string path; + bool has_library; + string library; int64_t pid; enum utrace_derived_probe_flags flags; bool target_symbol_seen; utrace_derived_probe (systemtap_session &s, probe* p, probe_point* l, - bool hp, string &pn, int64_t pd, - enum utrace_derived_probe_flags f); + bool hp, string &pn, bool hl, string &ln, + int64_t pd, enum utrace_derived_probe_flags f); void join_group (systemtap_session& s); void emit_unprivileged_assertion (translator_output*); @@ -115,10 +118,10 @@ struct utrace_var_expanding_visitor: public var_expanding_visitor 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): + bool hp, string &pn, bool hl, string &ln, + int64_t pd, enum utrace_derived_probe_flags f): derived_probe (p, new probe_point (*l) /* .components soon rewritten */ ), - has_path(hp), path(pn), pid(pd), flags(f), + has_path(hp), path(pn), has_library(hl), library(ln), pid(pd), flags(f), target_symbol_seen(false) { if (s.kernel_config["CONFIG_UTRACE"] != string("y")) @@ -599,9 +602,11 @@ struct utrace_builder: public derived_probe_builder vector<derived_probe *> & finished_results) { string path; + string lib; int64_t pid; bool has_path = get_param (parameters, TOK_PROCESS, path); + bool has_lib = get_param (parameters, TOK_LIBRARY, lib); bool has_pid = get_param (parameters, TOK_PROCESS, pid); enum utrace_derived_probe_flags flags = UDPF_NONE; @@ -647,9 +652,11 @@ struct utrace_builder: public derived_probe_builder // XXX: could we use /proc/$pid/exe in unwindsym_modules and elsewhere? } + if (has_lib) + lib = find_executable (lib, "LD_LIBRARY_PATH"); finished_results.push_back(new utrace_derived_probe(sess, base, location, - has_path, path, pid, + has_path, path, has_lib, lib, pid, flags)); } @@ -750,11 +757,25 @@ utrace_derived_probe_group::emit_probe_decl (systemtap_session& s, s.op->line() << " .engine_attached=0,"; if (p->sdt_semaphore_addr != 0) - s.op->line() << " .sdt_sem_address=(unsigned long)0x" + s.op->line() << " .sdt_sem_offset=(unsigned long)0x" << hex << p->sdt_semaphore_addr << dec << "ULL,"; - + s.op->line() << " .sdt_sem_address=0,"; s.op->line() << " .tsk=0,"; s.op->line() << " },"; + + if (p->has_library && p->sdt_semaphore_addr != 0) + { + s.op->newline() << "{"; + s.op->line() << " .tgt={"; + s.op->line() << " .procname=\"" << p->path << "\","; + s.op->line() << " .mmap_callback=&stap_utrace_mmap_found,"; + s.op->line() << " },"; + s.op->line() << " .pathname=\"" << p->library << "\","; + s.op->line() << " .sdt_sem_offset=(unsigned long)0x" + << hex << p->sdt_semaphore_addr << dec << "ULL,"; + s.op->line() << " .sdt_sem_address=0,"; + s.op->line() << " },"; + } } @@ -789,7 +810,11 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "struct utrace_engine_ops ops;"; s.op->newline() << "unsigned long events;"; s.op->newline() << "struct task_struct *tsk;"; + s.op->newline() << "unsigned long sdt_sem_offset;"; + // FIXME: if this probe is attached to more than 1 task, having 1 + // address here won't work s.op->newline() << "unsigned long sdt_sem_address;"; + s.op->newline(0) << "const char *pathname;"; s.op->newline(-1) << "};"; @@ -913,15 +938,23 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->indent(-1); s.op->newline(-1) << "}"; + s.op->newline() << "if (p->sdt_sem_offset && p->sdt_sem_address == 0) {"; + s.op->indent(1); + // If the probe is in the executable itself, the offset *is* the address. + s.op->newline() << "p->sdt_sem_address = p->sdt_sem_offset;"; + s.op->newline(-1) << "}"; + + // Before writing to the semaphore, we need to check for VM_WRITE access. s.op->newline() << "if (p->sdt_sem_address != 0) {"; s.op->newline(1) << "size_t sdt_semaphore;"; // XXX p could get registered to more than one task! s.op->newline() << "p->tsk = tsk;"; - s.op->newline() << "__access_process_vm (tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0);"; - s.op->newline() << "sdt_semaphore += 1;"; + + s.op->newline() << "if (__access_process_vm (tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0) == sizeof (sdt_semaphore)) {"; + s.op->newline(1) << "sdt_semaphore ++;"; s.op->newline() << "__access_process_vm (tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 1);"; s.op->newline(-1) << "}"; - + s.op->newline(-1) << "}"; s.op->newline(-1) << "}"; // Since this engine could be attached to multiple threads, don't @@ -985,6 +1018,44 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "return rc;"; s.op->newline(-1) << "}"; + // The task_finder_mmap_callback + s.op->newline() << "static int stap_utrace_mmap_found (struct stap_task_finder_target *tgt, struct task_struct *tsk, char *path, unsigned long addr, unsigned long length, unsigned long offset, unsigned long vm_flags) {"; + s.op->newline(1) << "struct stap_utrace_probe *p = container_of(tgt, struct stap_utrace_probe, tgt);"; + s.op->newline() << "int rc = 0;"; + // the shared library we're interested in + s.op->newline() << "if (path == NULL || strcmp (path, p->pathname)) return 0;"; + s.op->newline() << "if (p->sdt_sem_address == 0) {"; + s.op->indent(1); + // If the probe is in the executable itself, the offset *is* the + // address. + s.op->newline() << "if (vm_flags & VM_EXECUTABLE) {"; + s.op->indent(1); + s.op->newline() << "p->sdt_sem_address = addr + p->sdt_sem_offset;"; + s.op->newline(-1) << "}"; + // If the probe is in a .so, we have to calculate the address. + s.op->newline() << "else {"; + s.op->indent(1); + s.op->newline() << "p->sdt_sem_address = (addr - offset) + p->sdt_sem_offset;"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; + + s.op->newline() << "if (p->sdt_sem_address) {"; + s.op->newline(1) << "unsigned short sdt_semaphore = 0;"; // NB: fixed size + s.op->newline() << "if (__access_process_vm (tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0) == sizeof (sdt_semaphore)) {"; + + s.op->newline(1) << "if (vm_flags & VM_WRITE) {"; + s.op->indent(1); + s.op->newline() << "sdt_semaphore ++;"; + s.op->newline() << "#ifdef DEBUG_UTRACE"; + s.op->newline() << "_stp_dbug (__FUNCTION__,__LINE__, \"+semaphore %#x @ %#lx\\n\", sdt_semaphore, p->sdt_sem_address);"; + s.op->newline() << "#endif"; + s.op->newline() << "__access_process_vm (tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 1);"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; + s.op->newline() << "return 0;"; + s.op->newline(-1) << "}"; + s.op->newline() << "static struct stap_utrace_probe stap_utrace_probes[] = {"; s.op->indent(1); @@ -1055,14 +1126,15 @@ utrace_derived_probe_group::emit_module_exit (systemtap_session& s) s.op->newline() << "if (p->engine_attached) {"; s.op->newline(1) << "stap_utrace_detach_ops(&p->ops);"; + // Before writing to the semaphore, we need to check for VM_WRITE access. s.op->newline() << "if (p->sdt_sem_address) {"; s.op->newline(1) << "size_t sdt_semaphore;"; // XXX p could get registered to more than one task! - s.op->newline() << "__access_process_vm (p->tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0);"; - s.op->newline() << "sdt_semaphore -= 1;"; + s.op->newline() << "if (__access_process_vm (p->tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0) == sizeof (sdt_semaphore)) {"; + s.op->newline(1) << "sdt_semaphore --;"; s.op->newline() << "__access_process_vm (p->tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 1);"; s.op->newline(-1) << "}"; - + s.op->newline(-1) << "}"; s.op->newline(-1) << "}"; s.op->newline(-1) << "}"; @@ -1094,6 +1166,8 @@ register_tapset_utrace(systemtap_session& s) ->bind(builder); roots[i]->bind(TOK_SYSCALL)->bind(TOK_RETURN) ->bind(builder); + roots[i]->bind_str(TOK_LIBRARY)->bind(TOK_SYSCALL) + ->bind(builder); } } diff --git a/tapsets.cxx b/tapsets.cxx index 51f1ccc9..bad72091 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -260,6 +260,7 @@ static const string TOK_PROCESS("process"); static const string TOK_MARK("mark"); static const string TOK_TRACE("trace"); static const string TOK_LABEL("label"); +static const string TOK_LIBRARY("library"); static int query_cu (Dwarf_Die * cudie, void * arg); static void query_addr(Dwarf_Addr addr, dwarf_query *q); @@ -349,8 +350,10 @@ struct dwarf_derived_probe: public derived_probe string module; string section; Dwarf_Addr addr; + string path; bool has_return; bool has_maxactive; + bool has_library; long maxactive_val; bool access_vars; @@ -460,7 +463,9 @@ struct base_query bool has_kernel; bool has_module; bool has_process; + bool has_library; string module_val; // has_kernel => module_val = "kernel" + string path; // executable path if module is a .so virtual void handle_query_module() = 0; }; @@ -478,8 +483,15 @@ base_query::base_query(dwflpp & dw, literal_map_t const & params): has_process = false; else { + string library_name; has_process = get_string_param(params, TOK_PROCESS, module_val); - if (has_process) + has_library = get_string_param (params, TOK_LIBRARY, library_name); + if (has_library) + { + path = find_executable (module_val); + module_val = find_executable (library_name, "LD_LIBRARY_PATH"); + } + else if (has_process) module_val = find_executable (module_val); } @@ -2840,11 +2852,13 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname, Dwarf_Die* scope_die /* may be null */) : derived_probe (q.base_probe, new probe_point(*q.base_loc) /* .components soon rewritten */ ), module (module), section (section), addr (addr), + path (q.path), has_return (q.has_return), has_maxactive (q.has_maxactive), + has_library (q.has_library), maxactive_val (q.maxactive_val), access_vars(false), - saved_longs(0), saved_strings(0), + saved_longs(0), saved_strings(0), entry_handler(0) { if (q.has_process) @@ -3140,6 +3154,8 @@ dwarf_derived_probe::register_patterns(systemtap_session& s) register_function_and_statement_variants(root->bind_str(TOK_PROCESS), dw); root->bind_str(TOK_PROCESS)->bind_str(TOK_FUNCTION)->bind_str(TOK_LABEL) ->bind(dw); + root->bind_str(TOK_PROCESS)->bind_str(TOK_LIBRARY)->bind_str(TOK_MARK) + ->bind(dw); root->bind_str(TOK_PROCESS)->bind_str(TOK_MARK) ->bind(dw); root->bind_str(TOK_PROCESS)->bind_num(TOK_MARK) @@ -3983,46 +3999,52 @@ sdt_query::convert_probe (probe *base) void sdt_query::convert_location (probe *base, probe_point *location) { - switch (probe_type) - { - case uprobe_type: - if (sess.verbose > 3) - clog << "probe_type == uprobe_type, use statement addr: 0x" - << hex << probe_arg << dec << endl; - // process("executable").statement(probe_arg) - location->components[1]->functor = TOK_STATEMENT; - location->components[1]->arg = new literal_number(probe_arg); - break; + for (unsigned i = 0; i < location->components.size(); ++i) + if (location->components[i]->functor == TOK_MARK) + switch (probe_type) + { + case uprobe_type: + if (sess.verbose > 3) + clog << "probe_type == uprobe_type, use statement addr: 0x" + << hex << probe_arg << dec << endl; + // process("executable").statement(probe_arg) + location->components[i]->functor = TOK_STATEMENT; + location->components[i]->arg = new literal_number(probe_arg); + break; - case kprobe_type: - if (sess.verbose > 3) - clog << "probe_type == kprobe_type" << endl; - // kernel.function("*getegid*") - location->components[0]->functor = TOK_KERNEL; - location->components[0]->arg = NULL; - location->components[1]->functor = TOK_FUNCTION; - location->components[1]->arg = new literal_string("*getegid*"); - break; + case kprobe_type: + if (sess.verbose > 3) + clog << "probe_type == kprobe_type" << endl; + // kernel.function("*getegid*") + location->components[i]->functor = TOK_FUNCTION; + location->components[i]->arg = new literal_string("*getegid*"); + break; - case utrace_type: - if (sess.verbose > 3) - clog << "probe_type == utrace_type" << endl; - // process("executable").syscall - location->components[1]->functor = "syscall"; - location->components[1]->arg = NULL; - break; + case utrace_type: + if (sess.verbose > 3) + clog << "probe_type == utrace_type" << endl; + // process("executable").syscall + location->components[i]->functor = "syscall"; + location->components[i]->arg = NULL; + break; - default: - if (sess.verbose > 3) - clog << "probe_type == use_uprobe_no_dwarf, use label name: " - << "_stapprobe1_" << mark_name << endl; - // process("executable").function("*").label("_stapprobe1_MARK_NAME") - location->components[1]->functor = TOK_FUNCTION; - location->components[1]->arg = new literal_string("*"); - location->components.push_back(new probe_point::component(TOK_LABEL)); - location->components[2]->arg = new literal_string("_stapprobe1_" + mark_name); - break; - } + default: + if (sess.verbose > 3) + clog << "probe_type == use_uprobe_no_dwarf, use label name: " + << "_stapprobe1_" << mark_name << endl; + // process("executable").function("*").label("_stapprobe1_MARK_NAME") + location->components[1]->functor = TOK_FUNCTION; + location->components[1]->arg = new literal_string("*"); + location->components.push_back(new probe_point::component(TOK_LABEL)); + location->components[2]->arg = new literal_string("_stapprobe1_" + mark_name); + break; + } + else if (location->components[i]->functor == TOK_PROCESS + && probe_type == kprobe_type) + { + location->components[i]->functor = TOK_KERNEL; + location->components[i]->arg = NULL; + } base->locations.clear(); base->locations.push_back(location); @@ -4053,7 +4075,11 @@ dwarf_builder::build(systemtap_session & sess, } else if (get_param (parameters, TOK_PROCESS, module_name)) { - module_name = find_executable (module_name); // canonicalize it + string library_name; + if (get_param (parameters, TOK_LIBRARY, library_name)) + module_name = find_executable (library_name, "LD_LIBRARY_PATH"); + else + module_name = find_executable (module_name); // canonicalize it if (sess.kernel_config["CONFIG_UTRACE"] != string("y")) throw semantic_error ("process probes not available without kernel CONFIG_UTRACE"); @@ -4557,7 +4583,6 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) // too big to embed in the initialized .data stap_uprobe_spec array. s.op->newline() << "static struct stap_uprobe {"; s.op->newline(1) << "union { struct uprobe up; struct uretprobe urp; };"; - s.op->newline() << "unsigned long sdt_sem_address;"; // relocated to this process s.op->newline() << "int spec_index;"; // index into stap_uprobe_specs; <0 == free && unregistered s.op->newline(-1) << "} stap_uprobes [MAXUPROBES];"; // XXX: consider a slab cache or somesuch s.op->newline() << "DEFINE_MUTEX(stap_uprobes_lock);"; // protects against concurrent registration/unregistration @@ -4600,6 +4625,8 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) } if (p->section != ".absolute") // ET_DYN { + if (p->has_library && p->sdt_semaphore_addr != 0) + s.op->line() << " .procname=\"" << p->path << "\", "; s.op->line() << " .mmap_callback=&stap_uprobe_mmap_found, "; s.op->line() << " .munmap_callback=&stap_uprobe_munmap_found, "; } @@ -4615,13 +4642,17 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->assert_0_indent(); + s.op->newline() << "unsigned long sdt_sem_address [] = {"; + for (unsigned i =0; i<probes.size(); i++) + s.op->line() << "0,"; + s.op->line() << "0};"; s.op->newline() << "static const struct stap_uprobe_spec {"; // NB: read-only structure s.op->newline(1) << "unsigned tfi;"; // index into stap_uprobe_finders[] s.op->newline() << "unsigned return_p:1;"; s.op->newline() << "unsigned long address;"; s.op->newline() << "const char *pp;"; s.op->newline() << "void (*ph) (struct context*);"; - s.op->newline() << "unsigned long sdt_sem_address;"; + s.op->newline() << "unsigned long sdt_sem_offset;"; s.op->newline(-1) << "} stap_uprobe_specs [] = {"; // NB: read-only structure s.op->indent(1); for (unsigned i =0; i<probes.size(); i++) @@ -4637,7 +4668,7 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->line() << " .ph=&" << p->name << ","; if (p->sdt_semaphore_addr != 0) - s.op->line() << " .sdt_sem_address=(unsigned long)0x" + s.op->line() << " .sdt_sem_offset=(unsigned long)0x" << hex << p->sdt_semaphore_addr << dec << "ULL,"; if (p->has_return) @@ -4706,7 +4737,7 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) // unregistration. s.op->newline(); - s.op->newline() << "static int stap_uprobe_change_plus (struct task_struct *tsk, unsigned long relocation, unsigned long length, const struct stap_uprobe_tf *stf) {"; + s.op->newline() << "static int stap_uprobe_change_plus (struct task_struct *tsk, unsigned long relocation, unsigned long length, const struct stap_uprobe_tf *stf, unsigned long offset, unsigned long vm_flags) {"; s.op->newline(1) << "int tfi = (stf - stap_uprobe_finders);"; s.op->newline() << "int spec_index;"; @@ -4757,6 +4788,20 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) // was full (registration; MAXUPROBES) or that no matching entry was // found (unregistration; should not happen). + s.op->newline() << "if (sups->sdt_sem_offset && sdt_sem_address[spec_index] == 0) {"; + s.op->indent(1); + // If the probe is in the executable itself, the offset *is* the address. + s.op->newline() << "if (vm_flags & VM_EXECUTABLE) {"; + s.op->indent(1); + s.op->newline() << "sdt_sem_address[spec_index] = relocation + sups->sdt_sem_offset;"; + s.op->newline(-1) << "}"; + // If the probe is in a .so, we have to calculate the address. + s.op->newline() << "else {"; + s.op->indent(1); + s.op->newline() << "sdt_sem_address[spec_index] = (relocation - offset) + sups->sdt_sem_offset;"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; // sdt_sem_offset + s.op->newline() << "if (slotted_p) {"; s.op->newline(1) << "struct stap_uprobe *sup = & stap_uprobes[i];"; s.op->newline() << "if (sups->return_p) {"; @@ -4770,23 +4815,23 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "sup->up.handler = &enter_uprobe_probe;"; s.op->newline() << "rc = register_uprobe (& sup->up);"; - // PR10655: also handle ENABLED semaphore manipulations here - s.op->newline() << "if (!rc && sups->sdt_sem_address) {"; - s.op->newline(1) << "unsigned short sdt_semaphore;"; // NB: fixed size - s.op->newline() << "sup->sdt_sem_address = relocation + sups->sdt_sem_address;"; - s.op->newline() << "(void) __access_process_vm (tsk, sup->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0);"; - s.op->newline() << "sdt_semaphore ++;"; - s.op->newline() << "#ifdef DEBUG_UPROBES"; - s.op->newline() << "_stp_dbug (__FUNCTION__,__LINE__, \"+semaphore %#x @ %#lx\\n\", sdt_semaphore, sup->sdt_sem_address);"; - s.op->newline() << "#endif"; - - s.op->newline() << "(void) __access_process_vm (tsk, sup->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 1);"; + s.op->newline() << "if (sdt_sem_address[spec_index]) {"; + s.op->newline(1) << "unsigned short sdt_semaphore = 0;"; // NB: fixed size + s.op->newline() << "if ( __access_process_vm (tsk, sdt_sem_address[spec_index], &sdt_semaphore, sizeof (sdt_semaphore), 0)) {"; + // We have an executable or a writeable segment of a .so + s.op->newline(1) << "if (vm_flags == 0 || vm_flags & VM_WRITE) {"; + s.op->newline(1) << "sdt_semaphore ++;"; + s.op->newline() << "__access_process_vm (tsk, sdt_sem_address[spec_index], &sdt_semaphore, sizeof (sdt_semaphore), 1);"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; // sdt_sem_address // XXX: error handling in __access_process_vm! // XXX: need to analyze possibility of race condition + s.op->newline(-1) << "}"; s.op->newline(-1) << "}"; - s.op->newline() << "if (rc) {"; // failed to register + + s.op->newline() << "if (rc && !(vm_flags & VM_WRITE)) {"; // failed to register s.op->newline(1) << "_stp_warn (\"u*probe failed %s[%d] '%s' addr %p rc %d\\n\", tsk->comm, tsk->tgid, sups->pp, (void*)(relocation + sups->address), rc);"; // NB: we need to release this slot, so we need to borrow the mutex temporarily. s.op->newline() << "mutex_lock (& stap_uprobes_lock);"; @@ -4919,7 +4964,7 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) // ET_EXEC events are modeled as if shlib events, but with 0 relocation bases s.op->newline() << "if (register_p)"; - s.op->newline(1) << "return stap_uprobe_change_plus (tsk, 0, TASK_SIZE, stf);"; + s.op->newline(1) << "return stap_uprobe_change_plus (tsk, 0, TASK_SIZE, stf, 0, 0);"; s.op->newline(-1) << "else"; s.op->newline(1) << "return stap_uprobe_change_minus (tsk, 0, TASK_SIZE, stf);"; s.op->indent(-1); @@ -4932,18 +4977,19 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "static int stap_uprobe_mmap_found (struct stap_task_finder_target *tgt, struct task_struct *tsk, char *path, unsigned long addr, unsigned long length, unsigned long offset, unsigned long vm_flags) {"; s.op->newline(1) << "const struct stap_uprobe_tf *stf = container_of(tgt, struct stap_uprobe_tf, finder);"; // 1 - shared libraries' executable segments load from offset 0 - ld.so convention - s.op->newline() << "if (offset != 0) return 0;"; + // offset != 0 is now allowed so stap_uprobe_change_plus can set a semaphore, + // i.e. a static extern, in a shared object // 2 - the shared library we're interested in s.op->newline() << "if (path == NULL || strcmp (path, stf->pathname)) return 0;"; - // 3 - mapping should be executable - s.op->newline() << "if (!(vm_flags & VM_EXEC)) return 0;"; + // 3 - mapping should be executable or writeable (for semaphore in .so) + s.op->newline() << "if (!((vm_flags & VM_EXEC) || (vm_flags & VM_WRITE))) return 0;"; s.op->newline() << "#ifdef DEBUG_TASK_FINDER_VMA"; s.op->newline() << "_stp_dbug (__FUNCTION__,__LINE__, \"+mmap pid %d path %s addr %p length %u offset %p stf %p %p path %s\\n\", " << "tsk->tgid, path, (void *) addr, (unsigned)length, (void*) offset, tgt, stf, stf->pathname);"; s.op->newline() << "#endif"; - s.op->newline() << "return stap_uprobe_change_plus (tsk, addr, length, stf);"; + s.op->newline() << "return stap_uprobe_change_plus (tsk, addr, length, stf, offset, vm_flags);"; s.op->newline(-1) << "}"; s.op->assert_0_indent(); @@ -5021,7 +5067,7 @@ uprobe_derived_probe_group::emit_module_exit (systemtap_session& s) s.op->newline() << "if (sup->spec_index < 0) continue;"; // free slot // PR10655: decrement that ENABLED semaphore - s.op->newline() << "if (sups->sdt_sem_address != 0) {"; + s.op->newline() << "if (sdt_sem_address[sup->spec_index]) {"; s.op->newline(1) << "unsigned short sdt_semaphore;"; // NB: fixed size s.op->newline() << "pid_t pid = (sups->return_p ? sup->urp.u.pid : sup->up.pid);"; s.op->newline() << "struct task_struct *tsk;"; @@ -5041,12 +5087,13 @@ uprobe_derived_probe_group::emit_module_exit (systemtap_session& s) s.op->newline() << "#endif /* 2.6.31 */"; s.op->newline() << "if (tsk) {"; // just in case the thing exited while we weren't watching - s.op->newline(1) << "(void) __access_process_vm (tsk, sup->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0);"; - s.op->newline() << "sdt_semaphore --;"; + s.op->newline(1) << "if (__access_process_vm (tsk, sdt_sem_address[sup->spec_index], &sdt_semaphore, sizeof (sdt_semaphore), 0)) {"; + s.op->newline(1) << "sdt_semaphore --;"; s.op->newline() << "#ifdef DEBUG_UPROBES"; - s.op->newline() << "_stp_dbug (__FUNCTION__,__LINE__, \"-semaphore %#x @ %#lx\\n\", sdt_semaphore, sup->sdt_sem_address);"; + s.op->newline() << "_stp_dbug (__FUNCTION__,__LINE__, \"-semaphore %#x @ %#lx\\n\", sdt_semaphore, sdt_sem_address[sup->spec_index]);"; s.op->newline() << "#endif"; - s.op->newline() << "(void) __access_process_vm (tsk, sup->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 1);"; + s.op->newline() << "(void) __access_process_vm (tsk, sdt_sem_address[sup->spec_index], &sdt_semaphore, sizeof (sdt_semaphore), 1);"; + s.op->newline(-1) << "}"; // access_process_vm // XXX: error handling in __access_process_vm! // XXX: need to analyze possibility of race condition s.op->newline(-1) << "}"; diff --git a/testsuite/lib/systemtap.exp b/testsuite/lib/systemtap.exp index f16facc2..13e6d1a2 100644 --- a/testsuite/lib/systemtap.exp +++ b/testsuite/lib/systemtap.exp @@ -175,7 +175,9 @@ proc shutdown_server {} { } # Remove the temporary stap script - exec /bin/rm -fr $net_path + if [file exists $net_path] { + exec /bin/rm -fr $net_path + } } proc get_system_info {} { diff --git a/testsuite/systemtap.base/dtrace.exp b/testsuite/systemtap.base/dtrace.exp index f4cac5aa..f68af4f5 100644 --- a/testsuite/systemtap.base/dtrace.exp +++ b/testsuite/systemtap.base/dtrace.exp @@ -8,7 +8,7 @@ if {[installtest_p]} { set dtrace ../dtrace } -exec mkdir /tmp/dtrace +exec mkdir -p /tmp/dtrace set dpath "/tmp/dtrace/test.d" set fp [open $dpath "w"] @@ -23,11 +23,34 @@ provider tstsyscall " close $fp +exec mkdir -p /tmp/dtrace_inc +set ipath "/tmp/dtrace_inc/dtest.h" +set $fp [open $ipath "w"] +puts $fp " +#define INT16 short +#define INT32 int +" +close $fp + +set idpath "/tmp/dtrace/itest.d" +set $fp [open $idpath "w"] +puts $fp " +#include <dtest.h> + +provider tstsyscall +{ + probe test(INT16 arg1, INT32 arg2, INT32 arg3, INT32 arg4, struct astruct arg5) +} +" +close $fp + +set incpath "/tmp/dtrace_inc" + # ----------------------------------------------------------------- # test command line option and file handling verbose -log "$dtrace -G -s $dpath -o XXX.o" -exec $dtrace -G -s $dpath -o XXX.o +catch {exec $dtrace -G -s $dpath -o XXX.o} if {[file exists XXX.o]} then { pass "dtrace -G -o XXX.o" } else { @@ -36,7 +59,7 @@ if {[file exists XXX.o]} then { exec rm -f XXX.o verbose -log "$dtrace -G -s $dpath -o XXX" -exec $dtrace -G -s $dpath -o XXX +catch {exec $dtrace -G -s $dpath -o XXX} if {[file exists XXX.o]} then { pass "dtrace -G -o XXX" } else { @@ -45,7 +68,7 @@ if {[file exists XXX.o]} then { exec rm -f XXX.o verbose -log "$dtrace -h -s $dpath -o XXX.h" -exec $dtrace -h -s $dpath -o XXX.h +catch {exec $dtrace -h -s $dpath -o XXX.h} if {[file exists XXX.h]} then { pass "dtrace -h -o XXX.h" } else { @@ -54,7 +77,7 @@ if {[file exists XXX.h]} then { exec rm -f XXX.h verbose -log "$dtrace -h -s $dpath -o XXX" -exec $dtrace -h -s $dpath -o XXX +catch {exec $dtrace -h -s $dpath -o XXX} if {[file exists XXX]} then { pass "dtrace -h -o XXX" } else { @@ -63,7 +86,7 @@ if {[file exists XXX]} then { exec rm -f XXX verbose -log "$dtrace -G -s $dpath -o /tmp/XXX.o" -exec $dtrace -G -s $dpath -o /tmp/XXX.o +catch {exec $dtrace -G -s $dpath -o /tmp/XXX.o} if {[file exists /tmp/XXX.o]} then { pass "dtrace -G -o /tmp/XXX.o" } else { @@ -72,7 +95,7 @@ if {[file exists /tmp/XXX.o]} then { exec rm -f /tmp/XXX.o verbose -log "$dtrace -G -s $dpath -o /tmp/XXX" -exec $dtrace -G -s $dpath -o /tmp/XXX +catch {exec $dtrace -G -s $dpath -o /tmp/XXX} if {[file exists /tmp/XXX.o]} then { pass "dtrace -G -o /tmp/XXX.o" } else { @@ -81,7 +104,7 @@ if {[file exists /tmp/XXX.o]} then { exec rm -f /tmp/XXX.o verbose -log "$dtrace -h -s $dpath -o /tmp/XXX.h" -exec $dtrace -h -s $dpath -o /tmp/XXX.h +catch {exec $dtrace -h -s $dpath -o /tmp/XXX.h} if {[file exists /tmp/XXX.h]} then { pass "dtrace -h -o /tmp/XXX.h" } else { @@ -90,7 +113,7 @@ if {[file exists /tmp/XXX.h]} then { exec rm -f /tmp/XXX.h verbose -log "$dtrace -h -s $dpath -o /tmp/XXX" -exec $dtrace -h -s $dpath -o /tmp/XXX +catch {exec $dtrace -h -s $dpath -o /tmp/XXX} if {[file exists /tmp/XXX]} then { pass "dtrace -h -o /tmp/XXX" } else { @@ -99,7 +122,7 @@ if {[file exists /tmp/XXX]} then { exec rm -f /tmp/XXX verbose -log "$dtrace -G -s $dpath" -exec $dtrace -G -s $dpath +catch {exec $dtrace -G -s $dpath} if {[file exists test.o]} then { pass "dtrace -G" } else { @@ -108,7 +131,7 @@ if {[file exists test.o]} then { exec rm -f test.o verbose -log "$dtrace -h -s $dpath" -exec $dtrace -h -s $dpath +catch {exec $dtrace -h -s $dpath} if {[file exists test.h]} then { pass "dtrace -h" } else { @@ -118,7 +141,7 @@ exec rm -f test.o set ok 0 verbose -log "$dtrace -C -h -s $dpath -o XXX.h" -exec $dtrace -C -h -s $dpath -o XXX.h +catch {exec $dtrace -C -h -s $dpath -o XXX.h} spawn cat XXX.h expect { "short arg1, int arg2, int arg3, int arg4" {incr ok} @@ -130,5 +153,28 @@ if { $ok != 0} { } exec rm -f XXX.h -exec /bin/rm -r /tmp/dtrace +set ok 0 +verbose -log "$dtrace -C -I$incpath -h -s $idpath -o XXX.h" +catch {exec $dtrace -C -I$incpath -h -s $idpath -o XXX.h} +spawn cat XXX.h +expect { + "short arg1, int arg2, int arg3, int arg4" {incr ok} +} +if { $ok != 0} { + pass "dtrace -C -Iincpath -h -o XXX.h" +} else { + fail "dtrace -C -Iincpath -h -o XXX.h" +} +exec rm -f XXX.h + +verbose -log "$dtrace -I$incpath -G -s $idpath" +catch {exec $dtrace -G -s $dpath} +if {[file exists test.o]} then { + pass "dtrace -Iincpath -G" +} else { + fail "dtrace -Iincpath -G" +} +exec rm -f test.o + +exec /bin/rm -r /tmp/dtrace /tmp/dtrace_inc # ----------------------------------------------------------------- diff --git a/testsuite/systemtap.base/sdt_misc.exp b/testsuite/systemtap.base/sdt_misc.exp index 4e6f953f..062181a5 100644 --- a/testsuite/systemtap.base/sdt_misc.exp +++ b/testsuite/systemtap.base/sdt_misc.exp @@ -3,20 +3,17 @@ set test "sdt_misc" # Test miscellaneous features of .mark probes # Compile a C program to use as the user-space probing target -set sup_srcpath "[pwd]/static_user_markers.c" -set sup_exepath "[pwd]/static_user_markers.x" -set sup_sopath "[pwd]/libsdt.so" -set supcplus_exepath "[pwd]/static_user_markers_cplus.x" +set sup_srcpath "[pwd]/sdt_misc.c" +set supcplus_exepath "[pwd]/sdt_misc_cplus.x" set fp [open $sup_srcpath "w"] puts $fp " #include <stdlib.h> -#define USE_STAP_PROBE 1 -#include \"static_user_markers_.h\" +#include \"sdt_misc_.h\" void bar (int i) { - STATIC_USER_MARKERS_TEST_PROBE_2(i); + SDT_MISC_TEST_PROBE_2(i); if (i == 0) i = 1000; STAP_PROBE1(static_uprobes,test_probe_2,i); @@ -28,7 +25,7 @@ baz (int i, char* s) STAP_PROBE1(static_uprobes,test_probe_0,i); if (i == 0) i = 1000; - STATIC_USER_MARKERS_TEST_PROBE_3(i,s); + SDT_MISC_TEST_PROBE_3(i,s); } void @@ -42,7 +39,7 @@ buz (int parm) struct astruct bstruct = {parm, parm + 1}; if (parm == 0) parm = 1000; - DTRACE_PROBE1(static_user_markers,test_probe_4,&bstruct); + DTRACE_PROBE1(sdt_misc,test_probe_4,&bstruct); } #ifndef NO_MAIN @@ -57,34 +54,50 @@ main () " close $fp -set sup_stppath "[pwd]/static_user_markers.stp" +set sup_stppath "[pwd]/sdt_misc.stp" set fp [open $sup_stppath "w"] puts $fp " -probe process(\"static_user_markers.x\").mark(\"test_probe_0\") +%( \$# > 1 %? +probe process(@1).library(@2).mark(\"test_probe_0\") +%: +probe process(@1).mark(\"test_probe_0\") +%) { printf(\"In %s probe %#x\\n\", \$\$name, \$arg1) } -probe process(\"static_user_markers.x\").mark(\"test_probe_2\") +%( \$# > 1 %? +probe process(@1).library(@2).mark(\"test_probe_2\") +%: +probe process(@1).mark(\"test_probe_2\") +%) { printf(\"In %s probe %#x\\n\", \$\$name, \$arg1) } -probe process(\"static_user_markers.x\").mark(\"test_probe_3\") +%( \$# > 1 %? +probe process(@1).library(@2).mark(\"test_probe_3\") +%: +probe process(@1).mark(\"test_probe_3\") +%) { printf(\"In %s probe %#x %#x\\n\", \$\$name, \$arg1, \$arg2) } -probe process(\"static_user_markers.x\").mark(\"test_probe_4\") +%( \$# > 1 %? +probe process(@1).library(@2).mark(\"test_probe_4\") +%: +probe process(@1).mark(\"test_probe_4\") +%) { printf(\"In %s dtrace probe %#x %#x\\n\", \$\$name, \$arg1->a, \$arg1->b) } " close $fp -set sup_dpath "[pwd]/static_user_markers_.d" -set sup_hpath "[pwd]/static_user_markers_.h" -set sup_opath "[pwd]/static_user_markers_.o" +set sup_dpath "[pwd]/sdt_misc_.d" +set sup_hpath "[pwd]/sdt_misc_.h" +set sup_opath "[pwd]/sdt_misc_.o" set fp [open $sup_dpath "w"] puts $fp " -provider static_user_markers { +provider sdt_misc { probe test_probe_0 (); probe test_probe_2 (int i); probe test_probe_3 (int i, char* x); @@ -130,7 +143,7 @@ set pbtype_mssgs {{uprobe} {utrace} {kprobe}} for {set i 0} {$i < [llength $pbtype_flags]} {incr i} { set pbtype_flag [lindex $pbtype_flags $i] set pbtype_mssg [lindex $pbtype_mssgs $i] -set testprog "sdt.c.exe.$i" +set sup_exepath "[pwd]/sdt_misc-$pbtype_mssg.x" set sup_flags "additional_flags=-I$srcdir/../includes/sys" set sup_flags "$sup_flags additional_flags=-I$sdtdir" @@ -163,7 +176,7 @@ if {![utrace_p]} { set ok 0 verbose -log "spawn stap -c $sup_exepath $sup_stppath" -spawn stap -c $sup_exepath $sup_stppath +spawn stap -c $sup_exepath $sup_stppath $sup_exepath expect { -timeout 180 -re {In test_probe_2 probe 0x2} { incr ok; exp_continue } @@ -198,17 +211,17 @@ set ok 0 set fail "types" verbose -log "spawn stap -c ./sdt_types.x $srcdir/$subdir/sdt_types.stp ./sdt_types.x" spawn stap -c ./sdt_types.x $srcdir/$subdir/sdt_types.stp ./sdt_types.x + expect { -timeout 180 -re {FAIL: [a-z_]+var} { regexp " .*$" $expect_out(0,string) s; incr ok; set fail "$fail $s"; exp_continue } - timeout { fail "$test (timeout) } + timeout { fail "$test (timeout)" } eof { } } wait -set pbtype_mssgs {{uprobe} {utrace} {kprobe}} if { $ok != 0} { if { $pbtype_mssg == "uprobe" } { fail "$test $fail $pbtype_mssg" @@ -222,7 +235,7 @@ if { $ok != 0} { # Test probe in shared object -set sup_srcmainpath "[pwd]/static_user_markers_.c" +set sup_srcmainpath "[pwd]/sdt_misc_.c" set fp [open $sup_srcmainpath "w"] puts $fp " int @@ -238,9 +251,11 @@ close $fp set sup_flags "$sup_flags additional_flags=-shared" set sup_flags "$sup_flags additional_flags=-fPIC" set sup_flags "$sup_flags additional_flags=-DNO_MAIN" +set sup_sopath "[pwd]/libsdt-$pbtype_mssg.so" +set sup_exepath "[pwd]/sdt_misc-$pbtype_mssg-shared.x" set res0 [target_compile $sup_srcpath $sup_sopath executable $sup_flags ] set sup0_flags "additional_flags=-g additional_flags=-Wl,-rpath,[pwd]" -set sup0_flags "$sup0_flags additional_flags=-L[pwd] additional_flags=-lsdt" +set sup0_flags "$sup0_flags additional_flags=-L[pwd] additional_flags=-lsdt-$pbtype_mssg" set res [target_compile $sup_srcmainpath $sup_exepath executable $sup0_flags ] if { $res0 != "" || $res != "" } { verbose "target_compile failed: $res0 $res" 2 @@ -254,21 +269,28 @@ if { $res0 != "" || $res != "" } { } set ok 0 -# verbose -log "stap -c $sup_exepath -e probe process(\"$sup_sopath\").mark(\"test_probe_2\") {printf(\"In %s probe %#x\\n\", \$\$name, \$arg1)}" -# spawn stap -c $sup_exepath -e "probe process(\"$sup_sopath\").mark(\"test_probe_2\") {printf(\"In %s probe %#x\\n\", \$\$name, \$arg1)}" -# expect { -# -timeout 180 -# -re {In test_probe_2 probe 0x2} { incr ok; exp_continue } -# timeout { fail "$test (timeout)" } -# eof { } -# } -# wait - -if {$ok == 2} { +verbose -log "spawn stap -c $sup_exepath $sup_stppath $sup_exepath $sup_sopath" +if { $pbtype_mssg != "kprobe" } { + spawn stap -c $sup_exepath $sup_stppath $sup_exepath $sup_sopath +} else { + spawn stap -c $sup_exepath $sup_stppath $sup_sopath +} +expect { + -timeout 180 + -re {In test_probe_2 probe 0x2} { incr ok; exp_continue } + -re {In test_probe_0 probe 0x3} { incr ok; exp_continue } + -re {In test_probe_3 probe 0x3 0x[0-9a-f][0-9a-f]} { incr ok; exp_continue } + -re {In test_probe_4 dtrace probe 0x4 0x5} { incr ok; exp_continue } + timeout { fail "$test (timeout)" } + eof { } +} + +wait + +if {$ok == 5} { pass "$test shared $pbtype_mssg" } else { -# fail "$test shared ($ok) $pbtype_mssg" - xfail "$test shared ($ok) $pbtype_mssg" + fail "$test ($ok) shared $pbtype_mssg" } # Test .mark probe wildcard matching @@ -288,9 +310,12 @@ if { $ok == 45 } { fail "$test wildcard ($ok) $pbtype_mssg" } +if { $verbose == 0 } { + catch {exec rm -f libsdt-$pbtype_mssg.so sdt_misc-$pbtype_mssg.x sdt_misc-$pbtype_mssg-shared.x } +} # for {set i 0} } if { $verbose == 0 } { -catch {exec rm -f $sup_srcpath $sup_exepath $sup_sopath $supcplus_exepath $sup_dpath $sup_hpath $sup_opath $sup_stppath $sdt_types.x $sup_srcmainpath} + catch {exec rm -f sdt_misc_.c sdt_misc.c sdt_misc_.d sdt_misc_.h sdt_misc_.o sdt_misc.stp sdt_types.x} } @@ -244,7 +244,7 @@ tokenize(const string& str, vector<string>& tokens, // same policy as execvp(). A program name not containing a slash // will be searched along the $PATH. -string find_executable(const string& name) +string find_executable(const string& name, const string& env_path) { string retpath; @@ -259,7 +259,7 @@ string find_executable(const string& name) } else // Nope, search $PATH. { - char *path = getenv("PATH"); + char *path = getenv(env_path.c_str()); if (path) { // Split PATH up. @@ -15,7 +15,8 @@ int remove_file_or_dir(const char *dir); bool in_group_id (gid_t target_gid); void tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters); -std::string find_executable(const std::string& name); +std::string find_executable(const std::string& name, + const std::string& env_path = "PATH"); const std::string cmdstr_quoted(const std::string& cmd); std::string git_revision(const std::string& path); int stap_system(int verbose, const std::string& command); |