]>
sourceware.org Git - systemtap.git/blob - tapset-perfmon.cxx
1 // tapset for HW performance monitoring
2 // Copyright (C) 2005-2014 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"
19 #define __STDC_FORMAT_MACROS
24 using namespace __gnu_cxx
;
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");
35 // ------------------------------------------------------------------------
36 // perf event derived probes
37 // ------------------------------------------------------------------------
38 // This is a new interface to the perfmon hw.
41 struct perf_derived_probe
: public derived_probe
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
);
56 struct perf_derived_probe_group
: public generic_dpg
<perf_derived_probe
>
58 void emit_module_decls (systemtap_session
& s
);
59 void emit_module_init (systemtap_session
& s
);
60 void emit_module_exit (systemtap_session
& s
);
64 perf_derived_probe::perf_derived_probe (probe
* p
, probe_point
* l
,
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
),
78 vector
<probe_point::component
*>& comps
= this->sole_location()->components
;
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
)));
85 comps
.push_back (new probe_point::component (TOK_PROCESS
, new literal_string (process_name
)));
87 comps
.push_back (new probe_point::component (TOK_COUNTER
, new literal_string (counter
)));
92 perf_derived_probe::join_group (systemtap_session
& s
)
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
;
99 if (has_process
&& !has_counter
)
100 enable_task_finder(s
);
105 perf_derived_probe_group::emit_module_decls (systemtap_session
& s
)
107 bool have_a_process_tag
= false;
109 for (unsigned i
=0; i
< probes
.size(); i
++)
110 if (probes
[i
]->has_process
&& !probes
[i
]->has_counter
)
112 have_a_process_tag
= true;
116 if (probes
.empty()) return;
118 s
.op
->newline() << "/* ---- perf probes ---- */";
119 s
.op
->newline() << "#include <linux/perf_event.h>";
120 s
.op
->newline() << "#include \"linux/perf.h\"";
124 s
.op
->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs);";
125 for (unsigned i
=0; i
< probes
.size(); i
++)
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";
141 // Output task finder callback routine
142 if (have_a_process_tag
)
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) {";
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);";
149 s
.op
->newline() << "if (register_p) ";
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) << "}";
159 /* data structures */
160 s
.op
->newline() << "static struct stap_perf_probe stap_perf_probes ["
161 << probes
.size() << "] = {";
163 for (unsigned i
=0; i
< probes
.size(); i
++)
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
]) << ", ";
173 if (probes
[i
]->has_process
&& !probes
[i
]->has_counter
)
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, ";
187 else if (probes
[i
]->has_counter
)
189 // process counters are currently task-found by uprobes
190 // set neither .system_wide nor .task_finder
193 s
.op
->newline() << ".system_wide=" << "1, ";
194 s
.op
->newline(-1) << "},";
196 s
.op
->newline(-1) << "};";
199 /* wrapper functions */
200 for (unsigned i
=0; i
< probes
.size(); i
++)
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) << "}";
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) << "}";
231 s
.op
->newline() << "(*stp->probe->ph) (c);";
232 common_probe_entryfn_epilogue (s
, true, otf_safe_context(s
));
233 s
.op
->newline(-1) << "}";
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\"";
243 perf_derived_probe_group::emit_module_init (systemtap_session
& s
)
245 if (probes
.empty()) return;
247 s
.op
->newline() << "rc = _stp_perf_init_n (stap_perf_probes, "
248 << probes
.size() << ", &probe_point);";
253 perf_derived_probe_group::emit_module_exit (systemtap_session
& s
)
255 if (probes
.empty()) return;
257 s
.op
->newline() << "_stp_perf_del_n (stap_perf_probes, "
258 << probes
.size() << ");";
262 struct perf_builder
: public derived_probe_builder
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
);
269 static void register_patterns(systemtap_session
& s
);
274 perf_builder::build(systemtap_session
& sess
,
276 probe_point
* location
,
277 literal_map_t
const & parameters
,
278 vector
<derived_probe
*> & finished_results
)
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"));
288 bool has_type
= get_param(parameters
, TOK_TYPE
, type
);
292 bool has_config
= get_param(parameters
, TOK_CONFIG
, config
);
296 bool has_period
= get_param(parameters
, TOK_SAMPLE
, period
);
298 period
= 1000000; // XXX: better parametrize this default
300 throw SEMANTIC_ERROR(_("invalid perf sample period ") + lex_cast(period
),
301 parameters
.find(TOK_SAMPLE
)->second
->tok
);
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"));
310 throw SEMANTIC_ERROR(_("missing perf probe counter component name"));
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
)
318 if (it
!= sess
.perf_counters
.end())
319 throw SEMANTIC_ERROR(_("duplicate counter name"));
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 ();
325 base
->body
= new block (n
, base
->body
);
329 interned_string proc_n
;
330 if ((proc_p
= has_null_param(parameters
, TOK_PROCESS
)))
334 proc_n
= sess
.cmd_file();
336 catch (semantic_error
& e
)
338 throw SEMANTIC_ERROR(_("invalid -c command for unspecified process"
339 " probe [man stapprobes]"), NULL
, NULL
, &e
);
342 throw SEMANTIC_ERROR(_("unspecified process probe is invalid without a "
343 "-c COMMAND [man stapprobes]"));
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
);
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
;
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
);
360 finished_results
.push_back
361 (new perf_derived_probe(new_base
, location
, type
, config
, period
, proc_p
,
362 has_counter
, proc_n
, var
));
364 sess
.perf_counters
.push_back(make_pair (var
, proc_n
));
369 register_tapset_perf(systemtap_session
& s
)
371 // NB: at this point, the binding is *not* unprivileged.
373 derived_probe_builder
*builder
= new perf_builder();
374 match_node
* perf
= s
.pattern_root
->bind(TOK_PERF
);
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
);
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.