Skip to content

Commit

Permalink
Add option to explicitly annotate Java frames with _[j]/_[i]
Browse files Browse the repository at this point in the history
This is done to natively support frame disambiguation. This is
more accurate than than the approach taken in
FlameGraph/stackcollapse-perf.pl which relies on package names.
I did lump together some code cleanup here, resolving many
warnings pointed out by CLion, refactoring deallocate etc. If
this is too much I can split the commit into separate PRs.
  • Loading branch information
nitsanw committed Jan 31, 2018
1 parent 9af95e9 commit 349f1c2
Showing 1 changed file with 66 additions and 40 deletions.
106 changes: 66 additions & 40 deletions src/c/perf-map-agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
#include <string.h>
#include <unistd.h>

#include <sys/types.h>

#include <jni.h>
#include <jvmti.h>
#include <jvmticmlr.h>
Expand All @@ -39,10 +37,17 @@ bool unfold_simple = false;
bool unfold_all = false;
bool print_method_signatures = false;
bool print_source_loc = false;

bool clean_class_names = false;
bool dotted_class_names = false;
bool annotate_java_frames = false;
char *unfold_delimiter = "->";

bool debug_dump_unfold_entries = false;

FILE *method_file = NULL;


void open_map_file() {
if (!method_file)
method_file = perf_map_open(getpid());
Expand All @@ -52,6 +57,14 @@ void close_map_file() {
method_file = NULL;
}

void deallocate(jvmtiEnv *jvmti, void *string) {
if (string != NULL) (*jvmti)->Deallocate(jvmti, (unsigned char *) string);
}

char *frame_annotation(bool inlined) {
return annotate_java_frames ? (inlined ? "_[i]" : "_[j]") : "";
}

static int get_line_number(jvmtiLineNumberEntry *table, jint entry_count, jlocation loc) {
int i;
for (i = 0; i < entry_count; i++)
Expand All @@ -61,21 +74,21 @@ static int get_line_number(jvmtiLineNumberEntry *table, jint entry_count, jlocat
}

void class_name_from_sig(char *dest, size_t dest_size, const char *sig) {
if (clean_class_names && sig[0] == 'L') {
const char *src = sig + 1;
if ((clean_class_names || dotted_class_names) && sig[0] == 'L') {
const char *src = clean_class_names ? sig + 1 : sig;
int i;
for(i = 0; i < (dest_size - 1) && src[i]; i++) {
char c = src[i];
if (c == '/') c = '.';
if (c == ';') c = 0;
if (dotted_class_names && c == '/') c = '.';
if (clean_class_names && c == ';') break;
dest[i] = c;
}
dest[i] = 0;
} else
strncpy(dest, sig, dest_size);
}

static void sig_string(jvmtiEnv *jvmti, jmethodID method, char *output, size_t noutput) {
static void sig_string(jvmtiEnv *jvmti, jmethodID method, char *output, size_t noutput, char *annotation) {
char *sourcefile = NULL;
char *method_name = NULL;
char *msig = NULL;
Expand All @@ -101,9 +114,9 @@ static void sig_string(jvmtiEnv *jvmti, jmethodID method, char *output, size_t n
if(entrycount > 0) lineno = lines[0].line_number;
snprintf(source_info, sizeof(source_info), "(%s:%d)", sourcefile, lineno);

if (lines != NULL) (*jvmti)->Deallocate(jvmti, (unsigned char *) lines);
deallocate(jvmti, lines);
}
if (sourcefile != NULL) (*jvmti)->Deallocate(jvmti, (unsigned char *) sourcefile);
deallocate(jvmti, (unsigned char *) sourcefile);
}
}

Expand All @@ -112,28 +125,38 @@ static void sig_string(jvmtiEnv *jvmti, jmethodID method, char *output, size_t n

char class_name[STRING_BUFFER_SIZE];
class_name_from_sig(class_name, sizeof(class_name), csig);
snprintf(output, noutput, "%s::%s%s%s", class_name, method_name, method_signature, source_info);
snprintf(output, noutput, "%s::%s%s%s%s",
class_name, method_name, method_signature, source_info, annotation);

if (csig != NULL) (*jvmti)->Deallocate(jvmti, (unsigned char *) csig);
deallocate(jvmti, (unsigned char *) csig);
}
if (method_name != NULL) (*jvmti)->Deallocate(jvmti, (unsigned char *) method_name);
if (msig != NULL) (*jvmti)->Deallocate(jvmti, (unsigned char *) msig);
deallocate(jvmti, (unsigned char *) method_name);
deallocate(jvmti, (unsigned char *) msig);
}
}

void generate_single_entry(jvmtiEnv *jvmti, jmethodID method, const void *code_addr, jint code_size) {
void generate_single_entry(
jvmtiEnv *jvmti,
jmethodID method,
const void *code_addr,
jint code_size) {
char entry[STRING_BUFFER_SIZE];
sig_string(jvmti, method, entry, sizeof(entry));
sig_string(jvmti, method, entry, sizeof(entry), frame_annotation(false));
perf_map_write_entry(method_file, code_addr, (unsigned int) code_size, entry);
}

/* Generates either a simple or a complex unfolded entry. */
void generate_unfolded_entry(jvmtiEnv *jvmti, jmethodID method, char *buffer, size_t buffer_size, const char *root_name) {
void generate_unfolded_entry(
jvmtiEnv *jvmti,
jmethodID method,
char *buffer,
size_t buffer_size,
const char *root_name) {
if (unfold_simple)
sig_string(jvmti, method, buffer, buffer_size);
sig_string(jvmti, method, buffer, buffer_size, "");
else {
char entry_name[STRING_BUFFER_SIZE];
sig_string(jvmti, method, entry_name, sizeof(entry_name));
sig_string(jvmti, method, entry_name, sizeof(entry_name), "");
snprintf(buffer, buffer_size, "%s in %s", entry_name, root_name);
}
}
Expand All @@ -154,20 +177,22 @@ void write_unfolded_entry(
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));
// the stack is ordered from leaf@[0] to root@[length-1], so we traverse backwards to construct the unfolded string
const jint first_frame = info->numstackframes - 1;
for (i = first_frame; i >= 0; i--) {
sig_string(jvmti, info->methods[i], inlined_name, sizeof(inlined_name), frame_annotation(i != first_frame));
strncat(full_name, inlined_name, sizeof(full_name) - 1 - strlen(full_name)); // TODO optimize
if (i != 0) strncat(full_name, "->", sizeof(full_name) -1 - strlen(full_name));
if (i != 0) strncat(full_name, unfold_delimiter, sizeof(full_name) -1 - strlen(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
} else {
entry_p = root_name;
}
}

perf_map_write_entry(method_file, start_addr, (unsigned int) (end_addr - start_addr), entry_p);
Expand All @@ -177,13 +202,11 @@ 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 void *code_addr,
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));
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;
Expand All @@ -197,7 +220,7 @@ void dump_entries(
int j;
for (j = 0; j < info->numstackframes; j++) {
char buf[2000];
sig_string(jvmti, info->methods[j], buf, sizeof(buf));
sig_string(jvmti, info->methods[j], buf, sizeof(buf), "");
printf(" %s\n", buf);
}
}
Expand All @@ -209,16 +232,14 @@ void generate_unfolded_entries(
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));
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);
dump_entries(jvmti, root_method, code_size, code_addr, compile_info);

