2323#include <string.h>
2424#include <unistd.h>
2525
26- #include <sys/types.h>
27-
2826#include <jni.h>
2927#include <jvmti.h>
3028#include <jvmticmlr.h>
@@ -39,10 +37,17 @@ bool unfold_simple = false;
3937bool unfold_all = false;
4038bool print_method_signatures = false;
4139bool print_source_loc = false;
40+
4241bool clean_class_names = false;
42+ bool dotted_class_names = false;
43+ bool annotate_java_frames = false;
44+ char * unfold_delimiter = "->" ;
45+
4346bool debug_dump_unfold_entries = false;
4447
4548FILE * method_file = NULL ;
49+
50+
4651void open_map_file () {
4752 if (!method_file )
4853 method_file = perf_map_open (getpid ());
@@ -52,6 +57,14 @@ void close_map_file() {
5257 method_file = NULL ;
5358}
5459
60+ void deallocate (jvmtiEnv * jvmti , void * string ) {
61+ if (string != NULL ) (* jvmti )-> Deallocate (jvmti , (unsigned char * ) string );
62+ }
63+
64+ char * frame_annotation (bool inlined ) {
65+ return annotate_java_frames ? (inlined ? "_[i]" : "_[j]" ) : "" ;
66+ }
67+
5568static int get_line_number (jvmtiLineNumberEntry * table , jint entry_count , jlocation loc ) {
5669 int i ;
5770 for (i = 0 ; i < entry_count ; i ++ )
@@ -61,21 +74,21 @@ static int get_line_number(jvmtiLineNumberEntry *table, jint entry_count, jlocat
6174}
6275
6376void class_name_from_sig (char * dest , size_t dest_size , const char * sig ) {
64- if (clean_class_names && sig [0 ] == 'L' ) {
65- const char * src = sig + 1 ;
77+ if (( clean_class_names || dotted_class_names ) && sig [0 ] == 'L' ) {
78+ const char * src = clean_class_names ? sig + 1 : sig ;
6679 int i ;
6780 for (i = 0 ; i < (dest_size - 1 ) && src [i ]; i ++ ) {
6881 char c = src [i ];
69- if (c == '/' ) c = '.' ;
70- if (c == ';' ) c = 0 ;
82+ if (dotted_class_names && c == '/' ) c = '.' ;
83+ if (clean_class_names && c == ';' ) break ;
7184 dest [i ] = c ;
7285 }
7386 dest [i ] = 0 ;
7487 } else
7588 strncpy (dest , sig , dest_size );
7689}
7790
78- static void sig_string (jvmtiEnv * jvmti , jmethodID method , char * output , size_t noutput ) {
91+ static void sig_string (jvmtiEnv * jvmti , jmethodID method , char * output , size_t noutput , char * annotation ) {
7992 char * sourcefile = NULL ;
8093 char * method_name = NULL ;
8194 char * msig = NULL ;
@@ -101,9 +114,9 @@ static void sig_string(jvmtiEnv *jvmti, jmethodID method, char *output, size_t n
101114 if (entrycount > 0 ) lineno = lines [0 ].line_number ;
102115 snprintf (source_info , sizeof (source_info ), "(%s:%d)" , sourcefile , lineno );
103116
104- if ( lines != NULL ) ( * jvmti ) -> Deallocate ( jvmti , ( unsigned char * ) lines );
117+ deallocate ( jvmti , lines );
105118 }
106- if ( sourcefile != NULL ) ( * jvmti ) -> Deallocate (jvmti , (unsigned char * ) sourcefile );
119+ deallocate (jvmti , (unsigned char * ) sourcefile );
107120 }
108121 }
109122
@@ -112,28 +125,38 @@ static void sig_string(jvmtiEnv *jvmti, jmethodID method, char *output, size_t n
112125
113126 char class_name [STRING_BUFFER_SIZE ];
114127 class_name_from_sig (class_name , sizeof (class_name ), csig );
115- snprintf (output , noutput , "%s::%s%s%s" , class_name , method_name , method_signature , source_info );
128+ snprintf (output , noutput , "%s::%s%s%s%s" ,
129+ class_name , method_name , method_signature , source_info , annotation );
116130
117- if ( csig != NULL ) ( * jvmti ) -> Deallocate (jvmti , (unsigned char * ) csig );
131+ deallocate (jvmti , (unsigned char * ) csig );
118132 }
119- if ( method_name != NULL ) ( * jvmti ) -> Deallocate (jvmti , (unsigned char * ) method_name );
120- if ( msig != NULL ) ( * jvmti ) -> Deallocate (jvmti , (unsigned char * ) msig );
133+ deallocate (jvmti , (unsigned char * ) method_name );
134+ deallocate (jvmti , (unsigned char * ) msig );
121135 }
122136}
123137
124- void generate_single_entry (jvmtiEnv * jvmti , jmethodID method , const void * code_addr , jint code_size ) {
138+ void generate_single_entry (
139+ jvmtiEnv * jvmti ,
140+ jmethodID method ,
141+ const void * code_addr ,
142+ jint code_size ) {
125143 char entry [STRING_BUFFER_SIZE ];
126- sig_string (jvmti , method , entry , sizeof (entry ));
144+ sig_string (jvmti , method , entry , sizeof (entry ), frame_annotation (false) );
127145 perf_map_write_entry (method_file , code_addr , (unsigned int ) code_size , entry );
128146}
129147
130148/* Generates either a simple or a complex unfolded entry. */
131- void generate_unfolded_entry (jvmtiEnv * jvmti , jmethodID method , char * buffer , size_t buffer_size , const char * root_name ) {
149+ void generate_unfolded_entry (
150+ jvmtiEnv * jvmti ,
151+ jmethodID method ,
152+ char * buffer ,
153+ size_t buffer_size ,
154+ const char * root_name ) {
132155 if (unfold_simple )
133- sig_string (jvmti , method , buffer , buffer_size );
156+ sig_string (jvmti , method , buffer , buffer_size , "" );
134157 else {
135158 char entry_name [STRING_BUFFER_SIZE ];
136- sig_string (jvmti , method , entry_name , sizeof (entry_name ));
159+ sig_string (jvmti , method , entry_name , sizeof (entry_name ), "" );
137160 snprintf (buffer , buffer_size , "%s in %s" , entry_name , root_name );
138161 }
139162}
@@ -154,20 +177,22 @@ void write_unfolded_entry(
154177 char full_name [BIG_STRING_BUFFER_SIZE ];
155178 full_name [0 ] = '\0' ;
156179 int i ;
157- for (i = info -> numstackframes - 1 ; i >= 0 ; i -- ) {
158- //printf("At %d method is %d len %d remaining %d\n", i, info->methods[i], strlen(full_name), sizeof(full_name) - 1 - strlen(full_name));
159- sig_string (jvmti , info -> methods [i ], inlined_name , sizeof (inlined_name ));
180+ // the stack is ordered from leaf@[0] to root@[length-1], so we traverse backwards to construct the unfolded string
181+ const jint first_frame = info -> numstackframes - 1 ;
182+ for (i = first_frame ; i >= 0 ; i -- ) {
183+ sig_string (jvmti , info -> methods [i ], inlined_name , sizeof (inlined_name ), frame_annotation (i != first_frame ));
160184 strncat (full_name , inlined_name , sizeof (full_name ) - 1 - strlen (full_name )); // TODO optimize
161- if (i != 0 ) strncat (full_name , "->" , sizeof (full_name ) - 1 - strlen (full_name ));
185+ if (i != 0 ) strncat (full_name , unfold_delimiter , sizeof (full_name ) - 1 - strlen (full_name ));
162186 }
163187 entry_p = full_name ;
164188 } else {
165189 jmethodID cur_method = info -> methods [0 ]; // top of stack
166190 if (cur_method != root_method ) {
167191 generate_unfolded_entry (jvmti , cur_method , inlined_name , sizeof (inlined_name ), root_name );
168192 entry_p = inlined_name ;
169- } else
193+ } else {
170194 entry_p = root_name ;
195+ }
171196 }
172197
173198 perf_map_write_entry (method_file , start_addr , (unsigned int ) (end_addr - start_addr ), entry_p );
@@ -177,13 +202,11 @@ void dump_entries(
177202 jvmtiEnv * jvmti ,
178203 jmethodID root_method ,
179204 jint code_size ,
180- const void * code_addr ,
181- jint map_length ,
182- const jvmtiAddrLocationMap * map ,
183- const void * compile_info ) {
205+ const void * code_addr ,
206+ const void * compile_info ) {
184207 const jvmtiCompiledMethodLoadRecordHeader * header = compile_info ;
185208 char root_name [STRING_BUFFER_SIZE ];
186- sig_string (jvmti , root_method , root_name , sizeof (root_name ));
209+ sig_string (jvmti , root_method , root_name , sizeof (root_name ), "" );
187210 printf ("At %s size %x from %p to %p" , root_name , code_size , code_addr , code_addr + code_size );
188211 if (header -> kind == JVMTI_CMLR_INLINE_INFO ) {
189212 const jvmtiCompiledMethodLoadInlineRecord * record = (jvmtiCompiledMethodLoadInlineRecord * ) header ;
@@ -197,7 +220,7 @@ void dump_entries(
197220 int j ;
198221 for (j = 0 ; j < info -> numstackframes ; j ++ ) {
199222 char buf [2000 ];
200- sig_string (jvmti , info -> methods [j ], buf , sizeof (buf ));
223+ sig_string (jvmti , info -> methods [j ], buf , sizeof (buf ), "" );
201224 printf (" %s\n" , buf );
202225 }
203226 }
@@ -209,16 +232,14 @@ void generate_unfolded_entries(
209232 jmethodID root_method ,
210233 jint code_size ,
211234 const void * code_addr ,
212- jint map_length ,
213- const jvmtiAddrLocationMap * map ,
214235 const void * compile_info ) {
215236 const jvmtiCompiledMethodLoadRecordHeader * header = compile_info ;
216237 char root_name [STRING_BUFFER_SIZE ];
217238
218- sig_string (jvmti , root_method , root_name , sizeof (root_name ));
239+ sig_string (jvmti , root_method , root_name , sizeof (root_name ), "" );
219240
220241 if (debug_dump_unfold_entries )
221- dump_entries (jvmti , root_method , code_size , code_addr , map_length , map , compile_info );
242+ dump_entries (jvmti , root_method , code_size , code_addr , compile_info );
222243
223244 if (header -> kind == JVMTI_CMLR_INLINE_INFO ) {
224245 const jvmtiCompiledMethodLoadInlineRecord * record = (jvmtiCompiledMethodLoadInlineRecord * ) header ;
@@ -258,8 +279,9 @@ void generate_unfolded_entries(
258279 else
259280 generate_single_entry (jvmti , root_method , start_addr , (unsigned int ) (end_addr - start_addr ));
260281 }
261- } else
282+ } else {
262283 generate_single_entry (jvmti , root_method , code_addr , code_size );
284+ }
263285}
264286
265287static void JNICALL
@@ -272,7 +294,7 @@ cbCompiledMethodLoad(
272294 const jvmtiAddrLocationMap * map ,
273295 const void * compile_info ) {
274296 if (unfold_inlined_methods && compile_info != NULL )
275- generate_unfolded_entries (jvmti , method , code_size , code_addr , map_length , map , compile_info );
297+ generate_unfolded_entries (jvmti , method , code_size , code_addr , compile_info );
276298 else
277299 generate_single_entry (jvmti , method , code_addr , code_size );
278300}
@@ -286,10 +308,8 @@ cbDynamicCodeGenerated(jvmtiEnv *jvmti,
286308}
287309
288310void set_notification_mode (jvmtiEnv * jvmti , jvmtiEventMode mode ) {
289- (* jvmti )-> SetEventNotificationMode (jvmti , mode ,
290- JVMTI_EVENT_COMPILED_METHOD_LOAD , (jthread )NULL );
291- (* jvmti )-> SetEventNotificationMode (jvmti , mode ,
292- JVMTI_EVENT_DYNAMIC_CODE_GENERATED , (jthread )NULL );
311+ (* jvmti )-> SetEventNotificationMode (jvmti , mode , JVMTI_EVENT_COMPILED_METHOD_LOAD , (jthread )NULL );
312+ (* jvmti )-> SetEventNotificationMode (jvmti , mode , JVMTI_EVENT_DYNAMIC_CODE_GENERATED , (jthread )NULL );
293313}
294314
295315jvmtiError enable_capabilities (jvmtiEnv * jvmti ) {
@@ -326,7 +346,13 @@ Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
326346 unfold_inlined_methods = strstr (options , "unfold" ) != NULL || unfold_simple || unfold_all ;
327347 print_method_signatures = strstr (options , "msig" ) != NULL ;
328348 print_source_loc = strstr (options , "sourcepos" ) != NULL ;
329- clean_class_names = strstr (options , "dottedclass" ) != NULL ;
349+ dotted_class_names = strstr (options , "dottedclass" ) != NULL ;
350+ clean_class_names = strstr (options , "cleanclass" ) != NULL ;
351+ annotate_java_frames = strstr (options , "annotate_java_frames" ) != NULL ;
352+
353+ bool use_semicolon_unfold_delimiter = strstr (options , "use_semicolon_unfold_delimiter" ) != NULL ;
354+ unfold_delimiter = use_semicolon_unfold_delimiter ? ";" : "->" ;
355+
330356 debug_dump_unfold_entries = strstr (options , "debug_dump_unfold_entries" ) != NULL ;
331357
332358 jvmtiEnv * jvmti ;
0 commit comments