// tapset for HW performance monitoring // Copyright (C) 2005-2010 Red Hat Inc. // Copyright (C) 2005-2007 Intel Corporation. // Copyright (C) 2008 James.Bottomley@HansenPartnership.com // // 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. #include "session.h" #include "tapsets.h" #include "translate.h" #include "util.h" #include using namespace std; using namespace __gnu_cxx; static const string TOK_PERF("perf"); static const string TOK_TYPE("type"); static const string TOK_CONFIG("config"); static const string TOK_SAMPLE("sample"); // ------------------------------------------------------------------------ // perf event derived probes // ------------------------------------------------------------------------ // This is a new interface to the perfmon hw. // struct perf_derived_probe: public derived_probe { int64_t event_type; int64_t event_config; int64_t interval; perf_derived_probe (probe* p, probe_point* l, int64_t type, int64_t config, int64_t i); virtual void join_group (systemtap_session& s); }; struct perf_derived_probe_group: public generic_dpg { void emit_module_decls (systemtap_session& s); void emit_module_init (systemtap_session& s); void emit_module_exit (systemtap_session& s); }; perf_derived_probe::perf_derived_probe (probe* p, probe_point* l, int64_t type, int64_t config, int64_t i): derived_probe (p, new probe_point(*l) /* .components soon rewritten */), event_type (type), event_config (config), interval (i) { vector& comps = this->sole_location()->components; comps.clear(); comps.push_back (new probe_point::component (TOK_PERF)); comps.push_back (new probe_point::component (TOK_TYPE, new literal_number(type))); 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))); } void perf_derived_probe::join_group (systemtap_session& s) { if (! s.perf_derived_probes) s.perf_derived_probes = new perf_derived_probe_group (); s.perf_derived_probes->enroll (this); } void perf_derived_probe_group::emit_module_decls (systemtap_session& s) { if (probes.empty()) return; s.op->newline() << "/* ---- perf probes ---- */"; s.op->newline() << "#include \"perf.c\""; s.op->newline(); /* declarations */ s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs);"; for (unsigned i=0; i < probes.size(); i++) s.op->newline() << "static void enter_perf_probe_" << i << " (struct perf_event *e, int nmi, " << "struct perf_sample_data *data, " << "struct pt_regs *regs);"; s.op->newline(); /* data structures */ s.op->newline() << "static struct stap_perf_probe stap_perf_probes [" << probes.size() << "] = {"; s.op->indent(1); for (unsigned i=0; i < probes.size(); i++) { s.op->newline() << "{"; s.op->newline(1) << ".attr={ " << ".type=" << probes[i]->event_type << "ULL, " << ".config=" << probes[i]->event_config << "ULL, " << "{ .sample_period=" << probes[i]->interval << "ULL }},"; s.op->newline() << ".callback=enter_perf_probe_" << i << ", "; s.op->newline() << ".pp=" << lex_cast_qstring (*probes[i]->sole_location()) << ","; s.op->newline() << ".ph=" << probes[i]->name << ","; s.op->newline(-1) << "},"; } s.op->newline(-1) << "};"; s.op->newline(); /* wrapper functions */ for (unsigned i=0; i < probes.size(); i++) { s.op->newline() << "static void enter_perf_probe_" << i << " (struct perf_event *e, int nmi, " << "struct perf_sample_data *data, " << "struct pt_regs *regs)"; s.op->newline() << "{"; s.op->newline(1) << "handle_perf_probe(" << i << ", regs);"; s.op->newline(-1) << "}"; } s.op->newline(); s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs)"; s.op->newline() << "{"; s.op->newline(1) << "struct stap_perf_probe* stp = & stap_perf_probes [i];"; common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "stp->pp"); s.op->newline() << "c->regs = regs;"; s.op->newline() << "(*stp->ph) (c);"; common_probe_entryfn_epilogue (s.op); s.op->newline(-1) << "}"; } void perf_derived_probe_group::emit_module_init (systemtap_session& s) { if (probes.empty()) return; s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {"; s.op->newline(1) << "struct stap_perf_probe* stp = & stap_perf_probes [i];"; s.op->newline() << "rc = _stp_perf_init(stp);"; s.op->newline() << "if (rc) {"; s.op->newline(1) << "probe_point = stp->pp;"; s.op->newline() << "for (j=0; jnewline(1) << "_stp_perf_del(& stap_perf_probes [j]);"; s.op->newline(-1) << "}"; // for unwind loop s.op->newline(-1) << "}"; // if-error s.op->newline() << "break;"; s.op->newline(-1) << "}"; // for loop } void perf_derived_probe_group::emit_module_exit (systemtap_session& s) { if (probes.empty()) return; s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {"; s.op->newline(1) << "_stp_perf_del(& stap_perf_probes [i]);"; s.op->newline(-1) << "}"; // for loop } struct perf_builder: public derived_probe_builder { virtual void build(systemtap_session & sess, probe * base, probe_point * location, literal_map_t const & parameters, vector & finished_results); static void register_patterns(systemtap_session& s); }; void perf_builder::build(systemtap_session & sess, probe * base, probe_point * location, literal_map_t const & parameters, vector & finished_results) { // XXX need additional version checks too? // --- perhaps look for export of perf_event_create_kernel_counter if (sess.kernel_exports.find("perf_event_create_kernel_counter") == sess.kernel_exports.end()) throw semantic_error ("perf probes not available without exported perf_event_create_kernel_counter"); if (sess.kernel_config["CONFIG_PERF_EVENTS"] != "y") throw semantic_error ("perf probes not available without CONFIG_PERF_EVENTS"); int64_t type; bool has_type = get_param(parameters, TOK_TYPE, type); assert(has_type); int64_t config; bool has_config = get_param(parameters, TOK_CONFIG, config); assert(has_config); int64_t period; bool has_period = get_param(parameters, TOK_SAMPLE, period); if (!has_period) period = 1000000; // XXX: better parametrize this default else if (period < 1) throw semantic_error("invalid perf sample period " + lex_cast(period), parameters.find(TOK_SAMPLE)->second->tok); if (sess.verbose > 1) clog << "perf probe type=" << type << " config=" << config << " period=" << period << endl; finished_results.push_back (new perf_derived_probe(base, location, type, config, period)); } void register_tapset_perf(systemtap_session& s) { // NB: at this point, the binding is *not* unprivileged. derived_probe_builder *builder = new perf_builder(); match_node* perf = s.pattern_root->bind(TOK_PERF); match_node* event = perf->bind_num(TOK_TYPE)->bind_num(TOK_CONFIG); event->bind(builder); event->bind_num(TOK_SAMPLE)->bind(builder); } /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */