]>
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"); |
6a8fe809 | 31 | static const string TOK_PROCESS("process"); |
3689db05 | 32 | static const string TOK_COUNTER("counter"); |
83ea76b1 | 33 | |
01c2eefe JS |
34 | |
35 | // ------------------------------------------------------------------------ | |
83ea76b1 | 36 | // perf event derived probes |
01c2eefe JS |
37 | // ------------------------------------------------------------------------ |
38 | // This is a new interface to the perfmon hw. | |
39 | // | |
40 | ||
83ea76b1 WC |
41 | struct perf_derived_probe: public derived_probe |
42 | { | |
95ef3b30 FCE |
43 | int64_t event_type; |
44 | int64_t event_config; | |
83ea76b1 | 45 | int64_t interval; |
6a8fe809 | 46 | bool has_process; |
3689db05 | 47 | bool has_counter; |
6a8fe809 | 48 | string process_name; |
3689db05 SC |
49 | string counter; |
50 | perf_derived_probe (probe* p, probe_point* l, int64_t type, int64_t config, | |
51 | int64_t i, bool pp, bool cp, string pn, string cv); | |
83ea76b1 WC |
52 | virtual void join_group (systemtap_session& s); |
53 | }; | |
54 | ||
55 | ||
56 | struct perf_derived_probe_group: public generic_dpg<perf_derived_probe> | |
57 | { | |
83ea76b1 WC |
58 | void emit_module_decls (systemtap_session& s); |
59 | void emit_module_init (systemtap_session& s); | |
60 | void emit_module_exit (systemtap_session& s); | |
61 | }; | |
62 | ||
63 | ||
64 | perf_derived_probe::perf_derived_probe (probe* p, probe_point* l, | |
95ef3b30 FCE |
65 | int64_t type, |
66 | int64_t config, | |
6a8fe809 SC |
67 | int64_t i, |
68 | bool process_p, | |
3689db05 SC |
69 | bool counter_p, |
70 | string process_n, | |
71 | string counter): | |
6a8fe809 | 72 | |
4c5d1300 | 73 | derived_probe (p, l, true /* .components soon rewritten */), |
6a8fe809 | 74 | event_type (type), event_config (config), interval (i), |
3689db05 SC |
75 | has_process (process_p), has_counter (counter_p), process_name (process_n), |
76 | counter (counter) | |
83ea76b1 | 77 | { |
a3cf75e6 JS |
78 | vector<probe_point::component*>& comps = this->sole_location()->components; |
79 | comps.clear(); | |
80 | comps.push_back (new probe_point::component (TOK_PERF)); | |
95ef3b30 FCE |
81 | comps.push_back (new probe_point::component (TOK_TYPE, new literal_number(type))); |
82 | comps.push_back (new probe_point::component (TOK_CONFIG, new literal_number (config))); | |
a3cf75e6 | 83 | comps.push_back (new probe_point::component (TOK_SAMPLE, new literal_number (interval))); |
8a1bdec5 JS |
84 | if (has_process) |
85 | comps.push_back (new probe_point::component (TOK_PROCESS, new literal_string (process_name))); | |
86 | if (has_counter) | |
87 | comps.push_back (new probe_point::component (TOK_COUNTER, new literal_string (counter))); | |
83ea76b1 WC |
88 | } |
89 | ||
90 | ||
91 | void | |
92 | perf_derived_probe::join_group (systemtap_session& s) | |
93 | { | |
94 | if (! s.perf_derived_probes) | |
95 | s.perf_derived_probes = new perf_derived_probe_group (); | |
96 | s.perf_derived_probes->enroll (this); | |
6a8fe809 | 97 | |
3689db05 SC |
98 | if (has_process && !has_counter) |
99 | enable_task_finder(s); | |
83ea76b1 WC |
100 | } |
101 | ||
102 | ||
83ea76b1 WC |
103 | void |
104 | perf_derived_probe_group::emit_module_decls (systemtap_session& s) | |
105 | { | |
3689db05 SC |
106 | bool have_a_process_tag = false; |
107 | ||
108 | for (unsigned i=0; i < probes.size(); i++) | |
109 | if (probes[i]->has_process && !probes[i]->has_counter) | |
110 | { | |
111 | have_a_process_tag = true; | |
112 | break; | |
113 | } | |
114 | ||
83ea76b1 WC |
115 | if (probes.empty()) return; |
116 | ||
117 | s.op->newline() << "/* ---- perf probes ---- */"; | |
6a8fe809 SC |
118 | s.op->newline() << "#include <linux/perf_event.h>"; |
119 | s.op->newline() << "#include \"linux/perf.h\""; | |
85955320 JS |
120 | s.op->newline(); |
121 | ||
83ea76b1 | 122 | /* declarations */ |
06d2888c | 123 | s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs);"; |
85955320 | 124 | for (unsigned i=0; i < probes.size(); i++) |
2f3df331 MW |
125 | { |
126 | s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI"; | |
127 | s.op->newline() << "static void enter_perf_probe_" << i | |
128 | << " (struct perf_event *e, int nmi, " | |
129 | << "struct perf_sample_data *data, " | |
130 | << "struct pt_regs *regs);"; | |
131 | s.op->newline() << "#else"; | |
132 | s.op->newline() << "static void enter_perf_probe_" << i | |
133 | << " (struct perf_event *e, " | |
134 | << "struct perf_sample_data *data, " | |
135 | << "struct pt_regs *regs);"; | |
136 | s.op->newline() << "#endif"; | |
137 | } | |
85955320 JS |
138 | s.op->newline(); |
139 | ||
3689db05 SC |
140 | // Output task finder callback routine |
141 | if (have_a_process_tag) | |
142 | { | |
143 | 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) {"; | |
144 | s.op->indent(1); | |
145 | s.op->newline() << "int rc = 0;"; | |
4fa83377 | 146 | s.op->newline() << "struct stap_perf_probe *p = container_of(tgt, struct stap_perf_probe, e.t.tgt);"; |
3689db05 SC |
147 | |
148 | s.op->newline() << "if (register_p) "; | |
149 | s.op->indent(1); | |
150 | ||
151 | s.op->newline() << "rc = _stp_perf_init(p, tsk);"; | |
152 | s.op->newline(-1) << "else"; | |
153 | s.op->newline(1) << "_stp_perf_del(p);"; | |
154 | s.op->newline(-1) << "return rc;"; | |
155 | s.op->newline(-1) << "}"; | |
156 | } | |
6a8fe809 | 157 | |
85955320 | 158 | /* data structures */ |
34fe8ec4 JS |
159 | s.op->newline() << "static struct stap_perf_probe stap_perf_probes [" |
160 | << probes.size() << "] = {"; | |
85955320 | 161 | s.op->indent(1); |
83ea76b1 WC |
162 | for (unsigned i=0; i < probes.size(); i++) |
163 | { | |
85955320 JS |
164 | s.op->newline() << "{"; |
165 | s.op->newline(1) << ".attr={ " | |
95ef3b30 FCE |
166 | << ".type=" << probes[i]->event_type << "ULL, " |
167 | << ".config=" << probes[i]->event_config << "ULL, " | |
85955320 | 168 | << "{ .sample_period=" << probes[i]->interval << "ULL }},"; |
34fe8ec4 | 169 | s.op->newline() << ".callback=enter_perf_probe_" << i << ", "; |
faea5e16 | 170 | s.op->newline() << ".probe=" << common_probe_init (probes[i]) << ", "; |
6a8fe809 | 171 | |
3689db05 | 172 | if (probes[i]->has_process && !probes[i]->has_counter) |
6a8fe809 | 173 | { |
4fa83377 SC |
174 | s.op->line() << " .e={"; |
175 | s.op->line() << " .t={"; | |
6a8fe809 | 176 | s.op->line() << " .tgt={"; |
3689db05 | 177 | s.op->line() << " .purpose=\"perfctr\","; |
48cd804a | 178 | s.op->line() << " .procname=\"" << probes[i]->process_name << "\","; |
6a8fe809 SC |
179 | s.op->line() << " .pid=0,"; |
180 | s.op->line() << " .callback=&_stp_perf_probe_cb,"; | |
181 | s.op->line() << " },"; | |
4fa83377 SC |
182 | s.op->line() << " },"; |
183 | s.op->line() << " },"; | |
ad6c8f90 | 184 | s.op->newline() << ".task_finder=" << "1, "; |
6a8fe809 | 185 | } |
3689db05 | 186 | else if (probes[i]->has_counter) |
ad6c8f90 JS |
187 | { |
188 | // process counters are currently task-found by uprobes | |
189 | // set neither .system_wide nor .task_finder | |
190 | } | |
6a8fe809 | 191 | else |
ad6c8f90 | 192 | s.op->newline() << ".system_wide=" << "1, "; |
85955320 | 193 | s.op->newline(-1) << "},"; |
83ea76b1 | 194 | } |
85955320 | 195 | s.op->newline(-1) << "};"; |
83ea76b1 WC |
196 | s.op->newline(); |
197 | ||
198 | /* wrapper functions */ | |
85955320 JS |
199 | for (unsigned i=0; i < probes.size(); i++) |
200 | { | |
2f3df331 | 201 | s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI"; |
85955320 JS |
202 | s.op->newline() << "static void enter_perf_probe_" << i |
203 | << " (struct perf_event *e, int nmi, " | |
204 | << "struct perf_sample_data *data, " | |
205 | << "struct pt_regs *regs)"; | |
2f3df331 MW |
206 | s.op->newline() << "#else"; |
207 | s.op->newline() << "static void enter_perf_probe_" << i | |
208 | << " (struct perf_event *e, " | |
209 | << "struct perf_sample_data *data, " | |
210 | << "struct pt_regs *regs)"; | |
211 | s.op->newline() << "#endif"; | |
85955320 | 212 | s.op->newline() << "{"; |
06d2888c | 213 | s.op->newline(1) << "handle_perf_probe(" << i << ", regs);"; |
85955320 JS |
214 | s.op->newline(-1) << "}"; |
215 | } | |
216 | s.op->newline(); | |
217 | ||
06d2888c | 218 | s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs)"; |
83ea76b1 | 219 | s.op->newline() << "{"; |
06d2888c | 220 | s.op->newline(1) << "struct stap_perf_probe* stp = & stap_perf_probes [i];"; |
71db462b | 221 | common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "stp->probe", |
cda141c2 | 222 | "stp_probe_type_perf"); |
d9aed31e | 223 | s.op->newline() << "if (user_mode(regs)) {"; |
e04b5d74 | 224 | s.op->newline(1)<< "c->user_mode_p = 1;"; |
d9aed31e MW |
225 | s.op->newline() << "c->uregs = regs;"; |
226 | s.op->newline(-1) << "} else {"; | |
227 | s.op->newline(1) << "c->kregs = regs;"; | |
228 | s.op->newline(-1) << "}"; | |
d3f9f732 | 229 | |
d9aed31e | 230 | s.op->newline() << "(*stp->probe->ph) (c);"; |
f887a8c9 | 231 | common_probe_entryfn_epilogue (s, true); |
83ea76b1 | 232 | s.op->newline(-1) << "}"; |
6a8fe809 | 233 | s.op->newline(); |
ad6c8f90 JS |
234 | if (have_a_process_tag) |
235 | s.op->newline() << "#define STP_PERF_USE_TASK_FINDER 1"; | |
6a8fe809 SC |
236 | s.op->newline() << "#include \"linux/perf.c\""; |
237 | s.op->newline(); | |
83ea76b1 WC |
238 | } |
239 | ||
240 | ||
241 | void | |
242 | perf_derived_probe_group::emit_module_init (systemtap_session& s) | |
243 | { | |
244 | if (probes.empty()) return; | |
245 | ||
ad6c8f90 JS |
246 | s.op->newline() << "rc = _stp_perf_init_n (stap_perf_probes, " |
247 | << probes.size() << ", &probe_point);"; | |
83ea76b1 WC |
248 | } |
249 | ||
250 | ||
251 | void | |
252 | perf_derived_probe_group::emit_module_exit (systemtap_session& s) | |
253 | { | |
254 | if (probes.empty()) return; | |
255 | ||
ad6c8f90 JS |
256 | s.op->newline() << "_stp_perf_del_n (stap_perf_probes, " |
257 | << probes.size() << ");"; | |
83ea76b1 WC |
258 | } |
259 | ||
260 | ||
261 | struct perf_builder: public derived_probe_builder | |
262 | { | |
263 | virtual void build(systemtap_session & sess, | |
264 | probe * base, probe_point * location, | |
265 | literal_map_t const & parameters, | |
266 | vector<derived_probe *> & finished_results); | |
267 | ||
268 | static void register_patterns(systemtap_session& s); | |
269 | }; | |
270 | ||
271 | ||
272 | void | |
273 | perf_builder::build(systemtap_session & sess, | |
3689db05 SC |
274 | probe * base, |
275 | probe_point * location, | |
276 | literal_map_t const & parameters, | |
277 | vector<derived_probe *> & finished_results) | |
83ea76b1 | 278 | { |
7acbe856 FCE |
279 | // XXX need additional version checks too? |
280 | // --- perhaps look for export of perf_event_create_kernel_counter | |
70e6d6c9 | 281 | if (sess.kernel_exports.find("perf_event_create_kernel_counter") == sess.kernel_exports.end()) |
dc09353a | 282 | throw SEMANTIC_ERROR (_("perf probes not available without exported perf_event_create_kernel_counter")); |
7acbe856 | 283 | if (sess.kernel_config["CONFIG_PERF_EVENTS"] != "y") |
dc09353a | 284 | throw SEMANTIC_ERROR (_("perf probes not available without CONFIG_PERF_EVENTS")); |
7acbe856 | 285 | |
95ef3b30 FCE |
286 | int64_t type; |
287 | bool has_type = get_param(parameters, TOK_TYPE, type); | |
288 | assert(has_type); | |
289 | ||
290 | int64_t config; | |
291 | bool has_config = get_param(parameters, TOK_CONFIG, config); | |
292 | assert(has_config); | |
a3cf75e6 | 293 | |
83ea76b1 | 294 | int64_t period; |
a3cf75e6 JS |
295 | bool has_period = get_param(parameters, TOK_SAMPLE, period); |
296 | if (!has_period) | |
95ef3b30 | 297 | period = 1000000; // XXX: better parametrize this default |
1f1b6bb1 | 298 | else if (period < 1) |
dc09353a | 299 | throw SEMANTIC_ERROR(_("invalid perf sample period ") + lex_cast(period), |
1f1b6bb1 | 300 | parameters.find(TOK_SAMPLE)->second->tok); |
3689db05 SC |
301 | |
302 | string var; | |
303 | bool has_counter = get_param(parameters, TOK_COUNTER, var); | |
304 | if (var.find_first_of("*?[") != string::npos) | |
dc09353a | 305 | throw SEMANTIC_ERROR(_("wildcard not allowed with perf probe counter component")); |
3689db05 SC |
306 | if (has_counter) |
307 | { | |
7c3feb93 | 308 | if (var.empty()) |
dc09353a | 309 | throw SEMANTIC_ERROR(_("missing perf probe counter component name")); |
7c3feb93 | 310 | |
3689db05 | 311 | period = 0; // perf_event_attr.sample_freq should be 0 |
7c3feb93 JS |
312 | if (sess.perf_counters.count(var) > 0) |
313 | throw SEMANTIC_ERROR(_("duplicate counter name")); | |
314 | ||
315 | // Splice a 'next' into the probe body, and then elaborate.cxx's | |
316 | // dead_stmtexpr_remover() will warn if anything of substance follows. | |
317 | statement* n = new next_statement (); | |
318 | n->tok = base->tok; | |
319 | base->body = new block (n, base->body); | |
3689db05 | 320 | } |
1f1b6bb1 | 321 | |
48cd804a JS |
322 | bool proc_p; |
323 | string proc_n; | |
324 | if ((proc_p = has_null_param(parameters, TOK_PROCESS))) | |
325 | { | |
326 | proc_n = sess.cmd_file(); | |
327 | if (proc_n.empty()) | |
328 | throw SEMANTIC_ERROR(_("process probe is invalid without a -c COMMAND")); | |
329 | } | |
330 | else | |
331 | proc_p = get_param(parameters, TOK_PROCESS, proc_n); | |
332 | if (proc_p && !proc_n.empty()) | |
333 | proc_n = find_executable (proc_n, sess.sysroot, sess.sysenv); | |
334 | ||
1f1b6bb1 | 335 | if (sess.verbose > 1) |
48cd804a JS |
336 | clog << _F("perf probe type=%" PRId64 " config=%" PRId64 " period=%" PRId64 " process=%s counter=%s", |
337 | type, config, period, proc_n.c_str(), var.c_str()) << endl; | |
a3cf75e6 | 338 | |
83ea76b1 | 339 | finished_results.push_back |
3689db05 SC |
340 | (new perf_derived_probe(base, location, type, config, period, proc_p, |
341 | has_counter, proc_n, var)); | |
48cd804a JS |
342 | if (!var.empty()) |
343 | sess.perf_counters[var] = make_pair(proc_n,finished_results.back()); | |
1f1b6bb1 JS |
344 | } |
345 | ||
346 | ||
83ea76b1 WC |
347 | void |
348 | register_tapset_perf(systemtap_session& s) | |
349 | { | |
95ef3b30 FCE |
350 | // NB: at this point, the binding is *not* unprivileged. |
351 | ||
83ea76b1 | 352 | derived_probe_builder *builder = new perf_builder(); |
a3cf75e6 | 353 | match_node* perf = s.pattern_root->bind(TOK_PERF); |
83ea76b1 | 354 | |
95ef3b30 | 355 | match_node* event = perf->bind_num(TOK_TYPE)->bind_num(TOK_CONFIG); |
a3cf75e6 JS |
356 | event->bind(builder); |
357 | event->bind_num(TOK_SAMPLE)->bind(builder); | |
6a8fe809 SC |
358 | event->bind_str(TOK_PROCESS)->bind(builder); |
359 | event->bind(TOK_PROCESS)->bind(builder); | |
3689db05 SC |
360 | event->bind_str(TOK_COUNTER)->bind(builder); |
361 | event->bind_str(TOK_PROCESS)->bind_str(TOK_COUNTER)->bind(builder); | |
83ea76b1 WC |
362 | } |
363 | ||
01c2eefe | 364 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |