Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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>

authored by

Stefano Sanfilippo and committed by
Arnaldo Carvalho de Melo
0284fecd eac05af2

+66 -3
+54 -3
tools/perf/util/jitdump.c
··· 37 37 bool needs_bswap; /* handles cross-endianess */ 38 38 bool use_arch_timestamp; 39 39 void *debug_data; 40 + void *unwinding_data; 41 + uint64_t unwinding_size; 42 + uint64_t unwinding_mapped_size; 43 + uint64_t eh_frame_hdr_size; 40 44 size_t nr_debug_entries; 41 45 uint32_t code_load_count; 42 46 u64 bytes_written; ··· 299 295 } 300 296 } 301 297 break; 298 + case JIT_CODE_UNWINDING_INFO: 299 + if (jd->needs_bswap) { 300 + jr->unwinding.unwinding_size = bswap_64(jr->unwinding.unwinding_size); 301 + jr->unwinding.eh_frame_hdr_size = bswap_64(jr->unwinding.eh_frame_hdr_size); 302 + jr->unwinding.mapped_size = bswap_64(jr->unwinding.mapped_size); 303 + } 304 + break; 302 305 case JIT_CODE_CLOSE: 303 306 break; 304 307 case JIT_CODE_LOAD: ··· 381 370 u16 idr_size; 382 371 const char *sym; 383 372 uint32_t count; 384 - int ret, csize; 373 + int ret, csize, usize; 385 374 pid_t pid, tid; 386 375 struct { 387 376 u32 pid, tid; ··· 391 380 pid = jr->load.pid; 392 381 tid = jr->load.tid; 393 382 csize = jr->load.code_size; 383 + usize = jd->unwinding_mapped_size; 394 384 addr = jr->load.code_addr; 395 385 sym = (void *)((unsigned long)jr + sizeof(jr->load)); 396 386 code = (unsigned long)jr + jr->load.p.total_size - csize; ··· 420 408 jd->nr_debug_entries = 0; 421 409 } 422 410 411 + if (jd->unwinding_data && jd->eh_frame_hdr_size) { 412 + free(jd->unwinding_data); 413 + jd->unwinding_data = NULL; 414 + jd->eh_frame_hdr_size = 0; 415 + jd->unwinding_mapped_size = 0; 416 + jd->unwinding_size = 0; 417 + } 418 + 423 419 if (ret) { 424 420 free(event); 425 421 return -1; ··· 442 422 443 423 event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; 444 424 event->mmap2.start = addr; 445 - event->mmap2.len = csize; 425 + event->mmap2.len = usize ? ALIGN_8(csize) + usize : csize; 446 426 event->mmap2.pid = pid; 447 427 event->mmap2.tid = tid; 448 428 event->mmap2.ino = st.st_ino; ··· 493 473 char *filename; 494 474 size_t size; 495 475 struct stat st; 476 + int usize; 496 477 u16 idr_size; 497 478 int ret; 498 479 pid_t pid, tid; ··· 504 483 505 484 pid = jr->move.pid; 506 485 tid = jr->move.tid; 486 + usize = jd->unwinding_mapped_size; 507 487 idr_size = jd->machine->id_hdr_size; 508 488 509 489 /* ··· 533 511 (sizeof(event->mmap2.filename) - size) + idr_size); 534 512 event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; 535 513 event->mmap2.start = jr->move.new_code_addr; 536 - event->mmap2.len = jr->move.code_size; 514 + event->mmap2.len = usize ? ALIGN_8(jr->move.code_size) + usize 515 + : jr->move.code_size; 537 516 event->mmap2.pid = pid; 538 517 event->mmap2.tid = tid; 539 518 event->mmap2.ino = st.st_ino; ··· 601 578 } 602 579 603 580 static int 581 + jit_repipe_unwinding_info(struct jit_buf_desc *jd, union jr_entry *jr) 582 + { 583 + void *unwinding_data; 584 + uint32_t unwinding_data_size; 585 + 586 + if (!(jd && jr)) 587 + return -1; 588 + 589 + unwinding_data_size = jr->prefix.total_size - sizeof(jr->unwinding); 590 + unwinding_data = malloc(unwinding_data_size); 591 + if (!unwinding_data) 592 + return -1; 593 + 594 + memcpy(unwinding_data, &jr->unwinding.unwinding_data, 595 + unwinding_data_size); 596 + 597 + jd->eh_frame_hdr_size = jr->unwinding.eh_frame_hdr_size; 598 + jd->unwinding_size = jr->unwinding.unwinding_size; 599 + jd->unwinding_mapped_size = jr->unwinding.mapped_size; 600 + jd->unwinding_data = unwinding_data; 601 + 602 + return 0; 603 + } 604 + 605 + static int 604 606 jit_process_dump(struct jit_buf_desc *jd) 605 607 { 606 608 union jr_entry *jr; ··· 641 593 break; 642 594 case JIT_CODE_DEBUG_INFO: 643 595 ret = jit_repipe_debug_info(jd, jr); 596 + break; 597 + case JIT_CODE_UNWINDING_INFO: 598 + ret = jit_repipe_unwinding_info(jd, jr); 644 599 break; 645 600 default: 646 601 ret = 0;
+12
tools/perf/util/jitdump.h
··· 19 19 #define JITHEADER_MAGIC_SW 0x4454694A 20 20 21 21 #define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7) 22 + #define ALIGN_8(x) (((x) + 7) & (~7)) 22 23 23 24 #define JITHEADER_VERSION 1 24 25 ··· 49 48 JIT_CODE_MOVE = 1, 50 49 JIT_CODE_DEBUG_INFO = 2, 51 50 JIT_CODE_CLOSE = 3, 51 + JIT_CODE_UNWINDING_INFO = 4, 52 52 53 53 JIT_CODE_MAX, 54 54 }; ··· 103 101 struct debug_entry entries[0]; 104 102 }; 105 103 104 + struct jr_code_unwinding_info { 105 + struct jr_prefix p; 106 + 107 + uint64_t unwinding_size; 108 + uint64_t eh_frame_hdr_size; 109 + uint64_t mapped_size; 110 + const char unwinding_data[0]; 111 + }; 112 + 106 113 union jr_entry { 107 114 struct jr_code_debug_info info; 108 115 struct jr_code_close close; 109 116 struct jr_code_load load; 110 117 struct jr_code_move move; 111 118 struct jr_prefix prefix; 119 + struct jr_code_unwinding_info unwinding; 112 120 }; 113 121 114 122 static inline struct debug_entry *