summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--buildrun.cxx75
-rw-r--r--buildrun.h1
-rw-r--r--session.h2
-rw-r--r--stapprobes.5.in36
-rw-r--r--tapsets.cxx768
5 files changed, 861 insertions, 21 deletions
diff --git a/buildrun.cxx b/buildrun.cxx
index 973343cd..2685949d 100644
--- a/buildrun.cxx
+++ b/buildrun.cxx
@@ -24,6 +24,7 @@ extern "C" {
#include <unistd.h>
#include <string.h>
#include <errno.h>
+#include <glob.h>
}
@@ -336,4 +337,78 @@ run_pass (systemtap_session& s)
return rc;
}
+
+// Build a tiny kernel module to query tracepoints
+int
+make_tracequery(systemtap_session& s, string& name)
+{
+ // create a subdirectory for the module
+ string dir(s.tmpdir + "/tracequery");
+ if (create_dir(dir.c_str()) != 0)
+ {
+ if (! s.suppress_warnings)
+ cerr << "Warning: failed to create directory for querying tracepoints." << endl;
+ return 1;
+ }
+
+ name = dir + "/tracequery.ko";
+
+ // create a simple Makefile
+ string makefile(dir + "/Makefile");
+ ofstream omf(makefile.c_str());
+ omf << "EXTRA_CFLAGS := -g" << endl; // force debuginfo generation
+ omf << "obj-m := tracequery.o" << endl;
+ omf.close();
+
+ // create our source file
+ string source(dir + "/tracequery.c");
+ ofstream osrc(source.c_str());
+ osrc << "#include <linux/module.h>" << endl;
+ osrc << "#ifdef CONFIG_TRACEPOINTS" << endl;
+ osrc << "#include <linux/tracepoint.h>" << endl;
+
+ // override DECLARE_TRACE to synthesize probe functions for us
+ osrc << "#undef DECLARE_TRACE" << endl;
+ osrc << "#define DECLARE_TRACE(name, proto, args) \\" << endl;
+ osrc << " void stapprobe_##name(proto) {}" << endl;
+
+ // older tracepoints used DEFINE_TRACE, so redirect that too
+ osrc << "#undef DEFINE_TRACE" << endl;
+ osrc << "#define DEFINE_TRACE(name, proto, args) \\" << endl;
+ osrc << " DECLARE_TRACE(name, TPPROTO(proto), TPARGS(args))" << endl;
+
+ // dynamically pull in all tracepoint headers from include/trace/
+ glob_t trace_glob;
+ string glob_str(s.kernel_build_tree + "/include/trace/*.h");
+ glob(glob_str.c_str(), 0, NULL, &trace_glob);
+ for (unsigned i = 0; i < trace_glob.gl_pathc; ++i)
+ {
+ string header(basename(trace_glob.gl_pathv[i]));
+
+ // filter out a few known "internal-only" headers
+ if (header == "trace_events.h")
+ continue;
+ if (header.find("_event_types.h") != string::npos)
+ continue;
+
+ osrc << "#include <trace/" << header << ">" << endl;
+ }
+ globfree(&trace_glob);
+
+ // finish up the module source
+ osrc << "#endif /* CONFIG_TRACEPOINTS */" << endl;
+ osrc << "int init_module(void) { return 0; }" << endl;
+ osrc << "void cleanup_module(void) {}" << endl;
+ osrc << "MODULE_DESCRIPTION(\"tracepoint query\");" << endl;
+ osrc << "MODULE_LICENSE(\"GPL\");" << endl;
+ osrc.close();
+
+ // make the module
+ string make_cmd = "make -C '" + s.kernel_build_tree + "'"
+ + " M='" + dir + "' modules";
+ if (s.verbose < 4)
+ make_cmd += " >/dev/null 2>&1";
+ return run_make_cmd(s, make_cmd);
+}
+
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/buildrun.h b/buildrun.h
index 98c81251..88127449 100644
--- a/buildrun.h
+++ b/buildrun.h
@@ -14,6 +14,7 @@
int compile_pass (systemtap_session& s);
int run_pass (systemtap_session& s);
+int make_tracequery(systemtap_session& s, std::string& name);
#endif // BUILDRUN_H
diff --git a/session.h b/session.h
index e6d8eb82..8fa491fd 100644
--- a/session.h
+++ b/session.h
@@ -37,6 +37,7 @@ struct task_finder_derived_probe_group;
struct timer_derived_probe_group;
struct profile_derived_probe_group;
struct mark_derived_probe_group;
+struct tracepoint_derived_probe_group;
struct hrtimer_derived_probe_group;
struct perfmon_derived_probe_group;
struct procfs_derived_probe_group;
@@ -162,6 +163,7 @@ struct systemtap_session
timer_derived_probe_group* timer_derived_probes;
profile_derived_probe_group* profile_derived_probes;
mark_derived_probe_group* mark_derived_probes;
+ tracepoint_derived_probe_group* tracepoint_derived_probes;
hrtimer_derived_probe_group* hrtimer_derived_probes;
perfmon_derived_probe_group* perfmon_derived_probes;
procfs_derived_probe_group* procfs_derived_probes;
diff --git a/stapprobes.5.in b/stapprobes.5.in
index 0854be53..0daeb973 100644
--- a/stapprobes.5.in
+++ b/stapprobes.5.in
@@ -548,6 +548,42 @@ The marker format string associated with a marker is available in
And also the marker name string is avalable in
.BR $name .
+.SS TRACEPOINTS
+
+This family of probe points hooks up to static probing tracepoints
+inserted into the kernel or modules. As with markers, these
+tracepoints are special macro calls inserted by kernel developers to
+make probing faster and more reliable than with DWARF-based probes,
+and DWARF debugging information is not required to probe tracepoints.
+Tracepoints have an extra advantage of more strongly-typed parameters
+than markers.
+
+Tracepoint probes begin with
+.BR kernel .
+The next part names the tracepoint itself:
+.BR trace("name") .
+The tracepoint name string, which may contain the usual wildcard
+characters, is matched against the names defined by the kernel
+developers in the tracepoint header files.
+
+The handler associated with a tracepoint-based probe may read the
+optional parameters specified at the macro call site. These are
+named according to the declaration by the tracepoint author. For
+example, the tracepoint probe
+.BR kernel.trace("sched_switch")
+provides the parameters
+.BR $rq ", " $prev ", and " $next .
+If the parameter is a complex type, as in a struct pointer, then a
+script can access fields with the same syntax as DWARF $target
+variables. Also, tracepoint parameters cannot be modified, but in
+guru-mode a script may modify fields of parameters.
+
+The name of the tracepoint is available in
+.BR $$name ,
+and a string of name=value pairs for all parameters of the tracepoint
+is available in
+.BR $$vars .
+
.SS PERFORMANCE MONITORING HARDWARE
The perfmon family of probe points is used to access the performance
diff --git a/tapsets.cxx b/tapsets.cxx
index 1c098425..defaf385 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -15,6 +15,7 @@
#include "translate.h"
#include "session.h"
#include "util.h"
+#include "buildrun.h"
#include "dwarf_wrappers.h"
#include "auto_free.h"
@@ -465,6 +466,7 @@ static string TOK_STATEMENT("statement");
static string TOK_ABSOLUTE("absolute");
static string TOK_PROCESS("process");
static string TOK_MARK("mark");
+static string TOK_TRACE("trace");
static string TOK_LABEL("label");
// Can we handle this query with just symbol-table info?
@@ -958,6 +960,9 @@ struct dwflpp
void setup_user(string module_name, bool debuginfo_needed = true)
{
+ if (! sess.module_cache)
+ sess.module_cache = new module_cache ();
+
static const char *debuginfo_path_arr = "+:.debug:/usr/lib/debug:build";
static const char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH");
// NB: kernel_build_tree doesn't enter into this, as it's for
@@ -1190,8 +1195,9 @@ struct dwflpp
return DWARF_CB_OK;
}
- int iterate_over_functions (int (* callback)(Dwarf_Die * func, void * arg),
- void * data);
+ int iterate_over_functions (int (* callback)(Dwarf_Die * func, base_query * q),
+ base_query * q, const string& function,
+ bool has_statement_num=false);
int iterate_over_globals (int (* callback)(Dwarf_Die *, void *),
void * data);
@@ -2934,13 +2940,13 @@ dwflpp::iterate_over_globals (int (* callback)(Dwarf_Die *, void *),
}
int
-dwflpp::iterate_over_functions (int (* callback)(Dwarf_Die * func, void * arg),
- void * data)
+dwflpp::iterate_over_functions (int (* callback)(Dwarf_Die * func, base_query * q),
+ base_query * q, const string& function,
+ bool has_statement_num)
{
int rc = DWARF_CB_OK;
assert (module);
assert (cu);
- dwarf_query * q = static_cast<dwarf_query *>(data);
string key = module_name + ":" + cu_name;
cu_function_cache_t *v = cu_function_cache[key];
@@ -2953,13 +2959,13 @@ dwflpp::iterate_over_functions (int (* callback)(Dwarf_Die * func, void * arg),
clog << "function cache " << key << " size " << v->size() << endl;
}
- string subkey = q->function;
+ string subkey = function;
if (v->find(subkey) != v->end())
{
Dwarf_Die die = v->find(subkey)->second;
if (q->sess.verbose > 4)
clog << "function cache " << key << " hit " << subkey << endl;
- return (*callback)(& die, data);
+ return (*callback)(& die, q);
}
else if (name_has_wildcard (subkey))
{
@@ -2973,17 +2979,17 @@ dwflpp::iterate_over_functions (int (* callback)(Dwarf_Die * func, void * arg),
if (q->sess.verbose > 4)
clog << "function cache " << key << " match " << func_name << " vs " << subkey << endl;
- rc = (*callback)(& die, data);
+ rc = (*callback)(& die, q);
if (rc != DWARF_CB_OK) break;
}
}
}
- else if (q->has_statement_num) // searching all for kernel.statement
+ else if (has_statement_num) // searching all for kernel.statement
{
for (cu_function_cache_t::iterator it = v->begin(); it != v->end(); it++)
{
Dwarf_Die die = it->second;
- rc = (*callback)(& die, data);
+ rc = (*callback)(& die, q);
if (rc != DWARF_CB_OK) break;
}
}
@@ -3873,9 +3879,9 @@ query_dwarf_inline_instance (Dwarf_Die * die, void * arg)
}
static int
-query_dwarf_func (Dwarf_Die * func, void * arg)
+query_dwarf_func (Dwarf_Die * func, base_query * bq)
{
- dwarf_query * q = static_cast<dwarf_query *>(arg);
+ dwarf_query * q = static_cast<dwarf_query *>(bq);
try
{
@@ -3889,7 +3895,7 @@ query_dwarf_func (Dwarf_Die * func, void * arg)
if (q->sess.verbose>3)
clog << "checking instances of inline " << q->dw.function_name
<< "\n";
- q->dw.iterate_over_inline_instances (query_dwarf_inline_instance, arg);
+ q->dw.iterate_over_inline_instances (query_dwarf_inline_instance, q);
if (q->dw.function_name_final_match (q->function))
return DWARF_CB_ABORT;
@@ -4023,7 +4029,9 @@ query_cu (Dwarf_Die * cudie, void * arg)
// Pick up [entrypc, name, DIE] tuples for all the functions
// matching the query, and fill in the prologue endings of them
// all in a single pass.
- int rc = q->dw.iterate_over_functions (query_dwarf_func, q);
+ int rc = q->dw.iterate_over_functions (query_dwarf_func, q,
+ q->function,
+ q->has_statement_num);
if (rc != DWARF_CB_OK)
q->query_done = true;
@@ -4993,9 +5001,6 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
if (e->module.empty())
e->module = "kernel"; // "*" may also be reasonable to search all kernel modules
- if (! s.module_cache)
- s.module_cache = new module_cache ();
-
string code;
exp_type type = pe_long;
try
@@ -5655,10 +5660,6 @@ dwarf_builder::build(systemtap_session & sess,
dwflpp* dw = 0;
- if (! sess.module_cache)
- sess.module_cache = new module_cache ();
-
-
string module_name;
if (has_null_param (parameters, TOK_KERNEL)
|| get_param (parameters, TOK_MODULE, module_name))
@@ -9202,6 +9203,726 @@ mark_builder::build(systemtap_session & sess,
// ------------------------------------------------------------------------
+// statically inserted kernel-tracepoint derived probes
+// ------------------------------------------------------------------------
+
+struct tracepoint_arg
+{
+ string name, c_type;
+ bool used, isptr;
+ Dwarf_Die type_die;
+ tracepoint_arg(): used(false), isptr(false) {}
+};
+
+struct tracepoint_derived_probe: public derived_probe
+{
+ tracepoint_derived_probe (systemtap_session& s,
+ dwflpp& dw, Dwarf_Die& func_die,
+ const string& tracepoint_name,
+ probe* base_probe, probe_point* location);
+
+ systemtap_session& sess;
+ string tracepoint_name, header;
+ vector <struct tracepoint_arg> args;
+
+ void build_args(dwflpp& dw, Dwarf_Die& func_die);
+ void join_group (systemtap_session& s);
+ void emit_probe_context_vars (translator_output* o);
+};
+
+
+struct tracepoint_derived_probe_group: public generic_dpg<tracepoint_derived_probe>
+{
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+struct tracepoint_var_expanding_visitor: public var_expanding_visitor
+{
+ tracepoint_var_expanding_visitor(dwflpp& dw, const string& probe_name,
+ vector <struct tracepoint_arg>& args):
+ dw (dw), probe_name (probe_name), args (args) {}
+ dwflpp& dw;
+ const string& probe_name;
+ vector <struct tracepoint_arg>& args;
+
+ void visit_target_symbol (target_symbol* e);
+ void visit_target_symbol_arg (target_symbol* e);
+ void visit_target_symbol_context (target_symbol* e);
+};
+
+
+void
+tracepoint_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
+{
+ string argname = e->base_name.substr(1);
+
+ // search for a tracepoint parameter matching this name
+ tracepoint_arg *arg = NULL;
+ for (unsigned i = 0; i < args.size(); ++i)
+ if (args[i].name == argname)
+ {
+ arg = &args[i];
+ arg->used = true;
+ break;
+ }
+
+ if (arg == NULL)
+ {
+ stringstream alternatives;
+ for (unsigned i = 0; i < args.size(); ++i)
+ alternatives << " $" << args[i].name;
+ alternatives << " $$name $$vars";
+
+ // We hope that this value ends up not being referenced after all, so it
+ // can be optimized out quietly.
+ semantic_error* saveme =
+ new semantic_error("unable to find tracepoint variable '" + e->base_name
+ + "' (alternatives:" + alternatives.str () + ")", e->tok);
+ // NB: we can have multiple errors, since a target variable
+ // may be expanded in several different contexts:
+ // trace ("*") { $foo->bar }
+ saveme->chain = e->saved_conversion_error;
+ e->saved_conversion_error = saveme;
+ provide (e);
+ return;
+ }
+
+ // make sure we're not dereferencing base types
+ if (!e->components.empty() && !arg->isptr)
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("tracepoint variable '" + e->base_name
+ + "' may not be used as array", e->tok);
+ case target_symbol::comp_struct_member:
+ throw semantic_error("tracepoint variable '" + e->base_name
+ + "' may not be used as a structure", e->tok);
+ default:
+ throw semantic_error("invalid use of tracepoint variable '"
+ + e->base_name + "'", e->tok);
+ }
+
+ // we can only write to dereferenced fields, and only if guru mode is on
+ bool lvalue = is_active_lvalue(e);
+ if (lvalue && (!dw.sess.guru_mode || e->components.empty()))
+ throw semantic_error("write to tracepoint variable '" + e->base_name
+ + "' not permitted", e->tok);
+
+ if (e->components.empty())
+ {
+ // Synthesize a simple function to grab the parameter
+ functiondecl *fdecl = new functiondecl;
+ fdecl->tok = e->tok;
+ embeddedcode *ec = new embeddedcode;
+ ec->tok = e->tok;
+
+ string fname = (string("_tracepoint_tvar_get")
+ + "_" + e->base_name.substr(1)
+ + "_" + lex_cast<string>(tick++));
+
+ fdecl->name = fname;
+ fdecl->body = ec;
+ fdecl->type = pe_long;
+
+ ec->code = (string("THIS->__retvalue = CONTEXT->locals[0].")
+ + probe_name + string(".__tracepoint_arg_")
+ + arg->name + string (";/* pure */"));
+
+ dw.sess.functions[fdecl->name] = fdecl;
+
+ // Synthesize a functioncall.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = fname;
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+
+ provide (n);
+ }
+ else
+ {
+ // Synthesize a function to dereference the dwarf fields,
+ // with a pointer parameter that is the base tracepoint variable
+ functiondecl *fdecl = new functiondecl;
+ fdecl->tok = e->tok;
+ embeddedcode *ec = new embeddedcode;
+ ec->tok = e->tok;
+
+ string fname = (string(lvalue ? "_tracepoint_tvar_set" : "_tracepoint_tvar_get")
+ + "_" + e->base_name.substr(1)
+ + "_" + lex_cast<string>(tick++));
+
+ fdecl->name = fname;
+ fdecl->body = ec;
+
+ try
+ {
+ ec->code = dw.literal_stmt_for_pointer (&arg->type_die, e->components,
+ lvalue, fdecl->type);
+ }
+ catch (const semantic_error& er)
+ {
+ // We suppress this error message, and pass the unresolved
+ // variable to the next pass. We hope that this value ends
+ // up not being referenced after all, so it can be optimized out
+ // quietly.
+ semantic_error* saveme = new semantic_error (er); // copy it
+ saveme->tok1 = e->tok; // XXX: token not passed to dw code generation routines
+ // NB: we can have multiple errors, since a target variable
+ // may be expanded in several different contexts:
+ // trace ("*") { $foo->bar }
+ saveme->chain = e->saved_conversion_error;
+ e->saved_conversion_error = saveme;
+ provide (e);
+ return;
+ }
+
+ // Give the fdecl an argument for the raw tracepoint value
+ vardecl *v1 = new vardecl;
+ v1->type = pe_long;
+ v1->name = "pointer";
+ v1->tok = e->tok;
+ fdecl->formal_args.push_back(v1);
+
+ if (lvalue)
+ {
+ // Modify the fdecl so it carries a pe_long formal
+ // argument called "value".
+
+ // FIXME: For the time being we only support setting target
+ // variables which have base types; these are 'pe_long' in
+ // stap's type vocabulary. Strings and pointers might be
+ // reasonable, some day, but not today.
+
+ vardecl *v2 = new vardecl;
+ v2->type = pe_long;
+ v2->name = "value";
+ v2->tok = e->tok;
+ fdecl->formal_args.push_back(v2);
+ }
+ else
+ ec->code += "/* pure */";
+
+ dw.sess.functions[fdecl->name] = fdecl;
+
+ // Synthesize a functioncall.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = fname;
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+
+ // make the original a bare target symbol for the tracepoint value,
+ // which will be passed into the dwarf dereferencing code
+ e->components.clear();
+ n->args.push_back(require(e));
+
+ if (lvalue)
+ {
+ // Provide the functioncall to our parent, so that it can be
+ // used to substitute for the assignment node immediately above
+ // us.
+ assert(!target_symbol_setter_functioncalls.empty());
+ *(target_symbol_setter_functioncalls.top()) = n;
+ }
+
+ provide (n);
+ }
+}
+
+
+void
+tracepoint_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
+{
+ if (is_active_lvalue (e))
+ throw semantic_error("write to tracepoint '" + e->base_name + "' not permitted", e->tok);
+
+ if (!e->components.empty())
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("tracepoint '" + e->base_name + "' may not be used as array",
+ e->tok);
+ case target_symbol::comp_struct_member:
+ throw semantic_error("tracepoint '" + e->base_name + "' may not be used as a structure",
+ e->tok);
+ default:
+ throw semantic_error("invalid tracepoint '" + e->base_name + "' use", e->tok);
+ }
+
+ if (e->base_name == "$$name")
+ {
+ // Synthesize a functioncall.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = "_mark_name_get";
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+ provide (n);
+ }
+ else if (e->base_name == "$$vars")
+ {
+ target_symbol *tsym = new target_symbol;
+ print_format* pf = new print_format;
+
+ // Convert $$vars to sprintf of a list of vars which we recursively evaluate
+ // NB: we synthesize a new token here rather than reusing
+ // e->tok, because print_format::print likes to use
+ // its tok->content.
+ token* pf_tok = new token(*e->tok);
+ pf_tok->content = "sprintf";
+
+ pf->tok = pf_tok;
+ pf->print_to_stream = false;
+ pf->print_with_format = true;
+ pf->print_with_delim = false;
+ pf->print_with_newline = false;
+ pf->print_char = false;
+
+ for (unsigned i = 0; i < args.size(); ++i)
+ {
+ if (i > 0)
+ pf->raw_components += " ";
+ pf->raw_components += args[i].name;
+ tsym->tok = e->tok;
+ tsym->base_name = "$" + args[i].name;
+
+ // every variable should always be accessible!
+ tsym->saved_conversion_error = 0;
+ expression *texp = require (tsym); // NB: throws nothing ...
+ assert (!tsym->saved_conversion_error); // ... but this is how we know it happened.
+
+ pf->raw_components += "=%#x";
+ pf->args.push_back(texp);
+ }
+
+ pf->components = print_format::string_to_components(pf->raw_components);
+ provide (pf);
+ }
+ else
+ assert(0); // shouldn't get here
+}
+
+void
+tracepoint_var_expanding_visitor::visit_target_symbol (target_symbol* e)
+{
+ assert(e->base_name.size() > 0 && e->base_name[0] == '$');
+
+ if (e->base_name == "$$name" || e->base_name == "$$vars")
+ visit_target_symbol_context (e);
+ else
+ visit_target_symbol_arg (e);
+}
+
+
+
+tracepoint_derived_probe::tracepoint_derived_probe (systemtap_session& s,
+ dwflpp& dw, Dwarf_Die& func_die,
+ const string& tracepoint_name,
+ probe* base, probe_point* loc):
+ derived_probe (base, new probe_point(*loc) /* .components soon rewritten */),
+ sess (s), tracepoint_name (tracepoint_name)
+{
+ // create synthetic probe point name; preserve condition
+ vector<probe_point::component*> comps;
+ comps.push_back (new probe_point::component (TOK_KERNEL));
+ comps.push_back (new probe_point::component (TOK_TRACE, new literal_string (tracepoint_name)));
+ this->sole_location()->components = comps;
+
+ // fill out the available arguments in this tracepoint
+ build_args(dw, func_die);
+
+ // determine which header defined this tracepoint
+ string decl_file = dwarf_decl_file(&func_die);
+ size_t header_pos = decl_file.rfind("trace/");
+ if (header_pos == string::npos)
+ throw semantic_error ("cannot parse header location for tracepoint '"
+ + tracepoint_name + "' in '"
+ + decl_file + "'");
+ header = decl_file.substr(header_pos);
+
+ // tracepoints from FOO_event_types.h should really be included from FOO.h
+ // XXX can dwarf tell us the include hierarchy? it would be better to
+ // ... walk up to see which one was directly included by tracequery.c
+ header_pos = header.find("_event_types");
+ if (header_pos != string::npos)
+ header.erase(header_pos, 12);
+
+ // Now expand the local variables in the probe body
+ tracepoint_var_expanding_visitor v (dw, name, args);
+ this->body = v.require (this->body);
+
+ if (sess.verbose > 2)
+ clog << "tracepoint-based " << name << " tracepoint='" << tracepoint_name
+ << "'" << endl;
+}
+
+
+static bool
+dwarf_type_name(Dwarf_Die& type_die, string& c_type)
+{
+ // if this die has a direct name, then we're done
+ const char *diename = dwarf_diename_integrate(&type_die);
+ if (diename != NULL)
+ {
+ switch (dwarf_tag(&type_die))
+ {
+ case DW_TAG_structure_type:
+ c_type.append("struct ");
+ break;
+ case DW_TAG_union_type:
+ c_type.append("union ");
+ break;
+ }
+ c_type.append(diename);
+ return true;
+ }
+
+ // otherwise, this die is a type modifier.
+
+ // recurse into the referent type
+ Dwarf_Attribute subtype_attr;
+ Dwarf_Die subtype_die;
+ if (!dwarf_attr_integrate(&type_die, DW_AT_type, &subtype_attr)
+ || !dwarf_formref_die(&subtype_attr, &subtype_die)
+ || !dwarf_type_name(subtype_die, c_type))
+ return false;
+
+ const char *suffix = NULL;
+ switch (dwarf_tag(&type_die))
+ {
+ case DW_TAG_pointer_type:
+ suffix = "*";
+ break;
+ case DW_TAG_array_type:
+ suffix = "[]";
+ break;
+ case DW_TAG_const_type:
+ suffix = " const";
+ break;
+ case DW_TAG_volatile_type:
+ suffix = " volatile";
+ break;
+ default:
+ return false;
+ }
+ c_type.append(suffix);
+ return true;
+}
+
+
+static bool
+resolve_tracepoint_arg_type(Dwarf_Die& type_die, bool& isptr)
+{
+ Dwarf_Attribute type_attr;
+ switch (dwarf_tag(&type_die))
+ {
+ case DW_TAG_typedef:
+ case DW_TAG_const_type:
+ case DW_TAG_volatile_type:
+ // iterate on the referent type
+ return (dwarf_attr_integrate(&type_die, DW_AT_type, &type_attr)
+ && dwarf_formref_die(&type_attr, &type_die)
+ && resolve_tracepoint_arg_type(type_die, isptr));
+ case DW_TAG_base_type:
+ // base types will simply be treated as script longs
+ isptr = false;
+ return true;
+ case DW_TAG_pointer_type:
+ // pointers can be either script longs,
+ // or dereferenced with their referent type
+ isptr = true;
+ return (dwarf_attr_integrate(&type_die, DW_AT_type, &type_attr)
+ && dwarf_formref_die(&type_attr, &type_die));
+ default:
+ // should we consider other types too?
+ return false;
+ }
+}
+
+
+void
+tracepoint_derived_probe::build_args(dwflpp& dw, Dwarf_Die& func_die)
+{
+ Dwarf_Die arg;
+ if (dwarf_child(&func_die, &arg) == 0)
+ do
+ if (dwarf_tag(&arg) == DW_TAG_formal_parameter)
+ {
+ // build a tracepoint_arg for this parameter
+ tracepoint_arg tparg;
+ tparg.name = dwarf_diename_integrate(&arg);
+
+ // read the type of this parameter
+ Dwarf_Attribute type_attr;
+ if (!dwarf_attr_integrate (&arg, DW_AT_type, &type_attr)
+ || !dwarf_formref_die (&type_attr, &tparg.type_die)
+ || !dwarf_type_name(tparg.type_die, tparg.c_type)
+ || !resolve_tracepoint_arg_type(tparg.type_die, tparg.isptr))
+ throw semantic_error ("cannot get type of tracepoint '"
+ + tracepoint_name + "' parameter '"
+ + tparg.name + "'");
+
+ args.push_back(tparg);
+ if (sess.verbose > 4)
+ clog << "found parameter for tracepoint '" << tracepoint_name
+ << "': type:'" << tparg.c_type
+ << "' name:'" << tparg.name << "'" << endl;
+ }
+ while (dwarf_siblingof(&arg, &arg) == 0);
+}
+
+
+void
+tracepoint_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.tracepoint_derived_probes)
+ s.tracepoint_derived_probes = new tracepoint_derived_probe_group ();
+ s.tracepoint_derived_probes->enroll (this);
+}
+
+
+void
+tracepoint_derived_probe::emit_probe_context_vars (translator_output* o)
+{
+ for (unsigned i = 0; i < args.size(); i++)
+ if (args[i].used)
+ o->newline() << "int64_t __tracepoint_arg_" << args[i].name << ";";
+}
+
+
+void
+tracepoint_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes.empty())
+ return;
+
+ s.op->newline() << "/* ---- tracepointer probes ---- */";
+
+ for (unsigned i = 0; i < probes.size(); ++i)
+ {
+ tracepoint_derived_probe *p = probes[i];
+ s.op->newline();
+ s.op->newline() << "#include <" << p->header << ">";
+ s.op->newline() << "static void enter_tracepoint_probe_" << i << "(";
+ for (unsigned j = 0; j < p->args.size(); ++j)
+ {
+ if (j > 0)
+ s.op->line() << ", ";
+ s.op->line() << p->args[j].c_type << " __tracepoint_arg_" << p->args[j].name;
+ }
+ s.op->line() << ") {";
+ s.op->indent(1);
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING");
+ s.op->newline() << "c->probe_point = "
+ << lex_cast_qstring (*p->sole_location()) << ";";
+ s.op->newline() << "c->marker_name = "
+ << lex_cast_qstring (p->tracepoint_name) << ";";
+ for (unsigned j = 0; j < p->args.size(); ++j)
+ if (p->args[j].used)
+ {
+ s.op->newline() << "c->locals[0]." << p->name << ".__tracepoint_arg_"
+ << p->args[j].name << " = (int64_t)";
+ if (p->args[j].isptr)
+ s.op->line() << "(intptr_t)";
+ s.op->line() << "__tracepoint_arg_" << p->args[j].name << ";";
+ }
+ s.op->newline() << p->name << " (c);";
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline(-1) << "}";
+ s.op->newline();
+ }
+}
+
+
+void
+tracepoint_derived_probe_group::emit_module_init (systemtap_session &s)
+{
+ if (probes.size () == 0)
+ return;
+
+ s.op->newline() << "/* init tracepoint probes */";
+
+ // We can't use a simple runtime loop because the probe registration
+ // functions are distinct inlines. Instead, this will generate nesting as
+ // deep as the number of probe points. Gotos are also possible, but the end
+ // result is the same.
+
+ for (unsigned i = 0; i < probes.size(); ++i)
+ {
+ s.op->newline() << "if (!rc) {";
+ s.op->newline(1) << "probe_point = "
+ << lex_cast_qstring (*probes[i]->sole_location()) << ";";
+ s.op->newline() << "rc = register_trace_" << probes[i]->tracepoint_name
+ << "(enter_tracepoint_probe_" << i << ");";
+ }
+
+ for (unsigned i = probes.size() - 1; i < probes.size(); --i)
+ {
+ s.op->newline() << "if (rc)";
+ s.op->newline(1) << "unregister_trace_" << probes[i]->tracepoint_name
+ << "(enter_tracepoint_probe_" << i << ");";
+ s.op->newline(-2) << "}";
+ }
+ s.op->newline() << "if (rc)";
+ s.op->newline(1) << "tracepoint_synchronize_unregister();";
+ s.op->indent(-1);
+}
+
+
+void
+tracepoint_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes.empty())
+ return;
+
+ s.op->newline() << "/* deregister tracepointer probes */";
+ for (unsigned i = 0; i < probes.size(); ++i)
+ s.op->newline() << "unregister_trace_" << probes[i]->tracepoint_name
+ << "(enter_tracepoint_probe_" << i << ");";
+ s.op->newline() << "tracepoint_synchronize_unregister();";
+}
+
+
+struct tracepoint_query : public base_query
+{
+ tracepoint_query(dwflpp & dw, const string & tracepoint,
+ probe * base_probe, probe_point * base_loc,
+ vector<derived_probe *> & results):
+ base_query(dw, "*"), tracepoint(tracepoint),
+ base_probe(base_probe), base_loc(base_loc),
+ results(results) {}
+
+ const string& tracepoint;
+
+ probe * base_probe;
+ probe_point * base_loc;
+ vector<derived_probe *> & results;
+
+ void handle_query_module();
+ int handle_query_cu(Dwarf_Die * cudie);
+ int handle_query_func(Dwarf_Die * func);
+
+ static int tracepoint_query_cu (Dwarf_Die * cudie, void * arg);
+ static int tracepoint_query_func (Dwarf_Die * func, base_query * query);
+};
+
+
+void
+tracepoint_query::handle_query_module()
+{
+ // look for the tracepoints in each CU
+ dw.iterate_over_cus(tracepoint_query_cu, this);
+}
+
+
+int
+tracepoint_query::handle_query_cu(Dwarf_Die * cudie)
+{
+ dw.focus_on_cu (cudie);
+
+ // look at each function to see if it's a tracepoint
+ string function = "stapprobe_" + tracepoint;
+ return dw.iterate_over_functions (tracepoint_query_func, this, function);
+}
+
+
+int
+tracepoint_query::handle_query_func(Dwarf_Die * func)
+{
+ dw.focus_on_function (func);
+
+ assert(dw.function_name.compare(0, 10, "stapprobe_") == 0);
+ string tracepoint_instance = dw.function_name.substr(10);
+ derived_probe *dp = new tracepoint_derived_probe (dw.sess, dw, *func,
+ tracepoint_instance,
+ base_probe, base_loc);
+ results.push_back (dp);
+ return DWARF_CB_OK;
+}
+
+
+int
+tracepoint_query::tracepoint_query_cu (Dwarf_Die * cudie, void * arg)
+{
+ tracepoint_query * q = static_cast<tracepoint_query *>(arg);
+ if (pending_interrupts) return DWARF_CB_ABORT;
+ return q->handle_query_cu(cudie);
+}
+
+
+int
+tracepoint_query::tracepoint_query_func (Dwarf_Die * func, base_query * query)
+{
+ tracepoint_query * q = static_cast<tracepoint_query *>(query);
+ if (pending_interrupts) return DWARF_CB_ABORT;
+ return q->handle_query_func(func);
+}
+
+
+struct tracepoint_builder: public derived_probe_builder
+{
+private:
+ dwflpp *dw;
+ bool init_dw(systemtap_session& s);
+
+public:
+ tracepoint_builder(): dw(0) {}
+ ~tracepoint_builder() { delete dw; }
+
+ void build_no_more (systemtap_session& s)
+ {
+ if (dw && s.verbose > 3)
+ clog << "tracepoint_builder releasing dwflpp" << endl;
+ delete dw;
+ dw = NULL;
+ }
+
+ void build(systemtap_session& s,
+ probe *base, probe_point *location,
+ literal_map_t const& parameters,
+ vector<derived_probe*>& finished_results);
+};
+
+
+bool
+tracepoint_builder::init_dw(systemtap_session& s)
+{
+ if (dw != NULL)
+ return true;
+
+ string tracequery_ko;
+ int rc = make_tracequery(s, tracequery_ko);
+ if (rc != 0)
+ return false;
+
+ // TODO cache tracequery.ko
+
+ dw = new dwflpp(s);
+ dw->setup_user(tracequery_ko);
+ return true;
+}
+
+
+void
+tracepoint_builder::build(systemtap_session& s,
+ probe *base, probe_point *location,
+ literal_map_t const& parameters,
+ vector<derived_probe*>& finished_results)
+{
+ if (!init_dw(s))
+ return;
+
+ string tracepoint;
+ assert(get_param (parameters, TOK_TRACE, tracepoint));
+
+ tracepoint_query q(*dw, tracepoint, base, location, finished_results);
+ dw->query_modules(&q);
+}
+
+
+
+// ------------------------------------------------------------------------
// hrtimer derived probes
// ------------------------------------------------------------------------
// This is a new timer interface that provides more flexibility in specifying
@@ -10003,6 +10724,10 @@ register_standard_tapsets(systemtap_session & s)
s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_MARK)->bind_str(TOK_FORMAT)
->bind(new mark_builder());
+ // kernel tracepoint probes
+ s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_TRACE)
+ ->bind(new tracepoint_builder());
+
// procfs parts
s.pattern_root->bind(TOK_PROCFS)->bind(TOK_READ)->bind(new procfs_builder());
s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_READ)
@@ -10031,6 +10756,7 @@ all_session_groups(systemtap_session& s)
DOONE(timer);
DOONE(profile);
DOONE(mark);
+ DOONE(tracepoint);
DOONE(hrtimer);
DOONE(perfmon);
DOONE(procfs);