if (header->kind == JVMTI_CMLR_INLINE_INFO) {
const jvmtiCompiledMethodLoadInlineRecord *record = (jvmtiCompiledMethodLoadInlineRecord *) header;
Expand Down Expand Up @@ -258,8 +279,9 @@ void generate_unfolded_entries(
else
generate_single_entry(jvmti, root_method, start_addr, (unsigned int) (end_addr - start_addr));
}
} else
} else {
generate_single_entry(jvmti, root_method, code_addr, code_size);
}
}

static void JNICALL
Expand All @@ -272,7 +294,7 @@ cbCompiledMethodLoad(
const jvmtiAddrLocationMap* map,
const void* compile_info) {
if (unfold_inlined_methods && compile_info != NULL)
generate_unfolded_entries(jvmti, method, code_size, code_addr, map_length, map, compile_info);
generate_unfolded_entries(jvmti, method, code_size, code_addr, compile_info);
else
generate_single_entry(jvmti, method, code_addr, code_size);
}
Expand All @@ -286,10 +308,8 @@ cbDynamicCodeGenerated(jvmtiEnv *jvmti,
}

void set_notification_mode(jvmtiEnv *jvmti, jvmtiEventMode mode) {
(*jvmti)->SetEventNotificationMode(jvmti, mode,
JVMTI_EVENT_COMPILED_METHOD_LOAD, (jthread)NULL);
(*jvmti)->SetEventNotificationMode(jvmti, mode,
JVMTI_EVENT_DYNAMIC_CODE_GENERATED, (jthread)NULL);
(*jvmti)->SetEventNotificationMode(jvmti, mode, JVMTI_EVENT_COMPILED_METHOD_LOAD, (jthread)NULL);
(*jvmti)->SetEventNotificationMode(jvmti, mode, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, (jthread)NULL);
}

jvmtiError enable_capabilities(jvmtiEnv *jvmti) {
Expand Down Expand Up @@ -326,7 +346,13 @@ Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
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;
dotted_class_names = strstr(options, "dottedclass") != NULL;
clean_class_names = strstr(options, "cleanclass") != NULL;
annotate_java_frames = strstr(options, "annotate_java_frames") != NULL;

bool use_semicolon_unfold_delimiter = strstr(options, "use_semicolon_unfold_delimiter") != NULL;
unfold_delimiter = use_semicolon_unfold_delimiter ? ";" : "->";

debug_dump_unfold_entries = strstr(options, "debug_dump_unfold_entries") != NULL;

jvmtiEnv *jvmti;
Expand Down

0 comments on commit 349f1c2

Please sign in to comment.