]> sourceware.org Git - systemtap.git/blob - tapset-method.cxx
PR18431: Function overloading
[systemtap.git] / tapset-method.cxx
1 // Tapset for per-method based probes
2 // Copyright (C) 2014 Red Hat Inc.
3
4 // This file is part of systemtap, and is free software. You can
5 // redistribute it and/or modify it under the terms of the GNU General
6 // Public License (GPL); either version 2, or (at your option) any
7 // later version.
8
9 #include "session.h"
10 #include "tapsets.h"
11 #include "translate.h"
12 #include "util.h"
13 #include "config.h"
14 #include "staptree.h"
15
16 #include "unistd.h"
17 #include "sys/wait.h"
18 #include "sys/types.h"
19
20 #include <cerrno>
21 #include <cstdlib>
22 #include <cstring>
23 #include <string>
24 #include <algorithm>
25
26 extern "C" {
27 #include <fnmatch.h>
28 }
29
30 using namespace std;
31 using namespace __gnu_cxx;
32
33 static const string TOK_CLASS ("class");
34 static const string TOK_METHOD ("method");
35 static const string TOK_PROCESS ("process");
36 static const string TOK_PROVIDER ("provider");
37 static const string TOK_MARK ("mark");
38 static const string TOK_JAVA ("java");
39 static const string TOK_RETURN ("return");
40 static const string TOK_BEGIN ("begin");
41 static const string TOK_END ("end");
42 static const string TOK_ERROR ("error");
43
44 // --------------------------------------------------------------------------
45
46 struct java_details_inspection: public functioncall_traversing_visitor
47 {
48
49 bool java_backtrace;
50 java_details_inspection(): java_backtrace(false) {}
51
52 void visit_functioncall(functioncall* e);
53 };
54
55 void
56 java_details_inspection::visit_functioncall(functioncall* e)
57 {
58 assert(e->referents.empty()); // we haven't elborated yet, so there should never be referents, bail if here is
59 if (e->function == "sprint_java_backtrace" || e->function == "print_java_backtrace" ){
60 java_backtrace = true;
61 return; // no need to search anymore we know we'll need the extra information
62 }
63 traversing_visitor::visit_functioncall(e);
64 }
65
66 struct java_builder: public derived_probe_builder
67 {
68 private:
69 typedef multimap<string, string> java_cache_t;
70 typedef multimap<string, string>::const_iterator java_cache_const_iterator_t;
71 typedef pair<java_cache_const_iterator_t, java_cache_const_iterator_t>
72 java_cache_const_iterator_pair_t;
73 java_cache_t java_cache;
74
75 public:
76 java_builder () {}
77
78 void build (systemtap_session & sess,
79 probe * base,
80 probe_point * location,
81 literal_map_t const & parameters,
82 vector <derived_probe *> & finished_results);
83 std::string mark_param(int i);
84 };
85
86 std::string
87 java_builder::mark_param(int i)
88 {
89 switch (i)
90 {
91 case 0:
92 return "method__0";
93 case 1:
94 return "method__1";
95 case 2:
96 return "method__2";
97 case 3:
98 return "method__3";
99 case 4:
100 return "method__4";
101 case 5:
102 return "method__5";
103 case 6:
104 return "method__6";
105 case 7:
106 return "method__7";
107 case 8:
108 return "method__8";
109 case 9:
110 return "method__9";
111 case 10:
112 return "method__10";
113 default:
114 return "*";
115 }
116 }
117 void
118 java_builder::build (systemtap_session & sess,
119 probe * base,
120 probe_point * loc,
121 literal_map_t const & parameters,
122 vector <derived_probe *> & finished_results)
123 {
124 interned_string method_str_val;
125 interned_string method_line_val;
126 bool has_method_str = get_param (parameters, TOK_METHOD, method_str_val);
127 int short_method_pos = method_str_val.find ('(');
128 //only if it exists, run check
129 bool one_arg = false; // used to check if there is an argument in the method
130 if (short_method_pos)
131 {
132 int second_method_pos = 0;
133 second_method_pos = method_str_val.find (')');
134 if ((second_method_pos - short_method_pos) > 1)
135 one_arg = true;
136 }
137 int64_t _java_pid = 0;
138 interned_string _java_proc_class = "";
139 // interned_string short_method_str = method_str_val.substr (0, short_method_pos);
140 interned_string class_str_val; // fully qualified class string
141 bool has_class_str = get_param (parameters, TOK_CLASS, class_str_val);
142 bool has_pid_int = get_param (parameters, TOK_JAVA, _java_pid);
143 bool has_pid_str = get_param (parameters, TOK_JAVA, _java_proc_class);
144 bool has_return = has_null_param (parameters, TOK_RETURN);
145 bool has_line_number = false;
146
147 // wildcards in Java probes are not allowed, so the location is already
148 // well-formed
149 loc->well_formed = true;
150
151 //find if we're probing at a specific line number
152 size_t line_position = 0;
153
154 size_t method_end_pos = method_str_val.size();
155 line_position = method_str_val.find_first_of(":"); //this will return the position ':' is found at
156 if (line_position == string::npos)
157 has_line_number = false;
158 else
159 {
160 has_line_number = true;
161 method_line_val = method_str_val.substr(line_position+1, method_end_pos);
162 method_str_val = method_str_val.substr(0, line_position);
163 line_position = method_line_val.find_first_of(":");
164 if (line_position != string::npos)
165 throw SEMANTIC_ERROR (_("maximum of one line number (:NNN)"));
166 if (has_line_number && has_return)
167 throw SEMANTIC_ERROR (_("conflict :NNN and .return probe"));
168 }
169
170 //need to count the number of parameters, exit if more than 10
171
172 int method_params_count = count (method_str_val.begin (), method_str_val.end (), ',');
173 if (one_arg)
174 method_params_count++; // in this case we know there was at least a var, but no ','
175
176 if (method_params_count > 10)
177 throw SEMANTIC_ERROR (_("maximum of 10 java method parameters may be specified"));
178
179 assert (has_method_str);
180 (void) has_method_str;
181 assert (has_class_str);
182 (void) has_class_str;
183
184 interned_string java_pid_str = "";
185 if(has_pid_int)
186 java_pid_str = lex_cast(_java_pid);
187 else
188 java_pid_str = _java_proc_class;
189
190 if (! (has_pid_int || has_pid_str) )
191 throw SEMANTIC_ERROR (_("missing JVMID"));
192
193 /* Java native backtrace probe point
194
195 In the event a java backtrace is requested (signaled by a '1' returned by the
196 _bt() stap function and appended to the stapbm call), we need to place a
197 probe point on the method__bt marker in the libHelperSDT_*.so . We've created
198 this new marker as we don't want it interfering with the method__X markers of
199 the same rulename. The overall flow of backtraces are the same as 'regular'
200 method probes, however run through STAP_BACKTRACE helper method, and
201 METHOD_STAP_BT native method in turn.
202
203 STAP_BACKTRACE also converts the throwable object to a string for us to pass/report
204
205 The end result is we need to place another probe point automatically for the user;
206 process("$pkglibdir/libHelperSDT_*.so").provider("HelperSDT").mark("method__bt")
207 and pass the backtrace string to the java_backtrace_string variable, which then gets
208 immediately passed to the subsequent mark("method_XX") probe through the java_backtrace()
209 function's return value.
210 */
211
212 struct java_details_inspection jdi;
213 base->body->visit(&jdi);
214
215 // the wildcard is deliberate to catch all architectures
216 string libhelper = string(PKGLIBDIR) + "/libHelperSDT_*.so";
217 string rule_name = "module_name() . " + lex_cast_qstring(base->name());
218 const token* tok = base->body->tok;
219
220 if (jdi.java_backtrace)
221 {
222 stringstream bt_code;
223 bt_code << "probe process(" << literal_string(libhelper) << ")"
224 << ".provider(\"HelperSDT\").mark(\"method__bt\") {" << endl;
225
226 // Make sure the rule name in the last arg matches this probe
227 bt_code << "if (user_string($arg3) != " << rule_name << ") next;" << endl;
228
229 // $arg1 is the backtrace string, $arg2 is the stack depth
230 bt_code << "__assign_stacktrace($arg1, $arg2);" << endl;
231
232 bt_code << "}" << endl; // End of probe
233
234 probe* new_mark_bt_probe = parse_synthetic_probe (sess, bt_code, tok);
235 if (!new_mark_bt_probe)
236 throw SEMANTIC_ERROR (_("can't create java backtrace probe"), tok);
237 derive_probes(sess, new_mark_bt_probe, finished_results);
238
239
240 // Now to delete the backtrace string
241 stringstream btd_code;
242 btd_code << "probe process(" << literal_string(libhelper) << ")"
243 << ".provider(\"HelperSDT\").mark(\"method__bt__delete\") {" << endl;
244
245 // make sure the rule name in the last arg matches this probe
246 btd_code << "if (user_string($arg1) != " << rule_name << ") next;" << endl;
247
248 btd_code << "__delete_backtrace();" << endl;
249
250 btd_code << "}" << endl; // End of probe
251
252 probe* new_mark_btd_probe = parse_synthetic_probe (sess, btd_code, tok);
253 if (!new_mark_btd_probe)
254 throw SEMANTIC_ERROR (_("can't create java backtrace delete probe"), tok);
255 derive_probes(sess, new_mark_btd_probe, finished_results);
256 }
257
258 /* The overall flow of control during a probed java method is something like this:
259
260 (java) java-method ->
261 (java) byteman ->
262 (java) HelperSDT::METHOD_STAP_PROBENN ->
263 (JNI) HelperSDT_arch.so ->
264 (C) sys/sdt.h marker STAP_PROBEN(hotspot,method__N,...,rulename)
265
266 To catch the java-method hit that belongs to this very systemtap
267 probe, we use the rulename string as the identifier. It has to have
268 some cool properties:
269 - be unique system-wide, so as to avoid collisions between concurrent users, even if
270 they run the same stap script
271 - be unique within the script, so distinct probe handlers get run if specified
272 - be computable from systemtap at run-time (since compile-time can't be unique enough)
273 - be passable to stapbm, back through a .btm (byteman rule) file, back through sdt.h parameters
274
275 The rulename is thusly synthesized as the string-concatenation expression
276 (module_name() . "probe_NNN")
277 */
278
279 stringstream code;
280 code << "probe process(" << literal_string(libhelper) << ")" << ".provider(\"HelperSDT\")"
281 << ".mark(" << literal_string (mark_param(method_params_count)) << ") {" << endl;
282
283
284 // Make sure the rule name in the last arg matches this probe
285 code << "if (user_string($arg" << (method_params_count+1)
286 << ") != " << rule_name << ") next;" << endl;
287
288 code << "}" << endl; // End of probe
289
290 probe* new_mark_probe = parse_synthetic_probe (sess, code, tok);
291 if (!new_mark_probe)
292 throw SEMANTIC_ERROR (_("can't create java method probe"), tok);
293
294 // Link this main probe back to the original base, with an
295 // additional probe intermediate to catch probe listing.
296 new_mark_probe->base = new probe(base, loc);
297
298 // Splice base->body in after the parsed body
299 new_mark_probe->body = new block (new_mark_probe->body, base->body);
300
301 derive_probes (sess, new_mark_probe, finished_results);
302
303
304 // the begin portion of the probe to install byteman rules in the target jvm
305 stringstream begin_code;
306 begin_code << "probe begin {" << endl;
307
308 /* stapbm takes the following arguments:
309 $1 - install/uninstall
310 $2 - JVM PID/unique name
311 $3 - RULE name <--- identifies this probe uniquely at run time
312 $4 - class
313 $5 - method
314 $6 - number of args
315 $7 - entry/exit/line
316 $8 - backtrace
317 */
318
319 string leftbits = string(PKGLIBDIR) + "/stapbm install " +
320 lex_cast_qstring(has_pid_int ? java_pid_str : _java_proc_class) + " ";
321
322 string rightbits = " " + lex_cast_qstring(class_str_val) +
323 " " + lex_cast_qstring(method_str_val) +
324 " " + lex_cast(method_params_count) +
325 " " + ((!has_return && !has_line_number) ? string("entry") :
326 ((has_return && !has_line_number) ? string("exit") :
327 (string)method_line_val)) +
328 " " + (jdi.java_backtrace ? string("1") : string("0"));
329
330 begin_code << "system(" << literal_string(leftbits) << " . " << rule_name
331 << " . " << literal_string(rightbits) << ");" << endl;
332
333 begin_code << "}" << endl; // End of probe
334
335 probe* new_begin_probe = parse_synthetic_probe (sess, begin_code, tok);
336 if (!new_begin_probe)
337 throw SEMANTIC_ERROR (_("can't create java begin probe"), tok);
338 derive_probes (sess, new_begin_probe, finished_results);
339
340
341 // the end/error portion of the probe to uninstall byteman rules from the target jvm
342 stringstream end_code;
343 end_code << "probe end, error {" << endl;
344
345 leftbits = string(PKGLIBDIR) + "/stapbm uninstall " +
346 lex_cast_qstring(has_pid_int ? java_pid_str : _java_proc_class) + " ";
347 // rightbits are the same as the begin probe
348
349 end_code << "system(" << literal_string(leftbits) << " . " << rule_name
350 << " . " << literal_string(rightbits) << ");" << endl;
351
352 end_code << "}" << endl; // End of probe
353
354 probe* new_end_probe = parse_synthetic_probe (sess, end_code, tok);
355 if (!new_end_probe)
356 throw SEMANTIC_ERROR (_("can't create java end probe"), tok);
357 derive_probes (sess, new_end_probe, finished_results);
358 }
359
360 void
361 register_tapset_java (systemtap_session& s)
362 {
363 (void) s;
364
365 #ifdef HAVE_JAVA
366 match_node* root = s.pattern_root;
367 derived_probe_builder *builder = new java_builder ();
368
369 root->bind_str (TOK_JAVA)
370 ->bind_str (TOK_CLASS)->bind_str (TOK_METHOD)
371 ->bind_privilege(pr_all)
372 ->bind(builder);
373
374 root->bind_str (TOK_JAVA)
375 ->bind_str (TOK_CLASS)->bind_str (TOK_METHOD)
376 ->bind (TOK_RETURN)
377 ->bind_privilege(pr_all)
378 ->bind(builder);
379
380 root->bind_num (TOK_JAVA)
381 ->bind_str (TOK_CLASS)->bind_str (TOK_METHOD)
382 ->bind_privilege(pr_all)
383 ->bind (builder);
384
385 root->bind_num (TOK_JAVA)
386 ->bind_str (TOK_CLASS)->bind_str (TOK_METHOD)
387 ->bind (TOK_RETURN)
388 ->bind_privilege(pr_all)
389 ->bind (builder);
390 #endif
391 }
392
393 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.050252 seconds and 5 git commands to generate.