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]

Re: [RFC V2] Systemtap translator support for kernel hardware breakpoints


Hi Frank,
Thanks for the prompt review. Attaching a new version of patches that address your comments.


On 01/27/2010 02:46 AM, Frank Ch. Eigler wrote:

OK.  The translator can detect the absence or presence of this
functionality, and should preclude translation without it.  Use
systemtap_session.kernel_config["CONFIG_HAVE_HW_BREAKPOINTS"]

Done.



2. The script translation fails if the number of hardware breakpoint
probes exceeds the number of debug registers for a given architecture.

OK, though I suggest making this part a warning only. This code has no way to know what the actual limits are at the moment of execution. They could easily be lower, and if perf etc. get some sort of magical multiplexing, the limits could be higher. The runtime should (but doesn't yet - see below) handle errors such as overreservations correctly.


Perf allows multiplexing only for non-pinned breakpoints, not the pinned ones used by systemtap. If this script is allowed to run with a warning, the runtime will register the first 'n' possible requests and reject the later ones. Is it not better to notify to the user that they are making more requests than the maximum limit imposed by hardware ? (more on this later)



3. For now, probing of a kernel function's local variables is not
possible. Also, with export of kallsyms_lookup_name(), systemtap
generated module can directly decode symbol address at run-time. IMO,
use of dwarf routines seems an overkill for now.

OK, assuming that this re-exported function stays re-exported, there may be no need to do an autoconf-level check for it, if the code already checks for CONFIG_HAVE_HW_BREAKPOINTS.


Agree.



[...]
3. Dynamic enabling / disabling a hardware breakpoint handler from a
kprobe context. (This will in turn allow one to set breakpoints on
local variables of kernel functions)

We hope to address this general area more comprehensively for systemtap-1.2.


+void hwbkpt_derived_probe::join_group (systemtap_session&  s)
+{
+  if (! s.hwbkpt_derived_probes) {
+        s.hwbkpt_derived_probes = new hwbkpt_derived_probe_group ();
+	if (s.architecture == "i386" || s.architecture == "x86_64" )
+			s.hwbkpt_derived_probes->max_hwbkpt_probes_by_arch = 4;
+	else {
+			s.hwbkpt_derived_probes->max_hwbkpt_probes_by_arch = 0;
+			throw semantic_error ("Hardware Breakpoints not supported on arch " + s.architecture);
+		}
+	}
+  s.hwbkpt_derived_probes->enroll (this, s);
+}

I suggest removing this error - or turning it into a (conditional) warning.

I've elucidated this below.




+  // Warn of misconfigured kernels
+  s.op->newline()<<  "#ifndef CONFIG_HAVE_HW_BREAKPOINT";
+  s.op->newline()<<  "#error \"Need CONFIG_HAVE_HW_BREAKPOINT!\"";
+  s.op->newline()<<  "#endif";
If the code also checks for the kernel_config[] above, this check
should never fail.

Done.




+// Define macros for access type
+  s.op->newline()<<  "#define HWBKPT_READ 0";
+  s.op->newline()<<  "#define HWBKPT_WRITE 1";
+  s.op->newline()<<  "#define HWBKPT_RW 2";

Why, instead of just using the symbols already defined in the kernel headers?



Removed this redundancy.


+#define CALCIT(var)                                                     \
+  s.op->newline()<<  "const char "<<  #var<<  "["<<  var##_name_max<<  "] ;";
+  CALCIT(pp);
+  CALCIT(symbol);
+#undef CALCIT

Considering that we're likely to have a small handful of such probes in a script, this is all unnecessary optimization. Just plop a char* in there.


Done.


+  s.op->newline()<<  "#ifdef CONFIG_X86";
+  s.op->newline()<<  "  switch(sdp->len) {";
+  s.op->newline()<<  "	case 1:";
+  s.op->newline()<<  "    hp->bp_len = HW_BREAKPOINT_LEN_1;";
+  s.op->newline()<<  "    break;";
+  s.op->newline()<<  "	case 2:";
+  s.op->newline()<<  "    hp->bp_len = HW_BREAKPOINT_LEN_2;";
+  s.op->newline()<<  "    break;";
+  s.op->newline()<<  "	case 3:";
+  s.op->newline()<<  "	case 4:";
+  s.op->newline()<<  "    hp->bp_len = HW_BREAKPOINT_LEN_4;";
+  s.op->newline()<<  "    break;";
+  s.op->newline()<<  "#ifdef CONFIG_X86_64";
+  s.op->newline()<<  "	case 5:";
+  s.op->newline()<<  "	case 6:";
+  s.op->newline()<<  "	case 7:";
+  s.op->newline()<<  "	case 8:";
+  s.op->newline()<<  "    hp->bp_len = HW_BREAKPOINT_LEN_8;";
+  s.op->newline()<<  "    break;";
+  s.op->newline()<<  "#endif /*CONFIG_X86_64*/";

Are you sure this CONFIG_arch* stuff is needed? Let the kernel try 8-byte watchpoints, and let us handle the error.


I cannot foresee the advantage of requesting an erroneous breakpoint, only to see the request fail later. Could you pls let me know if there are any specific issues wrt above, apart from the hassle of arch-specific checks?


+  s.op->newline()<<  "if ( !IS_ERR(stap_hwbkpt_ret_array[i]) ) {";
+  s.op->newline()<<  " stap_hwbkpt_ret_array[i] = register_wide_hw_breakpoint( hp, (void *)&enter_hwbkpt_probe );";
+  s.op->newline()<<  " if (IS_ERR(stap_hwbkpt_ret_array[i])) {";
+  s.op->newline()<<  "     rc = PTR_ERR(stap_hwbkpt_ret_array[i]);";
+  s.op->newline()<<  "      sdp->registered_p = 0;";
+  s.op->newline(1)<<  "	    _stp_warn(\" Hwbkpt Probe %s : Registration error rc= %d, Addr = %p, name = %s\", probe_point, rc, addr, hwbkpt_symbol_name);";
+  s.op->newline(-1)<<  "  }";
+  s.op->newline()<<  "    else sdp->registered_p = 1;";
+  s.op->newline()<<  "}";
+  s.op->newline(-1)<<  "}"; // for loop

I mentioned this in the previous review, but this is insufficient. (Please review my earlier comments in case something else was missed.) If the [i]th registration attempt fails, and you return an rc != 0 to the wrapping code, this code right here must ensure that all already-registered entries ([0..i-1]) have been unregistered.

I beg to differ here. As is the case with dwarfless probes, is it not better to ignore an erroneous probe(that failed registration) and continue running the other probes that got successfully registered ? There might be numerous reasons for probe registration to fail -- incorrect length, bad symbol name, unsupported type (eg a read-only breakpoint on x86) apart from the unavailability of a debug register. I dont see sense in penalizing the other valid probes just because of this singular, incorrect request. The registration failure is notified to the user via a warning. If it defeats the purpose of the script, the user has the option of tweaking his script.


Going by your suggestion, we are essentially allowing the user to overcommit breakpoint requests in systemtap scripts, and rejecting the script completely (unregistering _all_ probes) even if a singular request fails. Is it not a better idea to ration the number of breakpoint requests to start with, and then allow as many breakpoints in the script to get registered as possible -- and at runtime, filter out only invalid requests / requests failing because some debug registers have already been consumed ?

+ s.op->newline()<< " if ( IS_ERR(stap_hwbkpt_ret_array[i]) ) continue;";
Just use registered_p = 0 or 1.  You don't need to archive the error
codes.

Done.


+  //Hwbkpt based probe
+  s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+	->bind(TOK_HWBKPT_WRITE)->bind(new hwbkpt_builder());
+  s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_HWBKPT)
+	->bind(TOK_HWBKPT_WRITE)->bind(new hwbkpt_builder());
+  s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+	->bind(TOK_HWBKPT_RW)->bind(new hwbkpt_builder());
+  s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_HWBKPT)
+	->bind(TOK_HWBKPT_RW)->bind(new hwbkpt_builder());
+  s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+	->bind_num(TOK_LENGTH)->bind(TOK_HWBKPT_WRITE)->bind(new hwbkpt_builder());
+  s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+	->bind_num(TOK_LENGTH)->bind(TOK_HWBKPT_RW)->bind(new hwbkpt_builder());
+  // length supported with address only, not symbol names

