]>
sourceware.org Git - systemtap.git/blob - tapset-perfmon.cxx
1 // tapset for HW performance monitoring
2 // Copyright (C) 2005-2013 Red Hat Inc.
3 // Copyright (C) 2005-2007 Intel Corporation.
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
12 #include "task_finder.h"
13 #include "translate.h"
20 #define __STDC_FORMAT_MACROS
25 using namespace __gnu_cxx
;
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");
36 // ------------------------------------------------------------------------
37 // perf event derived probes
38 // ------------------------------------------------------------------------
39 // This is a new interface to the perfmon hw.
42 struct perf_derived_probe
: public derived_probe
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
);
57 struct perf_derived_probe_group
: public generic_dpg
<perf_derived_probe
>
59 void emit_module_decls (systemtap_session
& s
);
60 void emit_module_init (systemtap_session
& s
);
61 void emit_module_exit (systemtap_session
& s
);
65 perf_derived_probe::perf_derived_probe (probe
* p
, probe_point
* l
,
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
),
79 vector
<probe_point::component
*>& comps
= this->sole_location()->components
;
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
)));
91 perf_derived_probe::join_group (systemtap_session
& s
)
93 if (! s
.perf_derived_probes
)
94 s
.perf_derived_probes
= new perf_derived_probe_group ();
95 s
.perf_derived_probes
->enroll (this);
97 if (has_process
&& !has_counter
)
98 enable_task_finder(s
);
103 perf_derived_probe_group::emit_module_decls (systemtap_session
& s
)
105 bool have_a_process_tag
= false;
107 for (unsigned i
=0; i
< probes
.size(); i
++)
108 if (probes
[i
]->has_process
&& !probes
[i
]->has_counter
)
110 have_a_process_tag
= true;
114 if (probes
.empty()) return;
116 s
.op
->newline() << "/* ---- perf probes ---- */";
117 s
.op
->newline() << "#include <linux/perf_event.h>";
118 s
.op
->newline() << "#include \"linux/perf.h\"";
122 s
.op
->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs);";
123 for (unsigned i
=0; i
< probes
.size(); i
++)
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";
139 // Output task finder callback routine
140 if (have_a_process_tag
)
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) {";
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);";
147 s
.op
->newline() << "if (register_p) ";
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) << "}";
157 /* data structures */
158 s
.op
->newline() << "static struct stap_perf_probe stap_perf_probes ["
159 << probes
.size() << "] = {";
161 for (unsigned i
=0; i
< probes
.size(); i
++)
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
]) << ", ";
171 string l_process_name
;
172 if (probes
[i
]->has_process
&& !probes
[i
]->has_counter
)
174 if (probes
[i
]->process_name
.length() == 0)
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];
184 l_process_name
= probes
[i
]->process_name
;
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, ";
198 else if (probes
[i
]->has_counter
)
199 s
.op
->newline() << ".per_thread=" << "1, ";
201 s
.op
->newline() << ".per_thread=" << "0, ";
202 s
.op
->newline(-1) << "},";
204 s
.op
->newline(-1) << "};";
207 /* wrapper functions */
208 for (unsigned i
=0; i
< probes
.size(); i
++)
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) << "}";
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) << "}";
239 s
.op
->newline() << "(*stp->probe->ph) (c);";
240 common_probe_entryfn_epilogue (s
, true);
241 s
.op
->newline(-1) << "}";
243 s
.op
->newline() << "#include \"linux/perf.c\"";
249 perf_derived_probe_group::emit_module_init (systemtap_session
& s
)
251 bool have_a_process_tag
= false;
253 for (unsigned i
=0; i
< probes
.size(); i
++)
254 if (probes
[i
]->has_process
)
256 have_a_process_tag
= true;
260 if (probes
.empty()) return;
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
279 perf_derived_probe_group::emit_module_exit (systemtap_session
& s
)
281 if (probes
.empty()) return;
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
289 struct perf_builder
: public derived_probe_builder
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
);
296 static void register_patterns(systemtap_session
& s
);
301 perf_builder::build(systemtap_session
& sess
,
303 probe_point
* location
,
304 literal_map_t
const & parameters
,
305 vector
<derived_probe
*> & finished_results
)
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"));
315 bool has_type
= get_param(parameters
, TOK_TYPE
, type
);
319 bool has_config
= get_param(parameters
, TOK_CONFIG
, config
);
323 bool has_period
= get_param(parameters
, TOK_SAMPLE
, period
);
325 period
= 1000000; // XXX: better parametrize this default
327 throw semantic_error(_("invalid perf sample period ") + lex_cast(period
),
328 parameters
.find(TOK_SAMPLE
)->second
->tok
);
331 proc_p
= has_null_param(parameters
, TOK_PROCESS
)
332 || get_param(parameters
, TOK_PROCESS
, proc_n
);
334 proc_n
= find_executable (proc_n
, sess
.sysroot
, sess
.sysenv
);
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"));
342 if (var
.length() == 0)
343 throw semantic_error(_("missing perf probe counter component name"));
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"));
352 if (sess
.verbose
> 1)
353 clog
<< _F("perf probe type=%" PRId64
" config=%" PRId64
" period=%" PRId64
, type
, config
, period
) << endl
;
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());
363 register_tapset_perf(systemtap_session
& s
)
365 // NB: at this point, the binding is *not* unprivileged.
367 derived_probe_builder
*builder
= new perf_builder();
368 match_node
* perf
= s
.pattern_root
->bind(TOK_PERF
);
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
);
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.