]> sourceware.org Git - systemtap.git/blob - tapset-perfmon.cxx
Merge remote-tracking branch 'origin/master' into dsmith/interactive
[systemtap.git] / tapset-perfmon.cxx
1 // tapset for HW performance monitoring
2 // Copyright (C) 2005-2014 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
18 extern "C" {
19 #define __STDC_FORMAT_MACROS
20 #include <inttypes.h>
21 }
22
23 using namespace std;
24 using namespace __gnu_cxx;
25
26
27 static const string TOK_PERF("perf");
28 static const string TOK_TYPE("type");
29 static const string TOK_CONFIG("config");
30 static const string TOK_SAMPLE("sample");
31 static const string TOK_PROCESS("process");
32 static const string TOK_COUNTER("counter");
33
34
35 // ------------------------------------------------------------------------
36 // perf event derived probes
37 // ------------------------------------------------------------------------
38 // This is a new interface to the perfmon hw.
39 //
40
41 struct perf_derived_probe: public derived_probe
42 {
43 int64_t event_type;
44 int64_t event_config;
45 int64_t interval;
46 bool has_process;
47 bool has_counter;
48 string process_name;
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);
52 virtual void join_group (systemtap_session& s);
53 };
54
55
56 struct perf_derived_probe_group: public generic_dpg<perf_derived_probe>
57 {
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,
65 int64_t type,
66 int64_t config,
67 int64_t i,
68 bool process_p,
69 bool counter_p,
70 string process_n,
71 string counter):
72
73 derived_probe (p, l, true /* .components soon rewritten */),
74 event_type (type), event_config (config), interval (i),
75 has_process (process_p), has_counter (counter_p), process_name (process_n),
76 counter (counter)
77 {
78 vector<probe_point::component*>& comps = this->sole_location()->components;
79 comps.clear();
80 comps.push_back (new probe_point::component (TOK_PERF));
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)));
83 comps.push_back (new probe_point::component (TOK_SAMPLE, new literal_number (interval)));
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)));
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);
97 this->group = s.perf_derived_probes;
98
99 if (has_process && !has_counter)
100 enable_task_finder(s);
101 }
102
103
104 void
105 perf_derived_probe_group::emit_module_decls (systemtap_session& s)
106 {
107 bool have_a_process_tag = false;
108
109 for (unsigned i=0; i < probes.size(); i++)
110 if (probes[i]->has_process && !probes[i]->has_counter)
111 {
112 have_a_process_tag = true;
113 break;
114 }
115
116 if (probes.empty()) return;
117
118 s.op->newline() << "/* ---- perf probes ---- */";
119 s.op->newline() << "#include <linux/perf_event.h>";
120 s.op->newline() << "#include \"linux/perf.h\"";
121 s.op->newline();
122
123 /* declarations */
124 s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs);";
125 for (unsigned i=0; i < probes.size(); i++)
126 {
127 s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI";
128 s.op->newline() << "static void enter_perf_probe_" << i
129 << " (struct perf_event *e, int nmi, "
130 << "struct perf_sample_data *data, "
131 << "struct pt_regs *regs);";
132 s.op->newline() << "#else";
133 s.op->newline() << "static void enter_perf_probe_" << i
134 << " (struct perf_event *e, "
135 << "struct perf_sample_data *data, "
136 << "struct pt_regs *regs);";
137 s.op->newline() << "#endif";
138 }
139 s.op->newline();
140
141 // Output task finder callback routine
142 if (have_a_process_tag)
143 {
144 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) {";
145 s.op->indent(1);
146 s.op->newline() << "int rc = 0;";
147 s.op->newline() << "struct stap_perf_probe *p = container_of(tgt, struct stap_perf_probe, e.t.tgt);";
148
149 s.op->newline() << "if (register_p) ";
150 s.op->indent(1);
151
152 s.op->newline() << "rc = _stp_perf_init(p, tsk);";
153 s.op->newline(-1) << "else";
154 s.op->newline(1) << "_stp_perf_del(p);";
155 s.op->newline(-1) << "return rc;";
156 s.op->newline(-1) << "}";
157 }
158
159 /* data structures */
160 s.op->newline() << "static struct stap_perf_probe stap_perf_probes ["
161 << probes.size() << "] = {";
162 s.op->indent(1);
163 for (unsigned i=0; i < probes.size(); i++)
164 {
165 s.op->newline() << "{";
166 s.op->newline(1) << ".attr={ "
167 << ".type=" << probes[i]->event_type << "ULL, "
168 << ".config=" << probes[i]->event_config << "ULL, "
169 << "{ .sample_period=" << probes[i]->interval << "ULL }},";
170 s.op->newline() << ".callback=enter_perf_probe_" << i << ", ";
171 s.op->newline() << ".probe=" << common_probe_init (probes[i]) << ", ";
172
173 if (probes[i]->has_process && !probes[i]->has_counter)
174 {
175 s.op->line() << " .e={";
176 s.op->line() << " .t={";
177 s.op->line() << " .tgt={";
178 s.op->line() << " .purpose=\"perfctr\",";
179 s.op->line() << " .procname=\"" << probes[i]->process_name << "\",";
180 s.op->line() << " .pid=0,";
181 s.op->line() << " .callback=&_stp_perf_probe_cb,";
182 s.op->line() << " },";
183 s.op->line() << " },";
184 s.op->line() << " },";
185 s.op->newline() << ".task_finder=" << "1, ";
186 }
187 else if (probes[i]->has_counter)
188 {
189 // process counters are currently task-found by uprobes
190 // set neither .system_wide nor .task_finder
191 }
192 else
193 s.op->newline() << ".system_wide=" << "1, ";
194 s.op->newline(-1) << "},";
195 }
196 s.op->newline(-1) << "};";
197 s.op->newline();
198
199 /* wrapper functions */
200 for (unsigned i=0; i < probes.size(); i++)
201 {
202 s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI";
203 s.op->newline() << "static void enter_perf_probe_" << i
204 << " (struct perf_event *e, int nmi, "
205 << "struct perf_sample_data *data, "
206 << "struct pt_regs *regs)";
207 s.op->newline() << "#else";
208 s.op->newline() << "static void enter_perf_probe_" << i
209 << " (struct perf_event *e, "
210 << "struct perf_sample_data *data, "
211 << "struct pt_regs *regs)";
212 s.op->newline() << "#endif";
213 s.op->newline() << "{";
214 s.op->newline(1) << "handle_perf_probe(" << i << ", regs);";
215 s.op->newline(-1) << "}";
216 }
217 s.op->newline();
218
219 s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs)";
220 s.op->newline() << "{";
221 s.op->newline(1) << "struct stap_perf_probe* stp = & stap_perf_probes [i];";
222 common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "stp->probe",
223 "stp_probe_type_perf");
224 s.op->newline() << "if (user_mode(regs)) {";
225 s.op->newline(1)<< "c->user_mode_p = 1;";
226 s.op->newline() << "c->uregs = regs;";
227 s.op->newline(-1) << "} else {";
228 s.op->newline(1) << "c->kregs = regs;";
229 s.op->newline(-1) << "}";
230
231 s.op->newline() << "(*stp->probe->ph) (c);";
232 common_probe_entryfn_epilogue (s, true, otf_safe_context(s));
233 s.op->newline(-1) << "}";
234 s.op->newline();
235 if (have_a_process_tag)
236 s.op->newline() << "#define STP_PERF_USE_TASK_FINDER 1";
237 s.op->newline() << "#include \"linux/perf.c\"";
238 s.op->newline();
239 }
240
241
242 void
243 perf_derived_probe_group::emit_module_init (systemtap_session& s)
244 {
245 if (probes.empty()) return;
246
247 s.op->newline() << "rc = _stp_perf_init_n (stap_perf_probes, "
248 << probes.size() << ", &probe_point);";
249 }
250
251
252 void
253 perf_derived_probe_group::emit_module_exit (systemtap_session& s)
254 {
255 if (probes.empty()) return;
256
257 s.op->newline() << "_stp_perf_del_n (stap_perf_probes, "
258 << probes.size() << ");";
259 }
260
261
262 struct perf_builder: public derived_probe_builder
263 {
264 virtual void build(systemtap_session & sess,
265 probe * base, probe_point * location,
266 literal_map_t const & parameters,
267 vector<derived_probe *> & finished_results);
268
269 static void register_patterns(systemtap_session& s);
270 };
271
272
273 void
274 perf_builder::build(systemtap_session & sess,
275 probe * base,
276 probe_point * location,
277 literal_map_t const & parameters,
278 vector<derived_probe *> & finished_results)
279 {
280 // XXX need additional version checks too?
281 // --- perhaps look for export of perf_event_create_kernel_counter
282 if (sess.kernel_exports.find("perf_event_create_kernel_counter") == sess.kernel_exports.end())
283 throw SEMANTIC_ERROR (_("perf probes not available without exported perf_event_create_kernel_counter"));
284 if (sess.kernel_config["CONFIG_PERF_EVENTS"] != "y")
285 throw SEMANTIC_ERROR (_("perf probes not available without CONFIG_PERF_EVENTS"));
286
287 int64_t type;
288 bool has_type = get_param(parameters, TOK_TYPE, type);
289 assert(has_type);
290
291 int64_t config;
292 bool has_config = get_param(parameters, TOK_CONFIG, config);
293 assert(has_config);
294
295 int64_t period;
296 bool has_period = get_param(parameters, TOK_SAMPLE, period);
297 if (!has_period)
298 period = 1000000; // XXX: better parametrize this default
299 else if (period < 1)
300 throw SEMANTIC_ERROR(_("invalid perf sample period ") + lex_cast(period),
301 parameters.find(TOK_SAMPLE)->second->tok);
302
303 interned_string var;
304 bool has_counter = get_param(parameters, TOK_COUNTER, var);
305 if (var.find_first_of("*?[") != string::npos)
306 throw SEMANTIC_ERROR(_("wildcard not allowed with perf probe counter component"));
307 if (has_counter)
308 {
309 if (var.empty())
310 throw SEMANTIC_ERROR(_("missing perf probe counter component name"));
311
312 period = 0; // perf_event_attr.sample_freq should be 0
313 vector<std::pair<string,string> >:: iterator it;
314 for (it=sess.perf_counters.begin() ;
315 it != sess.perf_counters.end(); it++)
316 if ((*it).first == var)
317 break;
318 if (it != sess.perf_counters.end())
319 throw SEMANTIC_ERROR(_("duplicate counter name"));
320
321 // Splice a 'next' into the probe body, and then elaborate.cxx's
322 // dead_stmtexpr_remover() will warn if anything of substance follows.
323 statement* n = new next_statement ();
324 n->tok = base->tok;
325 base->body = new block (n, base->body);
326 }
327
328 bool proc_p;
329 interned_string proc_n;
330 if ((proc_p = has_null_param(parameters, TOK_PROCESS)))
331 {
332 try
333 {
334 proc_n = sess.cmd_file();
335 }
336 catch (semantic_error& e)
337 {
338 throw SEMANTIC_ERROR(_("invalid -c command for unspecified process"
339 " probe [man stapprobes]"), NULL, NULL, &e);
340 }
341 if (proc_n.empty())
342 throw SEMANTIC_ERROR(_("unspecified process probe is invalid without a "
343 "-c COMMAND [man stapprobes]"));
344 }
345 else
346 proc_p = get_param(parameters, TOK_PROCESS, proc_n);
347 if (proc_p && !proc_n.empty())
348 proc_n = find_executable (proc_n, sess.sysroot, sess.sysenv);
349
350 if (sess.verbose > 1)
351 clog << _F("perf probe type=%" PRId64 " config=%" PRId64 " period=%" PRId64 " process=%s counter=%s",
352 type, config, period, proc_n.c_str(), var.c_str()) << endl;
353
354 // The user-provided pp is already well-formed. Let's add a copy on the chain
355 // and set it as the new base
356 probe_point *new_location = new probe_point(*location);
357 new_location->well_formed = true;
358 probe *new_base = new probe(base, new_location);
359
360 finished_results.push_back
361 (new perf_derived_probe(new_base, location, type, config, period, proc_p,
362 has_counter, proc_n, var));
363 if (!var.empty())
364 sess.perf_counters.push_back(make_pair (var, proc_n));
365 }
366
367
368 void
369 register_tapset_perf(systemtap_session& s)
370 {
371 // NB: at this point, the binding is *not* unprivileged.
372
373 derived_probe_builder *builder = new perf_builder();
374 match_node* perf = s.pattern_root->bind(TOK_PERF);
375
376 match_node* event = perf->bind_num(TOK_TYPE)->bind_num(TOK_CONFIG);
377 event->bind(builder);
378 event->bind_num(TOK_SAMPLE)->bind(builder);
379 event->bind_str(TOK_PROCESS)->bind(builder);
380 event->bind(TOK_PROCESS)->bind(builder);
381 event->bind_str(TOK_COUNTER)->bind(builder);
382 event->bind_str(TOK_PROCESS)->bind_str(TOK_COUNTER)->bind(builder);
383 }
384
385 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.059136 seconds and 5 git commands to generate.