}
+// Simple object to temporarily make sure a process is stopped
+class mutatee_freezer {
+ mutatee& m;
+ bool already_stopped;
+
+ public:
+ mutatee_freezer(mutatee& m):
+ m(m), already_stopped(m.is_stopped())
+ {
+ // If process is currently running, stop it.
+ if (!already_stopped && !m.stop_execution())
+ {
+ staplog(3) << "stopping process failed, stopped="
+ << m.is_stopped() << ", terminated="
+ << m.is_terminated() << endl;
+ }
+ }
+
+ ~mutatee_freezer()
+ {
+ // Let the process continue (if it wasn't stopped when we started).
+ if (!already_stopped)
+ m.continue_execution();
+ }
+};
+
+
mutatee::mutatee(BPatch_process* process):
pid(process? process->getPid() : 0),
process(process), stap_dso(NULL),
mutatee::begin_callback()
{
// process->oneTimeCode() requires that the process be stopped
- bool stopped = process->isStopped();
- if (!stopped && !stop_execution())
- {
- staplog(3) << "stopping process failed, stopped="
- << process->isStopped() << ", terminated="
- << process->isTerminated() << endl;
- return;
- }
+ mutatee_freezer mf(*this);
+ if (!is_stopped())
+ return;
for (size_t i = 0; i < attached_probes.size(); ++i)
{
call_utrace_dynprobe(probe);
}
}
-
- // Let the process continue (if it wasn't stopped when we started).
- if (!stopped)
- continue_execution();
}
// thread->oneTimeCode() requires that the process (not just the
// thread) be stopped. So, stop the process if needed.
- bool stopped = process->isStopped();
- if (!stopped && !stop_execution())
- {
- staplog(3) << "stopping process failed, stopped="
- << process->isStopped() << ", terminated="
- << process->isTerminated() << endl;
- return;
- }
+ mutatee_freezer mf(*this);
+ if (!is_stopped())
+ return;
for (size_t i = 0; i < attached_probes.size(); ++i)
{
call_utrace_dynprobe(probe, thread);
}
}
-
- // Let the process continue (if it wasn't stopped when we started).
- if (!stopped)
- continue_execution();
}
void
// Look for probe matches in all objects.
void
-mutatee::instrument_dynprobes(const vector<dynprobe_target>& targets)
+mutatee::instrument_dynprobes(const vector<dynprobe_target>& targets,
+ bool after_exec_p)
{
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);
if (!process)
return;
+ // Freeze both processes, so we have a stable base.
+ mutatee_freezer mf_parent(other), mf(*this);
+
// Find the same stap module in the fork
if (other.stap_dso)
{
}
+// Reset instrumentation after an exec
+void
+mutatee::exec_reset_instrumentation()
+{
+ // Reset members that are now out of date
+ stap_dso = NULL;
+ 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.
+ utrace_enter_function = NULL;
+}
+
+
// Remove all BPatch snippets we've instrumented in the target
void
mutatee::remove_instrumentation()
}
+static void
+g_exec_callback(BPatch_thread *thread)
+{
+ for (size_t i = 0; i < g_mutators.size(); ++i)
+ g_mutators[i]->exec_callback(thread);
+}
+
+
static void
g_exit_callback(BPatch_thread *thread, BPatch_exitType type)
{
// STAPDYN_PROBE_FLAG_PROC_BEGIN, because we might want to trigger
// any of the other types of probes in new processes too.
patch.registerPostForkCallback(g_post_fork_callback);
+ patch.registerExecCallback(g_exec_callback);
// Do we need a exit callback?
if (matching_probes_exist(STAPDYN_PROBE_FLAG_PROC_END))
return;
BPatch_process* process = thread->getProcess();
+ staplog(1) << "dlopen \"" << module->libraryName()
+ << "\", pid = " << process->getPid() << endl;
boost::shared_ptr<mutatee> mut = find_mutatee(process);
if (mut)
mut->instrument_object_dynprobes(module->getObject(), targets);
}
+// Callback to respond to exec events. Check if it matches our
+// targets, and handle accordingly.
+void
+mutator::exec_callback(BPatch_thread *thread)
+{
+ if (!thread)
+ return;
+
+ BPatch_process* process = thread->getProcess();
+
+ staplog(1) << "exec, pid = " << process->getPid() << endl;
+
+ boost::shared_ptr<mutatee> mut = find_mutatee(process);
+ if (mut)
+ {
+ // Clear previous instrumentation
+ mut->exec_reset_instrumentation();
+
+ // FIXME the loadLibrary is hanging in Dyninst waiting for IRPC.
+ // I've tried deferring this until update_mutatees() too - same hang.
+#if 0
+ // Load our module again in the new process
+ if (mut->load_stap_dso(module_name))
+ mut->instrument_dynprobes(targets, true);
+#endif
+ }
+}
+
+
void
mutator::exit_callback(BPatch_thread *thread,
BPatch_exitType type __attribute__((unused)))