This is probably the easiest place to wrap with a kernel_config[...] conditional to disable this probe point family on unsupporting kernels.

Thanks for the suggestion, I added a kernel_config() check here.



-- Prerna Saxena

Linux Technology Centre,
IBM Systems and Technology Lab,
Bangalore, India
Signed-off-by: Prerna Saxena <prerna@linux.vnet.ibm.com>

Index: stap-git-jan-25/tapsets.cxx
===================================================================
--- stap-git-jan-25.orig/tapsets.cxx
+++ stap-git-jan-25/tapsets.cxx
@@ -5319,7 +5319,342 @@ kprobe_builder::build(systemtap_session 
     }
 }
 
+// ------------------------------------------------------------------------
+//  Hardware breakpoint based probes.
+// ------------------------------------------------------------------------
+
+static const string TOK_HWBKPT("data");
+static const string TOK_HWBKPT_WRITE("write");
+static const string TOK_HWBKPT_RW("rw");
+static const string TOK_LENGTH("length");
+
+#define HWBKPT_READ 0
+#define HWBKPT_WRITE 1
+#define HWBKPT_RW 2
+struct hwbkpt_derived_probe: public derived_probe
+{
+  hwbkpt_derived_probe (probe *base,
+                        probe_point *location,
+                        uint64_t addr,
+			string symname,
+			unsigned int len,
+			bool has_only_read_access,
+			bool has_only_write_access,
+			bool has_rw_access
+                        );
+  Dwarf_Addr hwbkpt_addr;
+  string symbol_name;
+  unsigned int hwbkpt_access,hwbkpt_len;
+
+  void printsig (std::ostream &o) const;
+  void join_group (systemtap_session& s);
+};
+
+struct hwbkpt_derived_probe_group: public derived_probe_group
+{
+  unsigned int max_hwbkpt_probes_by_arch;
+
+private:
+  vector<hwbkpt_derived_probe*> hwbkpt_probes_vector;
+  typedef vector<hwbkpt_derived_probe*>::iterator h_p_v_iterator;
 
+public:
+  void enroll (hwbkpt_derived_probe* probe, systemtap_session& s);
+  void emit_module_decls (systemtap_session& s);
+  void emit_module_init (systemtap_session& s);
+  void emit_module_exit (systemtap_session& s);
+};
+
+hwbkpt_derived_probe::hwbkpt_derived_probe (probe *base,
+					    probe_point *location,
+					    uint64_t addr,
+					    string symname,
+					    unsigned int len,
+					    bool has_only_read_access,
+					    bool has_only_write_access,
+					    bool has_rw_access
+					    ):
+  derived_probe (base, location),
+  hwbkpt_addr (addr),
+  symbol_name (symname),
+  hwbkpt_len (len)
+{
+  this->tok = base->tok;
+
+  vector<probe_point::component*> comps;
+  comps.push_back (new probe_point::component(TOK_KERNEL));
+
+  if (hwbkpt_addr)
+	  comps.push_back (new probe_point::component (TOK_HWBKPT, new literal_number(hwbkpt_addr)));
+  else
+	if (symbol_name.size())
+	  comps.push_back (new probe_point::component (TOK_HWBKPT, new literal_string(symbol_name)));
+
+  comps.push_back (new probe_point::component (TOK_LENGTH, new literal_number(hwbkpt_len)));
+
+  if (has_only_read_access)
+	this->hwbkpt_access = HWBKPT_READ ;
+//TODO add code for comps.push_back for read, since this flag is not for x86
+
+  else
+	{
+	  if (has_only_write_access)
+		{
+		  this->hwbkpt_access = HWBKPT_WRITE ;
+	  	  comps.push_back (new probe_point::component(TOK_HWBKPT_WRITE));
+		}
+	  else
+		{
+		  this->hwbkpt_access = HWBKPT_RW ;
+	  	  comps.push_back (new probe_point::component(TOK_HWBKPT_RW));
+		}
+	}
+
+  this->sole_location()->components = comps;
+}
+
+void hwbkpt_derived_probe::printsig (ostream& o) const
+{
+  sole_location()->print (o);
+  printsig_nested (o);
+}
+
+void hwbkpt_derived_probe::join_group (systemtap_session& s)
+{
+  if (! s.hwbkpt_derived_probes) {
+        s.hwbkpt_derived_probes = new hwbkpt_derived_probe_group ();
+	if (s.architecture == "i386" || s.architecture == "x86_64" )
+			s.hwbkpt_derived_probes->max_hwbkpt_probes_by_arch = 4;
+	else {
+			s.hwbkpt_derived_probes->max_hwbkpt_probes_by_arch = 0;
+			throw semantic_error ("Hardware Breakpoints not supported on arch " + s.architecture);
+		}
+	}
+  s.hwbkpt_derived_probes->enroll (this, s);
+}
+
+void hwbkpt_derived_probe_group::enroll (hwbkpt_derived_probe* p, systemtap_session& s)
+{
+  if (hwbkpt_probes_vector.size() < max_hwbkpt_probes_by_arch )
+	hwbkpt_probes_vector.push_back (p);
+  else  {
+	  std::stringstream hwbkpt_err_msg;
+	  hwbkpt_err_msg << "No of Hardware breakpoint probes in script exceeds the number of hardware breakpoints that the architecture can support : max " << max_hwbkpt_probes_by_arch << " on " << s.architecture ;
+	  throw semantic_error (hwbkpt_err_msg.str());
+	}
+}
+
+void
+hwbkpt_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+  if (hwbkpt_probes_vector.empty()) return;
+
+  s.op->newline() << "/* ---- hwbkpt-based probes ---- */";
+
+  s.op->newline() << "#include <linux/perf_event.h>";
+  s.op->newline() << "#include <linux/hw_breakpoint.h>";
+  s.op->newline();
+
+  // Forward declare the master entry functions
+  s.op->newline() << "static int enter_hwbkpt_probe (struct perf_event *bp,";
+  s.op->line() << " int nmi,";
+  s.op->line() << " struct perf_sample_data *data,";
+  s.op->line() << " struct pt_regs *regs);";
+
+  // Emit the actual probe list.
+
+  s.op->newline() << "static struct perf_event_attr ";
+  s.op->newline() << "stap_hwbkpt_probe_array[" << hwbkpt_probes_vector.size() << "];";
+
+  s.op->newline() << "static struct perf_event **";
+  s.op->newline() << "stap_hwbkpt_ret_array[" << hwbkpt_probes_vector.size() << "];";
+  s.op->newline() << "static struct stap_hwbkpt_probe {";
+  s.op->newline() << "int registered_p:1;";
+// registered_p =  0 signifies a probe that failed registration
+// registered_p =  1 signifies a probe that got registered successfully
+
+  // Probe point & Symbol Names are mostly small and uniform enough
+  // to justify putting const char*.
+#define CALCIT(var)                                                     \
+  s.op->newline() << "const char * const " << #var << ";";
+  CALCIT(pp);
+  CALCIT(symbol);
+#undef CALCIT
+
+  s.op->newline() << "const unsigned long address;";
+  s.op->newline() << "uint8_t atype;";
+  s.op->newline() << "uint8_t len;";
+  s.op->newline() << "void (* const ph) (struct context*);";
+  s.op->newline() << "} stap_hwbkpt_probes[] = {";
+  s.op->indent(1);
+
+  for (unsigned int it = 0; it < hwbkpt_probes_vector.size(); it++)
+    {
+      hwbkpt_derived_probe* p = hwbkpt_probes_vector.at(it);
+      s.op->newline() << "{";
+      s.op->line() << " .registered_p=1,";
+      if (p->symbol_name.size())
+      s.op->line() << " .address=(unsigned long)0x0" << "ULL,";
+      else
+      s.op->line() << " .address=(unsigned long)0x" << hex << p->hwbkpt_addr << dec << "ULL,";
+      switch(p->hwbkpt_access){
+      case HWBKPT_READ:
+		s.op->line() << " .atype=HW_BREAKPOINT_R ,";
+      case HWBKPT_WRITE:
+		s.op->line() << " .atype=HW_BREAKPOINT_W ,";
+      case HWBKPT_RW:
+		s.op->line() << " .atype=HW_BREAKPOINT_R|HW_BREAKPOINT_W ,";
+	};
+      s.op->line() << " .len=" << p->hwbkpt_len << ",";
+      s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
+      s.op->line() << " .symbol=\"" << p->symbol_name << "\",";
+      s.op->line() << " .ph=&" << p->name << "";
+      s.op->line() << " },";
+    }
+  s.op->newline() << "};";
+
+  // Emit the hwbkpt callback function
+  s.op->newline() ;
+  s.op->newline() << "static int enter_hwbkpt_probe (struct perf_event *bp,";
+  s.op->line() << " int nmi,";
+  s.op->line() << " struct perf_sample_data *data,";
+  s.op->line() << " struct pt_regs *regs) {";
+  s.op->newline() << "unsigned int i;";
+  s.op->newline() << "if (bp->attr.type != PERF_TYPE_BREAKPOINT )";
+  s.op->newline() << "return -1;";
+  s.op->newline() << "for ( i=0; i<" << hwbkpt_probes_vector.size() << "; i++) {";
+  s.op->newline() << "struct perf_event_attr *hp = & stap_hwbkpt_probe_array[i];";
+  s.op->newline() << "if (bp->attr.bp_addr==hp->bp_addr && bp->attr.bp_type==hp->bp_type && bp->attr.bp_len==hp->bp_len ) {";
+  s.op->newline() << "struct stap_hwbkpt_probe *sdp = &stap_hwbkpt_probes[i];";
+  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() << "}";
+  s.op->newline(-1) << "}";
+  s.op->newline() << "return 0;";
+  s.op->newline() << "}";
+}
+
+void
+hwbkpt_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+  s.op->newline() << "for (i=0; i<" << hwbkpt_probes_vector.size() << "; i++) {";
+  s.op->newline(1) << "struct stap_hwbkpt_probe *sdp = & stap_hwbkpt_probes[i];";
+  s.op->newline() << "struct perf_event_attr *hp = & stap_hwbkpt_probe_array[i];";
+  s.op->newline() << "void *addr = (void *) sdp->address;";
+  s.op->newline() << "const char *hwbkpt_symbol_name = addr ? NULL : sdp->symbol;";
+  s.op->newline() << "	hw_breakpoint_init(hp);";
+  s.op->newline() << "	if (addr) ";
+  s.op->newline() << "	    hp->bp_addr = (unsigned long) addr;";
+  s.op->newline() << "	else { ";
+  s.op->newline() << "	   hp->bp_addr = kallsyms_lookup_name(hwbkpt_symbol_name);";
+  s.op->newline() << "	if (!hp->bp_addr) { ";
+  s.op->newline() << " 	_stp_warn(\"Probe %s registration skipped : invalid symbol %s \",sdp->pp,hwbkpt_symbol_name);";
+  s.op->newline() << "	sdp->registered_p = 0;";
+  s.op->newline() << "		} ";
+  s.op->newline() << "	} ";
+  s.op->newline() << "#ifdef CONFIG_X86";
+  s.op->newline() << "  switch(sdp->len) {";
+  s.op->newline() << "	case 1:";
+  s.op->newline() << "    hp->bp_len = HW_BREAKPOINT_LEN_1;";
+  s.op->newline() << "    break;";
+  s.op->newline() << "	case 2:";
+  s.op->newline() << "    hp->bp_len = HW_BREAKPOINT_LEN_2;";
+  s.op->newline() << "    break;";
+  s.op->newline() << "	case 3:";
+  s.op->newline() << "	case 4:";
+  s.op->newline() << "    hp->bp_len = HW_BREAKPOINT_LEN_4;";
+  s.op->newline() << "    break;";
+  s.op->newline() << "#ifdef CONFIG_X86_64";
+  s.op->newline() << "	case 5:";
+  s.op->newline() << "	case 6:";
+  s.op->newline() << "	case 7:";
+  s.op->newline() << "	case 8:";
+  s.op->newline() << "    hp->bp_len = HW_BREAKPOINT_LEN_8;";
+  s.op->newline() << "    break;";
+  s.op->newline() << "#endif /*CONFIG_X86_64*/";
+  s.op->newline() << "	default:";
+  s.op->newline() << " 	_stp_warn(\"Probe %s registration skipped : unsupported length. Supported lengths for x86 are 1,2,4 {8 for x86_84 only} \",sdp->pp);";
+  s.op->newline() << "	sdp->registered_p = 0;";
+  s.op->newline() << "	}";
+  s.op->newline() << "  hp->bp_type = sdp->atype;";
+  s.op->newline() << "  if (sdp->atype == HW_BREAKPOINT_R) {";
+  s.op->newline() << "  	_stp_warn(\"Probe %s registration skipped : READ-ONLY hardware breakpoints not supported on x86\",sdp->pp);";
+  s.op->newline() << "		sdp->registered_p = 0;";
+  s.op->newline() << "  }";
+  s.op->newline() << "#endif /*CONFIG_X86*/";
+  s.op->newline() << "probe_point = sdp->pp;"; // for error messages
+  s.op->newline() << "if ( sdp->registered_p ) {";
+  s.op->newline() << " stap_hwbkpt_ret_array[i] = register_wide_hw_breakpoint( hp, (void *)&enter_hwbkpt_probe );";
+  s.op->newline() << " if (IS_ERR(stap_hwbkpt_ret_array[i])) {";
+  s.op->newline() << "     rc = PTR_ERR(stap_hwbkpt_ret_array[i]);";
+  s.op->newline() << "      sdp->registered_p = 0;";
+  s.op->newline(1) << "	    _stp_warn(\" Hwbkpt Probe %s : Registration error rc= %d, Addr = %p, name = %s\", probe_point, rc, addr, hwbkpt_symbol_name);";
+  s.op->newline(-1) << "  }";
+  s.op->newline() << "    else sdp->registered_p = 1;";
+  s.op->newline() << "}";
+  s.op->newline(-1) << "}"; // for loop
+}
+
+void
+hwbkpt_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+  //Unregister hwbkpt probes.
+  s.op->newline() << "for (i=0; i<" << hwbkpt_probes_vector.size() << "; i++) {";
+  s.op->newline(1) << "struct stap_hwbkpt_probe *sdp = & stap_hwbkpt_probes[i];";
+  s.op->newline() << " if ( sdp->registered_p == 0 ) continue;";
+  s.op->newline() << " unregister_wide_hw_breakpoint(stap_hwbkpt_ret_array[i]);";
+  s.op->newline() << "sdp->registered_p = 0;";
+  s.op->newline(-1) << "}";
+}
+
+struct hwbkpt_builder: public derived_probe_builder
+{
+  hwbkpt_builder() {}
+  virtual void build(systemtap_session & sess,
+		     probe * base,
+		     probe_point * location,
+		     literal_map_t const & parameters,
+		     vector<derived_probe *> & finished_results);
+};
+
+void
+hwbkpt_builder::build(systemtap_session & sess,
+		      probe * base,
+		      probe_point * location,
+		      literal_map_t const & parameters,
+		      vector<derived_probe *> & finished_results)
+{
+  string symbol_str_val;
+  int64_t hwbkpt_address, len;
+  bool has_addr, has_symbol_str, has_write, has_rw, has_len;
+
+  has_addr = get_param (parameters, TOK_HWBKPT, hwbkpt_address);
+  has_symbol_str = get_param (parameters, TOK_HWBKPT, symbol_str_val);
+  has_len = get_param (parameters, TOK_LENGTH, len);
+  has_write = (parameters.find(TOK_HWBKPT_WRITE) != parameters.end());
+  has_rw = (parameters.find(TOK_HWBKPT_RW) != parameters.end());
+
+  if (!has_len)
+	len = 1;
+
+  if (has_addr)
+      finished_results.push_back (new hwbkpt_derived_probe (base,
+							    location,
+							    hwbkpt_address,
+							    "",len,0,
+							    has_write,
+							    has_rw));
+  else // has symbol_str
+      finished_results.push_back (new hwbkpt_derived_probe (base,
+							    location,
+							    0,
+							    symbol_str_val,len,0,
+							    has_write,
+							    has_rw));
+}
 
 // ------------------------------------------------------------------------
 // statically inserted kernel-tracepoint derived probes
