From: Serguei Makarov Date: Fri, 22 Mar 2013 22:11:27 +0000 (-0400) Subject: PR15053: stapdyn -G global variable setting support (initial patch) X-Git-Tag: release-2.2~153 X-Git-Url: https://sourceware.org/git/?a=commitdiff_plain;h=bd2682885663e200833001fa0e7b67eb44114722;p=systemtap.git PR15053: stapdyn -G global variable setting support (initial patch) * buildrun.cxx (make_dyninst_run_command): pass module arguments on to stapdyn. * runtime/dyninst/runtime.h (set_int64_t): new function. * runtime/dyninst/stapdyn.h (stp_global_setter): function prototype. * stapdyn/mutator.cxx (mutator::mutator): add modoptions to mutator. (mutator::init_modoptions): new function. (mutator::run_module_init): call init_modoptions to initialize globals. * stapdyn/mutator.h (class mutator): add modoptions to mutator. * stapdyn/stapdyn.cxx (main): collect module arguments from command line. * translate.cxx (struct c_unparser): new emit_global_init_* functions. (c_unparser::emit_global_param): stapdyn no longer calls this function. (c_unparser::emit_global_init_setters): new function. (c_unparser::emit_global_init): emit default initial values in stapdyn mode. (c_unparser::emit_global_init_type): new function. (c_unparser::emit_module_init): copy initial values of stapdyn globals to shared memory as appropriate. (translate_pass): emit static-initialization struct for stapdyn, and also emit the setter function to access this struct. * translate.h (struct c_unparser): document new emit_global_init_* functions. --- diff --git a/buildrun.cxx b/buildrun.cxx index 61bdfa6ca..459580e1f 100644 --- a/buildrun.cxx +++ b/buildrun.cxx @@ -656,6 +656,9 @@ make_dyninst_run_command (systemtap_session& s, const string& remotedir, cmd.push_back((remotedir.empty() ? s.tmpdir : remotedir) + "/" + s.module_filename()); + // add module arguments + cmd.insert(cmd.end(), s.globalopts.begin(), s.globalopts.end()); + return cmd; } diff --git a/runtime/dyninst/runtime.h b/runtime/dyninst/runtime.h index 0deceb756..c23bd5446 100644 --- a/runtime/dyninst/runtime.h +++ b/runtime/dyninst/runtime.h @@ -133,6 +133,24 @@ static int stp_pthread_mutex_init_shared(pthread_mutex_t *mutex); #include "stat.c" #include "unwind.c" +/* Support function for int64_t module parameters. */ +static int set_int64_t(const char *val, int64_t *mp) +{ + char *endp; + long long ll; + + if (!val) + return -EINVAL; + + ll = strtoull(val, &endp, 0); + + if ((endp == val) || ((int64_t)ll != ll) || (*endp != '\0')) + return -EINVAL; + + *mp = (int64_t)ll; + return 0; +} + static int systemtap_module_init(void); static void systemtap_module_exit(void); diff --git a/runtime/dyninst/stapdyn.h b/runtime/dyninst/stapdyn.h index 0c98e2d4a..6f81df4b4 100644 --- a/runtime/dyninst/stapdyn.h +++ b/runtime/dyninst/stapdyn.h @@ -63,6 +63,10 @@ extern int enter_dyninst_utrace_probe(uint64_t index, struct pt_regs *regs); extern const char* stp_dyninst_shm_init(void); extern int stp_dyninst_shm_connect(const char* name); +/* The following function is dynamically generated by systemtap, and + used by stapdyn to modify global variables at module startup only + (that is, *before* running stp_dyninst_session_init()): */ +extern int stp_global_setter(const char *name, const char *value); #define STAPDYN_PROBE_ALL_FLAGS (uint64_t)(STAPDYN_PROBE_FLAG_RETURN \ | STAPDYN_PROBE_FLAG_PROC_BEGIN | STAPDYN_PROBE_FLAG_PROC_END \ diff --git a/stapdyn/mutator.cxx b/stapdyn/mutator.cxx index d1445ed3d..9d95bfac3 100644 --- a/stapdyn/mutator.cxx +++ b/stapdyn/mutator.cxx @@ -110,9 +110,11 @@ setup_signals (void) } -mutator:: mutator (const string& module_name): +mutator:: mutator (const string& module_name, + vector& module_options): module(NULL), module_name(resolve_path(module_name)), - p_target_created(false), signal_count(0), utrace_enter_fn(NULL) + modoptions(module_options), p_target_created(false), + signal_count(0), utrace_enter_fn(NULL) { // NB: dlopen does a library-path search if the filename doesn't have any // path components, which is why we use resolve_path(module_name) @@ -279,6 +281,46 @@ mutator::attach_process(pid_t pid) return true; } +bool +mutator::init_modoptions() +{ + typeof(&stp_global_setter) global_setter = NULL; + set_dlsym(global_setter, module, "stp_global_setter", false); + + if (global_setter == NULL) + { + // Hypothetical backwards compatibility with older stapdyn: + stapwarn() << "compiled module does not support -G globals" << endl; + return false; + } + + for (vector::iterator it = modoptions.begin(); + it != modoptions.end(); it++) + { + string modoption = *it; + + // Parse modoption as "name=value" + // XXX: compare whether this behaviour fits safety regex in buildrun.cxx + string::size_type separator = modoption.find('='); + if (separator == string::npos) + { + stapwarn() << "could not parse module option '" << modoption << "'" << endl; + return false; // XXX: perhaps ignore the option instead? + } + string name = modoption.substr(0, separator); + string value = modoption.substr(separator+1); + + int rc = global_setter(name.c_str(), value.c_str()); + if (rc != 0) + { + stapwarn() << "incorrect module option '" << modoption << "'" << endl; + return false; // XXX: perhaps ignore the option instead? + } + } + + return true; +} + // Initialize the module session bool mutator::run_module_init() @@ -325,6 +367,10 @@ mutator::run_module_init() return false; } + // Before init runs, set any custom variables + if (!modoptions.empty() && !init_modoptions()) + return false; + int rc = session_init(); if (rc) { diff --git a/stapdyn/mutator.h b/stapdyn/mutator.h index b990bfc91..280c6080f 100644 --- a/stapdyn/mutator.h +++ b/stapdyn/mutator.h @@ -34,6 +34,7 @@ class mutator { void* module; // the locally dlopened probe module std::string module_name; // the filename of the probe module + std::vector modoptions; // custom globals from -G option std::string module_shmem; // the global name of this module's shared memory std::vector targets; // the probe targets in the module @@ -47,6 +48,9 @@ class mutator { mutator (const mutator& other); mutator& operator= (const mutator& other); + // Initialize the module global variables + bool init_modoptions(); + // Initialize the module session bool run_module_init(); @@ -66,7 +70,8 @@ class mutator { typeof(&enter_dyninst_utrace_probe) utrace_enter_fn; public: - mutator (const std::string& module_name); + mutator (const std::string& module_name, + std::vector& module_options); ~mutator (); // Load the stap module and initialize all probe info. diff --git a/stapdyn/stapdyn.cxx b/stapdyn/stapdyn.cxx index 9dab47790..da43578b0 100644 --- a/stapdyn/stapdyn.cxx +++ b/stapdyn/stapdyn.cxx @@ -91,8 +91,15 @@ main(int argc, char * const argv[]) } // The first non-option is our stap module, required. - if (optind == argc - 1) - module = argv[optind]; + if (optind < argc) + module = argv[optind++]; + + // Remaining non-options, if any, specify global variables. + vector modoptions; + while (optind < argc) + { + modoptions.push_back(string(argv[optind++])); + } if (!module || (command && pid)) usage (1); @@ -103,7 +110,7 @@ main(int argc, char * const argv[]) if (!check_dyninst_sebools()) return 1; - auto_ptr session(new mutator(module)); + auto_ptr session(new mutator(module, modoptions)); if (!session.get() || !session->load()) { staperror() << "failed to create the mutator!" << endl; diff --git a/translate.cxx b/translate.cxx index 04e1f80ff..72ffb2ea8 100644 --- a/translate.cxx +++ b/translate.cxx @@ -90,7 +90,9 @@ struct c_unparser: public unparser, public visitor void emit_common_header (); void emit_global (vardecl* v); void emit_global_init (vardecl* v); + void emit_global_init_type (vardecl *v); void emit_global_param (vardecl* v); + void emit_global_init_setters (); void emit_functionsig (functiondecl* v); void emit_module_init (); void emit_module_refresh (); @@ -1480,9 +1482,8 @@ c_unparser::emit_global_param (vardecl *v) { string vn = c_globalname (v->name); - // XXX: No module parameters with dyninst. - if (session->runtime_usermode_p()) - return; + // For dyninst, use the emit_global_init_* functionality instead. + assert (!session->runtime_usermode_p()); // NB: systemtap globals can collide with linux macros, // e.g. VM_FAULT_MAJOR. We want the parameter name anyway. This @@ -1506,6 +1507,41 @@ c_unparser::emit_global_param (vardecl *v) } +void +c_unparser::emit_global_init_setters () +{ + // Hack for dyninst module params: setter function forms a little + // linear lookup table ditty to find a global variable by name. + o->newline() << "int stp_global_setter (const char *name, const char *value) {"; + o->newline(1); + for (unsigned i=0; iglobals.size(); i++) + { + vardecl* v = session->globals[i]; + if (v->arity > 0) continue; + if (v->type != pe_string && v->type != pe_long) continue; + + // Do not mangle v->name for the comparison! + o->line() << "if (0 == strcmp(name,\"" << v->name << "\"))" << " {"; + + o->indent(1); + if (v->type == pe_string) + { + c_assign("stp_global_init." + c_globalname(v->name), "value", pe_string, "BUG: global module param", v->tok); + o->newline() << "return 0;"; + } + else + { + o->newline() << "return set_int64_t(value, &stp_global_init." << c_globalname(v->name) << ");"; + } + + o->newline(-1) << "} else "; + } + o->line() << "return -EINVAL;"; + o->newline(-1) << "}"; + o->newline(); +} + + void c_unparser::emit_global (vardecl *v) { @@ -1555,10 +1591,30 @@ c_unparser::emit_global_init (vardecl *v) v->init->visit(this); o->line() << ","; } + else if (v->arity == 0 && session->runtime_usermode_p()) + { + // For dyninst: always try to put a default value into the initial + // static structure, so we don't have to guess if it was customized. + if (v->type == pe_long) + o->newline() << "." << c_globalname (v->name) << " = 0,"; + else if (v->type == pe_string) + o->newline() << "." << c_globalname (v->name) << " = { '\\0' },"; // XXX: "" + } // The lock and lock_skip_count are handled in emit_module_init. } +void +c_unparser::emit_global_init_type (vardecl *v) +{ + // We can only statically initialize some scalars. + if (v->arity == 0) // ... although we still allow !v->init here. + { + o->newline() << c_typename(v->type) << " " << c_globalname(v->name) << ";"; + } +} + + void c_unparser::emit_functionsig (functiondecl* v) @@ -1705,8 +1761,9 @@ c_unparser::emit_module_init () vardecl* v = session->globals[i]; if (v->index_types.size() > 0) o->newline() << getmap (v).init(); - else if (v->init && session->runtime_usermode_p()) - c_assign(getvar (v).value(), v->init, "global initialization"); + else if (session->runtime_usermode_p() && v->arity == 0 + && (v->type == pe_long || v->type == pe_string)) + c_assign(getvar (v).value(), "stp_global_init." + c_globalname(v->name), v->type, "BUG: global initialization", v->tok); else o->newline() << getvar (v).init(); // NB: in case of failure of allocation, "rc" will be set to non-zero. @@ -6690,19 +6747,29 @@ translate_pass (systemtap_session& s) // We only need to statically initialize globals in kernel modules, // where module parameters may want to override the script's value. In - // stapdyn, the globals are actually part of the dynamic shared memory. - if (!s.runtime_usermode_p()) + // stapdyn, the globals are actually part of the dynamic shared memory, + // and the static structure is merely used as a source of default values. + s.op->newline(); + if (!s.runtime_usermode_p ()) + s.op->newline() << "static struct stp_globals stp_global = {"; + else + { + s.op->newline() << "static struct {"; + s.op->indent(1); + for (unsigned i=0; iemit_global_init_type (s.globals[i]); + } + s.op->newline(-1) << "} stp_global_init = {"; + } + s.op->newline(1); + for (unsigned i=0; inewline(); - s.op->newline() << "static struct stp_globals stp_global = {"; - s.op->newline(1); - for (unsigned i=0; iemit_global_init (s.globals[i]); - } - s.op->newline(-1) << "};"; + assert_no_interrupts(); + s.up->emit_global_init (s.globals[i]); } + s.op->newline(-1) << "};"; s.op->assert_0_indent(); } @@ -6883,12 +6950,15 @@ translate_pass (systemtap_session& s) s.op->assert_0_indent(); - // PR10298: attempt to avoid collisions with symbols - for (unsigned i=0; inewline(); - s.up->emit_global_param (s.globals[i]); - } + if (s.runtime_usermode_p()) + s.up->emit_global_init_setters(); + else + // PR10298: attempt to avoid collisions with symbols + for (unsigned i=0; inewline(); + s.up->emit_global_param (s.globals[i]); + } s.op->assert_0_indent(); } catch (const semantic_error& e) diff --git a/translate.h b/translate.h index 72ed3fab8..34124c1b5 100644 --- a/translate.h +++ b/translate.h @@ -76,9 +76,20 @@ struct unparser virtual void emit_global_init (vardecl* v) = 0; // }; - virtual void emit_global_param (vardecl* v) = 0; + // struct { + virtual void emit_global_init_type (vardecl* v) = 0; + // TYPE s_NAME; + // } global_init = { + // ... + // }; // variation of the above for dyninst static-initialization + + virtual void emit_global_param (vardecl* v) = 0; // for kernel // module_param_... -- at end of file + virtual void emit_global_init_setters () = 0; // for dyninst + // int stp_global_setter (const char *name, const char *value); + // -- at end of file; returns -EINVAL on error + virtual void emit_functionsig (functiondecl* v) = 0; // static void function_NAME (context* c);