From: Josh Stone Date: Sat, 15 Feb 2014 01:25:12 +0000 (-0800) Subject: stapdyn: Reorganize utrace probing, and get end-by-exec working X-Git-Tag: release-2.5~272 X-Git-Url: https://sourceware.org/git/?a=commitdiff_plain;h=90b7e794f2a8ea8ddf4dd0cf2ec8d74ee1aabd4b;p=systemtap.git stapdyn: Reorganize utrace probing, and get end-by-exec working * mutatee.cxx (mutatee::exec_reset_instrumentation): Save process.end probes into exec_proc_end_probes, and clear the rest of attached_probes. (mutatee::call_utrace_dynprobes): Take a whole vector of probes, and do the lookup for utrace_enter_function here. (mutatee::find_attached_probes): Return the vector directly. (mutatee::thread_callback): Use find_attached_probes, and pass that to call_utrace_dynprobes as is. (mutatee::begin_callback): Ditto. (mutatee::exit_callback): Ditto, except treat exec specially. (mutatee::instrument_dynprobes): Don't try to deal with exec here, because the stap module probably isn't ready yet. * mutator.cxx (mutator::exec_callback): Call mutatee::exit_callback. (mutator::exit_callback): Align the fake-local probe hits to work more similarly to mutatee::exit_callback. --- diff --git a/stapdyn/mutatee.cxx b/stapdyn/mutatee.cxx index 08adfe2dd..de0120208 100644 --- a/stapdyn/mutatee.cxx +++ b/stapdyn/mutatee.cxx @@ -194,24 +194,40 @@ mutatee::update_semaphores(unsigned short delta, size_t start) void -mutatee::call_utrace_dynprobe(const dynprobe_location& probe, - BPatch_thread* thread) +mutatee::call_utrace_dynprobes(const vector& probes, + BPatch_thread* thread) { - if (utrace_enter_function != NULL) + if (!stap_dso || probes.empty()) + return; + + if (utrace_enter_function == NULL) + { + vector functions; + stap_dso->findFunction("enter_dyninst_utrace_probe", + functions); + if (!functions.empty()) + utrace_enter_function = functions[0]; + else + { + staplog(1) << "no utrace enter function in pid " << pid << "!" << endl; + return; + } + } + + for (size_t i = 0; i < probes.size(); ++i) { + const dynprobe_location& probe = probes[i]; vector args; args.push_back(new BPatch_constExpr((int64_t)probe.index)); args.push_back(new BPatch_constExpr((void*)NULL)); // pt_regs - staplog(2) << "calling utrace function in pid " << pid - << " with " << args.size() << " args" << endl; BPatch_funcCallExpr call(*utrace_enter_function, args); + staplog(3) << "calling utrace function in pid " << pid + << " for probe index " << probe.index << endl; if (thread) thread->oneTimeCode(call); else process->oneTimeCode(call); } - else - staplog(1) << "no utrace enter function in pid " << pid << "!" << endl; } @@ -220,19 +236,7 @@ mutatee::call_utrace_dynprobe(const dynprobe_location& probe, void mutatee::instrument_utrace_dynprobe(const dynprobe_location& probe) { - if (!stap_dso) - return; - - if (utrace_enter_function == NULL) - { - vector functions; - stap_dso->findFunction("enter_dyninst_utrace_probe", - functions); - if (!functions.empty()) - utrace_enter_function = functions[0]; - } - - // Remember this probe. It will get called from a callback function. + // Just remember this probe. It will get called from a callback function. attached_probes.push_back(probe); } @@ -474,56 +478,65 @@ mutatee::instrument_object_dynprobes(BPatch_object* object, void -mutatee::begin_callback() +mutatee::begin_callback(BPatch_thread *thread) { + const vector& proc_begin_probes = + find_attached_probes(STAPDYN_PROBE_FLAG_PROC_BEGIN); + + // Shortcut out if there aren't any relevant probes + if (proc_begin_probes.empty()) + return; + // process->oneTimeCode() requires that the process be stopped mutatee_freezer mf(*this); if (!is_stopped()) return; - for (size_t i = 0; i < attached_probes.size(); ++i) - { - const dynprobe_location& probe = attached_probes[i]; - if (probe.flags & STAPDYN_PROBE_FLAG_PROC_BEGIN) - { - staplog(2) << "found begin proc probe in pid " << pid - << ", index = " << probe.index << endl; - call_utrace_dynprobe(probe); - } - } + staplog(2) << "firing " << proc_begin_probes.size() + << " process.begin probes in pid " << pid << endl; + call_utrace_dynprobes(proc_begin_probes, thread); } -// FIXME: We have a problem with STAPDYN_PROBE_FLAG_PROC_END -// (i.e. 'process.end' probes). -// -// If we use dyninst's registerExitCallback(), when that callback -// hits, we can't stop the process before it exits. So, we can't call -// oneTimeCode() as we've done for the thread callbacks. So, that -// doesn't work. -// -// When registerExitCallback() hits, we could just run the probe -// locally, but then the probe context wouldn't be correct. - +// FIXME: When dyninst's registerExitCallback() hits, it's too late +// to stop the process and inject oneTimeCode() for process.end. +// So while the code here works for post-exec "end", for now the +// mutator::exit_callback() will run its probes locally in stapdyn. void -mutatee::exit_callback(BPatch_thread *thread) +mutatee::exit_callback(BPatch_thread *thread, bool exec_p) { - for (size_t i = 0; i < attached_probes.size(); ++i) - { - const dynprobe_location& probe = attached_probes[i]; - if (probe.flags & STAPDYN_PROBE_FLAG_PROC_END) - { - staplog(2) << "found end proc probe in pid " << pid - << ", index = " << probe.index << endl; - call_utrace_dynprobe(probe, thread); - } - } + const vector& proc_end_probes = + exec_p ? exec_proc_end_probes + : find_attached_probes(STAPDYN_PROBE_FLAG_PROC_END); + + // Shortcut out if there aren't any relevant probes + if (proc_end_probes.empty()) + return; + + // thread->oneTimeCode() requires that the process (not just the + // thread) be stopped. So, stop the process if needed. + mutatee_freezer mf(*this); + if (!is_stopped()) + return; + + staplog(2) << "firing " << proc_end_probes.size() + << " process.end probes in pid " << pid << endl; + call_utrace_dynprobes(proc_end_probes, thread); } void mutatee::thread_callback(BPatch_thread *thread, bool create_p) { + const vector& probes = + find_attached_probes(create_p + ? STAPDYN_PROBE_FLAG_THREAD_BEGIN + : STAPDYN_PROBE_FLAG_THREAD_END); + + // Shortcut out if there aren't any relevant probes + if (probes.empty()) + return; + // If 'thread' is the main process, just return. We can't stop the // process before it terminates. if (thread->getLWP() == process->getPid()) @@ -535,36 +548,30 @@ mutatee::thread_callback(BPatch_thread *thread, bool create_p) if (!is_stopped()) return; - for (size_t i = 0; i < attached_probes.size(); ++i) - { - const dynprobe_location& probe = attached_probes[i]; - if ((create_p && probe.flags & STAPDYN_PROBE_FLAG_THREAD_BEGIN) - || (!create_p && probe.flags & STAPDYN_PROBE_FLAG_THREAD_END)) - { - staplog(2) << "found " << (create_p ? "begin" : "end") - << " thread probe in pid " << pid - << ", index = " << probe.index << endl; - call_utrace_dynprobe(probe, thread); - } - } + staplog(2) << "firing " << probes.size() + << " process.thread." << (create_p ? "begin" : "end") + << " probes in pid " << pid << endl; + call_utrace_dynprobes(probes, thread); } -void -mutatee::find_attached_probes(uint64_t flag, - vector&probes) + +vector +mutatee::find_attached_probes(uint64_t flag) { + vector probes; for (size_t i = 0; i < attached_probes.size(); ++i) { const dynprobe_location& probe = attached_probes[i]; if (probe.flags & flag) - probes.push_back(&probe); + probes.push_back(probe); } + return probes; } + // Look for probe matches in all objects. void -mutatee::instrument_dynprobes(const vector& targets, - bool after_exec_p) +mutatee::instrument_dynprobes(const vector& targets) { if (!process || !stap_dso || targets.empty()) return; @@ -573,13 +580,6 @@ mutatee::instrument_dynprobes(const vector& targets, if (!image) return; - // If this is post-exec, run any process.end from the pre-exec process - if (after_exec_p && !attached_probes.empty()) - { - exit_callback(NULL); - attached_probes.clear(); - } - // Match non object/path specific probes. instrument_global_dynprobes(targets); @@ -638,9 +638,14 @@ mutatee::copy_forked_instrumentation(mutatee& other) semaphores.push_back(semaphore); } - // Update utrace probes to match + // Update utrace probes to match, except PID-based probes. + // (A forked PID will never be the same as the parent.) for (size_t i = 0; i < other.attached_probes.size(); ++i) - instrument_utrace_dynprobe(other.attached_probes[i]); + { + const dynprobe_location& probe = other.attached_probes[i]; + if (probe.offset == 0) + instrument_utrace_dynprobe(probe); + } } @@ -653,8 +658,11 @@ mutatee::exec_reset_instrumentation() snippets.clear(); semaphores.clear(); - // NB: the utrace attached_probes are saved, so process.end can run as the - // new process is instrumented. Thus, no attached_probes.clear() yet. + // NB: the utrace process.end probes are saved, so they can run right + // before the new process does its process.begin. + exec_proc_end_probes = find_attached_probes(STAPDYN_PROBE_FLAG_PROC_END); + + attached_probes.clear(); utrace_enter_function = NULL; } diff --git a/stapdyn/mutatee.h b/stapdyn/mutatee.h index b7abb88c7..432774a40 100644 --- a/stapdyn/mutatee.h +++ b/stapdyn/mutatee.h @@ -36,14 +36,17 @@ class mutatee { std::vector attached_probes; BPatch_function* utrace_enter_function; + // process.end probes saved to run after exec + std::vector exec_proc_end_probes; + // disable implicit constructors by not implementing these mutatee (const mutatee& other); mutatee& operator= (const mutatee& other); void update_semaphores(unsigned short delta, size_t start=0); - void call_utrace_dynprobe(const dynprobe_location& probe, - BPatch_thread* thread=NULL); + void call_utrace_dynprobes(const std::vector& probes, + BPatch_thread* thread=NULL); void instrument_utrace_dynprobe(const dynprobe_location& probe); void instrument_global_dynprobe_target(const dynprobe_target& target); void instrument_global_dynprobes(const std::vector& targets); @@ -73,8 +76,7 @@ class mutatee { const std::vector& targets); // Look for probe matches in all objects. - void instrument_dynprobes(const std::vector& targets, - bool after_exec_p=false); + void instrument_dynprobes(const std::vector& targets); // Copy data for forked instrumentation void copy_forked_instrumentation(mutatee& other); @@ -102,12 +104,11 @@ class mutatee { bool check_exit() { return check_dyninst_exit(process); } - void begin_callback(); - void exit_callback(BPatch_thread *thread); + void begin_callback(BPatch_thread *thread=NULL); + void exit_callback(BPatch_thread *thread, bool exec_p=false); void thread_callback(BPatch_thread *thread, bool create_p); - void find_attached_probes(uint64_t flag, - std::vector&probes); + std::vector find_attached_probes(uint64_t flag); }; #endif // MUTATEE_H diff --git a/stapdyn/mutator.cxx b/stapdyn/mutator.cxx index 93217697e..8c1a32cd7 100644 --- a/stapdyn/mutator.cxx +++ b/stapdyn/mutator.cxx @@ -689,7 +689,7 @@ mutator::post_fork_callback(BPatch_thread *parent, BPatch_thread *child) m->copy_forked_instrumentation(*mut); // Trigger any process.begin probes. - m->begin_callback(); + m->begin_callback(child); } } @@ -719,7 +719,7 @@ mutator::exec_callback(BPatch_thread *thread) if (mut->load_stap_dso(module_name)) { if (!targets.empty()) - mut->instrument_dynprobes(targets, true); + mut->instrument_dynprobes(targets); // Now we map the shared-memory into the target if (!module_shmem.empty()) @@ -729,8 +729,11 @@ mutator::exec_callback(BPatch_thread *thread) mut->call_function("stp_dyninst_shm_connect", args); } + // Trigger any process.end probes for the pre-exec process. + mut->exit_callback(thread, true); + // Trigger any process.begin probes. - mut->begin_callback(); + mut->begin_callback(thread); } #endif } @@ -747,21 +750,8 @@ mutator::exit_callback(BPatch_thread *thread, // 'thread' is the thread that requested the exit, not necessarily the // main thread. BPatch_process* process = thread->getProcess(); - - if (utrace_enter_fn == NULL) - { - try - { - set_dlsym(utrace_enter_fn, module, "enter_dyninst_utrace_probe"); - } - catch (runtime_error& e) - { - staperror() << e.what() << endl; - return; - } - } - - staplog(1) << "exit callback, pid = " << process->getPid() << endl; + int pid = process->getPid(); + staplog(1) << "exit callback, pid = " << pid << endl; boost::shared_ptr mut = find_mutatee(process); if (mut) @@ -772,14 +762,31 @@ mutator::exit_callback(BPatch_thread *thread, // exiting before we can). So, we'll call the probe(s) locally // here. This works, but the context is wrong (the mutator, not // the mutatee). - vector exit_probes; - mut->find_attached_probes(STAPDYN_PROBE_FLAG_PROC_END, exit_probes); - for (size_t p = 0; p < exit_probes.size(); ++p) + const vector& proc_end_probes = + mut->find_attached_probes(STAPDYN_PROBE_FLAG_PROC_END); + if (proc_end_probes.empty()) + return; + + if (utrace_enter_fn == NULL) + try + { + set_dlsym(utrace_enter_fn, module, "enter_dyninst_utrace_probe"); + } + catch (runtime_error& e) + { + staperror() << e.what() << endl; + return; + } + + staplog(2) << "firing " << proc_end_probes.size() + << " process.end probes in the mutator for pid " + << pid << endl; + for (size_t p = 0; p < proc_end_probes.size(); ++p) { - const dynprobe_location *probe = exit_probes[p]; - staplog(2) << "found end proc probe in pid " << process->getPid() - << ", index = " << probe->index << endl; - int rc = utrace_enter_fn(probe->index, NULL); + const dynprobe_location& probe = proc_end_probes[p]; + staplog(3) << "calling utrace function in the mutator for pid " + << pid << ", probe index " << probe.index << endl; + int rc = utrace_enter_fn(probe.index, NULL); if (rc) stapwarn() << "enter_dyninst_utrace_probe returned " << rc << endl;