Commit 0284fecd authored by Stefano Sanfilippo's avatar Stefano Sanfilippo Committed by Arnaldo Carvalho de Melo

perf jit: Add unwinding support

This record is intended to provide unwinding information in the
eh_frame format. This is required to unwind JITed code which
does not maintain the frame pointer register during function calls.

The eh_frame unwinding information can be emitted by V8 / Chromium
when the --perf_prof_unwinding_info is passed.

A record of type jr_code_unwinding_info comes before the jr_code_load
it referred to and contains both the .eh_frame and .eh_frame_hdr.

The fields in the header have the following meaning:

  * unwinding_size: size of the eh_frame and eh_frame_hdr, necessary
    for distinguishing the content from the padding.

  * eh_frame_hdr_size: as the name says.

  * mapped_size: size of the payload that was in memory at runtime.
    typically unwinding_size if the .eh_frame_hdr and .eh_frame were
    mapped, or 0 if they weren't. It should always be the former case,
    since the .eh_frame is guaranteed to be mapped in memory. However,
    certain JITs might want to inject an .eh_frame_hdr with an empty LUT
    to trigger fp-based unwinding fallback in libunwind. The only part
    of the .eh_frame_hdr that libunwind reads from remote memory is the
    LUT, and since there is none, mapping the unwinding info in memory
    is not necessary, and 0 in this field signifies that it wasn't.
    This practical hack allows to save bytes in code memory for those
    JIT compilers that might or might not maintain a valid frame pointer.

