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]

reading perf counters


This adds the capability for a task to read a perf counter.

usage syntax:
perf.type(0).config(0).counter("a")
...
probe process("name")...
{...
 x = @perf("name")
}

translation data structures
tapsets.cxx
dwarf_var_expanding_visitor
std::vector<derived_probe*> perf_counter_refs;
A vector of derived probes corresponding to @perf ops used by this probe.
session.h
std::map<std::string,derived_probe*> perf_counters;
A mapping from the counter name to the associated probe


runtime data structures
 stap_inode_uprobe_consumers
  perf_counters is a list of indices into stap_perf_probes
  corresponding to the perf counters that a process probe uses.  e.g.
  {0, 2}  AND
  static struct stap_perf_probe stap_perf_probes [3] = {...}
uprobes-inode.c::stapiu_target_reg
 calls _stp_perf_read_init to setup a counter corresponding to each
  @perf using the index to stap_perf_probes
 _stp_perf_read is passed the same index to read the counter

EXAMPLE
for the script:
global perm_begin
global perm_end
global tower_begin
global tower_end
probe perf.type(0).config(0).counter("a") { }
probe perf.type(0).config(0).counter("b") { }
probe perf.type(0).config(1).counter("c") { }
probe process ("/work/scox/stap/perf/bench.x").statement("*@bench.c:1131")
{
 perm_begin <<< @perf("a")
 perm_begin <<< @perf("c")
}
probe process ("/work/scox/stap/perf/bench.x").statement("tower@bench.c")
{}
probe process ("/work/scox/stap/perf/bench.x").statement("*@bench.c:1131")
{
 perm_end <<< @perf("a")
 perm_begin <<< @perf("c")
}
probe process ("/work/scox/stap/perf/bench.x").statement("*@bench.c:1135")
{
 tower_begin <<< @perf("b")
}
probe process ("/work/scox/stap/perf/bench.x").statement("*@bench.c:1136")
{
 tower_end <<< @perf("b")
}


