diff options
author | wcohen <wcohen> | 2006-09-12 22:05:48 +0000 |
---|---|---|
committer | wcohen <wcohen> | 2006-09-12 22:05:48 +0000 |
commit | 47dd066dfec8ab73f7b1886920e153402baa4597 (patch) | |
tree | 01331b69b773f6076c10837085e7eb6e39a99219 | |
parent | 7f0e10b1a9623425f4600658e0358d5c607abba6 (diff) | |
download | systemtap-steved-47dd066dfec8ab73f7b1886920e153402baa4597.tar.gz systemtap-steved-47dd066dfec8ab73f7b1886920e153402baa4597.tar.xz systemtap-steved-47dd066dfec8ab73f7b1886920e153402baa4597.zip |
Systemtap perfmon support to access the processors perfmon hardware.
-rwxr-xr-x | configure | 113 | ||||
-rw-r--r-- | configure.ac | 26 | ||||
-rw-r--r-- | elaborate.cxx | 7 | ||||
-rw-r--r-- | elaborate.h | 4 | ||||
-rw-r--r-- | main.cxx | 1 | ||||
-rw-r--r-- | runtime/perf.c | 135 | ||||
-rw-r--r-- | runtime/perf.h | 27 | ||||
-rw-r--r-- | runtime/runtime.h | 3 | ||||
-rw-r--r-- | session.h | 1 | ||||
-rw-r--r-- | stapfuncs.5.in | 7 | ||||
-rw-r--r-- | stapprobes.5.in | 31 | ||||
-rw-r--r-- | tapset/perfmon.stp | 13 | ||||
-rw-r--r-- | tapsets.cxx | 470 | ||||
-rwxr-xr-x | testsuite/buildok/perfmon01.stp | 26 | ||||
-rw-r--r-- | testsuite/systemtap.pass1-4/buildok.exp | 1 | ||||
-rw-r--r-- | translate.cxx | 3 |
16 files changed, 865 insertions, 3 deletions
@@ -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]); } @@ -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 ********************/ @@ -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\""; |