perf jit: Add unwinding support
authorStefano Sanfilippo <ssanfilippo@chromium.org>
Thu, 13 Oct 2016 10:59:40 +0000 (03:59 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 24 Oct 2016 14:07:39 +0000 (11:07 -0300)
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: Stefano Sanfilippo <ssanfilippo@chromium.org>
Signed-off-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Stephane 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.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/jitdump.c
tools/perf/util/jitdump.h

index 75b66bbf8429c5eeb5a3444cd0929bc294bfce6f..9bae66cc78f2e157f3daa8943830a2c0923bb798 100644 (file)
@@ -37,6 +37,10 @@ struct jit_buf_desc {
        bool             needs_bswap; /* handles cross-endianess */
        bool             use_arch_timestamp;
        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;
        uint32_t         code_load_count;
        u64              bytes_written;
@@ -295,6 +299,13 @@ jit_get_next_entry(struct jit_buf_desc *jd)
                        }
                }
                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:
                break;
        case JIT_CODE_LOAD:
@@ -370,7 +381,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
        u16 idr_size;
        const char *sym;
        uint32_t count;
-       int ret, csize;
+       int ret, csize, usize;
        pid_t pid, tid;
        struct {
                u32 pid, tid;
@@ -380,6 +391,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
        pid   = jr->load.pid;
        tid   = jr->load.tid;
        csize = jr->load.code_size;
+       usize = jd->unwinding_mapped_size;
        addr  = jr->load.code_addr;
        sym   = (void *)((unsigned long)jr + sizeof(jr->load));
        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)
                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) {
                free(event);
                return -1;
@@ -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.start = addr;
-       event->mmap2.len   = csize;
+       event->mmap2.len   = usize ? ALIGN_8(csize) + usize : csize;
        event->mmap2.pid   = pid;
        event->mmap2.tid   = tid;
        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)
        char *filename;
        size_t size;
        struct stat st;
+       int usize;
        u16 idr_size;
        int ret;
        pid_t pid, tid;
@@ -483,6 +504,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)
 
        pid = jr->move.pid;
        tid =  jr->move.tid;
+       usize = jd->unwinding_mapped_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)
                        (sizeof(event->mmap2.filename) - size) + idr_size);
        event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET;
        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.tid   = tid;
        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)
        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
 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:
                        ret = jit_repipe_debug_info(jd, jr);
                        break;
+               case JIT_CODE_UNWINDING_INFO:
+                       ret = jit_repipe_unwinding_info(jd, jr);
+                       break;
                default:
                        ret = 0;
                        continue;
index bcacd20d0c1c709ed3f983c4792393d851fa47d0..c6b9b67f43bfbbbcec0d1ae06f11aab17aa9d4ce 100644 (file)
@@ -19,6 +19,7 @@
 #define JITHEADER_MAGIC_SW     0x4454694A
 
 #define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7)
+#define ALIGN_8(x) (((x) + 7) & (~7))
 
 #define JITHEADER_VERSION 1
 
@@ -48,6 +49,7 @@ enum jit_record_type {
         JIT_CODE_MOVE           = 1,
        JIT_CODE_DEBUG_INFO     = 2,
        JIT_CODE_CLOSE          = 3,
+       JIT_CODE_UNWINDING_INFO = 4,
 
        JIT_CODE_MAX,
 };
@@ -101,12 +103,22 @@ struct jr_code_debug_info {
        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 {
         struct jr_code_debug_info info;
         struct jr_code_close close;
         struct jr_code_load load;
         struct jr_code_move move;
         struct jr_prefix prefix;
+        struct jr_code_unwinding_info unwinding;
 };
 
 static inline struct debug_entry *