1 // Tapset for per-method based probes
2 // Copyright (C) 2014 Red Hat Inc.
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
11 #include "translate.h"
18 #include "sys/types.h"
31 using namespace __gnu_cxx
;
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");
44 // --------------------------------------------------------------------------
46 struct java_details_inspection
: public functioncall_traversing_visitor
50 java_details_inspection(): java_backtrace(false) {}
52 void visit_functioncall(functioncall
* e
);
56 java_details_inspection::visit_functioncall(functioncall
* e
)
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
63 traversing_visitor::visit_functioncall(e
);
66 struct java_builder
: public derived_probe_builder
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
;
78 void build (systemtap_session
& sess
,
80 probe_point
* location
,
81 literal_map_t
const & parameters
,
82 vector
<derived_probe
*> & finished_results
);
83 std::string
mark_param(int i
);
87 java_builder::mark_param(int i
)
118 java_builder::build (systemtap_session
& sess
,
121 literal_map_t
const & parameters
,
122 vector
<derived_probe
*> & finished_results
)
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
)
132 int second_method_pos
= 0;
133 second_method_pos
= method_str_val
.find (')');
134 if ((second_method_pos
- short_method_pos
) > 1)
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;
147 // wildcards in Java probes are not allowed, so the location is already
149 loc
->well_formed
= true;
151 //find if we're probing at a specific line number
152 size_t line_position
= 0;
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;
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"));
170 //need to count the number of parameters, exit if more than 10
172 int method_params_count
= count (method_str_val
.begin (), method_str_val
.end (), ',');
174 method_params_count
++; // in this case we know there was at least a var, but no ','
176 if (method_params_count
> 10)
177 throw SEMANTIC_ERROR (_("maximum of 10 java method parameters may be specified"));
179 assert (has_method_str
);
180 (void) has_method_str
;
181 assert (has_class_str
);
182 (void) has_class_str
;
184 interned_string java_pid_str
= "";
186 java_pid_str
= lex_cast(_java_pid
);
188 java_pid_str
= _java_proc_class
;
190 if (! (has_pid_int
|| has_pid_str
) )
191 throw SEMANTIC_ERROR (_("missing JVMID"));
193 /* Java native backtrace probe point
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.
203 STAP_BACKTRACE also converts the throwable object to a string for us to pass/report
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.
212 struct java_details_inspection jdi
;
213 base
->body
->visit(&jdi
);
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
;
220 if (jdi
.java_backtrace
)
222 stringstream bt_code
;
223 bt_code
<< "probe process(" << literal_string(libhelper
) << ")"
224 << ".provider(\"HelperSDT\").mark(\"method__bt\") {" << endl
;
226 // Make sure the rule name in the last arg matches this probe
227 bt_code
<< "if (user_string($arg3) != " << rule_name
<< ") next;" << endl
;
229 // $arg1 is the backtrace string, $arg2 is the stack depth
230 bt_code
<< "__assign_stacktrace($arg1, $arg2);" << endl
;
232 bt_code
<< "}" << endl
; // End of probe
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
);
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
;
245 // make sure the rule name in the last arg matches this probe
246 btd_code
<< "if (user_string($arg1) != " << rule_name
<< ") next;" << endl
;
248 btd_code
<< "__delete_backtrace();" << endl
;
250 btd_code
<< "}" << endl
; // End of probe
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
);
258 /* The overall flow of control during a probed java method is something like this:
260 (java) java-method ->
262 (java) HelperSDT::METHOD_STAP_PROBENN ->
263 (JNI) HelperSDT_arch.so ->
264 (C) sys/sdt.h marker STAP_PROBEN(hotspot,method__N,...,rulename)
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
275 The rulename is thusly synthesized as the string-concatenation expression
276 (module_name() . "probe_NNN")
280 code
<< "probe process(" << literal_string(libhelper
) << ")" << ".provider(\"HelperSDT\")"
281 << ".mark(" << literal_string (mark_param(method_params_count
)) << ") {" << endl
;
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
;
288 code
<< "}" << endl
; // End of probe
290 probe
* new_mark_probe
= parse_synthetic_probe (sess
, code
, tok
);
292 throw SEMANTIC_ERROR (_("can't create java method probe"), tok
);
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
);
298 // Splice base->body in after the parsed body
299 new_mark_probe
->body
= new block (new_mark_probe
->body
, base
->body
);
301 derive_probes (sess
, new_mark_probe
, finished_results
);
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
;
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
319 string leftbits
= string(PKGLIBDIR
) + "/stapbm install " +
320 lex_cast_qstring(has_pid_int
? java_pid_str
: _java_proc_class
) + " ";
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"));
330 begin_code
<< "system(" << literal_string(leftbits
) << " . " << rule_name
331 << " . " << literal_string(rightbits
) << ");" << endl
;
333 begin_code
<< "}" << endl
; // End of probe
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
);
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
;
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
349 end_code
<< "system(" << literal_string(leftbits
) << " . " << rule_name
350 << " . " << literal_string(rightbits
) << ");" << endl
;
352 end_code
<< "}" << endl
; // End of probe
354 probe
* new_end_probe
= parse_synthetic_probe (sess
, end_code
, tok
);
356 throw SEMANTIC_ERROR (_("can't create java end probe"), tok
);
357 derive_probes (sess
, new_end_probe
, finished_results
);
361 register_tapset_java (systemtap_session
& s
)
366 match_node
* root
= s
.pattern_root
;
367 derived_probe_builder
*builder
= new java_builder ();
369 root
->bind_str (TOK_JAVA
)
370 ->bind_str (TOK_CLASS
)->bind_str (TOK_METHOD
)
371 ->bind_privilege(pr_all
)
374 root
->bind_str (TOK_JAVA
)
375 ->bind_str (TOK_CLASS
)->bind_str (TOK_METHOD
)
377 ->bind_privilege(pr_all
)
380 root
->bind_num (TOK_JAVA
)
381 ->bind_str (TOK_CLASS
)->bind_str (TOK_METHOD
)
382 ->bind_privilege(pr_all
)
385 root
->bind_num (TOK_JAVA
)
386 ->bind_str (TOK_CLASS
)->bind_str (TOK_METHOD
)
388 ->bind_privilege(pr_all
)
393 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */