]> sourceware.org Git - systemtap.git/blob - tapset-perfmon.cxx
the omnibus change act of 2013 for copyright and task_finding excellence
[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
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 string process_name;
48 perf_derived_probe (probe* p, probe_point* l, int64_t type, int64_t config, int64_t i, bool pp, string pn);
49 virtual void join_group (systemtap_session& s);
50 };
51
52
53 struct perf_derived_probe_group: public generic_dpg<perf_derived_probe>
54 {
55 void emit_module_decls (systemtap_session& s);
56 void emit_module_init (systemtap_session& s);
57 void emit_module_exit (systemtap_session& s);
58 };
59
60
61 perf_derived_probe::perf_derived_probe (probe* p, probe_point* l,
62 int64_t type,
63 int64_t config,
64 int64_t i,
65 bool process_p,
66 string process_n):
67
68 derived_probe (p, l, true /* .components soon rewritten */),
69 event_type (type), event_config (config), interval (i),
70 has_process (process_p), process_name (process_n)
71 {
72 vector<probe_point::component*>& comps = this->sole_location()->components;
73 comps.clear();
74 comps.push_back (new probe_point::component (TOK_PERF));
75 comps.push_back (new probe_point::component (TOK_TYPE, new literal_number(type)));
76 comps.push_back (new probe_point::component (TOK_CONFIG, new literal_number (config)));
77 comps.push_back (new probe_point::component (TOK_SAMPLE, new literal_number (interval)));
78 comps.push_back (new probe_point::component (TOK_PROCESS, new literal_string (process_name)));
79 }
80
81
82 void
83 perf_derived_probe::join_group (systemtap_session& s)
84 {
85 if (! s.perf_derived_probes)
86 s.perf_derived_probes = new perf_derived_probe_group ();
87 s.perf_derived_probes->enroll (this);
88
89 enable_task_finder(s);
90
91 }
92
93
94 void
95 perf_derived_probe_group::emit_module_decls (systemtap_session& s)
96 {
97 if (probes.empty()) return;
98
99 s.op->newline() << "/* ---- perf probes ---- */";
100 s.op->newline() << "#include <linux/perf_event.h>";
101 s.op->newline() << "#include \"linux/perf.h\"";
102 s.op->newline();
103
104 /* declarations */
105 s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs);";
106 for (unsigned i=0; i < probes.size(); i++)
107 {
108 s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI";
109 s.op->newline() << "static void enter_perf_probe_" << i
110 << " (struct perf_event *e, int nmi, "
111 << "struct perf_sample_data *data, "
112 << "struct pt_regs *regs);";
113 s.op->newline() << "#else";
114 s.op->newline() << "static void enter_perf_probe_" << i
115 << " (struct perf_event *e, "
116 << "struct perf_sample_data *data, "
117 << "struct pt_regs *regs);";
118 s.op->newline() << "#endif";
119 }
120 s.op->newline();
121
122 // Output task finder callback routine that gets called for all
123 // perf probe types.
124 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) {";
125 s.op->indent(1);
126 s.op->newline() << "int rc = 0;";
127 s.op->newline() << "struct stap_perf_probe *p = container_of(tgt, struct stap_perf_probe, tgt);";
128
129 // s.op->newline() << "_stp_printf(\"XXX %d %d\\n\", current->pid, task_pid_nr_ns(tsk,task_active_pid_ns(current->parent)));";
130 s.op->newline() << "if (register_p) ";
131 s.op->indent(1);
132
133 s.op->newline() << "rc = _stp_perf_init(p, tsk);";
134 s.op->newline(-1) << "else";
135 s.op->newline(1) << "_stp_perf_del(p);";
136 s.op->newline(-1) << "return rc;";
137 s.op->newline(-1) << "}";
138
139 /* data structures */
140 s.op->newline() << "static struct stap_perf_probe stap_perf_probes ["
141 << probes.size() << "] = {";
142 s.op->indent(1);
143 for (unsigned i=0; i < probes.size(); i++)
144 {
145 s.op->newline() << "{";
146 s.op->newline(1) << ".attr={ "
147 << ".type=" << probes[i]->event_type << "ULL, "
148 << ".config=" << probes[i]->event_config << "ULL, "
149 << "{ .sample_period=" << probes[i]->interval << "ULL }},";
150 s.op->newline() << ".callback=enter_perf_probe_" << i << ", ";
151 s.op->newline() << ".probe=" << common_probe_init (probes[i]) << ", ";
152
153 string l_process_name;
154 if (probes[i]->has_process)
155 {
156 if (probes[i]->process_name.length() == 0)
157 {
158 wordexp_t words;
159 int rc = wordexp(s.cmd.c_str(), &words, WRDE_NOCMD|WRDE_UNDEF);
160 if (rc || words.we_wordc <= 0)
161 throw semantic_error(_("unspecified process probe is invalid without a -c COMMAND"));
162 l_process_name = words.we_wordv[0];
163 wordfree (& words);
164 }
165 else
166 l_process_name = probes[i]->process_name;
167
168 s.op->line() << " .tgt={";
169 s.op->line() << " .purpose=\"perfctr\",";
170 s.op->line() << " .procname=\"" << l_process_name << "\",";
171 s.op->line() << " .pid=0,";
172 s.op->line() << " .callback=&_stp_perf_probe_cb,";
173 s.op->line() << " },";
174 s.op->newline() << ".per_thread=" << "1, ";
175 }
176 else
177 s.op->newline() << ".per_thread=" << "0, ";
178 s.op->newline(-1) << "},";
179 }
180 s.op->newline(-1) << "};";
181 s.op->newline();
182
183 /* wrapper functions */
184 for (unsigned i=0; i < probes.size(); i++)
185 {
186 s.op->newline() << "#ifdef STAPCONF_PERF_HANDLER_NMI";
187 s.op->newline() << "static void enter_perf_probe_" << i
188 << " (struct perf_event *e, int nmi, "
189 << "struct perf_sample_data *data, "
190 << "struct pt_regs *regs)";
191 s.op->newline() << "#else";
192 s.op->newline() << "static void enter_perf_probe_" << i
193 << " (struct perf_event *e, "
194 << "struct perf_sample_data *data, "
195 << "struct pt_regs *regs)";
196 s.op->newline() << "#endif";
197 s.op->newline() << "{";
198 s.op->newline(1) << "handle_perf_probe(" << i << ", regs);";
199 s.op->newline(-1) << "}";
200 }
201 s.op->newline();
202
203 s.op->newline() << "static void handle_perf_probe (unsigned i, struct pt_regs *regs)";
204 s.op->newline() << "{";
205 s.op->newline(1) << "struct stap_perf_probe* stp = & stap_perf_probes [i];";
206 common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "stp->probe",
207 "stp_probe_type_perf");
208 s.op->newline() << "if (user_mode(regs)) {";
209 s.op->newline(1)<< "c->user_mode_p = 1;";
210 s.op->newline() << "c->uregs = regs;";
211 s.op->newline(-1) << "} else {";
212 s.op->newline(1) << "c->kregs = regs;";
213 s.op->newline(-1) << "}";
214
215 s.op->newline() << "(*stp->probe->ph) (c);";
216 common_probe_entryfn_epilogue (s, true);
217 s.op->newline(-1) << "}";
218
219 s.op->newline();
220 s.op->newline() << "#include \"linux/perf.c\"";
221 s.op->newline();
222 }
223
224
225 void
226 perf_derived_probe_group::emit_module_init (systemtap_session& s)
227 {
228 if (probes.empty()) return;
229
230 s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
231 s.op->newline(1) << "struct stap_perf_probe* stp = & stap_perf_probes [i];";
232 s.op->newline() << "rc = _stp_perf_init(stp, 0);";
233 s.op->newline() << "if (rc) {";
234 s.op->newline(1) << "probe_point = stp->probe->pp;";
235 s.op->newline() << "for (j=0; j<i; j++) {";
236 s.op->newline(1) << "_stp_perf_del(& stap_perf_probes [j]);";
237 s.op->newline(-1) << "}"; // for unwind loop
238 s.op->newline() << "break;";
239 s.op->newline(-1) << "}"; // if-error
240 s.op->newline() << "rc = stap_register_task_finder_target(&stp->tgt);";
241 s.op->newline(-1) << "}"; // for loop
242 }
243
244
245 void
246 perf_derived_probe_group::emit_module_exit (systemtap_session& s)
247 {
248 if (probes.empty()) return;
249
250 s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
251 s.op->newline(1) << "_stp_perf_del(& stap_perf_probes [i]);";
252 s.op->newline(-1) << "}"; // for loop
253 }
254
255
256 struct perf_builder: public derived_probe_builder
257 {
258 virtual void build(systemtap_session & sess,
259 probe * base, probe_point * location,
260 literal_map_t const & parameters,
261 vector<derived_probe *> & finished_results);
262
263 static void register_patterns(systemtap_session& s);
264 };
265
266
267 void
268 perf_builder::build(systemtap_session & sess,
269 probe * base,
270 probe_point * location,
271 literal_map_t const & parameters,
272 vector<derived_probe *> & finished_results)
273 {
274 // XXX need additional version checks too?
275 // --- perhaps look for export of perf_event_create_kernel_counter
276 if (sess.kernel_exports.find("perf_event_create_kernel_counter") == sess.kernel_exports.end())
277 throw semantic_error (_("perf probes not available without exported perf_event_create_kernel_counter"));
278 if (sess.kernel_config["CONFIG_PERF_EVENTS"] != "y")
279 throw semantic_error (_("perf probes not available without CONFIG_PERF_EVENTS"));
280
281 int64_t type;
282 bool has_type = get_param(parameters, TOK_TYPE, type);
283 assert(has_type);
284
285 int64_t config;
286 bool has_config = get_param(parameters, TOK_CONFIG, config);
287 assert(has_config);
288
289 int64_t period;
290 bool has_period = get_param(parameters, TOK_SAMPLE, period);
291 if (!has_period)
292 period = 1000000; // XXX: better parametrize this default
293 else if (period < 1)
294 throw semantic_error(_("invalid perf sample period ") + lex_cast(period),
295 parameters.find(TOK_SAMPLE)->second->tok);
296 bool proc_p;
297 string proc_n;
298 proc_p = has_null_param(parameters, TOK_PROCESS)
299 || get_param(parameters, TOK_PROCESS, proc_n);
300
301 if (sess.verbose > 1)
302 clog << _F("perf probe type=%" PRId64 " config=%" PRId64 " period=%" PRId64, type, config, period) << endl;
303
304 finished_results.push_back
305 (new perf_derived_probe(base, location, type, config, period, proc_p, proc_n));
306 }
307
308
309 void
310 register_tapset_perf(systemtap_session& s)
311 {
312 // NB: at this point, the binding is *not* unprivileged.
313
314 derived_probe_builder *builder = new perf_builder();
315 match_node* perf = s.pattern_root->bind(TOK_PERF);
316
317 match_node* event = perf->bind_num(TOK_TYPE)->bind_num(TOK_CONFIG);
318 event->bind(builder);
319 event->bind_num(TOK_SAMPLE)->bind(builder);
320 event->bind_str(TOK_PROCESS)->bind(builder);
321 event->bind(TOK_PROCESS)->bind(builder);
322 }
323
324 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.055468 seconds and 6 git commands to generate.