Commit 598b7c69 authored by Stephane Eranian's avatar Stephane Eranian Committed by Arnaldo Carvalho de Melo

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: default avatarStephane 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.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 209045ad
...@@ -374,20 +374,20 @@ jvmti_write_code(void *agent, char const *sym, ...@@ -374,20 +374,20 @@ jvmti_write_code(void *agent, char const *sym,
int int
jvmti_write_debug_info(void *agent, uint64_t code, const char *file, jvmti_write_debug_info(void *agent, uint64_t code, const char *file,
jvmtiAddrLocationMap const *map, jvmti_line_info_t *li, int nr_lines)
jvmtiLineNumberEntry *li, jint num)
{ {
static const char *prev_str = "\xff";
struct jr_code_debug_info rec; struct jr_code_debug_info rec;
size_t sret, len, size, flen; size_t sret, len, size, flen;
size_t padding_count; size_t padding_count;
uint64_t addr;
const char *fn = file;
FILE *fp = agent; FILE *fp = agent;
int i; int i;
/* /*
* no entry to write * no entry to write
*/ */
if (!num) if (!nr_lines)
return 0; return 0;
if (!fp) { if (!fp) {
...@@ -401,17 +401,18 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file, ...@@ -401,17 +401,18 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file,
size = sizeof(rec); size = sizeof(rec);
rec.p.timestamp = perf_get_timestamp(); rec.p.timestamp = perf_get_timestamp();
rec.code_addr = (uint64_t)(uintptr_t)code; rec.code_addr = (uint64_t)(uintptr_t)code;
rec.nr_entry = num; rec.nr_entry = nr_lines;
/* /*
* on disk source line info layout: * on disk source line info layout:
* uint64_t : addr * uint64_t : addr
* int : line number * int : line number
* int : column discriminator
* file[] : source file name * file[] : source file name
* padding : pad to multiple of 8 bytes * padding : pad to multiple of 8 bytes
*/ */
size += num * (sizeof(uint64_t) + sizeof(int)); size += nr_lines * sizeof(struct debug_entry);
size += flen + (num - 1) * 2; size += flen * nr_lines;
/* /*
* pad to 8 bytes * pad to 8 bytes
*/ */
...@@ -429,28 +430,27 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file, ...@@ -429,28 +430,27 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file,
if (sret != 1) if (sret != 1)
goto error; goto error;
for (i = 0; i < num; i++) { for (i = 0; i < nr_lines; i++) {
uint64_t addr;
addr = (uint64_t)map[i].start_address; addr = (uint64_t)li[i].pc;
len = sizeof(addr); len = sizeof(addr);
sret = fwrite_unlocked(&addr, len, 1, fp); sret = fwrite_unlocked(&addr, len, 1, fp);
if (sret != 1) if (sret != 1)
goto error; goto error;
len = sizeof(int); len = sizeof(li[0].line_number);
sret = fwrite_unlocked(&li[i].line_number, len, 1, fp); sret = fwrite_unlocked(&li[i].line_number, len, 1, fp);
if (sret != 1) if (sret != 1)
goto error; goto error;
if (i == 0) { len = sizeof(li[0].discrim);
sret = fwrite_unlocked(file, flen, 1, fp); sret = fwrite_unlocked(&li[i].discrim, len, 1, fp);
} else {
sret = fwrite_unlocked(prev_str, 2, 1, fp);
}
if (sret != 1) if (sret != 1)
goto error; goto error;
sret = fwrite_unlocked(fn, flen, 1, fp);
if (sret != 1)
goto error;
} }
if (padding_count) if (padding_count)
sret = fwrite_unlocked(pad_bytes, padding_count, 1, fp); sret = fwrite_unlocked(pad_bytes, padding_count, 1, fp);
......
...@@ -11,16 +11,23 @@ ...@@ -11,16 +11,23 @@
extern "C" { extern "C" {
#endif #endif
typedef struct {
unsigned long pc;
int line_number;
int discrim; /* discriminator -- 0 for now */
} jvmti_line_info_t;
void *jvmti_open(void); void *jvmti_open(void);
int jvmti_close(void *agent); int jvmti_close(void *agent);
int jvmti_write_code(void *agent, char const *symbol_name, int jvmti_write_code(void *agent, char const *symbol_name,
uint64_t vma, void const *code, uint64_t vma, void const *code,
const unsigned int code_size); const unsigned int code_size);
int jvmti_write_debug_info(void *agent, int jvmti_write_debug_info(void *agent,
uint64_t code, uint64_t code,
const char *file, const char *file,
jvmtiAddrLocationMap const *map, jvmti_line_info_t *li,
jvmtiLineNumberEntry *tab, jint nr); int nr_lines);
#if defined(__cplusplus) #if defined(__cplusplus)
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <err.h> #include <err.h>
#include <jvmti.h> #include <jvmti.h>
#include <jvmticmlr.h>
#include <limits.h> #include <limits.h>
#include "jvmti_agent.h" #include "jvmti_agent.h"
...@@ -11,6 +12,100 @@ ...@@ -11,6 +12,100 @@
static int has_line_numbers; static int has_line_numbers;
void *jvmti_agent; 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 static void JNICALL
compiled_method_load_cb(jvmtiEnv *jvmti, compiled_method_load_cb(jvmtiEnv *jvmti,
jmethodID method, jmethodID method,
...@@ -18,9 +113,9 @@ compiled_method_load_cb(jvmtiEnv *jvmti, ...@@ -18,9 +113,9 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
void const *code_addr, void const *code_addr,
jint map_length, jint map_length,
jvmtiAddrLocationMap const *map, 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; jclass decl_class;
char *class_sign = NULL; char *class_sign = NULL;
char *func_name = NULL; char *func_name = NULL;
...@@ -29,7 +124,7 @@ compiled_method_load_cb(jvmtiEnv *jvmti, ...@@ -29,7 +124,7 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
char fn[PATH_MAX]; char fn[PATH_MAX];
uint64_t addr = (uint64_t)(uintptr_t)code_addr; uint64_t addr = (uint64_t)(uintptr_t)code_addr;
jvmtiError ret; jvmtiError ret;
jint nr_lines = 0; int nr_lines = 0; /* in line_tab[] */
size_t len; size_t len;
ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method, ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method,
...@@ -40,17 +135,17 @@ compiled_method_load_cb(jvmtiEnv *jvmti, ...@@ -40,17 +135,17 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
} }
if (has_line_numbers && map && map_length) { if (has_line_numbers && map && map_length) {
ret = get_line_numbers(jvmti, compile_info, &line_tab, &nr_lines);
ret = (*jvmti)->GetLineNumberTable(jvmti, method, &nr_lines, &tab);
if (ret != JVMTI_ERROR_NONE) { if (ret != JVMTI_ERROR_NONE) {
warnx("jvmti: cannot get line table for method"); 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, ret = (*jvmti)->GetClassSignature(jvmti, decl_class,
...@@ -92,13 +187,14 @@ compiled_method_load_cb(jvmtiEnv *jvmti, ...@@ -92,13 +187,14 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
/* /*
* write source line info record if we have it * 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"); warnx("jvmti: write_debug_info() failed");
len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2; len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2;
{ {
char str[len]; char str[len];
snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign); snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign);
if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size)) if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size))
warnx("jvmti: write_code() failed"); warnx("jvmti: write_code() failed");
} }
...@@ -106,8 +202,8 @@ compiled_method_load_cb(jvmtiEnv *jvmti, ...@@ -106,8 +202,8 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
(*jvmti)->Deallocate(jvmti, (unsigned char *)func_name); (*jvmti)->Deallocate(jvmti, (unsigned char *)func_name);
(*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign); (*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign);
(*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign); (*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign);
(*jvmti)->Deallocate(jvmti, (unsigned char *)tab);
(*jvmti)->Deallocate(jvmti, (unsigned char *)file_name); (*jvmti)->Deallocate(jvmti, (unsigned char *)file_name);
free(line_tab);
} }
static void JNICALL static void JNICALL
......
...@@ -108,8 +108,11 @@ libperf-$(CONFIG_LZMA) += lzma.o ...@@ -108,8 +108,11 @@ libperf-$(CONFIG_LZMA) += lzma.o
libperf-y += demangle-java.o libperf-y += demangle-java.o
libperf-$(CONFIG_LIBELF) += jitdump.o libperf-$(CONFIG_LIBELF) += jitdump.o
libperf-$(CONFIG_LIBELF) += genelf.o libperf-$(CONFIG_LIBELF) += genelf.o
libperf-$(CONFIG_LIBELF) += genelf_debug.o
CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
# avoid compiler warnings in 32-bit mode
CFLAGS_genelf_debug.o += -Wno-packed
$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
$(call rule_mkdir) $(call rule_mkdir)
......
...@@ -156,7 +156,8 @@ gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *cod ...@@ -156,7 +156,8 @@ gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *cod
*/ */
int int
jit_write_elf(int fd, uint64_t load_addr, const char *sym, jit_write_elf(int fd, uint64_t load_addr, const char *sym,
const void *code, int csize) const void *code, int csize,
void *debug, int nr_debug_entries)
{ {
Elf *e; Elf *e;
Elf_Data *d; Elf_Data *d;
...@@ -385,10 +386,16 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, ...@@ -385,10 +386,16 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym,
shdr->sh_size = sizeof(bnote); shdr->sh_size = sizeof(bnote);
shdr->sh_entsize = 0; shdr->sh_entsize = 0;
if (debug && nr_debug_entries) {
retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries);
if (retval)
goto error;
} else {
if (elf_update(e, ELF_C_WRITE) < 0) { if (elf_update(e, ELF_C_WRITE) < 0) {
warnx("elf_update 4 failed"); warnx("elf_update 4 failed");
goto error; goto error;
} }
}
retval = 0; retval = 0;
error: error:
......
...@@ -3,7 +3,11 @@ ...@@ -3,7 +3,11 @@
/* genelf.c */ /* genelf.c */
extern int jit_write_elf(int fd, uint64_t code_addr, const char *sym, extern int jit_write_elf(int fd, uint64_t code_addr, const char *sym,
const void *code, int csize); const void *code, int csize,
void *debug, int nr_debug_entries);
/* genelf_debug.c */
extern int jit_add_debug_info(Elf *e, uint64_t code_addr,
void *debug, int nr_debug_entries);
#if defined(__arm__) #if defined(__arm__)
#define GEN_ELF_ARCH EM_ARM #define GEN_ELF_ARCH EM_ARM
......
This diff is collapsed.
...@@ -63,7 +63,9 @@ jit_emit_elf(char *filename, ...@@ -63,7 +63,9 @@ jit_emit_elf(char *filename,
const char *sym, const char *sym,
uint64_t code_addr, uint64_t code_addr,
const void *code, const void *code,
int csize) int csize,
void *debug,
int nr_debug_entries)
{ {
int ret, fd; int ret, fd;
...@@ -76,7 +78,7 @@ jit_emit_elf(char *filename, ...@@ -76,7 +78,7 @@ jit_emit_elf(char *filename,
return -1; return -1;
} }
ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize); ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries);
close(fd); close(fd);
...@@ -347,7 +349,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) ...@@ -347,7 +349,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
size = PERF_ALIGN(size, sizeof(u64)); size = PERF_ALIGN(size, sizeof(u64));
uaddr = (uintptr_t)code; uaddr = (uintptr_t)code;
ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize); ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries);
if (jd->debug_data && jd->nr_debug_entries) { if (jd->debug_data && jd->nr_debug_entries) {
free(jd->debug_data); free(jd->debug_data);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment