diff options
| -rw-r--r-- | tools/perf/Documentation/perf-inject.txt | 7 | ||||
| -rw-r--r-- | tools/perf/builtin-inject.c | 98 | ||||
| -rw-r--r-- | tools/perf/util/Build | 2 | ||||
| -rw-r--r-- | tools/perf/util/genelf.c | 442 | ||||
| -rw-r--r-- | tools/perf/util/genelf.h | 63 | ||||
| -rw-r--r-- | tools/perf/util/jit.h | 15 | ||||
| -rw-r--r-- | tools/perf/util/jitdump.c | 670 | ||||
| -rw-r--r-- | tools/perf/util/jitdump.h | 124 |
8 files changed, 1418 insertions, 3 deletions
diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt index 0b1cedeef895..87b2588d1cbd 100644 --- a/tools/perf/Documentation/perf-inject.txt +++ b/tools/perf/Documentation/perf-inject.txt | |||
| @@ -53,6 +53,13 @@ include::itrace.txt[] | |||
| 53 | --strip:: | 53 | --strip:: |
| 54 | Use with --itrace to strip out non-synthesized events. | 54 | Use with --itrace to strip out non-synthesized events. |
| 55 | 55 | ||
| 56 | -j:: | ||
| 57 | --jit:: | ||
| 58 | Process jitdump files by injecting the mmap records corresponding to jitted | ||
| 59 | functions. This option also generates the ELF images for each jitted function | ||
| 60 | found in the jitdumps files captured in the input perf.data file. Use this option | ||
| 61 | if you are monitoring environment using JIT runtimes, such as Java, DART or V8. | ||
| 62 | |||
| 56 | SEE ALSO | 63 | SEE ALSO |
| 57 | -------- | 64 | -------- |
| 58 | linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1] | 65 | linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1] |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 6567baedd92a..b38445f08c2f 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "util/build-id.h" | 17 | #include "util/build-id.h" |
| 18 | #include "util/data.h" | 18 | #include "util/data.h" |
| 19 | #include "util/auxtrace.h" | 19 | #include "util/auxtrace.h" |
| 20 | #include "util/jit.h" | ||
| 20 | 21 | ||
| 21 | #include <subcmd/parse-options.h> | 22 | #include <subcmd/parse-options.h> |
| 22 | 23 | ||
| @@ -29,6 +30,7 @@ struct perf_inject { | |||
| 29 | bool sched_stat; | 30 | bool sched_stat; |
| 30 | bool have_auxtrace; | 31 | bool have_auxtrace; |
| 31 | bool strip; | 32 | bool strip; |
| 33 | bool jit_mode; | ||
| 32 | const char *input_name; | 34 | const char *input_name; |
| 33 | struct perf_data_file output; | 35 | struct perf_data_file output; |
| 34 | u64 bytes_written; | 36 | u64 bytes_written; |
| @@ -71,6 +73,15 @@ static int perf_event__repipe_oe_synth(struct perf_tool *tool, | |||
| 71 | return perf_event__repipe_synth(tool, event); | 73 | return perf_event__repipe_synth(tool, event); |
| 72 | } | 74 | } |
| 73 | 75 | ||
| 76 | #ifdef HAVE_LIBELF_SUPPORT | ||
| 77 | static int perf_event__drop_oe(struct perf_tool *tool __maybe_unused, | ||
| 78 | union perf_event *event __maybe_unused, | ||
| 79 | struct ordered_events *oe __maybe_unused) | ||
| 80 | { | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | #endif | ||
| 84 | |||
| 74 | static int perf_event__repipe_op2_synth(struct perf_tool *tool, | 85 | static int perf_event__repipe_op2_synth(struct perf_tool *tool, |
| 75 | union perf_event *event, | 86 | union perf_event *event, |
| 76 | struct perf_session *session | 87 | struct perf_session *session |
| @@ -234,6 +245,27 @@ static int perf_event__repipe_mmap(struct perf_tool *tool, | |||
| 234 | return err; | 245 | return err; |
| 235 | } | 246 | } |
| 236 | 247 | ||
| 248 | #ifdef HAVE_LIBELF_SUPPORT | ||
| 249 | static int perf_event__jit_repipe_mmap(struct perf_tool *tool, | ||
| 250 | union perf_event *event, | ||
| 251 | struct perf_sample *sample, | ||
| 252 | struct machine *machine) | ||
| 253 | { | ||
| 254 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
| 255 | u64 n = 0; | ||
| 256 | |||
| 257 | /* | ||
| 258 | * if jit marker, then inject jit mmaps and generate ELF images | ||
| 259 | */ | ||
| 260 | if (!jit_process(inject->session, &inject->output, machine, | ||
| 261 | event->mmap.filename, sample->pid, &n)) { | ||
| 262 | inject->bytes_written += n; | ||
| 263 | return 0; | ||
| 264 | } | ||
| 265 | return perf_event__repipe_mmap(tool, event, sample, machine); | ||
| 266 | } | ||
| 267 | #endif | ||
| 268 | |||
| 237 | static int perf_event__repipe_mmap2(struct perf_tool *tool, | 269 | static int perf_event__repipe_mmap2(struct perf_tool *tool, |
| 238 | union perf_event *event, | 270 | union perf_event *event, |
| 239 | struct perf_sample *sample, | 271 | struct perf_sample *sample, |
| @@ -247,6 +279,27 @@ static int perf_event__repipe_mmap2(struct perf_tool *tool, | |||
| 247 | return err; | 279 | return err; |
| 248 | } | 280 | } |
| 249 | 281 | ||
| 282 | #ifdef HAVE_LIBELF_SUPPORT | ||
| 283 | static int perf_event__jit_repipe_mmap2(struct perf_tool *tool, | ||
| 284 | union perf_event *event, | ||
| 285 | struct perf_sample *sample, | ||
| 286 | struct machine *machine) | ||
| 287 | { | ||
| 288 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
| 289 | u64 n = 0; | ||
| 290 | |||
| 291 | /* | ||
| 292 | * if jit marker, then inject jit mmaps and generate ELF images | ||
| 293 | */ | ||
| 294 | if (!jit_process(inject->session, &inject->output, machine, | ||
| 295 | event->mmap2.filename, sample->pid, &n)) { | ||
| 296 | inject->bytes_written += n; | ||
| 297 | return 0; | ||
| 298 | } | ||
| 299 | return perf_event__repipe_mmap2(tool, event, sample, machine); | ||
| 300 | } | ||
| 301 | #endif | ||
| 302 | |||
| 250 | static int perf_event__repipe_fork(struct perf_tool *tool, | 303 | static int perf_event__repipe_fork(struct perf_tool *tool, |
| 251 | union perf_event *event, | 304 | union perf_event *event, |
| 252 | struct perf_sample *sample, | 305 | struct perf_sample *sample, |
| @@ -664,6 +717,23 @@ static int __cmd_inject(struct perf_inject *inject) | |||
| 664 | return ret; | 717 | return ret; |
| 665 | } | 718 | } |
| 666 | 719 | ||
| 720 | #ifdef HAVE_LIBELF_SUPPORT | ||
| 721 | static int | ||
| 722 | jit_validate_events(struct perf_session *session) | ||
| 723 | { | ||
| 724 | struct perf_evsel *evsel; | ||
| 725 | |||
| 726 | /* | ||
| 727 | * check that all events use CLOCK_MONOTONIC | ||
| 728 | */ | ||
| 729 | evlist__for_each(session->evlist, evsel) { | ||
| 730 | if (evsel->attr.use_clockid == 0 || evsel->attr.clockid != CLOCK_MONOTONIC) | ||
| 731 | return -1; | ||
| 732 | } | ||
| 733 | return 0; | ||
| 734 | } | ||
| 735 | #endif | ||
| 736 | |||
| 667 | int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | 737 | int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) |
| 668 | { | 738 | { |
| 669 | struct perf_inject inject = { | 739 | struct perf_inject inject = { |
| @@ -703,7 +773,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 703 | }; | 773 | }; |
| 704 | int ret; | 774 | int ret; |
| 705 | 775 | ||
| 706 | const struct option options[] = { | 776 | struct option options[] = { |
| 707 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, | 777 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, |
| 708 | "Inject build-ids into the output stream"), | 778 | "Inject build-ids into the output stream"), |
| 709 | OPT_STRING('i', "input", &inject.input_name, "file", | 779 | OPT_STRING('i', "input", &inject.input_name, "file", |
| @@ -713,6 +783,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 713 | OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, | 783 | OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, |
| 714 | "Merge sched-stat and sched-switch for getting events " | 784 | "Merge sched-stat and sched-switch for getting events " |
| 715 | "where and how long tasks slept"), | 785 | "where and how long tasks slept"), |
| 786 | OPT_BOOLEAN('j', "jit", &inject.jit_mode, "merge jitdump files into perf.data file"), | ||
| 716 | OPT_INCR('v', "verbose", &verbose, | 787 | OPT_INCR('v', "verbose", &verbose, |
| 717 | "be more verbose (show build ids, etc)"), | 788 | "be more verbose (show build ids, etc)"), |
| 718 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", | 789 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", |
| @@ -729,7 +800,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 729 | "perf inject [<options>]", | 800 | "perf inject [<options>]", |
| 730 | NULL | 801 | NULL |
| 731 | }; | 802 | }; |
| 732 | 803 | #ifndef HAVE_LIBELF_SUPPORT | |
| 804 | set_option_nobuild(options, 'j', "jit", "NO_LIBELF=1", true); | ||
| 805 | #endif | ||
| 733 | argc = parse_options(argc, argv, options, inject_usage, 0); | 806 | argc = parse_options(argc, argv, options, inject_usage, 0); |
| 734 | 807 | ||
| 735 | /* | 808 | /* |
| @@ -765,7 +838,26 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 765 | inject.tool.ordered_events = true; | 838 | inject.tool.ordered_events = true; |
| 766 | inject.tool.ordering_requires_timestamps = true; | 839 | inject.tool.ordering_requires_timestamps = true; |
| 767 | } | 840 | } |
| 768 | 841 | #ifdef HAVE_LIBELF_SUPPORT | |
| 842 | if (inject.jit_mode) { | ||
| 843 | /* | ||
| 844 | * validate event is using the correct clockid | ||
| 845 | */ | ||
| 846 | if (jit_validate_events(inject.session)) { | ||
| 847 | fprintf(stderr, "error, jitted code must be sampled with perf record -k 1\n"); | ||
| 848 | return -1; | ||
| 849 | } | ||
| 850 | inject.tool.mmap2 = perf_event__jit_repipe_mmap2; | ||
| 851 | inject.tool.mmap = perf_event__jit_repipe_mmap; | ||
| 852 | inject.tool.ordered_events = true; | ||
| 853 | inject.tool.ordering_requires_timestamps = true; | ||
| 854 | /* | ||
| 855 | * JIT MMAP injection injects all MMAP events in one go, so it | ||
| 856 | * does not obey finished_round semantics. | ||
| 857 | */ | ||
| 858 | inject.tool.finished_round = perf_event__drop_oe; | ||
| 859 | } | ||
| 860 | #endif | ||
| 769 | ret = symbol__init(&inject.session->header.env); | 861 | ret = symbol__init(&inject.session->header.env); |
| 770 | if (ret < 0) | 862 | if (ret < 0) |
| 771 | goto out_delete; | 863 | goto out_delete; |
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index edae107416b6..52a4a806ee2f 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
| @@ -106,6 +106,8 @@ libperf-y += scripting-engines/ | |||
| 106 | libperf-$(CONFIG_ZLIB) += zlib.o | 106 | libperf-$(CONFIG_ZLIB) += zlib.o |
| 107 | libperf-$(CONFIG_LZMA) += lzma.o | 107 | libperf-$(CONFIG_LZMA) += lzma.o |
| 108 | libperf-y += demangle-java.o | 108 | libperf-y += demangle-java.o |
| 109 | libperf-$(CONFIG_LIBELF) += jitdump.o | ||
| 110 | libperf-$(CONFIG_LIBELF) += genelf.o | ||
| 109 | 111 | ||
| 110 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" | 112 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" |
| 111 | 113 | ||
diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c new file mode 100644 index 000000000000..145f8116ef56 --- /dev/null +++ b/tools/perf/util/genelf.c | |||
| @@ -0,0 +1,442 @@ | |||
| 1 | /* | ||
| 2 | * genelf.c | ||
| 3 | * Copyright (C) 2014, Google, Inc | ||
| 4 | * | ||
| 5 | * Contributed by: | ||
| 6 | * Stephane Eranian <eranian@gmail.com> | ||
| 7 | * | ||
| 8 | * Released under the GPL v2. (and only v2, not any later version) | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <sys/types.h> | ||
| 12 | #include <stdio.h> | ||
| 13 | #include <getopt.h> | ||
| 14 | #include <stddef.h> | ||
| 15 | #include <libelf.h> | ||
| 16 | #include <string.h> | ||
| 17 | #include <stdlib.h> | ||
| 18 | #include <inttypes.h> | ||
| 19 | #include <limits.h> | ||
| 20 | #include <fcntl.h> | ||
| 21 | #include <err.h> | ||
| 22 | #include <dwarf.h> | ||
| 23 | |||
| 24 | #include "perf.h" | ||
| 25 | #include "genelf.h" | ||
| 26 | #include "../util/jitdump.h" | ||
| 27 | |||
| 28 | #define JVMTI | ||
| 29 | |||
| 30 | #define BUILD_ID_URANDOM /* different uuid for each run */ | ||
| 31 | |||
| 32 | #ifdef HAVE_LIBCRYPTO | ||
| 33 | |||
| 34 | #define BUILD_ID_MD5 | ||
| 35 | #undef BUILD_ID_SHA /* does not seem to work well when linked with Java */ | ||
| 36 | #undef BUILD_ID_URANDOM /* different uuid for each run */ | ||
| 37 | |||
| 38 | #ifdef BUILD_ID_SHA | ||
| 39 | #include <openssl/sha.h> | ||
| 40 | #endif | ||
| 41 | |||
| 42 | #ifdef BUILD_ID_MD5 | ||
| 43 | #include <openssl/md5.h> | ||
| 44 | #endif | ||
| 45 | #endif | ||
| 46 | |||
| 47 | |||
| 48 | typedef struct { | ||
| 49 | unsigned int namesz; /* Size of entry's owner string */ | ||
| 50 | unsigned int descsz; /* Size of the note descriptor */ | ||
| 51 | unsigned int type; /* Interpretation of the descriptor */ | ||
| 52 | char name[0]; /* Start of the name+desc data */ | ||
| 53 | } Elf_Note; | ||
| 54 | |||
| 55 | struct options { | ||
| 56 | char *output; | ||
| 57 | int fd; | ||
| 58 | }; | ||
| 59 | |||
| 60 | static char shd_string_table[] = { | ||
| 61 | 0, | ||
| 62 | '.', 't', 'e', 'x', 't', 0, /* 1 */ | ||
| 63 | '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0, /* 7 */ | ||
| 64 | '.', 's', 'y', 'm', 't', 'a', 'b', 0, /* 17 */ | ||
| 65 | '.', 's', 't', 'r', 't', 'a', 'b', 0, /* 25 */ | ||
| 66 | '.', 'n', 'o', 't', 'e', '.', 'g', 'n', 'u', '.', 'b', 'u', 'i', 'l', 'd', '-', 'i', 'd', 0, /* 33 */ | ||
| 67 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'l', 'i', 'n', 'e', 0, /* 52 */ | ||
| 68 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'i', 'n', 'f', 'o', 0, /* 64 */ | ||
| 69 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'a', 'b', 'b', 'r', 'e', 'v', 0, /* 76 */ | ||
| 70 | }; | ||
| 71 | |||
| 72 | static struct buildid_note { | ||
| 73 | Elf_Note desc; /* descsz: size of build-id, must be multiple of 4 */ | ||
| 74 | char name[4]; /* GNU\0 */ | ||
| 75 | char build_id[20]; | ||
| 76 | } bnote; | ||
| 77 | |||
| 78 | static Elf_Sym symtab[]={ | ||
| 79 | /* symbol 0 MUST be the undefined symbol */ | ||
| 80 | { .st_name = 0, /* index in sym_string table */ | ||
| 81 | .st_info = ELF_ST_TYPE(STT_NOTYPE), | ||
| 82 | .st_shndx = 0, /* for now */ | ||
| 83 | .st_value = 0x0, | ||
| 84 | .st_other = ELF_ST_VIS(STV_DEFAULT), | ||
| 85 | .st_size = 0, | ||
| 86 | }, | ||
| 87 | { .st_name = 1, /* index in sym_string table */ | ||
| 88 | .st_info = ELF_ST_BIND(STB_LOCAL) | ELF_ST_TYPE(STT_FUNC), | ||
| 89 | .st_shndx = 1, | ||
| 90 | .st_value = 0, /* for now */ | ||
| 91 | .st_other = ELF_ST_VIS(STV_DEFAULT), | ||
| 92 | .st_size = 0, /* for now */ | ||
| 93 | } | ||
| 94 | }; | ||
| 95 | |||
| 96 | #ifdef BUILD_ID_URANDOM | ||
| 97 | static void | ||
| 98 | gen_build_id(struct buildid_note *note, | ||
| 99 | unsigned long load_addr __maybe_unused, | ||
| 100 | const void *code __maybe_unused, | ||
| 101 | size_t csize __maybe_unused) | ||
| 102 | { | ||
| 103 | int fd; | ||
| 104 | size_t sz = sizeof(note->build_id); | ||
| 105 | ssize_t sret; | ||
| 106 | |||
| 107 | fd = open("/dev/urandom", O_RDONLY); | ||
| 108 | if (fd == -1) | ||
| 109 | err(1, "cannot access /dev/urandom for builid"); | ||
| 110 | |||
| 111 | sret = read(fd, note->build_id, sz); | ||
| 112 | |||
| 113 | close(fd); | ||
| 114 | |||
| 115 | if (sret != (ssize_t)sz) | ||
| 116 | memset(note->build_id, 0, sz); | ||
| 117 | } | ||
| 118 | #endif | ||
| 119 | |||
| 120 | #ifdef BUILD_ID_SHA | ||
| 121 | static void | ||
| 122 | gen_build_id(struct buildid_note *note, | ||
| 123 | unsigned long load_addr __maybe_unused, | ||
| 124 | const void *code, | ||
| 125 | size_t csize) | ||
| 126 | { | ||
| 127 | if (sizeof(note->build_id) < SHA_DIGEST_LENGTH) | ||
| 128 | errx(1, "build_id too small for SHA1"); | ||
| 129 | |||
| 130 | SHA1(code, csize, (unsigned char *)note->build_id); | ||
| 131 | } | ||
| 132 | #endif | ||
| 133 | |||
| 134 | #ifdef BUILD_ID_MD5 | ||
| 135 | static void | ||
| 136 | gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *code, size_t csize) | ||
| 137 | { | ||
| 138 | MD5_CTX context; | ||
| 139 | |||
| 140 | if (sizeof(note->build_id) < 16) | ||
| 141 | errx(1, "build_id too small for MD5"); | ||
| 142 | |||
| 143 | MD5_Init(&context); | ||
| 144 | MD5_Update(&context, &load_addr, sizeof(load_addr)); | ||
| 145 | MD5_Update(&context, code, csize); | ||
| 146 | MD5_Final((unsigned char *)note->build_id, &context); | ||
| 147 | } | ||
| 148 | #endif | ||
| 149 | |||
| 150 | /* | ||
| 151 | * fd: file descriptor open for writing for the output file | ||
| 152 | * load_addr: code load address (could be zero, just used for buildid) | ||
| 153 | * sym: function name (for native code - used as the symbol) | ||
| 154 | * code: the native code | ||
| 155 | * csize: the code size in bytes | ||
| 156 | */ | ||
| 157 | int | ||
| 158 | jit_write_elf(int fd, uint64_t load_addr, const char *sym, | ||
| 159 | const void *code, int csize) | ||
| 160 | { | ||
| 161 | Elf *e; | ||
| 162 | Elf_Data *d; | ||
| 163 | Elf_Scn *scn; | ||
| 164 | Elf_Ehdr *ehdr; | ||
| 165 | Elf_Shdr *shdr; | ||
| 166 | char *strsym = NULL; | ||
| 167 | int symlen; | ||
| 168 | int retval = -1; | ||
| 169 | |||
| 170 | if (elf_version(EV_CURRENT) == EV_NONE) { | ||
| 171 | warnx("ELF initialization failed"); | ||
| 172 | return -1; | ||
| 173 | } | ||
| 174 | |||
| 175 | e = elf_begin(fd, ELF_C_WRITE, NULL); | ||
| 176 | if (!e) { | ||
| 177 | warnx("elf_begin failed"); | ||
| 178 | goto error; | ||
| 179 | } | ||
| 180 | |||
| 181 | /* | ||
| 182 | * setup ELF header | ||
| 183 | */ | ||
| 184 | ehdr = elf_newehdr(e); | ||
| 185 | if (!ehdr) { | ||
| 186 | warnx("cannot get ehdr"); | ||
| 187 | goto error; | ||
| 188 | } | ||
| 189 | |||
| 190 | ehdr->e_ident[EI_DATA] = GEN_ELF_ENDIAN; | ||
| 191 | ehdr->e_ident[EI_CLASS] = GEN_ELF_CLASS; | ||
| 192 | ehdr->e_machine = GEN_ELF_ARCH; | ||
| 193 | ehdr->e_type = ET_DYN; | ||
| 194 | ehdr->e_entry = GEN_ELF_TEXT_OFFSET; | ||
| 195 | ehdr->e_version = EV_CURRENT; | ||
| 196 | ehdr->e_shstrndx= 2; /* shdr index for section name */ | ||
| 197 | |||
| 198 | /* | ||
| 199 | * setup text section | ||
| 200 | */ | ||
| 201 | scn = elf_newscn(e); | ||
| 202 | if (!scn) { | ||
| 203 | warnx("cannot create section"); | ||
| 204 | goto error; | ||
| 205 | } | ||
| 206 | |||
| 207 | d = elf_newdata(scn); | ||
| 208 | if (!d) { | ||
| 209 | warnx("cannot get new data"); | ||
| 210 | goto error; | ||
| 211 | } | ||
| 212 | |||
| 213 | d->d_align = 16; | ||
| 214 | d->d_off = 0LL; | ||
| 215 | d->d_buf = (void *)code; | ||
| 216 | d->d_type = ELF_T_BYTE; | ||
| 217 | d->d_size = csize; | ||
| 218 | d->d_version = EV_CURRENT; | ||
| 219 | |||
| 220 | shdr = elf_getshdr(scn); | ||
| 221 | if (!shdr) { | ||
| 222 | warnx("cannot get section header"); | ||
| 223 | goto error; | ||
| 224 | } | ||
| 225 | |||
| 226 | shdr->sh_name = 1; | ||
| 227 | shdr->sh_type = SHT_PROGBITS; | ||
| 228 | shdr->sh_addr = GEN_ELF_TEXT_OFFSET; | ||
| 229 | shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC; | ||
| 230 | shdr->sh_entsize = 0; | ||
| 231 | |||
| 232 | /* | ||
| 233 | * setup section headers string table | ||
| 234 | */ | ||
| 235 | scn = elf_newscn(e); | ||
| 236 | if (!scn) { | ||
| 237 | warnx("cannot create section"); | ||
| 238 | goto error; | ||
| 239 | } | ||
| 240 | |||
| 241 | d = elf_newdata(scn); | ||
| 242 | if (!d) { | ||
| 243 | warnx("cannot get new data"); | ||
| 244 | goto error; | ||
| 245 | } | ||
| 246 | |||
| 247 | d->d_align = 1; | ||
| 248 | d->d_off = 0LL; | ||
| 249 | d->d_buf = shd_string_table; | ||
| 250 | d->d_type = ELF_T_BYTE; | ||
| 251 | d->d_size = sizeof(shd_string_table); | ||
| 252 | d->d_version = EV_CURRENT; | ||
| 253 | |||
| 254 | shdr = elf_getshdr(scn); | ||
| 255 | if (!shdr) { | ||
| 256 | warnx("cannot get section header"); | ||
| 257 | goto error; | ||
| 258 | } | ||
| 259 | |||
| 260 | shdr->sh_name = 7; /* offset of '.shstrtab' in shd_string_table */ | ||
| 261 | shdr->sh_type = SHT_STRTAB; | ||
| 262 | shdr->sh_flags = 0; | ||
| 263 | shdr->sh_entsize = 0; | ||
| 264 | |||
| 265 | /* | ||
| 266 | * setup symtab section | ||
| 267 | */ | ||
| 268 | symtab[1].st_size = csize; | ||
| 269 | symtab[1].st_value = GEN_ELF_TEXT_OFFSET; | ||
| 270 | |||
| 271 | scn = elf_newscn(e); | ||
| 272 | if (!scn) { | ||
| 273 | warnx("cannot create section"); | ||
| 274 | goto error; | ||
| 275 | } | ||
| 276 | |||
| 277 | d = elf_newdata(scn); | ||
| 278 | if (!d) { | ||
| 279 | warnx("cannot get new data"); | ||
| 280 | goto error; | ||
| 281 | } | ||
| 282 | |||
| 283 | d->d_align = 8; | ||
| 284 | d->d_off = 0LL; | ||
| 285 | d->d_buf = symtab; | ||
| 286 | d->d_type = ELF_T_SYM; | ||
| 287 | d->d_size = sizeof(symtab); | ||
| 288 | d->d_version = EV_CURRENT; | ||
| 289 | |||
| 290 | shdr = elf_getshdr(scn); | ||
| 291 | if (!shdr) { | ||
| 292 | warnx("cannot get section header"); | ||
| 293 | goto error; | ||
| 294 | } | ||
| 295 | |||
| 296 | shdr->sh_name = 17; /* offset of '.symtab' in shd_string_table */ | ||
| 297 | shdr->sh_type = SHT_SYMTAB; | ||
| 298 | shdr->sh_flags = 0; | ||
| 299 | shdr->sh_entsize = sizeof(Elf_Sym); | ||
| 300 | shdr->sh_link = 4; /* index of .strtab section */ | ||
| 301 | |||
| 302 | /* | ||
| 303 | * setup symbols string table | ||
| 304 | * 2 = 1 for 0 in 1st entry, 1 for the 0 at end of symbol for 2nd entry | ||
| 305 | */ | ||
| 306 | symlen = 2 + strlen(sym); | ||
| 307 | strsym = calloc(1, symlen); | ||
| 308 | if (!strsym) { | ||
| 309 | warnx("cannot allocate strsym"); | ||
| 310 | goto error; | ||
| 311 | } | ||
| 312 | strcpy(strsym + 1, sym); | ||
| 313 | |||
| 314 | scn = elf_newscn(e); | ||
| 315 | if (!scn) { | ||
| 316 | warnx("cannot create section"); | ||
| 317 | goto error; | ||
| 318 | } | ||
| 319 | |||
| 320 | d = elf_newdata(scn); | ||
| 321 | if (!d) { | ||
| 322 | warnx("cannot get new data"); | ||
| 323 | goto error; | ||
| 324 | } | ||
| 325 | |||
| 326 | d->d_align = 1; | ||
| 327 | d->d_off = 0LL; | ||
| 328 | d->d_buf = strsym; | ||
| 329 | d->d_type = ELF_T_BYTE; | ||
| 330 | d->d_size = symlen; | ||
| 331 | d->d_version = EV_CURRENT; | ||
| 332 | |||
| 333 | shdr = elf_getshdr(scn); | ||
| 334 | if (!shdr) { | ||
| 335 | warnx("cannot get section header"); | ||
| 336 | goto error; | ||
| 337 | } | ||
| 338 | |||
| 339 | shdr->sh_name = 25; /* offset in shd_string_table */ | ||
| 340 | shdr->sh_type = SHT_STRTAB; | ||
| 341 | shdr->sh_flags = 0; | ||
| 342 | shdr->sh_entsize = 0; | ||
| 343 | |||
| 344 | /* | ||
| 345 | * setup build-id section | ||
| 346 | */ | ||
| 347 | scn = elf_newscn(e); | ||
| 348 | if (!scn) { | ||
| 349 | warnx("cannot create section"); | ||
| 350 | goto error; | ||
| 351 | } | ||
| 352 | |||
| 353 | d = elf_newdata(scn); | ||
| 354 | if (!d) { | ||
| 355 | warnx("cannot get new data"); | ||
| 356 | goto error; | ||
| 357 | } | ||
| 358 | |||
| 359 | /* | ||
| 360 | * build-id generation | ||
| 361 | */ | ||
| 362 | gen_build_id(&bnote, load_addr, code, csize); | ||
| 363 | bnote.desc.namesz = sizeof(bnote.name); /* must include 0 termination */ | ||
| 364 | bnote.desc.descsz = sizeof(bnote.build_id); | ||
| 365 | bnote.desc.type = NT_GNU_BUILD_ID; | ||
| 366 | strcpy(bnote.name, "GNU"); | ||
| 367 | |||
| 368 | d->d_align = 4; | ||
| 369 | d->d_off = 0LL; | ||
| 370 | d->d_buf = &bnote; | ||
| 371 | d->d_type = ELF_T_BYTE; | ||
| 372 | d->d_size = sizeof(bnote); | ||
| 373 | d->d_version = EV_CURRENT; | ||
| 374 | |||
| 375 | shdr = elf_getshdr(scn); | ||
| 376 | if (!shdr) { | ||
| 377 | warnx("cannot get section header"); | ||
| 378 | goto error; | ||
| 379 | } | ||
| 380 | |||
| 381 | shdr->sh_name = 33; /* offset in shd_string_table */ | ||
| 382 | shdr->sh_type = SHT_NOTE; | ||
| 383 | shdr->sh_addr = 0x0; | ||
| 384 | shdr->sh_flags = SHF_ALLOC; | ||
| 385 | shdr->sh_size = sizeof(bnote); | ||
| 386 | shdr->sh_entsize = 0; | ||
| 387 | |||
| 388 | if (elf_update(e, ELF_C_WRITE) < 0) { | ||
| 389 | warnx("elf_update 4 failed"); | ||
| 390 | goto error; | ||
| 391 | } | ||
| 392 | |||
| 393 | retval = 0; | ||
| 394 | error: | ||
| 395 | (void)elf_end(e); | ||
| 396 | |||
| 397 | free(strsym); | ||
| 398 | |||
| 399 | |||
| 400 | return retval; | ||
| 401 | } | ||
| 402 | |||
| 403 | #ifndef JVMTI | ||
| 404 | |||
| 405 | static unsigned char x86_code[] = { | ||
| 406 | 0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */ | ||
| 407 | 0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */ | ||
| 408 | 0xCD, 0x80 /* int $0x80 */ | ||
| 409 | }; | ||
| 410 | |||
| 411 | static struct options options; | ||
| 412 | |||
| 413 | int main(int argc, char **argv) | ||
| 414 | { | ||
| 415 | int c, fd, ret; | ||
| 416 | |||
| 417 | while ((c = getopt(argc, argv, "o:h")) != -1) { | ||
| 418 | switch (c) { | ||
| 419 | case 'o': | ||
| 420 | options.output = optarg; | ||
| 421 | break; | ||
| 422 | case 'h': | ||
| 423 | printf("Usage: genelf -o output_file [-h]\n"); | ||
| 424 | return 0; | ||
| 425 | default: | ||
| 426 | errx(1, "unknown option"); | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | fd = open(options.output, O_CREAT|O_TRUNC|O_RDWR, 0666); | ||
| 431 | if (fd == -1) | ||
| 432 | err(1, "cannot create file %s", options.output); | ||
| 433 | |||
| 434 | ret = jit_write_elf(fd, "main", x86_code, sizeof(x86_code)); | ||
| 435 | close(fd); | ||
| 436 | |||
| 437 | if (ret != 0) | ||
| 438 | unlink(options.output); | ||
| 439 | |||
| 440 | return ret; | ||
| 441 | } | ||
| 442 | #endif | ||
diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h new file mode 100644 index 000000000000..d8e9ece13c8b --- /dev/null +++ b/tools/perf/util/genelf.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | #ifndef __GENELF_H__ | ||
| 2 | #define __GENELF_H__ | ||
| 3 | |||
| 4 | /* genelf.c */ | ||
| 5 | extern int jit_write_elf(int fd, uint64_t code_addr, const char *sym, | ||
| 6 | const void *code, int csize); | ||
| 7 | |||
| 8 | #if defined(__arm__) | ||
| 9 | #define GEN_ELF_ARCH EM_ARM | ||
| 10 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
| 11 | #define GEN_ELF_CLASS ELFCLASS32 | ||
| 12 | #elif defined(__aarch64__) | ||
| 13 | #define GEN_ELF_ARCH EM_AARCH64 | ||
| 14 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
| 15 | #define GEN_ELF_CLASS ELFCLASS64 | ||
| 16 | #elif defined(__x86_64__) | ||
| 17 | #define GEN_ELF_ARCH EM_X86_64 | ||
| 18 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
| 19 | #define GEN_ELF_CLASS ELFCLASS64 | ||
| 20 | #elif defined(__i386__) | ||
| 21 | #define GEN_ELF_ARCH EM_386 | ||
| 22 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
| 23 | #define GEN_ELF_CLASS ELFCLASS32 | ||
| 24 | #elif defined(__ppcle__) | ||
| 25 | #define GEN_ELF_ARCH EM_PPC | ||
| 26 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
| 27 | #define GEN_ELF_CLASS ELFCLASS64 | ||
| 28 | #elif defined(__powerpc__) | ||
| 29 | #define GEN_ELF_ARCH EM_PPC64 | ||
| 30 | #define GEN_ELF_ENDIAN ELFDATA2MSB | ||
| 31 | #define GEN_ELF_CLASS ELFCLASS64 | ||
| 32 | #elif defined(__powerpcle__) | ||
| 33 | #define GEN_ELF_ARCH EM_PPC64 | ||
| 34 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
| 35 | #define GEN_ELF_CLASS ELFCLASS64 | ||
| 36 | #else | ||
| 37 | #error "unsupported architecture" | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #if GEN_ELF_CLASS == ELFCLASS64 | ||
| 41 | #define elf_newehdr elf64_newehdr | ||
| 42 | #define elf_getshdr elf64_getshdr | ||
| 43 | #define Elf_Ehdr Elf64_Ehdr | ||
| 44 | #define Elf_Shdr Elf64_Shdr | ||
| 45 | #define Elf_Sym Elf64_Sym | ||
| 46 | #define ELF_ST_TYPE(a) ELF64_ST_TYPE(a) | ||
| 47 | #define ELF_ST_BIND(a) ELF64_ST_BIND(a) | ||
| 48 | #define ELF_ST_VIS(a) ELF64_ST_VISIBILITY(a) | ||
| 49 | #else | ||
| 50 | #define elf_newehdr elf32_newehdr | ||
| 51 | #define elf_getshdr elf32_getshdr | ||
| 52 | #define Elf_Ehdr Elf32_Ehdr | ||
| 53 | #define Elf_Shdr Elf32_Shdr | ||
| 54 | #define Elf_Sym Elf32_Sym | ||
| 55 | #define ELF_ST_TYPE(a) ELF32_ST_TYPE(a) | ||
| 56 | #define ELF_ST_BIND(a) ELF32_ST_BIND(a) | ||
| 57 | #define ELF_ST_VIS(a) ELF32_ST_VISIBILITY(a) | ||
| 58 | #endif | ||
| 59 | |||
| 60 | /* The .text section is directly after the ELF header */ | ||
| 61 | #define GEN_ELF_TEXT_OFFSET sizeof(Elf_Ehdr) | ||
| 62 | |||
| 63 | #endif | ||
diff --git a/tools/perf/util/jit.h b/tools/perf/util/jit.h new file mode 100644 index 000000000000..a1e99da0715a --- /dev/null +++ b/tools/perf/util/jit.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #ifndef __JIT_H__ | ||
| 2 | #define __JIT_H__ | ||
| 3 | |||
| 4 | #include <data.h> | ||
| 5 | |||
| 6 | extern int jit_process(struct perf_session *session, | ||
| 7 | struct perf_data_file *output, | ||
| 8 | struct machine *machine, | ||
| 9 | char *filename, | ||
| 10 | pid_t pid, | ||
| 11 | u64 *nbytes); | ||
| 12 | |||
| 13 | extern int jit_inject_record(const char *filename); | ||
| 14 | |||
| 15 | #endif /* __JIT_H__ */ | ||
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c new file mode 100644 index 000000000000..9f7a01289efe --- /dev/null +++ b/tools/perf/util/jitdump.c | |||
| @@ -0,0 +1,670 @@ | |||
| 1 | #include <sys/types.h> | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <stdlib.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include <fcntl.h> | ||
| 6 | #include <unistd.h> | ||
| 7 | #include <inttypes.h> | ||
| 8 | #include <byteswap.h> | ||
| 9 | #include <sys/stat.h> | ||
| 10 | #include <sys/mman.h> | ||
| 11 | |||
| 12 | #include "util.h" | ||
| 13 | #include "event.h" | ||
| 14 | #include "debug.h" | ||
| 15 | #include "evlist.h" | ||
| 16 | #include "symbol.h" | ||
| 17 | #include "strlist.h" | ||
| 18 | #include <elf.h> | ||
| 19 | |||
| 20 | #include "session.h" | ||
| 21 | #include "jit.h" | ||
| 22 | #include "jitdump.h" | ||
| 23 | #include "genelf.h" | ||
| 24 | #include "../builtin.h" | ||
| 25 | |||
| 26 | struct jit_buf_desc { | ||
| 27 | struct perf_data_file *output; | ||
| 28 | struct perf_session *session; | ||
| 29 | struct machine *machine; | ||
| 30 | union jr_entry *entry; | ||
| 31 | void *buf; | ||
| 32 | uint64_t sample_type; | ||
| 33 | size_t bufsize; | ||
| 34 | FILE *in; | ||
| 35 | bool needs_bswap; /* handles cross-endianess */ | ||
| 36 | void *debug_data; | ||
| 37 | size_t nr_debug_entries; | ||
| 38 | uint32_t code_load_count; | ||
| 39 | u64 bytes_written; | ||
| 40 | struct rb_root code_root; | ||
| 41 | char dir[PATH_MAX]; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct debug_line_info { | ||
| 45 | unsigned long vma; | ||
| 46 | unsigned int lineno; | ||
| 47 | /* The filename format is unspecified, absolute path, relative etc. */ | ||
| 48 | char const filename[0]; | ||
| 49 | }; | ||
| 50 | |||
| 51 | struct jit_tool { | ||
| 52 | struct perf_tool tool; | ||
| 53 | struct perf_data_file output; | ||
| 54 | struct perf_data_file input; | ||
| 55 | u64 bytes_written; | ||
| 56 | }; | ||
| 57 | |||
| 58 | #define hmax(a, b) ((a) > (b) ? (a) : (b)) | ||
| 59 | #define get_jit_tool(t) (container_of(tool, struct jit_tool, tool)) | ||
| 60 | |||
| 61 | static int | ||
| 62 | jit_emit_elf(char *filename, | ||
| 63 | const char *sym, | ||
| 64 | uint64_t code_addr, | ||
| 65 | const void *code, | ||
| 66 | int csize) | ||
| 67 | { | ||
| 68 | int ret, fd; | ||
| 69 | |||
| 70 | if (verbose > 0) | ||
| 71 | fprintf(stderr, "write ELF image %s\n", filename); | ||
| 72 | |||
| 73 | fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0644); | ||
| 74 | if (fd == -1) { | ||
| 75 | pr_warning("cannot create jit ELF %s: %s\n", filename, strerror(errno)); | ||
| 76 | return -1; | ||
| 77 | } | ||
| 78 | |||
| 79 | ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize); | ||
| 80 | |||
| 81 | close(fd); | ||
| 82 | |||
| 83 | if (ret) | ||
| 84 | unlink(filename); | ||
| 85 | |||
| 86 | return ret; | ||
| 87 | } | ||
| 88 | |||
| 89 | static void | ||
| 90 | jit_close(struct jit_buf_desc *jd) | ||
| 91 | { | ||
| 92 | if (!(jd && jd->in)) | ||
| 93 | return; | ||
| 94 | funlockfile(jd->in); | ||
| 95 | fclose(jd->in); | ||
| 96 | jd->in = NULL; | ||
| 97 | } | ||
| 98 | |||
| 99 | static int | ||
| 100 | jit_open(struct jit_buf_desc *jd, const char *name) | ||
| 101 | { | ||
| 102 | struct jitheader header; | ||
| 103 | struct jr_prefix *prefix; | ||
| 104 | ssize_t bs, bsz = 0; | ||
| 105 | void *n, *buf = NULL; | ||
| 106 | int ret, retval = -1; | ||
| 107 | |||
| 108 | jd->in = fopen(name, "r"); | ||
| 109 | if (!jd->in) | ||
| 110 | return -1; | ||
| 111 | |||
| 112 | bsz = hmax(sizeof(header), sizeof(*prefix)); | ||
| 113 | |||
| 114 | buf = malloc(bsz); | ||
| 115 | if (!buf) | ||
| 116 | goto error; | ||
| 117 | |||
| 118 | /* | ||
| 119 | * protect from writer modifying the file while we are reading it | ||
| 120 | */ | ||
| 121 | flockfile(jd->in); | ||
| 122 | |||
| 123 | ret = fread(buf, sizeof(header), 1, jd->in); | ||
| 124 | if (ret != 1) | ||
| 125 | goto error; | ||
| 126 | |||
| 127 | memcpy(&header, buf, sizeof(header)); | ||
| 128 | |||
| 129 | if (header.magic != JITHEADER_MAGIC) { | ||
| 130 | if (header.magic != JITHEADER_MAGIC_SW) | ||
| 131 | goto error; | ||
| 132 | jd->needs_bswap = true; | ||
| 133 | } | ||
| 134 | |||
| 135 | if (jd->needs_bswap) { | ||
| 136 | header.version = bswap_32(header.version); | ||
| 137 | header.total_size = bswap_32(header.total_size); | ||
| 138 | header.pid = bswap_32(header.pid); | ||
| 139 | header.elf_mach = bswap_32(header.elf_mach); | ||
| 140 | header.timestamp = bswap_64(header.timestamp); | ||
| 141 | header.flags = bswap_64(header.flags); | ||
| 142 | } | ||
| 143 | |||
| 144 | if (verbose > 2) | ||
| 145 | pr_debug("version=%u\nhdr.size=%u\nts=0x%llx\npid=%d\nelf_mach=%d\n", | ||
| 146 | header.version, | ||
| 147 | header.total_size, | ||
| 148 | (unsigned long long)header.timestamp, | ||
| 149 | header.pid, | ||
| 150 | header.elf_mach); | ||
| 151 | |||
| 152 | if (header.flags & JITDUMP_FLAGS_RESERVED) { | ||
| 153 | pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", | ||
| 154 | (unsigned long long)header.flags & JITDUMP_FLAGS_RESERVED); | ||
| 155 | goto error; | ||
| 156 | } | ||
| 157 | |||
| 158 | bs = header.total_size - sizeof(header); | ||
| 159 | |||
| 160 | if (bs > bsz) { | ||
| 161 | n = realloc(buf, bs); | ||
| 162 | if (!n) | ||
| 163 | goto error; | ||
| 164 | bsz = bs; | ||
| 165 | buf = n; | ||
| 166 | /* read extra we do not know about */ | ||
| 167 | ret = fread(buf, bs - bsz, 1, jd->in); | ||
| 168 | if (ret != 1) | ||
| 169 | goto error; | ||
| 170 | } | ||
| 171 | /* | ||
| 172 | * keep dirname for generating files and mmap records | ||
| 173 | */ | ||
| 174 | strcpy(jd->dir, name); | ||
| 175 | dirname(jd->dir); | ||
| 176 | |||
| 177 | return 0; | ||
| 178 | error: | ||
| 179 | funlockfile(jd->in); | ||
| 180 | fclose(jd->in); | ||
| 181 | return retval; | ||
| 182 | } | ||
| 183 | |||
| 184 | static union jr_entry * | ||
| 185 | jit_get_next_entry(struct jit_buf_desc *jd) | ||
| 186 | { | ||
| 187 | struct jr_prefix *prefix; | ||
| 188 | union jr_entry *jr; | ||
| 189 | void *addr; | ||
| 190 | size_t bs, size; | ||
| 191 | int id, ret; | ||
| 192 | |||
| 193 | if (!(jd && jd->in)) | ||
| 194 | return NULL; | ||
| 195 | |||
| 196 | if (jd->buf == NULL) { | ||
| 197 | size_t sz = getpagesize(); | ||
| 198 | if (sz < sizeof(*prefix)) | ||
| 199 | sz = sizeof(*prefix); | ||
| 200 | |||
| 201 | jd->buf = malloc(sz); | ||
| 202 | if (jd->buf == NULL) | ||
| 203 | return NULL; | ||
| 204 | |||
| 205 | jd->bufsize = sz; | ||
| 206 | } | ||
| 207 | |||
| 208 | prefix = jd->buf; | ||
| 209 | |||
| 210 | /* | ||
| 211 | * file is still locked at this point | ||
| 212 | */ | ||
| 213 | ret = fread(prefix, sizeof(*prefix), 1, jd->in); | ||
| 214 | if (ret != 1) | ||
| 215 | return NULL; | ||
| 216 | |||
| 217 | if (jd->needs_bswap) { | ||
| 218 | prefix->id = bswap_32(prefix->id); | ||
| 219 | prefix->total_size = bswap_32(prefix->total_size); | ||
| 220 | prefix->timestamp = bswap_64(prefix->timestamp); | ||
| 221 | } | ||
| 222 | id = prefix->id; | ||
| 223 | size = prefix->total_size; | ||
| 224 | |||
| 225 | bs = (size_t)size; | ||
| 226 | if (bs < sizeof(*prefix)) | ||
| 227 | return NULL; | ||
| 228 | |||
| 229 | if (id >= JIT_CODE_MAX) { | ||
| 230 | pr_warning("next_entry: unknown prefix %d, skipping\n", id); | ||
| 231 | return NULL; | ||
| 232 | } | ||
| 233 | if (bs > jd->bufsize) { | ||
| 234 | void *n; | ||
| 235 | n = realloc(jd->buf, bs); | ||
| 236 | if (!n) | ||
| 237 | return NULL; | ||
| 238 | jd->buf = n; | ||
| 239 | jd->bufsize = bs; | ||
| 240 | } | ||
| 241 | |||
| 242 | addr = ((void *)jd->buf) + sizeof(*prefix); | ||
| 243 | |||
| 244 | ret = fread(addr, bs - sizeof(*prefix), 1, jd->in); | ||
| 245 | if (ret != 1) | ||
| 246 | return NULL; | ||
| 247 | |||
| 248 | jr = (union jr_entry *)jd->buf; | ||
| 249 | |||
| 250 | switch(id) { | ||
| 251 | case JIT_CODE_DEBUG_INFO: | ||
| 252 | if (jd->needs_bswap) { | ||
| 253 | uint64_t n; | ||
| 254 | jr->info.code_addr = bswap_64(jr->info.code_addr); | ||
| 255 | jr->info.nr_entry = bswap_64(jr->info.nr_entry); | ||
| 256 | for (n = 0 ; n < jr->info.nr_entry; n++) { | ||
| 257 | jr->info.entries[n].addr = bswap_64(jr->info.entries[n].addr); | ||
| 258 | jr->info.entries[n].lineno = bswap_32(jr->info.entries[n].lineno); | ||
| 259 | jr->info.entries[n].discrim = bswap_32(jr->info.entries[n].discrim); | ||
| 260 | } | ||
| 261 | } | ||
| 262 | break; | ||
| 263 | case JIT_CODE_CLOSE: | ||
| 264 | break; | ||
| 265 | case JIT_CODE_LOAD: | ||
| 266 | if (jd->needs_bswap) { | ||
| 267 | jr->load.pid = bswap_32(jr->load.pid); | ||
| 268 | jr->load.tid = bswap_32(jr->load.tid); | ||
| 269 | jr->load.vma = bswap_64(jr->load.vma); | ||
| 270 | jr->load.code_addr = bswap_64(jr->load.code_addr); | ||
| 271 | jr->load.code_size = bswap_64(jr->load.code_size); | ||
| 272 | jr->load.code_index= bswap_64(jr->load.code_index); | ||
| 273 | } | ||
| 274 | jd->code_load_count++; | ||
| 275 | break; | ||
| 276 | case JIT_CODE_MOVE: | ||
| 277 | if (jd->needs_bswap) { | ||
| 278 | jr->move.pid = bswap_32(jr->move.pid); | ||
| 279 | jr->move.tid = bswap_32(jr->move.tid); | ||
| 280 | jr->move.vma = bswap_64(jr->move.vma); | ||
| 281 | jr->move.old_code_addr = bswap_64(jr->move.old_code_addr); | ||
| 282 | jr->move.new_code_addr = bswap_64(jr->move.new_code_addr); | ||
| 283 | jr->move.code_size = bswap_64(jr->move.code_size); | ||
| 284 | jr->move.code_index = bswap_64(jr->move.code_index); | ||
| 285 | } | ||
| 286 | break; | ||
| 287 | case JIT_CODE_MAX: | ||
| 288 | default: | ||
| 289 | return NULL; | ||
| 290 | } | ||
| 291 | return jr; | ||
| 292 | } | ||
| 293 | |||
| 294 | static int | ||
| 295 | jit_inject_event(struct jit_buf_desc *jd, union perf_event *event) | ||
| 296 | { | ||
| 297 | ssize_t size; | ||
| 298 | |||
| 299 | size = perf_data_file__write(jd->output, event, event->header.size); | ||
| 300 | if (size < 0) | ||
| 301 | return -1; | ||
| 302 | |||
| 303 | jd->bytes_written += size; | ||
| 304 | return 0; | ||
| 305 | } | ||
| 306 | |||
| 307 | static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | ||
| 308 | { | ||
| 309 | struct perf_sample sample; | ||
| 310 | union perf_event *event; | ||
| 311 | struct perf_tool *tool = jd->session->tool; | ||
| 312 | uint64_t code, addr; | ||
| 313 | uintptr_t uaddr; | ||
| 314 | char *filename; | ||
| 315 | struct stat st; | ||
| 316 | size_t size; | ||
| 317 | u16 idr_size; | ||
| 318 | const char *sym; | ||
| 319 | uint32_t count; | ||
| 320 | int ret, csize; | ||
| 321 | pid_t pid, tid; | ||
| 322 | struct { | ||
| 323 | u32 pid, tid; | ||
| 324 | u64 time; | ||
| 325 | } *id; | ||
| 326 | |||
| 327 | pid = jr->load.pid; | ||
| 328 | tid = jr->load.tid; | ||
| 329 | csize = jr->load.code_size; | ||
| 330 | addr = jr->load.code_addr; | ||
| 331 | sym = (void *)((unsigned long)jr + sizeof(jr->load)); | ||
| 332 | code = (unsigned long)jr + jr->load.p.total_size - csize; | ||
| 333 | count = jr->load.code_index; | ||
| 334 | idr_size = jd->machine->id_hdr_size; | ||
| 335 | |||
| 336 | event = calloc(1, sizeof(*event) + idr_size); | ||
| 337 | if (!event) | ||
| 338 | return -1; | ||
| 339 | |||
| 340 | filename = event->mmap2.filename; | ||
| 341 | size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%u.so", | ||
| 342 | jd->dir, | ||
| 343 | pid, | ||
| 344 | count); | ||
| 345 | |||
| 346 | size++; /* for \0 */ | ||
| 347 | |||
| 348 | size = PERF_ALIGN(size, sizeof(u64)); | ||
| 349 | uaddr = (uintptr_t)code; | ||
| 350 | ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize); | ||
| 351 | |||
| 352 | if (jd->debug_data && jd->nr_debug_entries) { | ||
| 353 | free(jd->debug_data); | ||
| 354 | jd->debug_data = NULL; | ||
| 355 | jd->nr_debug_entries = 0; | ||
| 356 | } | ||
| 357 | |||
| 358 | if (ret) { | ||
| 359 | free(event); | ||
| 360 | return -1; | ||
| 361 | } | ||
| 362 | if (stat(filename, &st)) | ||
| 363 | memset(&st, 0, sizeof(stat)); | ||
| 364 | |||
| 365 | event->mmap2.header.type = PERF_RECORD_MMAP2; | ||
| 366 | event->mmap2.header.misc = PERF_RECORD_MISC_USER; | ||
| 367 | event->mmap2.header.size = (sizeof(event->mmap2) - | ||
| 368 | (sizeof(event->mmap2.filename) - size) + idr_size); | ||
| 369 | |||
| 370 | event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; | ||
| 371 | event->mmap2.start = addr; | ||
| 372 | event->mmap2.len = csize; | ||
| 373 | event->mmap2.pid = pid; | ||
| 374 | event->mmap2.tid = tid; | ||
| 375 | event->mmap2.ino = st.st_ino; | ||
| 376 | event->mmap2.maj = major(st.st_dev); | ||
| 377 | event->mmap2.min = minor(st.st_dev); | ||
| 378 | event->mmap2.prot = st.st_mode; | ||
| 379 | event->mmap2.flags = MAP_SHARED; | ||
| 380 | event->mmap2.ino_generation = 1; | ||
| 381 | |||
| 382 | id = (void *)((unsigned long)event + event->mmap.header.size - idr_size); | ||
| 383 | if (jd->sample_type & PERF_SAMPLE_TID) { | ||
| 384 | id->pid = pid; | ||
| 385 | id->tid = tid; | ||
| 386 | } | ||
| 387 | if (jd->sample_type & PERF_SAMPLE_TIME) | ||
| 388 | id->time = jr->load.p.timestamp; | ||
| 389 | |||
| 390 | /* | ||
| 391 | * create pseudo sample to induce dso hit increment | ||
| 392 | * use first address as sample address | ||
| 393 | */ | ||
| 394 | memset(&sample, 0, sizeof(sample)); | ||
| 395 | sample.pid = pid; | ||
| 396 | sample.tid = tid; | ||
| 397 | sample.time = id->time; | ||
| 398 | sample.ip = addr; | ||
| 399 | |||
| 400 | ret = perf_event__process_mmap2(tool, event, &sample, jd->machine); | ||
| 401 | if (ret) | ||
| 402 | return ret; | ||
| 403 | |||
| 404 | ret = jit_inject_event(jd, event); | ||
| 405 | /* | ||
| 406 | * mark dso as use to generate buildid in the header | ||
| 407 | */ | ||
| 408 | if (!ret) | ||
| 409 | build_id__mark_dso_hit(tool, event, &sample, NULL, jd->machine); | ||
| 410 | |||
| 411 | return ret; | ||
| 412 | } | ||
| 413 | |||
| 414 | static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) | ||
| 415 | { | ||
| 416 | struct perf_sample sample; | ||
| 417 | union perf_event *event; | ||
| 418 | struct perf_tool *tool = jd->session->tool; | ||
| 419 | char *filename; | ||
| 420 | size_t size; | ||
| 421 | struct stat st; | ||
| 422 | u16 idr_size; | ||
| 423 | int ret; | ||
| 424 | pid_t pid, tid; | ||
| 425 | struct { | ||
| 426 | u32 pid, tid; | ||
| 427 | u64 time; | ||
| 428 | } *id; | ||
| 429 | |||
| 430 | pid = jr->move.pid; | ||
| 431 | tid = jr->move.tid; | ||
| 432 | idr_size = jd->machine->id_hdr_size; | ||
| 433 | |||
| 434 | /* | ||
| 435 | * +16 to account for sample_id_all (hack) | ||
| 436 | */ | ||
| 437 | event = calloc(1, sizeof(*event) + 16); | ||
| 438 | if (!event) | ||
| 439 | return -1; | ||
| 440 | |||
| 441 | filename = event->mmap2.filename; | ||
| 442 | size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%"PRIu64, | ||
| 443 | jd->dir, | ||
| 444 | pid, | ||
| 445 | jr->move.code_index); | ||
| 446 | |||
| 447 | size++; /* for \0 */ | ||
| 448 | |||
| 449 | if (stat(filename, &st)) | ||
| 450 | memset(&st, 0, sizeof(stat)); | ||
| 451 | |||
| 452 | size = PERF_ALIGN(size, sizeof(u64)); | ||
| 453 | |||
| 454 | event->mmap2.header.type = PERF_RECORD_MMAP2; | ||
| 455 | event->mmap2.header.misc = PERF_RECORD_MISC_USER; | ||
| 456 | event->mmap2.header.size = (sizeof(event->mmap2) - | ||
| 457 | (sizeof(event->mmap2.filename) - size) + idr_size); | ||
| 458 | event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; | ||
| 459 | event->mmap2.start = jr->move.new_code_addr; | ||
| 460 | event->mmap2.len = jr->move.code_size; | ||
| 461 | event->mmap2.pid = pid; | ||
| 462 | event->mmap2.tid = tid; | ||
| 463 | event->mmap2.ino = st.st_ino; | ||
| 464 | event->mmap2.maj = major(st.st_dev); | ||
| 465 | event->mmap2.min = minor(st.st_dev); | ||
| 466 | event->mmap2.prot = st.st_mode; | ||
| 467 | event->mmap2.flags = MAP_SHARED; | ||
| 468 | event->mmap2.ino_generation = 1; | ||
| 469 | |||
| 470 | id = (void *)((unsigned long)event + event->mmap.header.size - idr_size); | ||
| 471 | if (jd->sample_type & PERF_SAMPLE_TID) { | ||
| 472 | id->pid = pid; | ||
| 473 | id->tid = tid; | ||
| 474 | } | ||
| 475 | if (jd->sample_type & PERF_SAMPLE_TIME) | ||
| 476 | id->time = jr->load.p.timestamp; | ||
| 477 | |||
| 478 | /* | ||
| 479 | * create pseudo sample to induce dso hit increment | ||
| 480 | * use first address as sample address | ||
| 481 | */ | ||
| 482 | memset(&sample, 0, sizeof(sample)); | ||
| 483 | sample.pid = pid; | ||
| 484 | sample.tid = tid; | ||
| 485 | sample.time = id->time; | ||
| 486 | sample.ip = jr->move.new_code_addr; | ||
| 487 | |||
| 488 | ret = perf_event__process_mmap2(tool, event, &sample, jd->machine); | ||
| 489 | if (ret) | ||
| 490 | return ret; | ||
| 491 | |||
| 492 | ret = jit_inject_event(jd, event); | ||
| 493 | if (!ret) | ||
| 494 | build_id__mark_dso_hit(tool, event, &sample, NULL, jd->machine); | ||
| 495 | |||
| 496 | return ret; | ||
| 497 | } | ||
| 498 | |||
| 499 | static int jit_repipe_debug_info(struct jit_buf_desc *jd, union jr_entry *jr) | ||
| 500 | { | ||
| 501 | void *data; | ||
| 502 | size_t sz; | ||
| 503 | |||
| 504 | if (!(jd && jr)) | ||
| 505 | return -1; | ||
| 506 | |||
| 507 | sz = jr->prefix.total_size - sizeof(jr->info); | ||
| 508 | data = malloc(sz); | ||
| 509 | if (!data) | ||
| 510 | return -1; | ||
| 511 | |||
| 512 | memcpy(data, &jr->info.entries, sz); | ||
| 513 | |||
| 514 | jd->debug_data = data; | ||
| 515 | |||
| 516 | /* | ||
| 517 | * we must use nr_entry instead of size here because | ||
| 518 | * we cannot distinguish actual entry from padding otherwise | ||
| 519 | */ | ||
| 520 | jd->nr_debug_entries = jr->info.nr_entry; | ||
| 521 | |||
| 522 | return 0; | ||
| 523 | } | ||
| 524 | |||
| 525 | static int | ||
| 526 | jit_process_dump(struct jit_buf_desc *jd) | ||
| 527 | { | ||
| 528 | union jr_entry *jr; | ||
| 529 | int ret; | ||
| 530 | |||
| 531 | while ((jr = jit_get_next_entry(jd))) { | ||
| 532 | switch(jr->prefix.id) { | ||
| 533 | case JIT_CODE_LOAD: | ||
| 534 | ret = jit_repipe_code_load(jd, jr); | ||
| 535 | break; | ||
| 536 | case JIT_CODE_MOVE: | ||
| 537 | ret = jit_repipe_code_move(jd, jr); | ||
| 538 | break; | ||
| 539 | case JIT_CODE_DEBUG_INFO: | ||
| 540 | ret = jit_repipe_debug_info(jd, jr); | ||
| 541 | break; | ||
| 542 | default: | ||
| 543 | ret = 0; | ||
| 544 | continue; | ||
| 545 | } | ||
| 546 | } | ||
| 547 | return ret; | ||
| 548 | } | ||
| 549 | |||
| 550 | static int | ||
| 551 | jit_inject(struct jit_buf_desc *jd, char *path) | ||
| 552 | { | ||
| 553 | int ret; | ||
| 554 | |||
| 555 | if (verbose > 0) | ||
| 556 | fprintf(stderr, "injecting: %s\n", path); | ||
| 557 | |||
| 558 | ret = jit_open(jd, path); | ||
| 559 | if (ret) | ||
| 560 | return -1; | ||
| 561 | |||
| 562 | ret = jit_process_dump(jd); | ||
| 563 | |||
| 564 | jit_close(jd); | ||
| 565 | |||
| 566 | if (verbose > 0) | ||
| 567 | fprintf(stderr, "injected: %s (%d)\n", path, ret); | ||
| 568 | |||
| 569 | return 0; | ||
| 570 | } | ||
| 571 | |||
| 572 | /* | ||
| 573 | * File must be with pattern .../jit-XXXX.dump | ||
| 574 | * where XXXX is the PID of the process which did the mmap() | ||
| 575 | * as captured in the RECORD_MMAP record | ||
| 576 | */ | ||
| 577 | static int | ||
| 578 | jit_detect(char *mmap_name, pid_t pid) | ||
| 579 | { | ||
| 580 | char *p; | ||
| 581 | char *end = NULL; | ||
| 582 | pid_t pid2; | ||
| 583 | |||
| 584 | if (verbose > 2) | ||
| 585 | fprintf(stderr, "jit marker trying : %s\n", mmap_name); | ||
| 586 | /* | ||
| 587 | * get file name | ||
| 588 | */ | ||
| 589 | p = strrchr(mmap_name, '/'); | ||
| 590 | if (!p) | ||
| 591 | return -1; | ||
| 592 | |||
| 593 | /* | ||
| 594 | * match prefix | ||
| 595 | */ | ||
| 596 | if (strncmp(p, "/jit-", 5)) | ||
| 597 | return -1; | ||
| 598 | |||
| 599 | /* | ||
| 600 | * skip prefix | ||
| 601 | */ | ||
| 602 | p += 5; | ||
| 603 | |||
| 604 | /* | ||
| 605 | * must be followed by a pid | ||
| 606 | */ | ||
| 607 | if (!isdigit(*p)) | ||
| 608 | return -1; | ||
| 609 | |||
| 610 | pid2 = (int)strtol(p, &end, 10); | ||
| 611 | if (!end) | ||
| 612 | return -1; | ||
| 613 | |||
| 614 | /* | ||
| 615 | * pid does not match mmap pid | ||
| 616 | * pid==0 in system-wide mode (synthesized) | ||
| 617 | */ | ||
| 618 | if (pid && pid2 != pid) | ||
| 619 | return -1; | ||
| 620 | /* | ||
| 621 | * validate suffix | ||
| 622 | */ | ||
| 623 | if (strcmp(end, ".dump")) | ||
| 624 | return -1; | ||
| 625 | |||
| 626 | if (verbose > 0) | ||
| 627 | fprintf(stderr, "jit marker found: %s\n", mmap_name); | ||
| 628 | |||
| 629 | return 0; | ||
| 630 | } | ||
| 631 | |||
| 632 | int | ||
| 633 | jit_process(struct perf_session *session, | ||
| 634 | struct perf_data_file *output, | ||
| 635 | struct machine *machine, | ||
| 636 | char *filename, | ||
| 637 | pid_t pid, | ||
| 638 | u64 *nbytes) | ||
| 639 | { | ||
| 640 | struct perf_evsel *first; | ||
| 641 | struct jit_buf_desc jd; | ||
| 642 | int ret; | ||
| 643 | |||
| 644 | /* | ||
| 645 | * first, detect marker mmap (i.e., the jitdump mmap) | ||
| 646 | */ | ||
| 647 | if (jit_detect(filename, pid)) | ||
| 648 | return -1; | ||
| 649 | |||
| 650 | memset(&jd, 0, sizeof(jd)); | ||
| 651 | |||
| 652 | jd.session = session; | ||
| 653 | jd.output = output; | ||
| 654 | jd.machine = machine; | ||
| 655 | |||
| 656 | /* | ||
| 657 | * track sample_type to compute id_all layout | ||
| 658 | * perf sets the same sample type to all events as of now | ||
| 659 | */ | ||
| 660 | first = perf_evlist__first(session->evlist); | ||
| 661 | jd.sample_type = first->attr.sample_type; | ||
| 662 | |||
| 663 | *nbytes = 0; | ||
| 664 | |||
| 665 | ret = jit_inject(&jd, filename); | ||
| 666 | if (!ret) | ||
| 667 | *nbytes = jd.bytes_written; | ||
| 668 | |||
| 669 | return ret; | ||
| 670 | } | ||
diff --git a/tools/perf/util/jitdump.h b/tools/perf/util/jitdump.h new file mode 100644 index 000000000000..b66c1f503d9e --- /dev/null +++ b/tools/perf/util/jitdump.h | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | /* | ||
| 2 | * jitdump.h: jitted code info encapsulation file format | ||
| 3 | * | ||
| 4 | * Adapted from OProfile GPLv2 support jidump.h: | ||
| 5 | * Copyright 2007 OProfile authors | ||
| 6 | * Jens Wilke | ||
| 7 | * Daniel Hansel | ||
| 8 | * Copyright IBM Corporation 2007 | ||
| 9 | */ | ||
| 10 | #ifndef JITDUMP_H | ||
| 11 | #define JITDUMP_H | ||
| 12 | |||
| 13 | #include <sys/time.h> | ||
| 14 | #include <time.h> | ||
| 15 | #include <stdint.h> | ||
| 16 | |||
| 17 | /* JiTD */ | ||
| 18 | #define JITHEADER_MAGIC 0x4A695444 | ||
| 19 | #define JITHEADER_MAGIC_SW 0x4454694A | ||
| 20 | |||
| 21 | #define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7) | ||
| 22 | |||
| 23 | #define JITHEADER_VERSION 1 | ||
| 24 | |||
| 25 | enum jitdump_flags_bits { | ||
| 26 | JITDUMP_FLAGS_MAX_BIT, | ||
| 27 | }; | ||
| 28 | |||
| 29 | #define JITDUMP_FLAGS_RESERVED (JITDUMP_FLAGS_MAX_BIT < 64 ? \ | ||
| 30 | (~((1ULL << JITDUMP_FLAGS_MAX_BIT) - 1)) : 0) | ||
| 31 | |||
| 32 | struct jitheader { | ||
| 33 | uint32_t magic; /* characters "jItD" */ | ||
| 34 | uint32_t version; /* header version */ | ||
| 35 | uint32_t total_size; /* total size of header */ | ||
| 36 | uint32_t elf_mach; /* elf mach target */ | ||
| 37 | uint32_t pad1; /* reserved */ | ||
| 38 | uint32_t pid; /* JIT process id */ | ||
| 39 | uint64_t timestamp; /* timestamp */ | ||
| 40 | uint64_t flags; /* flags */ | ||
| 41 | }; | ||
| 42 | |||
| 43 | enum jit_record_type { | ||
| 44 | JIT_CODE_LOAD = 0, | ||
| 45 | JIT_CODE_MOVE = 1, | ||
| 46 | JIT_CODE_DEBUG_INFO = 2, | ||
| 47 | JIT_CODE_CLOSE = 3, | ||
| 48 | |||
| 49 | JIT_CODE_MAX, | ||
| 50 | }; | ||
| 51 | |||
| 52 | /* record prefix (mandatory in each record) */ | ||
| 53 | struct jr_prefix { | ||
| 54 | uint32_t id; | ||
| 55 | uint32_t total_size; | ||
| 56 | uint64_t timestamp; | ||
| 57 | }; | ||
| 58 | |||
| 59 | struct jr_code_load { | ||
| 60 | struct jr_prefix p; | ||
| 61 | |||
| 62 | uint32_t pid; | ||
| 63 | uint32_t tid; | ||
| 64 | uint64_t vma; | ||
| 65 | uint64_t code_addr; | ||
| 66 | uint64_t code_size; | ||
| 67 | uint64_t code_index; | ||
| 68 | }; | ||
| 69 | |||
| 70 | struct jr_code_close { | ||
| 71 | struct jr_prefix p; | ||
| 72 | }; | ||
| 73 | |||
| 74 | struct jr_code_move { | ||
| 75 | struct jr_prefix p; | ||
| 76 | |||
| 77 | uint32_t pid; | ||
| 78 | uint32_t tid; | ||
| 79 | uint64_t vma; | ||
| 80 | uint64_t old_code_addr; | ||
| 81 | uint64_t new_code_addr; | ||
| 82 | uint64_t code_size; | ||
| 83 | uint64_t code_index; | ||
| 84 | }; | ||
| 85 | |||
| 86 | struct debug_entry { | ||
| 87 | uint64_t addr; | ||
| 88 | int lineno; /* source line number starting at 1 */ | ||
| 89 | int discrim; /* column discriminator, 0 is default */ | ||
| 90 | const char name[0]; /* null terminated filename, \xff\0 if same as previous entry */ | ||
| 91 | }; | ||
| 92 | |||
| 93 | struct jr_code_debug_info { | ||
| 94 | struct jr_prefix p; | ||
| 95 | |||
| 96 | uint64_t code_addr; | ||
| 97 | uint64_t nr_entry; | ||
| 98 | struct debug_entry entries[0]; | ||
| 99 | }; | ||
| 100 | |||
| 101 | union jr_entry { | ||
| 102 | struct jr_code_debug_info info; | ||
| 103 | struct jr_code_close close; | ||
| 104 | struct jr_code_load load; | ||
| 105 | struct jr_code_move move; | ||
| 106 | struct jr_prefix prefix; | ||
| 107 | }; | ||
| 108 | |||
| 109 | static inline struct debug_entry * | ||
| 110 | debug_entry_next(struct debug_entry *ent) | ||
| 111 | { | ||
| 112 | void *a = ent + 1; | ||
| 113 | size_t l = strlen(ent->name) + 1; | ||
| 114 | return a + l; | ||
| 115 | } | ||
| 116 | |||
| 117 | static inline char * | ||
| 118 | debug_entry_file(struct debug_entry *ent) | ||
| 119 | { | ||
| 120 | void *a = ent + 1; | ||
| 121 | return a; | ||
| 122 | } | ||
| 123 | |||
| 124 | #endif /* !JITDUMP_H */ | ||