@@ -5811,7 +6146,7 @@ tracepoint_derived_probe_group::emit_mod
         }
       s.op->line() << ") {";
       s.op->indent(1);
-      common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", 
+      common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING",
                                      lex_cast_qstring (*p->sole_location()));
       s.op->newline() << "c->marker_name = "
                       << lex_cast_qstring (p->tracepoint_name)
@@ -6140,7 +6475,6 @@ tracepoint_builder::init_dw(systemtap_se
   return true;
 }
 
-
 void
 tracepoint_builder::build(systemtap_session& s,
                           probe *base, probe_point *location,
@@ -6158,7 +6492,6 @@ tracepoint_builder::build(systemtap_sess
 }
 
 
-
 // ------------------------------------------------------------------------
 //  Standard tapset registry.
 // ------------------------------------------------------------------------
@@ -6205,6 +6538,25 @@ register_standard_tapsets(systemtap_sess
      ->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());
+
+  //Hwbkpt based probe
+  if (s.kernel_config["CONFIG_PERF_EVENTS"] == string("y") &&
+	s.kernel_config["CONFIG_HAVE_HW_BREAKPOINT"] == string("y")) {
+	  s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+		->bind(TOK_HWBKPT_WRITE)->bind(new hwbkpt_builder());
+	  s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_HWBKPT)
+		->bind(TOK_HWBKPT_WRITE)->bind(new hwbkpt_builder());
+	  s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+		->bind(TOK_HWBKPT_RW)->bind(new hwbkpt_builder());
+	  s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_HWBKPT)
+		->bind(TOK_HWBKPT_RW)->bind(new hwbkpt_builder());
+	  s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+		->bind_num(TOK_LENGTH)->bind(TOK_HWBKPT_WRITE)->bind(new hwbkpt_builder());
+	  s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+		->bind_num(TOK_LENGTH)->bind(TOK_HWBKPT_RW)->bind(new hwbkpt_builder());
+	  // length supported with address only, not symbol names
+	}
+
 }
 
 