long perf_counters_0[] = {0, 2};
long perf_counters_1[] = {};
long perf_counters_2[] = {0, 2};
long perf_counters_3[] = {1};
long perf_counters_4[] = {1};
static struct stapiu_consumer stap_inode_uprobe_consumers[] = {
{ .target=&stap_inode_uprobe_targets[0], .offset=(loff_t)0x31f1ULL, .perf_counters_dim=ARRAY_SIZE(perf_counters_0), .perf_counters=&perf_counters_0, .probe=(&stap_probes[3]), },
{ .target=&stap_inode_uprobe_targets[0], .offset=(loff_t)0xa7aULL, .perf_counters_dim=ARRAY_SIZE(perf_counters_1), .perf_counters=&perf_counters_1, .probe=(&stap_probes[4]), },


...
static struct stap_perf_probe stap_perf_probes [3] = {
{
.attr={ .type=0ULL, .config=0ULL, { .sample_period=0ULL }},
.callback=enter_perf_probe_0,
.probe=(&stap_probes[0]),
.per_thread=1,
},
{
.attr={ .type=0ULL, .config=0ULL, { .sample_period=0ULL }},
.callback=enter_perf_probe_1,
.probe=(&stap_probes[1]),
.per_thread=1,
},
{
.attr={ .type=0ULL, .config=1ULL, { .sample_period=0ULL }},
.callback=enter_perf_probe_2,
.probe=(&stap_probes[2]),
.per_thread=1,
},
};
... and a read is done with:
l->l___perf_read_b = (((int64_t) (_stp_perf_read(smp_processor_id(),1)))); # where 1 is the index into stap_perf_probes corresponding to counter("b")




This adds the capability for a task to read a perf counter.

usage syntax:
perf.type(0).config(0).counter("a")
...
probe process("name")...
{...
 x = @perf("name")
}

translation data structures
tapsets.cxx
 dwarf_var_expanding_visitor
  std::vector<derived_probe*> perf_counter_refs;
   A list of derived probes corresponding to @perf ops used by this probe. 
session.h
 std::map<std::string,derived_probe*> perf_counters;
  A mapping from the counter name to the associated probe

runtime data structures
 stap_inode_uprobe_consumers
  perf_counters is a list of indices into stap_perf_probes
  corresponding to the perf counters that a process probe uses.  e.g.
  {0, 2}  AND
  static struct stap_perf_probe stap_perf_probes [3] = {...}
uprobes-inode.c::stapiu_target_reg
 calls _stp_perf_read_init to setup a counter corresponding to each
  @perf using the index to stap_perf_probes
 _stp_perf_read is passed the same index to read the counter

EXAMPLE
for the script:
global perm_begin
global perm_end
global tower_begin
global tower_end
probe perf.type(0).config(0).counter("a") { }
probe perf.type(0).config(0).counter("b") { }
probe perf.type(0).config(1).counter("c") { }
probe process ("/work/scox/stap/perf/bench.x").statement("*@bench.c:1131")
{
 perm_begin <<< @perf("a")
 perm_begin <<< @perf("c")
}
probe process ("/work/scox/stap/perf/bench.x").statement("tower@bench.c")
{}
probe process ("/work/scox/stap/perf/bench.x").statement("*@bench.c:1131")
{
 perm_end <<< @perf("a")
 perm_begin <<< @perf("c")
}
probe process ("/work/scox/stap/perf/bench.x").statement("*@bench.c:1135")
{
 tower_begin <<< @perf("b")
}
probe process ("/work/scox/stap/perf/bench.x").statement("*@bench.c:1136")
{
 tower_end <<< @perf("b")
}


long perf_counters_0[] = {0, 2};
long perf_counters_1[] = {};
long perf_counters_2[] = {0, 2};
long perf_counters_3[] = {1};
long perf_counters_4[] = {1};
static struct stapiu_consumer stap_inode_uprobe_consumers[] = {
  { .target=&stap_inode_uprobe_targets[0], .offset=(loff_t)0x31f1ULL, .perf_counters_dim=ARRAY_SIZE(perf_counters_0), .perf_counters=&perf_counters_0, .probe=(&stap_probes[3]), },
  { .target=&stap_inode_uprobe_targets[0], .offset=(loff_t)0xa7aULL, .perf_counters_dim=ARRAY_SIZE(perf_counters_1), .perf_counters=&perf_counters_1, .probe=(&stap_probes[4]), },
  { .target=&stap_inode_uprobe_targets[0], .offset=(loff_t)0x31f1ULL, .perf_counters_dim=ARRAY_SIZE(perf_counters_2), .perf_counters=&perf_counters_2, .probe=(&stap_probes[5]), },
  { .target=&stap_inode_uprobe_targets[0], .offset=(loff_t)0x320eULL, .perf_counters_dim=ARRAY_SIZE(perf_counters_3), .perf_counters=&perf_counters_3, .probe=(&stap_probes[6]), },
  { .target=&stap_inode_uprobe_targets[0], .offset=(loff_t)0x3239ULL, .perf_counters_dim=ARRAY_SIZE(perf_counters_4), .perf_counters=&perf_counters_4, .probe=(&stap_probes[7]), },
...
static struct stap_perf_probe stap_perf_probes [3] = {
  {
    .attr={ .type=0ULL, .config=0ULL, { .sample_period=0ULL }},
    .callback=enter_perf_probe_0, 
    .probe=(&stap_probes[0]), 
    .per_thread=1, 
  },
  {
    .attr={ .type=0ULL, .config=0ULL, { .sample_period=0ULL }},
    .callback=enter_perf_probe_1, 
    .probe=(&stap_probes[1]), 
    .per_thread=1, 
  },
  {
    .attr={ .type=0ULL, .config=1ULL, { .sample_period=0ULL }},
    .callback=enter_perf_probe_2, 
    .probe=(&stap_probes[2]), 
    .per_thread=1, 
  },
};
... and the read is done with:
l->l___perf_read_b = (((int64_t) (_stp_perf_read(smp_processor_id(),1))));


diff --git a/elaborate.cxx b/elaborate.cxx
index 684f4a8..cabb03a 100644
--- a/elaborate.cxx
+++ b/elaborate.cxx
@@ -4488,6 +4488,13 @@ typeresolution_info::visit_entry_op (entry_op* e)
 
 
 void
+typeresolution_info::visit_perf_op (perf_op* e)
+{
+  e->type = pe_long;
+}
+
+
+void
 typeresolution_info::visit_cast_op (cast_op* e)
 {
   // Like target_symbol, a cast_op shouldn't survive this far
diff --git a/elaborate.h b/elaborate.h
index bf1915d..d159fa7 100644
--- a/elaborate.h
+++ b/elaborate.h
@@ -121,6 +121,7 @@ struct typeresolution_info: public visitor
   void visit_cast_op (cast_op* e);
   void visit_defined_op (defined_op* e);
   void visit_entry_op (entry_op* e);
+  void visit_perf_op (perf_op* e);
 };
 
 
@@ -163,7 +164,7 @@ struct derived_probe: public probe
   // From within unparser::emit_probe, initialized any extra variables
   // in this probe's context locals.
 
-  virtual void emit_probe_local_init (translator_output*) {}
+  virtual void emit_probe_local_init (systemtap_session& s, translator_output*) {}
   // From within unparser::emit_probe, emit any extra processing block
   // for this probe.
 
@@ -189,6 +190,9 @@ public:
   // Location of semaphores to activate sdt probes
   Dwarf_Addr sdt_semaphore_addr;
 
+  // perf.counter probes that this probe references
+  std::vector<derived_probe*> perf_counter_refs;
+
   // index into session.probes[], set and used during translation
   unsigned session_index;
 };
diff --git a/parse.cxx b/parse.cxx
index 8d241c2..7929793 100644
--- a/parse.cxx
+++ b/parse.cxx
@@ -180,6 +180,7 @@ private: // nonterminals
   target_symbol *parse_target_symbol (const token* t);
   expression* parse_entry_op (const token* t);
   expression* parse_defined_op (const token* t);
+  expression* parse_perf_op (const token* t);
   expression* parse_expression ();
   expression* parse_assignment ();
   expression* parse_ternary ();
@@ -3330,6 +3331,9 @@ expression* parser::parse_symbol ()
       if (name == "@entry")
         return parse_entry_op (t);
 
+      if (name == "@perf")
+        return parse_perf_op (t);
+
       if (name.size() > 0 && name[0] == '@')
 	{
 	  stat_op *sop = new stat_op;
@@ -3623,6 +3627,20 @@ expression* parser::parse_entry_op (const token* t)
 }
 
 
+// Parse a @perf().  Given head token has already been consumed.
+expression* parser::parse_perf_op (const token* t)
+{
+  perf_op* pop = new perf_op;
+  pop->tok = t;
+  expect_op("(");
+  pop->operand = (literal_string*)parse_literal ();
+  if (pop->operand->tok->type != tok_string)
+    throw parse_error (_("expected 'string'"));
+  expect_op(")");
+  return pop;
+}
+
+
 
