From e00f3fb72501daed6e0df97675a57560c509b645 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 27 Sep 2012 17:55:05 -0700 Subject: [PATCH] PR14489: Revamp stapdyn probe metadata Rather than having a fixed data structure for stapdyn to read from the module, now stapdyn queries the module dynamically for its data. Thus, we dlopen the module within stapdyn, then dlsym a few query functions and use those to enumerate all of the probe data. * runtime/dyninst/stapdyn.h: Add functions for metadata. * runtime/dyninst/uprobes.h: New, define module internal datastructures. * runtime/dyninst/uprobes.c: Implement the metadata functions. * tapsets.cxx: Generate probe metadat in the new datastructures. * stapdyn/stapdyn.cxx: Query probes using the new functions. * stapdyn/Makefile.am: No longer need -lelf for stapdyn. * stapdyn/Makefile.in: Regenerate. --- runtime/dyninst/stapdyn.h | 19 +++- runtime/dyninst/uprobes.c | 54 +++++++--- runtime/dyninst/uprobes.h | 34 ++++++ stapdyn/Makefile.am | 2 +- stapdyn/Makefile.in | 2 +- stapdyn/stapdyn.cxx | 214 ++++++++++++++++++++++---------------- tapsets.cxx | 50 +++++---- 7 files changed, 252 insertions(+), 123 deletions(-) create mode 100644 runtime/dyninst/uprobes.h diff --git a/runtime/dyninst/stapdyn.h b/runtime/dyninst/stapdyn.h index b2183116f..5beb0a552 100644 --- a/runtime/dyninst/stapdyn.h +++ b/runtime/dyninst/stapdyn.h @@ -19,14 +19,31 @@ extern "C" { /* These are declarations of all interfaces that stapdyn may call in the - * module, either directly or via dyninst in the mutatee. */ + * module, either directly or via dyninst in the mutatee. To maintain + * compatibility as much as possible, function signatures should not be + * changed between releases, only deprecated/renamed as necessary. + */ + + +/* STAP 2.0 : */ extern int stp_dyninst_session_init(void); extern void stp_dyninst_session_exit(void); +extern uint64_t stp_dyninst_target_count(void); +extern const char* stp_dyninst_target_path(uint64_t index); + +extern uint64_t stp_dyninst_probe_count(void); +extern uint64_t stp_dyninst_probe_target(uint64_t index); +extern uint64_t stp_dyninst_probe_offset(uint64_t index); +extern uint64_t stp_dyninst_probe_semaphore(uint64_t index); + extern int enter_dyninst_uprobe(uint64_t index, struct pt_regs *regs); +/* STAP 2.x : */ + + #ifdef __cplusplus } #endif diff --git a/runtime/dyninst/uprobes.c b/runtime/dyninst/uprobes.c index 628f27f23..bf54e5740 100644 --- a/runtime/dyninst/uprobes.c +++ b/runtime/dyninst/uprobes.c @@ -13,22 +13,52 @@ /* STAPDU: SystemTap Dyninst Uprobes */ -/* A target has the dyninst view of what we want to probe. +/* These functions implement the ABI in stapdyn.h * - * NB: This becomes an ABI in the module with stapdyn, so be cautious. It - * might be better to go with a more flexible format - perhaps JSON? + * NB: tapsets.cxx will generate two arrays used here: + * struct stapdu_target stapdu_targets[]; + * struct stapdu_probe stapdu_probes[]; */ -struct stapdu_target { - char filename[240]; - uint64_t offset; /* the probe offset within the file */ - uint64_t sdt_sem_offset; /* the semaphore offset from process->base */ -}; -/* A consumer has the runtime information for a probe. */ -struct stapdu_consumer { - struct stap_probe * const probe; -}; +uint64_t stp_dyninst_target_count(void) +{ + return ARRAY_SIZE(stapdu_targets); +} + +const char* stp_dyninst_target_path(uint64_t index) +{ + if (index >= stp_dyninst_target_count()) + return NULL; + return stapdu_targets[index].path; +} + + +uint64_t stp_dyninst_probe_count(void) +{ + return ARRAY_SIZE(stapdu_probes); +} + +uint64_t stp_dyninst_probe_target(uint64_t index) +{ + if (index >= stp_dyninst_probe_count()) + return (uint64_t)-1; + return stapdu_probes[index].target; +} + +uint64_t stp_dyninst_probe_offset(uint64_t index) +{ + if (index >= stp_dyninst_probe_count()) + return (uint64_t)-1; + return stapdu_probes[index].offset; +} + +uint64_t stp_dyninst_probe_semaphore(uint64_t index) +{ + if (index >= stp_dyninst_probe_count()) + return (uint64_t)-1; + return stapdu_probes[index].semaphore; +} #endif /* _UPROBES_DYNINST_C_ */ diff --git a/runtime/dyninst/uprobes.h b/runtime/dyninst/uprobes.h new file mode 100644 index 000000000..0a89cbb38 --- /dev/null +++ b/runtime/dyninst/uprobes.h @@ -0,0 +1,34 @@ +/* -*- linux-c -*- + * Common data types for dyninst-based uprobes + * Copyright (C) 2012 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. + */ + +#ifndef _UPROBES_DYNINST_H_ +#define _UPROBES_DYNINST_H_ + +/* STAPDU: SystemTap Dyninst Uprobes */ + + +/* A stapdu_target describes the files to probe */ +struct stapdu_target { + const char * const path; + /* buildid? */ +}; + + +/* A stapdu_probe has the individual information for a probe. */ +struct stapdu_probe { + uint64_t target; /* the target index for this probe */ + uint64_t offset; /* the probe offset within the file */ + uint64_t semaphore; /* the sdt semaphore offset within the file */ + struct stap_probe * const probe; +}; + + +#endif /* _UPROBES_DYNINST_H_ */ + diff --git a/stapdyn/Makefile.am b/stapdyn/Makefile.am index a54fb19ee..5d5cbca86 100644 --- a/stapdyn/Makefile.am +++ b/stapdyn/Makefile.am @@ -30,7 +30,7 @@ noinst_PROGRAMS += dynsdt stapdyn_SOURCES = stapdyn.cxx dynutil.cxx ../util.cxx stapdyn_CXXFLAGS = $(AM_CXXFLAGS) @DYNINST_CXXFLAGS@ stapdyn_LDFLAGS = $(AM_LDFLAGS) @DYNINST_LDFLAGS@ -stapdyn_LDADD = -ldl -lelf $(dyninst_LIBS) +stapdyn_LDADD = -ldl $(dyninst_LIBS) dynsdt_SOURCES = dynsdt.cxx dynutil.cxx ../util.cxx dynsdt_CXXFLAGS = $(AM_CXXFLAGS) @DYNINST_CXXFLAGS@ diff --git a/stapdyn/Makefile.in b/stapdyn/Makefile.in index 1700dcb56..f57e32965 100644 --- a/stapdyn/Makefile.in +++ b/stapdyn/Makefile.in @@ -293,7 +293,7 @@ AM_LDFLAGS = @PIELDFLAGS@ $(am__append_2) @HAVE_DYNINST_TRUE@stapdyn_SOURCES = stapdyn.cxx dynutil.cxx ../util.cxx @HAVE_DYNINST_TRUE@stapdyn_CXXFLAGS = $(AM_CXXFLAGS) @DYNINST_CXXFLAGS@ @HAVE_DYNINST_TRUE@stapdyn_LDFLAGS = $(AM_LDFLAGS) @DYNINST_LDFLAGS@ -@HAVE_DYNINST_TRUE@stapdyn_LDADD = -ldl -lelf $(dyninst_LIBS) +@HAVE_DYNINST_TRUE@stapdyn_LDADD = -ldl $(dyninst_LIBS) @HAVE_DYNINST_TRUE@dynsdt_SOURCES = dynsdt.cxx dynutil.cxx ../util.cxx @HAVE_DYNINST_TRUE@dynsdt_CXXFLAGS = $(AM_CXXFLAGS) @DYNINST_CXXFLAGS@ @HAVE_DYNINST_TRUE@dynsdt_LDFLAGS = $(AM_LDFLAGS) @DYNINST_LDFLAGS@ diff --git a/stapdyn/stapdyn.cxx b/stapdyn/stapdyn.cxx index 04a2d5f75..90e8686ff 100644 --- a/stapdyn/stapdyn.cxx +++ b/stapdyn/stapdyn.cxx @@ -7,8 +7,6 @@ extern "C" { #include #include #include -#include -#include #include #include #include @@ -41,6 +39,36 @@ using namespace std; static const char* program = "stapdyn"; + +struct stapdyn_uprobe_probe { + uint64_t index, offset, semaphore; + stapdyn_uprobe_probe(uint64_t index, uint64_t offset, uint64_t semaphore): + index(index), offset(offset), semaphore(semaphore) {} +}; + + +struct stapdyn_uprobe_target { + string path; + vector probes; + stapdyn_uprobe_target(const char* path): path(path) {} +}; + + +template void +set_dlsym(T*& pointer, void* handle, const char* symbol, bool required=true) +{ + const char* err = dlerror(); // clear previous errors + pointer = reinterpret_cast(dlsym(handle, symbol)); + if (required) + { + if ((err = dlerror())) + throw runtime_error(err); + if (pointer == NULL) + throw runtime_error(string(symbol) + " is NULL"); + } +} + + static void __attribute__ ((noreturn)) usage (int rc) { @@ -65,24 +93,16 @@ call_inferior_function(BPatch_process *app, const string& name) } } -// XXX: copied from runtime/dyninst/uprobes-dyninst.c -struct stapdu_target { - char filename[240]; - uint64_t offset; /* the probe offset within the file */ - uint64_t sdt_sem_offset; /* the semaphore offset from process->base */ -}; - static void -instrument_uprobes(BPatch_process *app, Elf_Data* data) +instrument_uprobes(BPatch_process *app, + const vector& targets) { - if (!app || !data) return; + if (!app || targets.empty()) + return; BPatch_image* image = app->getImage(); - if (!image) return; - - const stapdu_target* targets = static_cast(data->d_buf); - size_t ntargets = data->d_size / sizeof(stapdu_target); - if (!ntargets) return; + if (!image) + return; map file_objects; vector objects; @@ -101,110 +121,126 @@ instrument_uprobes(BPatch_process *app, Elf_Data* data) BPatch_function& enter_function = *functions[0]; app->beginInsertionSet(); - for (size_t i = 0; i < ntargets; ++i) + for (uint64_t i = 0; i < targets.size(); ++i) { - const stapdu_target* target = &targets[i]; - clog << "found target " << target->filename - << " 0x" << hex << target->offset << dec << endl; + const stapdyn_uprobe_target& target = targets[i]; - BPatch_object* object = file_objects[target->filename]; + BPatch_object* object = file_objects[target.path]; if (!object) { - clog << "no object named " << target->filename << endl; + clog << "no object named " << target.path << endl; continue; } + clog << "found target " << target.path << ", inserting " + << target.probes.size() << " probes" << endl; - Dyninst::Address address = object->fileOffsetToAddr(target->offset); - if (address == BPatch_object::E_OUT_OF_BOUNDS) + for (uint64_t j = 0; j < target.probes.size(); ++j) { - clog << "couldn't convert 0x" << hex << target->offset << dec - << " in " << target->filename << " to an address" << endl; - continue; + const stapdyn_uprobe_probe& probe = target.probes[j]; + + Dyninst::Address address = object->fileOffsetToAddr(probe.offset); + if (address == BPatch_object::E_OUT_OF_BOUNDS) + { + clog << "couldn't convert " << lex_cast_hex(probe.offset) + << " in " << target.path << " to an address" << endl; + continue; + } + + vector points; + object->findPoints(address, points); + if (points.empty()) + { + clog << "couldn't find instrumentation point for address " + << lex_cast_hex(address) << " at offset " + << lex_cast_hex(probe.offset) << " in " << target.path << endl; + continue; + } + + BPatch_Vector args; + args.push_back(new BPatch_constExpr((int64_t)probe.index)); + args.push_back(new BPatch_constExpr((void*)NULL)); // pt_regs + BPatch_funcCallExpr call(enter_function, args); + app->insertSnippet(call, points); + + // XXX write the semaphore too! } - - vector points; - object->findPoints(address, points); - if (points.empty()) - { - clog << "couldn't find instrumentation point 0x" << hex << target->offset << dec - << " in " << target->filename << endl; - continue; - } - - BPatch_Vector args; - args.push_back(new BPatch_constExpr((int64_t)i)); // probe index - args.push_back(new BPatch_constExpr((void*)NULL)); // pt_regs - BPatch_funcCallExpr call(enter_function, args); - app->insertSnippet(call, points); - - // XXX write the semaphore too! } app->finalizeInsertionSet(false); } -static void -find_uprobes(BPatch_process *app, const string& module) +static vector +find_uprobes(void* module) { - int fd = -1; - size_t shstrndx; - Elf* elf = NULL; - Elf_Scn* scn = NULL; + vector targets; + + typeof(&stp_dyninst_target_count) target_count = NULL; + typeof(&stp_dyninst_target_path) target_path = NULL; + + typeof(&stp_dyninst_probe_count) probe_count = NULL; + typeof(&stp_dyninst_probe_target) probe_target = NULL; + typeof(&stp_dyninst_probe_offset) probe_offset = NULL; + typeof(&stp_dyninst_probe_semaphore) probe_semaphore = NULL; - fd = open(module.c_str(), O_RDONLY); - if (fd < 0) + set_dlsym(target_count, module, "stp_dyninst_target_count", false); + if (target_count == NULL) // no uprobes + return targets; + + try { - clog << "can't open " << module << endl; - goto out; + // if target_count exists, the rest of these should too + set_dlsym(target_path, module, "stp_dyninst_target_path"); + set_dlsym(probe_count, module, "stp_dyninst_probe_count"); + set_dlsym(probe_target, module, "stp_dyninst_probe_target"); + set_dlsym(probe_offset, module, "stp_dyninst_probe_offset"); + set_dlsym(probe_semaphore, module, "stp_dyninst_probe_semaphore"); } - - elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); - if (!elf) + catch (runtime_error& e) { - clog << "can't read elf " << module << endl; - goto out_fd; + clog << program << ": dlsym " << e.what() << endl; + return targets; } - elf_getshdrstrndx (elf, &shstrndx); + const uint64_t ntargets = target_count(); + for (uint64_t i = 0; i < ntargets; ++i) + targets.push_back(stapdyn_uprobe_target(target_path(i))); + + const uint64_t nprobes = probe_count(); + for (uint64_t i = 0; i < nprobes; ++i) + { + uint64_t ti = probe_target(i); + stapdyn_uprobe_probe p(i, probe_offset(i), probe_semaphore(i)); + if (ti < ntargets) + targets[ti].probes.push_back(p); + } - scn = NULL; - while ((scn = elf_nextscn(elf, scn))) +#if 0 // verbose debug + for (uint64_t i = 0; i < ntargets; ++i) { - GElf_Shdr shdr; - if (gelf_getshdr (scn, &shdr) == NULL) - continue; - if (shdr.sh_type != SHT_PROGBITS) - continue; - if (!(shdr.sh_flags & SHF_ALLOC)) - continue; - if (!strcmp(".stap_dyninst", elf_strptr(elf, shstrndx, shdr.sh_name))) - instrument_uprobes(app, elf_rawdata(scn, NULL)); + stapdyn_uprobe_target& t = targets[i]; + clog << "target " << t.path << " has " + << t.probes.size() << " probes" << endl; + for (uint64_t j = 0; j < t.probes.size(); ++j) + clog << " offset:" << (void*)t.probes[i].offset + << " semaphore:" << t.probes[i].semaphore << endl; } +#endif - elf_end(elf); -out_fd: - close(fd); -out: - ; + return targets; } static int run_simple_module(void* module) { - typedef typeof(stp_dyninst_session_init) init_t; - typedef typeof(stp_dyninst_session_exit) exit_t; - - const char* err = dlerror(); // clear previous errors - init_t *session_init = (init_t*)dlsym(module, "stp_dyninst_session_init"); - if ((err = dlerror())) + typeof(&stp_dyninst_session_init) session_init = NULL; + typeof(&stp_dyninst_session_exit) session_exit = NULL; + try { - clog << program << ": dlsym " << err << endl; - return 1; + set_dlsym(session_init, module, "stp_dyninst_session_init"); + set_dlsym(session_exit, module, "stp_dyninst_session_exit"); } - - exit_t *session_exit = (exit_t*)dlsym(module, "stp_dyninst_session_exit"); - if ((err = dlerror())) + catch (runtime_error& e) { - clog << program << ": dlsym " << err << endl; + clog << program << ": dlsym " << e.what() << endl; return 1; } @@ -296,7 +332,7 @@ main(int argc, char * const argv[]) app->loadLibrary(module); - find_uprobes(app, module); + instrument_uprobes(app, find_uprobes(dlmodule)); call_inferior_function(app, "stp_dyninst_session_init"); diff --git a/tapsets.cxx b/tapsets.cxx index 65e6974d2..f55f73ed1 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -7701,49 +7701,61 @@ uprobe_derived_probe_group::emit_module_dyninst_decls (systemtap_session& s) if (probes.empty()) return; s.op->newline() << "/* ---- dyninst uprobes ---- */"; emit_module_maxuprobes (s); - s.op->newline() << "#include \"dyninst/uprobes.c\""; + s.op->newline() << "#include \"dyninst/uprobes.h\""; - // Discover and declare targets for dyninst to use - s.op->newline() << "const struct stapdu_target " - << "stap_dyninst_uprobe_targets[] " - << "__attribute__ ((section (\".stap_dyninst\"))) " - << "= {"; + // Assign "task-finder" numbers as we build up the stapdu_target table. + // This means we process probes[] in two passes. + map module_index; + unsigned module_index_ctr = 0; + + // Discover and declare file targets + s.op->newline() << "static const struct stapdu_target stapdu_targets[] = {"; s.op->indent(1); for (unsigned i=0; inewline() << "{"; - // XXX pid/procname filtering? - s.op->line() << " .filename=" << lex_cast_qstring(p->module) << ","; - s.op->line() << " .offset=(loff_t)0x" << hex << p->addr << dec << "ULL,"; - if (p->sdt_semaphore_addr) - s.op->line() << " .sdt_sem_offset=(loff_t)0x" - << hex << p->sdt_semaphore_addr << dec << "ULL,"; - s.op->line() << " },"; + string pbmkey = make_pbm_key (p); + if (module_index.find (pbmkey) == module_index.end()) + { + module_index[pbmkey] = module_index_ctr++; + + s.op->newline() << "{"; + // NB: it's essential that make_pbm_key() use all of and + // only the same fields as we're about to emit. + s.op->line() << " .path=" << lex_cast_qstring(p->module) << ","; + // XXX pid/procname filtering? + s.op->line() << " },"; + } } s.op->newline(-1) << "};"; s.op->assert_0_indent(); - // Declare the actual probes for the runtime to use - s.op->newline() << "static struct stapdu_consumer " - << "stap_dyninst_uprobe_consumers[] = {"; + // Declare the individual probes + s.op->newline() << "static struct stapdu_probe stapdu_probes[] = {"; s.op->indent(1); for (unsigned i=0; inewline() << "{"; + s.op->line() << " .target=" << index << ","; + s.op->line() << " .offset=" << lex_cast_hex(p->addr) << "ULL,"; + if (p->sdt_semaphore_addr) + s.op->line() << " .semaphore=" + << lex_cast_hex(p->sdt_semaphore_addr) << "ULL,"; s.op->line() << " .probe=" << common_probe_init (p) << ","; s.op->line() << " },"; } s.op->newline(-1) << "};"; s.op->assert_0_indent(); + s.op->newline() << "#include \"dyninst/uprobes.c\""; + // Write the probe handler. // NB: not static, so dyninst can find it s.op->newline() << "int enter_dyninst_uprobe " << "(uint64_t index, struct pt_regs *regs) {"; - s.op->newline(1) << "struct stapdu_consumer *sup = " - << "&stap_dyninst_uprobe_consumers[index];"; + s.op->newline(1) << "struct stapdu_probe *sup = &stapdu_probes[index];"; common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "sup->probe", "stp_probe_type_uprobe"); s.op->newline() << "c->uregs = regs;"; -- 2.43.5