@@ -6231,6 +6583,7 @@ all_session_groups(systemtap_session& s)
   DOONE(mark);
   DOONE(tracepoint);
   DOONE(kprobe);
+  DOONE(hwbkpt);
   DOONE(hrtimer);
   DOONE(perfmon);
   DOONE(procfs);
Index: stap-git-jan-25/session.h
===================================================================
--- stap-git-jan-25.orig/session.h
+++ stap-git-jan-25/session.h
@@ -31,6 +31,7 @@ struct derived_probe;
 struct be_derived_probe_group;
 struct dwarf_derived_probe_group;
 struct kprobe_derived_probe_group;
+struct hwbkpt_derived_probe_group;
 struct uprobe_derived_probe_group;
 struct utrace_derived_probe_group;
 struct itrace_derived_probe_group;
@@ -175,6 +176,7 @@ struct systemtap_session
   be_derived_probe_group* be_derived_probes;
   dwarf_derived_probe_group* dwarf_derived_probes;
   kprobe_derived_probe_group* kprobe_derived_probes;
+  hwbkpt_derived_probe_group* hwbkpt_derived_probes;
   uprobe_derived_probe_group* uprobe_derived_probes;
   utrace_derived_probe_group* utrace_derived_probes;
   itrace_derived_probe_group* itrace_derived_probes;
