]>
Commit | Line | Data |
---|---|---|
7a212aa8 JS |
1 | // tapset for procfs |
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 "translate.h" | |
15 | #include "util.h" | |
16 | ||
17 | #include <cstring> | |
18 | #include <string> | |
19 | ||
20 | ||
21 | using namespace std; | |
22 | using namespace __gnu_cxx; | |
23 | ||
24 | ||
4627ed58 JS |
25 | static const string TOK_PROCFS("procfs"); |
26 | static const string TOK_READ("read"); | |
27 | static const string TOK_WRITE("write"); | |
7a212aa8 JS |
28 | |
29 | ||
30 | // ------------------------------------------------------------------------ | |
31 | // procfs file derived probes | |
32 | // ------------------------------------------------------------------------ | |
33 | ||
34 | ||
35 | struct procfs_derived_probe: public derived_probe | |
36 | { | |
37 | string path; | |
38 | bool write; | |
39 | bool target_symbol_seen; | |
40 | ||
41 | procfs_derived_probe (systemtap_session &, probe* p, probe_point* l, string ps, bool w); | |
42 | void join_group (systemtap_session& s); | |
43 | }; | |
44 | ||
45 | ||
46 | struct procfs_probe_set | |
47 | { | |
48 | procfs_derived_probe* read_probe; | |
49 | procfs_derived_probe* write_probe; | |
50 | ||
51 | procfs_probe_set () : read_probe (NULL), write_probe (NULL) {} | |
52 | }; | |
53 | ||
54 | ||
55 | struct procfs_derived_probe_group: public generic_dpg<procfs_derived_probe> | |
56 | { | |
57 | private: | |
58 | map<string, procfs_probe_set*> probes_by_path; | |
59 | typedef map<string, procfs_probe_set*>::iterator p_b_p_iterator; | |
60 | bool has_read_probes; | |
61 | bool has_write_probes; | |
62 | ||
63 | public: | |
64 | procfs_derived_probe_group () : | |
65 | has_read_probes(false), has_write_probes(false) {} | |
66 | ||
67 | void enroll (procfs_derived_probe* probe); | |
68 | void emit_module_decls (systemtap_session& s); | |
69 | void emit_module_init (systemtap_session& s); | |
70 | void emit_module_exit (systemtap_session& s); | |
71 | }; | |
72 | ||
73 | ||
74 | struct procfs_var_expanding_visitor: public var_expanding_visitor | |
75 | { | |
76 | procfs_var_expanding_visitor(systemtap_session& s, const string& pn, | |
77 | string path, bool write_probe): | |
78 | sess (s), probe_name (pn), path (path), write_probe (write_probe), | |
79 | target_symbol_seen (false) {} | |
80 | ||
81 | systemtap_session& sess; | |
82 | string probe_name; | |
83 | string path; | |
84 | bool write_probe; | |
85 | bool target_symbol_seen; | |
86 | ||
87 | void visit_target_symbol (target_symbol* e); | |
88 | }; | |
89 | ||
90 | ||
91 | procfs_derived_probe::procfs_derived_probe (systemtap_session &s, probe* p, | |
92 | probe_point* l, string ps, bool w): | |
93 | derived_probe(p, l), path(ps), write(w), target_symbol_seen(false) | |
94 | { | |
95 | // Expand local variables in the probe body | |
96 | procfs_var_expanding_visitor v (s, name, path, write); | |
8b095b45 | 97 | v.replace (this->body); |
7a212aa8 JS |
98 | target_symbol_seen = v.target_symbol_seen; |
99 | } | |
100 | ||
101 | ||
102 | void | |
103 | procfs_derived_probe::join_group (systemtap_session& s) | |
104 | { | |
105 | if (! s.procfs_derived_probes) | |
8e0049e8 DS |
106 | { |
107 | s.procfs_derived_probes = new procfs_derived_probe_group (); | |
108 | ||
109 | // Make sure 'struct _stp_procfs_data' is defined early. | |
110 | embeddedcode *ec = new embeddedcode; | |
111 | ec->tok = NULL; | |
112 | ec->code = string("struct _stp_procfs_data {\n") | |
23b7dbfa | 113 | + string(" char *buffer;\n") |
8e0049e8 DS |
114 | + string(" unsigned long count;\n") |
115 | + string("};\n"); | |
116 | s.embeds.push_back(ec); | |
117 | } | |
7a212aa8 JS |
118 | s.procfs_derived_probes->enroll (this); |
119 | } | |
120 | ||
121 | ||
122 | void | |
123 | procfs_derived_probe_group::enroll (procfs_derived_probe* p) | |
124 | { | |
125 | procfs_probe_set *pset; | |
126 | ||
127 | if (probes_by_path.count(p->path) == 0) | |
128 | { | |
129 | pset = new procfs_probe_set; | |
130 | probes_by_path[p->path] = pset; | |
131 | } | |
132 | else | |
133 | { | |
134 | pset = probes_by_path[p->path]; | |
135 | ||
136 | // You can only specify 1 read and 1 write probe. | |
137 | if (p->write && pset->write_probe != NULL) | |
138 | throw semantic_error("only one write procfs probe can exist for procfs path \"" + p->path + "\""); | |
139 | else if (! p->write && pset->read_probe != NULL) | |
140 | throw semantic_error("only one read procfs probe can exist for procfs path \"" + p->path + "\""); | |
141 | ||
142 | // XXX: multiple writes should be acceptable | |
143 | } | |
144 | ||
145 | if (p->write) | |
146 | { | |
147 | pset->write_probe = p; | |
148 | has_write_probes = true; | |
149 | } | |
150 | else | |
151 | { | |
152 | pset->read_probe = p; | |
153 | has_read_probes = true; | |
154 | } | |
155 | } | |
156 | ||
157 | ||
158 | void | |
159 | procfs_derived_probe_group::emit_module_decls (systemtap_session& s) | |
160 | { | |
161 | if (probes_by_path.empty()) | |
162 | return; | |
163 | ||
164 | s.op->newline() << "/* ---- procfs probes ---- */"; | |
165 | s.op->newline() << "#include \"procfs.c\""; | |
23b7dbfa | 166 | s.op->newline() << "#include \"procfs-probes.c\""; |
7a212aa8 JS |
167 | |
168 | // Emit the procfs probe data list | |
23b7dbfa | 169 | s.op->newline() << "static struct stap_procfs_probe stap_procfs_probes[] = {"; |
7a212aa8 JS |
170 | s.op->indent(1); |
171 | ||
172 | for (p_b_p_iterator it = probes_by_path.begin(); it != probes_by_path.end(); | |
173 | it++) | |
174 | { | |
175 | procfs_probe_set *pset = it->second; | |
176 | ||
177 | s.op->newline() << "{"; | |
178 | s.op->line() << " .path=" << lex_cast_qstring (it->first) << ","; | |
179 | ||
180 | if (pset->read_probe != NULL) | |
181 | { | |
182 | s.op->line() << " .read_pp=" | |
183 | << lex_cast_qstring (*pset->read_probe->sole_location()) | |
184 | << ","; | |
185 | s.op->line() << " .read_ph=&" << pset->read_probe->name << ","; | |
186 | } | |
187 | else | |
188 | { | |
189 | s.op->line() << " .read_pp=NULL,"; | |
190 | s.op->line() << " .read_ph=NULL,"; | |
191 | } | |
192 | ||
193 | if (pset->write_probe != NULL) | |
194 | { | |
195 | s.op->line() << " .write_pp=" | |
196 | << lex_cast_qstring (*pset->write_probe->sole_location()) | |
197 | << ","; | |
198 | s.op->line() << " .write_ph=&" << pset->write_probe->name; | |
199 | } | |
200 | else | |
201 | { | |
202 | s.op->line() << " .write_pp=NULL,"; | |
203 | s.op->line() << " .write_ph=NULL"; | |
204 | } | |
205 | s.op->line() << " },"; | |
206 | } | |
207 | s.op->newline(-1) << "};"; | |
208 | ||
23b7dbfa DS |
209 | // Output routine to fill in the buffer with our data. Note that we |
210 | // need to do this even in the case where we have no read probes, | |
211 | // but we can skip most of it then. | |
212 | s.op->newline(); | |
213 | ||
214 | s.op->newline() << "static int _stp_proc_fill_read_buffer(struct stap_procfs_probe *spp) {"; | |
215 | s.op->indent(1); | |
7a212aa8 JS |
216 | if (has_read_probes) |
217 | { | |
8e0049e8 | 218 | s.op->newline() << "struct _stp_procfs_data pdata;"; |
7a212aa8 JS |
219 | |
220 | common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "spp->read_pp"); | |
221 | ||
23b7dbfa | 222 | s.op->newline() << "pdata.buffer = spp->buffer;"; |
7a212aa8 | 223 | s.op->newline() << "if (c->data == NULL)"; |
8e0049e8 | 224 | s.op->newline(1) << "c->data = &pdata;"; |
7a212aa8 JS |
225 | s.op->newline(-1) << "else {"; |
226 | ||
227 | s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {"; | |
228 | s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);"; | |
229 | s.op->newline() << "_stp_exit ();"; | |
230 | s.op->newline(-1) << "}"; | |
231 | s.op->newline() << "atomic_dec (& c->busy);"; | |
232 | s.op->newline() << "goto probe_epilogue;"; | |
233 | s.op->newline(-1) << "}"; | |
234 | ||
8e0049e8 | 235 | // call probe function |
7a212aa8 JS |
236 | s.op->newline() << "(*spp->read_ph) (c);"; |
237 | ||
23b7dbfa | 238 | // Note that _procfs_value_set copied string data into spp->buffer |
7a212aa8 | 239 | s.op->newline() << "c->data = NULL;"; |
23b7dbfa DS |
240 | s.op->newline() << "spp->needs_fill = 0;"; |
241 | s.op->newline() << "spp->count = strlen(spp->buffer);"; | |
242 | ||
7a212aa8 | 243 | common_probe_entryfn_epilogue (s.op); |
7a212aa8 | 244 | |
23b7dbfa DS |
245 | s.op->newline() << "if (spp->needs_fill) {"; |
246 | s.op->newline(1) << "spp->needs_fill = 0;"; | |
247 | s.op->newline() << "return -EIO;"; | |
7a212aa8 JS |
248 | s.op->newline(-1) << "}"; |
249 | } | |
23b7dbfa DS |
250 | s.op->newline() << "return 0;"; |
251 | s.op->newline(-1) << "}"; | |
252 | ||
253 | // Output routine to read data. Note that we need to do this even | |
254 | // in the case where we have no write probes, but we can skip most | |
255 | // of it then. | |
256 | s.op->newline() << "static int _stp_process_write_buffer(struct stap_procfs_probe *spp, const char __user *buf, size_t count) {"; | |
257 | s.op->indent(1); | |
258 | s.op->newline() << "int retval = 0;"; | |
7a212aa8 JS |
259 | if (has_write_probes) |
260 | { | |
8e0049e8 | 261 | s.op->newline() << "struct _stp_procfs_data pdata;"; |
7a212aa8 JS |
262 | |
263 | common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "spp->write_pp"); | |
264 | ||
23b7dbfa | 265 | s.op->newline() << "if (count >= MAXSTRINGLEN)"; |
7a212aa8 | 266 | s.op->newline(1) << "count = MAXSTRINGLEN - 1;"; |
8e0049e8 | 267 | s.op->indent(-1); |
23b7dbfa | 268 | s.op->newline() << "pdata.buffer = (char *)buf;"; |
8e0049e8 | 269 | s.op->newline() << "pdata.count = count;"; |
7a212aa8 JS |
270 | |
271 | s.op->newline() << "if (c->data == NULL)"; | |
8e0049e8 | 272 | s.op->newline(1) << "c->data = &pdata;"; |
7a212aa8 JS |
273 | s.op->newline(-1) << "else {"; |
274 | ||
275 | s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {"; | |
276 | s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);"; | |
277 | s.op->newline() << "_stp_exit ();"; | |
278 | s.op->newline(-1) << "}"; | |
279 | s.op->newline() << "atomic_dec (& c->busy);"; | |
280 | s.op->newline() << "goto probe_epilogue;"; | |
281 | s.op->newline(-1) << "}"; | |
282 | ||
8e0049e8 | 283 | // call probe function |
7a212aa8 JS |
284 | s.op->newline() << "(*spp->write_ph) (c);"; |
285 | ||
286 | s.op->newline() << "c->data = NULL;"; | |
23b7dbfa DS |
287 | s.op->newline() << "if (c->last_error == 0) {"; |
288 | s.op->newline(1) << "retval = count;"; | |
7a212aa8 | 289 | s.op->newline(-1) << "}"; |
23b7dbfa DS |
290 | |
291 | common_probe_entryfn_epilogue (s.op); | |
7a212aa8 | 292 | } |
23b7dbfa DS |
293 | s.op->newline() << "return retval;"; |
294 | s.op->newline(-1) << "}"; | |
7a212aa8 JS |
295 | } |
296 | ||
297 | ||
298 | void | |
299 | procfs_derived_probe_group::emit_module_init (systemtap_session& s) | |
300 | { | |
301 | if (probes_by_path.empty()) | |
302 | return; | |
303 | ||
304 | s.op->newline() << "for (i = 0; i < " << probes_by_path.size() << "; i++) {"; | |
305 | s.op->newline(1) << "struct stap_procfs_probe *spp = &stap_procfs_probes[i];"; | |
306 | ||
307 | s.op->newline() << "if (spp->read_pp)"; | |
308 | s.op->newline(1) << "probe_point = spp->read_pp;"; | |
309 | s.op->newline(-1) << "else"; | |
310 | s.op->newline(1) << "probe_point = spp->write_pp;"; | |
23b7dbfa | 311 | s.op->indent(-1); |
7a212aa8 | 312 | |
23b7dbfa DS |
313 | s.op->newline() << "_spp_lock_init(spp);"; |
314 | s.op->newline() << "rc = _stp_create_procfs(spp->path, i, &_stp_proc_fops);"; | |
7a212aa8 JS |
315 | |
316 | s.op->newline() << "if (rc) {"; | |
317 | s.op->newline(1) << "_stp_close_procfs();"; | |
23b7dbfa DS |
318 | |
319 | s.op->newline() << "for (i = 0; i < " << probes_by_path.size() << "; i++) {"; | |
320 | s.op->newline(1) << "spp = &stap_procfs_probes[i];"; | |
321 | s.op->newline() << "_spp_lock_shutdown(spp);"; | |
322 | s.op->newline(-1) << "}"; | |
7a212aa8 JS |
323 | s.op->newline() << "break;"; |
324 | s.op->newline(-1) << "}"; | |
325 | ||
7a212aa8 JS |
326 | s.op->newline() << "_stp_procfs_files[i]->data = spp;"; |
327 | s.op->newline(-1) << "}"; // for loop | |
328 | } | |
329 | ||
330 | ||
331 | void | |
332 | procfs_derived_probe_group::emit_module_exit (systemtap_session& s) | |
333 | { | |
334 | if (probes_by_path.empty()) | |
335 | return; | |
336 | ||
337 | s.op->newline() << "_stp_close_procfs();"; | |
23b7dbfa DS |
338 | s.op->newline() << "for (i = 0; i < " << probes_by_path.size() << "; i++) {"; |
339 | s.op->newline(1) << "struct stap_procfs_probe *spp = &stap_procfs_probes[i];"; | |
340 | s.op->newline() << "_spp_lock_shutdown(spp);"; | |
341 | s.op->newline(-1) << "}"; | |
7a212aa8 JS |
342 | } |
343 | ||
344 | ||
345 | void | |
346 | procfs_var_expanding_visitor::visit_target_symbol (target_symbol* e) | |
347 | { | |
348 | assert(e->base_name.size() > 0 && e->base_name[0] == '$'); | |
349 | ||
350 | if (e->base_name != "$value") | |
351 | throw semantic_error ("invalid target symbol for procfs probe, $value expected", | |
352 | e->tok); | |
353 | ||
dc5a09fc | 354 | e->assert_no_components("procfs"); |
7a212aa8 JS |
355 | |
356 | bool lvalue = is_active_lvalue(e); | |
357 | if (write_probe && lvalue) | |
358 | throw semantic_error("procfs $value variable is read-only in a procfs write probe", e->tok); | |
359 | else if (! write_probe && ! lvalue) | |
360 | throw semantic_error("procfs $value variable cannot be read in a procfs read probe", e->tok); | |
361 | ||
03c75a4a JS |
362 | if (e->addressof) |
363 | throw semantic_error("cannot take address of procfs variable", e->tok); | |
364 | ||
7a212aa8 JS |
365 | // Remember that we've seen a target variable. |
366 | target_symbol_seen = true; | |
367 | ||
368 | // Synthesize a function. | |
369 | functiondecl *fdecl = new functiondecl; | |
370 | fdecl->tok = e->tok; | |
371 | embeddedcode *ec = new embeddedcode; | |
372 | ec->tok = e->tok; | |
373 | ||
23b7dbfa | 374 | string fname = (string(lvalue ? "_procfs_value_set" : "_procfs_value_get")); |
7a212aa8 JS |
375 | string locvalue = "CONTEXT->data"; |
376 | ||
377 | if (! lvalue) | |
6390a48a DS |
378 | ec->code = string(" struct _stp_procfs_data *data = (struct _stp_procfs_data *)(") + locvalue + string("); /* pure */\n") |
379 | ||
380 | + string(" _stp_copy_from_user(THIS->__retvalue, data->buffer, data->count);\n") | |
381 | + string(" THIS->__retvalue[data->count] = '\\0';\n"); | |
7a212aa8 | 382 | else |
8e0049e8 DS |
383 | ec->code = string("int bytes = 0;\n") |
384 | + string(" struct _stp_procfs_data *data = (struct _stp_procfs_data *)(") + locvalue + string(");\n") | |
385 | + string(" bytes = strnlen(THIS->value, MAXSTRINGLEN - 1);\n") | |
23b7dbfa DS |
386 | + string(" memcpy((void *)data->buffer, THIS->value, bytes);\n") |
387 | + string(" data->buffer[bytes] = '\\0';\n") | |
8e0049e8 | 388 | + string(" data->count = bytes;\n"); |
7a212aa8 JS |
389 | |
390 | fdecl->name = fname; | |
391 | fdecl->body = ec; | |
392 | fdecl->type = pe_string; | |
393 | ||
394 | if (lvalue) | |
395 | { | |
396 | // Modify the fdecl so it carries a single pe_string formal | |
397 | // argument called "value". | |
398 | ||
399 | vardecl *v = new vardecl; | |
400 | v->type = pe_string; | |
401 | v->name = "value"; | |
402 | v->tok = e->tok; | |
403 | fdecl->formal_args.push_back(v); | |
404 | } | |
405 | sess.functions[fdecl->name]=fdecl; | |
406 | ||
407 | // Synthesize a functioncall. | |
408 | functioncall* n = new functioncall; | |
409 | n->tok = e->tok; | |
410 | n->function = fname; | |
411 | n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session | |
412 | ||
413 | if (lvalue) | |
414 | { | |
415 | // Provide the functioncall to our parent, so that it can be | |
416 | // used to substitute for the assignment node immediately above | |
417 | // us. | |
418 | assert(!target_symbol_setter_functioncalls.empty()); | |
419 | *(target_symbol_setter_functioncalls.top()) = n; | |
420 | } | |
421 | ||
422 | provide (n); | |
423 | } | |
424 | ||
425 | ||
426 | struct procfs_builder: public derived_probe_builder | |
427 | { | |
428 | procfs_builder() {} | |
429 | virtual void build(systemtap_session & sess, | |
430 | probe * base, | |
431 | probe_point * location, | |
432 | literal_map_t const & parameters, | |
433 | vector<derived_probe *> & finished_results); | |
434 | }; | |
435 | ||
436 | ||
437 | void | |
438 | procfs_builder::build(systemtap_session & sess, | |
439 | probe * base, | |
440 | probe_point * location, | |
441 | literal_map_t const & parameters, | |
442 | vector<derived_probe *> & finished_results) | |
443 | { | |
444 | string path; | |
445 | bool has_procfs = get_param(parameters, TOK_PROCFS, path); | |
446 | bool has_read = (parameters.find(TOK_READ) != parameters.end()); | |
447 | bool has_write = (parameters.find(TOK_WRITE) != parameters.end()); | |
448 | ||
449 | // If no procfs path, default to "command". The runtime will do | |
450 | // this for us, but if we don't do it here, we'll think the | |
451 | // following 2 probes are attached to different paths: | |
452 | // | |
453 | // probe procfs("command").read {}" | |
454 | // probe procfs.write {} | |
455 | ||
456 | if (! has_procfs) | |
457 | path = "command"; | |
458 | // If we have a path, we need to validate it. | |
459 | else | |
460 | { | |
461 | string::size_type start_pos, end_pos; | |
462 | string component; | |
463 | start_pos = 0; | |
464 | while ((end_pos = path.find('/', start_pos)) != string::npos) | |
465 | { | |
466 | // Make sure it doesn't start with '/'. | |
467 | if (end_pos == 0) | |
468 | throw semantic_error ("procfs path cannot start with a '/'", | |
f1a0157a | 469 | location->components.front()->tok); |
7a212aa8 JS |
470 | |
471 | component = path.substr(start_pos, end_pos - start_pos); | |
472 | // Make sure it isn't empty. | |
473 | if (component.size() == 0) | |
474 | throw semantic_error ("procfs path component cannot be empty", | |
f1a0157a | 475 | location->components.front()->tok); |
7a212aa8 JS |
476 | // Make sure it isn't relative. |
477 | else if (component == "." || component == "..") | |
f1a0157a | 478 | throw semantic_error ("procfs path cannot be relative (and contain '.' or '..')", location->components.front()->tok); |
7a212aa8 JS |
479 | |
480 | start_pos = end_pos + 1; | |
481 | } | |
482 | component = path.substr(start_pos); | |
483 | // Make sure it doesn't end with '/'. | |
484 | if (component.size() == 0) | |
f1a0157a | 485 | throw semantic_error ("procfs path cannot end with a '/'", location->components.front()->tok); |
7a212aa8 JS |
486 | // Make sure it isn't relative. |
487 | else if (component == "." || component == "..") | |
f1a0157a | 488 | throw semantic_error ("procfs path cannot be relative (and contain '.' or '..')", location->components.front()->tok); |
7a212aa8 JS |
489 | } |
490 | ||
491 | if (!(has_read ^ has_write)) | |
f1a0157a | 492 | throw semantic_error ("need read/write component", location->components.front()->tok); |
7a212aa8 JS |
493 | |
494 | finished_results.push_back(new procfs_derived_probe(sess, base, location, | |
495 | path, has_write)); | |
496 | } | |
497 | ||
498 | ||
499 | void | |
500 | register_tapset_procfs(systemtap_session& s) | |
501 | { | |
502 | match_node* root = s.pattern_root; | |
503 | derived_probe_builder *builder = new procfs_builder(); | |
504 | ||
505 | root->bind(TOK_PROCFS)->bind(TOK_READ)->bind(builder); | |
506 | root->bind_str(TOK_PROCFS)->bind(TOK_READ)->bind(builder); | |
507 | root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(builder); | |
508 | root->bind_str(TOK_PROCFS)->bind(TOK_WRITE)->bind(builder); | |
509 | } | |
510 | ||
511 | ||
512 | ||
513 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |