Skip to content

Commit

Permalink
Merge pull request #35 from jrudolph/w/25-output-all-inlined-frames
Browse files Browse the repository at this point in the history
add `unfoldall` output mode
  • Loading branch information
jrudolph committed May 14, 2016
2 parents 279a616 + 826a8c3 commit 307dc69
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 11 deletions.
15 changes: 14 additions & 1 deletion README.md
Expand Up @@ -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
Expand Down Expand Up @@ -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-<pid>.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
Expand Down
81 changes: 71 additions & 10 deletions src/c/perf-map-agent.c
Expand Up @@ -32,12 +32,15 @@
#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;
bool debug_dump_unfold_entries = false;

FILE *method_file = NULL;
void open_map_file() {
Expand Down Expand Up @@ -140,7 +143,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,
Expand All @@ -149,15 +152,60 @@ 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);
}

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 %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);

int i;
for (i = 0; i < record->numpcs; i++) {
PCStackInfo *info = &record->pcinfo[i];
printf(" %p 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,
Expand All @@ -170,6 +218,10 @@ void generate_unfolded_entries(
char root_name[STRING_BUFFER_SIZE];

sig_string(jvmti, root_method, root_name, sizeof(root_name));

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;

Expand All @@ -185,10 +237,13 @@ void generate_unfolded_entries(

// as long as the top method remains the same we delay recording
if (cur_method != top_method) {

// 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);

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;
Expand All @@ -199,7 +254,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, cur_method, 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);
Expand Down Expand Up @@ -265,10 +324,12 @@ 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;
debug_dump_unfold_entries = strstr(options, "debug_dump_unfold_entries") != NULL;

jvmtiEnv *jvmti;
(*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1);
Expand Down

0 comments on commit 307dc69

Please sign in to comment.