]> sourceware.org Git - systemtap.git/blob - tapset-perfmon.cxx
Do not use unnamed structs.
[systemtap.git] / tapset-perfmon.cxx
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 static const string TOK_PERF("perf");
29 static const string TOK_TYPE("type");
30 static const string TOK_CONFIG("config");
31 static const string TOK_SAMPLE("sample");
32 static const string TOK_PROCESS("process");
33 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 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 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 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 counter (counter)
78 {
79 vector<probe_point::component*>& comps = this->sole_location()->components;
80 comps.clear();
81 comps.push_back (new probe_point::component (TOK_PERF));
82 comps.push_back (new probe_point::component (TOK_TYPE, new literal_number(type)));
83 comps.push_back (new probe_point::component (TOK_CONFIG, new literal_number (config)));
84 comps.push_back (new probe_point::component (TOK_SAMPLE, new literal_number (interval)));
85 comps.push_back (new probe_point::component (TOK_PROCESS, new literal_string (process_name)));
86 comps.push_back (new probe_point::component (TOK_COUNTER, new literal_string (counter)));
87 }
88
89
90 void
91 perf_derived_probe::join_group (systemtap_session& s)
92 {
93 if (! s.perf_derived_probes)
94 s.perf_derived_probes = new perf_derived_probe_group ();
95 s.perf_derived_probes->enroll (this);
96
97 if (has_process && !has_counter)
98 enable_task_finder(s);
99 }
100
101
102 void
103 perf_derived_probe_group::emit_module_decls (systemtap_session& s)
104 {
105 bool have_a_process_tag = false;
106
107 for (unsigned i=0; i < probes.size(); i++)
108 if (probes[i]->has_process && !probes[i]->has_counter)
109 {
110 have_a_process_tag = true;
111 break;
112 }
113
114 if (probes.empty()) return;
115
116 s.op->newline() << "/* ---- perf probes ---- */";
117 s.op->newline() << "#include <linux/perf_event.h>";
118 s.op->newline() << "#include \"linux/perf.h\"";
119 s.op->newline();
120
121 /* declarations */
122 s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs);";
123 for (unsigned i=0; i < probes.size(); i++)
124 {
125 s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI";
126 s.op->newline() << "static void enter_perf_probe_" << i
127 << " (struct perf_event *e, int nmi, "
128 << "struct perf_sample_data *data, "
129 << "struct pt_regs *regs);";
130 s.op->newline() << "#else";
131 s.op->newline() << "static void enter_perf_probe_" << i
132 << " (struct perf_event *e, "
133 << "struct perf_sample_data *data, "
134 << "struct pt_regs *regs);";
135 s.op->newline() << "#endif";
136 }
137 s.op->newline();
138
139 // Output task finder callback routine
140 if (have_a_process_tag)
141 {
142 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 s.op->indent(1);
144 s.op->newline() << "int rc = 0;";
145 s.op->newline() << "struct stap_perf_probe *p = container_of(tgt, struct stap_perf_probe, e.t.tgt);";
146
147 s.op->newline() << "if (register_p) ";
148 s.op->indent(1);
149
150 s.op->newline() << "rc = _stp_perf_init(p, tsk);";
151 s.op->newline(-1) << "else";
152 s.op->newline(1) << "_stp_perf_del(p);";
153 s.op->newline(-1) << "return rc;";
154 s.op->newline(-1) << "}";
155 }
156
157 /* data structures */
158 s.op->newline() << "static struct stap_perf_probe stap_perf_probes ["
159 << probes.size() << "] = {";
160 s.op->indent(1);
161 for (unsigned i=0; i < probes.size(); i++)
162 {
163 s.op->newline() << "{";
164 s.op->newline(1) << ".attr={ "
165 << ".type=" << probes[i]->event_type << "ULL, "
166 << ".config=" << probes[i]->event_config << "ULL, "
167 << "{ .sample_period=" << probes[i]->interval << "ULL }},";
168 s.op->newline() << ".callback=enter_perf_probe_" << i << ", ";
169 s.op->newline() << ".probe=" << common_probe_init (probes[i]) << ", ";
170
171 string l_process_name;
172 if (probes[i]->has_process && !probes[i]->has_counter)
173 {
174 if (probes[i]->process_name.length() == 0)
175 {
176 wordexp_t words;
177 int rc = wordexp(s.cmd.c_str(), &words, WRDE_NOCMD|WRDE_UNDEF);
178 if (rc || words.we_wordc <= 0)
179 throw semantic_error(_("unspecified process probe is invalid without a -c COMMAND"));
180 l_process_name = words.we_wordv[0];
181 wordfree (& words);
182 }
183 else
184 l_process_name = probes[i]->process_name;
185
186 s.op->line() << " .e={";
187 s.op->line() << " .t={";
188 s.op->line() << " .tgt={";
189 s.op->line() << " .purpose=\"perfctr\",";
190 s.op->line() << " .procname=\"" << l_process_name << "\",";
191 s.op->line() << " .pid=0,";
192 s.op->line() << " .callback=&_stp_perf_probe_cb,";
193 s.op->line() << " },";
194 s.op->line() << " },";
195 s.op->line() << " },";
196 s.op->newline() << ".per_thread=" << "1, ";
197 }
198 else if (probes[i]->has_counter)
199 s.op->newline() << ".per_thread=" << "1, ";
200 else
201 s.op->newline() << ".per_thread=" << "0, ";
202 s.op->newline(-1) << "},";
203 }
204 s.op->newline(-1) << "};";
205 s.op->newline();
206
207 /* wrapper functions */
208 for (unsigned i=0; i < probes.size(); i++)
209 {
210 s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI";
211 s.op->newline() << "static void enter_perf_probe_" << i
212 << " (struct perf_event *e, int nmi, "
213 << "struct perf_sample_data *data, "
214 << "struct pt_regs *regs)";
215 s.op->newline() << "#else";
216 s.op->newline() << "static void enter_perf_probe_" << i
217 << " (struct perf_event *e, "
218 << "struct perf_sample_data *data, "
219 << "struct pt_regs *regs)";
220 s.op->newline() << "#endif";
221 s.op->newline() << "{";
222 s.op->newline(1) << "handle_perf_probe(" << i << ", regs);";
223 s.op->newline(-1) << "}";
224 }
225 s.op->newline();
226
227 s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs)";
228 s.op->newline() << "{";
229 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 "stp_probe_type_perf");
232 s.op->newline() << "if (user_mode(regs)) {";
233 s.op->newline(1)<< "c->user_mode_p = 1;";
234 s.op->newline() << "c->uregs = regs;";
235 s.op->newline(-1) << "} else {";
236 s.op->newline(1) << "c->kregs = regs;";
237 s.op->newline(-1) << "}";
238
239 s.op->newline() << "(*stp->probe->ph) (c);";
240 common_probe_entryfn_epilogue (s, true);
241 s.op->newline(-1) << "}";
242 s.op->newline();
243 s.op->newline() << "#include \"linux/perf.c\"";
244 s.op->newline();
245 }
246
247
248 void
249 perf_derived_probe_group::emit_module_init (systemtap_session& s)
250 {
251 bool have_a_process_tag = false;
252
253 for (unsigned i=0; i < probes.size(); i++)
254 if (probes[i]->has_process)
255 {
256 have_a_process_tag = true;
257 break;
258 }
259
260 if (probes.empty()) return;
261
262 s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
263 s.op->newline(1) << "struct stap_perf_probe* stp = & stap_perf_probes [i];";
264 s.op->newline() << "rc = _stp_perf_init(stp, 0);";
265 s.op->newline() << "if (rc) {";
266 s.op->newline(1) << "probe_point = stp->probe->pp;";
267 s.op->newline() << "for (j=0; j<i; j++) {";
268 s.op->newline(1) << "_stp_perf_del(& stap_perf_probes [j]);";
269 s.op->newline(-1) << "}"; // for unwind loop
270 s.op->newline() << "break;";
271 s.op->newline(-1) << "}"; // if-error
272 if (have_a_process_tag)
273 s.op->newline() << "rc = stap_register_task_finder_target(&stp->e.t.tgt);";
274 s.op->newline(-1) << "}"; // for loop
275 }
276
277
278 void
279 perf_derived_probe_group::emit_module_exit (systemtap_session& s)
280 {
281 if (probes.empty()) return;
282
283 s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
284 s.op->newline(1) << "_stp_perf_del(& stap_perf_probes [i]);";
285 s.op->newline(-1) << "}"; // for loop
286 }
287
288
289 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 void
301 perf_builder::build(systemtap_session & sess,
302 probe * base,
303 probe_point * location,
304 literal_map_t const & parameters,
305 vector<derived_probe *> & finished_results)
306 {
307 // XXX need additional version checks too?
308 // --- perhaps look for export of perf_event_create_kernel_counter
309 if (sess.kernel_exports.find("perf_event_create_kernel_counter") == sess.kernel_exports.end())
310 throw semantic_error (_("perf probes not available without exported perf_event_create_kernel_counter"));
311 if (sess.kernel_config["CONFIG_PERF_EVENTS"] != "y")
312 throw semantic_error (_("perf probes not available without CONFIG_PERF_EVENTS"));
313
314 int64_t type;
315 bool has_type = get_param(parameters, TOK_TYPE, type);
316 assert(has_type);
317
318 int64_t config;
319 bool has_config = get_param(parameters, TOK_CONFIG, config);
320 assert(has_config);
321
322 int64_t period;
323 bool has_period = get_param(parameters, TOK_SAMPLE, period);
324 if (!has_period)
325 period = 1000000; // XXX: better parametrize this default
326 else if (period < 1)
327 throw semantic_error(_("invalid perf sample period ") + lex_cast(period),
328 parameters.find(TOK_SAMPLE)->second->tok);
329 bool proc_p;
330 string proc_n;
331 proc_p = has_null_param(parameters, TOK_PROCESS)
332 || get_param(parameters, TOK_PROCESS, proc_n);
333 if (proc_p)
334 proc_n = find_executable (proc_n, sess.sysroot, sess.sysenv);
335
336 string var;
337 bool has_counter = get_param(parameters, TOK_COUNTER, var);
338 if (var.find_first_of("*?[") != string::npos)
339 throw semantic_error(_("wildcard not allowed with perf probe counter component"));
340 if (has_counter)
341 {
342 if (var.length() == 0)
343 throw semantic_error(_("missing perf probe counter component name"));
344
345 period = 0; // perf_event_attr.sample_freq should be 0
346 map<string, pair<string,derived_probe*> >::iterator it;
347 for (it=sess.perf_counters.begin(); it != sess.perf_counters.end(); it++)
348 if ((*it).first == var)
349 throw semantic_error(_("duplicate counter name"));
350 }
351
352 if (sess.verbose > 1)
353 clog << _F("perf probe type=%" PRId64 " config=%" PRId64 " period=%" PRId64, type, config, period) << endl;
354
355 finished_results.push_back
356 (new perf_derived_probe(base, location, type, config, period, proc_p,
357 has_counter, proc_n, var));
358 sess.perf_counters[var] = make_pair(proc_n,finished_results.back());
359 }
360
361
362 void
363 register_tapset_perf(systemtap_session& s)
364 {
365 // NB: at this point, the binding is *not* unprivileged.
366
367 derived_probe_builder *builder = new perf_builder();
368 match_node* perf = s.pattern_root->bind(TOK_PERF);
369
370 match_node* event = perf->bind_num(TOK_TYPE)->bind_num(TOK_CONFIG);
371 event->bind(builder);
372 event->bind_num(TOK_SAMPLE)->bind(builder);
373 event->bind_str(TOK_PROCESS)->bind(builder);
374 event->bind(TOK_PROCESS)->bind(builder);
375 event->bind_str(TOK_COUNTER)->bind(builder);
376 event->bind_str(TOK_PROCESS)->bind_str(TOK_COUNTER)->bind(builder);
377 }
378
379 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.062506 seconds and 6 git commands to generate.