]>
Commit | Line | Data |
---|---|---|
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 |
18 | extern "C" { |
19 | #define __STDC_FORMAT_MACROS | |
20 | #include <inttypes.h> | |
21 | } | |
22 | ||
01c2eefe JS |
23 | using namespace std; |
24 | using namespace __gnu_cxx; | |
25 | ||
26 | ||
83ea76b1 | 27 | static const string TOK_PERF("perf"); |
95ef3b30 FCE |
28 | static const string TOK_TYPE("type"); |
29 | static const string TOK_CONFIG("config"); | |
a3cf75e6 | 30 | static const string TOK_SAMPLE("sample"); |
07c818a0 | 31 | static const string TOK_HZ("hz"); |
6a8fe809 | 32 | static const string TOK_PROCESS("process"); |
3689db05 | 33 | static 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 |
42 | struct 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 | ||
58 | struct 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 | ||
70 | perf_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 | ||
98 | void | |
99 | perf_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 |
111 | void |
112 | perf_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 | ||
258 | void | |
259 | perf_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 | ||
268 | void | |
269 | perf_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 | ||
278 | struct 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 | ||
291 | void | |
292 | perf_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 |
391 | void |
392 | register_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 | 409 | bool |
fcdd71ba WC |
410 | sort_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 : */ |