aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephane Eranian <eranian@google.com>2015-11-30 04:02:21 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2016-02-05 07:46:45 -0500
commit9b07e27f88b9cd785cdb23f9a2231c12521dda94 (patch)
tree5634fa81d6d34df7ef2378f0c37f76bff3ec6a5e
parent921f3fadbc48c7c3799b415b895297cd476cf7f1 (diff)
perf inject: Add jitdump mmap injection support
This patch adds a --jit/-j option to perf inject. This options injects MMAP records into the perf.data file to cover the jitted code mmaps. It also emits ELF images for each function in the jidump file. Those images are created where the jitdump file is. The MMAP records point to that location as well. Typical flow: $ perf record -k mono -- java -agentpath:libpjvmti.so java_class $ perf inject --jit -i perf.data -o perf.data.jitted $ perf report -i perf.data.jitted Note that jitdump.h support is not limited to Java, it works with any jitted environment modified to emit the jitdump file format, include those where code can be jitted multiple times and moved around. The jitdump.h format is adapted from the Oprofile project. The genelf.c (ELF binary generation) depends on MD5 hash encoding for the buildid. To enable this, libssl-dev must be installed. If not, then genelf.c defaults to using urandom to generate the buildid, which is not ideal. The Makefile auto-detects the presence on libssl-dev. This version mmaps the jitdump file to create a marker MMAP record in the perf.data file. The marker is used to detect jitdump and cause perf inject to inject the jitted mmaps and generate ELF images for jitted functions. In V8, the following fixes and changes were made among other things: - the jidump header format include a new flags field to be used to carry information about the configuration of the runtime agent. Contributed by: Adrian Hunter <adrian.hunter@intel.com> - Fix mmap pgoff: MMAP event pgoff must be the offset within the ELF file at which the code resides. Contributed by: Adrian Hunter <adrian.hunter@intel.com> - Fix ELF virtual addresses: perf tools expect the ELF virtual addresses of dynamic objects to match the file offset. Contributed by: Adrian Hunter <adrian.hunter@intel.com> - JIT MMAP injection does not obey finished_round semantics. JIT MMAP injection injects all MMAP events in one go, so it does not obey finished_round semantics, so drop the finished_round events from the output perf.data file. Contributed by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Stephane Eranian <eranian@google.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Carl Love <cel@us.ibm.com> Cc: David Ahern <dsahern@gmail.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: John McCutchan <johnmccutchan@google.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Pawel Moll <pawel.moll@arm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Sonny Rao <sonnyrao@chromium.org> Cc: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> Link: http://lkml.kernel.org/r/1448874143-7269-3-git-send-email-eranian@google.com [ Moved inject.build_ids ordering bits to a separate patch, fixed the NO_LIBELF=1 build ] Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/Documentation/perf-inject.txt7
-rw-r--r--tools/perf/builtin-inject.c98
-rw-r--r--tools/perf/util/Build2
-rw-r--r--tools/perf/util/genelf.c442
-rw-r--r--tools/perf/util/genelf.h63
-rw-r--r--tools/perf/util/jit.h15
-rw-r--r--tools/perf/util/jitdump.c670
-rw-r--r--tools/perf/util/jitdump.h124
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
56SEE ALSO 63SEE ALSO
57-------- 64--------
58linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1] 65linkperf: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
77static 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
74static int perf_event__repipe_op2_synth(struct perf_tool *tool, 85static 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
249static 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
237static int perf_event__repipe_mmap2(struct perf_tool *tool, 269static 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
283static 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
250static int perf_event__repipe_fork(struct perf_tool *tool, 303static 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
721static int
722jit_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
667int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) 737int 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/
106libperf-$(CONFIG_ZLIB) += zlib.o 106libperf-$(CONFIG_ZLIB) += zlib.o
107libperf-$(CONFIG_LZMA) += lzma.o 107libperf-$(CONFIG_LZMA) += lzma.o
108libperf-y += demangle-java.o 108libperf-y += demangle-java.o
109libperf-$(CONFIG_LIBELF) += jitdump.o
110libperf-$(CONFIG_LIBELF) += genelf.o
109 111
110CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" 112CFLAGS_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
48typedef 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
55struct options {
56 char *output;
57 int fd;
58};
59
60static 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
72static 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
78static 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
97static void
98gen_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
121static void
122gen_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
135static void
136gen_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 */
157int
158jit_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;
394error:
395 (void)elf_end(e);
396
397 free(strsym);
398
399
400 return retval;
401}
402
403#ifndef JVMTI
404
405static 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
411static struct options options;
412
413int 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 */
5extern 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
6extern 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
13extern 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
26struct 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
44struct 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
51struct 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
61static int
62jit_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
89static void
90jit_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
99static int
100jit_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;
178error:
179 funlockfile(jd->in);
180 fclose(jd->in);
181 return retval;
182}
183
184static union jr_entry *
185jit_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
294static int
295jit_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
307static 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
414static 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
499static 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
525static int
526jit_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
550static int
551jit_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 */
577static int
578jit_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
632int
633jit_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
25enum 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
32struct 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
43enum 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) */
53struct jr_prefix {
54 uint32_t id;
55 uint32_t total_size;
56 uint64_t timestamp;
57};
58
59struct 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
70struct jr_code_close {
71 struct jr_prefix p;
72};
73
74struct 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
86struct 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
93struct 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
101union 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
109static inline struct debug_entry *
110debug_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
117static inline char *
118debug_entry_file(struct debug_entry *ent)
119{
120 void *a = ent + 1;
121 return a;
122}
123
124#endif /* !JITDUMP_H */