Index: stap-git-jan-25/elaborate.cxx
===================================================================
--- stap-git-jan-25.orig/elaborate.cxx
+++ stap-git-jan-25/elaborate.cxx
@@ -1523,6 +1523,7 @@ systemtap_session::systemtap_session ():
   be_derived_probes(0),
   dwarf_derived_probes(0),
   kprobe_derived_probes(0),
+  hwbkpt_derived_probes(0),
   uprobe_derived_probes(0),
   utrace_derived_probes(0),
   itrace_derived_probes(0),
Index: stap-git-jan-25/stapprobes.3stap.in
===================================================================
--- stap-git-jan-25.orig/stapprobes.3stap.in
+++ stap-git-jan-25/stapprobes.3stap.in
@@ -660,6 +660,55 @@ and a string of name=value pairs for all
 is available in
 .BR $$vars " or " $$parms .
 
+.SS HARDWARE BREAKPOINTS
+This family of probes is used to set hardware watchpoints for a given
+ (global) kernel symbol. The probes take three components as inputs :
+
+1. The
+.BR virtual address / name
+of the kernel symbol to be traced is supplied as argument to this class
+of probes. ( Probes for only data segment variables are supported. Probing
+local variables of a function cannot be done.)
+
+2. Nature of access to be probed :
+a.
+.I .write
+probe gets triggered when a write happens at the specified address/symbol
+name.
+b.
+.I rw
+probe is triggered when either a read or write happens.
+
+3.
+.BR .length
+(optional)
+Users have the option of specifying the address interval to be probed
+using "length" constructs. The user-specified length gets approximated
+to the closest possible address length that the architecture can
+support. If the specified length exceeds the limits imposed by
+architecture, an error message is flagged and probe registration fails.
+Wherever 'length' is not specified, the translator requests a hardware
+breakpoint probe of length 1. It should be noted that the "length"
+construct is not valid with symbol names.
+
+Following constructs are supported :
+.SAMPLE
+probe kernel.data(ADDRESS).write
+probe kernel.data(ADDRESS).rw
+probe kernel.data(ADDRESS).length(LEN).write
+probe kernel.data(ADDRESS).length(LEN).rw
+probe kernel.data("SYMBOL_NAME").write
+probe kernel.data("SYMBOL_NAME").rw
+.ESAMPLE
+
+This set of probes make use of the debug registers of the processor,
+which is a scarce resource. (4 on x86 , 1 on powerpc ) The script
+translation fails if a user sets more hardware breakpoint probes than
+the limits set by architecture. For example,a pass-2 error is flagged
+when an input script requests 5 hardware breakpoint probes on an x86
+system while x86 architecture supports a maximum of 4 breakpoints.
+Users are cautioned to set probes judiciously.
+
 .SH EXAMPLES
 .PP
 Here are some example probe points, defining the associated events.
@@ -696,6 +745,9 @@ refers to the statement of line 2917 wit
 kernel.statement("bio_init@fs/bio.c+3")
 refers to the statement at line bio_init+3 within "fs/bio.c".
 .TP
+kernel.data("pid_max").write
+refers to a hardware preakpoint of type "write" set on pid_max
+.TP
 syscall.*.return
 refers to the group of probe aliases with any name in the third position
 
Index: stap-git-jan-25/testsuite/buildok/hwbkpt.stp
===================================================================
--- /dev/null
+++ stap-git-jan-25/testsuite/buildok/hwbkpt.stp
@@ -0,0 +1,4 @@
+#! stap -wp4
+
+probe kernel.data("pid_max").write {}
+probe kernel.data("pid_max").rw {}
Index: stap-git-jan-25/NEWS
===================================================================
--- stap-git-jan-25.orig/NEWS
+++ stap-git-jan-25/NEWS
@@ -1,3 +1,5 @@
+- New : systemtap now supports probes for Hardware Breakpoints. For syntax details, see man page for stapprobes.
+
 * What's new in version 1.1
 
 - New tracepoint based tapset for memory subsystem.

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