]>
Commit | Line | Data |
---|---|---|
7a212aa8 | 1 | // tapset for procfs |
c69a87e0 | 2 | // Copyright (C) 2005-2010 Red Hat Inc. |
7a212aa8 | 3 | // Copyright (C) 2005-2007 Intel Corporation. |
7a212aa8 JS |
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 | ||
11 | #include "session.h" | |
12 | #include "tapsets.h" | |
13 | #include "translate.h" | |
14 | #include "util.h" | |
15 | ||
16 | #include <cstring> | |
17 | #include <string> | |
18 | ||
19 | ||
20 | using namespace std; | |
21 | using namespace __gnu_cxx; | |
22 | ||
23 | ||
4627ed58 JS |
24 | static const string TOK_PROCFS("procfs"); |
25 | static const string TOK_READ("read"); | |
26 | static const string TOK_WRITE("write"); | |
38975255 | 27 | static const string TOK_MAXSIZE("maxsize"); |
c243f608 | 28 | static const string TOK_UMASK("umask"); |
7a212aa8 JS |
29 | |
30 | ||
31 | // ------------------------------------------------------------------------ | |
32 | // procfs file derived probes | |
33 | // ------------------------------------------------------------------------ | |
34 | ||
35 | ||
36 | struct procfs_derived_probe: public derived_probe | |
37 | { | |
38 | string path; | |
39 | bool write; | |
40 | bool target_symbol_seen; | |
38975255 | 41 | int64_t maxsize_val; |
c243f608 | 42 | int64_t umask; |
7a212aa8 | 43 | |
c243f608 LB |
44 | |
45 | procfs_derived_probe (systemtap_session &, probe* p, probe_point* l, string ps, bool w, int64_t m, int64_t umask); | |
7a212aa8 JS |
46 | void join_group (systemtap_session& s); |
47 | }; | |
48 | ||
49 | ||
50 | struct procfs_probe_set | |
51 | { | |
52 | procfs_derived_probe* read_probe; | |
53 | procfs_derived_probe* write_probe; | |
54 | ||
c243f608 | 55 | procfs_probe_set () : read_probe (NULL), write_probe (NULL) {} |
7a212aa8 JS |
56 | }; |
57 | ||
58 | ||
59 | struct procfs_derived_probe_group: public generic_dpg<procfs_derived_probe> | |
60 | { | |
61 | private: | |
62 | map<string, procfs_probe_set*> probes_by_path; | |
63 | typedef map<string, procfs_probe_set*>::iterator p_b_p_iterator; | |
64 | bool has_read_probes; | |
65 | bool has_write_probes; | |
66 | ||
67 | public: | |
68 | procfs_derived_probe_group () : | |
c243f608 | 69 | has_read_probes(false), has_write_probes(false) {} |
7a212aa8 JS |
70 | |
71 | void enroll (procfs_derived_probe* probe); | |
be66b6e1 DS |
72 | void emit_kernel_module_init (systemtap_session& s); |
73 | void emit_kernel_module_exit (systemtap_session& s); | |
7a212aa8 JS |
74 | void emit_module_decls (systemtap_session& s); |
75 | void emit_module_init (systemtap_session& s); | |
76 | void emit_module_exit (systemtap_session& s); | |
77 | }; | |
78 | ||
79 | ||
80 | struct procfs_var_expanding_visitor: public var_expanding_visitor | |
81 | { | |
82 | procfs_var_expanding_visitor(systemtap_session& s, const string& pn, | |
a50de939 | 83 | string path, bool write_probe); |
7a212aa8 JS |
84 | |
85 | systemtap_session& sess; | |
86 | string probe_name; | |
87 | string path; | |
88 | bool write_probe; | |
89 | bool target_symbol_seen; | |
90 | ||
91 | void visit_target_symbol (target_symbol* e); | |
92 | }; | |
93 | ||
94 | ||
95 | procfs_derived_probe::procfs_derived_probe (systemtap_session &s, probe* p, | |
38975255 | 96 | probe_point* l, string ps, bool w, |
c243f608 | 97 | int64_t m, int64_t umask): |
38975255 | 98 | derived_probe(p, l), path(ps), write(w), target_symbol_seen(false), |
c243f608 | 99 | maxsize_val(m), umask(umask) |
7a212aa8 JS |
100 | { |
101 | // Expand local variables in the probe body | |
c243f608 | 102 | procfs_var_expanding_visitor v (s, name, path, write); |
8b095b45 | 103 | v.replace (this->body); |
7a212aa8 JS |
104 | target_symbol_seen = v.target_symbol_seen; |
105 | } | |
106 | ||
107 | ||
108 | void | |
109 | procfs_derived_probe::join_group (systemtap_session& s) | |
110 | { | |
111 | if (! s.procfs_derived_probes) | |
8e0049e8 DS |
112 | { |
113 | s.procfs_derived_probes = new procfs_derived_probe_group (); | |
114 | ||
115 | // Make sure 'struct _stp_procfs_data' is defined early. | |
116 | embeddedcode *ec = new embeddedcode; | |
117 | ec->tok = NULL; | |
118 | ec->code = string("struct _stp_procfs_data {\n") | |
23b7dbfa | 119 | + string(" char *buffer;\n") |
38975255 DS |
120 | + string(" size_t bufsize;\n") |
121 | + string(" size_t count;\n") | |
122 | + string("};\n") | |
123 | + string("#ifndef STP_PROCFS_BUFSIZE\n") | |
124 | + string("#define STP_PROCFS_BUFSIZE MAXSTRINGLEN\n") | |
125 | + string("#endif\n"); | |
8e0049e8 DS |
126 | s.embeds.push_back(ec); |
127 | } | |
7a212aa8 JS |
128 | s.procfs_derived_probes->enroll (this); |
129 | } | |
130 | ||
131 | ||
132 | void | |
133 | procfs_derived_probe_group::enroll (procfs_derived_probe* p) | |
134 | { | |
135 | procfs_probe_set *pset; | |
136 | ||
137 | if (probes_by_path.count(p->path) == 0) | |
138 | { | |
139 | pset = new procfs_probe_set; | |
140 | probes_by_path[p->path] = pset; | |
141 | } | |
142 | else | |
143 | { | |
144 | pset = probes_by_path[p->path]; | |
145 | ||
146 | // You can only specify 1 read and 1 write probe. | |
147 | if (p->write && pset->write_probe != NULL) | |
efee9a98 | 148 | throw semantic_error(_("only one write procfs probe can exist for procfs path \"") + p->path + "\""); |
7a212aa8 | 149 | else if (! p->write && pset->read_probe != NULL) |
efee9a98 | 150 | throw semantic_error(_("only one read procfs probe can exist for procfs path \"") + p->path + "\""); |
7a212aa8 JS |
151 | |
152 | // XXX: multiple writes should be acceptable | |
153 | } | |
154 | ||
155 | if (p->write) | |
38975255 DS |
156 | { |
157 | pset->write_probe = p; | |
158 | has_write_probes = true; | |
159 | } | |
7a212aa8 | 160 | else |
38975255 DS |
161 | { |
162 | pset->read_probe = p; | |
163 | has_read_probes = true; | |
164 | } | |
7a212aa8 JS |
165 | } |
166 | ||
167 | ||
be66b6e1 DS |
168 | void |
169 | procfs_derived_probe_group::emit_kernel_module_init (systemtap_session& s) | |
170 | { | |
171 | if (probes_by_path.empty()) | |
172 | return; | |
173 | s.op->newline() << "rc = _stp_mkdir_proc_module();"; | |
174 | } | |
175 | ||
176 | ||
177 | void | |
178 | procfs_derived_probe_group::emit_kernel_module_exit (systemtap_session& s) | |
179 | { | |
180 | if (probes_by_path.empty()) | |
181 | return; | |
b85e750d DS |
182 | // If we're using the original transport, it uses the |
183 | // '/proc/systemtap/{module_name}' directory to store control | |
184 | // files. Let the transport layer clean up that directory. | |
185 | s.op->newline() << "#if (STP_TRANSPORT_VERSION != 1)"; | |
be66b6e1 | 186 | s.op->newline() << "_stp_rmdir_proc_module();"; |
b85e750d | 187 | s.op->newline() << "#endif"; |
be66b6e1 DS |
188 | } |
189 | ||
190 | ||
7a212aa8 JS |
191 | void |
192 | procfs_derived_probe_group::emit_module_decls (systemtap_session& s) | |
193 | { | |
194 | if (probes_by_path.empty()) | |
195 | return; | |
196 | ||
197 | s.op->newline() << "/* ---- procfs probes ---- */"; | |
198 | s.op->newline() << "#include \"procfs.c\""; | |
23b7dbfa | 199 | s.op->newline() << "#include \"procfs-probes.c\""; |
7a212aa8 | 200 | |
38975255 DS |
201 | // Emit the procfs probe buffer structure |
202 | s.op->newline() << "static struct stap_procfs_probe_buffer {"; | |
203 | s.op->indent(1); | |
9a5fa202 | 204 | unsigned buf_index = 0; // used for buffer naming |
38975255 DS |
205 | for (p_b_p_iterator it = probes_by_path.begin(); it != probes_by_path.end(); |
206 | it++) | |
207 | { | |
208 | procfs_probe_set *pset = it->second; | |
9a5fa202 | 209 | s.op->newline() << "char buf_" << buf_index++; |
38975255 DS |
210 | |
211 | if (pset->read_probe != NULL) | |
212 | { | |
213 | if (pset->read_probe->maxsize_val == 0) | |
9a5fa202 | 214 | s.op->line() << "[STP_PROCFS_BUFSIZE];"; |
38975255 | 215 | else |
9a5fa202 | 216 | s.op->line() << "[" << pset->read_probe->maxsize_val << "];"; |
38975255 DS |
217 | } |
218 | else | |
9a5fa202 | 219 | s.op->line() << "[MAXSTRINGLEN];"; |
38975255 DS |
220 | } |
221 | s.op->newline(-1) << "} stap_procfs_probe_buffers;"; | |
222 | ||
7a212aa8 | 223 | // Emit the procfs probe data list |
23b7dbfa | 224 | s.op->newline() << "static struct stap_procfs_probe stap_procfs_probes[] = {"; |
7a212aa8 JS |
225 | s.op->indent(1); |
226 | ||
9a5fa202 | 227 | buf_index = 0; |
7a212aa8 JS |
228 | for (p_b_p_iterator it = probes_by_path.begin(); it != probes_by_path.end(); |
229 | it++) | |
38975255 | 230 | { |
7a212aa8 JS |
231 | procfs_probe_set *pset = it->second; |
232 | ||
233 | s.op->newline() << "{"; | |
234 | s.op->line() << " .path=" << lex_cast_qstring (it->first) << ","; | |
235 | ||
236 | if (pset->read_probe != NULL) | |
faea5e16 | 237 | s.op->line() << " .read_probe=" << common_probe_init (pset->read_probe) << ","; |
7a212aa8 JS |
238 | |
239 | if (pset->write_probe != NULL) | |
faea5e16 | 240 | s.op->line() << " .write_probe=" << common_probe_init (pset->write_probe) << ","; |
38975255 | 241 | |
9a5fa202 | 242 | s.op->line() << " .buffer=stap_procfs_probe_buffers.buf_" << buf_index++ << ","; |
38975255 | 243 | if (pset->read_probe != NULL) |
9a5fa202 | 244 | { |
38975255 DS |
245 | if (pset->read_probe->maxsize_val == 0) |
246 | s.op->line() << " .bufsize=STP_PROCFS_BUFSIZE,"; | |
247 | else | |
248 | s.op->line() << " .bufsize=" | |
249 | << pset->read_probe->maxsize_val << ","; | |
250 | } | |
251 | else | |
9a5fa202 JS |
252 | s.op->line() << " .bufsize=MAXSTRINGLEN,"; |
253 | ||
c243f608 LB |
254 | s.op->line() << " .permissions=" << (((pset->read_probe ? 0444 : 0) |
255 | | (pset->write_probe ? 0222 : 0)) &~ | |
256 | ((pset->read_probe ? pset->read_probe->umask : 0) | |
257 | | (pset->write_probe ? pset->write_probe->umask : 0))) | |
258 | << ","; | |
38975255 | 259 | |
7a212aa8 | 260 | s.op->line() << " },"; |
38975255 | 261 | } |
7a212aa8 JS |
262 | s.op->newline(-1) << "};"; |
263 | ||
23b7dbfa DS |
264 | // Output routine to fill in the buffer with our data. Note that we |
265 | // need to do this even in the case where we have no read probes, | |
266 | // but we can skip most of it then. | |
267 | s.op->newline(); | |
268 | ||
269 | s.op->newline() << "static int _stp_proc_fill_read_buffer(struct stap_procfs_probe *spp) {"; | |
270 | s.op->indent(1); | |
7a212aa8 JS |
271 | if (has_read_probes) |
272 | { | |
8e0049e8 | 273 | s.op->newline() << "struct _stp_procfs_data pdata;"; |
7a212aa8 | 274 | |
71db462b | 275 | common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", |
6eefe942 | 276 | "spp->read_probe", |
cda141c2 | 277 | "stp_probe_type_procfs"); |
7a212aa8 | 278 | |
23b7dbfa | 279 | s.op->newline() << "pdata.buffer = spp->buffer;"; |
38975255 | 280 | s.op->newline() << "pdata.bufsize = spp->bufsize;"; |
6dceb5c9 MW |
281 | s.op->newline() << "if (c->ips.procfs_data == NULL)"; |
282 | s.op->newline(1) << "c->ips.procfs_data = &pdata;"; | |
7a212aa8 JS |
283 | s.op->newline(-1) << "else {"; |
284 | ||
065d5567 JS |
285 | s.op->newline(1) << "if (unlikely (atomic_inc_return (skipped_count()) > MAXSKIPPED)) {"; |
286 | s.op->newline(1) << "atomic_set (session_state(), STAP_SESSION_ERROR);"; | |
7a212aa8 JS |
287 | s.op->newline() << "_stp_exit ();"; |
288 | s.op->newline(-1) << "}"; | |
289 | s.op->newline() << "atomic_dec (& c->busy);"; | |
290 | s.op->newline() << "goto probe_epilogue;"; | |
291 | s.op->newline(-1) << "}"; | |
292 | ||
8e0049e8 | 293 | // call probe function |
26e63673 | 294 | s.op->newline() << "(*spp->read_probe->ph) (c);"; |
7a212aa8 | 295 | |
23b7dbfa | 296 | // Note that _procfs_value_set copied string data into spp->buffer |
6dceb5c9 | 297 | s.op->newline() << "c->ips.procfs_data = NULL;"; |
23b7dbfa DS |
298 | s.op->newline() << "spp->needs_fill = 0;"; |
299 | s.op->newline() << "spp->count = strlen(spp->buffer);"; | |
300 | ||
f887a8c9 | 301 | common_probe_entryfn_epilogue (s, true); |
7a212aa8 | 302 | |
23b7dbfa DS |
303 | s.op->newline() << "if (spp->needs_fill) {"; |
304 | s.op->newline(1) << "spp->needs_fill = 0;"; | |
305 | s.op->newline() << "return -EIO;"; | |
7a212aa8 JS |
306 | s.op->newline(-1) << "}"; |
307 | } | |
23b7dbfa DS |
308 | s.op->newline() << "return 0;"; |
309 | s.op->newline(-1) << "}"; | |
310 | ||
311 | // Output routine to read data. Note that we need to do this even | |
312 | // in the case where we have no write probes, but we can skip most | |
313 | // of it then. | |
314 | s.op->newline() << "static int _stp_process_write_buffer(struct stap_procfs_probe *spp, const char __user *buf, size_t count) {"; | |
315 | s.op->indent(1); | |
316 | s.op->newline() << "int retval = 0;"; | |
7a212aa8 JS |
317 | if (has_write_probes) |
318 | { | |
8e0049e8 | 319 | s.op->newline() << "struct _stp_procfs_data pdata;"; |
7a212aa8 | 320 | |
71db462b | 321 | common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", |
6eefe942 | 322 | "spp->write_probe", |
cda141c2 | 323 | "stp_probe_type_procfs"); |
7a212aa8 | 324 | |
38975255 DS |
325 | // We've got 2 problems here. The data count could be greater |
326 | // than MAXSTRINGLEN or greater than the bufsize (if the same | |
327 | // procfs file had a size less than MAXSTRINGLEN). | |
23b7dbfa | 328 | s.op->newline() << "if (count >= MAXSTRINGLEN)"; |
7a212aa8 | 329 | s.op->newline(1) << "count = MAXSTRINGLEN - 1;"; |
8e0049e8 | 330 | s.op->indent(-1); |
38975255 DS |
331 | s.op->newline() << "pdata.bufsize = spp->bufsize;"; |
332 | s.op->newline() << "if (count >= pdata.bufsize)"; | |
333 | s.op->newline(1) << "count = pdata.bufsize - 1;"; | |
334 | s.op->indent(-1); | |
335 | ||
23b7dbfa | 336 | s.op->newline() << "pdata.buffer = (char *)buf;"; |
8e0049e8 | 337 | s.op->newline() << "pdata.count = count;"; |
7a212aa8 | 338 | |
6dceb5c9 MW |
339 | s.op->newline() << "if (c->ips.procfs_data == NULL)"; |
340 | s.op->newline(1) << "c->ips.procfs_data = &pdata;"; | |
7a212aa8 JS |
341 | s.op->newline(-1) << "else {"; |
342 | ||
065d5567 JS |
343 | s.op->newline(1) << "if (unlikely (atomic_inc_return (skipped_count()) > MAXSKIPPED)) {"; |
344 | s.op->newline(1) << "atomic_set (session_state(), STAP_SESSION_ERROR);"; | |
7a212aa8 JS |
345 | s.op->newline() << "_stp_exit ();"; |
346 | s.op->newline(-1) << "}"; | |
347 | s.op->newline() << "atomic_dec (& c->busy);"; | |
348 | s.op->newline() << "goto probe_epilogue;"; | |
349 | s.op->newline(-1) << "}"; | |
350 | ||
8e0049e8 | 351 | // call probe function |
26e63673 | 352 | s.op->newline() << "(*spp->write_probe->ph) (c);"; |
7a212aa8 | 353 | |
6dceb5c9 | 354 | s.op->newline() << "c->ips.procfs_data = NULL;"; |
23b7dbfa DS |
355 | s.op->newline() << "if (c->last_error == 0) {"; |
356 | s.op->newline(1) << "retval = count;"; | |
7a212aa8 | 357 | s.op->newline(-1) << "}"; |
23b7dbfa | 358 | |
f887a8c9 | 359 | common_probe_entryfn_epilogue (s, true); |
7a212aa8 | 360 | } |
c243f608 | 361 | |
23b7dbfa DS |
362 | s.op->newline() << "return retval;"; |
363 | s.op->newline(-1) << "}"; | |
7a212aa8 JS |
364 | } |
365 | ||
366 | ||
367 | void | |
368 | procfs_derived_probe_group::emit_module_init (systemtap_session& s) | |
369 | { | |
370 | if (probes_by_path.empty()) | |
371 | return; | |
372 | ||
373 | s.op->newline() << "for (i = 0; i < " << probes_by_path.size() << "; i++) {"; | |
374 | s.op->newline(1) << "struct stap_procfs_probe *spp = &stap_procfs_probes[i];"; | |
375 | ||
26e63673 JS |
376 | s.op->newline() << "if (spp->read_probe)"; |
377 | s.op->newline(1) << "probe_point = spp->read_probe->pp;"; | |
7a212aa8 | 378 | s.op->newline(-1) << "else"; |
26e63673 | 379 | s.op->newline(1) << "probe_point = spp->write_probe->pp;"; |
23b7dbfa | 380 | s.op->indent(-1); |
7a212aa8 | 381 | |
bfbbb76e | 382 | s.op->newline() << "_spp_init(spp);"; |
b85e750d | 383 | s.op->newline() << "rc = _stp_create_procfs(spp->path, i, &_stp_proc_fops, spp->permissions, spp);"; |
7a212aa8 JS |
384 | |
385 | s.op->newline() << "if (rc) {"; | |
386 | s.op->newline(1) << "_stp_close_procfs();"; | |
23b7dbfa DS |
387 | |
388 | s.op->newline() << "for (i = 0; i < " << probes_by_path.size() << "; i++) {"; | |
389 | s.op->newline(1) << "spp = &stap_procfs_probes[i];"; | |
bfbbb76e | 390 | s.op->newline() << "_spp_shutdown(spp);"; |
23b7dbfa | 391 | s.op->newline(-1) << "}"; |
7a212aa8 JS |
392 | s.op->newline() << "break;"; |
393 | s.op->newline(-1) << "}"; | |
7a212aa8 JS |
394 | s.op->newline(-1) << "}"; // for loop |
395 | } | |
396 | ||
397 | ||
398 | void | |
399 | procfs_derived_probe_group::emit_module_exit (systemtap_session& s) | |
400 | { | |
401 | if (probes_by_path.empty()) | |
402 | return; | |
403 | ||
404 | s.op->newline() << "_stp_close_procfs();"; | |
23b7dbfa DS |
405 | s.op->newline() << "for (i = 0; i < " << probes_by_path.size() << "; i++) {"; |
406 | s.op->newline(1) << "struct stap_procfs_probe *spp = &stap_procfs_probes[i];"; | |
bfbbb76e | 407 | s.op->newline() << "_spp_shutdown(spp);"; |
23b7dbfa | 408 | s.op->newline(-1) << "}"; |
7a212aa8 JS |
409 | } |
410 | ||
411 | ||
a50de939 DS |
412 | procfs_var_expanding_visitor::procfs_var_expanding_visitor (systemtap_session& s, |
413 | const string& pn, | |
414 | string path, | |
415 | bool write_probe): | |
416 | sess (s), probe_name (pn), path (path), write_probe (write_probe), | |
417 | target_symbol_seen (false) | |
418 | { | |
419 | // procfs probes can also handle '.='. | |
420 | valid_ops.insert (".="); | |
421 | } | |
422 | ||
423 | ||
7a212aa8 JS |
424 | void |
425 | procfs_var_expanding_visitor::visit_target_symbol (target_symbol* e) | |
426 | { | |
c69a87e0 FCE |
427 | try |
428 | { | |
277c21bc | 429 | assert(e->name.size() > 0 && e->name[0] == '$'); |
c69a87e0 | 430 | |
277c21bc | 431 | if (e->name != "$value") |
efee9a98 | 432 | throw semantic_error (_("invalid target symbol for procfs probe, $value expected"), |
c69a87e0 | 433 | e->tok); |
f8809d54 | 434 | |
c69a87e0 | 435 | e->assert_no_components("procfs"); |
f8809d54 | 436 | |
c69a87e0 FCE |
437 | bool lvalue = is_active_lvalue(e); |
438 | if (write_probe && lvalue) | |
efee9a98 | 439 | throw semantic_error(_("procfs $value variable is read-only in a procfs write probe"), e->tok); |
7a212aa8 | 440 | else if (! write_probe && ! lvalue) |
efee9a98 | 441 | throw semantic_error(_("procfs $value variable cannot be read in a procfs read probe"), e->tok); |
f8809d54 | 442 | |
c69a87e0 | 443 | if (e->addressof) |
efee9a98 | 444 | throw semantic_error(_("cannot take address of procfs variable"), e->tok); |
f8809d54 | 445 | |
c69a87e0 FCE |
446 | // Remember that we've seen a target variable. |
447 | target_symbol_seen = true; | |
f8809d54 | 448 | |
c69a87e0 FCE |
449 | // Synthesize a function. |
450 | functiondecl *fdecl = new functiondecl; | |
59de45f1 | 451 | fdecl->synthetic = true; |
c69a87e0 FCE |
452 | fdecl->tok = e->tok; |
453 | embeddedcode *ec = new embeddedcode; | |
454 | ec->tok = e->tok; | |
f8809d54 | 455 | |
c69a87e0 | 456 | string fname; |
6dceb5c9 | 457 | string locvalue = "CONTEXT->ips.procfs_data"; |
f8809d54 | 458 | |
c69a87e0 | 459 | if (! lvalue) |
a50de939 | 460 | { |
c69a87e0 FCE |
461 | fname = "_procfs_value_get"; |
462 | ec->code = string(" struct _stp_procfs_data *data = (struct _stp_procfs_data *)(") + locvalue + string("); /* pure */\n") | |
f8809d54 | 463 | |
0386bbcf SM |
464 | + string(" _stp_copy_from_user(STAP_RETVALUE, data->buffer, data->count);\n") |
465 | + string(" STAP_RETVALUE[data->count] = '\\0';\n"); | |
c69a87e0 FCE |
466 | } |
467 | else // lvalue | |
a50de939 | 468 | { |
c69a87e0 FCE |
469 | if (*op == "=") |
470 | { | |
471 | fname = "_procfs_value_set"; | |
472 | ec->code = string("struct _stp_procfs_data *data = (struct _stp_procfs_data *)(") + locvalue + string(");\n") | |
0386bbcf | 473 | + string(" strlcpy(data->buffer, STAP_ARG_value, data->bufsize);\n") |
c69a87e0 FCE |
474 | + string(" data->count = strlen(data->buffer);\n"); |
475 | } | |
476 | else if (*op == ".=") | |
477 | { | |
478 | fname = "_procfs_value_append"; | |
479 | ec->code = string("struct _stp_procfs_data *data = (struct _stp_procfs_data *)(") + locvalue + string(");\n") | |
0386bbcf | 480 | + string(" strlcat(data->buffer, STAP_ARG_value, data->bufsize);\n") |
c69a87e0 FCE |
481 | + string(" data->count = strlen(data->buffer);\n"); |
482 | } | |
483 | else | |
484 | { | |
efee9a98 | 485 | throw semantic_error (_("Only the following assign operators are" |
c69a87e0 | 486 | " implemented on procfs read target variables:" |
efee9a98 | 487 | " '=', '.='"), e->tok); |
c69a87e0 FCE |
488 | } |
489 | } | |
f8809d54 JS |
490 | fname += lex_cast(++tick); |
491 | ||
c69a87e0 FCE |
492 | fdecl->name = fname; |
493 | fdecl->body = ec; | |
494 | fdecl->type = pe_string; | |
f8809d54 | 495 | |
c69a87e0 | 496 | if (lvalue) |
a50de939 | 497 | { |
c69a87e0 FCE |
498 | // Modify the fdecl so it carries a single pe_string formal |
499 | // argument called "value". | |
f8809d54 | 500 | |
c69a87e0 FCE |
501 | vardecl *v = new vardecl; |
502 | v->type = pe_string; | |
503 | v->name = "value"; | |
504 | v->tok = e->tok; | |
505 | fdecl->formal_args.push_back(v); | |
506 | } | |
f8809d54 JS |
507 | fdecl->join (sess); |
508 | ||
c69a87e0 FCE |
509 | // Synthesize a functioncall. |
510 | functioncall* n = new functioncall; | |
511 | n->tok = e->tok; | |
512 | n->function = fname; | |
f8809d54 | 513 | |
c69a87e0 FCE |
514 | if (lvalue) |
515 | { | |
516 | // Provide the functioncall to our parent, so that it can be | |
517 | // used to substitute for the assignment node immediately above | |
518 | // us. | |
519 | assert(!target_symbol_setter_functioncalls.empty()); | |
520 | *(target_symbol_setter_functioncalls.top()) = n; | |
521 | } | |
f8809d54 | 522 | |
c69a87e0 | 523 | provide (n); |
7a212aa8 | 524 | } |
c69a87e0 | 525 | catch (const semantic_error &er) |
7a212aa8 | 526 | { |
1af1e62d | 527 | e->chain (er); |
c69a87e0 | 528 | provide (e); |
7a212aa8 | 529 | } |
7a212aa8 JS |
530 | } |
531 | ||
532 | ||
533 | struct procfs_builder: public derived_probe_builder | |
534 | { | |
535 | procfs_builder() {} | |
536 | virtual void build(systemtap_session & sess, | |
537 | probe * base, | |
538 | probe_point * location, | |
539 | literal_map_t const & parameters, | |
540 | vector<derived_probe *> & finished_results); | |
541 | }; | |
542 | ||
543 | ||
544 | void | |
545 | procfs_builder::build(systemtap_session & sess, | |
546 | probe * base, | |
547 | probe_point * location, | |
548 | literal_map_t const & parameters, | |
549 | vector<derived_probe *> & finished_results) | |
550 | { | |
551 | string path; | |
552 | bool has_procfs = get_param(parameters, TOK_PROCFS, path); | |
553 | bool has_read = (parameters.find(TOK_READ) != parameters.end()); | |
554 | bool has_write = (parameters.find(TOK_WRITE) != parameters.end()); | |
c243f608 | 555 | bool has_umask = (parameters.find(TOK_UMASK) != parameters.end()); |
38975255 | 556 | int64_t maxsize_val = 0; |
c243f608 LB |
557 | int64_t umask_val; |
558 | if(has_umask) | |
559 | get_param(parameters, TOK_UMASK, umask_val); | |
560 | else /* no .umask */ | |
561 | { | |
562 | if(has_read) | |
563 | umask_val = 0044; | |
564 | else if(has_write) | |
565 | umask_val = 0022; | |
566 | else | |
567 | assert(0); | |
568 | } | |
38975255 DS |
569 | // Validate '.maxsize(NNN)', if it exists. |
570 | if (get_param(parameters, TOK_MAXSIZE, maxsize_val)) | |
571 | { | |
572 | if (maxsize_val <= 0) | |
efee9a98 | 573 | throw semantic_error (_("maxsize must be greater than 0")); |
38975255 | 574 | } |
7a212aa8 JS |
575 | |
576 | // If no procfs path, default to "command". The runtime will do | |
577 | // this for us, but if we don't do it here, we'll think the | |
578 | // following 2 probes are attached to different paths: | |
579 | // | |
580 | // probe procfs("command").read {}" | |
581 | // probe procfs.write {} | |
582 | ||
583 | if (! has_procfs) | |
584 | path = "command"; | |
585 | // If we have a path, we need to validate it. | |
586 | else | |
587 | { | |
588 | string::size_type start_pos, end_pos; | |
589 | string component; | |
590 | start_pos = 0; | |
591 | while ((end_pos = path.find('/', start_pos)) != string::npos) | |
592 | { | |
593 | // Make sure it doesn't start with '/'. | |
594 | if (end_pos == 0) | |
efee9a98 | 595 | throw semantic_error (_("procfs path cannot start with a '/'"), |
f1a0157a | 596 | location->components.front()->tok); |
7a212aa8 JS |
597 | |
598 | component = path.substr(start_pos, end_pos - start_pos); | |
599 | // Make sure it isn't empty. | |
600 | if (component.size() == 0) | |
efee9a98 | 601 | throw semantic_error (_("procfs path component cannot be empty"), |
f1a0157a | 602 | location->components.front()->tok); |
7a212aa8 JS |
603 | // Make sure it isn't relative. |
604 | else if (component == "." || component == "..") | |
efee9a98 | 605 | throw semantic_error (_("procfs path cannot be relative (and contain '.' or '..')"), location->components.front()->tok); |
7a212aa8 JS |
606 | |
607 | start_pos = end_pos + 1; | |
608 | } | |
609 | component = path.substr(start_pos); | |
610 | // Make sure it doesn't end with '/'. | |
611 | if (component.size() == 0) | |
efee9a98 | 612 | throw semantic_error (_("procfs path cannot end with a '/'"), location->components.front()->tok); |
7a212aa8 JS |
613 | // Make sure it isn't relative. |
614 | else if (component == "." || component == "..") | |
efee9a98 | 615 | throw semantic_error (_("procfs path cannot be relative (and contain '.' or '..')"), location->components.front()->tok); |
7a212aa8 JS |
616 | } |
617 | ||
618 | if (!(has_read ^ has_write)) | |
efee9a98 | 619 | throw semantic_error (_("need read/write component"), location->components.front()->tok); |
7a212aa8 JS |
620 | |
621 | finished_results.push_back(new procfs_derived_probe(sess, base, location, | |
38975255 | 622 | path, has_write, |
c243f608 | 623 | maxsize_val, umask_val)); |
7a212aa8 JS |
624 | } |
625 | ||
626 | ||
627 | void | |
628 | register_tapset_procfs(systemtap_session& s) | |
629 | { | |
630 | match_node* root = s.pattern_root; | |
631 | derived_probe_builder *builder = new procfs_builder(); | |
632 | ||
633 | root->bind(TOK_PROCFS)->bind(TOK_READ)->bind(builder); | |
c243f608 | 634 | root->bind(TOK_PROCFS)->bind_num(TOK_UMASK)->bind(TOK_READ)->bind(builder); |
38975255 | 635 | root->bind(TOK_PROCFS)->bind(TOK_READ)->bind_num(TOK_MAXSIZE)->bind(builder); |
c243f608 | 636 | root->bind(TOK_PROCFS)->bind_num(TOK_UMASK)->bind(TOK_READ)->bind_num(TOK_MAXSIZE)->bind(builder); |
7a212aa8 | 637 | root->bind_str(TOK_PROCFS)->bind(TOK_READ)->bind(builder); |
c243f608 | 638 | root->bind_str(TOK_PROCFS)->bind_num(TOK_UMASK)->bind(TOK_READ)->bind(builder); |
38975255 | 639 | root->bind_str(TOK_PROCFS)->bind(TOK_READ)->bind_num(TOK_MAXSIZE)->bind(builder); |
c243f608 | 640 | root->bind_str(TOK_PROCFS)->bind_num(TOK_UMASK)->bind(TOK_READ)->bind_num(TOK_MAXSIZE)->bind(builder); |
38975255 | 641 | |
7a212aa8 | 642 | root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(builder); |
c243f608 | 643 | root->bind(TOK_PROCFS)->bind_num(TOK_UMASK)->bind(TOK_WRITE)->bind(builder); |
7a212aa8 | 644 | root->bind_str(TOK_PROCFS)->bind(TOK_WRITE)->bind(builder); |
c243f608 | 645 | root->bind_str(TOK_PROCFS)->bind_num(TOK_UMASK)->bind(TOK_WRITE)->bind(builder); |
7a212aa8 JS |
646 | } |
647 | ||
648 | ||
649 | ||
650 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |