From 13f2d57342523e0dfcc05ff63071f0af7c10b298 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Thu, 17 Dec 2015 17:41:20 +0100 Subject: [PATCH 1/4] new unfoldall mode that outputs complete inlined stack per entry --- src/c/perf-map-agent.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/c/perf-map-agent.c b/src/c/perf-map-agent.c index 9138210..a302466 100644 --- a/src/c/perf-map-agent.c +++ b/src/c/perf-map-agent.c @@ -32,9 +32,11 @@ #include "perf-map-file.h" #define STRING_BUFFER_SIZE 2000 +#define BIG_STRING_BUFFER_SIZE 20000 bool unfold_inlined_methods = false; bool unfold_simple = false; +bool unfold_all = false; bool print_method_signatures = false; bool print_source_loc = false; bool clean_class_names = false; @@ -140,7 +142,7 @@ void generate_unfolded_entry(jvmtiEnv *jvmti, jmethodID method, char *buffer, si /* Generates and writes a single entry for a given inlined method. */ void write_unfolded_entry( jvmtiEnv *jvmti, - jmethodID cur_method, + PCStackInfo *info, jmethodID root_method, const char *root_name, const void *start_addr, @@ -149,11 +151,25 @@ void write_unfolded_entry( char inlined_name[STRING_BUFFER_SIZE * 2 + 4]; const char *entry_p; - if (cur_method != root_method) { - generate_unfolded_entry(jvmti, cur_method, inlined_name, sizeof(inlined_name), root_name); - entry_p = inlined_name; - } else - entry_p = root_name; + if (unfold_all) { + char full_name[BIG_STRING_BUFFER_SIZE]; + full_name[0] = '\0'; + int i; + for (i = info->numstackframes - 1; i >= 0; i--) { + //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)); + sig_string(jvmti, info->methods[i], inlined_name, sizeof(inlined_name)); + strncat(full_name, inlined_name, sizeof(full_name) - 1 - strlen(full_name)); // TODO optimize + if (i != 0) strncat(full_name, "->", sizeof(full_name)); + } + entry_p = full_name; + } else { + jmethodID cur_method = info->methods[0]; // top of stack + if (cur_method != root_method) { + generate_unfolded_entry(jvmti, cur_method, inlined_name, sizeof(inlined_name), root_name); + entry_p = inlined_name; + } else + entry_p = root_name; + } perf_map_write_entry(method_file, start_addr, end_addr - start_addr, entry_p); } @@ -184,11 +200,11 @@ void generate_unfolded_entries( jmethodID top_method = info->methods[0]; // as long as the top method remains the same we delay recording - if (cur_method != top_method) { + if (cur_method != top_method && i > 0) { // top method has changed, record the range for current method void *end_addr = info->pc; - write_unfolded_entry(jvmti, cur_method, root_method, root_name, start_addr, end_addr); + write_unfolded_entry(jvmti, &record->pcinfo[i - 1], root_method, root_name, start_addr, end_addr); start_addr = info->pc; cur_method = top_method; @@ -199,7 +215,7 @@ void generate_unfolded_entries( if (start_addr != code_addr + code_size) { // end_addr is end of this complete code blob const void *end_addr = code_addr + code_size; - write_unfolded_entry(jvmti, cur_method, root_method, root_name, start_addr, end_addr); + write_unfolded_entry(jvmti, &record->pcinfo[i - 1], root_method, root_name, start_addr, end_addr); } } else generate_single_entry(jvmti, root_method, code_addr, code_size); @@ -265,7 +281,8 @@ Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { open_map_file(); unfold_simple = strstr(options, "unfoldsimple") != NULL; - unfold_inlined_methods = strstr(options, "unfold") != NULL || unfold_simple; + unfold_all = strstr(options, "unfoldall") != NULL; + unfold_inlined_methods = strstr(options, "unfold") != NULL || unfold_simple || unfold_all; print_method_signatures = strstr(options, "msig") != NULL; print_source_loc = strstr(options, "sourcepos") != NULL; clean_class_names = strstr(options, "dottedclass") != NULL; From 4163ad28e3b0c0b41d5adcbfebd69cea7004b33d Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Thu, 17 Dec 2015 20:12:43 +0100 Subject: [PATCH 2/4] fixes and debug output function --- src/c/perf-map-agent.c | 48 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/c/perf-map-agent.c b/src/c/perf-map-agent.c index a302466..8b49b61 100644 --- a/src/c/perf-map-agent.c +++ b/src/c/perf-map-agent.c @@ -174,6 +174,37 @@ void write_unfolded_entry( perf_map_write_entry(method_file, start_addr, end_addr - start_addr, entry_p); } +void dump_entries( + jvmtiEnv *jvmti, + jmethodID root_method, + jint code_size, + const void* code_addr, + jint map_length, + const jvmtiAddrLocationMap* map, + const void* compile_info) { + const jvmtiCompiledMethodLoadRecordHeader *header = compile_info; + char root_name[STRING_BUFFER_SIZE]; + sig_string(jvmti, root_method, root_name, sizeof(root_name)); + printf("At %s size %lx from %lx to %lx", root_name, code_size, code_addr, code_addr + code_size); + if (header->kind == JVMTI_CMLR_INLINE_INFO) { + const jvmtiCompiledMethodLoadInlineRecord *record = (jvmtiCompiledMethodLoadInlineRecord *) header; + printf(" with %d entries\n", record->numpcs); + + int i; + for (i = 0; i < record->numpcs; i++) { + PCStackInfo *info = &record->pcinfo[i]; + printf(" %lx has %d stack entries\n", info->pc, info->numstackframes); + + int j; + for (j = 0; j < info->numstackframes; j++) { + char buf[2000]; + sig_string(jvmti, info->methods[j], buf, sizeof(buf)); + printf(" %s\n", buf); + } + } + } else printf(" with no inline info\n"); +} + void generate_unfolded_entries( jvmtiEnv *jvmti, jmethodID root_method, @@ -186,6 +217,8 @@ void generate_unfolded_entries( char root_name[STRING_BUFFER_SIZE]; sig_string(jvmti, root_method, root_name, sizeof(root_name)); + //dump_entries(jvmti, root_method, code_size, code_addr, map_length, map, compile_info); + //printf("At %s size %lx from %lx to %lx\n", root_name, code_size, code_addr, code_addr + code_size); if (header->kind == JVMTI_CMLR_INLINE_INFO) { const jvmtiCompiledMethodLoadInlineRecord *record = (jvmtiCompiledMethodLoadInlineRecord *) header; @@ -200,11 +233,14 @@ void generate_unfolded_entries( jmethodID top_method = info->methods[0]; // as long as the top method remains the same we delay recording - if (cur_method != top_method && i > 0) { - + if (cur_method != top_method) { // top method has changed, record the range for current method void *end_addr = info->pc; - write_unfolded_entry(jvmti, &record->pcinfo[i - 1], root_method, root_name, start_addr, end_addr); + + if (i > 0) + write_unfolded_entry(jvmti, &record->pcinfo[i - 1], root_method, root_name, start_addr, end_addr); + else + generate_single_entry(jvmti, root_method, start_addr, end_addr - start_addr); start_addr = info->pc; cur_method = top_method; @@ -215,7 +251,11 @@ void generate_unfolded_entries( if (start_addr != code_addr + code_size) { // end_addr is end of this complete code blob const void *end_addr = code_addr + code_size; - write_unfolded_entry(jvmti, &record->pcinfo[i - 1], root_method, root_name, start_addr, end_addr); + + if (i > 0) + write_unfolded_entry(jvmti, &record->pcinfo[i - 1], root_method, root_name, start_addr, end_addr); + else + generate_single_entry(jvmti, root_method, start_addr, end_addr - start_addr); } } else generate_single_entry(jvmti, root_method, code_addr, code_size); From a23d678373549462e38aa025bd251d4c8dbfc5b3 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Fri, 29 Apr 2016 13:38:51 +0200 Subject: [PATCH 3/4] new debug option: dump unfold entries to the console of the target process with `debug_dump_unfold_entries` --- src/c/perf-map-agent.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/c/perf-map-agent.c b/src/c/perf-map-agent.c index 8b49b61..337519f 100644 --- a/src/c/perf-map-agent.c +++ b/src/c/perf-map-agent.c @@ -40,6 +40,7 @@ bool unfold_all = false; bool print_method_signatures = false; bool print_source_loc = false; bool clean_class_names = false; +bool debug_dump_unfold_entries = false; FILE *method_file = NULL; void open_map_file() { @@ -185,7 +186,7 @@ void dump_entries( const jvmtiCompiledMethodLoadRecordHeader *header = compile_info; char root_name[STRING_BUFFER_SIZE]; sig_string(jvmti, root_method, root_name, sizeof(root_name)); - printf("At %s size %lx from %lx to %lx", root_name, code_size, code_addr, code_addr + code_size); + printf("At %s size %x from %p to %p", root_name, code_size, code_addr, code_addr + code_size); if (header->kind == JVMTI_CMLR_INLINE_INFO) { const jvmtiCompiledMethodLoadInlineRecord *record = (jvmtiCompiledMethodLoadInlineRecord *) header; printf(" with %d entries\n", record->numpcs); @@ -193,7 +194,7 @@ void dump_entries( int i; for (i = 0; i < record->numpcs; i++) { PCStackInfo *info = &record->pcinfo[i]; - printf(" %lx has %d stack entries\n", info->pc, info->numstackframes); + printf(" %p has %d stack entries\n", info->pc, info->numstackframes); int j; for (j = 0; j < info->numstackframes; j++) { @@ -217,8 +218,10 @@ void generate_unfolded_entries( char root_name[STRING_BUFFER_SIZE]; sig_string(jvmti, root_method, root_name, sizeof(root_name)); - //dump_entries(jvmti, root_method, code_size, code_addr, map_length, map, compile_info); - //printf("At %s size %lx from %lx to %lx\n", root_name, code_size, code_addr, code_addr + code_size); + + if (debug_dump_unfold_entries) + dump_entries(jvmti, root_method, code_size, code_addr, map_length, map, compile_info); + if (header->kind == JVMTI_CMLR_INLINE_INFO) { const jvmtiCompiledMethodLoadInlineRecord *record = (jvmtiCompiledMethodLoadInlineRecord *) header; @@ -326,6 +329,7 @@ Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { print_method_signatures = strstr(options, "msig") != NULL; print_source_loc = strstr(options, "sourcepos") != NULL; clean_class_names = strstr(options, "dottedclass") != NULL; + debug_dump_unfold_entries = strstr(options, "debug_dump_unfold_entries") != NULL; jvmtiEnv *jvmti; (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1); From 826a8c3b366dd253f281a241cfdf33ba7b87864d Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Fri, 29 Apr 2016 14:14:34 +0200 Subject: [PATCH 4/4] documentation for the `unfoldall` option --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 19ca7ff..8d11c6e 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,9 @@ You can add a comma separated list of options to `perf-java` (or the `AttachOnce - `unfold`: Create extra entries for every codeblock inside a method that was inlined from elsewhere (named <inlined_method> in <root_method>). Be aware of the effects of 'skid' in relation with unfolding. - See the section below. + See the section below. Also, see the below section about inaccurate inlining information. + - `unfoldall`: Similar to `unfold` but will include the complete inlined stack at a code location in the form + `root_method->inlined method 1->inlined method 2->...->inlined method on top`. - `unfoldsimple`: similar to `unfold`, however, the extra entries do not include the " in <root_method>" part - `msig`: include full method signature in the name string - `dottedclass`: convert class signature (`Ljava/lang/Class;`) to the usual class names with segments separated by dots @@ -94,6 +96,17 @@ tools that operate on the symbol level like the standard views of `perf report`, So, while it is tempting to enable unfolded entries for the perceived extra resolution, this extra information is sometimes just noise which will not only clutter the overall view but may also be misleading or wrong. +### Inaccurate mappings using the `unfold*` options + +Hotspot does not retain line number and other debug information for inlined code at other places than safepoints. This +makes sense because you don't usually observe code running between safepoints from the JVM's perspective. This is different +when observing a process from the outside like with `perf`. For observed code locations outside of safepoints, the JVM will +not report any inlining information and perf-map-agent will assign those areas to the host method of the inlining. + +For more fidelity, Hotspot can be instructed to include debug information for non-safepoints as well. Use +`-XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints` when running the target process. Note, however, that this will +produce a lot more information with the generated `perf-.map` file potentially growing to MBs of size. + ### Agent Library Unloading Unloading or reloading of a changed agent library is not supported by the JVM (but re-attaching is). Therefore, if you make changes to the