]> sourceware.org Git - systemtap.git/blame - tapset-perfmon.cxx
PR23285 (1): enable procfs probes for stapbpf
[systemtap.git] / tapset-perfmon.cxx
CommitLineData
01c2eefe 1// tapset for HW performance monitoring
ef36f781 2// Copyright (C) 2005-2014 Red Hat Inc.
01c2eefe 3// Copyright (C) 2005-2007 Intel Corporation.
01c2eefe
JS
4//
5// This file is part of systemtap, and is free software. You can
6// redistribute it and/or modify it under the terms of the GNU General
7// Public License (GPL); either version 2, or (at your option) any
8// later version.
9
10#include "session.h"
11#include "tapsets.h"
6a8fe809 12#include "task_finder.h"
83ea76b1 13#include "translate.h"
01c2eefe
JS
14#include "util.h"
15
16#include <string>
17
b29e794d
LB
18extern "C" {
19#define __STDC_FORMAT_MACROS
20#include <inttypes.h>
21}
22
01c2eefe
JS
23using namespace std;
24using namespace __gnu_cxx;
25
26
83ea76b1 27static const string TOK_PERF("perf");
95ef3b30
FCE
28static const string TOK_TYPE("type");
29static const string TOK_CONFIG("config");
a3cf75e6 30static const string TOK_SAMPLE("sample");
07c818a0 31static const string TOK_HZ("hz");
6a8fe809 32static const string TOK_PROCESS("process");
3689db05 33static const string TOK_COUNTER("counter");
83ea76b1 34
01c2eefe
JS
35
36// ------------------------------------------------------------------------
83ea76b1 37// perf event derived probes
01c2eefe
JS
38// ------------------------------------------------------------------------
39// This is a new interface to the perfmon hw.
40//
41
83ea76b1
WC
42struct perf_derived_probe: public derived_probe
43{
95ef3b30
FCE
44 int64_t event_type;
45 int64_t event_config;
83ea76b1 46 int64_t interval;
6a8fe809 47 bool has_process;
3689db05 48 bool has_counter;
07c818a0 49 bool has_freq;
6a8fe809 50 string process_name;
3689db05
SC
51 string counter;
52 perf_derived_probe (probe* p, probe_point* l, int64_t type, int64_t config,
07c818a0 53 int64_t i, bool pp, bool cp, bool freq, string pn, string cv);
83ea76b1
WC
54 virtual void join_group (systemtap_session& s);
55};
56
57
58struct perf_derived_probe_group: public generic_dpg<perf_derived_probe>
59{
fcdd71ba
WC
60 friend bool sort_for_bpf(systemtap_session& s
61 ,perf_derived_probe_group *pg,
23aac614
AM
62 sort_for_bpf_probe_arg_vector &v);
63
83ea76b1
WC
64 void emit_module_decls (systemtap_session& s);
65 void emit_module_init (systemtap_session& s);
66 void emit_module_exit (systemtap_session& s);
67};
68
69
70perf_derived_probe::perf_derived_probe (probe* p, probe_point* l,
95ef3b30
FCE
71 int64_t type,
72 int64_t config,
6a8fe809
SC
73 int64_t i,
74 bool process_p,
3689db05 75 bool counter_p,
07c818a0 76 bool freq,
3689db05
SC
77 string process_n,
78 string counter):
6a8fe809 79
4c5d1300 80 derived_probe (p, l, true /* .components soon rewritten */),
6a8fe809 81 event_type (type), event_config (config), interval (i),
07c818a0
FL
82 has_process (process_p), has_counter (counter_p), has_freq(freq),
83 process_name (process_n), counter (counter)
83ea76b1 84{
a3cf75e6
JS
85 vector<probe_point::component*>& comps = this->sole_location()->components;
86 comps.clear();
87 comps.push_back (new probe_point::component (TOK_PERF));
95ef3b30
FCE
88 comps.push_back (new probe_point::component (TOK_TYPE, new literal_number(type)));
89 comps.push_back (new probe_point::component (TOK_CONFIG, new literal_number (config)));
a3cf75e6 90 comps.push_back (new probe_point::component (TOK_SAMPLE, new literal_number (interval)));
8a1bdec5
JS
91 if (has_process)
92 comps.push_back (new probe_point::component (TOK_PROCESS, new literal_string (process_name)));
93 if (has_counter)
94 comps.push_back (new probe_point::component (TOK_COUNTER, new literal_string (counter)));
83ea76b1
WC
95}
96
97
98void
99perf_derived_probe::join_group (systemtap_session& s)
100{
101 if (! s.perf_derived_probes)
102 s.perf_derived_probes = new perf_derived_probe_group ();
103 s.perf_derived_probes->enroll (this);
ca6d3b0f 104 this->group = s.perf_derived_probes;
6a8fe809 105
3689db05
SC
106 if (has_process && !has_counter)
107 enable_task_finder(s);
83ea76b1
WC
108}
109
110
83ea76b1
WC
111void
112perf_derived_probe_group::emit_module_decls (systemtap_session& s)
113{
3689db05
SC
114 bool have_a_process_tag = false;
115
116 for (unsigned i=0; i < probes.size(); i++)
117 if (probes[i]->has_process && !probes[i]->has_counter)
118 {
119 have_a_process_tag = true;
120 break;
121 }
122
83ea76b1
WC
123 if (probes.empty()) return;
124
125 s.op->newline() << "/* ---- perf probes ---- */";
6a8fe809
SC
126 s.op->newline() << "#include <linux/perf_event.h>";
127 s.op->newline() << "#include \"linux/perf.h\"";
85955320
JS
128 s.op->newline();
129
83ea76b1 130 /* declarations */
06d2888c 131 s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs);";
85955320 132 for (unsigned i=0; i < probes.size(); i++)
2f3df331
MW
133 {
134 s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI";
135 s.op->newline() << "static void enter_perf_probe_" << i
136 << " (struct perf_event *e, int nmi, "
137 << "struct perf_sample_data *data, "
138 << "struct pt_regs *regs);";
139 s.op->newline() << "#else";
140 s.op->newline() << "static void enter_perf_probe_" << i
141 << " (struct perf_event *e, "
142 << "struct perf_sample_data *data, "
143 << "struct pt_regs *regs);";
144 s.op->newline() << "#endif";
145 }
85955320
JS
146 s.op->newline();
147
3689db05
SC
148 // Output task finder callback routine
149 if (have_a_process_tag)
150 {
151 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) {";
152 s.op->indent(1);
153 s.op->newline() << "int rc = 0;";
4fa83377 154 s.op->newline() << "struct stap_perf_probe *p = container_of(tgt, struct stap_perf_probe, e.t.tgt);";
3689db05
SC
155
156 s.op->newline() << "if (register_p) ";
157 s.op->indent(1);
158
159 s.op->newline() << "rc = _stp_perf_init(p, tsk);";
160 s.op->newline(-1) << "else";
161 s.op->newline(1) << "_stp_perf_del(p);";
162 s.op->newline(-1) << "return rc;";
163 s.op->newline(-1) << "}";
164 }
6a8fe809 165
85955320 166 /* data structures */
34fe8ec4
JS
167 s.op->newline() << "static struct stap_perf_probe stap_perf_probes ["
168 << probes.size() << "] = {";
85955320 169 s.op->indent(1);
83ea76b1
WC
170 for (unsigned i=0; i < probes.size(); i++)
171 {
85955320
JS
172 s.op->newline() << "{";
173 s.op->newline(1) << ".attr={ "
95ef3b30 174 << ".type=" << probes[i]->event_type << "ULL, "
07c818a0
FL
175 << ".config=" << probes[i]->event_config << "ULL, ";
176 if (probes[i]->has_freq)
177 {
178 s.op->line() << "{ .sample_freq=" << probes[i]->interval << "ULL }, ";
179 s.op->line() << ".freq=1, ";
180 }
181 else
182 {
183 s.op->line() << "{ .sample_period=" << probes[i]->interval << "ULL }, ";
184 }
185 s.op->line() << "},";
34fe8ec4 186 s.op->newline() << ".callback=enter_perf_probe_" << i << ", ";
faea5e16 187 s.op->newline() << ".probe=" << common_probe_init (probes[i]) << ", ";
6a8fe809 188
3689db05 189 if (probes[i]->has_process && !probes[i]->has_counter)
6a8fe809 190 {
4fa83377
SC
191 s.op->line() << " .e={";
192 s.op->line() << " .t={";
6a8fe809 193 s.op->line() << " .tgt={";
3689db05 194 s.op->line() << " .purpose=\"perfctr\",";
48cd804a 195 s.op->line() << " .procname=\"" << probes[i]->process_name << "\",";
6a8fe809
SC
196 s.op->line() << " .pid=0,";
197 s.op->line() << " .callback=&_stp_perf_probe_cb,";
198 s.op->line() << " },";
4fa83377
SC
199 s.op->line() << " },";
200 s.op->line() << " },";
ad6c8f90 201 s.op->newline() << ".task_finder=" << "1, ";
6a8fe809 202 }
3689db05 203 else if (probes[i]->has_counter)
ad6c8f90
JS
204 {
205 // process counters are currently task-found by uprobes
206 // set neither .system_wide nor .task_finder
207 }
6a8fe809 208 else
ad6c8f90 209 s.op->newline() << ".system_wide=" << "1, ";
85955320 210 s.op->newline(-1) << "},";
83ea76b1 211 }
85955320 212 s.op->newline(-1) << "};";
83ea76b1
WC
213 s.op->newline();
214
215 /* wrapper functions */
85955320
JS
216 for (unsigned i=0; i < probes.size(); i++)
217 {
2f3df331 218 s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI";
85955320
JS
219 s.op->newline() << "static void enter_perf_probe_" << i
220 << " (struct perf_event *e, int nmi, "
221 << "struct perf_sample_data *data, "
222 << "struct pt_regs *regs)";
2f3df331
MW
223 s.op->newline() << "#else";
224 s.op->newline() << "static void enter_perf_probe_" << i
225 << " (struct perf_event *e, "
226 << "struct perf_sample_data *data, "
227 << "struct pt_regs *regs)";
228 s.op->newline() << "#endif";
85955320 229 s.op->newline() << "{";
06d2888c 230 s.op->newline(1) << "handle_perf_probe(" << i << ", regs);";
85955320
JS
231 s.op->newline(-1) << "}";
232 }
233 s.op->newline();
234
06d2888c 235 s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs)";
83ea76b1 236 s.op->newline() << "{";
06d2888c 237 s.op->newline(1) << "struct stap_perf_probe* stp = & stap_perf_probes [i];";
71db462b 238 common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "stp->probe",
cda141c2 239 "stp_probe_type_perf");
d9aed31e 240 s.op->newline() << "if (user_mode(regs)) {";
e04b5d74 241 s.op->newline(1)<< "c->user_mode_p = 1;";
d9aed31e
MW
242 s.op->newline() << "c->uregs = regs;";
243 s.op->newline(-1) << "} else {";
244 s.op->newline(1) << "c->kregs = regs;";
245 s.op->newline(-1) << "}";
d3f9f732 246
d9aed31e 247 s.op->newline() << "(*stp->probe->ph) (c);";
ef1337ee 248 common_probe_entryfn_epilogue (s, true, otf_safe_context(s));
83ea76b1 249 s.op->newline(-1) << "}";
6a8fe809 250 s.op->newline();
ad6c8f90
JS
251 if (have_a_process_tag)
252 s.op->newline() << "#define STP_PERF_USE_TASK_FINDER 1";
6a8fe809
SC
253 s.op->newline() << "#include \"linux/perf.c\"";
254 s.op->newline();
83ea76b1
WC
255}
256
257
258void
259perf_derived_probe_group::emit_module_init (systemtap_session& s)
260{
261 if (probes.empty()) return;
262
ad6c8f90
JS
263 s.op->newline() << "rc = _stp_perf_init_n (stap_perf_probes, "
264 << probes.size() << ", &probe_point);";
83ea76b1
WC
265}
266
267
268void
269perf_derived_probe_group::emit_module_exit (systemtap_session& s)
270{
271 if (probes.empty()) return;
272
ad6c8f90
JS
273 s.op->newline() << "_stp_perf_del_n (stap_perf_probes, "
274 << probes.size() << ");";
83ea76b1
WC
275}
276
277
278struct perf_builder: public derived_probe_builder
279{
280 virtual void build(systemtap_session & sess,
281 probe * base, probe_point * location,
282 literal_map_t const & parameters,
283 vector<derived_probe *> & finished_results);
284
285 static void register_patterns(systemtap_session& s);
352c84fe
FL
286
287 virtual string name() { return "perf builder"; }
83ea76b1
WC
288};
289
290
291void
292perf_builder::build(systemtap_session & sess,
3689db05
SC
293 probe * base,
294 probe_point * location,
295 literal_map_t const & parameters,
296 vector<derived_probe *> & finished_results)
83ea76b1 297{
7acbe856
FCE
298 // XXX need additional version checks too?
299 // --- perhaps look for export of perf_event_create_kernel_counter
70e6d6c9 300 if (sess.kernel_exports.find("perf_event_create_kernel_counter") == sess.kernel_exports.end())
dc09353a 301 throw SEMANTIC_ERROR (_("perf probes not available without exported perf_event_create_kernel_counter"));
7acbe856 302 if (sess.kernel_config["CONFIG_PERF_EVENTS"] != "y")
dc09353a 303 throw SEMANTIC_ERROR (_("perf probes not available without CONFIG_PERF_EVENTS"));
7acbe856 304
95ef3b30
FCE
305 int64_t type;
306 bool has_type = get_param(parameters, TOK_TYPE, type);
307 assert(has_type);
308
309 int64_t config;
310 bool has_config = get_param(parameters, TOK_CONFIG, config);
311 assert(has_config);
a3cf75e6 312
83ea76b1 313 int64_t period;
a3cf75e6
JS
314 bool has_period = get_param(parameters, TOK_SAMPLE, period);
315 if (!has_period)
95ef3b30 316 period = 1000000; // XXX: better parametrize this default
1f1b6bb1 317 else if (period < 1)
dc09353a 318 throw SEMANTIC_ERROR(_("invalid perf sample period ") + lex_cast(period),
1f1b6bb1 319 parameters.find(TOK_SAMPLE)->second->tok);
3689db05 320
07c818a0
FL
321 int64_t freq;
322 bool has_freq = get_param(parameters, TOK_HZ, freq);
323
45a63356 324 interned_string var;
3689db05
SC
325 bool has_counter = get_param(parameters, TOK_COUNTER, var);
326 if (var.find_first_of("*?[") != string::npos)
dc09353a 327 throw SEMANTIC_ERROR(_("wildcard not allowed with perf probe counter component"));
3689db05
SC
328 if (has_counter)
329 {
7c3feb93 330 if (var.empty())
dc09353a 331 throw SEMANTIC_ERROR(_("missing perf probe counter component name"));
7c3feb93 332
3689db05 333 period = 0; // perf_event_attr.sample_freq should be 0
aa2c662f
SC
334 vector<std::pair<string,string> >:: iterator it;
335 for (it=sess.perf_counters.begin() ;
336 it != sess.perf_counters.end(); it++)
337 if ((*it).first == var)
338 break;
339 if (it != sess.perf_counters.end())
7c3feb93
JS
340 throw SEMANTIC_ERROR(_("duplicate counter name"));
341
342 // Splice a 'next' into the probe body, and then elaborate.cxx's
343 // dead_stmtexpr_remover() will warn if anything of substance follows.
344 statement* n = new next_statement ();
345 n->tok = base->tok;
346 base->body = new block (n, base->body);
3689db05 347 }
1f1b6bb1 348
48cd804a 349 bool proc_p;
45a63356 350 interned_string proc_n;
48cd804a
JS
351 if ((proc_p = has_null_param(parameters, TOK_PROCESS)))
352 {
fc18e6c4
JL
353 try
354 {
355 proc_n = sess.cmd_file();
356 }
357 catch (semantic_error& e)
358 {
359 throw SEMANTIC_ERROR(_("invalid -c command for unspecified process"
360 " probe [man stapprobes]"), NULL, NULL, &e);
361 }
48cd804a 362 if (proc_n.empty())
fc18e6c4
JL
363 throw SEMANTIC_ERROR(_("unspecified process probe is invalid without a "
364 "-c COMMAND [man stapprobes]"));
48cd804a
JS
365 }
366 else
367 proc_p = get_param(parameters, TOK_PROCESS, proc_n);
368 if (proc_p && !proc_n.empty())
369 proc_n = find_executable (proc_n, sess.sysroot, sess.sysenv);
370
1f1b6bb1 371 if (sess.verbose > 1)
07c818a0
FL
372 clog << _F("perf probe type=%" PRId64 " config=%" PRId64 " %s=%" PRId64 " process=%s counter=%s",
373 type, config, has_freq ? "freq" : "period", has_freq ? freq : period,
374 proc_n.to_string().c_str(), var.to_string().c_str()) << endl;
a3cf75e6 375
24ef175c
JL
376 // The user-provided pp is already well-formed. Let's add a copy on the chain
377 // and set it as the new base
378 probe_point *new_location = new probe_point(*location);
379 new_location->well_formed = true;
380 probe *new_base = new probe(base, new_location);
381
83ea76b1 382 finished_results.push_back
07c818a0
FL
383 (new perf_derived_probe(new_base, location, type, config,
384 has_freq ? freq : period, proc_p,
385 has_counter, has_freq, proc_n, var));
48cd804a 386 if (!var.empty())
aa2c662f 387 sess.perf_counters.push_back(make_pair (var, proc_n));
1f1b6bb1
JS
388}
389
390
83ea76b1
WC
391void
392register_tapset_perf(systemtap_session& s)
393{
95ef3b30
FCE
394 // NB: at this point, the binding is *not* unprivileged.
395
83ea76b1 396 derived_probe_builder *builder = new perf_builder();
a3cf75e6 397 match_node* perf = s.pattern_root->bind(TOK_PERF);
83ea76b1 398
95ef3b30 399 match_node* event = perf->bind_num(TOK_TYPE)->bind_num(TOK_CONFIG);
a3cf75e6
JS
400 event->bind(builder);
401 event->bind_num(TOK_SAMPLE)->bind(builder);
07c818a0 402 event->bind_num(TOK_HZ)->bind(builder);
6a8fe809
SC
403 event->bind_str(TOK_PROCESS)->bind(builder);
404 event->bind(TOK_PROCESS)->bind(builder);
3689db05
SC
405 event->bind_str(TOK_COUNTER)->bind(builder);
406 event->bind_str(TOK_PROCESS)->bind_str(TOK_COUNTER)->bind(builder);
83ea76b1
WC
407}
408
23aac614 409bool
fcdd71ba
WC
410sort_for_bpf(systemtap_session& s __attribute__ ((unused)),
411 perf_derived_probe_group *pg, sort_for_bpf_probe_arg_vector &v)
23aac614
AM
412{
413 if (!pg)
414 return false;
415
416 for (auto i = pg->probes.begin(); i != pg->probes.end(); ++i)
417 {
418 perf_derived_probe *p = *i;
419 std::stringstream o;
420
421 o << "perf/" << p->event_type << "/" << p->event_config << "/";
422 if (p->has_freq)
423 o << "f/" << p->interval; // uses sample_freq (hz)
424 else
425 o << "p/" << p->interval; // uses sample_period
426
427 // XXX .process, .counter
428 v.push_back(std::pair<derived_probe *, std::string>(p, o.str()));
429 }
430
431 return v.empty();
432}
433
01c2eefe 434/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.238975 seconds and 5 git commands to generate.