This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
Re: [RFC][Patch 1/2] New Probe family : probe kprobe.function()
- From: Prerna Saxena <prerna at linux dot vnet dot ibm dot com>
- To: systemtap at sourceware dot org
- Date: Thu, 16 Apr 2009 18:01:31 +0530
- Subject: Re: [RFC][Patch 1/2] New Probe family : probe kprobe.function()
- References: <49E724C5.2070904@linux.vnet.ibm.com>
This patch addresses changes to tapsets.cxx for creating a new probe
type "kprobe"
Regards,
--
Prerna Saxena
Linux Technology Centre,
IBM Systems and Technology Lab,
Bangalore, India
Signed-off-by: Prerna Saxena <prerna@linux.vnet.ibm.com>
Index: git-24-mar/tapsets.cxx
===================================================================
--- git-24-mar.orig/tapsets.cxx
+++ git-24-mar/tapsets.cxx
@@ -2649,7 +2649,34 @@ struct uprobe_derived_probe: public deri
void join_group (systemtap_session& s);
};
+struct kprobe_derived_probe: public derived_probe
+{
+ kprobe_derived_probe (probe *base,
+ probe_point *location,
+ const string& name,
+ bool has_return
+ );
+ string symbol_name;
+ bool has_return;
+ bool has_maxactive;
+ long maxactive_val;
+ bool access_var;
+ void printsig (std::ostream &o) const;
+ void join_group (systemtap_session& s);
+};
+
+struct kprobe_derived_probe_group: public derived_probe_group
+{
+private:
+ multimap<string,kprobe_derived_probe*> probes_by_module;
+ typedef multimap<string,kprobe_derived_probe*>::iterator p_b_m_iterator;
+public:
+ void enroll (kprobe_derived_probe* probe);
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
struct dwarf_derived_probe_group: public derived_probe_group
{
@@ -4343,7 +4370,6 @@ struct dwarf_var_expanding_visitor: publ
};
-
unsigned var_expanding_visitor::tick = 0;
void
@@ -5360,6 +5386,23 @@ dwarf_derived_probe_group::enroll (dwarf
// sequentially.
}
+/*
+void
+dwarf_derived_probe_group::enroll (kprobe_derived_probe* p)
+{
+ dwarf_derived_probe *dw_probe = new dwarf_derived_probe (p->symbol_name,
+ "",0,
+ p->module_name,
+ p->section_name,
+ 0,0,
+ p->q,NULL);
+ probes_by_module.insert (make_pair (p->module, p));
+
+ // XXX: probes put at the same address should all share a
+ // single kprobe/kretprobe, and have their handlers executed
+ // sequentially.
+}
+*/
void
dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
@@ -5442,6 +5485,7 @@ dwarf_derived_probe_group::emit_module_d
CALCIT(module);
CALCIT(section);
CALCIT(pp);
+#undef CALCIT
s.op->newline() << "const unsigned long address;";
s.op->newline() << "void (* const ph) (struct context*);";
@@ -6180,7 +6224,6 @@ module_info::~module_info()
}
-
// ------------------------------------------------------------------------
// task_finder derived 'probes': These don't really exist. The whole
// purpose of the task_finder_derived_probe_group is to make sure that
@@ -7735,8 +7778,378 @@ uprobe_derived_probe_group::emit_module_
s.op->newline() << "mutex_destroy (& stap_uprobes_lock);";
}
+// ------------------------------------------------------------------------
+// Kprobe derived probes
+// ------------------------------------------------------------------------
+
+static string TOK_KPROBE("kprobe");
+
+kprobe_derived_probe::kprobe_derived_probe (probe *base,
+ probe_point *location,
+ const string& name,
+ bool has_return
+):
+ derived_probe (base, location),
+ symbol_name (name)
+{
+ this->tok = base->tok;
+ this->access_var = false;
+
+#ifndef USHRT_MAX
+#define USHRT_MAX 32767
+#endif
+
+ // Expansion of $target variables in the probe body produces an error during translate phase
+ vector<probe_point::component*> comps;
+
+ if (has_return)
+ comps.push_back (new probe_point::component(TOK_RETURN));
+
+ this->sole_location()->components = comps;
+}
+
+void kprobe_derived_probe::printsig (ostream& o) const
+{
+ sole_location()->print (o);
+ o << " /* " << " name = " << symbol_name << "*/";
+ printsig_nested (o);
+}
+
+void kprobe_derived_probe::join_group (systemtap_session& s)
+{
+
+ if (! s.kprobe_derived_probes)
+ s.kprobe_derived_probes = new kprobe_derived_probe_group ();
+ s.kprobe_derived_probes->enroll (this);
+
+}
+
+void kprobe_derived_probe_group::enroll (kprobe_derived_probe* p)
+{
+ probes_by_module.insert (make_pair (p->symbol_name, p));
+ // probes of same symbol should share single kprobe/kretprobe
+}
+
+void
+kprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes_by_module.empty()) return;
+
+ s.op->newline() << "/* ---- kprobe-based probes ---- */";
+
+ // Warn of misconfigured kernels
+ s.op->newline() << "#if ! defined(CONFIG_KPROBES)";
+ s.op->newline() << "#error \"Need CONFIG_KPROBES!\"";
+ s.op->newline() << "#endif";
+ s.op->newline();
+
+ // Forward declare the master entry functions
+ s.op->newline() << "static int enter_kprobe_probe (struct kprobe *inst,";
+ s.op->line() << " struct pt_regs *regs);";
+ s.op->newline() << "static int enter_kretprobe_probe (struct kretprobe_instance *inst,";
+ s.op->line() << " struct pt_regs *regs);";
+
+ // Emit an array of kprobe/kretprobe pointers
+ s.op->newline() << "#if defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline() << "static void * stap_unreg_kprobes[" << probes_by_module.size() << "];";
+ s.op->newline() << "#endif";
+
+ // Emit the actual probe list.
+
+ s.op->newline() << "static struct stap_dwarfless_kprobe {";
+ s.op->newline(1) << "union { struct kprobe kp; struct kretprobe krp; } u;";
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "struct kprobe dummy;";
+ s.op->newline() << "#endif";
+ s.op->newline(-1) << "} stap_dwarfless_kprobes[" << probes_by_module.size() << "];";
+ // NB: bss!
+
+ s.op->newline() << "static struct stap_dwarfless_probe {";
+ s.op->newline(1) << "const unsigned return_p:1;";
+ s.op->newline() << "const unsigned maxactive_p:1;";
+ s.op->newline() << "unsigned registered_p:1;";
+ s.op->newline() << "const unsigned short maxactive_val;";
+
+ // Let's find some stats for the three embedded strings. Maybe they
+ // are small and uniform enough to justify putting char[MAX]'s into
+ // the array instead of relocated char*'s.
+ size_t pp_name_max = 0, symbol_string_name_max = 0;
+ size_t pp_name_tot = 0, symbol_string_name_tot = 0;
+ size_t all_name_cnt = probes_by_module.size(); // for average
+ for (p_b_m_iterator it = probes_by_module.begin(); it != probes_by_module.end(); it++)
+ {
+ kprobe_derived_probe* p = it->second;
+#define DOIT(var,expr) do { \
+ size_t var##_size = (expr) + 1; \
+ var##_max = max (var##_max, var##_size); \
+ var##_tot += var##_size; } while (0)
+ DOIT(pp_name, lex_cast_qstring(*p->sole_location()).size());
+ DOIT(symbol_string_name, p->symbol_name.size());
+#undef DOIT
+
+ }
+
+ // Decide whether it's worthwhile to use char[] or char* by comparing
+ // the amount of average waste (max - avg) to the relocation data size
+ // (3 native long words).
+#define CALCIT(var) \
+ if ((var##_name_max-(var##_name_tot/all_name_cnt)) < (3 * sizeof(void*))) \
+ { \
+ s.op->newline() << "const char " << #var << "[" << var##_name_max << "];"; \
+ if (s.verbose > 2) clog << "stap_dwarfless_probe " << #var \
+ << "[" << var##_name_max << "]" << endl; \
+ } \
+ else \
+ { \
+ s.op->newline() << "const char * const " << #var << ";"; \
+ if (s.verbose > 2) clog << "stap_dwarfless_probe *" << #var << endl; \
+ }
+
+ CALCIT(pp);
+ CALCIT(symbol_string);
+#undef CALCIT
+
+ s.op->newline() << "const unsigned long address;";
+ s.op->newline() << "void (* const ph) (struct context*);";
+ s.op->newline(-1) << "} stap_dwarfless_probes[] = {";
+ s.op->indent(1);
+
+ for (p_b_m_iterator it = probes_by_module.begin(); it != probes_by_module.end(); it++)
+ {
+ kprobe_derived_probe* p = it->second;
+ s.op->newline() << "{";
+ if (p->has_return)
+ s.op->line() << " .return_p=1,";
+ if (p->has_maxactive)
+ {
+ s.op->line() << " .maxactive_p=1,";
+ assert (p->maxactive_val >= 0 && p->maxactive_val <= USHRT_MAX);
+ s.op->line() << " .maxactive_val=" << p->maxactive_val << ",";
+ }
+ s.op->line() << " .address=(unsigned long)0x" << hex << 0 << dec << "ULL,";
+ s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
+ s.op->line() << " .ph=&" << p->name << ",";
+ s.op->line() << " .symbol_string=\"" << p->symbol_name << "\"";
+ s.op->line() << " },";
+ }
+
+ s.op->newline(-1) << "};";
+ // Emit the kprobes callback function
+ s.op->newline();
+ s.op->newline() << "static int enter_kprobe_probe (struct kprobe *inst,";
+ s.op->line() << " struct pt_regs *regs) {";
+ // NB: as of PR5673, the kprobe|kretprobe union struct is in BSS
+ s.op->newline(1) << "int kprobe_idx = ((uintptr_t)inst-(uintptr_t)stap_dwarfless_kprobes)/sizeof(struct stap_dwarfless_kprobe);";
+ // Check that the index is plausible
+ s.op->newline() << "struct stap_dwarfless_probe *sdp = &stap_dwarfless_probes[";
+ s.op->line() << "((kprobe_idx >= 0 && kprobe_idx < " << probes_by_module.size() << ")?";
+ s.op->line() << "kprobe_idx:0)"; // NB: at least we avoid memory corruption
+ // XXX: it would be nice to give a more verbose error though; BUG_ON later?
+ s.op->line() << "];";
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp");
+ s.op->newline() << "c->regs = regs;";
+ s.op->newline() << "(*sdp->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline() << "return 0;";
+ s.op->newline(-1) << "}";
+ // Same for kretprobes
+ s.op->newline();
+ s.op->newline() << "static int enter_kretprobe_probe (struct kretprobe_instance *inst,";
+ s.op->line() << " struct pt_regs *regs) {";
+ s.op->newline(1) << "struct kretprobe *krp = inst->rp;";
+
+ // NB: as of PR5673, the kprobe|kretprobe union struct is in BSS
+ s.op->newline() << "int kprobe_idx = ((uintptr_t)krp-(uintptr_t)stap_dwarfless_kprobes)/sizeof(struct stap_dwarfless_kprobe);";
+ // Check that the index is plausible
+ s.op->newline() << "struct stap_dwarfless_probe *sdp = &stap_dwarfless_probes[";
+ s.op->line() << "((kprobe_idx >= 0 && kprobe_idx < " << probes_by_module.size() << ")?";
+ s.op->line() << "kprobe_idx:0)"; // NB: at least we avoid memory corruption
+ // XXX: it would be nice to give a more verbose error though; BUG_ON later?
+ s.op->line() << "];";
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp");
+ s.op->newline() << "c->regs = regs;";
+ s.op->newline() << "c->pi = inst;"; // for assisting runtime's backtrace logic
+ s.op->newline() << "(*sdp->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline() << "return 0;";
+ s.op->newline(-1) << "}";
+}
+
+
+void
+kprobe_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline() << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "unsigned long relocated_addr = 0;";
+ s.op->newline() << "probe_point = sdp->pp;"; // for error messages
+ s.op->newline() << "if (sdp->return_p) {";
+ s.op->newline(1) << "kp->u.krp.kp.addr = (void *) relocated_addr;";
+ s.op->newline() << "kp->u.krp.kp.symbol_name = sdp->symbol_string;";
+ s.op->newline() << "if (sdp->maxactive_p) {";
+ s.op->newline(1) << "kp->u.krp.maxactive = sdp->maxactive_val;";
+ s.op->newline(-1) << "} else {";
+ s.op->newline(1) << "kp->u.krp.maxactive = max(10, 4*NR_CPUS);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "kp->u.krp.handler = &enter_kretprobe_probe;";
+ // to ensure safeness of bspcache, always use aggr_kprobe on ia64
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "kp->dummy.addr = kp->u.krp.kp.addr;";
+ s.op->newline() << "kp->dummy.pre_handler = NULL;";
+ s.op->newline() << "kp->dummy.symbol_name = sdp->symbol_string;";
+ s.op->newline() << "rc = register_kprobe (& kp->dummy);";
+ s.op->newline() << "if (rc == 0) {";
+ s.op->newline(1) << "rc = register_kretprobe (& kp->u.krp);";
+ s.op->newline() << "if (rc != 0)";
+ s.op->newline(1) << "unregister_kprobe (& kp->dummy);";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "#else";
+ s.op->newline() << "rc = register_kretprobe (& kp->u.krp);";
+ s.op->newline() << "#endif";
+ s.op->newline(-1) << "} else {";
+ // to ensure safeness of bspcache, always use aggr_kprobe on ia64
+ s.op->newline(1) << "kp->u.kp.addr = (void *) relocated_addr;";
+ s.op->newline(1) << "kp->u.kp.symbol_name = sdp->symbol_string;";
+ s.op->newline() << "kp->u.kp.pre_handler = &enter_kprobe_probe;";
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "kp->dummy.addr = kp->u.kp.addr;";
+ s.op->newline() << "kp->dummy.pre_handler = NULL;";
+ s.op->newline() << "kp->dummy.symbol_name = sdp->symbol_string;";
+ s.op->newline() << "rc = register_kprobe (& kp->dummy);";
+ s.op->newline() << "if (rc == 0) {";
+ s.op->newline(1) << "rc = register_kprobe (& kp->u.kp);";
+ s.op->newline() << "if (rc != 0)";
+ s.op->newline(1) << "unregister_kprobe (& kp->dummy);";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "#else";
+ s.op->newline() << "rc = register_kprobe (& kp->u.kp);";
+ s.op->newline() << "#endif";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "if (rc) {"; // PR6749: tolerate a failed register_*probe.
+ s.op->newline(1) << "sdp->registered_p = 0;";
+ s.op->newline() << "if (rc == -EINVAL)";
+ s.op->newline() << "{";
+ s.op->newline() << " _stp_error (\"Error registering kprobe,possibly an incorrect name %s \", sdp->symbol_string);";
+ s.op->newline() << " atomic_set (&session_state, STAP_SESSION_ERROR);";
+ s.op->newline() << " goto out;";
+ s.op->newline() << "}";
+ s.op->newline() << "else";
+ s.op->newline() << "_stp_warn (\"probe %s for %s registration error (rc %d)\", probe_point, sdp->pp, rc);";
+ s.op->newline() << "rc = 0;"; // continue with other probes
+ // XXX: shall we increment numskipped?
+ s.op->newline(-1) << "}";
+
+ s.op->newline() << "else sdp->registered_p = 1;";
+ s.op->newline(-1) << "}"; // for loop
+}
+
+void
+kprobe_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ //Unregister kprobes by batch interfaces.
+ s.op->newline() << "#if defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline() << "j = 0;";
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "if (!sdp->return_p)";
+ s.op->newline(1) << "stap_unreg_kprobes[j++] = &kp->u.kp;";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "unregister_kprobes((struct kprobe **)stap_unreg_kprobes, j);";
+ s.op->newline() << "j = 0;";
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "if (sdp->return_p)";
+ s.op->newline(1) << "stap_unreg_kprobes[j++] = &kp->u.krp;";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "unregister_kretprobes((struct kretprobe **)stap_unreg_kprobes, j);";
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "j = 0;";
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "stap_unreg_kprobes[j++] = &kp->dummy;";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "unregister_kprobes((struct kprobe **)stap_unreg_kprobes, j);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "#endif";
+
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "if (sdp->return_p) {";
+ s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline(1) << "unregister_kretprobe (&kp->u.krp);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "atomic_add (kp->u.krp.nmissed, & skipped_count);";
+ s.op->newline() << "#ifdef STP_TIMING";
+ s.op->newline() << "if (kp->u.krp.nmissed)";
+ s.op->newline(1) << "_stp_warn (\"Skipped due to missed kretprobe/1 on '%s': %d\\n\", sdp->pp, kp->u.krp.nmissed);";
+ s.op->newline(-1) << "#endif";
+ s.op->newline() << "atomic_add (kp->u.krp.kp.nmissed, & skipped_count);";
+ s.op->newline() << "#ifdef STP_TIMING";
+ s.op->newline() << "if (kp->u.krp.kp.nmissed)";
+ s.op->newline(1) << "_stp_warn (\"Skipped due to missed kretprobe/2 on '%s': %d\\n\", sdp->pp, kp->u.krp.kp.nmissed);";
+ s.op->newline(-1) << "#endif";
+ s.op->newline(-1) << "} else {";
+ s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline(1) << "unregister_kprobe (&kp->u.kp);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "atomic_add (kp->u.kp.nmissed, & skipped_count);";
+ s.op->newline() << "#ifdef STP_TIMING";
+ s.op->newline() << "if (kp->u.kp.nmissed)";
+ s.op->newline(1) << "_stp_warn (\"Skipped due to missed kprobe on '%s': %d\\n\", sdp->pp, kp->u.kp.nmissed);";
+ s.op->newline(-1) << "#endif";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES) && defined(__ia64__)";
+ s.op->newline() << "unregister_kprobe (&kp->dummy);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "sdp->registered_p = 0;";
+ s.op->newline(-1) << "}";
+}
+
+struct kprobe_builder: public derived_probe_builder
+{
+ kprobe_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results);
+};
+
+
+void
+kprobe_builder::build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+{
+ string function_string_val;
+
+ bool has_function_str = this->get_param(parameters, TOK_FUNCTION, function_string_val);
+ if ( ! has_function_str )
+ throw semantic_error("\n Function name required for kprobe based probes");
+ bool has_inline = this->has_null_param (parameters, TOK_INLINE);
+ bool has_return = this->has_null_param (parameters, TOK_RETURN);
+
+ if (has_inline)
+ throw semantic_error("\n Kprobe-based probes cannot handle inlines \n");
+
+ finished_results.push_back(new kprobe_derived_probe ( base, location,
+ function_string_val,
+ has_return) );
+}
// ------------------------------------------------------------------------
// timer derived probes
// ------------------------------------------------------------------------
@@ -10685,6 +11098,10 @@ perfmon_derived_probe_group::emit_module
}
#endif
+// ------------------------------------------------------------------------
+// kprobes-based probes,which postpone symbol resolution until runtime.
+// ------------------------------------------------------------------------
+
// ------------------------------------------------------------------------
// Standard tapset registry.
@@ -10779,6 +11196,10 @@ register_standard_tapsets(systemtap_sess
s.pattern_root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(new procfs_builder());
s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_WRITE)
->bind(new procfs_builder());
+
+ // Kprobe based probe
+ s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_FUNCTION)
+ ->bind(new kprobe_builder());
}
@@ -10801,6 +11222,7 @@ all_session_groups(systemtap_session& s)
DOONE(profile);
DOONE(mark);
DOONE(tracepoint);
+ DOONE(kprobe);
DOONE(hrtimer);
DOONE(perfmon);
DOONE(procfs);