The payload that follows is assumed to contain first the .eh_frame and
then the .eh_header_hdr, with no padding between the two.
Signed-off-by: default avatarStefano Sanfilippo <ssanfilippo@chromium.org>
Signed-off-by: default avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: default avatarStephane Eranian <eranian@google.com>
Cc: Anton Blanchard <anton@ozlabs.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/1476356383-30100-7-git-send-email-eranian@google.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent eac05af2
...@@ -37,6 +37,10 @@ struct jit_buf_desc { ...@@ -37,6 +37,10 @@ struct jit_buf_desc {
bool needs_bswap; /* handles cross-endianess */ bool needs_bswap; /* handles cross-endianess */
bool use_arch_timestamp; bool use_arch_timestamp;
void *debug_data; void *debug_data;
void *unwinding_data;
uint64_t unwinding_size;
uint64_t unwinding_mapped_size;
uint64_t eh_frame_hdr_size;
size_t nr_debug_entries; size_t nr_debug_entries;
uint32_t code_load_count; uint32_t code_load_count;
u64 bytes_written; u64 bytes_written;
...@@ -295,6 +299,13 @@ jit_get_next_entry(struct jit_buf_desc *jd) ...@@ -295,6 +299,13 @@ jit_get_next_entry(struct jit_buf_desc *jd)
} }
} }
break; break;
case JIT_CODE_UNWINDING_INFO:
if (jd->needs_bswap) {
jr->unwinding.unwinding_size = bswap_64(jr->unwinding.unwinding_size);
jr->unwinding.eh_frame_hdr_size = bswap_64(jr->unwinding.eh_frame_hdr_size);
jr->unwinding.mapped_size = bswap_64(jr->unwinding.mapped_size);
}
break;
case JIT_CODE_CLOSE: case JIT_CODE_CLOSE:
break; break;
case JIT_CODE_LOAD: case JIT_CODE_LOAD:
...@@ -370,7 +381,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) ...@@ -370,7 +381,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
u16 idr_size; u16 idr_size;
const char *sym; const char *sym;
uint32_t count; uint32_t count;
int ret, csize; int ret, csize, usize;
pid_t pid, tid; pid_t pid, tid;
struct { struct {
u32 pid, tid; u32 pid, tid;
...@@ -380,6 +391,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) ...@@ -380,6 +391,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
pid = jr->load.pid; pid = jr->load.pid;
tid = jr->load.tid; tid = jr->load.tid;
csize = jr->load.code_size; csize = jr->load.code_size;
usize = jd->unwinding_mapped_size;
addr = jr->load.code_addr; addr = jr->load.code_addr;
sym = (void *)((unsigned long)jr + sizeof(jr->load)); sym = (void *)((unsigned long)jr + sizeof(jr->load));
code = (unsigned long)jr + jr->load.p.total_size - csize; code = (unsigned long)jr + jr->load.p.total_size - csize;
...@@ -408,6 +420,14 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) ...@@ -408,6 +420,14 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
jd->nr_debug_entries = 0; jd->nr_debug_entries = 0;
} }
if (jd->unwinding_data && jd->eh_frame_hdr_size) {
free(jd->unwinding_data);
jd->unwinding_data = NULL;
jd->eh_frame_hdr_size = 0;
jd->unwinding_mapped_size = 0;
jd->unwinding_size = 0;
}
if (ret) { if (ret) {
free(event); free(event);
return -1; return -1;
...@@ -422,7 +442,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) ...@@ -422,7 +442,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET;
event->mmap2.start = addr; event->mmap2.start = addr;
event->mmap2.len = csize; event->mmap2.len = usize ? ALIGN_8(csize) + usize : csize;
event->mmap2.pid = pid; event->mmap2.pid = pid;
event->mmap2.tid = tid; event->mmap2.tid = tid;
event->mmap2.ino = st.st_ino; event->mmap2.ino = st.st_ino;
...@@ -473,6 +493,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) ...@@ -473,6 +493,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)
char *filename; char *filename;
size_t size; size_t size;
struct stat st; struct stat st;
int usize;
u16 idr_size; u16 idr_size;
int ret; int ret;
pid_t pid, tid; pid_t pid, tid;
...@@ -483,6 +504,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) ...@@ -483,6 +504,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)
pid = jr->move.pid; pid = jr->move.pid;
tid = jr->move.tid; tid = jr->move.tid;
usize = jd->unwinding_mapped_size;
idr_size = jd->machine->id_hdr_size; idr_size = jd->machine->id_hdr_size;
/* /*
...@@ -511,7 +533,8 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) ...@@ -511,7 +533,8 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)
(sizeof(event->mmap2.filename) - size) + idr_size); (sizeof(event->mmap2.filename) - size) + idr_size);
event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET;
event->mmap2.start = jr->move.new_code_addr; event->mmap2.start = jr->move.new_code_addr;
event->mmap2.len = jr->move.code_size; event->mmap2.len = usize ? ALIGN_8(jr->move.code_size) + usize
: jr->move.code_size;
event->mmap2.pid = pid; event->mmap2.pid = pid;
event->mmap2.tid = tid; event->mmap2.tid = tid;
event->mmap2.ino = st.st_ino; event->mmap2.ino = st.st_ino;
...@@ -577,6 +600,31 @@ static int jit_repipe_debug_info(struct jit_buf_desc *jd, union jr_entry *jr) ...@@ -577,6 +600,31 @@ static int jit_repipe_debug_info(struct jit_buf_desc *jd, union jr_entry *jr)
return 0; return 0;
} }
static int
jit_repipe_unwinding_info(struct jit_buf_desc *jd, union jr_entry *jr)
{
void *unwinding_data;
uint32_t unwinding_data_size;
if (!(jd && jr))
return -1;
unwinding_data_size = jr->prefix.total_size - sizeof(jr->unwinding);
unwinding_data = malloc(unwinding_data_size);
if (!unwinding_data)
return -1;
memcpy(unwinding_data, &jr->unwinding.unwinding_data,
unwinding_data_size);
jd->eh_frame_hdr_size = jr->unwinding.eh_frame_hdr_size;
jd->unwinding_size = jr->unwinding.unwinding_size;
jd->unwinding_mapped_size = jr->unwinding.mapped_size;
jd->unwinding_data = unwinding_data;
return 0;
}
static int static int
jit_process_dump(struct jit_buf_desc *jd) jit_process_dump(struct jit_buf_desc *jd)
{ {
...@@ -594,6 +642,9 @@ jit_process_dump(struct jit_buf_desc *jd) ...@@ -594,6 +642,9 @@ jit_process_dump(struct jit_buf_desc *jd)
case JIT_CODE_DEBUG_INFO: case JIT_CODE_DEBUG_INFO:
ret = jit_repipe_debug_info(jd, jr); ret = jit_repipe_debug_info(jd, jr);
break; break;
case JIT_CODE_UNWINDING_INFO:
ret = jit_repipe_unwinding_info(jd, jr);
break;
default: default:
ret = 0; ret = 0;
continue; continue;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#define JITHEADER_MAGIC_SW 0x4454694A #define JITHEADER_MAGIC_SW 0x4454694A
#define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7) #define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7)
#define ALIGN_8(x) (((x) + 7) & (~7))
#define JITHEADER_VERSION 1 #define JITHEADER_VERSION 1
...@@ -48,6 +49,7 @@ enum jit_record_type { ...@@ -48,6 +49,7 @@ enum jit_record_type {
JIT_CODE_MOVE = 1, JIT_CODE_MOVE = 1,
JIT_CODE_DEBUG_INFO = 2, JIT_CODE_DEBUG_INFO = 2,
JIT_CODE_CLOSE = 3, JIT_CODE_CLOSE = 3,
JIT_CODE_UNWINDING_INFO = 4,
JIT_CODE_MAX, JIT_CODE_MAX,
}; };
...@@ -101,12 +103,22 @@ struct jr_code_debug_info { ...@@ -101,12 +103,22 @@ struct jr_code_debug_info {
struct debug_entry entries[0]; struct debug_entry entries[0];
}; };
struct jr_code_unwinding_info {
struct jr_prefix p;
uint64_t unwinding_size;
uint64_t eh_frame_hdr_size;
uint64_t mapped_size;
const char unwinding_data[0];
};
union jr_entry { union jr_entry {
struct jr_code_debug_info info; struct jr_code_debug_info info;
struct jr_code_close close; struct jr_code_close close;
struct jr_code_load load; struct jr_code_load load;
struct jr_code_move move; struct jr_code_move move;
struct jr_prefix prefix; struct jr_prefix prefix;
struct jr_code_unwinding_info unwinding;
}; };
static inline struct debug_entry * static inline struct debug_entry *
......
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