void
-mutatee::call_utrace_dynprobe(const dynprobe_location& probe,
- BPatch_thread* thread)
+mutatee::call_utrace_dynprobes(const vector<dynprobe_location>& probes,
+ BPatch_thread* thread)
{
- if (utrace_enter_function != NULL)
+ if (!stap_dso || probes.empty())
+ return;
+
+ if (utrace_enter_function == NULL)
+ {
+ vector<BPatch_function *> 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<BPatch_snippet *> 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;
}
void
mutatee::instrument_utrace_dynprobe(const dynprobe_location& probe)
{
- if (!stap_dso)
- return;
-
- if (utrace_enter_function == NULL)
- {
- vector<BPatch_function *> 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);
}
void
-mutatee::begin_callback()
+mutatee::begin_callback(BPatch_thread *thread)
{
+ const vector<dynprobe_location>& 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<dynprobe_location>& 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<dynprobe_location>& 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())
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<const dynprobe_location *>&probes)
+
+vector<dynprobe_location>
+mutatee::find_attached_probes(uint64_t flag)
{
+ vector<dynprobe_location> 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<dynprobe_target>& targets,
- bool after_exec_p)
+mutatee::instrument_dynprobes(const vector<dynprobe_target>& targets)
{
if (!process || !stap_dso || targets.empty())
return;
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);
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);
+ }
}
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;
}
std::vector<dynprobe_location> attached_probes;
BPatch_function* utrace_enter_function;
+ // process.end probes saved to run after exec
+ std::vector<dynprobe_location> 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<dynprobe_location>& 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<dynprobe_target>& targets);
const std::vector<dynprobe_target>& targets);
// Look for probe matches in all objects.
- void instrument_dynprobes(const std::vector<dynprobe_target>& targets,
- bool after_exec_p=false);
+ void instrument_dynprobes(const std::vector<dynprobe_target>& targets);
// Copy data for forked instrumentation
void copy_forked_instrumentation(mutatee& other);
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<const dynprobe_location *>&probes);
+ std::vector<dynprobe_location> find_attached_probes(uint64_t flag);
};
#endif // MUTATEE_H
m->copy_forked_instrumentation(*mut);
// Trigger any process.begin probes.
- m->begin_callback();
+ m->begin_callback(child);
}
}
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())
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
}
// '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<mutatee> mut = find_mutatee(process);
if (mut)
// 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<const dynprobe_location *> 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<dynprobe_location>& 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;