This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] PR6983 kmmio probes


I have come up with an initial implementation for kmmio probe support (PR6983). I am having trouble getting the probe to trigger on a given address, additionally it can also hang at the call to register_kmmio_probe.

There is an ftrace-like tracing tool that will intercept calls to ioremap and register a kmmio probe using the returned __iomem address from ioremap.

As it is currently implemented, creating a systemtap probe on a particular address will pass that address directly to register_kmmio_probe. To test I have been using physical addresses from /proc/iomem. Perhaps someone familiar with memory-mapped I/O could shed some light on what I'm doing wrong.

Thanks,
Elliott
diff --git a/elaborate.cxx b/elaborate.cxx
index 7c4a5fc..c98cbb0 100644
--- a/elaborate.cxx
+++ b/elaborate.cxx
@@ -1464,6 +1464,7 @@ systemtap_session::systemtap_session ():
   hrtimer_derived_probes(0),
   perfmon_derived_probes(0),
   procfs_derived_probes(0),
+  kmmio_derived_probes(0),
   op (0), up (0),
   sym_kprobes_text_start (0),
   sym_kprobes_text_end (0),
diff --git a/session.h b/session.h
index 151094b..d2a3396 100644
--- a/session.h
+++ b/session.h
@@ -42,6 +42,7 @@ struct tracepoint_derived_probe_group;
 struct hrtimer_derived_probe_group;
 struct perfmon_derived_probe_group;
 struct procfs_derived_probe_group;
+struct kmmio_derived_probe_group;
 struct embeddedcode;
 struct translator_output;
 struct unparser;
@@ -184,6 +185,7 @@ struct systemtap_session
   hrtimer_derived_probe_group* hrtimer_derived_probes;
   perfmon_derived_probe_group* perfmon_derived_probes;
   procfs_derived_probe_group* procfs_derived_probes;
+  kmmio_derived_probe_group* kmmio_derived_probes;
 
   // NB: It is very important for all of the above (and below) fields
   // to be cleared in the systemtap_session ctor (elaborate.cxx)
diff --git a/tapsets.cxx b/tapsets.cxx
index d7e9ab4..6c248a1 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -7950,7 +7950,168 @@ tracepoint_builder::build(systemtap_session& s,
   dw->query_modules(&q);
 }
 
+//-------------------------------------------------------------------------
+// kernel MMIO derived probes
+//-------------------------------------------------------------------------
 
