]>
Commit | Line | Data |
---|---|---|
dd0e4fa7 | 1 | // tapset for kernel static markers |
16aa72d9 | 2 | // Copyright (C) 2005-2017 Red Hat Inc. |
dd0e4fa7 | 3 | // Copyright (C) 2005-2007 Intel Corporation. |
dd0e4fa7 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 | #include "session.h" | |
11 | #include "tapsets.h" | |
12 | #include "translate.h" | |
13 | #include "util.h" | |
14 | ||
15 | #include <cerrno> | |
16 | #include <cstdlib> | |
17 | #include <cstring> | |
18 | #include <string> | |
19 | ||
20 | extern "C" { | |
21 | #include <fnmatch.h> | |
22 | } | |
23 | ||
24 | ||
25 | using namespace std; | |
26 | using namespace __gnu_cxx; | |
27 | ||
28 | ||
4627ed58 JS |
29 | static const string TOK_KERNEL("kernel"); |
30 | static const string TOK_MARK("mark"); | |
31 | static const string TOK_FORMAT("format"); | |
dd0e4fa7 JS |
32 | |
33 | ||
34 | // ------------------------------------------------------------------------ | |
35 | // statically inserted macro-based derived probes | |
36 | // ------------------------------------------------------------------------ | |
37 | ||
38 | struct mark_arg | |
39 | { | |
40 | bool str; | |
92dad52b | 41 | bool isptr; |
dd0e4fa7 JS |
42 | string c_type; |
43 | exp_type stp_type; | |
44 | }; | |
45 | ||
46 | struct mark_derived_probe: public derived_probe | |
47 | { | |
48 | mark_derived_probe (systemtap_session &s, | |
49 | const string& probe_name, const string& probe_format, | |
50 | probe* base_probe, probe_point* location); | |
51 | ||
52 | systemtap_session& sess; | |
53 | string probe_name, probe_format; | |
54 | vector <struct mark_arg *> mark_args; | |
55 | bool target_symbol_seen; | |
56 | ||
57 | void join_group (systemtap_session& s); | |
58 | void print_dupe_stamp (ostream& o); | |
dd0e4fa7 | 59 | void initialize_probe_context_vars (translator_output* o); |
d0bfd2ac | 60 | void getargs (std::list<std::string> &arg_set) const; |
dd0e4fa7 JS |
61 | |
62 | void parse_probe_format (); | |
63 | }; | |
64 | ||
65 | ||
66 | struct mark_derived_probe_group: public generic_dpg<mark_derived_probe> | |
67 | { | |
68 | public: | |
69 | void emit_module_decls (systemtap_session& s); | |
70 | void emit_module_init (systemtap_session& s); | |
71 | void emit_module_exit (systemtap_session& s); | |
72 | }; | |
73 | ||
74 | ||
75 | struct mark_var_expanding_visitor: public var_expanding_visitor | |
76 | { | |
70719cbf | 77 | mark_var_expanding_visitor(systemtap_session& s, |
dd0e4fa7 | 78 | vector <struct mark_arg *> &mark_args): |
16aa72d9 | 79 | var_expanding_visitor(s), mark_args (mark_args), |
dd0e4fa7 | 80 | target_symbol_seen (false) {} |
16aa72d9 | 81 | |
dd0e4fa7 JS |
82 | vector <struct mark_arg *> &mark_args; |
83 | bool target_symbol_seen; | |
84 | ||
85 | void visit_target_symbol (target_symbol* e); | |
86 | void visit_target_symbol_arg (target_symbol* e); | |
87 | void visit_target_symbol_context (target_symbol* e); | |
88 | }; | |
89 | ||
90 | ||
91 | void | |
92 | mark_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e) | |
93 | { | |
47d349b1 | 94 | string argnum_s = (string)e->name.substr(4,e->name.length()-4); |
dd0e4fa7 JS |
95 | int argnum = atoi (argnum_s.c_str()); |
96 | ||
97 | if (argnum < 1 || argnum > (int)mark_args.size()) | |
dc09353a | 98 | throw SEMANTIC_ERROR (_("invalid marker argument number"), e->tok); |
dd0e4fa7 JS |
99 | |
100 | if (is_active_lvalue (e)) | |
dc09353a | 101 | throw SEMANTIC_ERROR(_("write to marker parameter not permitted"), e->tok); |
dd0e4fa7 | 102 | |
dc5a09fc | 103 | e->assert_no_components("marker"); |
dd0e4fa7 JS |
104 | |
105 | // Remember that we've seen a target variable. | |
106 | target_symbol_seen = true; | |
107 | ||
a45664f4 JS |
108 | symbol* sym = new symbol; |
109 | sym->tok = e->tok; | |
110 | sym->name = "__mark_arg" + lex_cast(argnum); | |
111 | provide (sym); | |
dd0e4fa7 JS |
112 | } |
113 | ||
114 | ||
115 | void | |
116 | mark_var_expanding_visitor::visit_target_symbol_context (target_symbol* e) | |
117 | { | |
47d349b1 | 118 | string sname = e->name; |
dd0e4fa7 JS |
119 | |
120 | if (is_active_lvalue (e)) | |
dc09353a | 121 | throw SEMANTIC_ERROR(_F("write to marker '%s' not permitted", sname.c_str()), e->tok); |
dd0e4fa7 | 122 | |
dc5a09fc | 123 | e->assert_no_components("marker"); |
dd0e4fa7 | 124 | |
277c21bc | 125 | if (e->name == "$format" || e->name == "$name") { |
bfdaad1e DS |
126 | // Synthesize an embedded expression. |
127 | embedded_expr *expr = new embedded_expr; | |
128 | expr->tok = e->tok; | |
129 | ||
130 | if (e->name == "$format") | |
131 | expr->code = string("/* string */ /* pure */ ") | |
6dceb5c9 | 132 | + string("c->ips.kmark.marker_format ? c->ips.kmark.marker_format : \"\""); |
bfdaad1e DS |
133 | else |
134 | expr->code = string("/* string */ /* pure */ ") | |
6dceb5c9 | 135 | + string("c->ips.kmark.marker_name ? c->ips.kmark.marker_name : \"\""); |
bfdaad1e DS |
136 | |
137 | provide (expr); | |
92dad52b | 138 | } |
277c21bc | 139 | else if (e->name == "$$vars" || e->name == "$$parms") |
92dad52b WH |
140 | { |
141 | //copy from tracepoint | |
1c922ad7 | 142 | print_format* pf = print_format::create(e->tok, "sprintf"); |
92dad52b WH |
143 | |
144 | for (unsigned i = 0; i < mark_args.size(); ++i) | |
145 | { | |
146 | if (i > 0) | |
147 | pf->raw_components += " "; | |
aca66a36 | 148 | pf->raw_components += "$arg" + lex_cast(i+1); |
92dad52b WH |
149 | target_symbol *tsym = new target_symbol; |
150 | tsym->tok = e->tok; | |
277c21bc | 151 | tsym->name = "$arg" + lex_cast(i+1); |
92dad52b WH |
152 | |
153 | tsym->saved_conversion_error = 0; | |
154 | expression *texp = require (tsym); //same treatment as tracepoint | |
155 | assert (!tsym->saved_conversion_error); | |
156 | switch (mark_args[i]->stp_type) | |
157 | { | |
158 | case pe_long: | |
159 | pf->raw_components += mark_args[i]->isptr ? "=%p" : "=%#x"; | |
160 | break; | |
161 | case pe_string: | |
162 | pf->raw_components += "=%s"; | |
163 | break; | |
164 | default: | |
165 | pf->raw_components += "=%#x"; | |
166 | break; | |
167 | } | |
168 | pf->args.push_back(texp); | |
169 | } | |
170 | pf->components = print_format::string_to_components(pf->raw_components); | |
171 | provide (pf); | |
dd0e4fa7 | 172 | } |
dd0e4fa7 JS |
173 | } |
174 | ||
175 | void | |
176 | mark_var_expanding_visitor::visit_target_symbol (target_symbol* e) | |
177 | { | |
277c21bc | 178 | assert(e->name.size() > 0 && e->name[0] == '$'); |
dd0e4fa7 | 179 | |
c69a87e0 FCE |
180 | try |
181 | { | |
182 | if (e->addressof) | |
dc09353a | 183 | throw SEMANTIC_ERROR(_("cannot take address of marker variable"), e->tok); |
277c21bc | 184 | |
47d349b1 | 185 | if (startswith(e->name, "$arg")) |
c69a87e0 | 186 | visit_target_symbol_arg (e); |
277c21bc JS |
187 | else if (e->name == "$format" || e->name == "$name" |
188 | || e->name == "$$parms" || e->name == "$$vars") | |
c69a87e0 FCE |
189 | visit_target_symbol_context (e); |
190 | else | |
dc09353a | 191 | throw SEMANTIC_ERROR (_("invalid target symbol for marker, $argN, $name, $format, $$parms or $$vars expected"), |
c69a87e0 FCE |
192 | e->tok); |
193 | } | |
194 | catch (const semantic_error &er) | |
195 | { | |
1af1e62d | 196 | e->chain (er); |
c69a87e0 FCE |
197 | provide (e); |
198 | } | |
dd0e4fa7 JS |
199 | } |
200 | ||
201 | ||
dd0e4fa7 JS |
202 | mark_derived_probe::mark_derived_probe (systemtap_session &s, |
203 | const string& p_n, | |
204 | const string& p_f, | |
205 | probe* base, probe_point* loc): | |
4c5d1300 | 206 | derived_probe (base, loc, true /* .components soon rewritten */), |
dd0e4fa7 JS |
207 | sess (s), probe_name (p_n), probe_format (p_f), |
208 | target_symbol_seen (false) | |
209 | { | |
210 | // create synthetic probe point name; preserve condition | |
211 | vector<probe_point::component*> comps; | |
212 | comps.push_back (new probe_point::component (TOK_KERNEL)); | |
213 | comps.push_back (new probe_point::component (TOK_MARK, new literal_string (probe_name))); | |
214 | comps.push_back (new probe_point::component (TOK_FORMAT, new literal_string (probe_format))); | |
215 | this->sole_location()->components = comps; | |
216 | ||
217 | // expand the marker format | |
218 | parse_probe_format(); | |
219 | ||
220 | // Now expand the local variables in the probe body | |
70719cbf | 221 | mark_var_expanding_visitor v (sess, mark_args); |
16aa72d9 FCE |
222 | var_expand_const_fold_loop (sess, this->body, v); |
223 | ||
dd0e4fa7 | 224 | target_symbol_seen = v.target_symbol_seen; |
a45664f4 JS |
225 | if (target_symbol_seen) |
226 | for (unsigned i = 0; i < mark_args.size(); ++i) | |
227 | { | |
228 | vardecl* v = new vardecl; | |
229 | v->name = "__mark_arg" + lex_cast(i+1); | |
230 | v->tok = this->tok; | |
58701b78 | 231 | v->set_arity(0, this->tok); |
a45664f4 | 232 | v->type = mark_args[i]->stp_type; |
69aa668e | 233 | v->synthetic = true; |
a45664f4 JS |
234 | this->locals.push_back (v); |
235 | } | |
dd0e4fa7 JS |
236 | |
237 | if (sess.verbose > 2) | |
1341a03c | 238 | clog << "marker-based " << name() << " mark=" << probe_name << " fmt='" << probe_format |
ce0f6648 | 239 | << "'" << endl; |
dd0e4fa7 JS |
240 | } |
241 | ||
242 | ||
243 | static int | |
244 | skip_atoi(const char **s) | |
245 | { | |
246 | int i = 0; | |
247 | while (isdigit(**s)) | |
248 | i = i * 10 + *((*s)++) - '0'; | |
249 | return i; | |
250 | } | |
251 | ||
252 | ||
253 | void | |
254 | mark_derived_probe::parse_probe_format() | |
255 | { | |
256 | const char *fmt = probe_format.c_str(); | |
257 | int qualifier; // 'h', 'l', or 'L' for integer fields | |
258 | mark_arg *arg; | |
259 | ||
260 | for (; *fmt ; ++fmt) | |
261 | { | |
262 | if (*fmt != '%') | |
263 | { | |
264 | /* Skip text */ | |
265 | continue; | |
266 | } | |
267 | ||
268 | repeat: | |
269 | ++fmt; | |
270 | ||
271 | // skip conversion flags (if present) | |
272 | switch (*fmt) | |
273 | { | |
274 | case '-': | |
275 | case '+': | |
276 | case ' ': | |
277 | case '#': | |
278 | case '0': | |
279 | goto repeat; | |
280 | } | |
281 | ||
282 | // skip minimum field witdh (if present) | |
283 | if (isdigit(*fmt)) | |
284 | skip_atoi(&fmt); | |
285 | ||
286 | // skip precision (if present) | |
287 | if (*fmt == '.') | |
288 | { | |
289 | ++fmt; | |
290 | if (isdigit(*fmt)) | |
291 | skip_atoi(&fmt); | |
292 | } | |
293 | ||
294 | // get the conversion qualifier (if present) | |
295 | qualifier = -1; | |
296 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') | |
297 | { | |
298 | qualifier = *fmt; | |
299 | ++fmt; | |
300 | if (qualifier == 'l' && *fmt == 'l') | |
301 | { | |
302 | qualifier = 'L'; | |
303 | ++fmt; | |
304 | } | |
305 | } | |
306 | ||
307 | // get the conversion type | |
308 | switch (*fmt) | |
309 | { | |
310 | case 'c': | |
311 | arg = new mark_arg; | |
312 | arg->str = false; | |
92dad52b | 313 | arg->isptr = false; |
dd0e4fa7 JS |
314 | arg->c_type = "int"; |
315 | arg->stp_type = pe_long; | |
316 | mark_args.push_back(arg); | |
317 | continue; | |
318 | ||
319 | case 's': | |
320 | arg = new mark_arg; | |
321 | arg->str = true; | |
92dad52b | 322 | arg->isptr = false; |
dd0e4fa7 JS |
323 | arg->c_type = "char *"; |
324 | arg->stp_type = pe_string; | |
325 | mark_args.push_back(arg); | |
326 | continue; | |
327 | ||
328 | case 'p': | |
329 | arg = new mark_arg; | |
330 | arg->str = false; | |
92dad52b | 331 | arg->isptr = true; |
dd0e4fa7 JS |
332 | // This should really be 'void *'. But, then we'll get a |
333 | // compile error when we assign the void pointer to an | |
334 | // integer without a cast. So, we use 'long' instead, since | |
335 | // it should have the same size as 'void *'. | |
336 | arg->c_type = "long"; | |
337 | arg->stp_type = pe_long; | |
338 | mark_args.push_back(arg); | |
339 | continue; | |
340 | ||
341 | case '%': | |
342 | continue; | |
343 | ||
344 | case 'o': | |
345 | case 'X': | |
346 | case 'x': | |
347 | case 'd': | |
348 | case 'i': | |
349 | case 'u': | |
350 | // fall through... | |
351 | break; | |
352 | ||
353 | default: | |
354 | if (!*fmt) | |
355 | --fmt; | |
356 | continue; | |
357 | } | |
358 | ||
359 | arg = new mark_arg; | |
360 | arg->str = false; | |
92dad52b | 361 | arg->isptr = false; |
dd0e4fa7 JS |
362 | arg->stp_type = pe_long; |
363 | switch (qualifier) | |
364 | { | |
365 | case 'L': | |
366 | arg->c_type = "long long"; | |
367 | break; | |
368 | ||
369 | case 'l': | |
370 | arg->c_type = "long"; | |
371 | break; | |
372 | ||
373 | case 'h': | |
374 | arg->c_type = "short"; | |
375 | break; | |
376 | ||
377 | default: | |
378 | arg->c_type = "int"; | |
379 | break; | |
380 | } | |
381 | mark_args.push_back(arg); | |
382 | } | |
383 | } | |
384 | ||
385 | ||
386 | void | |
387 | mark_derived_probe::join_group (systemtap_session& s) | |
388 | { | |
389 | if (! s.mark_derived_probes) | |
390 | { | |
391 | s.mark_derived_probes = new mark_derived_probe_group (); | |
392 | ||
393 | // Make sure <linux/marker.h> is included early. | |
394 | embeddedcode *ec = new embeddedcode; | |
395 | ec->tok = NULL; | |
396 | ec->code = string("#if ! defined(CONFIG_MARKERS)\n") | |
397 | + string("#error \"Need CONFIG_MARKERS!\"\n") | |
398 | + string("#endif\n") | |
399 | + string("#include <linux/marker.h>\n"); | |
400 | ||
401 | s.embeds.push_back(ec); | |
402 | } | |
403 | s.mark_derived_probes->enroll (this); | |
ca6d3b0f | 404 | this->group = s.mark_derived_probes; |
dd0e4fa7 JS |
405 | } |
406 | ||
407 | ||
408 | void | |
409 | mark_derived_probe::print_dupe_stamp (ostream& o) | |
410 | { | |
411 | if (target_symbol_seen) | |
412 | for (unsigned i = 0; i < mark_args.size(); i++) | |
413 | o << mark_args[i]->c_type << " __mark_arg" << (i+1) << endl; | |
414 | } | |
415 | ||
416 | ||
dd0e4fa7 JS |
417 | void |
418 | mark_derived_probe::initialize_probe_context_vars (translator_output* o) | |
419 | { | |
420 | // If we haven't seen a target symbol for this probe, quit. | |
421 | if (! target_symbol_seen) | |
422 | return; | |
423 | ||
424 | bool deref_fault_needed = false; | |
425 | for (unsigned i = 0; i < mark_args.size(); i++) | |
426 | { | |
3acf16ff JS |
427 | string localname = "l->" + |
428 | sess.up->c_localname("__mark_arg" + lex_cast(i+1)); | |
dd0e4fa7 JS |
429 | switch (mark_args[i]->stp_type) |
430 | { | |
431 | case pe_long: | |
6dceb5c9 | 432 | o->newline() << localname << " = va_arg(*c->ips.kmark.mark_va_list, " |
dd0e4fa7 JS |
433 | << mark_args[i]->c_type << ");"; |
434 | break; | |
435 | ||
436 | case pe_string: | |
437 | // We're assuming that this is a kernel string (this code is | |
438 | // basically the guts of kernel_string), not a user string. | |
439 | o->newline() << "{ " << mark_args[i]->c_type | |
6dceb5c9 | 440 | << " tmp_str = va_arg(*c->ips.kmark.mark_va_list, " |
dd0e4fa7 | 441 | << mark_args[i]->c_type << ");"; |
f1e8e7e0 | 442 | o->newline() << "kderef_string (" << localname |
dd0e4fa7 JS |
443 | << ", tmp_str, MAXSTRINGLEN); }"; |
444 | deref_fault_needed = true; | |
445 | break; | |
446 | ||
447 | default: | |
dc09353a | 448 | throw SEMANTIC_ERROR (_("cannot expand unknown type")); |
dd0e4fa7 JS |
449 | break; |
450 | } | |
451 | } | |
452 | if (deref_fault_needed) | |
453 | // Need to report errors? | |
454 | o->newline() << "deref_fault: ;"; | |
455 | } | |
456 | ||
457 | void | |
d0bfd2ac | 458 | mark_derived_probe::getargs(std::list<std::string> &arg_set) const |
dd0e4fa7 | 459 | { |
b2017fdd WH |
460 | //PR11761: hard-coded the basic variables |
461 | arg_set.push_back("$name:string"); | |
462 | arg_set.push_back("$format:string"); | |
dd0e4fa7 JS |
463 | for (unsigned i = 0; i < mark_args.size(); i++) |
464 | { | |
aca66a36 | 465 | string localname = "$arg" + lex_cast(i+1); |
dd0e4fa7 JS |
466 | switch (mark_args[i]->stp_type) |
467 | { | |
468 | case pe_long: | |
d0bfd2ac | 469 | arg_set.push_back(localname+":long"); |
dd0e4fa7 JS |
470 | break; |
471 | case pe_string: | |
d0bfd2ac | 472 | arg_set.push_back(localname+":string"); |
dd0e4fa7 JS |
473 | break; |
474 | default: | |
d0bfd2ac | 475 | arg_set.push_back(localname+":unknown"); |
dd0e4fa7 JS |
476 | break; |
477 | } | |
478 | } | |
479 | } | |
480 | ||
481 | ||
482 | void | |
483 | mark_derived_probe_group::emit_module_decls (systemtap_session& s) | |
484 | { | |
485 | if (probes.empty()) | |
486 | return; | |
487 | ||
488 | s.op->newline() << "/* ---- marker probes ---- */"; | |
489 | ||
490 | s.op->newline() << "static struct stap_marker_probe {"; | |
491 | s.op->newline(1) << "const char * const name;"; | |
492 | s.op->newline() << "const char * const format;"; | |
7c3e97f4 | 493 | s.op->newline() << "const struct stap_probe * const probe;"; |
dd0e4fa7 JS |
494 | s.op->newline(-1) << "} stap_marker_probes [" << probes.size() << "] = {"; |
495 | s.op->indent(1); | |
496 | for (unsigned i=0; i < probes.size(); i++) | |
497 | { | |
498 | s.op->newline () << "{"; | |
faea5e16 JS |
499 | s.op->line() << " .name=" << lex_cast_qstring(probes[i]->probe_name) << ","; |
500 | s.op->line() << " .format=" << lex_cast_qstring(probes[i]->probe_format) << ","; | |
501 | s.op->line() << " .probe=" << common_probe_init (probes[i]) << ","; | |
dd0e4fa7 JS |
502 | s.op->line() << " },"; |
503 | } | |
504 | s.op->newline(-1) << "};"; | |
505 | s.op->newline(); | |
506 | ||
507 | ||
508 | // Emit the marker callback function | |
509 | s.op->newline(); | |
510 | s.op->newline() << "static void enter_marker_probe (void *probe_data, void *call_data, const char *fmt, va_list *args) {"; | |
511 | s.op->newline(1) << "struct stap_marker_probe *smp = (struct stap_marker_probe *)probe_data;"; | |
71db462b | 512 | common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "smp->probe", |
cda141c2 | 513 | "stp_probe_type_marker"); |
6dceb5c9 MW |
514 | s.op->newline() << "c->ips.kmark.marker_name = smp->name;"; |
515 | s.op->newline() << "c->ips.kmark.marker_format = smp->format;"; | |
516 | s.op->newline() << "c->ips.kmark.mark_va_list = args;"; | |
26e63673 | 517 | s.op->newline() << "(*smp->probe->ph) (c);"; |
6dceb5c9 | 518 | s.op->newline() << "c->ips.kmark.mark_va_list = NULL;"; |
dd0e4fa7 | 519 | |
ef1337ee | 520 | common_probe_entryfn_epilogue (s, true, otf_safe_context(s)); |
dd0e4fa7 JS |
521 | s.op->newline(-1) << "}"; |
522 | ||
523 | return; | |
524 | } | |
525 | ||
526 | ||
527 | void | |
528 | mark_derived_probe_group::emit_module_init (systemtap_session &s) | |
529 | { | |
530 | if (probes.size () == 0) | |
531 | return; | |
532 | ||
533 | s.op->newline() << "/* init marker probes */"; | |
534 | s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {"; | |
535 | s.op->newline(1) << "struct stap_marker_probe *smp = &stap_marker_probes[i];"; | |
26e63673 | 536 | s.op->newline() << "probe_point = smp->probe->pp;"; |
dd0e4fa7 JS |
537 | s.op->newline() << "rc = marker_probe_register(smp->name, smp->format, enter_marker_probe, smp);"; |
538 | s.op->newline() << "if (rc) {"; | |
539 | s.op->newline(1) << "for (j=i-1; j>=0; j--) {"; // partial rollback | |
540 | s.op->newline(1) << "struct stap_marker_probe *smp2 = &stap_marker_probes[j];"; | |
541 | s.op->newline() << "marker_probe_unregister(smp2->name, enter_marker_probe, smp2);"; | |
542 | s.op->newline(-1) << "}"; | |
543 | s.op->newline() << "break;"; // don't attempt to register any more probes | |
544 | s.op->newline(-1) << "}"; | |
545 | s.op->newline(-1) << "}"; // for loop | |
546 | } | |
547 | ||
548 | ||
549 | void | |
550 | mark_derived_probe_group::emit_module_exit (systemtap_session& s) | |
551 | { | |
552 | if (probes.empty()) | |
553 | return; | |
554 | ||
555 | s.op->newline() << "/* deregister marker probes */"; | |
556 | s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {"; | |
557 | s.op->newline(1) << "struct stap_marker_probe *smp = &stap_marker_probes[i];"; | |
558 | s.op->newline() << "marker_probe_unregister(smp->name, enter_marker_probe, smp);"; | |
559 | s.op->newline(-1) << "}"; // for loop | |
560 | } | |
561 | ||
562 | ||
563 | struct mark_builder: public derived_probe_builder | |
564 | { | |
565 | private: | |
566 | bool cache_initialized; | |
567 | typedef multimap<string, string> mark_cache_t; | |
568 | typedef multimap<string, string>::const_iterator mark_cache_const_iterator_t; | |
569 | typedef pair<mark_cache_const_iterator_t, mark_cache_const_iterator_t> | |
570 | mark_cache_const_iterator_pair_t; | |
571 | mark_cache_t mark_cache; | |
572 | ||
e3718b64 JL |
573 | string suggest_marks(systemtap_session& sess, |
574 | const string& mark); | |
575 | ||
dd0e4fa7 JS |
576 | public: |
577 | mark_builder(): cache_initialized(false) {} | |
578 | ||
579 | void build_no_more (systemtap_session &s) | |
580 | { | |
581 | if (! mark_cache.empty()) | |
582 | { | |
583 | if (s.verbose > 3) | |
efee9a98 | 584 | clog << _("mark_builder releasing cache") << endl; |
dd0e4fa7 JS |
585 | mark_cache.clear(); |
586 | } | |
587 | } | |
588 | ||
589 | void build(systemtap_session & sess, | |
590 | probe * base, | |
591 | probe_point * location, | |
592 | literal_map_t const & parameters, | |
593 | vector<derived_probe *> & finished_results); | |
352c84fe FL |
594 | |
595 | virtual string name() { return "mark builder"; } | |
dd0e4fa7 JS |
596 | }; |
597 | ||
598 | ||
e3718b64 JL |
599 | string |
600 | mark_builder::suggest_marks(systemtap_session& sess, | |
601 | const string& mark) | |
602 | { | |
603 | if (mark.empty() || mark_cache.empty()) | |
604 | return ""; | |
605 | ||
606 | set<string> marks; | |
607 | ||
608 | // Collect all markers from cache | |
609 | for (mark_cache_const_iterator_t it = mark_cache.begin(); | |
610 | it != mark_cache.end(); it++) | |
611 | marks.insert(it->first); | |
612 | ||
613 | if (sess.verbose > 2) | |
614 | clog << "suggesting from " << marks.size() | |
615 | << " kernel marks" << endl; | |
616 | ||
617 | if (marks.empty()) | |
618 | return ""; | |
619 | ||
620 | return levenshtein_suggest(mark, marks, 5); // print top 5 marks only | |
621 | } | |
622 | ||
dd0e4fa7 JS |
623 | void |
624 | mark_builder::build(systemtap_session & sess, | |
625 | probe * base, | |
626 | probe_point *loc, | |
627 | literal_map_t const & parameters, | |
628 | vector<derived_probe *> & finished_results) | |
629 | { | |
45a63356 | 630 | interned_string mark_str_val; |
dd0e4fa7 | 631 | bool has_mark_str = get_param (parameters, TOK_MARK, mark_str_val); |
45a63356 | 632 | interned_string mark_format_val; |
dd0e4fa7 JS |
633 | bool has_mark_format = get_param (parameters, TOK_FORMAT, mark_format_val); |
634 | assert (has_mark_str); | |
635 | (void) has_mark_str; | |
636 | ||
637 | if (! cache_initialized) | |
638 | { | |
639 | cache_initialized = true; | |
640 | string module_markers_path = sess.kernel_build_tree + "/Module.markers"; | |
641 | ||
642 | ifstream module_markers; | |
643 | module_markers.open(module_markers_path.c_str(), ifstream::in); | |
644 | if (! module_markers) | |
645 | { | |
646 | if (sess.verbose>3) | |
efee9a98 LB |
647 | //TRANSLATORS: specific path cannot be opened |
648 | clog << module_markers_path << _(" cannot be opened: ") | |
dd0e4fa7 JS |
649 | << strerror(errno) << endl; |
650 | return; | |
651 | } | |
652 | ||
653 | string name, module, format; | |
654 | do | |
655 | { | |
656 | module_markers >> name >> module; | |
657 | getline(module_markers, format); | |
658 | ||
659 | // trim leading whitespace | |
1f4b9e55 | 660 | ltrim(format); |
dd0e4fa7 JS |
661 | |
662 | // If the format is empty, make sure we add back a space | |
663 | // character, which is what MARK_NOARGS expands to. | |
664 | if (format.length() == 0) | |
665 | format = " "; | |
666 | ||
667 | if (sess.verbose>3) | |
668 | clog << "'" << name << "' '" << module << "' '" << format | |
669 | << "'" << endl; | |
670 | ||
671 | if (mark_cache.count(name) > 0) | |
672 | { | |
673 | // If we have 2 markers with the same we've got 2 cases: | |
674 | // different format strings or duplicate format strings. | |
675 | // If an existing marker in the cache doesn't have the | |
676 | // same format string, add this marker. | |
677 | mark_cache_const_iterator_pair_t ret; | |
678 | mark_cache_const_iterator_t it; | |
679 | bool matching_format_string = false; | |
680 | ||
681 | ret = mark_cache.equal_range(name); | |
682 | for (it = ret.first; it != ret.second; ++it) | |
683 | { | |
684 | if (format == it->second) | |
685 | { | |
686 | matching_format_string = true; | |
687 | break; | |
688 | } | |
689 | } | |
690 | ||
691 | if (! matching_format_string) | |
692 | mark_cache.insert(pair<string,string>(name, format)); | |
693 | } | |
694 | else | |
695 | mark_cache.insert(pair<string,string>(name, format)); | |
696 | } | |
697 | while (! module_markers.eof()); | |
698 | module_markers.close(); | |
699 | } | |
700 | ||
e3718b64 JL |
701 | unsigned results_pre = finished_results.size(); |
702 | ||
dd0e4fa7 | 703 | // Search marker list for matching markers |
7371cd19 JS |
704 | const string& str_val = mark_str_val; |
705 | const string& format_val = mark_format_val; | |
dd0e4fa7 JS |
706 | for (mark_cache_const_iterator_t it = mark_cache.begin(); |
707 | it != mark_cache.end(); it++) | |
708 | { | |
709 | // Below, "rc" has negative polarity: zero iff matching. | |
7371cd19 | 710 | int rc = fnmatch(str_val.c_str(), it->first.c_str(), 0); |
dd0e4fa7 JS |
711 | if (! rc) |
712 | { | |
713 | bool add_result = true; | |
714 | ||
715 | // Match format strings (if the user specified one) | |
7371cd19 | 716 | if (has_mark_format && fnmatch(format_val.c_str(), |
dd0e4fa7 JS |
717 | it->second.c_str(), 0)) |
718 | add_result = false; | |
719 | ||
720 | if (add_result) | |
721 | { | |
722 | derived_probe *dp | |
723 | = new mark_derived_probe (sess, | |
724 | it->first, it->second, | |
725 | base, loc); | |
726 | finished_results.push_back (dp); | |
727 | } | |
728 | } | |
729 | } | |
e3718b64 | 730 | |
d2eaa03b JL |
731 | if (results_pre == finished_results.size() |
732 | && !loc->from_globby_comp(TOK_MARK)) | |
e3718b64 JL |
733 | { |
734 | string sugs = suggest_marks(sess, mark_str_val); | |
735 | if (!sugs.empty()) | |
736 | throw SEMANTIC_ERROR (_NF("no match (similar mark: %s)", | |
737 | "no match (similar marks: %s)", | |
738 | sugs.find(',') == string::npos, | |
739 | sugs.c_str())); | |
740 | } | |
dd0e4fa7 JS |
741 | } |
742 | ||
743 | ||
744 | ||
745 | void | |
746 | register_tapset_mark(systemtap_session& s) | |
747 | { | |
748 | match_node* root = s.pattern_root; | |
749 | derived_probe_builder *builder = new mark_builder(); | |
750 | ||
751 | root = root->bind(TOK_KERNEL); | |
752 | root = root->bind_str(TOK_MARK); | |
753 | ||
754 | root->bind(builder); | |
755 | root->bind_str(TOK_FORMAT)->bind(builder); | |
756 | } | |
757 | ||
758 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |