Branch data Line data Source code
1 : : // tapset for HW performance monitoring
2 : : // Copyright (C) 2005-2013 Red Hat Inc.
3 : : // Copyright (C) 2005-2007 Intel Corporation.
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"
12 : : #include "task_finder.h"
13 : : #include "translate.h"
14 : : #include "util.h"
15 : :
16 : : #include <string>
17 : : #include <wordexp.h>
18 : :
19 : : extern "C" {
20 : : #define __STDC_FORMAT_MACROS
21 : : #include <inttypes.h>
22 : : }
23 : :
24 : : using namespace std;
25 : : using namespace __gnu_cxx;
26 : :
27 : :
28 [ + - ]: 2414 : static const string TOK_PERF("perf");
29 [ + - ]: 2414 : static const string TOK_TYPE("type");
30 [ + - ]: 2414 : static const string TOK_CONFIG("config");
31 [ + - ]: 2414 : static const string TOK_SAMPLE("sample");
32 [ + - ]: 2414 : static const string TOK_PROCESS("process");
33 [ + - ]: 2414 : static const string TOK_COUNTER("counter");
34 : :
35 : :
36 : : // ------------------------------------------------------------------------
37 : : // perf event derived probes
38 : : // ------------------------------------------------------------------------
39 : : // This is a new interface to the perfmon hw.
40 : : //
41 : :
42 [ # # ][ # # ]: 0 : struct perf_derived_probe: public derived_probe
[ # # ]
43 : : {
44 : : int64_t event_type;
45 : : int64_t event_config;
46 : : int64_t interval;
47 : : bool has_process;
48 : : bool has_counter;
49 : : string process_name;
50 : : string counter;
51 : : perf_derived_probe (probe* p, probe_point* l, int64_t type, int64_t config,
52 : : int64_t i, bool pp, bool cp, string pn, string cv);
53 : : virtual void join_group (systemtap_session& s);
54 : : };
55 : :
56 : :
57 [ # # ]: 14 : struct perf_derived_probe_group: public generic_dpg<perf_derived_probe>
58 : : {
59 : : void emit_module_decls (systemtap_session& s);
60 : : void emit_module_init (systemtap_session& s);
61 : : void emit_module_exit (systemtap_session& s);
62 : : };
63 : :
64 : :
65 : 94 : perf_derived_probe::perf_derived_probe (probe* p, probe_point* l,
66 : : int64_t type,
67 : : int64_t config,
68 : : int64_t i,
69 : : bool process_p,
70 : : bool counter_p,
71 : : string process_n,
72 : : string counter):
73 : :
74 : : derived_probe (p, l, true /* .components soon rewritten */),
75 : : event_type (type), event_config (config), interval (i),
76 : : has_process (process_p), has_counter (counter_p), process_name (process_n),
77 [ + - ][ + - ]: 94 : counter (counter)
78 : : {
79 [ + - ]: 94 : vector<probe_point::component*>& comps = this->sole_location()->components;
80 [ + - ]: 94 : comps.clear();
81 [ + - ][ + - ]: 94 : comps.push_back (new probe_point::component (TOK_PERF));
[ + - ]
82 [ + - ][ + - ]: 94 : comps.push_back (new probe_point::component (TOK_TYPE, new literal_number(type)));
[ + - ][ + - ]
[ + - ]
83 [ + - ][ + - ]: 94 : comps.push_back (new probe_point::component (TOK_CONFIG, new literal_number (config)));
[ + - ][ + - ]
[ + - ]
84 [ + - ][ + - ]: 94 : comps.push_back (new probe_point::component (TOK_SAMPLE, new literal_number (interval)));
[ + - ][ + - ]
[ + - ]
85 [ + - ][ + - ]: 94 : comps.push_back (new probe_point::component (TOK_PROCESS, new literal_string (process_name)));
[ + - ][ + - ]
[ + - ]
86 [ + - ][ + - ]: 94 : comps.push_back (new probe_point::component (TOK_COUNTER, new literal_string (counter)));
[ + - ][ + - ]
[ + - ]
87 : 94 : }
88 : :
89 : :
90 : : void
91 : 94 : perf_derived_probe::join_group (systemtap_session& s)
92 : : {
93 [ + + ]: 94 : if (! s.perf_derived_probes)
94 [ + - ]: 14 : s.perf_derived_probes = new perf_derived_probe_group ();
95 : 94 : s.perf_derived_probes->enroll (this);
96 : :
97 [ + + ][ + + ]: 94 : if (has_process && !has_counter)
98 : 3 : enable_task_finder(s);
99 : 94 : }
100 : :
101 : :
102 : : void
103 : 8 : perf_derived_probe_group::emit_module_decls (systemtap_session& s)
104 : : {
105 : 8 : bool have_a_process_tag = false;
106 : :
107 [ + + ]: 92 : for (unsigned i=0; i < probes.size(); i++)
108 [ + + ][ + - ]: 86 : if (probes[i]->has_process && !probes[i]->has_counter)
[ + + ]
109 : : {
110 : 2 : have_a_process_tag = true;
111 : 2 : break;
112 : : }
113 : :
114 [ - + ]: 16 : if (probes.empty()) return;
115 : :
116 : 8 : s.op->newline() << "/* ---- perf probes ---- */";
117 : 8 : s.op->newline() << "#include <linux/perf_event.h>";
118 : 8 : s.op->newline() << "#include \"linux/perf.h\"";
119 : 8 : s.op->newline();
120 : :
121 : : /* declarations */
122 : 8 : s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs);";
123 [ + + ]: 95 : for (unsigned i=0; i < probes.size(); i++)
124 : : {
125 : 87 : s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI";
126 : 87 : s.op->newline() << "static void enter_perf_probe_" << i
127 : 87 : << " (struct perf_event *e, int nmi, "
128 : 87 : << "struct perf_sample_data *data, "
129 : 87 : << "struct pt_regs *regs);";
130 : 87 : s.op->newline() << "#else";
131 : 87 : s.op->newline() << "static void enter_perf_probe_" << i
132 : 87 : << " (struct perf_event *e, "
133 : 87 : << "struct perf_sample_data *data, "
134 : 87 : << "struct pt_regs *regs);";
135 : 87 : s.op->newline() << "#endif";
136 : : }
137 : 8 : s.op->newline();
138 : :
139 : : // Output task finder callback routine
140 [ + + ]: 8 : if (have_a_process_tag)
141 : : {
142 : 2 : 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) {";
143 : 2 : s.op->indent(1);
144 : 2 : s.op->newline() << "int rc = 0;";
145 : 2 : s.op->newline() << "struct stap_perf_probe *p = container_of(tgt, struct stap_perf_probe, e.t.tgt);";
146 : :
147 : 2 : s.op->newline() << "if (register_p) ";
148 : 2 : s.op->indent(1);
149 : :
150 : 2 : s.op->newline() << "rc = _stp_perf_init(p, tsk);";
151 : 2 : s.op->newline(-1) << "else";
152 : 2 : s.op->newline(1) << "_stp_perf_del(p);";
153 : 2 : s.op->newline(-1) << "return rc;";
154 : 2 : s.op->newline(-1) << "}";
155 : : }
156 : :
157 : : /* data structures */
158 : 8 : s.op->newline() << "static struct stap_perf_probe stap_perf_probes ["
159 : 8 : << probes.size() << "] = {";
160 : 8 : s.op->indent(1);
161 [ + + ]: 95 : for (unsigned i=0; i < probes.size(); i++)
162 : : {
163 [ + - ][ + - ]: 87 : s.op->newline() << "{";
164 [ + - ][ + - ]: 87 : s.op->newline(1) << ".attr={ "
165 [ + - ][ + - ]: 87 : << ".type=" << probes[i]->event_type << "ULL, "
[ + - ]
166 [ + - ][ + - ]: 87 : << ".config=" << probes[i]->event_config << "ULL, "
[ + - ]
167 [ + - ][ + - ]: 87 : << "{ .sample_period=" << probes[i]->interval << "ULL }},";
[ + - ]
168 [ + - ][ + - ]: 87 : s.op->newline() << ".callback=enter_perf_probe_" << i << ", ";
[ + - ][ + - ]
169 [ + - ][ + - ]: 87 : s.op->newline() << ".probe=" << common_probe_init (probes[i]) << ", ";
[ + - ][ + - ]
[ + - ][ + - ]
170 : :
171 [ + - ]: 87 : string l_process_name;
172 [ + + ][ + - ]: 87 : if (probes[i]->has_process && !probes[i]->has_counter)
[ + + ]
173 : : {
174 [ + - ][ + + ]: 3 : if (probes[i]->process_name.length() == 0)
175 : : {
176 : : wordexp_t words;
177 [ + - ][ + - ]: 1 : int rc = wordexp(s.cmd.c_str(), &words, WRDE_NOCMD|WRDE_UNDEF);
178 [ + - ][ - + ]: 1 : if (rc || words.we_wordc <= 0)
179 [ # # ][ # # ]: 0 : throw semantic_error(_("unspecified process probe is invalid without a -c COMMAND"));
180 [ + - ]: 1 : l_process_name = words.we_wordv[0];
181 : 1 : wordfree (& words);
182 : : }
183 : : else
184 [ + - ]: 2 : l_process_name = probes[i]->process_name;
185 : :
186 [ + - ][ + - ]: 3 : s.op->line() << " .e={";
187 [ + - ][ + - ]: 3 : s.op->line() << " .t={";
188 [ + - ][ + - ]: 3 : s.op->line() << " .tgt={";
189 [ + - ][ + - ]: 3 : s.op->line() << " .purpose=\"perfctr\",";
190 [ + - ][ + - ]: 3 : s.op->line() << " .procname=\"" << l_process_name << "\",";
[ + - ][ + - ]
191 [ + - ][ + - ]: 3 : s.op->line() << " .pid=0,";
192 [ + - ][ + - ]: 3 : s.op->line() << " .callback=&_stp_perf_probe_cb,";
193 [ + - ][ + - ]: 3 : s.op->line() << " },";
194 [ + - ][ + - ]: 3 : s.op->line() << " },";
195 [ + - ][ + - ]: 3 : s.op->line() << " },";
196 [ + - ][ + - ]: 3 : s.op->newline() << ".per_thread=" << "1, ";
[ + - ]
197 : : }
198 [ + + ]: 84 : else if (probes[i]->has_counter)
199 [ + - ][ + - ]: 1 : s.op->newline() << ".per_thread=" << "1, ";
[ + - ]
200 : : else
201 [ + - ][ + - ]: 83 : s.op->newline() << ".per_thread=" << "0, ";
[ + - ]
202 [ + - ][ + - ]: 87 : s.op->newline(-1) << "},";
203 [ + - ]: 87 : }
204 : 8 : s.op->newline(-1) << "};";
205 : 8 : s.op->newline();
206 : :
207 : : /* wrapper functions */
208 [ + + ]: 95 : for (unsigned i=0; i < probes.size(); i++)
209 : : {
210 : 87 : s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI";
211 : 87 : s.op->newline() << "static void enter_perf_probe_" << i
212 : 87 : << " (struct perf_event *e, int nmi, "
213 : 87 : << "struct perf_sample_data *data, "
214 : 87 : << "struct pt_regs *regs)";
215 : 87 : s.op->newline() << "#else";
216 : 87 : s.op->newline() << "static void enter_perf_probe_" << i
217 : 87 : << " (struct perf_event *e, "
218 : 87 : << "struct perf_sample_data *data, "
219 : 87 : << "struct pt_regs *regs)";
220 : 87 : s.op->newline() << "#endif";
221 : 87 : s.op->newline() << "{";
222 : 87 : s.op->newline(1) << "handle_perf_probe(" << i << ", regs);";
223 : 87 : s.op->newline(-1) << "}";
224 : : }
225 : 8 : s.op->newline();
226 : :
227 : 8 : s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs)";
228 : 8 : s.op->newline() << "{";
229 : 8 : s.op->newline(1) << "struct stap_perf_probe* stp = & stap_perf_probes [i];";
230 : : common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "stp->probe",
231 [ + - ][ + - ]: 8 : "stp_probe_type_perf");
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
232 : 8 : s.op->newline() << "if (user_mode(regs)) {";
233 : 8 : s.op->newline(1)<< "c->user_mode_p = 1;";
234 : 8 : s.op->newline() << "c->uregs = regs;";
235 : 8 : s.op->newline(-1) << "} else {";
236 : 8 : s.op->newline(1) << "c->kregs = regs;";
237 : 8 : s.op->newline(-1) << "}";
238 : :
239 : 8 : s.op->newline() << "(*stp->probe->ph) (c);";
240 : 8 : common_probe_entryfn_epilogue (s, true);
241 : 8 : s.op->newline(-1) << "}";
242 : 8 : s.op->newline();
243 : 8 : s.op->newline() << "#include \"linux/perf.c\"";
244 : 8 : s.op->newline();
245 : : }
246 : :
247 : :
248 : : void
249 : 8 : perf_derived_probe_group::emit_module_init (systemtap_session& s)
250 : : {
251 : 8 : bool have_a_process_tag = false;
252 : :
253 [ + + ]: 92 : for (unsigned i=0; i < probes.size(); i++)
254 [ + + ]: 86 : if (probes[i]->has_process)
255 : : {
256 : 2 : have_a_process_tag = true;
257 : 2 : break;
258 : : }
259 : :
260 [ - + ]: 16 : if (probes.empty()) return;
261 : :
262 : 8 : s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
263 : 8 : s.op->newline(1) << "struct stap_perf_probe* stp = & stap_perf_probes [i];";
264 : 8 : s.op->newline() << "rc = _stp_perf_init(stp, 0);";
265 : 8 : s.op->newline() << "if (rc) {";
266 : 8 : s.op->newline(1) << "probe_point = stp->probe->pp;";
267 : 8 : s.op->newline() << "for (j=0; j<i; j++) {";
268 : 8 : s.op->newline(1) << "_stp_perf_del(& stap_perf_probes [j]);";
269 : 8 : s.op->newline(-1) << "}"; // for unwind loop
270 : 8 : s.op->newline() << "break;";
271 : 8 : s.op->newline(-1) << "}"; // if-error
272 [ + + ]: 8 : if (have_a_process_tag)
273 : 2 : s.op->newline() << "rc = stap_register_task_finder_target(&stp->e.t.tgt);";
274 : 8 : s.op->newline(-1) << "}"; // for loop
275 : : }
276 : :
277 : :
278 : : void
279 : 15 : perf_derived_probe_group::emit_module_exit (systemtap_session& s)
280 : : {
281 [ - + ]: 30 : if (probes.empty()) return;
282 : :
283 : 15 : s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
284 : 15 : s.op->newline(1) << "_stp_perf_del(& stap_perf_probes [i]);";
285 : 15 : s.op->newline(-1) << "}"; // for loop
286 : : }
287 : :
288 : :
289 [ # # ]: 1218 : struct perf_builder: public derived_probe_builder
290 : : {
291 : : virtual void build(systemtap_session & sess,
292 : : probe * base, probe_point * location,
293 : : literal_map_t const & parameters,
294 : : vector<derived_probe *> & finished_results);
295 : :
296 : : static void register_patterns(systemtap_session& s);
297 : : };
298 : :
299 : :
300 [ - + ]: 7 : struct statement_counter: public update_visitor
301 : : {
302 : : bool empty;
303 : : const token* first_tok;
304 : :
305 : 7 : statement_counter () {}
306 : :
307 : 7 : void visit_block (block *b)
308 : : {
309 [ + - ]: 7 : if (b->statements.size() > 0)
310 : : {
311 : 7 : empty = false;
312 : 7 : first_tok = b->statements[0]->tok;
313 : : }
314 : : else
315 : 0 : empty = true;
316 : 7 : };
317 : : };
318 : :
319 : :
320 : : void
321 : 95 : perf_builder::build(systemtap_session & sess,
322 : : probe * base,
323 : : probe_point * location,
324 : : literal_map_t const & parameters,
325 : : vector<derived_probe *> & finished_results)
326 : : {
327 : : // XXX need additional version checks too?
328 : : // --- perhaps look for export of perf_event_create_kernel_counter
329 [ + - ][ + - ]: 95 : if (sess.kernel_exports.find("perf_event_create_kernel_counter") == sess.kernel_exports.end())
[ + - ][ + - ]
[ - + ]
330 [ # # ][ # # ]: 0 : throw semantic_error (_("perf probes not available without exported perf_event_create_kernel_counter"));
331 [ + - ][ + - ]: 95 : if (sess.kernel_config["CONFIG_PERF_EVENTS"] != "y")
[ + - ][ + - ]
[ - + ]
332 [ # # ][ # # ]: 0 : throw semantic_error (_("perf probes not available without CONFIG_PERF_EVENTS"));
333 : :
334 : : int64_t type;
335 [ + - ]: 95 : bool has_type = get_param(parameters, TOK_TYPE, type);
336 [ - + ]: 95 : assert(has_type);
337 : :
338 : : int64_t config;
339 [ + - ]: 95 : bool has_config = get_param(parameters, TOK_CONFIG, config);
340 [ - + ]: 95 : assert(has_config);
341 : :
342 : : int64_t period;
343 [ + - ]: 95 : bool has_period = get_param(parameters, TOK_SAMPLE, period);
344 [ + - ]: 95 : if (!has_period)
345 : 95 : period = 1000000; // XXX: better parametrize this default
346 [ # # ]: 0 : else if (period < 1)
347 : 0 : throw semantic_error(_("invalid perf sample period ") + lex_cast(period),
348 [ # # ][ # # ]: 0 : parameters.find(TOK_SAMPLE)->second->tok);
[ # # ][ # # ]
[ # # ]
349 : : bool proc_p;
350 [ + - ]: 95 : string proc_n;
351 [ + - ]: 95 : proc_p = has_null_param(parameters, TOK_PROCESS)
352 [ + + ][ + - ]: 95 : || get_param(parameters, TOK_PROCESS, proc_n);
[ + + ]
353 [ + + ]: 95 : if (proc_p)
354 [ + - ][ + - ]: 10 : proc_n = find_executable (proc_n, sess.sysroot, sess.sysenv);
[ + - ][ + - ]
[ + - ]
355 : :
356 [ + - ]: 95 : string var;
357 [ + - ]: 95 : bool has_counter = get_param(parameters, TOK_COUNTER, var);
358 [ + - ][ - + ]: 95 : if (var.find_first_of("*?[") != string::npos)
359 [ # # ][ # # ]: 0 : throw semantic_error(_("wildcard not allowed with perf probe counter component"));
360 [ + + ]: 95 : if (has_counter)
361 : : {
362 [ + - ][ + + ]: 8 : if (var.length() == 0)
363 [ + - ][ + - ]: 1 : throw semantic_error(_("missing perf probe counter component name"));
364 : :
365 : 7 : period = 0; // perf_event_attr.sample_freq should be 0
366 : 7 : map<string, pair<string,derived_probe*> >::iterator it;
367 [ + - ][ + - ]: 8 : for (it=sess.perf_counters.begin(); it != sess.perf_counters.end(); it++)
[ + + ]
368 [ + - ][ - + ]: 1 : if ((*it).first == var)
369 [ # # ][ # # ]: 0 : throw semantic_error(_("duplicate counter name"));
370 : :
371 [ + - ]: 7 : struct statement_counter sc;
372 [ + - ]: 7 : base->body->visit(&sc);
373 [ + - ]: 7 : if (! sc.empty)
374 [ + - ][ + - ]: 7 : sess.print_warning(_("Statements in perf counter probe will never be reached."), sc.first_tok);
[ + - ]
375 : :
376 [ + - ][ + - ]: 7 : if_statement *ifs = new if_statement ();
377 : 7 : ifs->tok = base->tok;
378 [ + - ][ + - ]: 7 : ifs->thenblock = new next_statement ();
379 : 7 : ifs->thenblock->tok = base->tok;
380 : 7 : ifs->elseblock = NULL;
381 [ + - ][ + - ]: 7 : ifs->condition = new literal_number(0);
382 [ + - ][ + - ]: 8 : base->body = new block (ifs, base->body);
[ + - ]
383 : : }
384 : :
385 [ + + ]: 94 : if (sess.verbose > 1)
386 [ + - ][ + - ]: 81 : clog << _F("perf probe type=%" PRId64 " config=%" PRId64 " period=%" PRId64, type, config, period) << endl;
[ + - ][ + - ]
387 : :
388 : : finished_results.push_back
389 : : (new perf_derived_probe(base, location, type, config, period, proc_p,
390 [ + - ][ + - ]: 94 : has_counter, proc_n, var));
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
391 [ + - ][ + - ]: 95 : sess.perf_counters[var] = make_pair(proc_n,finished_results.back());
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
392 : 94 : }
393 : :
394 : :
395 : : void
396 : 1218 : register_tapset_perf(systemtap_session& s)
397 : : {
398 : : // NB: at this point, the binding is *not* unprivileged.
399 : :
400 : 1218 : derived_probe_builder *builder = new perf_builder();
401 : 1218 : match_node* perf = s.pattern_root->bind(TOK_PERF);
402 : :
403 : 1218 : match_node* event = perf->bind_num(TOK_TYPE)->bind_num(TOK_CONFIG);
404 : 1218 : event->bind(builder);
405 : 1218 : event->bind_num(TOK_SAMPLE)->bind(builder);
406 : 1218 : event->bind_str(TOK_PROCESS)->bind(builder);
407 : 1218 : event->bind(TOK_PROCESS)->bind(builder);
408 : 1218 : event->bind_str(TOK_COUNTER)->bind(builder);
409 : 1218 : event->bind_str(TOK_PROCESS)->bind_str(TOK_COUNTER)->bind(builder);
410 [ + - ][ + - ]: 8460 : }
411 : :
412 : : /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
|