+static string TOK_MMIO("mmio");
+static string TOK_LENGTH("length");
+
+struct kmmio_derived_probe: public derived_probe
+{
+  kmmio_derived_probe (systemtap_session &s, probe *p, probe_point *l,
+      unsigned long addr, unsigned long length);
+  void join_group (systemtap_session &s);
+
+  unsigned long addr;
+  unsigned long length;
+};
+
+struct kmmio_derived_probe_group: public generic_dpg<kmmio_derived_probe>
+{
+public:
+  void emit_module_decls (systemtap_session& s);
+  void emit_module_init (systemtap_session& s);
+  void emit_module_exit (systemtap_session& s);
+};
+
+struct kmmio_builder: public derived_probe_builder
+{
+  virtual void build(systemtap_session& sess,
+                     probe* base,
+                     probe_point* location,
+                     literal_map_t const & parameters,
+                     std::vector<derived_probe*> & finished_results);
+};
+
+kmmio_derived_probe::kmmio_derived_probe (systemtap_session &s, probe *p, probe_point *l,
+    unsigned long addr, unsigned long length) : derived_probe(p, l),
+    addr(addr), length(length) {}
+
+void
+kmmio_derived_probe::join_group(systemtap_session &s)
+{
+  if (!s.kmmio_derived_probes)
+    {
+      s.kmmio_derived_probes = new kmmio_derived_probe_group();
+    }
+  s.kmmio_derived_probes->enroll(this);
+}
+
+void
+kmmio_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+  if (probes.empty()) return;
+
+    s.op->newline() << "/* ---- kernel mmio probes ---- */";
+
+    s.op->newline() << "#if ! defined(CONFIG_MMIOTRACE)";
+    s.op->newline() << "#error \"Need CONFIG_MMIOTRACE!\"";
+    s.op->newline() << "#endif";
+    s.op->newline();
+
+    s.op->newline() << "#include <linux/mmiotrace.h>";
+    s.op->newline();
+
+    // Forward declare the master entry functions
+    s.op->newline() << "static void enter_kmmio_probe (struct kmmio_probe *inst,";
+    s.op->line() << " struct pt_regs *regs, unsigned long addr);";
+
+    // Emit the actual probe list.
+
+    s.op->newline() << "static struct kmmio_probe ";
+    s.op->line() << "kmmio_probes[" << probes.size() << "];";
+
+    s.op->newline() << "static struct stap_kmmio_probe {";
+    s.op->newline(1) << "unsigned registered_p:1;";
+    s.op->newline() << "const unsigned long address;";
+    s.op->newline() << "const unsigned long length;";
+    s.op->newline() << "const char * const pp;";
+    s.op->newline() << "void (* const ph) (struct context*);";
+    s.op->newline(-1) << "} stap_kmmio_probes[] = {";
+    s.op->indent(1);
+
+    for (unsigned int i = 0; i < probes.size(); i++)
+      {
+        kmmio_derived_probe *p = probes[i];
+        s.op->newline() << "{ ";
+        s.op->line() << ".address = (unsigned long) 0x" << hex << p->addr << dec << "ULL, ";
+        s.op->line() << ".length = (unsigned long) 0x" << hex << p->length << dec << "ULL, ";
+        s.op->line() << ".pp=" << lex_cast_qstring (*p->sole_location()) << ", ";
+        s.op->line() << ".ph = &" << p->name;
+        s.op->line() << " }";
+      }
+
+    s.op->newline(-1) << "};";
+
+    // Emit kmmio_probe callback.
+    s.op->newline();
+    s.op->newline() << "static void enter_kmmio_probe (struct kmmio_probe *inst,";
+    s.op->line() << " struct pt_regs *regs, unsigned long addr) {";
+    s.op->newline(1) << "int kmmio_probe_idx = ((uintptr_t)inst-(uintptr_t)kmmio_probes)/sizeof(struct kmmio_probe);";
+    // Check that the index is plausible
+    s.op->newline() << "struct stap_kmmio_probe *sp = &stap_kmmio_probes[";
+    s.op->line() << "((kmmio_probe_idx >= 0 && kmmio_probe_idx < " << probes.size() << ") ? ";
+    s.op->line() << "kmmio_probe_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", "sp->pp");
+    s.op->newline() << "c->regs = regs;";
+    s.op->newline() << "(*sp->ph) (c);";
+    common_probe_entryfn_epilogue (s.op);
+    s.op->newline(-1) << "}";
+}
+
+void
+kmmio_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+  s.op->newline() << "for (i = 0; i < " << probes.size() << "; i++) {";
+  s.op->newline(1) << "struct stap_kmmio_probe *sp = &stap_kmmio_probes[i];";
+  s.op->newline() << "struct kmmio_probe *kp = &kmmio_probes[i];";
+  s.op->newline() << "probe_point = sp->pp;"; // for error messages
+  s.op->newline() << "kp->addr = sp->address;";
+  s.op->newline() << "kp->len = sp->length;";
+  s.op->newline() << "kp->pre_handler = &enter_kmmio_probe;";
+  s.op->newline() << "kp->post_handler = NULL;";
+  s.op->newline() << "rc = register_kmmio_probe(kp);";
+  s.op->newline() << "if (rc != 0) {";
+  s.op->newline(1) << "sp->registered_p = 0;";
+  s.op->newline() << "_stp_warn(\"probe %s registration error (rc %d)\", probe_point, rc);";
+  s.op->newline() << "atomic_inc(&skipped_count);"; // increment skipped count here
+  s.op->newline() << "rc = 0;"; // continue with other probes
+  s.op->newline(-1) << "}";
+  s.op->newline() << "else sp->registered_p = 1;";
+  s.op->newline(-1) << "}"; // for loop
+}
+
+void
+kmmio_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+  s.op->newline() << "for (i = 0; i < " << probes.size() << "; i++) {";
+  s.op->newline(1) << "struct stap_kmmio_probe *sp = &stap_kmmio_probes[i];";
+  s.op->newline() << "struct kmmio_probe *kp = &kmmio_probes[i];";
+  s.op->newline() << "if (sp->registered_p) {";
+  s.op->newline(1) << "unregister_kmmio_probe(kp);";
+  s.op->newline() << "sp->registered_p = 0;";
+  s.op->newline(-1) << "}";
+  s.op->newline(-1) << "}"; // for loop
+}
+
+void
+kmmio_builder::build(systemtap_session& sess,
+    probe* base,
+    probe_point* location,
+    literal_map_t const & parameters,
+    std::vector<derived_probe*> & finished_results)
+{
+  int64_t addr, length;
+
+  assert(get_param(parameters, TOK_MMIO, addr));
+  assert(get_param(parameters, TOK_LENGTH, length));
+
+  finished_results.push_back(new kmmio_derived_probe(sess, base, location,
+      addr, length));
+}
 
 // ------------------------------------------------------------------------
 //  Standard tapset registry.
@@ -7999,6 +8160,10 @@ register_standard_tapsets(systemtap_session & s)
      ->bind_num(TOK_MAXACTIVE)->bind(new kprobe_builder());
   s.pattern_root->bind(TOK_KPROBE)->bind_num(TOK_STATEMENT)
       ->bind(TOK_ABSOLUTE)->bind(new kprobe_builder());
+
+  // kernel mmio probes
+  s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_MMIO)->bind_num(TOK_LENGTH)
+    ->bind(new kmmio_builder());
 }
 
 
@@ -8028,6 +8193,7 @@ all_session_groups(systemtap_session& s)
   DOONE(hrtimer);
   DOONE(perfmon);
   DOONE(procfs);
+  DOONE(kmmio);
 
   // Another "order is important" item.  We want to make sure we
   // "register" the dummy task_finder probe group after all probe

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]