 void
 parser::parse_target_symbol_components (target_symbol* e)
diff --git a/runtime/linux/perf.c b/runtime/linux/perf.c
index b631007..833e0b1 100644
--- a/runtime/linux/perf.c
+++ b/runtime/linux/perf.c
@@ -28,6 +28,7 @@
 static long _stp_perf_init (struct stap_perf_probe *stp, struct task_struct* task)
 {
 	int cpu;
+	struct task_struct * perf_task;
 
 	if (stp->per_thread) {
 	  if (task == 0) /* need to setup later when we know the task */
@@ -113,4 +114,49 @@ static void _stp_perf_del (struct stap_perf_probe *stp)
 }
 
 
+/*
+The first call to _stp_perf_init, via systemtap_module_init at runtime, is for
+setting up aggregate counters.  Per thread counters need to be setup when the
+thread is known.  This is done by calling _stp_perf_init later when the thread
+is known.  A per thread perf counter is defined by a counter("var") suffix on
+the perf probe.  It is defined by perf_builder.  This counter is read on demand 
+via the "@perf("var")" builtin which is treated as an expression right hand side
+which reads the perf counter associated with the previously defined perf
+counter.  It is expanded by dwarf_var_expanding_visitor
+*/
+
+static int _stp_perf_read_init (unsigned i, void* task)
+{
+  /* Choose the stap_perf_probes entry */
+  struct stap_perf_probe* stp = & stap_perf_probes[i];
+
+  return _stp_perf_init (stp, (struct task_struct*)task);
+}
+
+
+long _stp_perf_read (int ncpu, unsigned i)
+{
+  /* Choose the stap_perf_probes entry */
+  struct stap_perf_probe* stp;
+  u64 enabled, running;
+
+  if (i > sizeof(stap_perf_probes)/sizeof(struct stap_perf_probe))
+    {
+      _stp_error ("_stp_perf_read\n");
+      return 0;
+    }
+  stp = & stap_perf_probes[i]; 
+    
+  if (stp == NULL || stp->per_thread_event == NULL)
+    {
+      _stp_error ("_stp_perf_read\n");
+      return 0;
+    }
+
+  might_sleep();
+  return perf_event_read_value (stp->per_thread_event, &enabled, &running);
+
+}
+
+
 #endif /* _PERF_C_ */
diff --git a/runtime/linux/perf.h b/runtime/linux/perf.h
index c4417ef..a12957b 100644
--- a/runtime/linux/perf.h
+++ b/runtime/linux/perf.h
@@ -37,4 +37,8 @@ static long _stp_perf_init (struct stap_perf_probe *stp, struct task_struct* tas
 
 static void _stp_perf_del (struct stap_perf_probe *stp);
 
+// moved to runtime_defines.h
+// static int _stp_perf_read_init (unsigned i);
+// static long _stp_perf_read (int ncpu, unsigned i);
+
 #endif /* _PERF_H_ */
diff --git a/runtime/linux/uprobes-common.h b/runtime/linux/uprobes-common.h
index 6b6b855..570ba9f 100644
--- a/runtime/linux/uprobes-common.h
+++ b/runtime/linux/uprobes-common.h
@@ -26,6 +26,8 @@ struct stap_uprobe_spec {
   unsigned return_p:1;
   unsigned long address;
   unsigned long sdt_sem_offset;
+  long perf_counters_dim;
+  long (*perf_counters) [];
   const struct stap_probe * const probe;
  };
 
diff --git a/runtime/linux/uprobes-inode.c b/runtime/linux/uprobes-inode.c
index 050f96f..137c5ba 100644
--- a/runtime/linux/uprobes-inode.c
+++ b/runtime/linux/uprobes-inode.c
@@ -1,6 +1,6 @@
 /* -*- linux-c -*-
  * Common functions for using inode-based uprobes
- * Copyright (C) 2011 Red Hat Inc.
+ * Copyright (C) 2011, 2012 Red Hat Inc.
  *
  * This file is part of systemtap, and is free software.  You can
  * redistribute it and/or modify it under the terms of the GNU General
@@ -109,6 +109,8 @@ struct stapiu_consumer {
 	loff_t offset; /* the probe offset within the inode */
 	loff_t sdt_sem_offset; /* the semaphore offset from process->base */
 
+        long perf_counters_dim;
+        long (*perf_counters) [];
         const struct stap_probe * const probe;
 };
 
@@ -320,13 +322,18 @@ stapiu_target_unreg(struct stapiu_target *target)
 
 /* Register all uprobe consumers of a target.  */
 static int
-stapiu_target_reg(struct stapiu_target *target)
+stapiu_target_reg(struct stapiu_target *target, struct task_struct* task)
 {
 	int ret = 0;
 	struct stapiu_consumer *c;
 
 	list_for_each_entry(c, &target->consumers, target_consumer) {
 		if (! c->registered) {
+			int i;
+			for (i=0; i < c->perf_counters_dim; i++) {
+			  if ((*(c->perf_counters))[i] > -1)
+			    _stp_perf_read_init ((*(c->perf_counters))[i], task);
+		        }
 			ret = stapiu_register(target->inode, c);
 			if (ret) {
 				c->registered = 0;
@@ -459,7 +466,7 @@ stapiu_change_plus(struct stapiu_target* target, struct task_struct *task,
 
 		/* OK, we've checked the target's buildid. Now
 		 * register all its consumers. */
-		rc = stapiu_target_reg(target);
+		rc = stapiu_target_reg(target, task);
 		if (rc) {
 			/* Be sure to release the inode on failure. */
 			iput(target->inode);
diff --git a/runtime/runtime_defines.h b/runtime/runtime_defines.h
index 676cf5b..c93cb3b 100644
--- a/runtime/runtime_defines.h
+++ b/runtime/runtime_defines.h
@@ -111,3 +111,9 @@ enum stp_probe_type {
 /* netfilter probe, triggered on network trafic */
 	stp_probe_type_netfilter,
 };
+
+
+// not the right place for this, but dcl is needed before use by probe_*
+static long _stp_perf_read (int ncpu, unsigned i);
+
+static int _stp_perf_read_init (unsigned i, void* pid);
diff --git a/session.h b/session.h
index 976bb79..86efaa8 100644
--- a/session.h
+++ b/session.h
@@ -286,6 +286,8 @@ public:
   std::vector<stapfile*> files;
   std::vector<vardecl*> globals;
   std::map<std::string,functiondecl*> functions;
+  // probe counter name -> probe associated with counter
+  std::map<std::string,derived_probe*> perf_counters;
   std::vector<derived_probe*> probes; // see also *_probes groups below
   std::vector<embeddedcode*> embeds;
   std::map<std::string, statistic_decl> stat_decls;
diff --git a/staptree.cxx b/staptree.cxx
index e8a4298..5f4e402 100644
--- a/staptree.cxx
+++ b/staptree.cxx
@@ -434,6 +434,12 @@ void entry_op::print (ostream& o) const
 }
 
 
+void perf_op::print (ostream& o) const
+{
+  o << "@perf(" << *operand << ")";
+}
+
+
 void vardecl::print (ostream& o) const
 {
   o << name;
@@ -1470,6 +1476,13 @@ entry_op::visit (visitor* u)
 
 
 void
+perf_op::visit (visitor* u)
+{
+  u->visit_perf_op(this);
+}
+
+
+void
 arrayindex::visit (visitor* u)
 {
   u->visit_arrayindex (this);
@@ -1877,6 +1890,13 @@ traversing_visitor::visit_entry_op (entry_op* e)
 
 
 void
+traversing_visitor::visit_perf_op (perf_op* e)
+{
+  e->operand->visit (this);
+}
+
+
+void
 traversing_visitor::visit_arrayindex (arrayindex* e)
 {
   for (unsigned i=0; i<e->indexes.size(); i++)
@@ -2075,6 +2095,13 @@ varuse_collecting_visitor::visit_entry_op (entry_op *e)
 
 
 void
+varuse_collecting_visitor::visit_perf_op (perf_op *e)
+{
+  functioncall_traversing_visitor::visit_perf_op (e);
+}
+
+
+void
 varuse_collecting_visitor::visit_print_format (print_format* e)
 {
   // NB: Instead of being top-level statements, "print" and "printf"
@@ -2514,6 +2541,13 @@ throwing_visitor::visit_entry_op (entry_op* e)
 
 
 void
+throwing_visitor::visit_perf_op (perf_op* e)
+{
+  throwone (e->tok);
+}
+
+
+void
 throwing_visitor::visit_arrayindex (arrayindex* e)
 {
   throwone (e->tok);
@@ -2786,6 +2820,13 @@ update_visitor::visit_entry_op (entry_op* e)
 }
 
 void
+update_visitor::visit_perf_op (perf_op* e)
+{
+  replace (e->operand);
+  provide (e);
+}
+
+void
 update_visitor::visit_arrayindex (arrayindex* e)
 {
   replace (e->base);
@@ -3047,6 +3088,12 @@ deep_copy_visitor::visit_entry_op (entry_op* e)
 }
 
 void
+deep_copy_visitor::visit_perf_op (perf_op* e)
+{
+  update_visitor::visit_perf_op(new perf_op(*e));
+}
+
+void
 deep_copy_visitor::visit_arrayindex (arrayindex* e)
 {
   update_visitor::visit_arrayindex(new arrayindex(*e));
diff --git a/staptree.h b/staptree.h
index ea97e33..ec98580 100644
--- a/staptree.h
+++ b/staptree.h
@@ -326,6 +326,14 @@ struct entry_op: public expression
 };
 
 
+struct perf_op: public expression
+{
+  literal_string *operand;
+  void print (std::ostream& o) const;
+  void visit (visitor* u);
+};
+
+
 struct arrayindex: public expression
 {
   std::vector<expression*> indexes;
@@ -527,7 +535,6 @@ struct vardecl_builtin: public vardecl
 {
 };
 
-
 struct statement;
 struct functiondecl: public symboldecl
 {
@@ -805,6 +812,7 @@ struct visitor
   virtual void visit_cast_op (cast_op* e) = 0;
   virtual void visit_defined_op (defined_op* e) = 0;
   virtual void visit_entry_op (entry_op* e) = 0;
+  virtual void visit_perf_op (perf_op* e) = 0;
 };
 
 
@@ -850,6 +858,7 @@ struct traversing_visitor: public visitor
   void visit_cast_op (cast_op* e);
   void visit_defined_op (defined_op* e);
   void visit_entry_op (entry_op* e);
+  void visit_perf_op (perf_op* e);
 };
 
 
@@ -899,7 +908,7 @@ struct varuse_collecting_visitor: public functioncall_traversing_visitor
   void visit_cast_op (cast_op* e);
   void visit_defined_op (defined_op* e);
   void visit_entry_op (entry_op* e);
-
+  void visit_perf_op (perf_op* e);
   bool side_effect_free ();
   bool side_effect_free_wrt (const std::set<vardecl*>& vars);
 };
@@ -953,6 +962,7 @@ struct throwing_visitor: public visitor
   void visit_cast_op (cast_op* e);
   void visit_defined_op (defined_op* e);
   void visit_entry_op (entry_op* e);
+  void visit_perf_op (perf_op* e);
 };
 
 // A visitor similar to a traversing_visitor, but with the ability to rewrite
@@ -1023,6 +1033,7 @@ struct update_visitor: public visitor
   virtual void visit_cast_op (cast_op* e);
   virtual void visit_defined_op (defined_op* e);
   virtual void visit_entry_op (entry_op* e);
+  virtual void visit_perf_op (perf_op* e);
 
 private:
   std::stack<void *> targets;
@@ -1082,6 +1093,7 @@ struct deep_copy_visitor: public update_visitor
   virtual void visit_cast_op (cast_op* e);
   virtual void visit_defined_op (defined_op* e);
   virtual void visit_entry_op (entry_op* e);
+  virtual void visit_perf_op (perf_op* e);
 };
 
 #endif // STAPTREE_H
diff --git a/tapset-perfmon.cxx b/tapset-perfmon.cxx
index 35237da..17e79e8 100644
--- a/tapset-perfmon.cxx
+++ b/tapset-perfmon.cxx
@@ -31,6 +31,7 @@ static const string TOK_TYPE("type");
 static const string TOK_CONFIG("config");
 static const string TOK_SAMPLE("sample");
 static const string TOK_PROCESS("process");
+static const string TOK_COUNTER("counter");
 
 
 // ------------------------------------------------------------------------
@@ -46,7 +47,8 @@ struct perf_derived_probe: public derived_probe
   int64_t interval;
   bool has_process;
   string process_name;
-  perf_derived_probe (probe* p, probe_point* l, int64_t type, int64_t config, int64_t i, bool pp, string pn);
+  string counter;
+  perf_derived_probe (probe* p, probe_point* l, int64_t type, int64_t config, int64_t i, bool pp, string pn, string cv);
   virtual void join_group (systemtap_session& s);
 };
 
@@ -64,11 +66,12 @@ perf_derived_probe::perf_derived_probe (probe* p, probe_point* l,
                                         int64_t config,
                                         int64_t i,
 					bool process_p,
-					string process_n):
+					string process_n,
+					string counter):
   
   derived_probe (p, l, true /* .components soon rewritten */),
   event_type (type), event_config (config), interval (i),
-  has_process (process_p), process_name (process_n)
+  has_process (process_p), process_name (process_n), counter (counter)
 {
   vector<probe_point::component*>& comps = this->sole_location()->components;
   comps.clear();
@@ -77,6 +80,7 @@ perf_derived_probe::perf_derived_probe (probe* p, probe_point* l,
   comps.push_back (new probe_point::component (TOK_CONFIG, new literal_number (config)));
   comps.push_back (new probe_point::component (TOK_SAMPLE, new literal_number (interval)));
   comps.push_back (new probe_point::component (TOK_PROCESS, new literal_string (process_name)));
+  comps.push_back (new probe_point::component (TOK_COUNTER, new literal_string (counter)));
 }
 
 
@@ -87,14 +91,23 @@ perf_derived_probe::join_group (systemtap_session& s)
     s.perf_derived_probes = new perf_derived_probe_group ();
   s.perf_derived_probes->enroll (this);
 
+  if (has_process)
     enable_task_finder(s);
-
 }
 
 
 void
 perf_derived_probe_group::emit_module_decls (systemtap_session& s)
 {
+  bool have_a_process_tag = false;
+
+  for (unsigned i=0; i < probes.size(); i++)
+    if (probes[i]->has_process)
+      {
+	have_a_process_tag = true;
+	break;
+      }
+
   if (probes.empty()) return;
 
   s.op->newline() << "/* ---- perf probes ---- */";
@@ -120,14 +133,14 @@ perf_derived_probe_group::emit_module_decls (systemtap_session& s)
     }
   s.op->newline();
 
-  // Output task finder callback routine that gets called for all
-  // perf probe types.
+  // Output task finder callback routine
+  if (have_a_process_tag)
+    {
       s.op->newline() << "static int _stp_perf_probe_cb(struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {";
       s.op->indent(1);
       s.op->newline() << "int rc = 0;";
       s.op->newline() << "struct stap_perf_probe *p = container_of(tgt, struct stap_perf_probe, tgt);";
 
-  s.op->newline() << "_stp_printf(\"XXX %d %d\\n\", current->pid, task_pid_nr_ns(tsk,task_active_pid_ns(current->parent)));";
       s.op->newline() << "if (register_p) ";
       s.op->indent(1);
 
@@ -136,6 +149,7 @@ perf_derived_probe_group::emit_module_decls (systemtap_session& s)
       s.op->newline(1) << "_stp_perf_del(p);";
       s.op->newline(-1) << "return rc;";
       s.op->newline(-1) << "}";
+    }
 
   /* data structures */
   s.op->newline() << "static struct stap_perf_probe stap_perf_probes ["
@@ -166,6 +180,8 @@ perf_derived_probe_group::emit_module_decls (systemtap_session& s)
 	  else
 	    l_process_name = probes[i]->process_name;
 
+	  if (probes[i]->has_process)
+	    {
 	      s.op->line() << " .tgt={";
 	      s.op->line() << " .procname=\"" << l_process_name << "\",";
 	      s.op->line() << " .pid=0,";
@@ -173,6 +189,9 @@ perf_derived_probe_group::emit_module_decls (systemtap_session& s)
 	      s.op->line() << " },";
 	      s.op->newline() << ".per_thread=" << "1, ";
 	    }
+	}
+      else if (probes[i]->counter.length() != 0)
+	s.op->newline() << ".per_thread=" << "1, ";
       else
 	s.op->newline() << ".per_thread=" << "0, ";
       s.op->newline(-1) << "},";
@@ -215,7 +234,6 @@ perf_derived_probe_group::emit_module_decls (systemtap_session& s)
   s.op->newline() << "(*stp->probe->ph) (c);";
   common_probe_entryfn_epilogue (s, true);
   s.op->newline(-1) << "}";
-
   s.op->newline();
   s.op->newline() << "#include \"linux/perf.c\"";
   s.op->newline();
@@ -225,6 +243,15 @@ perf_derived_probe_group::emit_module_decls (systemtap_session& s)
 void
 perf_derived_probe_group::emit_module_init (systemtap_session& s)
 {
+  bool have_a_process_tag = false;
+  
+  for (unsigned i=0; i < probes.size(); i++)
+    if (probes[i]->has_process)
+      {
+	have_a_process_tag = true;
+	break;
+      }
+
   if (probes.empty()) return;
 
   s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
@@ -237,6 +264,7 @@ perf_derived_probe_group::emit_module_init (systemtap_session& s)
   s.op->newline(-1) << "}"; // for unwind loop
   s.op->newline() << "break;";
   s.op->newline(-1) << "}"; // if-error
+  if (have_a_process_tag)
     s.op->newline() << "rc = stap_register_task_finder_target(&stp->tgt);";
   s.op->newline(-1) << "}"; // for loop
 }
@@ -298,11 +326,23 @@ perf_builder::build(systemtap_session & sess,
   proc_p = has_null_param(parameters, TOK_PROCESS)
     || get_param(parameters, TOK_PROCESS, proc_n);
 
+  string var;
+  get_param(parameters, TOK_COUNTER, var);
+  if (var.length() > 0)
+    {
+      period = 0;		// perf_event_attr.sample_freq should be 0
+      map<string, derived_probe*>::iterator it;
+      for (it=sess.perf_counters.begin(); it != sess.perf_counters.end(); it++)
+	if ((*it).first == var)
+	  throw semantic_error(_("duplicate counter name"));
+    }
+
   if (sess.verbose > 1)
     clog << _F("perf probe type=%" PRId64 " config=%" PRId64 " period=%" PRId64, type, config, period) << endl;
 
   finished_results.push_back
-    (new perf_derived_probe(base, location, type, config, period, proc_p, proc_n));
+    (new perf_derived_probe(base, location, type, config, period, proc_p, proc_n, var));
+  sess.perf_counters[var] = finished_results.back();
 }
 
 
@@ -318,6 +358,7 @@ register_tapset_perf(systemtap_session& s)
   event->bind(builder);
   event->bind_num(TOK_SAMPLE)->bind(builder);
   event->bind_str(TOK_PROCESS)->bind(builder);
+  event->bind_str(TOK_COUNTER)->bind(builder);
   event->bind(TOK_PROCESS)->bind(builder);
 }
 
diff --git a/tapsets.cxx b/tapsets.cxx
index 8c94f23..c314fa0 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -469,7 +469,7 @@ struct dwarf_derived_probe: public derived_probe
 
   void printsig (std::ostream &o) const;
   virtual void join_group (systemtap_session& s);
-  void emit_probe_local_init(translator_output * o);
+  void emit_probe_local_init(systemtap_session& s, translator_output * o);
   void getargs(std::list<std::string> &arg_set) const;
 
   void emit_privilege_assertion (translator_output*);
@@ -2230,6 +2230,8 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor
   unsigned saved_longs, saved_strings; // data saved within kretprobes
   map<std::string, expression *> return_ts_map;
   vector<Dwarf_Die> scopes;
+  // probe counter name -> ordinal position of associated probe
+  std::vector<derived_probe*> perf_counter_refs;
   bool visited;
 
   dwarf_var_expanding_visitor(dwarf_query & q, Dwarf_Die *sd, Dwarf_Addr a):
@@ -2243,6 +2245,7 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor
   void visit_target_symbol (target_symbol* e);
   void visit_cast_op (cast_op* e);
   void visit_entry_op (entry_op* e);
+  void visit_perf_op (perf_op* e);
 private:
   vector<Dwarf_Die>& getcuscope(target_symbol *e);
   vector<Dwarf_Die>& getscopes(target_symbol *e);
@@ -3805,6 +3808,39 @@ dwarf_var_expanding_visitor::visit_entry_op (entry_op *e)
   provide (repl);
 }
 
+void
+dwarf_var_expanding_visitor::visit_perf_op (perf_op *e)
+{
+  token* t = new token;
+  t->location = e->tok->location;
+  t->type = tok_identifier;
+  t->content = e->operand->value;
+
+  add_block = new block;
+
+  systemtap_session &s = this->q.sess;
+  map<string, derived_probe*>::iterator it;
+  unsigned i;
+  // Find the associated perf.counter probe
+  for (it=s.perf_counters.begin(), i=0;
+       it != s.perf_counters.end();
+       it++, i++)
+    if ((*it).first == e->operand->value)
+      break;
+  // remember perf counter's associated probe
+  if (it != s.perf_counters.end())
+    {
+      perf_counter_refs.push_back((*it).second);
+      // Just grab the value from the probe locals
+      symbol* sym = new symbol;
+      sym->tok = t;
+      sym->name = "__perf_read_" + (*it).first;
+      provide (sym);
+    }
+  else
+    throw semantic_error(_("perf counter not defined"));
+}
+
 vector<Dwarf_Die>&
 dwarf_var_expanding_visitor::getcuscope(target_symbol *e)
 {
@@ -4319,6 +4355,30 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname,
       // XXX: user-space deref's for q.has_process!
       dwarf_var_expanding_visitor v (q, scope_die, dwfl_addr);
       v.replace (this->body);
+
+      // Propagate perf.counters so we can emit later
+      this->perf_counter_refs = v.perf_counter_refs;
+      // Emit local var to save the read perf counter value
+      std::vector<derived_probe*>::iterator pcii;
+      for (pcii = v.perf_counter_refs.begin();
+	   pcii != v.perf_counter_refs.end(); pcii++)
+	{
+	  map<string, derived_probe*>::iterator it;
+	  // Find the associated perf counter probe
+	  for (it=q.sess.perf_counters.begin() ;
+	       it != q.sess.perf_counters.end(); it++)
+	    if ((*it).second == (*pcii))
+	      break;
+	  vardecl* vd = new vardecl;
+	  vd->name = "__perf_read_" + (*it).first;
+	  vd->tok = this->tok;
+	  vd->set_arity(0, this->tok);
+	  vd->type = pe_long;
+	  vd->synthetic = true;
+	  this->locals.push_back (vd);
+	}
+
+
       if (!q.has_process)
         access_vars = v.visited;
 
@@ -4714,8 +4774,26 @@ dwarf_derived_probe::register_patterns(systemtap_session& s)
 }
 
 void
-dwarf_derived_probe::emit_probe_local_init(translator_output * o)
-{
+dwarf_derived_probe::emit_probe_local_init(systemtap_session& s, translator_output * o)
+{
+  std::vector<derived_probe*>::iterator pcii;
+  for (pcii = perf_counter_refs.begin();
+       pcii != perf_counter_refs.end();
+       pcii++)
+    {
+      map<string, derived_probe*>::iterator it;
+      // Find the associated perf.counter probe
+      unsigned i = 0;
+      for (it=s.perf_counters.begin() ;
+	   it != s.perf_counters.end(); it++, i++)
+	if ((*it).second == (*pcii))
+	  break;
+      // place the perf counter read so it precedes stp_lock_probe
+      o->newline() << "l->l___perf_read_" + (*it).first
+	+ " = (((int64_t) (_stp_perf_read(smp_processor_id(),"
+	+ lex_cast(i) + "))));";
+    }
+  
   if (access_vars)
     {
       // if accessing $variables, emit bsp cache setup for speeding up
@@ -7395,6 +7473,28 @@ uprobe_derived_probe_group::emit_module_utrace_decls (systemtap_session& s)
 
   s.op->assert_0_indent();
 
+  unsigned pci;
+  for (pci=0; pci<probes.size(); pci++)
+    {
+      // List of perf.counter used by a probe
+      uprobe_derived_probe *p = probes[pci];
+      std::vector<derived_probe*>::iterator pcii;
+      s.op->newline() << "long perf_counters_" + lex_cast(pci) + "[] = {";
+      for (pcii = p->perf_counter_refs.begin();
+	   pcii != p->perf_counter_refs.end(); pcii++)
+	{
+	  map<string, derived_probe*>::iterator it;
+	  unsigned i = 0;
+	  // Find the associated perf.counter probe
+	  for (it=s.perf_counters.begin() ;
+	       it != s.perf_counters.end(); it++, i++)
+	    if ((*it).second == (*pcii))
+	      break;
+	  s.op->line() << lex_cast(i) << ", ";
+	}
+      s.op->newline() << "};";
+    }
+
    // NB: read-only structure
   s.op->newline() << "static const struct stap_uprobe_spec stap_uprobe_specs [] = {";
   s.op->indent(1);
@@ -7413,6 +7513,10 @@ uprobe_derived_probe_group::emit_module_utrace_decls (systemtap_session& s)
         s.op->line() << " .sdt_sem_offset=(unsigned long)0x"
                      << hex << p->sdt_semaphore_addr << dec << "ULL,";
 
+      s.op->line() << " .perf_counters_dim=ARRAY_SIZE(perf_counters_" << lex_cast(i) << "),";
+      // List of perf.counter used by a probe from above
+      s.op->line() << " .perf_counters=&perf_counters_" + lex_cast(i) + ",";
+
       if (p->has_return)
         s.op->line() << " .return_p=1,";
       s.op->line() << " },";
@@ -7671,6 +7775,27 @@ uprobe_derived_probe_group::emit_module_inode_decls (systemtap_session& s)
   s.op->assert_0_indent();
 
   // Declare the actual probes.
+  unsigned pci;
+  for (pci=0; pci<probes.size(); pci++)
+    {
+      // List of perf.counter used by a probe
+      uprobe_derived_probe *p = probes[pci];
+      std::vector<derived_probe*>::iterator pcii;
+      s.op->newline() << "long perf_counters_" + lex_cast(pci) + "[] = {";
+      for (pcii = p->perf_counter_refs.begin();
+	   pcii != p->perf_counter_refs.end(); pcii++)
+	{
+	  map<string, derived_probe*>::iterator it;
+	  unsigned i = 0;
+	  // Find the associated perf.counter probe
+	  for (it=s.perf_counters.begin() ; it != s.perf_counters.end(); it++, i++)
+	    if ((*it).second == (*pcii))
+	      break;
+	  s.op->line() << lex_cast(i) << ", ";
+	}
+      s.op->newline() << "};";
+    }
+
   s.op->newline() << "static struct stapiu_consumer "
                   << "stap_inode_uprobe_consumers[] = {";
   s.op->indent(1);
@@ -7686,6 +7811,9 @@ uprobe_derived_probe_group::emit_module_inode_decls (systemtap_session& s)
       if (p->sdt_semaphore_addr)
         s.op->line() << " .sdt_sem_offset=(loff_t)0x"
                      << hex << p->sdt_semaphore_addr << dec << "ULL,";
+      s.op->line() << " .perf_counters_dim=ARRAY_SIZE(perf_counters_" << lex_cast(i) << "),";
+      // List of perf.counter used by a probe from above
+      s.op->line() << " .perf_counters=&perf_counters_" + lex_cast(i) + ",";
       s.op->line() << " .probe=" << common_probe_init (p) << ",";
       s.op->line() << " },";
     }
diff --git a/testsuite/systemtap.base/perf.exp b/testsuite/systemtap.base/perf.exp
index 39fdab0..018f3d0 100644
--- a/testsuite/systemtap.base/perf.exp
+++ b/testsuite/systemtap.base/perf.exp
@@ -6,9 +6,8 @@ proc cleanup_handler { verbose } {
 
 set stap_path $env(SYSTEMTAP_PATH)/stap
 set exepath "[pwd]/towers.x"
-set flags ""
-
 set subtest "process()"
+set flags "additional_flags=-g"
 
 set res [target_compile $srcdir/$subdir/towers.c $exepath executable $flags]
 if { $res != "" } {
@@ -78,4 +77,33 @@ if {$ok == 1} {
     fail "$test $subtest ($ok)"
 }
 
+set subtest "counter"
+
+spawn $stap_path -c $exepath -e "
+global towers
+global main
+probe perf.type(0).config(0).counter(\"a\")
+{
+ counter_a += 1
+}
+probe perf.type(0).config(0).counter(\"b\")
+{
+ counter_b += 1
+}
+probe process(\"$exepath\").statement(\"towers@towers.c\")
+{
+ towers <<< @perf(\"a\")
+}
+probe process(\"$exepath\").statement(\"main@towers.c\")
+{
+ main <<< @perf(\"b\")
+}
+probe end
+{
+ printf(\"main=%d\\ntowers=%d\\n\", @count(main), @count(towers))
+}
+"
+
+catch {close}; catch {wait}
+
 cleanup_handler $verbose
diff --git a/translate.cxx b/translate.cxx
index 6ff6695..c729f06 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -196,6 +196,7 @@ struct c_unparser: public unparser, public visitor
   void visit_cast_op (cast_op* e);
   void visit_defined_op (defined_op* e);
   void visit_entry_op (entry_op* e);
+  void visit_perf_op (perf_op* e);
 };
 
 // A shadow visitor, meant to generate temporary variable declarations
@@ -2133,7 +2134,8 @@ c_unparser::emit_probe (derived_probe* v)
         v->emit_privilege_assertion (o);
 
       // emit probe local initialization block
-      v->emit_probe_local_init(o);
+
+      v->emit_probe_local_init(*this->session, o);
 
       // emit all read/write locks for global variables
       if (v->needs_global_locks ())
@@ -4234,6 +4236,13 @@ c_unparser::visit_entry_op (entry_op* e)
 
 
 void
+c_unparser::visit_perf_op (perf_op* e)
+{
+  throw semantic_error(_("cannot translate general @perf expression"), e->tok);
+}
+
+
+void
 c_tmpcounter::load_map_indices(arrayindex *e)
 {
   symbol *array;

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