summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwcohen <wcohen>2006-09-12 22:05:48 +0000
committerwcohen <wcohen>2006-09-12 22:05:48 +0000
commit47dd066dfec8ab73f7b1886920e153402baa4597 (patch)
tree01331b69b773f6076c10837085e7eb6e39a99219
parent7f0e10b1a9623425f4600658e0358d5c607abba6 (diff)
downloadsystemtap-steved-47dd066dfec8ab73f7b1886920e153402baa4597.tar.gz
systemtap-steved-47dd066dfec8ab73f7b1886920e153402baa4597.tar.xz
systemtap-steved-47dd066dfec8ab73f7b1886920e153402baa4597.zip
Systemtap perfmon support to access the processors perfmon hardware.
-rwxr-xr-xconfigure113
-rw-r--r--configure.ac26
-rw-r--r--elaborate.cxx7
-rw-r--r--elaborate.h4
-rw-r--r--main.cxx1
-rw-r--r--runtime/perf.c135
-rw-r--r--runtime/perf.h27
-rw-r--r--runtime/runtime.h3
-rw-r--r--session.h1
-rw-r--r--stapfuncs.5.in7
-rw-r--r--stapprobes.5.in31
-rw-r--r--tapset/perfmon.stp13
-rw-r--r--tapsets.cxx470
-rwxr-xr-xtestsuite/buildok/perfmon01.stp26
-rw-r--r--testsuite/systemtap.pass1-4/buildok.exp1
-rw-r--r--translate.cxx3
16 files changed, 865 insertions, 3 deletions
diff --git a/configure b/configure
index f3ef0064..c497becf 100755
--- a/configure
+++ b/configure
@@ -860,10 +860,13 @@ Optional Features:
(and sometimes confusing) to the casual installer
--disable-dependency-tracking speeds up one-time build
--enable-dependency-tracking do not reject slow dependency extractors
+ --enable-perfmon enable perfmon support (default is disabled)
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-perfmon=DIRECTORY
+ Path to Perfmon installation to use
--with-elfutils=DIRECTORY
find elfutils source code in DIRECTORY
@@ -5078,6 +5081,115 @@ fi
+# Check whether --enable-perfmon or --disable-perfmon was given.
+if test "${enable_perfmon+set}" = set; then
+ enableval="$enable_perfmon"
+ perfmon_support=$enableval
+else
+ perfmon_support=no
+fi;
+
+
+# Check whether --with-perfmon or --without-perfmon was given.
+if test "${with_perfmon+set}" = set; then
+ withval="$with_perfmon"
+
+case "$with_perfmon" in
+yes ) { { echo "$as_me:$LINENO: error: --with-perfmon requires an argument" >&5
+echo "$as_me: error: --with-perfmon requires an argument" >&2;}
+ { (exit 1); exit 1; }; } ;;
+''|no) ;;
+*) perfmon_support=yes ;;
+esac
+fi;
+
+
+if test $perfmon_support = yes; then
+
+
+echo "$as_me:$LINENO: checking for pfm_start in -lpfm" >&5
+echo $ECHO_N "checking for pfm_start in -lpfm... $ECHO_C" >&6
+if test "${ac_cv_lib_pfm_pfm_start+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpfm $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char pfm_start ();
+int
+main ()
+{
+pfm_start ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_pfm_pfm_start=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_pfm_pfm_start=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_pfm_pfm_start" >&5
+echo "${ECHO_T}$ac_cv_lib_pfm_pfm_start" >&6
+if test $ac_cv_lib_pfm_pfm_start = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBPFM 1
+_ACEOF
+
+ LIBS="-lpfm $LIBS"
+
+else
+
+ { { echo "$as_me:$LINENO: error: systemtap cannot find required perfmon libs" >&5
+echo "$as_me: error: systemtap cannot find required perfmon libs" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ CFLAGS="$CFLAGS -DPERFMON -I$with_perfmon/include"
+ CXXFLAGS="$CXXFLAGS -DPERFMON -I$with_perfmon/include"
+ LDFLAGS="$LDFLAGS -DPERFMON -L$with_perfmon/lib -lpfm"
+fi
+
build_elfutils=no
# Check whether --with-elfutils or --without-elfutils was given.
@@ -5124,7 +5236,6 @@ if test $build_elfutils = no; then
# Need libdwfl-capable recent elfutils from Fedora
save_LIBS="$LIBS"
-
echo "$as_me:$LINENO: checking for dwfl_module_getsym in -ldw" >&5
echo $ECHO_N "checking for dwfl_module_getsym in -ldw... $ECHO_C" >&6
if test "${ac_cv_lib_dw_dwfl_module_getsym+set}" = set; then
diff --git a/configure.ac b/configure.ac
index e2ce76a9..1431086a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -22,6 +22,32 @@ AC_PROG_MAKE_SET
AC_SUBST(CFLAGS)
AC_SUBST(CXXFLAGS)
+dnl enable option to generate code for using perfmon
+AC_ARG_ENABLE(perfmon,
+ [ --enable-perfmon enable perfmon support (default is disabled)],
+ perfmon_support=$enableval, perfmon_support=no)
+
+AC_ARG_WITH([perfmon],
+ AC_HELP_STRING([--with-perfmon=DIRECTORY],
+ [Path to Perfmon installation to use]),
+ [
+case "$with_perfmon" in
+yes ) AC_MSG_ERROR([--with-perfmon requires an argument]) ;;
+''|no) ;;
+*) perfmon_support=yes ;;
+esac])
+
+
+dnl XXX fixme so that it it can use perfmon installed for real
+dnl check that perfmon libraries are actually there
+if test $perfmon_support = yes; then
+ AC_CHECK_LIB(pfm, pfm_start,,[
+ AC_MSG_ERROR([systemtap cannot find required perfmon libs])])
+ CFLAGS="$CFLAGS -DPERFMON -I$with_perfmon/include"
+ CXXFLAGS="$CXXFLAGS -DPERFMON -I$with_perfmon/include"
+ LDFLAGS="$LDFLAGS -DPERFMON -L$with_perfmon/lib -lpfm"
+fi
+
build_elfutils=no
AC_ARG_WITH([elfutils],
AC_HELP_STRING([--with-elfutils=DIRECTORY],
diff --git a/elaborate.cxx b/elaborate.cxx
index 25c223ec..c3b9cbe9 100644
--- a/elaborate.cxx
+++ b/elaborate.cxx
@@ -122,6 +122,13 @@ derived_probe_group::register_probe(timer_derived_probe* p)
}
+void
+derived_probe_group::register_probe(perfmon_derived_probe* p)
+{
+ throw semantic_error ("unexpected registration of a perfmon_derived_probe");
+}
+
+
// ------------------------------------------------------------------------
// Members of derived_probe_builder
diff --git a/elaborate.h b/elaborate.h
index 096d4238..d2251802 100644
--- a/elaborate.h
+++ b/elaborate.h
@@ -159,6 +159,7 @@ struct mark_derived_probe;
struct never_derived_probe;
struct profile_derived_probe;
struct timer_derived_probe;
+struct perfmon_derived_probe;
struct unparser;
struct derived_probe_group
@@ -172,6 +173,7 @@ struct derived_probe_group
virtual void register_probe(never_derived_probe* p);
virtual void register_probe(profile_derived_probe* p);
virtual void register_probe(timer_derived_probe* p);
+ virtual void register_probe(perfmon_derived_probe* p);
virtual size_t size () = 0;
virtual void emit_probes (translator_output* op, unparser* up) = 0;
@@ -265,6 +267,7 @@ private:
derived_probe_group* never_probe_group;
derived_probe_group* profile_probe_group;
derived_probe_group* timer_probe_group;
+ derived_probe_group* perfmon_probe_group;
public:
derived_probe_group_container ();
@@ -277,6 +280,7 @@ public:
void register_probe (never_derived_probe* p);
void register_probe (profile_derived_probe* p);
void register_probe (timer_derived_probe* p);
+ void register_probe (perfmon_derived_probe* p);
size_t size () { return (probes.size ()); }
derived_probe* operator[] (size_t n) { return (probes[n]); }
diff --git a/main.cxx b/main.cxx
index ddd91fe2..8f084024 100644
--- a/main.cxx
+++ b/main.cxx
@@ -139,6 +139,7 @@ main (int argc, char * const argv [])
s.cmd = "";
s.target_pid = 0;
s.merge=true;
+ s.perfmon=0;
const char* s_p = getenv ("SYSTEMTAP_TAPSET");
if (s_p != NULL)
diff --git a/runtime/perf.c b/runtime/perf.c
new file mode 100644
index 00000000..0b812630
--- /dev/null
+++ b/runtime/perf.c
@@ -0,0 +1,135 @@
+/* -*- linux-c -*-
+ * Perf Functions
+ * Copyright (C) 2006 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
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#ifndef _PERF_C_
+#define _PERF_C_
+
+#include <linux/perfmon.h>
+
+#include "perf.h"
+
+/** @file perf.c
+ * @brief Implements performance monitoring hardware support
+ */
+
+/* TODO fix so this works on SMP machines
+ * Need to do context load, register setup, and start on each processor
+ *
+ * Similarly need to stop and unload on each processor
+ */
+
+/* TODO make this work with sampling. There needs to be a help thread
+ * handling the sampling. */
+
+
+static int _stp_pfm_register_setup(void *desc,
+ struct pfarg_pmc pmc[], int pmc_count,
+ struct pfarg_pmd pmd[], int pmd_count)
+{
+ int err = 0;
+
+ if (desc == 0) return -EINVAL;
+ err = pfmk_write_pmcs(desc, pmc, pmc_count);
+ if (err) return err;
+
+ err = pfmk_write_pmds(desc, pmd, pmd_count);
+ return err;
+}
+
+static struct completion c;
+static struct pfarg_load load_args;
+static struct pfarg_start start_args;
+
+/** Sets up the performance monitoring hardware.
+ * The locations desc and context point to are modified as
+ * side-effects of the setup. desc is a unique pointer used
+ * by the various routines.
+ * @param desc pointer to void *, handle to describe perfmon config
+ * @param context pointer to context information
+ * @param pmc, pointer to array describing control register setup
+ * @param pmc_count, number of entries in pmc
+ * @param pmd, pointer to array describing data register setup
+ * @param pmd_count, number of entries in pmd
+ * @returns an int, 0 if no errors encountered during setup
+ */
+int _stp_perfmon_setup(void **desc,
+ struct pfarg_ctx *context,
+ struct pfarg_pmc pmc[], int pmc_count,
+ struct pfarg_pmd pmd[], int pmd_count)
+{
+ int err = 0;
+
+ /* create a context */
+ err = pfmk_create_context(context, NULL, 0, &c, desc, NULL);
+ if (err) goto cleanup;
+
+ /* set up the counters */
+ err = _stp_pfm_register_setup(*desc, pmc, pmc_count, pmd, pmd_count);
+ if (err) goto cleanup2;
+
+ /* start measuring */
+ err = pfmk_load_context(*desc, &load_args);
+ if (err) {
+ printk("pfmk_load_context error\n");
+ goto cleanup2;
+ }
+ err = pfmk_start(*desc, &start_args);
+ if (err) {
+ printk("pfmk_start error\n");
+ goto cleanup3;
+ }
+
+ return err;
+
+cleanup3: pfmk_unload_context(*desc);
+cleanup2: pfmk_close(*desc);
+cleanup: *desc=NULL;
+ return err;
+}
+
+/** Shuts down the performance monitoring hardware.
+ * @param desc unique pointer to describe configuration
+ * @returns an int, 0 if no errors encountered during shutdown
+ */
+int _stp_perfmon_shutdown(void *desc)
+{
+ int err=0;
+
+ if (desc == 0) return -EINVAL;
+ /* stop the counters */
+ err=pfmk_stop(desc);
+ if (err) return err;
+ err=pfmk_unload_context(desc);
+ if (err) return err;
+ err=pfmk_close(desc);
+ return err;
+}
+
+/** Reads the performance counter
+ * @param desc unique pointer to describe configuration
+ * @returns an int64, raw value of counter
+ */
+int64_t _stp_perfmon_read(void *desc, int counter)
+{
+ struct pfarg_pmd storage;
+
+ storage.reg_set = 0;
+ storage.reg_num = counter;
+
+ if ( desc != NULL) {
+ if (pfmk_read_pmds(desc, &storage, 1))
+ printk( "pfm_read_pmds error\n");
+ }
+
+ return storage.reg_value;
+}
+
+#endif /* _PERF_C_ */
+
diff --git a/runtime/perf.h b/runtime/perf.h
new file mode 100644
index 00000000..e3212228
--- /dev/null
+++ b/runtime/perf.h
@@ -0,0 +1,27 @@
+/* -*- linux-c -*-
+ * Perf Header File
+ * Copyright (C) 2006 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
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#ifndef _PERF_H_
+#define _PERF_H_
+
+/** @file perf.h
+ * @brief Header file for performance monitoring hardware support
+ */
+
+int _stp_perfmon_setup(void **desc,
+ struct pfarg_ctx *context,
+ struct pfarg_pmc pmc[], int pmc_count,
+ struct pfarg_pmd pmd[], int pmd_count);
+
+int _stp_perfmon_shutdown(void *desc);
+
+int64_t _stp_perfmon_read(void *desc, int counter);
+
+#endif /* _PERF_H_ */
diff --git a/runtime/runtime.h b/runtime/runtime.h
index f0dec3c4..84c79f79 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -69,6 +69,9 @@ static struct
#include "copy.c"
#include "sym.h"
#include "alloc.c"
+#ifdef STP_PERFMON
+#include "perf.c"
+#endif
/************* Module Stuff ********************/
diff --git a/session.h b/session.h
index 50e06437..9627925e 100644
--- a/session.h
+++ b/session.h
@@ -78,6 +78,7 @@ struct systemtap_session
bool unoptimized;
bool merge;
int buffer_size;
+ unsigned perfmon;
// temporary directory for module builds etc.
// hazardous - it is "rm -rf"'d at exit
diff --git a/stapfuncs.5.in b/stapfuncs.5.in
index 74f65969..51cdddd8 100644
--- a/stapfuncs.5.in
+++ b/stapfuncs.5.in
@@ -261,6 +261,13 @@ Return a simple textual rendering (e.g., "Wed\ Jun\ 30\ 21:49:008\ 1993")
of the given number of seconds since the epoch, as perhaps returned by
.IR gettimeofday_s() .
+.SS PERFMON
+.TP
+read_counter:long (handle:long)
+Returns the value for the processor's performance counter for the associated
+handle. The body of the a perfmon probe should set record
+the handle being used for that event.
+
.SH FILES
.nh
.IR /usr/share/systemtap/tapset
diff --git a/stapprobes.5.in b/stapprobes.5.in
index 38314f8b..b56fe432 100644
--- a/stapprobes.5.in
+++ b/stapprobes.5.in
@@ -230,6 +230,37 @@ named
where NN is the number of parameters supplied by the macro. Number
and string parameters are passed in a type-safe manner.
+.SS PERFORMANCE MONITORING HARDWARE
+
+The perfmon family of probe points is used to access the performance
+monitoring hardware available in modern processors. This family of
+probes points needs the perfmon2 support in the kernel to access the
+performance monitoring hardware.
+.PP
+Performance monitor hardware points begin with a
+.BR perfmon ". "
+The next part of the names the event being counted
+.BR counter("event") .
+The event names are processor implementation specific with the
+execption of the generic
+.BR cycles " and " instructions
+events, which are available on all processors. This sets up a counter
+on the processor to count the number of events occuring on the
+processor. For more details on the performance monitoring events
+available on a specific processor use the command perfmon2 command:
+.SAMPLE
+pfmon -l
+.ESAMPLE
+.TP
+$counter
+is a handle used in the body of the probe for operations
+involving the counter associated with the probe.
+.TP
+read_counter
+is a function that is passed the handle for the perfmon probe and returns
+the current count for the event.
+
+
.SS IO SCHEDULER
This family of probe points is used to probe the IO scheduler activities.
diff --git a/tapset/perfmon.stp b/tapset/perfmon.stp
new file mode 100644
index 00000000..3ed21856
--- /dev/null
+++ b/tapset/perfmon.stp
@@ -0,0 +1,13 @@
+// perfmon
+// Copyright (C) 2006 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
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+// timeout (TCP_RTO_MAX)
+function read_counter:long(handle:long)
+%{
+ THIS->__retvalue = _stp_perfmon_read(_pfm_desc, THIS->handle);
+%}
diff --git a/tapsets.cxx b/tapsets.cxx
index ba6a0061..ddea8f94 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -40,6 +40,12 @@ extern "C" {
}
+#ifdef PERFMON
+#include <perfmon/pfmlib.h>
+#include <perfmon/perfmon.h>
+#endif
+
+
using namespace std;
@@ -126,6 +132,16 @@ emit_probe_timing(derived_probe* p, translator_output* o)
void
derived_probe::emit_common_header (translator_output* o)
{
+
+#ifdef PERFMON
+ o->newline() << "static struct pfarg_ctx _pfm_context;";
+ o->newline() << "static void *_pfm_desc;";
+ o->newline() << "static struct pfarg_pmc *_pfm_pmc_x;";
+ o->newline() << "static int _pfm_num_pmc_x;";
+ o->newline() << "static struct pfarg_pmd *_pfm_pmd_x;";
+ o->newline() << "static int _pfm_num_pmd_x;";
+#endif
+
o->newline();
o->newline() << "static struct context* common_probe_prologue (int state) {";
o->newline(1);
@@ -4943,6 +4959,419 @@ bad_time:
// ------------------------------------------------------------------------
+// perfmon derived probes
+// ------------------------------------------------------------------------
+// This is a new interface to the perfmon hw.
+//
+
+
+struct perfmon_var_expanding_copy_visitor: public var_expanding_copy_visitor
+{
+ systemtap_session & sess;
+ unsigned counter_number;
+ perfmon_var_expanding_copy_visitor(systemtap_session & s, unsigned c):
+ sess(s), counter_number(c) {}
+ void visit_target_symbol (target_symbol* e);
+};
+
+
+void
+perfmon_var_expanding_copy_visitor::visit_target_symbol (target_symbol *e)
+{
+ assert(e->base_name.size() > 0 && e->base_name[0] == '$');
+
+ // Synthesize a function.
+ functiondecl *fdecl = new functiondecl;
+ fdecl->tok = e->tok;
+ embeddedcode *ec = new embeddedcode;
+ ec->tok = e->tok;
+ bool lvalue = is_active_lvalue(e);
+
+ if (lvalue )
+ throw semantic_error("writes to $counter not permitted");
+
+ string fname = string("_perfmon_tvar_get")
+ + "_" + e->base_name.substr(1)
+ + "_" + lex_cast<string>(counter_number);
+
+ if (e->base_name != "$counter")
+ throw semantic_error ("target variables not available to perfmon probes");
+
+ ec->code = "THIS->__retvalue = _pfm_pmd_x[" +
+ lex_cast<string>(counter_number) + "].reg_num;";
+ ec->code += "/* pure */";
+ fdecl->name = fname;
+ fdecl->body = ec;
+ fdecl->type = pe_long;
+ sess.functions.push_back(fdecl);
+
+ // Synthesize a functioncall.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = fname;
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+
+ provide <functioncall*> (this, n);
+}
+
+
+enum perfmon_mode
+{
+ perfmon_count,
+ perfmon_sample
+};
+
+
+struct perfmon_derived_probe: public derived_probe
+{
+protected:
+ static unsigned probes_allocated;
+
+public:
+ systemtap_session & sess;
+ string event;
+ perfmon_mode mode;
+
+ perfmon_derived_probe (probe* p, probe_point* l, systemtap_session &s,
+ string e, perfmon_mode m);
+ virtual void register_probe (systemtap_session& s);
+ virtual void emit_registrations_start (translator_output* o, unsigned index);
+ virtual void emit_registrations_end (translator_output * o, unsigned index);
+ virtual void emit_deregistrations (translator_output * o);
+ virtual void emit_probe_entries (translator_output * o);
+};
+
+
+struct perfmon_derived_probe_group: public derived_probe_group
+{
+private:
+ vector<perfmon_derived_probe*> probes;
+
+public:
+ virtual void register_probe(perfmon_derived_probe* p)
+ { probes.push_back (p); }
+ virtual size_t size () { return probes.size (); }
+ virtual void emit_probes (translator_output* op, unparser* up);
+ virtual void emit_module_init (translator_output* o);
+};
+
+
+struct perfmon_builder: public derived_probe_builder
+{
+ perfmon_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ std::map<std::string, literal *> const & parameters,
+ vector<derived_probe *> & finished_results)
+ {
+ string event;
+ if (!get_param (parameters, "counter", event))
+ throw semantic_error("perfmon requires an event");
+
+ sess.perfmon++;
+
+ // XXX: need to revise when doing sampling
+ finished_results.push_back(new perfmon_derived_probe(base, location,
+ sess, event,
+ perfmon_count));
+ }
+};
+
+unsigned perfmon_derived_probe::probes_allocated;
+
+perfmon_derived_probe::perfmon_derived_probe (probe* p, probe_point* l,
+ systemtap_session &s,
+ string e, perfmon_mode m)
+ : derived_probe (p, l), sess(s), event(e), mode(m)
+{
+ ++probes_allocated;
+
+ // Now make a local-variable-expanded copy of the probe body
+ perfmon_var_expanding_copy_visitor v (sess, probes_allocated-1);
+ require <block*> (&v, &(this->body), base->body);
+
+ if (sess.verbose > 1)
+ clog << "perfmon-based probe" << endl;
+}
+
+
+void
+perfmon_derived_probe::register_probe (systemtap_session& s)
+{
+ s.probes.register_probe(this);
+}
+
+
+void
+perfmon_derived_probe::emit_registrations_start (translator_output* o,
+ unsigned index)
+{
+ for (unsigned i=0; i<locations.size(); i++)
+ o->newline() << "enter_" << name << "_" << i << " ();";
+}
+
+
+void
+perfmon_derived_probe::emit_registrations_end (translator_output * o,
+ unsigned index)
+{
+}
+
+
+void
+perfmon_derived_probe::emit_deregistrations (translator_output * o)
+{
+}
+
+
+void
+perfmon_derived_probe::emit_probe_entries (translator_output * o)
+{
+ o->newline() << "#ifdef STP_TIMING";
+ o->newline() << "static __cacheline_aligned Stat " << "time_" << basest()->name << ";";
+ o->newline() << "#endif";
+
+ for (unsigned i=0; i<locations.size(); i++)
+ {
+ probe_point *l = locations[i];
+ o->newline() << "/* location " << i << ": " << *l << " */";
+ o->newline() << "static void enter_" << name << "_" << i << " (void) {";
+
+ o->indent(1);
+ o->newline() << "const char* probe_point = "
+ << lex_cast_qstring(*l) << ";";
+ emit_probe_prologue (o,
+ (mode == perfmon_count ?
+ "STAP_SESSION_STARTING" :
+ "STAP_SESSION_RUNNING"));
+
+ // NB: locals are initialized by probe function itself
+ o->newline() << name << " (c);";
+
+ emit_probe_epilogue (o);
+
+ o->newline(-1) << "}\n";
+ }
+}
+
+
+#ifdef PERFMON
+void no_pfm_event_error (string s)
+{
+ string msg(string("Cannot find event:" + s));
+ throw semantic_error(msg);
+}
+
+
+void no_pfm_mask_error (string s)
+{
+ string msg(string("Cannot find mask:" + s));
+ throw semantic_error(msg);
+}
+
+
+void
+split(const string& s, vector<string>& v, const string & separator)
+{
+ string::size_type last_pos = s.find_first_not_of(separator, 0);
+ string::size_type pos = s.find_first_of(separator, last_pos);
+
+ while (string::npos != pos || string::npos != last_pos) {
+ v.push_back(s.substr(last_pos, pos - last_pos));
+ last_pos = s.find_first_not_of(separator, pos);
+ pos = s.find_first_of(separator, last_pos);
+ }
+}
+
+
+void
+perfmon_derived_probe_group::emit_probes (translator_output* op, unparser* up)
+{
+ for (unsigned i=0; i < probes.size(); i++)
+ {
+ op->newline ();
+ up->emit_probe (probes[i]);
+ }
+}
+
+
+void
+perfmon_derived_probe_group::emit_module_init (translator_output* o)
+{
+ int ret;
+ pfmlib_input_param_t inp;
+ pfmlib_output_param_t outp;
+ pfarg_pmd_t pd[PFMLIB_MAX_PMDS];
+ pfarg_pmc_t pc[PFMLIB_MAX_PMCS];
+ pfarg_ctx_t ctx;
+ pfarg_load_t load_args;
+ pfmlib_options_t pfmlib_options;
+ unsigned int max_counters;
+
+ if ( probes.size() == 0)
+ return;
+ ret = pfm_initialize();
+ if (ret != PFMLIB_SUCCESS)
+ throw semantic_error("Unable to generate performance monitoring events (no libpfm)");
+
+ pfm_get_num_counters(&max_counters);
+
+ memset(&pfmlib_options, 0, sizeof(pfmlib_options));
+ pfmlib_options.pfm_debug = 0; /* set to 1 for debug */
+ pfmlib_options.pfm_verbose = 0; /* set to 1 for debug */
+ pfm_set_options(&pfmlib_options);
+
+ memset(pd, 0, sizeof(pd));
+ memset(pc, 0, sizeof(pc));
+ memset(&ctx, 0, sizeof(ctx));
+ memset(&load_args, 0, sizeof(load_args));
+
+ /*
+ * prepare parameters to library.
+ */
+ memset(&inp,0, sizeof(inp));
+ memset(&outp,0, sizeof(outp));
+
+ /* figure out the events */
+ for (unsigned i=0; i<probes.size(); ++i)
+ {
+ if (probes[i]->event == "cycles") {
+ if (pfm_get_cycle_event( &inp.pfp_events[i].event) != PFMLIB_SUCCESS)
+ no_pfm_event_error(probes[i]->event);
+ } else if (probes[i]->event == "instructions") {
+ if (pfm_get_inst_retired_event( &inp.pfp_events[i].event) !=
+ PFMLIB_SUCCESS)
+ no_pfm_event_error(probes[i]->event);
+ } else {
+ unsigned int event_id = 0;
+ unsigned int mask_id = 0;
+ vector<string> event_spec;
+ split(probes[i]->event, event_spec, ":");
+ int num = event_spec.size();
+ int masks = num - 1;
+
+ if (num == 0)
+ throw semantic_error("No events found");
+
+ /* setup event */
+ if (pfm_find_event(event_spec[0].c_str(), &event_id) != PFMLIB_SUCCESS)
+ no_pfm_event_error(event_spec[0]);
+ inp.pfp_events[i].event = event_id;
+
+ /* set up masks */
+ if (masks > PFMLIB_MAX_MASKS_PER_EVENT)
+ throw semantic_error("Too many unit masks specified");
+
+ for (int j=0; j < masks; j++) {
+ if (pfm_find_event_mask(event_id, event_spec[j+1].c_str(),
+ &mask_id) != PFMLIB_SUCCESS)
+ no_pfm_mask_error(string(event_spec[j+1]));
+ inp.pfp_events[i].unit_masks[j] = mask_id;
+ }
+ inp.pfp_events[i].num_masks = masks;
+ }
+ }
+
+ /* number of counters in use */
+ inp.pfp_event_count = probes.size();
+
+ // XXX: no elimination of duplicated counters
+ if (inp.pfp_event_count>max_counters)
+ throw semantic_error("Too many performance monitoring events.");
+
+ /* count events both in kernel and user-space */
+ inp.pfp_dfl_plm = PFM_PLM0 | PFM_PLM3;
+
+ /* XXX: some cases a perfmon register might be used of watch dog
+ this code doesn't handle that case */
+
+ /* figure out the pmcs for the events */
+ if ((ret=pfm_dispatch_events(&inp, NULL, &outp, NULL)) != PFMLIB_SUCCESS)
+ throw semantic_error("Cannot configure events");
+
+ for (unsigned i=0; i < outp.pfp_pmc_count; i++) {
+ pc[i].reg_num = outp.pfp_pmcs[i].reg_num;
+ pc[i].reg_value = outp.pfp_pmcs[i].reg_value;
+ }
+
+ /*
+ * There could be more pmc settings than pmd.
+ * Figure out the actual pmds to use.
+ */
+ for (unsigned i=0, j=0; i < inp.pfp_event_count; i++) {
+ pd[i].reg_num = outp.pfp_pmcs[j].reg_pmd_num;
+ for(; j < outp.pfp_pmc_count; j++)
+ if (outp.pfp_pmcs[j].reg_evt_idx != i) break;
+ }
+
+ // Output the be probes create function
+ o->newline() << "static int register_perfmon_probes (void) {";
+ o->newline(1) << "int rc = 0;";
+
+ o->newline() << "/* data for perfmon */";
+ o->newline() << "static int _pfm_num_pmc = " << outp.pfp_pmc_count << ";";
+ o->newline() << "static struct pfarg_pmc _pfm_pmc[" << outp.pfp_pmc_count
+ << "] = {";
+ /* output the needed bits for pmc here */
+ for (unsigned i=0; i < outp.pfp_pmc_count; i++) {
+ o->newline() << "{.reg_num=" << pc[i].reg_num << ", "
+ << ".reg_value=" << lex_cast_hex<string>(pc[i].reg_value)
+ << "},";
+ }
+
+ o->newline() << "};";
+ o->newline() << "static int _pfm_num_pmd = " << inp.pfp_event_count << ";";
+ o->newline() << "static struct pfarg_pmd _pfm_pmd[" << inp.pfp_event_count
+ << "] = {";
+ /* output the needed bits for pmd here */
+ for (unsigned i=0; i < inp.pfp_event_count; i++) {
+ o->newline() << "{.reg_num=" << pd[i].reg_num << ", "
+ << ".reg_value=" << pd[i].reg_value << "},";
+ }
+ o->newline() << "};";
+ o->newline();
+
+ o->newline() << "_pfm_pmc_x=_pfm_pmc;";
+ o->newline() << "_pfm_num_pmc_x=_pfm_num_pmc;";
+ o->newline() << "_pfm_pmd_x=_pfm_pmd;";
+ o->newline() << "_pfm_num_pmd_x=_pfm_num_pmd;";
+
+ // call all the function bodies associated with perfcounters
+ for (unsigned i=0; i < probes.size (); i++)
+ probes[i]->emit_registrations_start (o,i);
+
+ /* generate call to turn on instrumentation */
+ o->newline() << "_pfm_context.ctx_flags |= PFM_FL_SYSTEM_WIDE;";
+ o->newline() << "rc = rc || _stp_perfmon_setup(&_pfm_desc, &_pfm_context,";
+ o->newline(1) << "_pfm_pmc, _pfm_num_pmc,";
+ o->newline() << "_pfm_pmd, _pfm_num_pmd);";
+ o->newline(-1);
+
+ o->newline() << "return rc;";
+ o->newline(-1) << "}\n";
+
+ // Output the be probes destroy function
+ o->newline() << "static void unregister_perfmon_probes (void) {";
+ o->newline(1) << "_stp_perfmon_shutdown(_pfm_desc);";
+ o->newline(-1) << "}\n";
+}
+#else
+void
+perfmon_derived_probe_group::emit_probes (translator_output* op, unparser* up)
+{
+}
+
+
+void
+perfmon_derived_probe_group::emit_module_init (translator_output* o)
+{
+ string msg(string("Cannot generate perfmon probes"));
+ throw semantic_error(msg);
+}
+#endif /* PERFMON */
+
+// ------------------------------------------------------------------------
// Standard tapset registry.
// ------------------------------------------------------------------------
@@ -4961,6 +5390,7 @@ register_standard_tapsets(systemtap_session & s)
s.pattern_root->bind("timer")->bind("profile")->bind(new profile_builder());
s.pattern_root->bind_str("hrtimer")->bind(new hrtimer_builder());
s.pattern_root->bind_str("hrtimer")->bind_str("randomize")->bind(new hrtimer_builder());
+ s.pattern_root->bind("perfmon")->bind_str("counter")->bind(new perfmon_builder());
// dwarf-based kernel/module parts
dwarf_derived_probe::register_patterns(s.pattern_root);
@@ -4978,7 +5408,8 @@ derived_probe_group_container::derived_probe_group_container ():
mark_probe_group(new mark_derived_probe_group),
never_probe_group(new never_derived_probe_group),
profile_probe_group(new profile_derived_probe_group),
- timer_probe_group(new timer_derived_probe_group)
+ timer_probe_group(new timer_derived_probe_group),
+ perfmon_probe_group(new perfmon_derived_probe_group)
{
}
@@ -4992,6 +5423,7 @@ derived_probe_group_container::~derived_probe_group_container ()
delete never_probe_group;
delete profile_probe_group;
delete timer_probe_group;
+ delete perfmon_probe_group;
}
@@ -5052,6 +5484,14 @@ derived_probe_group_container::register_probe(timer_derived_probe* p)
void
+derived_probe_group_container::register_probe(perfmon_derived_probe* p)
+{
+ probes.push_back (p);
+ perfmon_probe_group->register_probe(p);
+}
+
+
+void
derived_probe_group_container::emit_probes (translator_output* op,
unparser* up)
{
@@ -5062,7 +5502,8 @@ derived_probe_group_container::emit_probes (translator_output* op,
+ mark_probe_group->size ()
+ never_probe_group->size ()
+ profile_probe_group->size ()
- + timer_probe_group->size ();
+ + timer_probe_group->size ()
+ + perfmon_probe_group->size ();
if (probes.size () != groups_size)
{
cerr << "There are " << probes.size () << " total probes, and "
@@ -5079,6 +5520,7 @@ derived_probe_group_container::emit_probes (translator_output* op,
never_probe_group->emit_probes (op, up);
profile_probe_group->emit_probes (op, up);
timer_probe_group->emit_probes (op, up);
+ perfmon_probe_group->emit_probes (op, up);
}
@@ -5093,9 +5535,11 @@ derived_probe_group_container::emit_module_init (translator_output* o)
never_probe_group->emit_module_init (o);
profile_probe_group->emit_module_init (o);
timer_probe_group->emit_module_init (o);
+ perfmon_probe_group->emit_module_init(o);
}
+#define PERFMON_ERROR_LABEL "unregister_perfmon"
#define BE_ERROR_LABEL "unregister_be"
#define DWARF_ERROR_LABEL "unregister_dwarf"
#define HRTIMER_ERROR_LABEL "unregister_hrtimer"
@@ -5109,6 +5553,19 @@ derived_probe_group_container::emit_module_init_call (translator_output* o)
int i = 0;
const char *error_label = "";
+ if (perfmon_probe_group->size () > 0)
+ {
+ o->newline() << "rc = register_perfmon_probes ();";
+ o->newline() << "if (rc)";
+ o->indent(1);
+ // We need to deregister any already probes set up - this is
+ // essential for kprobes.
+ o->newline() << "goto out;";
+ o->indent(-1);
+ i++;
+ error_label = PERFMON_ERROR_LABEL;
+ }
+
if (be_probe_group->size () > 0)
{
o->newline() << "rc = register_be_probes ();";
@@ -5258,6 +5715,12 @@ derived_probe_group_container::emit_module_init_call (translator_output* o)
o->newline(1) << "unregister_be_probes();";
i--;
}
+ if (i > 0 && perfmon_probe_group->size () > 0)
+ {
+ o->newline(-1) << PERFMON_ERROR_LABEL << ":";
+ o->newline(1) << "unregister_perfmon_probes();";
+ i--;
+ }
}
@@ -5283,4 +5746,7 @@ derived_probe_group_container::emit_module_exit (translator_output* o)
if (timer_probe_group->size () > 0)
o->newline() << "unregister_timer_probes ();";
+
+ if (perfmon_probe_group->size () > 0)
+ o->newline() << "unregister_perfmon_probes ();";
}
diff --git a/testsuite/buildok/perfmon01.stp b/testsuite/buildok/perfmon01.stp
new file mode 100755
index 00000000..00a47cc3
--- /dev/null
+++ b/testsuite/buildok/perfmon01.stp
@@ -0,0 +1,26 @@
+#! stap -p4
+
+# This exercises the perfom tapset.
+# for this to work the following needs to be set up:
+# 1) the processor on machine supported perfmon hardware
+# 2) libpfm and libpfm-devel available on machine
+# 3) systemtap translator built with "--enable-perfmon"
+
+global h1, h2
+global startt, starti
+
+probe perfmon.counter("cycles") { h1=$counter; }
+probe perfmon.counter("instructions") { h2=$counter; }
+
+probe kernel.function("sys_read"){
+ startt=read_counter(h1);
+ starti=read_counter(h2);
+}
+
+probe kernel.function("sys_read").return {
+ stopt=read_counter(h1);
+ stopi=read_counter(h2);
+
+ printf ("time = %d\n", stopt-startt);
+ printf ("instructions = %d\n", stopi-starti);
+}
diff --git a/testsuite/systemtap.pass1-4/buildok.exp b/testsuite/systemtap.pass1-4/buildok.exp
index 657f8558..9d05a9bb 100644
--- a/testsuite/systemtap.pass1-4/buildok.exp
+++ b/testsuite/systemtap.pass1-4/buildok.exp
@@ -9,6 +9,7 @@ foreach file [lsort [glob -nocomplain $srcdir/$self/*.stp]] {
buildok/seven.stp {setup_kfail 9999 *-*-*}
buildok/process_test.stp {setup_kfail 9999 *-*-*}
buildok/syscall.stp {setup_kfail 9999 *-*-*}
+ buildok/perfmon01.stp {setup_kfail 9999 *-*-*}
}
if {$rc == 0} { pass $test } else { fail $test }
}
diff --git a/translate.cxx b/translate.cxx
index da90e495..88b6ca4d 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -3790,6 +3790,9 @@ translate_pass (systemtap_session& s)
if (s.timing)
s.op->newline() << "#define STP_TIMING" << " " << s.timing ;
+ if (s.perfmon)
+ s.op->newline() << "#define STP_PERFMON";
+
s.op->newline() << "#include \"runtime.h\"";
s.op->newline() << "#include \"current.c\"";
s.op->newline() << "#include \"stack.c\"";