]>
Commit | Line | Data |
---|---|---|
93646f4d JS |
1 | // tapset for timers |
2 | // Copyright (C) 2005-2009 Red Hat Inc. | |
3 | // Copyright (C) 2005-2007 Intel Corporation. | |
4 | // Copyright (C) 2008 James.Bottomley@HansenPartnership.com | |
5 | // | |
6 | // This file is part of systemtap, and is free software. You can | |
7 | // redistribute it and/or modify it under the terms of the GNU General | |
8 | // Public License (GPL); either version 2, or (at your option) any | |
9 | // later version. | |
10 | ||
11 | ||
12 | #include "session.h" | |
13 | #include "tapsets.h" | |
14 | #include "task_finder.h" | |
15 | #include "translate.h" | |
16 | #include "util.h" | |
17 | ||
18 | #include <cstring> | |
19 | #include <string> | |
20 | ||
21 | ||
22 | using namespace std; | |
23 | using namespace __gnu_cxx; | |
24 | ||
25 | ||
4627ed58 JS |
26 | static const string TOK_PROCESS("process"); |
27 | static const string TOK_INSN("insn"); | |
28 | static const string TOK_BLOCK("block"); | |
93646f4d JS |
29 | |
30 | ||
31 | // ------------------------------------------------------------------------ | |
32 | // itrace user-space probes | |
33 | // ------------------------------------------------------------------------ | |
34 | ||
35 | ||
36 | struct itrace_derived_probe: public derived_probe | |
37 | { | |
38 | bool has_path; | |
39 | string path; | |
40 | int64_t pid; | |
41 | int single_step; | |
42 | ||
43 | itrace_derived_probe (systemtap_session &s, probe* p, probe_point* l, | |
44 | bool hp, string &pn, int64_t pd, int ss | |
45 | ); | |
46 | void join_group (systemtap_session& s); | |
47 | }; | |
48 | ||
49 | ||
50 | struct itrace_derived_probe_group: public generic_dpg<itrace_derived_probe> | |
51 | { | |
52 | private: | |
53 | map<string, vector<itrace_derived_probe*> > probes_by_path; | |
54 | typedef map<string, vector<itrace_derived_probe*> >::iterator p_b_path_iterator; | |
55 | map<int64_t, vector<itrace_derived_probe*> > probes_by_pid; | |
56 | typedef map<int64_t, vector<itrace_derived_probe*> >::iterator p_b_pid_iterator; | |
57 | unsigned num_probes; | |
58 | ||
59 | void emit_probe_decl (systemtap_session& s, itrace_derived_probe *p); | |
60 | ||
61 | public: | |
62 | itrace_derived_probe_group(): num_probes(0) { } | |
63 | ||
64 | void enroll (itrace_derived_probe* probe); | |
65 | void emit_module_decls (systemtap_session& s); | |
66 | void emit_module_init (systemtap_session& s); | |
67 | void emit_module_exit (systemtap_session& s); | |
68 | }; | |
69 | ||
70 | ||
71 | itrace_derived_probe::itrace_derived_probe (systemtap_session &s, | |
72 | probe* p, probe_point* l, | |
73 | bool hp, string &pn, int64_t pd, | |
74 | int ss | |
75 | ): | |
76 | derived_probe(p, l), has_path(hp), path(pn), pid(pd), single_step(ss) | |
77 | { | |
e34d5d13 | 78 | if (s.kernel_config["CONFIG_UTRACE"] != string("y")) |
efee9a98 | 79 | throw semantic_error (_("process probes not available without kernel CONFIG_UTRACE")); |
93646f4d JS |
80 | } |
81 | ||
82 | ||
83 | void | |
84 | itrace_derived_probe::join_group (systemtap_session& s) | |
85 | { | |
86 | if (! s.itrace_derived_probes) | |
87 | s.itrace_derived_probes = new itrace_derived_probe_group (); | |
88 | ||
89 | s.itrace_derived_probes->enroll (this); | |
90 | ||
91 | enable_task_finder(s); | |
92 | } | |
93 | ||
94 | struct itrace_builder: public derived_probe_builder | |
95 | { | |
96 | itrace_builder() {} | |
97 | virtual void build(systemtap_session & sess, | |
98 | probe * base, | |
99 | probe_point * location, | |
100 | std::map<std::string, literal *> const & parameters, | |
101 | vector<derived_probe *> & finished_results) | |
102 | { | |
103 | string path; | |
104 | int64_t pid = 0; | |
105 | int single_step; | |
106 | ||
107 | bool has_path = get_param (parameters, TOK_PROCESS, path); | |
108 | bool has_pid = get_param (parameters, TOK_PROCESS, pid); | |
109 | // XXX: PR 6445 needs !has_path && !has_pid support | |
110 | assert (has_path || has_pid); | |
111 | ||
112 | single_step = ! has_null_param (parameters, TOK_BLOCK); | |
113 | ||
114 | // If we have a path, we need to validate it. | |
115 | if (has_path) | |
671ceda8 FCE |
116 | { |
117 | path = find_executable (path); | |
118 | sess.unwindsym_modules.insert (path); | |
119 | } | |
93646f4d JS |
120 | |
121 | finished_results.push_back(new itrace_derived_probe(sess, base, location, | |
122 | has_path, path, pid, | |
123 | single_step | |
124 | )); | |
125 | } | |
126 | }; | |
127 | ||
128 | ||
129 | void | |
130 | itrace_derived_probe_group::enroll (itrace_derived_probe* p) | |
131 | { | |
132 | if (p->has_path) | |
133 | probes_by_path[p->path].push_back(p); | |
134 | else | |
135 | probes_by_pid[p->pid].push_back(p); | |
136 | num_probes++; | |
137 | ||
138 | // XXX: multiple exec probes (for instance) for the same path (or | |
139 | // pid) should all share a itrace report function, and have their | |
140 | // handlers executed sequentially. | |
141 | } | |
142 | ||
143 | ||
144 | void | |
145 | itrace_derived_probe_group::emit_probe_decl (systemtap_session& s, | |
146 | itrace_derived_probe *p) | |
147 | { | |
148 | s.op->newline() << "{"; | |
149 | s.op->line() << " .tgt={"; | |
150 | ||
151 | if (p->has_path) | |
152 | { | |
b6921d59 | 153 | s.op->line() << " .procname=\"" << p->path << "\","; |
93646f4d JS |
154 | s.op->line() << " .pid=0,"; |
155 | } | |
156 | else | |
157 | { | |
b6921d59 | 158 | s.op->line() << " .procname=NULL,"; |
93646f4d JS |
159 | s.op->line() << " .pid=" << p->pid << ","; |
160 | } | |
161 | ||
162 | s.op->line() << " .callback=&_stp_itrace_probe_cb,"; | |
163 | s.op->line() << " },"; | |
faea5e16 | 164 | s.op->line() << " .probe=" << common_probe_init (p) << ","; |
93646f4d | 165 | s.op->line() << " .single_step=" << p->single_step << ","; |
93646f4d JS |
166 | s.op->line() << " },"; |
167 | } | |
168 | ||
169 | ||
170 | void | |
171 | itrace_derived_probe_group::emit_module_decls (systemtap_session& s) | |
172 | { | |
173 | if (probes_by_path.empty() && probes_by_pid.empty()) | |
174 | return; | |
175 | ||
176 | s.op->newline(); | |
177 | s.op->newline() << "/* ---- itrace probes ---- */"; | |
178 | ||
179 | s.op->newline() << "struct stap_itrace_probe {"; | |
180 | s.op->indent(1); | |
181 | s.op->newline() << "struct stap_task_finder_target tgt;"; | |
26e63673 | 182 | s.op->newline() << "struct stap_probe * const probe;"; |
93646f4d JS |
183 | s.op->newline() << "int single_step;"; |
184 | s.op->newline(-1) << "};"; | |
185 | s.op->newline() << "static void enter_itrace_probe(struct stap_itrace_probe *p, struct pt_regs *regs, void *data);"; | |
186 | s.op->newline() << "#include \"itrace.c\""; | |
187 | ||
188 | // output routine to call itrace probe | |
189 | s.op->newline() << "static void enter_itrace_probe(struct stap_itrace_probe *p, struct pt_regs *regs, void *data) {"; | |
190 | s.op->indent(1); | |
191 | ||
6eefe942 MW |
192 | common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "p->probe", |
193 | "_STP_PROBE_HANDLER_ITRACE"); | |
d9aed31e | 194 | s.op->newline() << "c->uregs = regs;"; |
92c25572 | 195 | s.op->newline() << "c->probe_flags |= _STP_PROBE_STATE_USER_MODE;"; |
93646f4d JS |
196 | |
197 | // call probe function | |
26e63673 | 198 | s.op->newline() << "(*p->probe->ph) (c);"; |
93646f4d JS |
199 | common_probe_entryfn_epilogue (s.op); |
200 | ||
201 | s.op->newline() << "return;"; | |
202 | s.op->newline(-1) << "}"; | |
203 | ||
204 | // Output task finder callback routine that gets called for all | |
205 | // itrace probe types. | |
206 | s.op->newline() << "static int _stp_itrace_probe_cb(struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {"; | |
207 | s.op->indent(1); | |
208 | s.op->newline() << "int rc = 0;"; | |
209 | s.op->newline() << "struct stap_itrace_probe *p = container_of(tgt, struct stap_itrace_probe, tgt);"; | |
210 | ||
211 | s.op->newline() << "if (register_p) "; | |
212 | s.op->indent(1); | |
213 | ||
c8a44dea | 214 | s.op->newline() << "rc = usr_itrace_init(p->single_step, tsk, p);"; |
93646f4d | 215 | s.op->newline(-1) << "else"; |
4f600a5f | 216 | s.op->newline(1) << "remove_usr_itrace_info(find_itrace_info(tsk));"; |
93646f4d JS |
217 | s.op->newline(-1) << "return rc;"; |
218 | s.op->newline(-1) << "}"; | |
219 | ||
93646f4d JS |
220 | s.op->newline() << "static struct stap_itrace_probe stap_itrace_probes[] = {"; |
221 | s.op->indent(1); | |
222 | ||
223 | // Set up 'process(PATH)' probes | |
224 | if (! probes_by_path.empty()) | |
225 | { | |
226 | for (p_b_path_iterator it = probes_by_path.begin(); | |
227 | it != probes_by_path.end(); it++) | |
228 | { | |
229 | for (unsigned i = 0; i < it->second.size(); i++) | |
230 | { | |
231 | itrace_derived_probe *p = it->second[i]; | |
232 | emit_probe_decl(s, p); | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
237 | // Set up 'process(PID)' probes | |
238 | if (! probes_by_pid.empty()) | |
239 | { | |
240 | for (p_b_pid_iterator it = probes_by_pid.begin(); | |
241 | it != probes_by_pid.end(); it++) | |
242 | { | |
243 | for (unsigned i = 0; i < it->second.size(); i++) | |
244 | { | |
245 | itrace_derived_probe *p = it->second[i]; | |
246 | emit_probe_decl(s, p); | |
247 | } | |
248 | } | |
249 | } | |
250 | s.op->newline(-1) << "};"; | |
251 | } | |
252 | ||
253 | ||
254 | void | |
255 | itrace_derived_probe_group::emit_module_init (systemtap_session& s) | |
256 | { | |
257 | if (probes_by_path.empty() && probes_by_pid.empty()) | |
258 | return; | |
259 | ||
93646f4d JS |
260 | s.op->newline(); |
261 | s.op->newline() << "/* ---- itrace probes ---- */"; | |
262 | ||
263 | s.op->newline() << "for (i=0; i<" << num_probes << "; i++) {"; | |
264 | s.op->indent(1); | |
265 | s.op->newline() << "struct stap_itrace_probe *p = &stap_itrace_probes[i];"; | |
266 | ||
267 | // 'arch_has_single_step' needs to be defined for either single step mode | |
268 | // or branch mode. | |
269 | s.op->newline() << "if (!arch_has_single_step()) {"; | |
270 | s.op->indent(1); | |
271 | s.op->newline() << "_stp_error (\"insn probe init: arch does not support step mode\");"; | |
272 | s.op->newline() << "rc = -EPERM;"; | |
273 | s.op->newline() << "break;"; | |
274 | s.op->newline(-1) << "}"; | |
275 | s.op->newline() << "if (!p->single_step && !arch_has_block_step()) {"; | |
276 | s.op->indent(1); | |
277 | s.op->newline() << "_stp_error (\"insn probe init: arch does not support block step mode\");"; | |
278 | s.op->newline() << "rc = -EPERM;"; | |
279 | s.op->newline() << "break;"; | |
280 | s.op->newline(-1) << "}"; | |
281 | ||
282 | s.op->newline() << "rc = stap_register_task_finder_target(&p->tgt);"; | |
283 | s.op->newline(-1) << "}"; | |
284 | } | |
285 | ||
286 | ||
287 | void | |
288 | itrace_derived_probe_group::emit_module_exit (systemtap_session& s) | |
289 | { | |
290 | if (probes_by_path.empty() && probes_by_pid.empty()) return; | |
291 | s.op->newline(); | |
292 | s.op->newline() << "/* ---- itrace probes ---- */"; | |
293 | s.op->newline() << "cleanup_usr_itrace();"; | |
294 | } | |
295 | ||
296 | void | |
297 | register_tapset_itrace(systemtap_session& s) | |
298 | { | |
299 | match_node* root = s.pattern_root; | |
300 | derived_probe_builder *builder = new itrace_builder(); | |
301 | ||
d2c9ec9b | 302 | root->bind_str(TOK_PROCESS)->bind(TOK_INSN) |
d2c9ec9b DB |
303 | ->bind(builder); |
304 | root->bind_num(TOK_PROCESS)->bind(TOK_INSN) | |
d2c9ec9b DB |
305 | ->bind(builder); |
306 | root->bind_str(TOK_PROCESS)->bind(TOK_INSN)->bind(TOK_BLOCK) | |
d2c9ec9b DB |
307 | ->bind(builder); |
308 | root->bind_num(TOK_PROCESS)->bind(TOK_INSN)->bind(TOK_BLOCK) | |
d2c9ec9b | 309 | ->bind(builder); |
93646f4d JS |
310 | } |
311 | ||
312 | ||
313 | ||
314 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |