summaryrefslogtreecommitdiff
path: root/tools/perf/jvmti
diff options
context:
space:
mode:
authorStephane Eranian <eranian@google.com>2015-11-30 12:02:23 +0300
committerArnaldo Carvalho de Melo <acme@redhat.com>2016-02-05 18:33:09 +0300
commit598b7c6919c7bbcc1243009721a01bc12275ff3e (patch)
treea774366184bc50f78d124bdb597965b378ec9a9d /tools/perf/jvmti
parent209045adc2bbdb2b315fa5539cec54d01cd3e7db (diff)
downloadlinux-598b7c6919c7bbcc1243009721a01bc12275ff3e.tar.xz
perf jit: add source line info support
This patch adds source line information support to perf for jitted code. The source line info must be emitted by the runtime, such as JVMTI. Perf injects extract the source line info from the jitdump file and adds the corresponding .debug_lines section in the ELF image generated for each jitted function. The source line enables matching any address in the profile with a source file and line number. The improvement is visible in perf annotate with the source code displayed alongside the assembly code. The dwarf code leverages the support from OProfile which is also released under GPLv2. Copyright 2007 OProfile authors. Signed-off-by: Stephane Eranian <eranian@google.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Carl Love <cel@us.ibm.com> Cc: David Ahern <dsahern@gmail.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: John McCutchan <johnmccutchan@google.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Pawel Moll <pawel.moll@arm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Sonny Rao <sonnyrao@chromium.org> Cc: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> Link: http://lkml.kernel.org/r/1448874143-7269-5-git-send-email-eranian@google.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/jvmti')
-rw-r--r--tools/perf/jvmti/jvmti_agent.c32
-rw-r--r--tools/perf/jvmti/jvmti_agent.h11
-rw-r--r--tools/perf/jvmti/libjvmti.c122
3 files changed, 134 insertions, 31 deletions
diff --git a/tools/perf/jvmti/jvmti_agent.c b/tools/perf/jvmti/jvmti_agent.c
index cbab139de5a4..6461e02ab940 100644
--- a/tools/perf/jvmti/jvmti_agent.c
+++ b/tools/perf/jvmti/jvmti_agent.c
@@ -374,20 +374,20 @@ jvmti_write_code(void *agent, char const *sym,
int
jvmti_write_debug_info(void *agent, uint64_t code, const char *file,
- jvmtiAddrLocationMap const *map,
- jvmtiLineNumberEntry *li, jint num)
+ jvmti_line_info_t *li, int nr_lines)
{
- static const char *prev_str = "\xff";
struct jr_code_debug_info rec;
size_t sret, len, size, flen;
size_t padding_count;
+ uint64_t addr;
+ const char *fn = file;
FILE *fp = agent;
int i;
/*
* no entry to write
*/
- if (!num)
+ if (!nr_lines)
return 0;
if (!fp) {
@@ -401,17 +401,18 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file,
size = sizeof(rec);
rec.p.timestamp = perf_get_timestamp();
rec.code_addr = (uint64_t)(uintptr_t)code;
- rec.nr_entry = num;
+ rec.nr_entry = nr_lines;
/*
* on disk source line info layout:
* uint64_t : addr
* int : line number
+ * int : column discriminator
* file[] : source file name
* padding : pad to multiple of 8 bytes
*/
- size += num * (sizeof(uint64_t) + sizeof(int));
- size += flen + (num - 1) * 2;
+ size += nr_lines * sizeof(struct debug_entry);
+ size += flen * nr_lines;
/*
* pad to 8 bytes
*/
@@ -429,28 +430,27 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file,
if (sret != 1)
goto error;
- for (i = 0; i < num; i++) {
- uint64_t addr;
+ for (i = 0; i < nr_lines; i++) {
- addr = (uint64_t)map[i].start_address;
+ addr = (uint64_t)li[i].pc;
len = sizeof(addr);
sret = fwrite_unlocked(&addr, len, 1, fp);
if (sret != 1)
goto error;
- len = sizeof(int);
+ len = sizeof(li[0].line_number);
sret = fwrite_unlocked(&li[i].line_number, len, 1, fp);
if (sret != 1)
goto error;
- if (i == 0) {
- sret = fwrite_unlocked(file, flen, 1, fp);
- } else {
- sret = fwrite_unlocked(prev_str, 2, 1, fp);
- }
+ len = sizeof(li[0].discrim);
+ sret = fwrite_unlocked(&li[i].discrim, len, 1, fp);
if (sret != 1)
goto error;
+ sret = fwrite_unlocked(fn, flen, 1, fp);
+ if (sret != 1)
+ goto error;
}
if (padding_count)
sret = fwrite_unlocked(pad_bytes, padding_count, 1, fp);
diff --git a/tools/perf/jvmti/jvmti_agent.h b/tools/perf/jvmti/jvmti_agent.h
index 8251a1c5ee3f..bedf5d0ba9ff 100644
--- a/tools/perf/jvmti/jvmti_agent.h
+++ b/tools/perf/jvmti/jvmti_agent.h
@@ -11,16 +11,23 @@
extern "C" {
#endif
+typedef struct {
+ unsigned long pc;
+ int line_number;
+ int discrim; /* discriminator -- 0 for now */
+} jvmti_line_info_t;
+
void *jvmti_open(void);
int jvmti_close(void *agent);
int jvmti_write_code(void *agent, char const *symbol_name,
uint64_t vma, void const *code,
const unsigned int code_size);
+
int jvmti_write_debug_info(void *agent,
uint64_t code,
const char *file,
- jvmtiAddrLocationMap const *map,
- jvmtiLineNumberEntry *tab, jint nr);
+ jvmti_line_info_t *li,
+ int nr_lines);
#if defined(__cplusplus)
}
diff --git a/tools/perf/jvmti/libjvmti.c b/tools/perf/jvmti/libjvmti.c
index 92ffbe4ff160..ac12e4b91a92 100644
--- a/tools/perf/jvmti/libjvmti.c
+++ b/tools/perf/jvmti/libjvmti.c
@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <err.h>
#include <jvmti.h>
+#include <jvmticmlr.h>
#include <limits.h>
#include "jvmti_agent.h"
@@ -11,6 +12,100 @@
static int has_line_numbers;
void *jvmti_agent;
+static jvmtiError
+do_get_line_numbers(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci,
+ jvmti_line_info_t *tab, jint *nr)
+{
+ jint i, lines = 0;
+ jint nr_lines = 0;
+ jvmtiLineNumberEntry *loc_tab = NULL;
+ jvmtiError ret;
+
+ ret = (*jvmti)->GetLineNumberTable(jvmti, m, &nr_lines, &loc_tab);
+ if (ret != JVMTI_ERROR_NONE)
+ return ret;
+
+ for (i = 0; i < nr_lines; i++) {
+ if (loc_tab[i].start_location < bci) {
+ tab[lines].pc = (unsigned long)pc;
+ tab[lines].line_number = loc_tab[i].line_number;
+ tab[lines].discrim = 0; /* not yet used */
+ lines++;
+ } else {
+ break;
+ }
+ }
+ (*jvmti)->Deallocate(jvmti, (unsigned char *)loc_tab);
+ *nr = lines;
+ return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError
+get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **tab, int *nr_lines)
+{
+ const jvmtiCompiledMethodLoadRecordHeader *hdr;
+ jvmtiCompiledMethodLoadInlineRecord *rec;
+ jvmtiLineNumberEntry *lne = NULL;
+ PCStackInfo *c;
+ jint nr, ret;
+ int nr_total = 0;
+ int i, lines_total = 0;
+
+ if (!(tab && nr_lines))
+ return JVMTI_ERROR_NULL_POINTER;
+
+ /*
+ * Phase 1 -- get the number of lines necessary
+ */
+ for (hdr = compile_info; hdr != NULL; hdr = hdr->next) {
+ if (hdr->kind == JVMTI_CMLR_INLINE_INFO) {
+ rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr;
+ for (i = 0; i < rec->numpcs; i++) {
+ c = rec->pcinfo + i;
+ nr = 0;
+ /*
+ * unfortunately, need a tab to get the number of lines!
+ */
+ ret = (*jvmti)->GetLineNumberTable(jvmti, c->methods[0], &nr, &lne);
+ if (ret == JVMTI_ERROR_NONE) {
+ /* free what was allocated for nothing */
+ (*jvmti)->Deallocate(jvmti, (unsigned char *)lne);
+ nr_total += (int)nr;
+ }
+ }
+ }
+ }
+
+ if (nr_total == 0)
+ return JVMTI_ERROR_NOT_FOUND;
+
+ /*
+ * Phase 2 -- allocate big enough line table
+ */
+ *tab = malloc(nr_total * sizeof(**tab));
+ if (!*tab)
+ return JVMTI_ERROR_OUT_OF_MEMORY;
+
+ for (hdr = compile_info; hdr != NULL; hdr = hdr->next) {
+ if (hdr->kind == JVMTI_CMLR_INLINE_INFO) {
+ rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr;
+ for (i = 0; i < rec->numpcs; i++) {
+ c = rec->pcinfo + i;
+ nr = 0;
+ ret = do_get_line_numbers(jvmti, c->pc,
+ c->methods[0],
+ c->bcis[0],
+ *tab + lines_total,
+ &nr);
+ if (ret == JVMTI_ERROR_NONE)
+ lines_total += nr;
+ }
+ }
+ }
+ *nr_lines = lines_total;
+ return JVMTI_ERROR_NONE;
+}
+
static void JNICALL
compiled_method_load_cb(jvmtiEnv *jvmti,
jmethodID method,
@@ -18,9 +113,9 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
void const *code_addr,
jint map_length,
jvmtiAddrLocationMap const *map,
- void const *compile_info __unused)
+ const void *compile_info)
{
- jvmtiLineNumberEntry *tab = NULL;
+ jvmti_line_info_t *line_tab = NULL;
jclass decl_class;
char *class_sign = NULL;
char *func_name = NULL;
@@ -29,7 +124,7 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
char fn[PATH_MAX];
uint64_t addr = (uint64_t)(uintptr_t)code_addr;
jvmtiError ret;
- jint nr_lines = 0;
+ int nr_lines = 0; /* in line_tab[] */
size_t len;
ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method,
@@ -40,19 +135,19 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
}
if (has_line_numbers && map && map_length) {
-
- ret = (*jvmti)->GetLineNumberTable(jvmti, method, &nr_lines, &tab);
+ ret = get_line_numbers(jvmti, compile_info, &line_tab, &nr_lines);
if (ret != JVMTI_ERROR_NONE) {
warnx("jvmti: cannot get line table for method");
- } else {
- ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name);
- if (ret != JVMTI_ERROR_NONE) {
- warnx("jvmti: cannot get source filename ret=%d", ret);
- nr_lines = 0;
- }
+ nr_lines = 0;
}
}
+ ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name);
+ if (ret != JVMTI_ERROR_NONE) {
+ warnx("jvmti: cannot get source filename ret=%d", ret);
+ goto error;
+ }
+
ret = (*jvmti)->GetClassSignature(jvmti, decl_class,
&class_sign, NULL);
if (ret != JVMTI_ERROR_NONE) {
@@ -92,13 +187,14 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
/*
* write source line info record if we have it
*/
- if (jvmti_write_debug_info(jvmti_agent, addr, fn, map, tab, nr_lines))
+ if (jvmti_write_debug_info(jvmti_agent, addr, fn, line_tab, nr_lines))
warnx("jvmti: write_debug_info() failed");
len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2;
{
char str[len];
snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign);
+
if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size))
warnx("jvmti: write_code() failed");
}
@@ -106,8 +202,8 @@ error:
(*jvmti)->Deallocate(jvmti, (unsigned char *)func_name);
(*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign);
(*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign);
- (*jvmti)->Deallocate(jvmti, (unsigned char *)tab);
(*jvmti)->Deallocate(jvmti, (unsigned char *)file_name);
+ free(line_tab);
}
static void JNICALL