stapdyn/mutatee.cxx - systemtap
Functions defined
Source code
// stapdyn mutatee functions
// Copyright (C) 2012-2014 Red Hat Inc.
//
// This file is part of systemtap, and is free software. You can
// redistribute it and/or modify it under the terms of the GNU General
// Public License (GPL); either version 2, or (at your option) any
// later version.
#include "mutatee.h"
extern "C" {
#include <signal.h>
#include <sys/types.h>
}
#include <BPatch_function.h>
#include <BPatch_image.h>
#include <BPatch_module.h>
#include <BPatch_point.h>
#include <BPatch_thread.h>
#include "dynutil.h"
#include "../util.h"
extern "C" {
#include "../runtime/dyninst/stapdyn.h"
}
using namespace std;
// Create snippets for all the DWARF registers,
// in their architecture-specific order.
static void
get_dwarf_registers(BPatch_process *app,
vector<BPatch_snippet*>& registers)
{
static const char* const names[] = {
#if defined(__i386__)
"eax", "ecx", "edx", "ebx",
"esp", "ebp", "esi", "edi",
#elif defined(__x86_64__)
"rax", "rdx", "rcx", "rbx",
"rsi", "rdi", "rbp", "rsp",
"r8", "r9", "r10", "r11",
"r12", "r13", "r14", "r15",
#elif defined(__powerpc__) || defined(__powerpc64__)
"r0", "r1", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11",
"r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19",
"r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27",
"r28", "r29", "r30", "r31",
#endif
NULL };
// First push the original PC, before instrumentation mucked anything up.
// (There's also BPatch_actualAddressExpr for the instrumented result...)
registers.push_back(new BPatch_originalAddressExpr());
vector<BPatch_register> bpregs;
app->getRegisters(bpregs);
ostream& debug_reg = staplog(4);
debug_reg << "pid " << app->getPid() << " has "
<< bpregs.size() << " registers available:";
for (size_t i = 0; i < bpregs.size(); ++i)
debug_reg << " " << bpregs[i].name();
debug_reg << endl;
// Look for each DWARF register in BPatch's register set.
// O(m*n) loop, but neither array is very large
for (const char* const* name = names; *name; ++name)
{
size_t i;
for (i = 0; i < bpregs.size(); ++i)
if (bpregs[i].name() == *name)
{
// Found it, add a snippet.
registers.push_back(new BPatch_registerExpr(bpregs[i]));
break;
}
// If we didn't find it, put a zero in its place.
if (i >= bpregs.size())
registers.push_back(new BPatch_constExpr((unsigned long)0));
}
#if defined(__powerpc__) || defined(__powerpc64__)
// In EmitterPOWER::emitCall(), Dyninst enforces a limit that "only 8
// arguments can (currently) be passed on the POWER architecture."
// We start with the probe index and nregs, leaving just 6 more...
while (registers.size() > 6)
{
delete registers.back();
registers.pop_back();
}
#endif
}
// 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),
utrace_enter_function(NULL)
{
get_dwarf_registers(process, registers);
}
mutatee::~mutatee()
{
remove_instrumentation();
unload_stap_dso();
if (process)
process->detach(true);
}
// Inject the stap module into the target process
bool
mutatee::load_stap_dso(const string& filename)
{
stap_dso = process->loadLibrary(filename.c_str());
if (!stap_dso)
{
staperror() << "Couldn't load " << filename
<< " into the target process" << endl;
return false;
}
return true;
}
// Unload the stap module from the target process
void
mutatee::unload_stap_dso()
{
if (!process || !stap_dso)
return;
// XXX Dyninst has no unloadLibrary() yet, as of 8.0
stap_dso = NULL;
}
void
mutatee::update_semaphores(unsigned short delta, size_t start)
{
if (!process || process->isTerminated())
return;
for (size_t i=start; i < semaphores.size(); ++i)
{
unsigned short value = 0;
BPatch_variableExpr* semaphore = semaphores[i];
// Read-modify-write the semaphore remotely.
semaphore->readValue(&value, (int)sizeof(value));
value += delta;
semaphore->writeValue(&value, (int)sizeof(value));
}
// NB: Alternatively, we could build up a oneTimeCode snippet to do them all
// at once within the target process. For now, remote seems good enough.
}
void
mutatee::call_utrace_dynprobes(const vector<dynprobe_location>& probes,
BPatch_thread* thread)
{
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
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);
}
}
// Remember utrace probes. They get handled when the associated
// callback hits.
void
mutatee::instrument_utrace_dynprobe(const dynprobe_location& probe)
{
// Just remember this probe. It will get called from a callback function.
attached_probes.push_back(probe);
}
// Handle "global" targets. They aren't really global, but non-path
// based probes, like:
// probe process.begin { ... }
// probe process(PID).begin { ... }
void
mutatee::instrument_global_dynprobe_target(const dynprobe_target& target)
{
staplog(1) << "found global target in pid " << pid << ", inserting "
<< target.probes.size() << " probes" << endl;
for (size_t j = 0; j < target.probes.size(); ++j)
{
const dynprobe_location& probe = target.probes[j];
// We already know this isn't a path-based probe. We've got 2
// other qualifications here:
// (1) Make sure this is a utrace probe by checking the flags.
// (2) If PID was specified, does the pid match?
if (((probe.flags & (STAPDYN_PROBE_FLAG_PROC_BEGIN
| STAPDYN_PROBE_FLAG_PROC_END
| STAPDYN_PROBE_FLAG_THREAD_BEGIN
| STAPDYN_PROBE_FLAG_THREAD_END)) != 0)
&& (probe.offset == 0 || (int)probe.offset == process->getPid()))
instrument_utrace_dynprobe(probe);
}
}
// Given a target and the matching object, instrument all of the probes
// with calls to the stap_dso's entry function.
void
mutatee::instrument_dynprobe_target(BPatch_object* object,
const dynprobe_target& target)
{
if (!process || !stap_dso || !object)
return;
vector<BPatch_function *> functions;
BPatch_function* enter_function = NULL;
bool use_pt_regs = false;
staplog(1) << "found target \"" << target.path << "\" in pid " << pid
<< ", inserting " << target.probes.size() << " probes" << endl;
process->beginInsertionSet();
for (size_t j = 0; j < target.probes.size(); ++j)
{
const dynprobe_location& probe = target.probes[j];
if ((probe.flags & (STAPDYN_PROBE_FLAG_PROC_BEGIN
| STAPDYN_PROBE_FLAG_PROC_END
| STAPDYN_PROBE_FLAG_THREAD_BEGIN
| STAPDYN_PROBE_FLAG_THREAD_END)) != 0)
{
instrument_utrace_dynprobe(probe);
continue;
}
if (! enter_function)
{
// XXX Until we know how to build pt_regs from here, we'll
// try the entry function for individual registers first.
if (!registers.empty())
stap_dso->findFunction("enter_dyninst_uprobe_regs", functions,
false);
if (!functions.empty())
enter_function = functions[0];
// If the other entry wasn't found, or we don't have
// registers for it anyway, try the form that takes pt_regs*
// and we'll just pass NULL.
if (!enter_function)
{
stap_dso->findFunction("enter_dyninst_uprobe", functions,
false);
if (functions.empty())
{
stapwarn() << "Couldn't find the uprobe entry function (either " << endl
<< "\"enter_dyninst_uprobe_regs\" or \"enter_dyninst_uprobe\"). Uprobe probes"
<< endl << "disabled." << endl;
return;
}
use_pt_regs = true;
enter_function = functions[0];
}
}
// Convert the file offset to a memory address.
Dyninst::Address address = object->fileOffsetToAddr(probe.offset);
if (address == BPatch_object::E_OUT_OF_BOUNDS)
{
stapwarn() << "Couldn't convert " << target.path << "+"
<< lex_cast_hex(probe.offset) << " to an address" << endl;
continue;
}
// Turn the address into instrumentation points.
// NB: There may be multiple results if Dyninst determined that multiple
// concrete functions have overlapping ranges. Rare, but possible.
vector<BPatch_point*> points;
object->findPoints(address, points);
if (points.empty())
{
stapwarn() << "Couldn't find an instrumentation point at "
<< lex_cast_hex(address) << ", " << target.path
<< "+" << lex_cast_hex(probe.offset) << endl;
continue;
}
// Check that the functions containing each point are actually
// instrumentable. Unfortunately, findPoints doesn't make any
// distinction, and insertSnippet doesn't tell us either, so this
// is the best way we have to let the user know.
vector<BPatch_point*> instrumentable_points;
for (size_t i = 0; i < points.size(); ++i)
if (points[i]->getFunction()->isInstrumentable())
instrumentable_points.push_back(points[i]);
else
stapwarn() << "Couldn't instrument the function containing "
<< lex_cast_hex(address) << ", " << target.path
<< "+" << lex_cast_hex(probe.offset) << endl;
points.swap(instrumentable_points);
if (probe.return_p)
{
// Transform the address points into function exits
vector<BPatch_point*> return_points;
for (size_t i = 0; i < points.size(); ++i)
{
vector<BPatch_point*>* exits =
points[i]->getFunction()->findPoint(BPatch_locExit);
if (!exits || exits->empty())
{
stapwarn() << "Couldn't find a return point from "
<< lex_cast_hex(address) << ", " << target.path
<< "+" << lex_cast_hex(probe.offset) << endl;
continue;
}
return_points.insert(return_points.end(),
exits->begin(), exits->end());
}
points.swap(return_points);
}
if (points.empty())
continue;
// The entry function needs the index of this particular probe, then
// the registers in whatever form we chose above.
vector<BPatch_snippet *> args;
args.push_back(new BPatch_constExpr((int64_t)probe.index));
if (use_pt_regs)
args.push_back(new BPatch_constExpr((void*)NULL)); // pt_regs
else
{
args.push_back(new BPatch_constExpr((unsigned long)registers.size()));
args.insert(args.end(), registers.begin(), registers.end());
}
BPatch_funcCallExpr call(*enter_function, args);
// Finally write the instrumentation for the probe!
BPatchSnippetHandle* handle = process->insertSnippet(call, points);
if (handle)
snippets.push_back(handle);
// Update SDT semaphores as needed.
if (probe.semaphore)
{
Dyninst::Address sem_address = object->fileOffsetToAddr(probe.semaphore);
if (sem_address == BPatch_object::E_OUT_OF_BOUNDS)
stapwarn() << "Couldn't convert semaphore " << target.path << "+"
<< lex_cast_hex(probe.offset) << " to an address" << endl;
else
{
// Create a variable to represent this semaphore
BPatch_type *sem_type = process->getImage()->findType("unsigned short");
BPatch_variableExpr *semaphore = process->createVariable(sem_address, sem_type);
if (semaphore)
semaphores.push_back(semaphore);
}
}
}
process->finalizeInsertionSet(false);
}
// Look for "global" (non-path based) probes and handle them.
void
mutatee::instrument_global_dynprobes(const vector<dynprobe_target>& targets)
{
if (!process || !stap_dso || targets.empty())
return;
// Look for global (non path-based probes), and remember them.
for (size_t i = 0; i < targets.size(); ++i)
{
const dynprobe_target& target = targets[i];
// Do the real work...
if (target.path.empty())
instrument_global_dynprobe_target(target);
}
}
// Look for all matches between this object and the targets
// we want to probe, then do the instrumentation.
void
mutatee::instrument_object_dynprobes(BPatch_object* object,
const vector<dynprobe_target>& targets)
{
if (!process || !stap_dso || !object || targets.empty())
return;
// We want to map objects by their full path, but the pathName from
// Dyninst might be relative, so fill it out.
string path = resolve_path(object->pathName());
staplog(2) << "found object \"" << path << "\" in pid " << pid << endl;
size_t semaphore_start = semaphores.size();
// Match the object to our targets, and instrument matches.
for (size_t i = 0; i < targets.size(); ++i)
{
const dynprobe_target& target = targets[i];
// Do the real work...
if (path == target.path)
instrument_dynprobe_target(object, target);
}
// Increment new semaphores
update_semaphores(1, semaphore_start);
}
void
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;
staplog(2) << "firing " << proc_begin_probes.size()
<< " process.begin probes in pid " << pid << endl;
call_utrace_dynprobes(proc_begin_probes, thread);
}
// 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, bool exec_p)
{
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())
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 " << probes.size()
<< " process.thread." << (create_p ? "begin" : "end")
<< " probes in pid " << pid << endl;
call_utrace_dynprobes(probes, thread);
}
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);
}
return probes;
}
// Look for probe matches in all objects.
void
mutatee::instrument_dynprobes(const vector<dynprobe_target>& targets)
{
if (!process || !stap_dso || targets.empty())
return;
BPatch_image* image = process->getImage();
if (!image)
return;
// Match non object/path specific probes.
instrument_global_dynprobes(targets);
// Read all of the objects in the process.
vector<BPatch_object *> objects;
image->getObjects(objects);
for (size_t i = 0; i < objects.size(); ++i)
instrument_object_dynprobes(objects[i], targets);
}
// Copy data for forked instrumentation
void
mutatee::copy_forked_instrumentation(mutatee& other)
{
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)
{
BPatch_image* image = process->getImage();
if (!image)
return;
string dso_path = other.stap_dso->pathName();
vector<BPatch_object *> objects;
image->getObjects(objects);
for (size_t i = 0; i < objects.size(); ++i)
if (objects[i]->pathName() == dso_path)
{
stap_dso = objects[i];
break;
}
}
// Get new handles for all inserted snippets
for (size_t i = 0; i < other.snippets.size(); ++i)
{
BPatchSnippetHandle *handle =
process->getInheritedSnippet(*other.snippets[i]);
if (handle)
snippets.push_back(handle);
}
// Get new variable representations of semaphores
for (size_t i = 0; i < other.semaphores.size(); ++i)
{
BPatch_variableExpr *semaphore =
process->getInheritedVariable(*other.semaphores[i]);
if (semaphore)
semaphores.push_back(semaphore);
}
// 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)
{
const dynprobe_location& probe = other.attached_probes[i];
if (probe.offset == 0)
instrument_utrace_dynprobe(probe);
}
}
// 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 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;
}
// Remove all BPatch snippets we've instrumented in the target
void
mutatee::remove_instrumentation()
{
if (!process || snippets.empty())
return;
process->beginInsertionSet();
for (size_t i = 0; i < snippets.size(); ++i)
process->deleteSnippet(snippets[i]);
process->finalizeInsertionSet(false);
snippets.clear();
// Decrement all semaphores
update_semaphores(-1);
semaphores.clear();
unload_stap_dso();
}
// Look up a function by name in the target and invoke it without parameters.
void
mutatee::call_function(const string& name)
{
vector<BPatch_snippet *> args;
call_function(name, args);
}
// Look up a function by name in the target and invoke it with parameters.
void
mutatee::call_function(const string& name,
const vector<BPatch_snippet *>& args)
{
if (!stap_dso)
return;
// process->oneTimeCode() requires that the process be stopped
mutatee_freezer mf(*this);
if (!is_stopped())
return;
vector<BPatch_function *> functions;
stap_dso->findFunction(name.c_str(), functions);
// XXX Dyninst can return multiple results, but we're not really
// expecting that... should we really call them all anyway?
for (size_t i = 0; i < functions.size(); ++i)
{
BPatch_funcCallExpr call(*functions[i], args);
staplog(3) << "calling function '" << name << "' in pid " << pid << endl;
process->oneTimeCode(call);
}
}
// Send a signal to the process.
int
mutatee::kill(int signal)
{
return pid ? ::kill(pid, signal) : -2;
}
void
mutatee::continue_execution()
{
if (is_stopped())
{
staplog(2) << "continuing execution of pid " << pid << endl;
process->continueExecution();
}
}
bool
mutatee::stop_execution()
{
if (process->isStopped())
{
// Process is already stopped, no need to do anything else.
return true;
}
staplog(2) << "stopping execution of pid " << pid << endl;
if (! process->stopExecution())
{
staplog(1) << "stopExecution on pid " << pid << " failed!" << endl;
return false;
}
if (! process->isStopped() || process->isTerminated())
{
staplog(1) << "couldn't stop pid " << pid << "!" << endl;
return false;
}
return true;
}
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */