diff options
-rw-r--r-- | tools/perf/jvmti/jvmti_agent.c | 32 | ||||
-rw-r--r-- | tools/perf/jvmti/jvmti_agent.h | 11 | ||||
-rw-r--r-- | tools/perf/jvmti/libjvmti.c | 122 | ||||
-rw-r--r-- | tools/perf/util/Build | 3 | ||||
-rw-r--r-- | tools/perf/util/genelf.c | 15 | ||||
-rw-r--r-- | tools/perf/util/genelf.h | 6 | ||||
-rw-r--r-- | tools/perf/util/genelf_debug.c | 610 | ||||
-rw-r--r-- | tools/perf/util/jitdump.c | 8 |
8 files changed, 768 insertions, 39 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, | |||
374 | 374 | ||
375 | int | 375 | int |
376 | jvmti_write_debug_info(void *agent, uint64_t code, const char *file, | 376 | jvmti_write_debug_info(void *agent, uint64_t code, const char *file, |
377 | jvmtiAddrLocationMap const *map, | 377 | jvmti_line_info_t *li, int nr_lines) |
378 | jvmtiLineNumberEntry *li, jint num) | ||
379 | { | 378 | { |
380 | static const char *prev_str = "\xff"; | ||
381 | struct jr_code_debug_info rec; | 379 | struct jr_code_debug_info rec; |
382 | size_t sret, len, size, flen; | 380 | size_t sret, len, size, flen; |
383 | size_t padding_count; | 381 | size_t padding_count; |
382 | uint64_t addr; | ||
383 | const char *fn = file; | ||
384 | FILE *fp = agent; | 384 | FILE *fp = agent; |
385 | int i; | 385 | int i; |
386 | 386 | ||
387 | /* | 387 | /* |
388 | * no entry to write | 388 | * no entry to write |
389 | */ | 389 | */ |
390 | if (!num) | 390 | if (!nr_lines) |
391 | return 0; | 391 | return 0; |
392 | 392 | ||
393 | if (!fp) { | 393 | if (!fp) { |
@@ -401,17 +401,18 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file, | |||
401 | size = sizeof(rec); | 401 | size = sizeof(rec); |
402 | rec.p.timestamp = perf_get_timestamp(); | 402 | rec.p.timestamp = perf_get_timestamp(); |
403 | rec.code_addr = (uint64_t)(uintptr_t)code; | 403 | rec.code_addr = (uint64_t)(uintptr_t)code; |
404 | rec.nr_entry = num; | 404 | rec.nr_entry = nr_lines; |
405 | 405 | ||
406 | /* | 406 | /* |
407 | * on disk source line info layout: | 407 | * on disk source line info layout: |
408 | * uint64_t : addr | 408 | * uint64_t : addr |
409 | * int : line number | 409 | * int : line number |
410 | * int : column discriminator | ||
410 | * file[] : source file name | 411 | * file[] : source file name |
411 | * padding : pad to multiple of 8 bytes | 412 | * padding : pad to multiple of 8 bytes |
412 | */ | 413 | */ |
413 | size += num * (sizeof(uint64_t) + sizeof(int)); | 414 | size += nr_lines * sizeof(struct debug_entry); |
414 | size += flen + (num - 1) * 2; | 415 | size += flen * nr_lines; |
415 | /* | 416 | /* |
416 | * pad to 8 bytes | 417 | * pad to 8 bytes |
417 | */ | 418 | */ |
@@ -429,28 +430,27 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file, | |||
429 | if (sret != 1) | 430 | if (sret != 1) |
430 | goto error; | 431 | goto error; |
431 | 432 | ||
432 | for (i = 0; i < num; i++) { | 433 | for (i = 0; i < nr_lines; i++) { |
433 | uint64_t addr; | ||
434 | 434 | ||
435 | addr = (uint64_t)map[i].start_address; | 435 | addr = (uint64_t)li[i].pc; |
436 | len = sizeof(addr); | 436 | len = sizeof(addr); |
437 | sret = fwrite_unlocked(&addr, len, 1, fp); | 437 | sret = fwrite_unlocked(&addr, len, 1, fp); |
438 | if (sret != 1) | 438 | if (sret != 1) |
439 | goto error; | 439 | goto error; |
440 | 440 | ||
441 | len = sizeof(int); | 441 | len = sizeof(li[0].line_number); |
442 | sret = fwrite_unlocked(&li[i].line_number, len, 1, fp); | 442 | sret = fwrite_unlocked(&li[i].line_number, len, 1, fp); |
443 | if (sret != 1) | 443 | if (sret != 1) |
444 | goto error; | 444 | goto error; |
445 | 445 | ||
446 | if (i == 0) { | 446 | len = sizeof(li[0].discrim); |
447 | sret = fwrite_unlocked(file, flen, 1, fp); | 447 | sret = fwrite_unlocked(&li[i].discrim, len, 1, fp); |
448 | } else { | ||
449 | sret = fwrite_unlocked(prev_str, 2, 1, fp); | ||
450 | } | ||
451 | if (sret != 1) | 448 | if (sret != 1) |
452 | goto error; | 449 | goto error; |
453 | 450 | ||
451 | sret = fwrite_unlocked(fn, flen, 1, fp); | ||
452 | if (sret != 1) | ||
453 | goto error; | ||
454 | } | 454 | } |
455 | if (padding_count) | 455 | if (padding_count) |
456 | sret = fwrite_unlocked(pad_bytes, padding_count, 1, fp); | 456 | 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 @@ | |||
11 | extern "C" { | 11 | extern "C" { |
12 | #endif | 12 | #endif |
13 | 13 | ||
14 | typedef struct { | ||
15 | unsigned long pc; | ||
16 | int line_number; | ||
17 | int discrim; /* discriminator -- 0 for now */ | ||
18 | } jvmti_line_info_t; | ||
19 | |||
14 | void *jvmti_open(void); | 20 | void *jvmti_open(void); |
15 | int jvmti_close(void *agent); | 21 | int jvmti_close(void *agent); |
16 | int jvmti_write_code(void *agent, char const *symbol_name, | 22 | int jvmti_write_code(void *agent, char const *symbol_name, |
17 | uint64_t vma, void const *code, | 23 | uint64_t vma, void const *code, |
18 | const unsigned int code_size); | 24 | const unsigned int code_size); |
25 | |||
19 | int jvmti_write_debug_info(void *agent, | 26 | int jvmti_write_debug_info(void *agent, |
20 | uint64_t code, | 27 | uint64_t code, |
21 | const char *file, | 28 | const char *file, |
22 | jvmtiAddrLocationMap const *map, | 29 | jvmti_line_info_t *li, |
23 | jvmtiLineNumberEntry *tab, jint nr); | 30 | int nr_lines); |
24 | 31 | ||
25 | #if defined(__cplusplus) | 32 | #if defined(__cplusplus) |
26 | } | 33 | } |
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 @@ | |||
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include <err.h> | 5 | #include <err.h> |
6 | #include <jvmti.h> | 6 | #include <jvmti.h> |
7 | #include <jvmticmlr.h> | ||
7 | #include <limits.h> | 8 | #include <limits.h> |
8 | 9 | ||
9 | #include "jvmti_agent.h" | 10 | #include "jvmti_agent.h" |
@@ -11,6 +12,100 @@ | |||
11 | static int has_line_numbers; | 12 | static int has_line_numbers; |
12 | void *jvmti_agent; | 13 | void *jvmti_agent; |
13 | 14 | ||
15 | static jvmtiError | ||
16 | do_get_line_numbers(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci, | ||
17 | jvmti_line_info_t *tab, jint *nr) | ||
18 | { | ||
19 | jint i, lines = 0; | ||
20 | jint nr_lines = 0; | ||
21 | jvmtiLineNumberEntry *loc_tab = NULL; | ||
22 | jvmtiError ret; | ||
23 | |||
24 | ret = (*jvmti)->GetLineNumberTable(jvmti, m, &nr_lines, &loc_tab); | ||
25 | if (ret != JVMTI_ERROR_NONE) | ||
26 | return ret; | ||
27 | |||
28 | for (i = 0; i < nr_lines; i++) { | ||
29 | if (loc_tab[i].start_location < bci) { | ||
30 | tab[lines].pc = (unsigned long)pc; | ||
31 | tab[lines].line_number = loc_tab[i].line_number; | ||
32 | tab[lines].discrim = 0; /* not yet used */ | ||
33 | lines++; | ||
34 | } else { | ||
35 | break; | ||
36 | } | ||
37 | } | ||
38 | (*jvmti)->Deallocate(jvmti, (unsigned char *)loc_tab); | ||
39 | *nr = lines; | ||
40 | return JVMTI_ERROR_NONE; | ||
41 | } | ||
42 | |||
43 | static jvmtiError | ||
44 | get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **tab, int *nr_lines) | ||
45 | { | ||
46 | const jvmtiCompiledMethodLoadRecordHeader *hdr; | ||
47 | jvmtiCompiledMethodLoadInlineRecord *rec; | ||
48 | jvmtiLineNumberEntry *lne = NULL; | ||
49 | PCStackInfo *c; | ||
50 | jint nr, ret; | ||
51 | int nr_total = 0; | ||
52 | int i, lines_total = 0; | ||
53 | |||
54 | if (!(tab && nr_lines)) | ||
55 | return JVMTI_ERROR_NULL_POINTER; | ||
56 | |||
57 | /* | ||
58 | * Phase 1 -- get the number of lines necessary | ||
59 | */ | ||
60 | for (hdr = compile_info; hdr != NULL; hdr = hdr->next) { | ||
61 | if (hdr->kind == JVMTI_CMLR_INLINE_INFO) { | ||
62 | rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr; | ||
63 | for (i = 0; i < rec->numpcs; i++) { | ||
64 | c = rec->pcinfo + i; | ||
65 | nr = 0; | ||
66 | /* | ||
67 | * unfortunately, need a tab to get the number of lines! | ||
68 | */ | ||
69 | ret = (*jvmti)->GetLineNumberTable(jvmti, c->methods[0], &nr, &lne); | ||
70 | if (ret == JVMTI_ERROR_NONE) { | ||
71 | /* free what was allocated for nothing */ | ||
72 | (*jvmti)->Deallocate(jvmti, (unsigned char *)lne); | ||
73 | nr_total += (int)nr; | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | if (nr_total == 0) | ||
80 | return JVMTI_ERROR_NOT_FOUND; | ||
81 | |||
82 | /* | ||
83 | * Phase 2 -- allocate big enough line table | ||
84 | */ | ||
85 | *tab = malloc(nr_total * sizeof(**tab)); | ||
86 | if (!*tab) | ||
87 | return JVMTI_ERROR_OUT_OF_MEMORY; | ||
88 | |||
89 | for (hdr = compile_info; hdr != NULL; hdr = hdr->next) { | ||
90 | if (hdr->kind == JVMTI_CMLR_INLINE_INFO) { | ||
91 | rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr; | ||
92 | for (i = 0; i < rec->numpcs; i++) { | ||
93 | c = rec->pcinfo + i; | ||
94 | nr = 0; | ||
95 | ret = do_get_line_numbers(jvmti, c->pc, | ||
96 | c->methods[0], | ||
97 | c->bcis[0], | ||
98 | *tab + lines_total, | ||
99 | &nr); | ||
100 | if (ret == JVMTI_ERROR_NONE) | ||
101 | lines_total += nr; | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | *nr_lines = lines_total; | ||
106 | return JVMTI_ERROR_NONE; | ||
107 | } | ||
108 | |||
14 | static void JNICALL | 109 | static void JNICALL |
15 | compiled_method_load_cb(jvmtiEnv *jvmti, | 110 | compiled_method_load_cb(jvmtiEnv *jvmti, |
16 | jmethodID method, | 111 | jmethodID method, |
@@ -18,9 +113,9 @@ compiled_method_load_cb(jvmtiEnv *jvmti, | |||
18 | void const *code_addr, | 113 | void const *code_addr, |
19 | jint map_length, | 114 | jint map_length, |
20 | jvmtiAddrLocationMap const *map, | 115 | jvmtiAddrLocationMap const *map, |
21 | void const *compile_info __unused) | 116 | const void *compile_info) |
22 | { | 117 | { |
23 | jvmtiLineNumberEntry *tab = NULL; | 118 | jvmti_line_info_t *line_tab = NULL; |
24 | jclass decl_class; | 119 | jclass decl_class; |
25 | char *class_sign = NULL; | 120 | char *class_sign = NULL; |
26 | char *func_name = NULL; | 121 | char *func_name = NULL; |
@@ -29,7 +124,7 @@ compiled_method_load_cb(jvmtiEnv *jvmti, | |||
29 | char fn[PATH_MAX]; | 124 | char fn[PATH_MAX]; |
30 | uint64_t addr = (uint64_t)(uintptr_t)code_addr; | 125 | uint64_t addr = (uint64_t)(uintptr_t)code_addr; |
31 | jvmtiError ret; | 126 | jvmtiError ret; |
32 | jint nr_lines = 0; | 127 | int nr_lines = 0; /* in line_tab[] */ |
33 | size_t len; | 128 | size_t len; |
34 | 129 | ||
35 | ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method, | 130 | ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method, |
@@ -40,19 +135,19 @@ compiled_method_load_cb(jvmtiEnv *jvmti, | |||
40 | } | 135 | } |
41 | 136 | ||
42 | if (has_line_numbers && map && map_length) { | 137 | if (has_line_numbers && map && map_length) { |
43 | 138 | ret = get_line_numbers(jvmti, compile_info, &line_tab, &nr_lines); | |
44 | ret = (*jvmti)->GetLineNumberTable(jvmti, method, &nr_lines, &tab); | ||
45 | if (ret != JVMTI_ERROR_NONE) { | 139 | if (ret != JVMTI_ERROR_NONE) { |
46 | warnx("jvmti: cannot get line table for method"); | 140 | warnx("jvmti: cannot get line table for method"); |
47 | } else { | 141 | nr_lines = 0; |
48 | ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name); | ||
49 | if (ret != JVMTI_ERROR_NONE) { | ||
50 | warnx("jvmti: cannot get source filename ret=%d", ret); | ||
51 | nr_lines = 0; | ||
52 | } | ||
53 | } | 142 | } |
54 | } | 143 | } |
55 | 144 | ||
145 | ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name); | ||
146 | if (ret != JVMTI_ERROR_NONE) { | ||
147 | warnx("jvmti: cannot get source filename ret=%d", ret); | ||
148 | goto error; | ||
149 | } | ||
150 | |||
56 | ret = (*jvmti)->GetClassSignature(jvmti, decl_class, | 151 | ret = (*jvmti)->GetClassSignature(jvmti, decl_class, |
57 | &class_sign, NULL); | 152 | &class_sign, NULL); |
58 | if (ret != JVMTI_ERROR_NONE) { | 153 | if (ret != JVMTI_ERROR_NONE) { |
@@ -92,13 +187,14 @@ compiled_method_load_cb(jvmtiEnv *jvmti, | |||
92 | /* | 187 | /* |
93 | * write source line info record if we have it | 188 | * write source line info record if we have it |
94 | */ | 189 | */ |
95 | if (jvmti_write_debug_info(jvmti_agent, addr, fn, map, tab, nr_lines)) | 190 | if (jvmti_write_debug_info(jvmti_agent, addr, fn, line_tab, nr_lines)) |
96 | warnx("jvmti: write_debug_info() failed"); | 191 | warnx("jvmti: write_debug_info() failed"); |
97 | 192 | ||
98 | len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2; | 193 | len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2; |
99 | { | 194 | { |
100 | char str[len]; | 195 | char str[len]; |
101 | snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign); | 196 | snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign); |
197 | |||
102 | if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size)) | 198 | if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size)) |
103 | warnx("jvmti: write_code() failed"); | 199 | warnx("jvmti: write_code() failed"); |
104 | } | 200 | } |
@@ -106,8 +202,8 @@ error: | |||
106 | (*jvmti)->Deallocate(jvmti, (unsigned char *)func_name); | 202 | (*jvmti)->Deallocate(jvmti, (unsigned char *)func_name); |
107 | (*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign); | 203 | (*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign); |
108 | (*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign); | 204 | (*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign); |
109 | (*jvmti)->Deallocate(jvmti, (unsigned char *)tab); | ||
110 | (*jvmti)->Deallocate(jvmti, (unsigned char *)file_name); | 205 | (*jvmti)->Deallocate(jvmti, (unsigned char *)file_name); |
206 | free(line_tab); | ||
111 | } | 207 | } |
112 | 208 | ||
113 | static void JNICALL | 209 | static void JNICALL |
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 52a4a806ee2f..a34752d28488 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
@@ -108,8 +108,11 @@ libperf-$(CONFIG_LZMA) += lzma.o | |||
108 | libperf-y += demangle-java.o | 108 | libperf-y += demangle-java.o |
109 | libperf-$(CONFIG_LIBELF) += jitdump.o | 109 | libperf-$(CONFIG_LIBELF) += jitdump.o |
110 | libperf-$(CONFIG_LIBELF) += genelf.o | 110 | libperf-$(CONFIG_LIBELF) += genelf.o |
111 | libperf-$(CONFIG_LIBELF) += genelf_debug.o | ||
111 | 112 | ||
112 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" | 113 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" |
114 | # avoid compiler warnings in 32-bit mode | ||
115 | CFLAGS_genelf_debug.o += -Wno-packed | ||
113 | 116 | ||
114 | $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c | 117 | $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c |
115 | $(call rule_mkdir) | 118 | $(call rule_mkdir) |
diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c index 145f8116ef56..c1ef805c6a8f 100644 --- a/tools/perf/util/genelf.c +++ b/tools/perf/util/genelf.c | |||
@@ -156,7 +156,8 @@ gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *cod | |||
156 | */ | 156 | */ |
157 | int | 157 | int |
158 | jit_write_elf(int fd, uint64_t load_addr, const char *sym, | 158 | jit_write_elf(int fd, uint64_t load_addr, const char *sym, |
159 | const void *code, int csize) | 159 | const void *code, int csize, |
160 | void *debug, int nr_debug_entries) | ||
160 | { | 161 | { |
161 | Elf *e; | 162 | Elf *e; |
162 | Elf_Data *d; | 163 | Elf_Data *d; |
@@ -385,9 +386,15 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, | |||
385 | shdr->sh_size = sizeof(bnote); | 386 | shdr->sh_size = sizeof(bnote); |
386 | shdr->sh_entsize = 0; | 387 | shdr->sh_entsize = 0; |
387 | 388 | ||
388 | if (elf_update(e, ELF_C_WRITE) < 0) { | 389 | if (debug && nr_debug_entries) { |
389 | warnx("elf_update 4 failed"); | 390 | retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries); |
390 | goto error; | 391 | if (retval) |
392 | goto error; | ||
393 | } else { | ||
394 | if (elf_update(e, ELF_C_WRITE) < 0) { | ||
395 | warnx("elf_update 4 failed"); | ||
396 | goto error; | ||
397 | } | ||
391 | } | 398 | } |
392 | 399 | ||
393 | retval = 0; | 400 | retval = 0; |
diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h index d8e9ece13c8b..45bf9c6d3257 100644 --- a/tools/perf/util/genelf.h +++ b/tools/perf/util/genelf.h | |||
@@ -3,7 +3,11 @@ | |||
3 | 3 | ||
4 | /* genelf.c */ | 4 | /* genelf.c */ |
5 | extern int jit_write_elf(int fd, uint64_t code_addr, const char *sym, | 5 | extern int jit_write_elf(int fd, uint64_t code_addr, const char *sym, |
6 | const void *code, int csize); | 6 | const void *code, int csize, |
7 | void *debug, int nr_debug_entries); | ||
8 | /* genelf_debug.c */ | ||
9 | extern int jit_add_debug_info(Elf *e, uint64_t code_addr, | ||
10 | void *debug, int nr_debug_entries); | ||
7 | 11 | ||
8 | #if defined(__arm__) | 12 | #if defined(__arm__) |
9 | #define GEN_ELF_ARCH EM_ARM | 13 | #define GEN_ELF_ARCH EM_ARM |
diff --git a/tools/perf/util/genelf_debug.c b/tools/perf/util/genelf_debug.c new file mode 100644 index 000000000000..5980f7d256b1 --- /dev/null +++ b/tools/perf/util/genelf_debug.c | |||
@@ -0,0 +1,610 @@ | |||
1 | /* | ||
2 | * genelf_debug.c | ||
3 | * Copyright (C) 2015, Google, Inc | ||
4 | * | ||
5 | * Contributed by: | ||
6 | * Stephane Eranian <eranian@google.com> | ||
7 | * | ||
8 | * Released under the GPL v2. | ||
9 | * | ||
10 | * based on GPLv2 source code from Oprofile | ||
11 | * @remark Copyright 2007 OProfile authors | ||
12 | * @author Philippe Elie | ||
13 | */ | ||
14 | #include <sys/types.h> | ||
15 | #include <stdio.h> | ||
16 | #include <getopt.h> | ||
17 | #include <stddef.h> | ||
18 | #include <libelf.h> | ||
19 | #include <string.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <inttypes.h> | ||
22 | #include <limits.h> | ||
23 | #include <fcntl.h> | ||
24 | #include <err.h> | ||
25 | #include <dwarf.h> | ||
26 | |||
27 | #include "perf.h" | ||
28 | #include "genelf.h" | ||
29 | #include "../util/jitdump.h" | ||
30 | |||
31 | #define BUFFER_EXT_DFL_SIZE (4 * 1024) | ||
32 | |||
33 | typedef uint32_t uword; | ||
34 | typedef uint16_t uhalf; | ||
35 | typedef int32_t sword; | ||
36 | typedef int16_t shalf; | ||
37 | typedef uint8_t ubyte; | ||
38 | typedef int8_t sbyte; | ||
39 | |||
40 | struct buffer_ext { | ||
41 | size_t cur_pos; | ||
42 | size_t max_sz; | ||
43 | void *data; | ||
44 | }; | ||
45 | |||
46 | static void | ||
47 | buffer_ext_dump(struct buffer_ext *be, const char *msg) | ||
48 | { | ||
49 | size_t i; | ||
50 | warnx("DUMP for %s", msg); | ||
51 | for (i = 0 ; i < be->cur_pos; i++) | ||
52 | warnx("%4zu 0x%02x", i, (((char *)be->data)[i]) & 0xff); | ||
53 | } | ||
54 | |||
55 | static inline int | ||
56 | buffer_ext_add(struct buffer_ext *be, void *addr, size_t sz) | ||
57 | { | ||
58 | void *tmp; | ||
59 | size_t be_sz = be->max_sz; | ||
60 | |||
61 | retry: | ||
62 | if ((be->cur_pos + sz) < be_sz) { | ||
63 | memcpy(be->data + be->cur_pos, addr, sz); | ||
64 | be->cur_pos += sz; | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | if (!be_sz) | ||
69 | be_sz = BUFFER_EXT_DFL_SIZE; | ||
70 | else | ||
71 | be_sz <<= 1; | ||
72 | |||
73 | tmp = realloc(be->data, be_sz); | ||
74 | if (!tmp) | ||
75 | return -1; | ||
76 | |||
77 | be->data = tmp; | ||
78 | be->max_sz = be_sz; | ||
79 | |||
80 | goto retry; | ||
81 | } | ||
82 | |||
83 | static void | ||
84 | buffer_ext_init(struct buffer_ext *be) | ||
85 | { | ||
86 | be->data = NULL; | ||
87 | be->cur_pos = 0; | ||
88 | be->max_sz = 0; | ||
89 | } | ||
90 | |||
91 | static inline size_t | ||
92 | buffer_ext_size(struct buffer_ext *be) | ||
93 | { | ||
94 | return be->cur_pos; | ||
95 | } | ||
96 | |||
97 | static inline void * | ||
98 | buffer_ext_addr(struct buffer_ext *be) | ||
99 | { | ||
100 | return be->data; | ||
101 | } | ||
102 | |||
103 | struct debug_line_header { | ||
104 | // Not counting this field | ||
105 | uword total_length; | ||
106 | // version number (2 currently) | ||
107 | uhalf version; | ||
108 | // relative offset from next field to | ||
109 | // program statement | ||
110 | uword prolog_length; | ||
111 | ubyte minimum_instruction_length; | ||
112 | ubyte default_is_stmt; | ||
113 | // line_base - see DWARF 2 specs | ||
114 | sbyte line_base; | ||
115 | // line_range - see DWARF 2 specs | ||
116 | ubyte line_range; | ||
117 | // number of opcode + 1 | ||
118 | ubyte opcode_base; | ||
119 | /* follow the array of opcode args nr: ubytes [nr_opcode_base] */ | ||
120 | /* follow the search directories index, zero terminated string | ||
121 | * terminated by an empty string. | ||
122 | */ | ||
123 | /* follow an array of { filename, LEB128, LEB128, LEB128 }, first is | ||
124 | * the directory index entry, 0 means current directory, then mtime | ||
125 | * and filesize, last entry is followed by en empty string. | ||
126 | */ | ||
127 | /* follow the first program statement */ | ||
128 | } __attribute__((packed)); | ||
129 | |||
130 | /* DWARF 2 spec talk only about one possible compilation unit header while | ||
131 | * binutils can handle two flavours of dwarf 2, 32 and 64 bits, this is not | ||
132 | * related to the used arch, an ELF 32 can hold more than 4 Go of debug | ||
133 | * information. For now we handle only DWARF 2 32 bits comp unit. It'll only | ||
134 | * become a problem if we generate more than 4GB of debug information. | ||
135 | */ | ||
136 | struct compilation_unit_header { | ||
137 | uword total_length; | ||
138 | uhalf version; | ||
139 | uword debug_abbrev_offset; | ||
140 | ubyte pointer_size; | ||
141 | } __attribute__((packed)); | ||
142 | |||
143 | #define DW_LNS_num_opcode (DW_LNS_set_isa + 1) | ||
144 | |||
145 | /* field filled at run time are marked with -1 */ | ||
146 | static struct debug_line_header const default_debug_line_header = { | ||
147 | .total_length = -1, | ||
148 | .version = 2, | ||
149 | .prolog_length = -1, | ||
150 | .minimum_instruction_length = 1, /* could be better when min instruction size != 1 */ | ||
151 | .default_is_stmt = 1, /* we don't take care about basic block */ | ||
152 | .line_base = -5, /* sensible value for line base ... */ | ||
153 | .line_range = -14, /* ... and line range are guessed statically */ | ||
154 | .opcode_base = DW_LNS_num_opcode | ||
155 | }; | ||
156 | |||
157 | static ubyte standard_opcode_length[] = | ||
158 | { | ||
159 | 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 | ||
160 | }; | ||
161 | #if 0 | ||
162 | { | ||
163 | [DW_LNS_advance_pc] = 1, | ||
164 | [DW_LNS_advance_line] = 1, | ||
165 | [DW_LNS_set_file] = 1, | ||
166 | [DW_LNS_set_column] = 1, | ||
167 | [DW_LNS_fixed_advance_pc] = 1, | ||
168 | [DW_LNS_set_isa] = 1, | ||
169 | }; | ||
170 | #endif | ||
171 | |||
172 | /* field filled at run time are marked with -1 */ | ||
173 | static struct compilation_unit_header default_comp_unit_header = { | ||
174 | .total_length = -1, | ||
175 | .version = 2, | ||
176 | .debug_abbrev_offset = 0, /* we reuse the same abbrev entries for all comp unit */ | ||
177 | .pointer_size = sizeof(void *) | ||
178 | }; | ||
179 | |||
180 | static void emit_uword(struct buffer_ext *be, uword data) | ||
181 | { | ||
182 | buffer_ext_add(be, &data, sizeof(uword)); | ||
183 | } | ||
184 | |||
185 | static void emit_string(struct buffer_ext *be, const char *s) | ||
186 | { | ||
187 | buffer_ext_add(be, (void *)s, strlen(s) + 1); | ||
188 | } | ||
189 | |||
190 | static void emit_unsigned_LEB128(struct buffer_ext *be, | ||
191 | unsigned long data) | ||
192 | { | ||
193 | do { | ||
194 | ubyte cur = data & 0x7F; | ||
195 | data >>= 7; | ||
196 | if (data) | ||
197 | cur |= 0x80; | ||
198 | buffer_ext_add(be, &cur, 1); | ||
199 | } while (data); | ||
200 | } | ||
201 | |||
202 | static void emit_signed_LEB128(struct buffer_ext *be, long data) | ||
203 | { | ||
204 | int more = 1; | ||
205 | int negative = data < 0; | ||
206 | int size = sizeof(long) * CHAR_BIT; | ||
207 | while (more) { | ||
208 | ubyte cur = data & 0x7F; | ||
209 | data >>= 7; | ||
210 | if (negative) | ||
211 | data |= - (1 << (size - 7)); | ||
212 | if ((data == 0 && !(cur & 0x40)) || | ||
213 | (data == -1l && (cur & 0x40))) | ||
214 | more = 0; | ||
215 | else | ||
216 | cur |= 0x80; | ||
217 | buffer_ext_add(be, &cur, 1); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static void emit_extended_opcode(struct buffer_ext *be, ubyte opcode, | ||
222 | void *data, size_t data_len) | ||
223 | { | ||
224 | buffer_ext_add(be, (char *)"", 1); | ||
225 | |||
226 | emit_unsigned_LEB128(be, data_len + 1); | ||
227 | |||
228 | buffer_ext_add(be, &opcode, 1); | ||
229 | buffer_ext_add(be, data, data_len); | ||
230 | } | ||
231 | |||
232 | static void emit_opcode(struct buffer_ext *be, ubyte opcode) | ||
233 | { | ||
234 | buffer_ext_add(be, &opcode, 1); | ||
235 | } | ||
236 | |||
237 | static void emit_opcode_signed(struct buffer_ext *be, | ||
238 | ubyte opcode, long data) | ||
239 | { | ||
240 | buffer_ext_add(be, &opcode, 1); | ||
241 | emit_signed_LEB128(be, data); | ||
242 | } | ||
243 | |||
244 | static void emit_opcode_unsigned(struct buffer_ext *be, ubyte opcode, | ||
245 | unsigned long data) | ||
246 | { | ||
247 | buffer_ext_add(be, &opcode, 1); | ||
248 | emit_unsigned_LEB128(be, data); | ||
249 | } | ||
250 | |||
251 | static void emit_advance_pc(struct buffer_ext *be, unsigned long delta_pc) | ||
252 | { | ||
253 | emit_opcode_unsigned(be, DW_LNS_advance_pc, delta_pc); | ||
254 | } | ||
255 | |||
256 | static void emit_advance_lineno(struct buffer_ext *be, long delta_lineno) | ||
257 | { | ||
258 | emit_opcode_signed(be, DW_LNS_advance_line, delta_lineno); | ||
259 | } | ||
260 | |||
261 | static void emit_lne_end_of_sequence(struct buffer_ext *be) | ||
262 | { | ||
263 | emit_extended_opcode(be, DW_LNE_end_sequence, NULL, 0); | ||
264 | } | ||
265 | |||
266 | static void emit_set_file(struct buffer_ext *be, unsigned long idx) | ||
267 | { | ||
268 | emit_opcode_unsigned(be, DW_LNS_set_file, idx); | ||
269 | } | ||
270 | |||
271 | static void emit_lne_define_filename(struct buffer_ext *be, | ||
272 | const char *filename) | ||
273 | { | ||
274 | buffer_ext_add(be, (void *)"", 1); | ||
275 | |||
276 | /* LNE field, strlen(filename) + zero termination, 3 bytes for: the dir entry, timestamp, filesize */ | ||
277 | emit_unsigned_LEB128(be, strlen(filename) + 5); | ||
278 | emit_opcode(be, DW_LNE_define_file); | ||
279 | emit_string(be, filename); | ||
280 | /* directory index 0=do not know */ | ||
281 | emit_unsigned_LEB128(be, 0); | ||
282 | /* last modification date on file 0=do not know */ | ||
283 | emit_unsigned_LEB128(be, 0); | ||
284 | /* filesize 0=do not know */ | ||
285 | emit_unsigned_LEB128(be, 0); | ||
286 | } | ||
287 | |||
288 | static void emit_lne_set_address(struct buffer_ext *be, | ||
289 | void *address) | ||
290 | { | ||
291 | emit_extended_opcode(be, DW_LNE_set_address, &address, sizeof(unsigned long)); | ||
292 | } | ||
293 | |||
294 | static ubyte get_special_opcode(struct debug_entry *ent, | ||
295 | unsigned int last_line, | ||
296 | unsigned long last_vma) | ||
297 | { | ||
298 | unsigned int temp; | ||
299 | unsigned long delta_addr; | ||
300 | |||
301 | /* | ||
302 | * delta from line_base | ||
303 | */ | ||
304 | temp = (ent->lineno - last_line) - default_debug_line_header.line_base; | ||
305 | |||
306 | if (temp >= default_debug_line_header.line_range) | ||
307 | return 0; | ||
308 | |||
309 | /* | ||
310 | * delta of addresses | ||
311 | */ | ||
312 | delta_addr = (ent->addr - last_vma) / default_debug_line_header.minimum_instruction_length; | ||
313 | |||
314 | /* This is not sufficient to ensure opcode will be in [0-256] but | ||
315 | * sufficient to ensure when summing with the delta lineno we will | ||
316 | * not overflow the unsigned long opcode */ | ||
317 | |||
318 | if (delta_addr <= 256 / default_debug_line_header.line_range) { | ||
319 | unsigned long opcode = temp + | ||
320 | (delta_addr * default_debug_line_header.line_range) + | ||
321 | default_debug_line_header.opcode_base; | ||
322 | |||
323 | return opcode <= 255 ? opcode : 0; | ||
324 | } | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static void emit_lineno_info(struct buffer_ext *be, | ||
329 | struct debug_entry *ent, size_t nr_entry, | ||
330 | unsigned long code_addr) | ||
331 | { | ||
332 | size_t i; | ||
333 | |||
334 | /* | ||
335 | * Machine state at start of a statement program | ||
336 | * address = 0 | ||
337 | * file = 1 | ||
338 | * line = 1 | ||
339 | * column = 0 | ||
340 | * is_stmt = default_is_stmt as given in the debug_line_header | ||
341 | * basic block = 0 | ||
342 | * end sequence = 0 | ||
343 | */ | ||
344 | |||
345 | /* start state of the state machine we take care of */ | ||
346 | unsigned long last_vma = code_addr; | ||
347 | char const *cur_filename = NULL; | ||
348 | unsigned long cur_file_idx = 0; | ||
349 | int last_line = 1; | ||
350 | |||
351 | emit_lne_set_address(be, (void *)code_addr); | ||
352 | |||
353 | for (i = 0; i < nr_entry; i++, ent = debug_entry_next(ent)) { | ||
354 | int need_copy = 0; | ||
355 | ubyte special_opcode; | ||
356 | |||
357 | /* | ||
358 | * check if filename changed, if so add it | ||
359 | */ | ||
360 | if (!cur_filename || strcmp(cur_filename, ent->name)) { | ||
361 | emit_lne_define_filename(be, ent->name); | ||
362 | cur_filename = ent->name; | ||
363 | emit_set_file(be, ++cur_file_idx); | ||
364 | need_copy = 1; | ||
365 | } | ||
366 | |||
367 | special_opcode = get_special_opcode(ent, last_line, last_vma); | ||
368 | if (special_opcode != 0) { | ||
369 | last_line = ent->lineno; | ||
370 | last_vma = ent->addr; | ||
371 | emit_opcode(be, special_opcode); | ||
372 | } else { | ||
373 | /* | ||
374 | * lines differ, emit line delta | ||
375 | */ | ||
376 | if (last_line != ent->lineno) { | ||
377 | emit_advance_lineno(be, ent->lineno - last_line); | ||
378 | last_line = ent->lineno; | ||
379 | need_copy = 1; | ||
380 | } | ||
381 | /* | ||
382 | * addresses differ, emit address delta | ||
383 | */ | ||
384 | if (last_vma != ent->addr) { | ||
385 | emit_advance_pc(be, ent->addr - last_vma); | ||
386 | last_vma = ent->addr; | ||
387 | need_copy = 1; | ||
388 | } | ||
389 | /* | ||
390 | * add new row to matrix | ||
391 | */ | ||
392 | if (need_copy) | ||
393 | emit_opcode(be, DW_LNS_copy); | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | |||
398 | static void add_debug_line(struct buffer_ext *be, | ||
399 | struct debug_entry *ent, size_t nr_entry, | ||
400 | unsigned long code_addr) | ||
401 | { | ||
402 | struct debug_line_header * dbg_header; | ||
403 | size_t old_size; | ||
404 | |||
405 | old_size = buffer_ext_size(be); | ||
406 | |||
407 | buffer_ext_add(be, (void *)&default_debug_line_header, | ||
408 | sizeof(default_debug_line_header)); | ||
409 | |||
410 | buffer_ext_add(be, &standard_opcode_length, sizeof(standard_opcode_length)); | ||
411 | |||
412 | // empty directory entry | ||
413 | buffer_ext_add(be, (void *)"", 1); | ||
414 | |||
415 | // empty filename directory | ||
416 | buffer_ext_add(be, (void *)"", 1); | ||
417 | |||
418 | dbg_header = buffer_ext_addr(be) + old_size; | ||
419 | dbg_header->prolog_length = (buffer_ext_size(be) - old_size) - | ||
420 | offsetof(struct debug_line_header, minimum_instruction_length); | ||
421 | |||
422 | emit_lineno_info(be, ent, nr_entry, code_addr); | ||
423 | |||
424 | emit_lne_end_of_sequence(be); | ||
425 | |||
426 | dbg_header = buffer_ext_addr(be) + old_size; | ||
427 | dbg_header->total_length = (buffer_ext_size(be) - old_size) - | ||
428 | offsetof(struct debug_line_header, version); | ||
429 | } | ||
430 | |||
431 | static void | ||
432 | add_debug_abbrev(struct buffer_ext *be) | ||
433 | { | ||
434 | emit_unsigned_LEB128(be, 1); | ||
435 | emit_unsigned_LEB128(be, DW_TAG_compile_unit); | ||
436 | emit_unsigned_LEB128(be, DW_CHILDREN_yes); | ||
437 | emit_unsigned_LEB128(be, DW_AT_stmt_list); | ||
438 | emit_unsigned_LEB128(be, DW_FORM_data4); | ||
439 | emit_unsigned_LEB128(be, 0); | ||
440 | emit_unsigned_LEB128(be, 0); | ||
441 | emit_unsigned_LEB128(be, 0); | ||
442 | } | ||
443 | |||
444 | static void | ||
445 | add_compilation_unit(struct buffer_ext *be, | ||
446 | size_t offset_debug_line) | ||
447 | { | ||
448 | struct compilation_unit_header *comp_unit_header; | ||
449 | size_t old_size = buffer_ext_size(be); | ||
450 | |||
451 | buffer_ext_add(be, &default_comp_unit_header, | ||
452 | sizeof(default_comp_unit_header)); | ||
453 | |||
454 | emit_unsigned_LEB128(be, 1); | ||
455 | emit_uword(be, offset_debug_line); | ||
456 | |||
457 | comp_unit_header = buffer_ext_addr(be) + old_size; | ||
458 | comp_unit_header->total_length = (buffer_ext_size(be) - old_size) - | ||
459 | offsetof(struct compilation_unit_header, version); | ||
460 | } | ||
461 | |||
462 | static int | ||
463 | jit_process_debug_info(uint64_t code_addr, | ||
464 | void *debug, int nr_debug_entries, | ||
465 | struct buffer_ext *dl, | ||
466 | struct buffer_ext *da, | ||
467 | struct buffer_ext *di) | ||
468 | { | ||
469 | struct debug_entry *ent = debug; | ||
470 | int i; | ||
471 | |||
472 | for (i = 0; i < nr_debug_entries; i++) { | ||
473 | ent->addr = ent->addr - code_addr; | ||
474 | ent = debug_entry_next(ent); | ||
475 | } | ||
476 | add_compilation_unit(di, buffer_ext_size(dl)); | ||
477 | add_debug_line(dl, debug, nr_debug_entries, 0); | ||
478 | add_debug_abbrev(da); | ||
479 | if (0) buffer_ext_dump(da, "abbrev"); | ||
480 | |||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | int | ||
485 | jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries) | ||
486 | { | ||
487 | Elf_Data *d; | ||
488 | Elf_Scn *scn; | ||
489 | Elf_Shdr *shdr; | ||
490 | struct buffer_ext dl, di, da; | ||
491 | int ret; | ||
492 | |||
493 | buffer_ext_init(&dl); | ||
494 | buffer_ext_init(&di); | ||
495 | buffer_ext_init(&da); | ||
496 | |||
497 | ret = jit_process_debug_info(code_addr, debug, nr_debug_entries, &dl, &da, &di); | ||
498 | if (ret) | ||
499 | return -1; | ||
500 | /* | ||
501 | * setup .debug_line section | ||
502 | */ | ||
503 | scn = elf_newscn(e); | ||
504 | if (!scn) { | ||
505 | warnx("cannot create section"); | ||
506 | return -1; | ||
507 | } | ||
508 | |||
509 | d = elf_newdata(scn); | ||
510 | if (!d) { | ||
511 | warnx("cannot get new data"); | ||
512 | return -1; | ||
513 | } | ||
514 | |||
515 | d->d_align = 1; | ||
516 | d->d_off = 0LL; | ||
517 | d->d_buf = buffer_ext_addr(&dl); | ||
518 | d->d_type = ELF_T_BYTE; | ||
519 | d->d_size = buffer_ext_size(&dl); | ||
520 | d->d_version = EV_CURRENT; | ||
521 | |||
522 | shdr = elf_getshdr(scn); | ||
523 | if (!shdr) { | ||
524 | warnx("cannot get section header"); | ||
525 | return -1; | ||
526 | } | ||
527 | |||
528 | shdr->sh_name = 52; /* .debug_line */ | ||
529 | shdr->sh_type = SHT_PROGBITS; | ||
530 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | ||
531 | shdr->sh_flags = 0; | ||
532 | shdr->sh_entsize = 0; | ||
533 | |||
534 | /* | ||
535 | * setup .debug_info section | ||
536 | */ | ||
537 | scn = elf_newscn(e); | ||
538 | if (!scn) { | ||
539 | warnx("cannot create section"); | ||
540 | return -1; | ||
541 | } | ||
542 | |||
543 | d = elf_newdata(scn); | ||
544 | if (!d) { | ||
545 | warnx("cannot get new data"); | ||
546 | return -1; | ||
547 | } | ||
548 | |||
549 | d->d_align = 1; | ||
550 | d->d_off = 0LL; | ||
551 | d->d_buf = buffer_ext_addr(&di); | ||
552 | d->d_type = ELF_T_BYTE; | ||
553 | d->d_size = buffer_ext_size(&di); | ||
554 | d->d_version = EV_CURRENT; | ||
555 | |||
556 | shdr = elf_getshdr(scn); | ||
557 | if (!shdr) { | ||
558 | warnx("cannot get section header"); | ||
559 | return -1; | ||
560 | } | ||
561 | |||
562 | shdr->sh_name = 64; /* .debug_info */ | ||
563 | shdr->sh_type = SHT_PROGBITS; | ||
564 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | ||
565 | shdr->sh_flags = 0; | ||
566 | shdr->sh_entsize = 0; | ||
567 | |||
568 | /* | ||
569 | * setup .debug_abbrev section | ||
570 | */ | ||
571 | scn = elf_newscn(e); | ||
572 | if (!scn) { | ||
573 | warnx("cannot create section"); | ||
574 | return -1; | ||
575 | } | ||
576 | |||
577 | d = elf_newdata(scn); | ||
578 | if (!d) { | ||
579 | warnx("cannot get new data"); | ||
580 | return -1; | ||
581 | } | ||
582 | |||
583 | d->d_align = 1; | ||
584 | d->d_off = 0LL; | ||
585 | d->d_buf = buffer_ext_addr(&da); | ||
586 | d->d_type = ELF_T_BYTE; | ||
587 | d->d_size = buffer_ext_size(&da); | ||
588 | d->d_version = EV_CURRENT; | ||
589 | |||
590 | shdr = elf_getshdr(scn); | ||
591 | if (!shdr) { | ||
592 | warnx("cannot get section header"); | ||
593 | return -1; | ||
594 | } | ||
595 | |||
596 | shdr->sh_name = 76; /* .debug_info */ | ||
597 | shdr->sh_type = SHT_PROGBITS; | ||
598 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | ||
599 | shdr->sh_flags = 0; | ||
600 | shdr->sh_entsize = 0; | ||
601 | |||
602 | /* | ||
603 | * now we update the ELF image with all the sections | ||
604 | */ | ||
605 | if (elf_update(e, ELF_C_WRITE) < 0) { | ||
606 | warnx("elf_update debug failed"); | ||
607 | return -1; | ||
608 | } | ||
609 | return 0; | ||
610 | } | ||
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 9f7a01289efe..99fa5eee9fe0 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c | |||
@@ -63,7 +63,9 @@ jit_emit_elf(char *filename, | |||
63 | const char *sym, | 63 | const char *sym, |
64 | uint64_t code_addr, | 64 | uint64_t code_addr, |
65 | const void *code, | 65 | const void *code, |
66 | int csize) | 66 | int csize, |
67 | void *debug, | ||
68 | int nr_debug_entries) | ||
67 | { | 69 | { |
68 | int ret, fd; | 70 | int ret, fd; |
69 | 71 | ||
@@ -76,7 +78,7 @@ jit_emit_elf(char *filename, | |||
76 | return -1; | 78 | return -1; |
77 | } | 79 | } |
78 | 80 | ||
79 | ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize); | 81 | ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries); |
80 | 82 | ||
81 | close(fd); | 83 | close(fd); |
82 | 84 | ||
@@ -347,7 +349,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | |||
347 | 349 | ||
348 | size = PERF_ALIGN(size, sizeof(u64)); | 350 | size = PERF_ALIGN(size, sizeof(u64)); |
349 | uaddr = (uintptr_t)code; | 351 | uaddr = (uintptr_t)code; |
350 | ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize); | 352 | ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries); |
351 | 353 | ||
352 | if (jd->debug_data && jd->nr_debug_entries) { | 354 | if (jd->debug_data && jd->nr_debug_entries) { |
353 | free(jd->debug_data); | 355 | free(jd->debug_data); |