diff options
author | Ingo Molnar <mingo@kernel.org> | 2016-02-09 04:38:40 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-02-09 04:38:40 -0500 |
commit | 156d22386503e1efc58b0f3244895b1c0f930018 (patch) | |
tree | a0a2764ff4ffe4e86f2858b2f6a09ea57d519cdc | |
parent | d0af1c0525d561fe3ab6d7a767cdd52704da25cd (diff) | |
parent | 598b7c6919c7bbcc1243009721a01bc12275ff3e (diff) |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
User visible fixes:
- Handle spaces in file names obtained from /proc/pid/maps (Marcin Ślusarz)
New features:
- Improved support for Java, using the JVMTI agent library to do jitdumps
that then will be inserted in synthesized PERF_RECORD_MMAP2 events via
'perf inject' pointed to synthesized ELF files stored in ~/.debug and
keyed with build-ids, to allow symbol resolution and even annotation with
source line info, see the changeset comments to see how to use it (Stephane Eranian)
Documentation changes:
- Document mmore variables in the 'perf config' man page (Taeung Song)
Infrastructure changes:
- Improve a bit the 'make -C tools/perf build-test' output (Arnaldo Carvalho de Melo)
- Do 'build-test' in parallel, using 'make -j' (Arnaldo Carvalho de Melo)
- Fix handling of 'clean' in multi-target make invokations for parallell builds (Jiri Olsa)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
26 files changed, 3357 insertions, 7 deletions
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 7bff2ea831cf..6b7707270aa3 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature | |||
@@ -46,6 +46,7 @@ FEATURE_TESTS_BASIC := \ | |||
46 | libpython \ | 46 | libpython \ |
47 | libpython-version \ | 47 | libpython-version \ |
48 | libslang \ | 48 | libslang \ |
49 | libcrypto \ | ||
49 | libunwind \ | 50 | libunwind \ |
50 | pthread-attr-setaffinity-np \ | 51 | pthread-attr-setaffinity-np \ |
51 | stackprotector-all \ | 52 | stackprotector-all \ |
@@ -87,6 +88,7 @@ FEATURE_DISPLAY ?= \ | |||
87 | libperl \ | 88 | libperl \ |
88 | libpython \ | 89 | libpython \ |
89 | libslang \ | 90 | libslang \ |
91 | libcrypto \ | ||
90 | libunwind \ | 92 | libunwind \ |
91 | libdw-dwarf-unwind \ | 93 | libdw-dwarf-unwind \ |
92 | zlib \ | 94 | zlib \ |
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index bf8f0352264d..c5f4c417428d 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile | |||
@@ -23,6 +23,7 @@ FILES= \ | |||
23 | test-libpython.bin \ | 23 | test-libpython.bin \ |
24 | test-libpython-version.bin \ | 24 | test-libpython-version.bin \ |
25 | test-libslang.bin \ | 25 | test-libslang.bin \ |
26 | test-libcrypto.bin \ | ||
26 | test-libunwind.bin \ | 27 | test-libunwind.bin \ |
27 | test-libunwind-debug-frame.bin \ | 28 | test-libunwind-debug-frame.bin \ |
28 | test-pthread-attr-setaffinity-np.bin \ | 29 | test-pthread-attr-setaffinity-np.bin \ |
@@ -105,6 +106,9 @@ $(OUTPUT)test-libaudit.bin: | |||
105 | $(OUTPUT)test-libslang.bin: | 106 | $(OUTPUT)test-libslang.bin: |
106 | $(BUILD) -I/usr/include/slang -lslang | 107 | $(BUILD) -I/usr/include/slang -lslang |
107 | 108 | ||
109 | $(OUTPUT)test-libcrypto.bin: | ||
110 | $(BUILD) -lcrypto | ||
111 | |||
108 | $(OUTPUT)test-gtk2.bin: | 112 | $(OUTPUT)test-gtk2.bin: |
109 | $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) | 113 | $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) |
110 | 114 | ||
diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c index 81025cade45f..e499a36c1e4a 100644 --- a/tools/build/feature/test-all.c +++ b/tools/build/feature/test-all.c | |||
@@ -129,6 +129,10 @@ | |||
129 | # include "test-bpf.c" | 129 | # include "test-bpf.c" |
130 | #undef main | 130 | #undef main |
131 | 131 | ||
132 | #define main main_test_libcrypto | ||
133 | # include "test-libcrypto.c" | ||
134 | #undef main | ||
135 | |||
132 | int main(int argc, char *argv[]) | 136 | int main(int argc, char *argv[]) |
133 | { | 137 | { |
134 | main_test_libpython(); | 138 | main_test_libpython(); |
@@ -158,6 +162,7 @@ int main(int argc, char *argv[]) | |||
158 | main_test_lzma(); | 162 | main_test_lzma(); |
159 | main_test_get_cpuid(); | 163 | main_test_get_cpuid(); |
160 | main_test_bpf(); | 164 | main_test_bpf(); |
165 | main_test_libcrypto(); | ||
161 | 166 | ||
162 | return 0; | 167 | return 0; |
163 | } | 168 | } |
diff --git a/tools/build/feature/test-libcrypto.c b/tools/build/feature/test-libcrypto.c new file mode 100644 index 000000000000..bd79dc7f28d3 --- /dev/null +++ b/tools/build/feature/test-libcrypto.c | |||
@@ -0,0 +1,17 @@ | |||
1 | #include <openssl/sha.h> | ||
2 | #include <openssl/md5.h> | ||
3 | |||
4 | int main(void) | ||
5 | { | ||
6 | MD5_CTX context; | ||
7 | unsigned char md[MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH]; | ||
8 | unsigned char dat[] = "12345"; | ||
9 | |||
10 | MD5_Init(&context); | ||
11 | MD5_Update(&context, &dat[0], sizeof(dat)); | ||
12 | MD5_Final(&md[0], &context); | ||
13 | |||
14 | SHA1(&dat[0], sizeof(dat), &md[0]); | ||
15 | |||
16 | return 0; | ||
17 | } | ||
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt index 74589c68558a..c7158bfb1649 100644 --- a/tools/perf/Documentation/perf-config.txt +++ b/tools/perf/Documentation/perf-config.txt | |||
@@ -296,6 +296,149 @@ hist.*:: | |||
296 | and 'baz' to 50.00% for each, while 'absolute' would show their | 296 | and 'baz' to 50.00% for each, while 'absolute' would show their |
297 | current overhead (33.33%). | 297 | current overhead (33.33%). |
298 | 298 | ||
299 | ui.*:: | ||
300 | ui.show-headers:: | ||
301 | This option controls display of column headers (like 'Overhead' and 'Symbol') | ||
302 | in 'report' and 'top'. If this option is false, they are hidden. | ||
303 | This option is only applied to TUI. | ||
304 | |||
305 | call-graph.*:: | ||
306 | When sub-commands 'top' and 'report' work with -g/—-children | ||
307 | there're options in control of call-graph. | ||
308 | |||
309 | call-graph.record-mode:: | ||
310 | The record-mode can be 'fp' (frame pointer), 'dwarf' and 'lbr'. | ||
311 | The value of 'dwarf' is effective only if perf detect needed library | ||
312 | (libunwind or a recent version of libdw). | ||
313 | 'lbr' only work for cpus that support it. | ||
314 | |||
315 | call-graph.dump-size:: | ||
316 | The size of stack to dump in order to do post-unwinding. Default is 8192 (byte). | ||
317 | When using dwarf into record-mode, the default size will be used if omitted. | ||
318 | |||
319 | call-graph.print-type:: | ||
320 | The print-types can be graph (graph absolute), fractal (graph relative), | ||
321 | flat and folded. This option controls a way to show overhead for each callchain | ||
322 | entry. Suppose a following example. | ||
323 | |||
324 | Overhead Symbols | ||
325 | ........ ....... | ||
326 | 40.00% foo | ||
327 | | | ||
328 | ---foo | ||
329 | | | ||
330 | |--50.00%--bar | ||
331 | | main | ||
332 | | | ||
333 | --50.00%--baz | ||
334 | main | ||
335 | |||
336 | This output is a 'fractal' format. The 'foo' came from 'bar' and 'baz' exactly | ||
337 | half and half so 'fractal' shows 50.00% for each | ||
338 | (meaning that it assumes 100% total overhead of 'foo'). | ||
339 | |||
340 | The 'graph' uses absolute overhead value of 'foo' as total so each of | ||
341 | 'bar' and 'baz' callchain will have 20.00% of overhead. | ||
342 | If 'flat' is used, single column and linear exposure of call chains. | ||
343 | 'folded' mean call chains are displayed in a line, separated by semicolons. | ||
344 | |||
345 | call-graph.order:: | ||
346 | This option controls print order of callchains. The default is | ||
347 | 'callee' which means callee is printed at top and then followed by its | ||
348 | caller and so on. The 'caller' prints it in reverse order. | ||
349 | |||
350 | If this option is not set and report.children or top.children is | ||
351 | set to true (or the equivalent command line option is given), | ||
352 | the default value of this option is changed to 'caller' for the | ||
353 | execution of 'perf report' or 'perf top'. Other commands will | ||
354 | still default to 'callee'. | ||
355 | |||
356 | call-graph.sort-key:: | ||
357 | The callchains are merged if they contain same information. | ||
358 | The sort-key option determines a way to compare the callchains. | ||
359 | A value of 'sort-key' can be 'function' or 'address'. | ||
360 | The default is 'function'. | ||
361 | |||
362 | call-graph.threshold:: | ||
363 | When there're many callchains it'd print tons of lines. So perf omits | ||
364 | small callchains under a certain overhead (threshold) and this option | ||
365 | control the threshold. Default is 0.5 (%). The overhead is calculated | ||
366 | by value depends on call-graph.print-type. | ||
367 | |||
368 | call-graph.print-limit:: | ||
369 | This is a maximum number of lines of callchain printed for a single | ||
370 | histogram entry. Default is 0 which means no limitation. | ||
371 | |||
372 | report.*:: | ||
373 | report.percent-limit:: | ||
374 | This one is mostly the same as call-graph.threshold but works for | ||
375 | histogram entries. Entries having an overhead lower than this | ||
376 | percentage will not be printed. Default is '0'. If percent-limit | ||
377 | is '10', only entries which have more than 10% of overhead will be | ||
378 | printed. | ||
379 | |||
380 | report.queue-size:: | ||
381 | This option sets up the maximum allocation size of the internal | ||
382 | event queue for ordering events. Default is 0, meaning no limit. | ||
383 | |||
384 | report.children:: | ||
385 | 'Children' means functions called from another function. | ||
386 | If this option is true, 'perf report' cumulates callchains of children | ||
387 | and show (accumulated) total overhead as well as 'Self' overhead. | ||
388 | Please refer to the 'perf report' manual. The default is 'true'. | ||
389 | |||
390 | report.group:: | ||
391 | This option is to show event group information together. | ||
392 | Example output with this turned on, notice that there is one column | ||
393 | per event in the group, ref-cycles and cycles: | ||
394 | |||
395 | # group: {ref-cycles,cycles} | ||
396 | # ======== | ||
397 | # | ||
398 | # Samples: 7K of event 'anon group { ref-cycles, cycles }' | ||
399 | # Event count (approx.): 6876107743 | ||
400 | # | ||
401 | # Overhead Command Shared Object Symbol | ||
402 | # ................ ....... ................. ................... | ||
403 | # | ||
404 | 99.84% 99.76% noploop noploop [.] main | ||
405 | 0.07% 0.00% noploop ld-2.15.so [.] strcmp | ||
406 | 0.03% 0.00% noploop [kernel.kallsyms] [k] timerqueue_del | ||
407 | |||
408 | top.*:: | ||
409 | top.children:: | ||
410 | Same as 'report.children'. So if it is enabled, the output of 'top' | ||
411 | command will have 'Children' overhead column as well as 'Self' overhead | ||
412 | column by default. | ||
413 | The default is 'true'. | ||
414 | |||
415 | man.*:: | ||
416 | man.viewer:: | ||
417 | This option can assign a tool to view manual pages when 'help' | ||
418 | subcommand was invoked. Supported tools are 'man', 'woman' | ||
419 | (with emacs client) and 'konqueror'. Default is 'man'. | ||
420 | |||
421 | New man viewer tool can be also added using 'man.<tool>.cmd' | ||
422 | or use different path using 'man.<tool>.path' config option. | ||
423 | |||
424 | pager.*:: | ||
425 | pager.<subcommand>:: | ||
426 | When the subcommand is run on stdio, determine whether it uses | ||
427 | pager or not based on this value. Default is 'unspecified'. | ||
428 | |||
429 | kmem.*:: | ||
430 | kmem.default:: | ||
431 | This option decides which allocator is to be analyzed if neither | ||
432 | '--slab' nor '--page' option is used. Default is 'slab'. | ||
433 | |||
434 | record.*:: | ||
435 | record.build-id:: | ||
436 | This option can be 'cache', 'no-cache' or 'skip'. | ||
437 | 'cache' is to post-process data and save/update the binaries into | ||
438 | the build-id cache (in ~/.debug). This is the default. | ||
439 | But if this option is 'no-cache', it will not update the build-id cache. | ||
440 | 'skip' skips post-processing and does not update the cache. | ||
441 | |||
299 | SEE ALSO | 442 | SEE ALSO |
300 | -------- | 443 | -------- |
301 | linkperf:perf[1] | 444 | linkperf:perf[1] |
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/Makefile b/tools/perf/Makefile index 4b68f465195c..32a64e619028 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -68,6 +68,20 @@ all tags TAGS: | |||
68 | $(print_msg) | 68 | $(print_msg) |
69 | $(make) | 69 | $(make) |
70 | 70 | ||
71 | ifdef MAKECMDGOALS | ||
72 | has_clean := 0 | ||
73 | ifneq ($(filter clean,$(MAKECMDGOALS)),) | ||
74 | has_clean := 1 | ||
75 | endif # clean | ||
76 | |||
77 | ifeq ($(has_clean),1) | ||
78 | rest := $(filter-out clean,$(MAKECMDGOALS)) | ||
79 | ifneq ($(rest),) | ||
80 | $(rest): clean | ||
81 | endif # rest | ||
82 | endif # has_clean | ||
83 | endif # MAKECMDGOALS | ||
84 | |||
71 | # | 85 | # |
72 | # The clean target is not really parallel, don't print the jobs info: | 86 | # The clean target is not really parallel, don't print the jobs info: |
73 | # | 87 | # |
@@ -85,7 +99,7 @@ clean: | |||
85 | # make -C tools/perf -f tests/make | 99 | # make -C tools/perf -f tests/make |
86 | # | 100 | # |
87 | build-test: | 101 | build-test: |
88 | @$(MAKE) SHUF=1 -f tests/make REUSE_FEATURES_DUMP=1 MK=Makefile --no-print-directory tarpkg out | 102 | @$(MAKE) SHUF=1 -f tests/make REUSE_FEATURES_DUMP=1 MK=Makefile SET_PARALLEL=1 --no-print-directory tarpkg out |
89 | 103 | ||
90 | # | 104 | # |
91 | # All other targets get passed through: | 105 | # All other targets get passed through: |
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 0ef3d97d7954..d404117810a7 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -58,6 +58,9 @@ include config/utilities.mak | |||
58 | # | 58 | # |
59 | # Define NO_LIBBIONIC if you do not want bionic support | 59 | # Define NO_LIBBIONIC if you do not want bionic support |
60 | # | 60 | # |
61 | # Define NO_LIBCRYPTO if you do not want libcrypto (openssl) support | ||
62 | # used for generating build-ids for ELFs generated by jitdump. | ||
63 | # | ||
61 | # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support | 64 | # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support |
62 | # for dwarf backtrace post unwind. | 65 | # for dwarf backtrace post unwind. |
63 | # | 66 | # |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 0022e02ed31a..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 | /* |
@@ -755,6 +828,36 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
755 | if (inject.session == NULL) | 828 | if (inject.session == NULL) |
756 | return -1; | 829 | return -1; |
757 | 830 | ||
831 | if (inject.build_ids) { | ||
832 | /* | ||
833 | * to make sure the mmap records are ordered correctly | ||
834 | * and so that the correct especially due to jitted code | ||
835 | * mmaps. We cannot generate the buildid hit list and | ||
836 | * inject the jit mmaps at the same time for now. | ||
837 | */ | ||
838 | inject.tool.ordered_events = true; | ||
839 | inject.tool.ordering_requires_timestamps = true; | ||
840 | } | ||
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 | ||
758 | ret = symbol__init(&inject.session->header.env); | 861 | ret = symbol__init(&inject.session->header.env); |
759 | if (ret < 0) | 862 | if (ret < 0) |
760 | goto out_delete; | 863 | goto out_delete; |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 0045a5ddd0ca..f7aeaf303f5a 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -404,6 +404,17 @@ ifndef NO_LIBAUDIT | |||
404 | endif | 404 | endif |
405 | endif | 405 | endif |
406 | 406 | ||
407 | ifndef NO_LIBCRYPTO | ||
408 | ifneq ($(feature-libcrypto), 1) | ||
409 | msg := $(warning No libcrypto.h found, disables jitted code injection, please install libssl-devel or libssl-dev); | ||
410 | NO_LIBCRYPTO := 1 | ||
411 | else | ||
412 | CFLAGS += -DHAVE_LIBCRYPTO_SUPPORT | ||
413 | EXTLIBS += -lcrypto | ||
414 | $(call detected,CONFIG_CRYPTO) | ||
415 | endif | ||
416 | endif | ||
417 | |||
407 | ifdef NO_NEWT | 418 | ifdef NO_NEWT |
408 | NO_SLANG=1 | 419 | NO_SLANG=1 |
409 | endif | 420 | endif |
diff --git a/tools/perf/jvmti/Makefile b/tools/perf/jvmti/Makefile new file mode 100644 index 000000000000..5968f8332a28 --- /dev/null +++ b/tools/perf/jvmti/Makefile | |||
@@ -0,0 +1,76 @@ | |||
1 | ARCH=$(shell uname -m) | ||
2 | |||
3 | ifeq ($(ARCH), x86_64) | ||
4 | JARCH=amd64 | ||
5 | endif | ||
6 | ifeq ($(ARCH), armv7l) | ||
7 | JARCH=armhf | ||
8 | endif | ||
9 | ifeq ($(ARCH), armv6l) | ||
10 | JARCH=armhf | ||
11 | endif | ||
12 | ifeq ($(ARCH), aarch64) | ||
13 | JARCH=aarch64 | ||
14 | endif | ||
15 | ifeq ($(ARCH), ppc64) | ||
16 | JARCH=powerpc | ||
17 | endif | ||
18 | ifeq ($(ARCH), ppc64le) | ||
19 | JARCH=powerpc | ||
20 | endif | ||
21 | |||
22 | DESTDIR=/usr/local | ||
23 | |||
24 | VERSION=1 | ||
25 | REVISION=0 | ||
26 | AGE=0 | ||
27 | |||
28 | LN=ln -sf | ||
29 | RM=rm | ||
30 | |||
31 | SLIBJVMTI=libjvmti.so.$(VERSION).$(REVISION).$(AGE) | ||
32 | VLIBJVMTI=libjvmti.so.$(VERSION) | ||
33 | SLDFLAGS=-shared -Wl,-soname -Wl,$(VLIBJVMTI) | ||
34 | SOLIBEXT=so | ||
35 | |||
36 | # The following works at least on fedora 23, you may need the next | ||
37 | # line for other distros. | ||
38 | JDIR=$(shell alternatives --display java | tail -1 | cut -d' ' -f 5 | sed 's%/jre/bin/java.%%g') | ||
39 | #JDIR=$(shell /usr/sbin/update-java-alternatives -l | head -1 | cut -d ' ' -f 3) | ||
40 | # -lrt required in 32-bit mode for clock_gettime() | ||
41 | LIBS=-lelf -lrt | ||
42 | INCDIR=-I $(JDIR)/include -I $(JDIR)/include/linux | ||
43 | |||
44 | TARGETS=$(SLIBJVMTI) | ||
45 | |||
46 | SRCS=libjvmti.c jvmti_agent.c | ||
47 | OBJS=$(SRCS:.c=.o) | ||
48 | SOBJS=$(OBJS:.o=.lo) | ||
49 | OPT=-O2 -g -Werror -Wall | ||
50 | |||
51 | CFLAGS=$(INCDIR) $(OPT) | ||
52 | |||
53 | all: $(TARGETS) | ||
54 | |||
55 | .c.o: | ||
56 | $(CC) $(CFLAGS) -c $*.c | ||
57 | .c.lo: | ||
58 | $(CC) -fPIC -DPIC $(CFLAGS) -c $*.c -o $*.lo | ||
59 | |||
60 | $(OBJS) $(SOBJS): Makefile jvmti_agent.h ../util/jitdump.h | ||
61 | |||
62 | $(SLIBJVMTI): $(SOBJS) | ||
63 | $(CC) $(CFLAGS) $(SLDFLAGS) -o $@ $(SOBJS) $(LIBS) | ||
64 | $(LN) $@ libjvmti.$(SOLIBEXT) | ||
65 | |||
66 | clean: | ||
67 | $(RM) -f *.o *.so.* *.so *.lo | ||
68 | |||
69 | install: | ||
70 | -mkdir -p $(DESTDIR)/lib | ||
71 | install -m 755 $(SLIBJVMTI) $(DESTDIR)/lib/ | ||
72 | (cd $(DESTDIR)/lib; $(LN) $(SLIBJVMTI) $(VLIBJVMTI)) | ||
73 | (cd $(DESTDIR)/lib; $(LN) $(SLIBJVMTI) libjvmti.$(SOLIBEXT)) | ||
74 | ldconfig | ||
75 | |||
76 | .SUFFIXES: .c .S .o .lo | ||
diff --git a/tools/perf/jvmti/jvmti_agent.c b/tools/perf/jvmti/jvmti_agent.c new file mode 100644 index 000000000000..6461e02ab940 --- /dev/null +++ b/tools/perf/jvmti/jvmti_agent.c | |||
@@ -0,0 +1,465 @@ | |||
1 | /* | ||
2 | * jvmti_agent.c: JVMTI agent interface | ||
3 | * | ||
4 | * Adapted from the Oprofile code in opagent.c: | ||
5 | * This library is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; either | ||
8 | * version 2.1 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This library is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * Lesser General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Lesser General Public | ||
16 | * License along with this library; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * Copyright 2007 OProfile authors | ||
20 | * Jens Wilke | ||
21 | * Daniel Hansel | ||
22 | * Copyright IBM Corporation 2007 | ||
23 | */ | ||
24 | #include <sys/types.h> | ||
25 | #include <sys/stat.h> /* for mkdir() */ | ||
26 | #include <stdio.h> | ||
27 | #include <errno.h> | ||
28 | #include <string.h> | ||
29 | #include <stdlib.h> | ||
30 | #include <stdint.h> | ||
31 | #include <limits.h> | ||
32 | #include <fcntl.h> | ||
33 | #include <unistd.h> | ||
34 | #include <time.h> | ||
35 | #include <sys/mman.h> | ||
36 | #include <syscall.h> /* for gettid() */ | ||
37 | #include <err.h> | ||
38 | |||
39 | #include "jvmti_agent.h" | ||
40 | #include "../util/jitdump.h" | ||
41 | |||
42 | #define JIT_LANG "java" | ||
43 | |||
44 | static char jit_path[PATH_MAX]; | ||
45 | static void *marker_addr; | ||
46 | |||
47 | /* | ||
48 | * padding buffer | ||
49 | */ | ||
50 | static const char pad_bytes[7]; | ||
51 | |||
52 | static inline pid_t gettid(void) | ||
53 | { | ||
54 | return (pid_t)syscall(__NR_gettid); | ||
55 | } | ||
56 | |||
57 | static int get_e_machine(struct jitheader *hdr) | ||
58 | { | ||
59 | ssize_t sret; | ||
60 | char id[16]; | ||
61 | int fd, ret = -1; | ||
62 | int m = -1; | ||
63 | struct { | ||
64 | uint16_t e_type; | ||
65 | uint16_t e_machine; | ||
66 | } info; | ||
67 | |||
68 | fd = open("/proc/self/exe", O_RDONLY); | ||
69 | if (fd == -1) | ||
70 | return -1; | ||
71 | |||
72 | sret = read(fd, id, sizeof(id)); | ||
73 | if (sret != sizeof(id)) | ||
74 | goto error; | ||
75 | |||
76 | /* check ELF signature */ | ||
77 | if (id[0] != 0x7f || id[1] != 'E' || id[2] != 'L' || id[3] != 'F') | ||
78 | goto error; | ||
79 | |||
80 | sret = read(fd, &info, sizeof(info)); | ||
81 | if (sret != sizeof(info)) | ||
82 | goto error; | ||
83 | |||
84 | m = info.e_machine; | ||
85 | if (m < 0) | ||
86 | m = 0; /* ELF EM_NONE */ | ||
87 | |||
88 | hdr->elf_mach = m; | ||
89 | ret = 0; | ||
90 | error: | ||
91 | close(fd); | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | #define NSEC_PER_SEC 1000000000 | ||
96 | static int perf_clk_id = CLOCK_MONOTONIC; | ||
97 | |||
98 | static inline uint64_t | ||
99 | timespec_to_ns(const struct timespec *ts) | ||
100 | { | ||
101 | return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec; | ||
102 | } | ||
103 | |||
104 | static inline uint64_t | ||
105 | perf_get_timestamp(void) | ||
106 | { | ||
107 | struct timespec ts; | ||
108 | int ret; | ||
109 | |||
110 | ret = clock_gettime(perf_clk_id, &ts); | ||
111 | if (ret) | ||
112 | return 0; | ||
113 | |||
114 | return timespec_to_ns(&ts); | ||
115 | } | ||
116 | |||
117 | static int | ||
118 | debug_cache_init(void) | ||
119 | { | ||
120 | char str[32]; | ||
121 | char *base, *p; | ||
122 | struct tm tm; | ||
123 | time_t t; | ||
124 | int ret; | ||
125 | |||
126 | time(&t); | ||
127 | localtime_r(&t, &tm); | ||
128 | |||
129 | base = getenv("JITDUMPDIR"); | ||
130 | if (!base) | ||
131 | base = getenv("HOME"); | ||
132 | if (!base) | ||
133 | base = "."; | ||
134 | |||
135 | strftime(str, sizeof(str), JIT_LANG"-jit-%Y%m%d", &tm); | ||
136 | |||
137 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/", base); | ||
138 | |||
139 | ret = mkdir(jit_path, 0755); | ||
140 | if (ret == -1) { | ||
141 | if (errno != EEXIST) { | ||
142 | warn("jvmti: cannot create jit cache dir %s", jit_path); | ||
143 | return -1; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit", base); | ||
148 | ret = mkdir(jit_path, 0755); | ||
149 | if (ret == -1) { | ||
150 | if (errno != EEXIST) { | ||
151 | warn("cannot create jit cache dir %s", jit_path); | ||
152 | return -1; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit/%s.XXXXXXXX", base, str); | ||
157 | |||
158 | p = mkdtemp(jit_path); | ||
159 | if (p != jit_path) { | ||
160 | warn("cannot create jit cache dir %s", jit_path); | ||
161 | return -1; | ||
162 | } | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static int | ||
168 | perf_open_marker_file(int fd) | ||
169 | { | ||
170 | long pgsz; | ||
171 | |||
172 | pgsz = sysconf(_SC_PAGESIZE); | ||
173 | if (pgsz == -1) | ||
174 | return -1; | ||
175 | |||
176 | /* | ||
177 | * we mmap the jitdump to create an MMAP RECORD in perf.data file. | ||
178 | * The mmap is captured either live (perf record running when we mmap) | ||
179 | * or in deferred mode, via /proc/PID/maps | ||
180 | * the MMAP record is used as a marker of a jitdump file for more meta | ||
181 | * data info about the jitted code. Perf report/annotate detect this | ||
182 | * special filename and process the jitdump file. | ||
183 | * | ||
184 | * mapping must be PROT_EXEC to ensure it is captured by perf record | ||
185 | * even when not using -d option | ||
186 | */ | ||
187 | marker_addr = mmap(NULL, pgsz, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0); | ||
188 | return (marker_addr == MAP_FAILED) ? -1 : 0; | ||
189 | } | ||
190 | |||
191 | static void | ||
192 | perf_close_marker_file(void) | ||
193 | { | ||
194 | long pgsz; | ||
195 | |||
196 | if (!marker_addr) | ||
197 | return; | ||
198 | |||
199 | pgsz = sysconf(_SC_PAGESIZE); | ||
200 | if (pgsz == -1) | ||
201 | return; | ||
202 | |||
203 | munmap(marker_addr, pgsz); | ||
204 | } | ||
205 | |||
206 | void *jvmti_open(void) | ||
207 | { | ||
208 | int pad_cnt; | ||
209 | char dump_path[PATH_MAX]; | ||
210 | struct jitheader header; | ||
211 | int fd; | ||
212 | FILE *fp; | ||
213 | |||
214 | /* | ||
215 | * check if clockid is supported | ||
216 | */ | ||
217 | if (!perf_get_timestamp()) | ||
218 | warnx("jvmti: kernel does not support %d clock id", perf_clk_id); | ||
219 | |||
220 | memset(&header, 0, sizeof(header)); | ||
221 | |||
222 | debug_cache_init(); | ||
223 | |||
224 | /* | ||
225 | * jitdump file name | ||
226 | */ | ||
227 | snprintf(dump_path, PATH_MAX, "%s/jit-%i.dump", jit_path, getpid()); | ||
228 | |||
229 | fd = open(dump_path, O_CREAT|O_TRUNC|O_RDWR, 0666); | ||
230 | if (fd == -1) | ||
231 | return NULL; | ||
232 | |||
233 | /* | ||
234 | * create perf.data maker for the jitdump file | ||
235 | */ | ||
236 | if (perf_open_marker_file(fd)) { | ||
237 | warnx("jvmti: failed to create marker file"); | ||
238 | return NULL; | ||
239 | } | ||
240 | |||
241 | fp = fdopen(fd, "w+"); | ||
242 | if (!fp) { | ||
243 | warn("jvmti: cannot create %s", dump_path); | ||
244 | close(fd); | ||
245 | goto error; | ||
246 | } | ||
247 | |||
248 | warnx("jvmti: jitdump in %s", dump_path); | ||
249 | |||
250 | if (get_e_machine(&header)) { | ||
251 | warn("get_e_machine failed\n"); | ||
252 | goto error; | ||
253 | } | ||
254 | |||
255 | header.magic = JITHEADER_MAGIC; | ||
256 | header.version = JITHEADER_VERSION; | ||
257 | header.total_size = sizeof(header); | ||
258 | header.pid = getpid(); | ||
259 | |||
260 | /* calculate amount of padding '\0' */ | ||
261 | pad_cnt = PADDING_8ALIGNED(header.total_size); | ||
262 | header.total_size += pad_cnt; | ||
263 | |||
264 | header.timestamp = perf_get_timestamp(); | ||
265 | |||
266 | if (!fwrite(&header, sizeof(header), 1, fp)) { | ||
267 | warn("jvmti: cannot write dumpfile header"); | ||
268 | goto error; | ||
269 | } | ||
270 | |||
271 | /* write padding '\0' if necessary */ | ||
272 | if (pad_cnt && !fwrite(pad_bytes, pad_cnt, 1, fp)) { | ||
273 | warn("jvmti: cannot write dumpfile header padding"); | ||
274 | goto error; | ||
275 | } | ||
276 | |||
277 | return fp; | ||
278 | error: | ||
279 | fclose(fp); | ||
280 | return NULL; | ||
281 | } | ||
282 | |||
283 | int | ||
284 | jvmti_close(void *agent) | ||
285 | { | ||
286 | struct jr_code_close rec; | ||
287 | FILE *fp = agent; | ||
288 | |||
289 | if (!fp) { | ||
290 | warnx("jvmti: incalid fd in close_agent"); | ||
291 | return -1; | ||
292 | } | ||
293 | |||
294 | rec.p.id = JIT_CODE_CLOSE; | ||
295 | rec.p.total_size = sizeof(rec); | ||
296 | |||
297 | rec.p.timestamp = perf_get_timestamp(); | ||
298 | |||
299 | if (!fwrite(&rec, sizeof(rec), 1, fp)) | ||
300 | return -1; | ||
301 | |||
302 | fclose(fp); | ||
303 | |||
304 | fp = NULL; | ||
305 | |||
306 | perf_close_marker_file(); | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | int | ||
312 | jvmti_write_code(void *agent, char const *sym, | ||
313 | uint64_t vma, void const *code, unsigned int const size) | ||
314 | { | ||
315 | static int code_generation = 1; | ||
316 | struct jr_code_load rec; | ||
317 | size_t sym_len; | ||
318 | size_t padding_count; | ||
319 | FILE *fp = agent; | ||
320 | int ret = -1; | ||
321 | |||
322 | /* don't care about 0 length function, no samples */ | ||
323 | if (size == 0) | ||
324 | return 0; | ||
325 | |||
326 | if (!fp) { | ||
327 | warnx("jvmti: invalid fd in write_native_code"); | ||
328 | return -1; | ||
329 | } | ||
330 | |||
331 | sym_len = strlen(sym) + 1; | ||
332 | |||
333 | rec.p.id = JIT_CODE_LOAD; | ||
334 | rec.p.total_size = sizeof(rec) + sym_len; | ||
335 | padding_count = PADDING_8ALIGNED(rec.p.total_size); | ||
336 | rec.p. total_size += padding_count; | ||
337 | rec.p.timestamp = perf_get_timestamp(); | ||
338 | |||
339 | rec.code_size = size; | ||
340 | rec.vma = vma; | ||
341 | rec.code_addr = vma; | ||
342 | rec.pid = getpid(); | ||
343 | rec.tid = gettid(); | ||
344 | |||
345 | if (code) | ||
346 | rec.p.total_size += size; | ||
347 | |||
348 | /* | ||
349 | * If JVM is multi-threaded, nultiple concurrent calls to agent | ||
350 | * may be possible, so protect file writes | ||
351 | */ | ||
352 | flockfile(fp); | ||
353 | |||
354 | /* | ||
355 | * get code index inside lock to avoid race condition | ||
356 | */ | ||
357 | rec.code_index = code_generation++; | ||
358 | |||
359 | ret = fwrite_unlocked(&rec, sizeof(rec), 1, fp); | ||
360 | fwrite_unlocked(sym, sym_len, 1, fp); | ||
361 | |||
362 | if (padding_count) | ||
363 | fwrite_unlocked(pad_bytes, padding_count, 1, fp); | ||
364 | |||
365 | if (code) | ||
366 | fwrite_unlocked(code, size, 1, fp); | ||
367 | |||
368 | funlockfile(fp); | ||
369 | |||
370 | ret = 0; | ||
371 | |||
372 | return ret; | ||
373 | } | ||
374 | |||
375 | int | ||
376 | jvmti_write_debug_info(void *agent, uint64_t code, const char *file, | ||
377 | jvmti_line_info_t *li, int nr_lines) | ||
378 | { | ||
379 | struct jr_code_debug_info rec; | ||
380 | size_t sret, len, size, flen; | ||
381 | size_t padding_count; | ||
382 | uint64_t addr; | ||
383 | const char *fn = file; | ||
384 | FILE *fp = agent; | ||
385 | int i; | ||
386 | |||
387 | /* | ||
388 | * no entry to write | ||
389 | */ | ||
390 | if (!nr_lines) | ||
391 | return 0; | ||
392 | |||
393 | if (!fp) { | ||
394 | warnx("jvmti: invalid fd in write_debug_info"); | ||
395 | return -1; | ||
396 | } | ||
397 | |||
398 | flen = strlen(file) + 1; | ||
399 | |||
400 | rec.p.id = JIT_CODE_DEBUG_INFO; | ||
401 | size = sizeof(rec); | ||
402 | rec.p.timestamp = perf_get_timestamp(); | ||
403 | rec.code_addr = (uint64_t)(uintptr_t)code; | ||
404 | rec.nr_entry = nr_lines; | ||
405 | |||
406 | /* | ||
407 | * on disk source line info layout: | ||
408 | * uint64_t : addr | ||
409 | * int : line number | ||
410 | * int : column discriminator | ||
411 | * file[] : source file name | ||
412 | * padding : pad to multiple of 8 bytes | ||
413 | */ | ||
414 | size += nr_lines * sizeof(struct debug_entry); | ||
415 | size += flen * nr_lines; | ||
416 | /* | ||
417 | * pad to 8 bytes | ||
418 | */ | ||
419 | padding_count = PADDING_8ALIGNED(size); | ||
420 | |||
421 | rec.p.total_size = size + padding_count; | ||
422 | |||
423 | /* | ||
424 | * If JVM is multi-threaded, nultiple concurrent calls to agent | ||
425 | * may be possible, so protect file writes | ||
426 | */ | ||
427 | flockfile(fp); | ||
428 | |||
429 | sret = fwrite_unlocked(&rec, sizeof(rec), 1, fp); | ||
430 | if (sret != 1) | ||
431 | goto error; | ||
432 | |||
433 | for (i = 0; i < nr_lines; i++) { | ||
434 | |||
435 | addr = (uint64_t)li[i].pc; | ||
436 | len = sizeof(addr); | ||
437 | sret = fwrite_unlocked(&addr, len, 1, fp); | ||
438 | if (sret != 1) | ||
439 | goto error; | ||
440 | |||
441 | len = sizeof(li[0].line_number); | ||
442 | sret = fwrite_unlocked(&li[i].line_number, len, 1, fp); | ||
443 | if (sret != 1) | ||
444 | goto error; | ||
445 | |||
446 | len = sizeof(li[0].discrim); | ||
447 | sret = fwrite_unlocked(&li[i].discrim, len, 1, fp); | ||
448 | if (sret != 1) | ||
449 | goto error; | ||
450 | |||
451 | sret = fwrite_unlocked(fn, flen, 1, fp); | ||
452 | if (sret != 1) | ||
453 | goto error; | ||
454 | } | ||
455 | if (padding_count) | ||
456 | sret = fwrite_unlocked(pad_bytes, padding_count, 1, fp); | ||
457 | if (sret != 1) | ||
458 | goto error; | ||
459 | |||
460 | funlockfile(fp); | ||
461 | return 0; | ||
462 | error: | ||
463 | funlockfile(fp); | ||
464 | return -1; | ||
465 | } | ||
diff --git a/tools/perf/jvmti/jvmti_agent.h b/tools/perf/jvmti/jvmti_agent.h new file mode 100644 index 000000000000..bedf5d0ba9ff --- /dev/null +++ b/tools/perf/jvmti/jvmti_agent.h | |||
@@ -0,0 +1,36 @@ | |||
1 | #ifndef __JVMTI_AGENT_H__ | ||
2 | #define __JVMTI_AGENT_H__ | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | #include <stdint.h> | ||
6 | #include <jvmti.h> | ||
7 | |||
8 | #define __unused __attribute__((unused)) | ||
9 | |||
10 | #if defined(__cplusplus) | ||
11 | extern "C" { | ||
12 | #endif | ||
13 | |||
14 | typedef struct { | ||
15 | unsigned long pc; | ||
16 | int line_number; | ||
17 | int discrim; /* discriminator -- 0 for now */ | ||
18 | } jvmti_line_info_t; | ||
19 | |||
20 | void *jvmti_open(void); | ||
21 | int jvmti_close(void *agent); | ||
22 | int jvmti_write_code(void *agent, char const *symbol_name, | ||
23 | uint64_t vma, void const *code, | ||
24 | const unsigned int code_size); | ||
25 | |||
26 | int jvmti_write_debug_info(void *agent, | ||
27 | uint64_t code, | ||
28 | const char *file, | ||
29 | jvmti_line_info_t *li, | ||
30 | int nr_lines); | ||
31 | |||
32 | #if defined(__cplusplus) | ||
33 | } | ||
34 | |||
35 | #endif | ||
36 | #endif /* __JVMTI_H__ */ | ||
diff --git a/tools/perf/jvmti/libjvmti.c b/tools/perf/jvmti/libjvmti.c new file mode 100644 index 000000000000..ac12e4b91a92 --- /dev/null +++ b/tools/perf/jvmti/libjvmti.c | |||
@@ -0,0 +1,304 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <stdio.h> | ||
3 | #include <string.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <err.h> | ||
6 | #include <jvmti.h> | ||
7 | #include <jvmticmlr.h> | ||
8 | #include <limits.h> | ||
9 | |||
10 | #include "jvmti_agent.h" | ||
11 | |||
12 | static int has_line_numbers; | ||
13 | void *jvmti_agent; | ||
14 | |||
15 | static jvmtiError | ||
16 | do_get_line_numbers(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci, | ||
17 | jvmti_line_info_t *tab, jint *nr) | ||
18 | { | ||
19 | jint i, lines = 0; | ||
20 | jint nr_lines = 0; | ||
21 | jvmtiLineNumberEntry *loc_tab = NULL; | ||
22 | jvmtiError ret; | ||
23 | |||
24 | ret = (*jvmti)->GetLineNumberTable(jvmti, m, &nr_lines, &loc_tab); | ||
25 | if (ret != JVMTI_ERROR_NONE) | ||
26 | return ret; | ||
27 | |||
28 | for (i = 0; i < nr_lines; i++) { | ||
29 | if (loc_tab[i].start_location < bci) { | ||
30 | tab[lines].pc = (unsigned long)pc; | ||
31 | tab[lines].line_number = loc_tab[i].line_number; | ||
32 | tab[lines].discrim = 0; /* not yet used */ | ||
33 | lines++; | ||
34 | } else { | ||
35 | break; | ||
36 | } | ||
37 | } | ||
38 | (*jvmti)->Deallocate(jvmti, (unsigned char *)loc_tab); | ||
39 | *nr = lines; | ||
40 | return JVMTI_ERROR_NONE; | ||
41 | } | ||
42 | |||
43 | static jvmtiError | ||
44 | get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **tab, int *nr_lines) | ||
45 | { | ||
46 | const jvmtiCompiledMethodLoadRecordHeader *hdr; | ||
47 | jvmtiCompiledMethodLoadInlineRecord *rec; | ||
48 | jvmtiLineNumberEntry *lne = NULL; | ||
49 | PCStackInfo *c; | ||
50 | jint nr, ret; | ||
51 | int nr_total = 0; | ||
52 | int i, lines_total = 0; | ||
53 | |||
54 | if (!(tab && nr_lines)) | ||
55 | return JVMTI_ERROR_NULL_POINTER; | ||
56 | |||
57 | /* | ||
58 | * Phase 1 -- get the number of lines necessary | ||
59 | */ | ||
60 | for (hdr = compile_info; hdr != NULL; hdr = hdr->next) { | ||
61 | if (hdr->kind == JVMTI_CMLR_INLINE_INFO) { | ||
62 | rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr; | ||
63 | for (i = 0; i < rec->numpcs; i++) { | ||
64 | c = rec->pcinfo + i; | ||
65 | nr = 0; | ||
66 | /* | ||
67 | * unfortunately, need a tab to get the number of lines! | ||
68 | */ | ||
69 | ret = (*jvmti)->GetLineNumberTable(jvmti, c->methods[0], &nr, &lne); | ||
70 | if (ret == JVMTI_ERROR_NONE) { | ||
71 | /* free what was allocated for nothing */ | ||
72 | (*jvmti)->Deallocate(jvmti, (unsigned char *)lne); | ||
73 | nr_total += (int)nr; | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | if (nr_total == 0) | ||
80 | return JVMTI_ERROR_NOT_FOUND; | ||
81 | |||
82 | /* | ||
83 | * Phase 2 -- allocate big enough line table | ||
84 | */ | ||
85 | *tab = malloc(nr_total * sizeof(**tab)); | ||
86 | if (!*tab) | ||
87 | return JVMTI_ERROR_OUT_OF_MEMORY; | ||
88 | |||
89 | for (hdr = compile_info; hdr != NULL; hdr = hdr->next) { | ||
90 | if (hdr->kind == JVMTI_CMLR_INLINE_INFO) { | ||
91 | rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr; | ||
92 | for (i = 0; i < rec->numpcs; i++) { | ||
93 | c = rec->pcinfo + i; | ||
94 | nr = 0; | ||
95 | ret = do_get_line_numbers(jvmti, c->pc, | ||
96 | c->methods[0], | ||
97 | c->bcis[0], | ||
98 | *tab + lines_total, | ||
99 | &nr); | ||
100 | if (ret == JVMTI_ERROR_NONE) | ||
101 | lines_total += nr; | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | *nr_lines = lines_total; | ||
106 | return JVMTI_ERROR_NONE; | ||
107 | } | ||
108 | |||
109 | static void JNICALL | ||
110 | compiled_method_load_cb(jvmtiEnv *jvmti, | ||
111 | jmethodID method, | ||
112 | jint code_size, | ||
113 | void const *code_addr, | ||
114 | jint map_length, | ||
115 | jvmtiAddrLocationMap const *map, | ||
116 | const void *compile_info) | ||
117 | { | ||
118 | jvmti_line_info_t *line_tab = NULL; | ||
119 | jclass decl_class; | ||
120 | char *class_sign = NULL; | ||
121 | char *func_name = NULL; | ||
122 | char *func_sign = NULL; | ||
123 | char *file_name= NULL; | ||
124 | char fn[PATH_MAX]; | ||
125 | uint64_t addr = (uint64_t)(uintptr_t)code_addr; | ||
126 | jvmtiError ret; | ||
127 | int nr_lines = 0; /* in line_tab[] */ | ||
128 | size_t len; | ||
129 | |||
130 | ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method, | ||
131 | &decl_class); | ||
132 | if (ret != JVMTI_ERROR_NONE) { | ||
133 | warnx("jvmti: cannot get declaring class"); | ||
134 | return; | ||
135 | } | ||
136 | |||
137 | if (has_line_numbers && map && map_length) { | ||
138 | ret = get_line_numbers(jvmti, compile_info, &line_tab, &nr_lines); | ||
139 | if (ret != JVMTI_ERROR_NONE) { | ||
140 | warnx("jvmti: cannot get line table for method"); | ||
141 | nr_lines = 0; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name); | ||
146 | if (ret != JVMTI_ERROR_NONE) { | ||
147 | warnx("jvmti: cannot get source filename ret=%d", ret); | ||
148 | goto error; | ||
149 | } | ||
150 | |||
151 | ret = (*jvmti)->GetClassSignature(jvmti, decl_class, | ||
152 | &class_sign, NULL); | ||
153 | if (ret != JVMTI_ERROR_NONE) { | ||
154 | warnx("jvmti: getclassignature failed"); | ||
155 | goto error; | ||
156 | } | ||
157 | |||
158 | ret = (*jvmti)->GetMethodName(jvmti, method, &func_name, | ||
159 | &func_sign, NULL); | ||
160 | if (ret != JVMTI_ERROR_NONE) { | ||
161 | warnx("jvmti: failed getmethodname"); | ||
162 | goto error; | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * Assume path name is class hierarchy, this is a common practice with Java programs | ||
167 | */ | ||
168 | if (*class_sign == 'L') { | ||
169 | int j, i = 0; | ||
170 | char *p = strrchr(class_sign, '/'); | ||
171 | if (p) { | ||
172 | /* drop the 'L' prefix and copy up to the final '/' */ | ||
173 | for (i = 0; i < (p - class_sign); i++) | ||
174 | fn[i] = class_sign[i+1]; | ||
175 | } | ||
176 | /* | ||
177 | * append file name, we use loops and not string ops to avoid modifying | ||
178 | * class_sign which is used later for the symbol name | ||
179 | */ | ||
180 | for (j = 0; i < (PATH_MAX - 1) && file_name && j < strlen(file_name); j++, i++) | ||
181 | fn[i] = file_name[j]; | ||
182 | fn[i] = '\0'; | ||
183 | } else { | ||
184 | /* fallback case */ | ||
185 | strcpy(fn, file_name); | ||
186 | } | ||
187 | /* | ||
188 | * write source line info record if we have it | ||
189 | */ | ||
190 | if (jvmti_write_debug_info(jvmti_agent, addr, fn, line_tab, nr_lines)) | ||
191 | warnx("jvmti: write_debug_info() failed"); | ||
192 | |||
193 | len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2; | ||
194 | { | ||
195 | char str[len]; | ||
196 | snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign); | ||
197 | |||
198 | if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size)) | ||
199 | warnx("jvmti: write_code() failed"); | ||
200 | } | ||
201 | error: | ||
202 | (*jvmti)->Deallocate(jvmti, (unsigned char *)func_name); | ||
203 | (*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign); | ||
204 | (*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign); | ||
205 | (*jvmti)->Deallocate(jvmti, (unsigned char *)file_name); | ||
206 | free(line_tab); | ||
207 | } | ||
208 | |||
209 | static void JNICALL | ||
210 | code_generated_cb(jvmtiEnv *jvmti, | ||
211 | char const *name, | ||
212 | void const *code_addr, | ||
213 | jint code_size) | ||
214 | { | ||
215 | uint64_t addr = (uint64_t)(unsigned long)code_addr; | ||
216 | int ret; | ||
217 | |||
218 | ret = jvmti_write_code(jvmti_agent, name, addr, code_addr, code_size); | ||
219 | if (ret) | ||
220 | warnx("jvmti: write_code() failed for code_generated"); | ||
221 | } | ||
222 | |||
223 | JNIEXPORT jint JNICALL | ||
224 | Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __unused) | ||
225 | { | ||
226 | jvmtiEventCallbacks cb; | ||
227 | jvmtiCapabilities caps1; | ||
228 | jvmtiJlocationFormat format; | ||
229 | jvmtiEnv *jvmti = NULL; | ||
230 | jint ret; | ||
231 | |||
232 | jvmti_agent = jvmti_open(); | ||
233 | if (!jvmti_agent) { | ||
234 | warnx("jvmti: open_agent failed"); | ||
235 | return -1; | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | * Request a JVMTI interface version 1 environment | ||
240 | */ | ||
241 | ret = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1); | ||
242 | if (ret != JNI_OK) { | ||
243 | warnx("jvmti: jvmti version 1 not supported"); | ||
244 | return -1; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * acquire method_load capability, we require it | ||
249 | * request line numbers (optional) | ||
250 | */ | ||
251 | memset(&caps1, 0, sizeof(caps1)); | ||
252 | caps1.can_generate_compiled_method_load_events = 1; | ||
253 | |||
254 | ret = (*jvmti)->AddCapabilities(jvmti, &caps1); | ||
255 | if (ret != JVMTI_ERROR_NONE) { | ||
256 | warnx("jvmti: acquire compiled_method capability failed"); | ||
257 | return -1; | ||
258 | } | ||
259 | ret = (*jvmti)->GetJLocationFormat(jvmti, &format); | ||
260 | if (ret == JVMTI_ERROR_NONE && format == JVMTI_JLOCATION_JVMBCI) { | ||
261 | memset(&caps1, 0, sizeof(caps1)); | ||
262 | caps1.can_get_line_numbers = 1; | ||
263 | caps1.can_get_source_file_name = 1; | ||
264 | ret = (*jvmti)->AddCapabilities(jvmti, &caps1); | ||
265 | if (ret == JVMTI_ERROR_NONE) | ||
266 | has_line_numbers = 1; | ||
267 | } | ||
268 | |||
269 | memset(&cb, 0, sizeof(cb)); | ||
270 | |||
271 | cb.CompiledMethodLoad = compiled_method_load_cb; | ||
272 | cb.DynamicCodeGenerated = code_generated_cb; | ||
273 | |||
274 | ret = (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof(cb)); | ||
275 | if (ret != JVMTI_ERROR_NONE) { | ||
276 | warnx("jvmti: cannot set event callbacks"); | ||
277 | return -1; | ||
278 | } | ||
279 | |||
280 | ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, | ||
281 | JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); | ||
282 | if (ret != JVMTI_ERROR_NONE) { | ||
283 | warnx("jvmti: setnotification failed for method_load"); | ||
284 | return -1; | ||
285 | } | ||
286 | |||
287 | ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, | ||
288 | JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL); | ||
289 | if (ret != JVMTI_ERROR_NONE) { | ||
290 | warnx("jvmti: setnotification failed on code_generated"); | ||
291 | return -1; | ||
292 | } | ||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | JNIEXPORT void JNICALL | ||
297 | Agent_OnUnload(JavaVM *jvm __unused) | ||
298 | { | ||
299 | int ret; | ||
300 | |||
301 | ret = jvmti_close(jvmti_agent); | ||
302 | if (ret) | ||
303 | errx(1, "Error: op_close_agent()"); | ||
304 | } | ||
diff --git a/tools/perf/tests/make b/tools/perf/tests/make index cc72b67bde5e..cac15d93aea6 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make | |||
@@ -80,6 +80,7 @@ make_no_libaudit := NO_LIBAUDIT=1 | |||
80 | make_no_libbionic := NO_LIBBIONIC=1 | 80 | make_no_libbionic := NO_LIBBIONIC=1 |
81 | make_no_auxtrace := NO_AUXTRACE=1 | 81 | make_no_auxtrace := NO_AUXTRACE=1 |
82 | make_no_libbpf := NO_LIBBPF=1 | 82 | make_no_libbpf := NO_LIBBPF=1 |
83 | make_no_libcrypto := NO_LIBCRYPTO=1 | ||
83 | make_tags := tags | 84 | make_tags := tags |
84 | make_cscope := cscope | 85 | make_cscope := cscope |
85 | make_help := help | 86 | make_help := help |
@@ -103,6 +104,7 @@ make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1 | |||
103 | make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 | 104 | make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 |
104 | make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 | 105 | make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 |
105 | make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1 | 106 | make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1 |
107 | make_minimal += NO_LIBCRYPTO=1 | ||
106 | 108 | ||
107 | # $(run) contains all available tests | 109 | # $(run) contains all available tests |
108 | run := make_pure | 110 | run := make_pure |
@@ -111,6 +113,9 @@ run := make_pure | |||
111 | # disable features detection | 113 | # disable features detection |
112 | ifeq ($(MK),Makefile) | 114 | ifeq ($(MK),Makefile) |
113 | run += make_clean_all | 115 | run += make_clean_all |
116 | MAKE_F := $(MAKE) | ||
117 | else | ||
118 | MAKE_F := $(MAKE) -f $(MK) | ||
114 | endif | 119 | endif |
115 | run += make_python_perf_so | 120 | run += make_python_perf_so |
116 | run += make_debug | 121 | run += make_debug |
@@ -270,12 +275,12 @@ endif | |||
270 | 275 | ||
271 | MAKEFLAGS := --no-print-directory | 276 | MAKEFLAGS := --no-print-directory |
272 | 277 | ||
273 | clean := @(cd $(PERF); make -s -f $(MK) $(O_OPT) clean >/dev/null) | 278 | clean := @(cd $(PERF); $(MAKE_F) -s $(O_OPT) clean >/dev/null) |
274 | 279 | ||
275 | $(run): | 280 | $(run): |
276 | $(call clean) | 281 | $(call clean) |
277 | @TMP_DEST=$$(mktemp -d); \ | 282 | @TMP_DEST=$$(mktemp -d); \ |
278 | cmd="cd $(PERF) && make -f $(MK) $(PARALLEL_OPT) $(O_OPT) DESTDIR=$$TMP_DEST $($@)"; \ | 283 | cmd="cd $(PERF) && $(MAKE_F) $($@) $(PARALLEL_OPT) $(O_OPT) DESTDIR=$$TMP_DEST"; \ |
279 | printf "%*.*s: %s\n" $(max_width) $(max_width) "$@" "$$cmd" && echo $$cmd > $@ && \ | 284 | printf "%*.*s: %s\n" $(max_width) $(max_width) "$@" "$$cmd" && echo $$cmd > $@ && \ |
280 | ( eval $$cmd ) >> $@ 2>&1; \ | 285 | ( eval $$cmd ) >> $@ 2>&1; \ |
281 | echo " test: $(call test,$@)" >> $@ 2>&1; \ | 286 | echo " test: $(call test,$@)" >> $@ 2>&1; \ |
@@ -286,7 +291,7 @@ $(run_O): | |||
286 | $(call clean) | 291 | $(call clean) |
287 | @TMP_O=$$(mktemp -d); \ | 292 | @TMP_O=$$(mktemp -d); \ |
288 | TMP_DEST=$$(mktemp -d); \ | 293 | TMP_DEST=$$(mktemp -d); \ |
289 | cmd="cd $(PERF) && make -f $(MK) $(PARALLEL_OPT) O=$$TMP_O DESTDIR=$$TMP_DEST $($(patsubst %_O,%,$@))"; \ | 294 | cmd="cd $(PERF) && $(MAKE_F) $($(patsubst %_O,%,$@)) $(PARALLEL_OPT) O=$$TMP_O DESTDIR=$$TMP_DEST"; \ |
290 | printf "%*.*s: %s\n" $(max_width) $(max_width) "$@" "$$cmd" && echo $$cmd > $@ && \ | 295 | printf "%*.*s: %s\n" $(max_width) $(max_width) "$@" "$$cmd" && echo $$cmd > $@ && \ |
291 | ( eval $$cmd ) >> $@ 2>&1 && \ | 296 | ( eval $$cmd ) >> $@ 2>&1 && \ |
292 | echo " test: $(call test_O,$@)" >> $@ 2>&1; \ | 297 | echo " test: $(call test_O,$@)" >> $@ 2>&1; \ |
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 5eec53a3f4ac..a34752d28488 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
@@ -105,8 +105,14 @@ libperf-y += scripting-engines/ | |||
105 | 105 | ||
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 | ||
109 | libperf-$(CONFIG_LIBELF) += jitdump.o | ||
110 | libperf-$(CONFIG_LIBELF) += genelf.o | ||
111 | libperf-$(CONFIG_LIBELF) += genelf_debug.o | ||
108 | 112 | ||
109 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" | 113 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" |
114 | # avoid compiler warnings in 32-bit mode | ||
115 | CFLAGS_genelf_debug.o += -Wno-packed | ||
110 | 116 | ||
111 | $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c | 117 | $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c |
112 | $(call rule_mkdir) | 118 | $(call rule_mkdir) |
diff --git a/tools/perf/util/demangle-java.c b/tools/perf/util/demangle-java.c new file mode 100644 index 000000000000..3e6062ab2cdd --- /dev/null +++ b/tools/perf/util/demangle-java.c | |||
@@ -0,0 +1,199 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <stdio.h> | ||
3 | #include <string.h> | ||
4 | #include "util.h" | ||
5 | #include "debug.h" | ||
6 | #include "symbol.h" | ||
7 | |||
8 | #include "demangle-java.h" | ||
9 | |||
10 | enum { | ||
11 | MODE_PREFIX = 0, | ||
12 | MODE_CLASS = 1, | ||
13 | MODE_FUNC = 2, | ||
14 | MODE_TYPE = 3, | ||
15 | MODE_CTYPE = 3, /* class arg */ | ||
16 | }; | ||
17 | |||
18 | #define BASE_ENT(c, n) [c - 'A']=n | ||
19 | static const char *base_types['Z' - 'A' + 1] = { | ||
20 | BASE_ENT('B', "byte" ), | ||
21 | BASE_ENT('C', "char" ), | ||
22 | BASE_ENT('D', "double" ), | ||
23 | BASE_ENT('F', "float" ), | ||
24 | BASE_ENT('I', "int" ), | ||
25 | BASE_ENT('J', "long" ), | ||
26 | BASE_ENT('S', "short" ), | ||
27 | BASE_ENT('Z', "bool" ), | ||
28 | }; | ||
29 | |||
30 | /* | ||
31 | * demangle Java symbol between str and end positions and stores | ||
32 | * up to maxlen characters into buf. The parser starts in mode. | ||
33 | * | ||
34 | * Use MODE_PREFIX to process entire prototype till end position | ||
35 | * Use MODE_TYPE to process return type if str starts on return type char | ||
36 | * | ||
37 | * Return: | ||
38 | * success: buf | ||
39 | * error : NULL | ||
40 | */ | ||
41 | static char * | ||
42 | __demangle_java_sym(const char *str, const char *end, char *buf, int maxlen, int mode) | ||
43 | { | ||
44 | int rlen = 0; | ||
45 | int array = 0; | ||
46 | int narg = 0; | ||
47 | const char *q; | ||
48 | |||
49 | if (!end) | ||
50 | end = str + strlen(str); | ||
51 | |||
52 | for (q = str; q != end; q++) { | ||
53 | |||
54 | if (rlen == (maxlen - 1)) | ||
55 | break; | ||
56 | |||
57 | switch (*q) { | ||
58 | case 'L': | ||
59 | if (mode == MODE_PREFIX || mode == MODE_CTYPE) { | ||
60 | if (mode == MODE_CTYPE) { | ||
61 | if (narg) | ||
62 | rlen += scnprintf(buf + rlen, maxlen - rlen, ", "); | ||
63 | narg++; | ||
64 | } | ||
65 | rlen += scnprintf(buf + rlen, maxlen - rlen, "class "); | ||
66 | if (mode == MODE_PREFIX) | ||
67 | mode = MODE_CLASS; | ||
68 | } else | ||
69 | buf[rlen++] = *q; | ||
70 | break; | ||
71 | case 'B': | ||
72 | case 'C': | ||
73 | case 'D': | ||
74 | case 'F': | ||
75 | case 'I': | ||
76 | case 'J': | ||
77 | case 'S': | ||
78 | case 'Z': | ||
79 | if (mode == MODE_TYPE) { | ||
80 | if (narg) | ||
81 | rlen += scnprintf(buf + rlen, maxlen - rlen, ", "); | ||
82 | rlen += scnprintf(buf + rlen, maxlen - rlen, "%s", base_types[*q - 'A']); | ||
83 | while (array--) | ||
84 | rlen += scnprintf(buf + rlen, maxlen - rlen, "[]"); | ||
85 | array = 0; | ||
86 | narg++; | ||
87 | } else | ||
88 | buf[rlen++] = *q; | ||
89 | break; | ||
90 | case 'V': | ||
91 | if (mode == MODE_TYPE) { | ||
92 | rlen += scnprintf(buf + rlen, maxlen - rlen, "void"); | ||
93 | while (array--) | ||
94 | rlen += scnprintf(buf + rlen, maxlen - rlen, "[]"); | ||
95 | array = 0; | ||
96 | } else | ||
97 | buf[rlen++] = *q; | ||
98 | break; | ||
99 | case '[': | ||
100 | if (mode != MODE_TYPE) | ||
101 | goto error; | ||
102 | array++; | ||
103 | break; | ||
104 | case '(': | ||
105 | if (mode != MODE_FUNC) | ||
106 | goto error; | ||
107 | buf[rlen++] = *q; | ||
108 | mode = MODE_TYPE; | ||
109 | break; | ||
110 | case ')': | ||
111 | if (mode != MODE_TYPE) | ||
112 | goto error; | ||
113 | buf[rlen++] = *q; | ||
114 | narg = 0; | ||
115 | break; | ||
116 | case ';': | ||
117 | if (mode != MODE_CLASS && mode != MODE_CTYPE) | ||
118 | goto error; | ||
119 | /* safe because at least one other char to process */ | ||
120 | if (isalpha(*(q + 1))) | ||
121 | rlen += scnprintf(buf + rlen, maxlen - rlen, "."); | ||
122 | if (mode == MODE_CLASS) | ||
123 | mode = MODE_FUNC; | ||
124 | else if (mode == MODE_CTYPE) | ||
125 | mode = MODE_TYPE; | ||
126 | break; | ||
127 | case '/': | ||
128 | if (mode != MODE_CLASS && mode != MODE_CTYPE) | ||
129 | goto error; | ||
130 | rlen += scnprintf(buf + rlen, maxlen - rlen, "."); | ||
131 | break; | ||
132 | default : | ||
133 | buf[rlen++] = *q; | ||
134 | } | ||
135 | } | ||
136 | buf[rlen] = '\0'; | ||
137 | return buf; | ||
138 | error: | ||
139 | return NULL; | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Demangle Java function signature (openJDK, not GCJ) | ||
144 | * input: | ||
145 | * str: string to parse. String is not modified | ||
146 | * flags: comobination of JAVA_DEMANGLE_* flags to modify demangling | ||
147 | * return: | ||
148 | * if input can be demangled, then a newly allocated string is returned. | ||
149 | * if input cannot be demangled, then NULL is returned | ||
150 | * | ||
151 | * Note: caller is responsible for freeing demangled string | ||
152 | */ | ||
153 | char * | ||
154 | java_demangle_sym(const char *str, int flags) | ||
155 | { | ||
156 | char *buf, *ptr; | ||
157 | char *p; | ||
158 | size_t len, l1 = 0; | ||
159 | |||
160 | if (!str) | ||
161 | return NULL; | ||
162 | |||
163 | /* find start of retunr type */ | ||
164 | p = strrchr(str, ')'); | ||
165 | if (!p) | ||
166 | return NULL; | ||
167 | |||
168 | /* | ||
169 | * expansion factor estimated to 3x | ||
170 | */ | ||
171 | len = strlen(str) * 3 + 1; | ||
172 | buf = malloc(len); | ||
173 | if (!buf) | ||
174 | return NULL; | ||
175 | |||
176 | buf[0] = '\0'; | ||
177 | if (!(flags & JAVA_DEMANGLE_NORET)) { | ||
178 | /* | ||
179 | * get return type first | ||
180 | */ | ||
181 | ptr = __demangle_java_sym(p + 1, NULL, buf, len, MODE_TYPE); | ||
182 | if (!ptr) | ||
183 | goto error; | ||
184 | |||
185 | /* add space between return type and function prototype */ | ||
186 | l1 = strlen(buf); | ||
187 | buf[l1++] = ' '; | ||
188 | } | ||
189 | |||
190 | /* process function up to return type */ | ||
191 | ptr = __demangle_java_sym(str, p + 1, buf + l1, len - l1, MODE_PREFIX); | ||
192 | if (!ptr) | ||
193 | goto error; | ||
194 | |||
195 | return buf; | ||
196 | error: | ||
197 | free(buf); | ||
198 | return NULL; | ||
199 | } | ||
diff --git a/tools/perf/util/demangle-java.h b/tools/perf/util/demangle-java.h new file mode 100644 index 000000000000..a981c1f968fe --- /dev/null +++ b/tools/perf/util/demangle-java.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #ifndef __PERF_DEMANGLE_JAVA | ||
2 | #define __PERF_DEMANGLE_JAVA 1 | ||
3 | /* | ||
4 | * demangle function flags | ||
5 | */ | ||
6 | #define JAVA_DEMANGLE_NORET 0x1 /* do not process return type */ | ||
7 | |||
8 | char * java_demangle_sym(const char *str, int flags); | ||
9 | |||
10 | #endif /* __PERF_DEMANGLE_JAVA */ | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 85155e91b61b..7bad5c3fa7b7 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -282,7 +282,7 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
282 | strcpy(execname, ""); | 282 | strcpy(execname, ""); |
283 | 283 | ||
284 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | 284 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ |
285 | n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n", | 285 | n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %[^\n]\n", |
286 | &event->mmap2.start, &event->mmap2.len, prot, | 286 | &event->mmap2.start, &event->mmap2.len, prot, |
287 | &event->mmap2.pgoff, &event->mmap2.maj, | 287 | &event->mmap2.pgoff, &event->mmap2.maj, |
288 | &event->mmap2.min, | 288 | &event->mmap2.min, |
diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c new file mode 100644 index 000000000000..c1ef805c6a8f --- /dev/null +++ b/tools/perf/util/genelf.c | |||
@@ -0,0 +1,449 @@ | |||
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 | void *debug, int nr_debug_entries) | ||
161 | { | ||
162 | Elf *e; | ||
163 | Elf_Data *d; | ||
164 | Elf_Scn *scn; | ||
165 | Elf_Ehdr *ehdr; | ||
166 | Elf_Shdr *shdr; | ||
167 | char *strsym = NULL; | ||
168 | int symlen; | ||
169 | int retval = -1; | ||
170 | |||
171 | if (elf_version(EV_CURRENT) == EV_NONE) { | ||
172 | warnx("ELF initialization failed"); | ||
173 | return -1; | ||
174 | } | ||
175 | |||
176 | e = elf_begin(fd, ELF_C_WRITE, NULL); | ||
177 | if (!e) { | ||
178 | warnx("elf_begin failed"); | ||
179 | goto error; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * setup ELF header | ||
184 | */ | ||
185 | ehdr = elf_newehdr(e); | ||
186 | if (!ehdr) { | ||
187 | warnx("cannot get ehdr"); | ||
188 | goto error; | ||
189 | } | ||
190 | |||
191 | ehdr->e_ident[EI_DATA] = GEN_ELF_ENDIAN; | ||
192 | ehdr->e_ident[EI_CLASS] = GEN_ELF_CLASS; | ||
193 | ehdr->e_machine = GEN_ELF_ARCH; | ||
194 | ehdr->e_type = ET_DYN; | ||
195 | ehdr->e_entry = GEN_ELF_TEXT_OFFSET; | ||
196 | ehdr->e_version = EV_CURRENT; | ||
197 | ehdr->e_shstrndx= 2; /* shdr index for section name */ | ||
198 | |||
199 | /* | ||
200 | * setup text section | ||
201 | */ | ||
202 | scn = elf_newscn(e); | ||
203 | if (!scn) { | ||
204 | warnx("cannot create section"); | ||
205 | goto error; | ||
206 | } | ||
207 | |||
208 | d = elf_newdata(scn); | ||
209 | if (!d) { | ||
210 | warnx("cannot get new data"); | ||
211 | goto error; | ||
212 | } | ||
213 | |||
214 | d->d_align = 16; | ||
215 | d->d_off = 0LL; | ||
216 | d->d_buf = (void *)code; | ||
217 | d->d_type = ELF_T_BYTE; | ||
218 | d->d_size = csize; | ||
219 | d->d_version = EV_CURRENT; | ||
220 | |||
221 | shdr = elf_getshdr(scn); | ||
222 | if (!shdr) { | ||
223 | warnx("cannot get section header"); | ||
224 | goto error; | ||
225 | } | ||
226 | |||
227 | shdr->sh_name = 1; | ||
228 | shdr->sh_type = SHT_PROGBITS; | ||
229 | shdr->sh_addr = GEN_ELF_TEXT_OFFSET; | ||
230 | shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC; | ||
231 | shdr->sh_entsize = 0; | ||
232 | |||
233 | /* | ||
234 | * setup section headers string table | ||
235 | */ | ||
236 | scn = elf_newscn(e); | ||
237 | if (!scn) { | ||
238 | warnx("cannot create section"); | ||
239 | goto error; | ||
240 | } | ||
241 | |||
242 | d = elf_newdata(scn); | ||
243 | if (!d) { | ||
244 | warnx("cannot get new data"); | ||
245 | goto error; | ||
246 | } | ||
247 | |||
248 | d->d_align = 1; | ||
249 | d->d_off = 0LL; | ||
250 | d->d_buf = shd_string_table; | ||
251 | d->d_type = ELF_T_BYTE; | ||
252 | d->d_size = sizeof(shd_string_table); | ||
253 | d->d_version = EV_CURRENT; | ||
254 | |||
255 | shdr = elf_getshdr(scn); | ||
256 | if (!shdr) { | ||
257 | warnx("cannot get section header"); | ||
258 | goto error; | ||
259 | } | ||
260 | |||
261 | shdr->sh_name = 7; /* offset of '.shstrtab' in shd_string_table */ | ||
262 | shdr->sh_type = SHT_STRTAB; | ||
263 | shdr->sh_flags = 0; | ||
264 | shdr->sh_entsize = 0; | ||
265 | |||
266 | /* | ||
267 | * setup symtab section | ||
268 | */ | ||
269 | symtab[1].st_size = csize; | ||
270 | symtab[1].st_value = GEN_ELF_TEXT_OFFSET; | ||
271 | |||
272 | scn = elf_newscn(e); | ||
273 | if (!scn) { | ||
274 | warnx("cannot create section"); | ||
275 | goto error; | ||
276 | } | ||
277 | |||
278 | d = elf_newdata(scn); | ||
279 | if (!d) { | ||
280 | warnx("cannot get new data"); | ||
281 | goto error; | ||
282 | } | ||
283 | |||
284 | d->d_align = 8; | ||
285 | d->d_off = 0LL; | ||
286 | d->d_buf = symtab; | ||
287 | d->d_type = ELF_T_SYM; | ||
288 | d->d_size = sizeof(symtab); | ||
289 | d->d_version = EV_CURRENT; | ||
290 | |||
291 | shdr = elf_getshdr(scn); | ||
292 | if (!shdr) { | ||
293 | warnx("cannot get section header"); | ||
294 | goto error; | ||
295 | } | ||
296 | |||
297 | shdr->sh_name = 17; /* offset of '.symtab' in shd_string_table */ | ||
298 | shdr->sh_type = SHT_SYMTAB; | ||
299 | shdr->sh_flags = 0; | ||
300 | shdr->sh_entsize = sizeof(Elf_Sym); | ||
301 | shdr->sh_link = 4; /* index of .strtab section */ | ||
302 | |||
303 | /* | ||
304 | * setup symbols string table | ||
305 | * 2 = 1 for 0 in 1st entry, 1 for the 0 at end of symbol for 2nd entry | ||
306 | */ | ||
307 | symlen = 2 + strlen(sym); | ||
308 | strsym = calloc(1, symlen); | ||
309 | if (!strsym) { | ||
310 | warnx("cannot allocate strsym"); | ||
311 | goto error; | ||
312 | } | ||
313 | strcpy(strsym + 1, sym); | ||
314 | |||
315 | scn = elf_newscn(e); | ||
316 | if (!scn) { | ||
317 | warnx("cannot create section"); | ||
318 | goto error; | ||
319 | } | ||
320 | |||
321 | d = elf_newdata(scn); | ||
322 | if (!d) { | ||
323 | warnx("cannot get new data"); | ||
324 | goto error; | ||
325 | } | ||
326 | |||
327 | d->d_align = 1; | ||
328 | d->d_off = 0LL; | ||
329 | d->d_buf = strsym; | ||
330 | d->d_type = ELF_T_BYTE; | ||
331 | d->d_size = symlen; | ||
332 | d->d_version = EV_CURRENT; | ||
333 | |||
334 | shdr = elf_getshdr(scn); | ||
335 | if (!shdr) { | ||
336 | warnx("cannot get section header"); | ||
337 | goto error; | ||
338 | } | ||
339 | |||
340 | shdr->sh_name = 25; /* offset in shd_string_table */ | ||
341 | shdr->sh_type = SHT_STRTAB; | ||
342 | shdr->sh_flags = 0; | ||
343 | shdr->sh_entsize = 0; | ||
344 | |||
345 | /* | ||
346 | * setup build-id section | ||
347 | */ | ||
348 | scn = elf_newscn(e); | ||
349 | if (!scn) { | ||
350 | warnx("cannot create section"); | ||
351 | goto error; | ||
352 | } | ||
353 | |||
354 | d = elf_newdata(scn); | ||
355 | if (!d) { | ||
356 | warnx("cannot get new data"); | ||
357 | goto error; | ||
358 | } | ||
359 | |||
360 | /* | ||
361 | * build-id generation | ||
362 | */ | ||
363 | gen_build_id(&bnote, load_addr, code, csize); | ||
364 | bnote.desc.namesz = sizeof(bnote.name); /* must include 0 termination */ | ||
365 | bnote.desc.descsz = sizeof(bnote.build_id); | ||
366 | bnote.desc.type = NT_GNU_BUILD_ID; | ||
367 | strcpy(bnote.name, "GNU"); | ||
368 | |||
369 | d->d_align = 4; | ||
370 | d->d_off = 0LL; | ||
371 | d->d_buf = &bnote; | ||
372 | d->d_type = ELF_T_BYTE; | ||
373 | d->d_size = sizeof(bnote); | ||
374 | d->d_version = EV_CURRENT; | ||
375 | |||
376 | shdr = elf_getshdr(scn); | ||
377 | if (!shdr) { | ||
378 | warnx("cannot get section header"); | ||
379 | goto error; | ||
380 | } | ||
381 | |||
382 | shdr->sh_name = 33; /* offset in shd_string_table */ | ||
383 | shdr->sh_type = SHT_NOTE; | ||
384 | shdr->sh_addr = 0x0; | ||
385 | shdr->sh_flags = SHF_ALLOC; | ||
386 | shdr->sh_size = sizeof(bnote); | ||
387 | shdr->sh_entsize = 0; | ||
388 | |||
389 | if (debug && nr_debug_entries) { | ||
390 | retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries); | ||
391 | if (retval) | ||
392 | goto error; | ||
393 | } else { | ||
394 | if (elf_update(e, ELF_C_WRITE) < 0) { | ||
395 | warnx("elf_update 4 failed"); | ||
396 | goto error; | ||
397 | } | ||
398 | } | ||
399 | |||
400 | retval = 0; | ||
401 | error: | ||
402 | (void)elf_end(e); | ||
403 | |||
404 | free(strsym); | ||
405 | |||
406 | |||
407 | return retval; | ||
408 | } | ||
409 | |||
410 | #ifndef JVMTI | ||
411 | |||
412 | static unsigned char x86_code[] = { | ||
413 | 0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */ | ||
414 | 0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */ | ||
415 | 0xCD, 0x80 /* int $0x80 */ | ||
416 | }; | ||
417 | |||
418 | static struct options options; | ||
419 | |||
420 | int main(int argc, char **argv) | ||
421 | { | ||
422 | int c, fd, ret; | ||
423 | |||
424 | while ((c = getopt(argc, argv, "o:h")) != -1) { | ||
425 | switch (c) { | ||
426 | case 'o': | ||
427 | options.output = optarg; | ||
428 | break; | ||
429 | case 'h': | ||
430 | printf("Usage: genelf -o output_file [-h]\n"); | ||
431 | return 0; | ||
432 | default: | ||
433 | errx(1, "unknown option"); | ||
434 | } | ||
435 | } | ||
436 | |||
437 | fd = open(options.output, O_CREAT|O_TRUNC|O_RDWR, 0666); | ||
438 | if (fd == -1) | ||
439 | err(1, "cannot create file %s", options.output); | ||
440 | |||
441 | ret = jit_write_elf(fd, "main", x86_code, sizeof(x86_code)); | ||
442 | close(fd); | ||
443 | |||
444 | if (ret != 0) | ||
445 | unlink(options.output); | ||
446 | |||
447 | return ret; | ||
448 | } | ||
449 | #endif | ||
diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h new file mode 100644 index 000000000000..45bf9c6d3257 --- /dev/null +++ b/tools/perf/util/genelf.h | |||
@@ -0,0 +1,67 @@ | |||
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 | void *debug, int nr_debug_entries); | ||
8 | /* genelf_debug.c */ | ||
9 | extern int jit_add_debug_info(Elf *e, uint64_t code_addr, | ||
10 | void *debug, int nr_debug_entries); | ||
11 | |||
12 | #if defined(__arm__) | ||
13 | #define GEN_ELF_ARCH EM_ARM | ||
14 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
15 | #define GEN_ELF_CLASS ELFCLASS32 | ||
16 | #elif defined(__aarch64__) | ||
17 | #define GEN_ELF_ARCH EM_AARCH64 | ||
18 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
19 | #define GEN_ELF_CLASS ELFCLASS64 | ||
20 | #elif defined(__x86_64__) | ||
21 | #define GEN_ELF_ARCH EM_X86_64 | ||
22 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
23 | #define GEN_ELF_CLASS ELFCLASS64 | ||
24 | #elif defined(__i386__) | ||
25 | #define GEN_ELF_ARCH EM_386 | ||
26 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
27 | #define GEN_ELF_CLASS ELFCLASS32 | ||
28 | #elif defined(__ppcle__) | ||
29 | #define GEN_ELF_ARCH EM_PPC | ||
30 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
31 | #define GEN_ELF_CLASS ELFCLASS64 | ||
32 | #elif defined(__powerpc__) | ||
33 | #define GEN_ELF_ARCH EM_PPC64 | ||
34 | #define GEN_ELF_ENDIAN ELFDATA2MSB | ||
35 | #define GEN_ELF_CLASS ELFCLASS64 | ||
36 | #elif defined(__powerpcle__) | ||
37 | #define GEN_ELF_ARCH EM_PPC64 | ||
38 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
39 | #define GEN_ELF_CLASS ELFCLASS64 | ||
40 | #else | ||
41 | #error "unsupported architecture" | ||
42 | #endif | ||
43 | |||
44 | #if GEN_ELF_CLASS == ELFCLASS64 | ||
45 | #define elf_newehdr elf64_newehdr | ||
46 | #define elf_getshdr elf64_getshdr | ||
47 | #define Elf_Ehdr Elf64_Ehdr | ||
48 | #define Elf_Shdr Elf64_Shdr | ||
49 | #define Elf_Sym Elf64_Sym | ||
50 | #define ELF_ST_TYPE(a) ELF64_ST_TYPE(a) | ||
51 | #define ELF_ST_BIND(a) ELF64_ST_BIND(a) | ||
52 | #define ELF_ST_VIS(a) ELF64_ST_VISIBILITY(a) | ||
53 | #else | ||
54 | #define elf_newehdr elf32_newehdr | ||
55 | #define elf_getshdr elf32_getshdr | ||
56 | #define Elf_Ehdr Elf32_Ehdr | ||
57 | #define Elf_Shdr Elf32_Shdr | ||
58 | #define Elf_Sym Elf32_Sym | ||
59 | #define ELF_ST_TYPE(a) ELF32_ST_TYPE(a) | ||
60 | #define ELF_ST_BIND(a) ELF32_ST_BIND(a) | ||
61 | #define ELF_ST_VIS(a) ELF32_ST_VISIBILITY(a) | ||
62 | #endif | ||
63 | |||
64 | /* The .text section is directly after the ELF header */ | ||
65 | #define GEN_ELF_TEXT_OFFSET sizeof(Elf_Ehdr) | ||
66 | |||
67 | #endif | ||
diff --git a/tools/perf/util/genelf_debug.c b/tools/perf/util/genelf_debug.c new file mode 100644 index 000000000000..5980f7d256b1 --- /dev/null +++ b/tools/perf/util/genelf_debug.c | |||
@@ -0,0 +1,610 @@ | |||
1 | /* | ||
2 | * genelf_debug.c | ||
3 | * Copyright (C) 2015, Google, Inc | ||
4 | * | ||
5 | * Contributed by: | ||
6 | * Stephane Eranian <eranian@google.com> | ||
7 | * | ||
8 | * Released under the GPL v2. | ||
9 | * | ||
10 | * based on GPLv2 source code from Oprofile | ||
11 | * @remark Copyright 2007 OProfile authors | ||
12 | * @author Philippe Elie | ||
13 | */ | ||
14 | #include <sys/types.h> | ||
15 | #include <stdio.h> | ||
16 | #include <getopt.h> | ||
17 | #include <stddef.h> | ||
18 | #include <libelf.h> | ||
19 | #include <string.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <inttypes.h> | ||
22 | #include <limits.h> | ||
23 | #include <fcntl.h> | ||
24 | #include <err.h> | ||
25 | #include <dwarf.h> | ||
26 | |||
27 | #include "perf.h" | ||
28 | #include "genelf.h" | ||
29 | #include "../util/jitdump.h" | ||
30 | |||
31 | #define BUFFER_EXT_DFL_SIZE (4 * 1024) | ||
32 | |||
33 | typedef uint32_t uword; | ||
34 | typedef uint16_t uhalf; | ||
35 | typedef int32_t sword; | ||
36 | typedef int16_t shalf; | ||
37 | typedef uint8_t ubyte; | ||
38 | typedef int8_t sbyte; | ||
39 | |||
40 | struct buffer_ext { | ||
41 | size_t cur_pos; | ||
42 | size_t max_sz; | ||
43 | void *data; | ||
44 | }; | ||
45 | |||
46 | static void | ||
47 | buffer_ext_dump(struct buffer_ext *be, const char *msg) | ||
48 | { | ||
49 | size_t i; | ||
50 | warnx("DUMP for %s", msg); | ||
51 | for (i = 0 ; i < be->cur_pos; i++) | ||
52 | warnx("%4zu 0x%02x", i, (((char *)be->data)[i]) & 0xff); | ||
53 | } | ||
54 | |||
55 | static inline int | ||
56 | buffer_ext_add(struct buffer_ext *be, void *addr, size_t sz) | ||
57 | { | ||
58 | void *tmp; | ||
59 | size_t be_sz = be->max_sz; | ||
60 | |||
61 | retry: | ||
62 | if ((be->cur_pos + sz) < be_sz) { | ||
63 | memcpy(be->data + be->cur_pos, addr, sz); | ||
64 | be->cur_pos += sz; | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | if (!be_sz) | ||
69 | be_sz = BUFFER_EXT_DFL_SIZE; | ||
70 | else | ||
71 | be_sz <<= 1; | ||
72 | |||
73 | tmp = realloc(be->data, be_sz); | ||
74 | if (!tmp) | ||
75 | return -1; | ||
76 | |||
77 | be->data = tmp; | ||
78 | be->max_sz = be_sz; | ||
79 | |||
80 | goto retry; | ||
81 | } | ||
82 | |||
83 | static void | ||
84 | buffer_ext_init(struct buffer_ext *be) | ||
85 | { | ||
86 | be->data = NULL; | ||
87 | be->cur_pos = 0; | ||
88 | be->max_sz = 0; | ||
89 | } | ||
90 | |||
91 | static inline size_t | ||
92 | buffer_ext_size(struct buffer_ext *be) | ||
93 | { | ||
94 | return be->cur_pos; | ||
95 | } | ||
96 | |||
97 | static inline void * | ||
98 | buffer_ext_addr(struct buffer_ext *be) | ||
99 | { | ||
100 | return be->data; | ||
101 | } | ||
102 | |||
103 | struct debug_line_header { | ||
104 | // Not counting this field | ||
105 | uword total_length; | ||
106 | // version number (2 currently) | ||
107 | uhalf version; | ||
108 | // relative offset from next field to | ||
109 | // program statement | ||
110 | uword prolog_length; | ||
111 | ubyte minimum_instruction_length; | ||
112 | ubyte default_is_stmt; | ||
113 | // line_base - see DWARF 2 specs | ||
114 | sbyte line_base; | ||
115 | // line_range - see DWARF 2 specs | ||
116 | ubyte line_range; | ||
117 | // number of opcode + 1 | ||
118 | ubyte opcode_base; | ||
119 | /* follow the array of opcode args nr: ubytes [nr_opcode_base] */ | ||
120 | /* follow the search directories index, zero terminated string | ||
121 | * terminated by an empty string. | ||
122 | */ | ||
123 | /* follow an array of { filename, LEB128, LEB128, LEB128 }, first is | ||
124 | * the directory index entry, 0 means current directory, then mtime | ||
125 | * and filesize, last entry is followed by en empty string. | ||
126 | */ | ||
127 | /* follow the first program statement */ | ||
128 | } __attribute__((packed)); | ||
129 | |||
130 | /* DWARF 2 spec talk only about one possible compilation unit header while | ||
131 | * binutils can handle two flavours of dwarf 2, 32 and 64 bits, this is not | ||
132 | * related to the used arch, an ELF 32 can hold more than 4 Go of debug | ||
133 | * information. For now we handle only DWARF 2 32 bits comp unit. It'll only | ||
134 | * become a problem if we generate more than 4GB of debug information. | ||
135 | */ | ||
136 | struct compilation_unit_header { | ||
137 | uword total_length; | ||
138 | uhalf version; | ||
139 | uword debug_abbrev_offset; | ||
140 | ubyte pointer_size; | ||
141 | } __attribute__((packed)); | ||
142 | |||
143 | #define DW_LNS_num_opcode (DW_LNS_set_isa + 1) | ||
144 | |||
145 | /* field filled at run time are marked with -1 */ | ||
146 | static struct debug_line_header const default_debug_line_header = { | ||
147 | .total_length = -1, | ||
148 | .version = 2, | ||
149 | .prolog_length = -1, | ||
150 | .minimum_instruction_length = 1, /* could be better when min instruction size != 1 */ | ||
151 | .default_is_stmt = 1, /* we don't take care about basic block */ | ||
152 | .line_base = -5, /* sensible value for line base ... */ | ||
153 | .line_range = -14, /* ... and line range are guessed statically */ | ||
154 | .opcode_base = DW_LNS_num_opcode | ||
155 | }; | ||
156 | |||
157 | static ubyte standard_opcode_length[] = | ||
158 | { | ||
159 | 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 | ||
160 | }; | ||
161 | #if 0 | ||
162 | { | ||
163 | [DW_LNS_advance_pc] = 1, | ||
164 | [DW_LNS_advance_line] = 1, | ||
165 | [DW_LNS_set_file] = 1, | ||
166 | [DW_LNS_set_column] = 1, | ||
167 | [DW_LNS_fixed_advance_pc] = 1, | ||
168 | [DW_LNS_set_isa] = 1, | ||
169 | }; | ||
170 | #endif | ||
171 | |||
172 | /* field filled at run time are marked with -1 */ | ||
173 | static struct compilation_unit_header default_comp_unit_header = { | ||
174 | .total_length = -1, | ||
175 | .version = 2, | ||
176 | .debug_abbrev_offset = 0, /* we reuse the same abbrev entries for all comp unit */ | ||
177 | .pointer_size = sizeof(void *) | ||
178 | }; | ||
179 | |||
180 | static void emit_uword(struct buffer_ext *be, uword data) | ||
181 | { | ||
182 | buffer_ext_add(be, &data, sizeof(uword)); | ||
183 | } | ||
184 | |||
185 | static void emit_string(struct buffer_ext *be, const char *s) | ||
186 | { | ||
187 | buffer_ext_add(be, (void *)s, strlen(s) + 1); | ||
188 | } | ||
189 | |||
190 | static void emit_unsigned_LEB128(struct buffer_ext *be, | ||
191 | unsigned long data) | ||
192 | { | ||
193 | do { | ||
194 | ubyte cur = data & 0x7F; | ||
195 | data >>= 7; | ||
196 | if (data) | ||
197 | cur |= 0x80; | ||
198 | buffer_ext_add(be, &cur, 1); | ||
199 | } while (data); | ||
200 | } | ||
201 | |||
202 | static void emit_signed_LEB128(struct buffer_ext *be, long data) | ||
203 | { | ||
204 | int more = 1; | ||
205 | int negative = data < 0; | ||
206 | int size = sizeof(long) * CHAR_BIT; | ||
207 | while (more) { | ||
208 | ubyte cur = data & 0x7F; | ||
209 | data >>= 7; | ||
210 | if (negative) | ||
211 | data |= - (1 << (size - 7)); | ||
212 | if ((data == 0 && !(cur & 0x40)) || | ||
213 | (data == -1l && (cur & 0x40))) | ||
214 | more = 0; | ||
215 | else | ||
216 | cur |= 0x80; | ||
217 | buffer_ext_add(be, &cur, 1); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static void emit_extended_opcode(struct buffer_ext *be, ubyte opcode, | ||
222 | void *data, size_t data_len) | ||
223 | { | ||
224 | buffer_ext_add(be, (char *)"", 1); | ||
225 | |||
226 | emit_unsigned_LEB128(be, data_len + 1); | ||
227 | |||
228 | buffer_ext_add(be, &opcode, 1); | ||
229 | buffer_ext_add(be, data, data_len); | ||
230 | } | ||
231 | |||
232 | static void emit_opcode(struct buffer_ext *be, ubyte opcode) | ||
233 | { | ||
234 | buffer_ext_add(be, &opcode, 1); | ||
235 | } | ||
236 | |||
237 | static void emit_opcode_signed(struct buffer_ext *be, | ||
238 | ubyte opcode, long data) | ||
239 | { | ||
240 | buffer_ext_add(be, &opcode, 1); | ||
241 | emit_signed_LEB128(be, data); | ||
242 | } | ||
243 | |||
244 | static void emit_opcode_unsigned(struct buffer_ext *be, ubyte opcode, | ||
245 | unsigned long data) | ||
246 | { | ||
247 | buffer_ext_add(be, &opcode, 1); | ||
248 | emit_unsigned_LEB128(be, data); | ||
249 | } | ||
250 | |||
251 | static void emit_advance_pc(struct buffer_ext *be, unsigned long delta_pc) | ||
252 | { | ||
253 | emit_opcode_unsigned(be, DW_LNS_advance_pc, delta_pc); | ||
254 | } | ||
255 | |||
256 | static void emit_advance_lineno(struct buffer_ext *be, long delta_lineno) | ||
257 | { | ||
258 | emit_opcode_signed(be, DW_LNS_advance_line, delta_lineno); | ||
259 | } | ||
260 | |||
261 | static void emit_lne_end_of_sequence(struct buffer_ext *be) | ||
262 | { | ||
263 | emit_extended_opcode(be, DW_LNE_end_sequence, NULL, 0); | ||
264 | } | ||
265 | |||
266 | static void emit_set_file(struct buffer_ext *be, unsigned long idx) | ||
267 | { | ||
268 | emit_opcode_unsigned(be, DW_LNS_set_file, idx); | ||
269 | } | ||
270 | |||
271 | static void emit_lne_define_filename(struct buffer_ext *be, | ||
272 | const char *filename) | ||
273 | { | ||
274 | buffer_ext_add(be, (void *)"", 1); | ||
275 | |||
276 | /* LNE field, strlen(filename) + zero termination, 3 bytes for: the dir entry, timestamp, filesize */ | ||
277 | emit_unsigned_LEB128(be, strlen(filename) + 5); | ||
278 | emit_opcode(be, DW_LNE_define_file); | ||
279 | emit_string(be, filename); | ||
280 | /* directory index 0=do not know */ | ||
281 | emit_unsigned_LEB128(be, 0); | ||
282 | /* last modification date on file 0=do not know */ | ||
283 | emit_unsigned_LEB128(be, 0); | ||
284 | /* filesize 0=do not know */ | ||
285 | emit_unsigned_LEB128(be, 0); | ||
286 | } | ||
287 | |||
288 | static void emit_lne_set_address(struct buffer_ext *be, | ||
289 | void *address) | ||
290 | { | ||
291 | emit_extended_opcode(be, DW_LNE_set_address, &address, sizeof(unsigned long)); | ||
292 | } | ||
293 | |||
294 | static ubyte get_special_opcode(struct debug_entry *ent, | ||
295 | unsigned int last_line, | ||
296 | unsigned long last_vma) | ||
297 | { | ||
298 | unsigned int temp; | ||
299 | unsigned long delta_addr; | ||
300 | |||
301 | /* | ||
302 | * delta from line_base | ||
303 | */ | ||
304 | temp = (ent->lineno - last_line) - default_debug_line_header.line_base; | ||
305 | |||
306 | if (temp >= default_debug_line_header.line_range) | ||
307 | return 0; | ||
308 | |||
309 | /* | ||
310 | * delta of addresses | ||
311 | */ | ||
312 | delta_addr = (ent->addr - last_vma) / default_debug_line_header.minimum_instruction_length; | ||
313 | |||
314 | /* This is not sufficient to ensure opcode will be in [0-256] but | ||
315 | * sufficient to ensure when summing with the delta lineno we will | ||
316 | * not overflow the unsigned long opcode */ | ||
317 | |||
318 | if (delta_addr <= 256 / default_debug_line_header.line_range) { | ||
319 | unsigned long opcode = temp + | ||
320 | (delta_addr * default_debug_line_header.line_range) + | ||
321 | default_debug_line_header.opcode_base; | ||
322 | |||
323 | return opcode <= 255 ? opcode : 0; | ||
324 | } | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static void emit_lineno_info(struct buffer_ext *be, | ||
329 | struct debug_entry *ent, size_t nr_entry, | ||
330 | unsigned long code_addr) | ||
331 | { | ||
332 | size_t i; | ||
333 | |||
334 | /* | ||
335 | * Machine state at start of a statement program | ||
336 | * address = 0 | ||
337 | * file = 1 | ||
338 | * line = 1 | ||
339 | * column = 0 | ||
340 | * is_stmt = default_is_stmt as given in the debug_line_header | ||
341 | * basic block = 0 | ||
342 | * end sequence = 0 | ||
343 | */ | ||
344 | |||
345 | /* start state of the state machine we take care of */ | ||
346 | unsigned long last_vma = code_addr; | ||
347 | char const *cur_filename = NULL; | ||
348 | unsigned long cur_file_idx = 0; | ||
349 | int last_line = 1; | ||
350 | |||
351 | emit_lne_set_address(be, (void *)code_addr); | ||
352 | |||
353 | for (i = 0; i < nr_entry; i++, ent = debug_entry_next(ent)) { | ||
354 | int need_copy = 0; | ||
355 | ubyte special_opcode; | ||
356 | |||
357 | /* | ||
358 | * check if filename changed, if so add it | ||
359 | */ | ||
360 | if (!cur_filename || strcmp(cur_filename, ent->name)) { | ||
361 | emit_lne_define_filename(be, ent->name); | ||
362 | cur_filename = ent->name; | ||
363 | emit_set_file(be, ++cur_file_idx); | ||
364 | need_copy = 1; | ||
365 | } | ||
366 | |||
367 | special_opcode = get_special_opcode(ent, last_line, last_vma); | ||
368 | if (special_opcode != 0) { | ||
369 | last_line = ent->lineno; | ||
370 | last_vma = ent->addr; | ||
371 | emit_opcode(be, special_opcode); | ||
372 | } else { | ||
373 | /* | ||
374 | * lines differ, emit line delta | ||
375 | */ | ||
376 | if (last_line != ent->lineno) { | ||
377 | emit_advance_lineno(be, ent->lineno - last_line); | ||
378 | last_line = ent->lineno; | ||
379 | need_copy = 1; | ||
380 | } | ||
381 | /* | ||
382 | * addresses differ, emit address delta | ||
383 | */ | ||
384 | if (last_vma != ent->addr) { | ||
385 | emit_advance_pc(be, ent->addr - last_vma); | ||
386 | last_vma = ent->addr; | ||
387 | need_copy = 1; | ||
388 | } | ||
389 | /* | ||
390 | * add new row to matrix | ||
391 | */ | ||
392 | if (need_copy) | ||
393 | emit_opcode(be, DW_LNS_copy); | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | |||
398 | static void add_debug_line(struct buffer_ext *be, | ||
399 | struct debug_entry *ent, size_t nr_entry, | ||
400 | unsigned long code_addr) | ||
401 | { | ||
402 | struct debug_line_header * dbg_header; | ||
403 | size_t old_size; | ||
404 | |||
405 | old_size = buffer_ext_size(be); | ||
406 | |||
407 | buffer_ext_add(be, (void *)&default_debug_line_header, | ||
408 | sizeof(default_debug_line_header)); | ||
409 | |||
410 | buffer_ext_add(be, &standard_opcode_length, sizeof(standard_opcode_length)); | ||
411 | |||
412 | // empty directory entry | ||
413 | buffer_ext_add(be, (void *)"", 1); | ||
414 | |||
415 | // empty filename directory | ||
416 | buffer_ext_add(be, (void *)"", 1); | ||
417 | |||
418 | dbg_header = buffer_ext_addr(be) + old_size; | ||
419 | dbg_header->prolog_length = (buffer_ext_size(be) - old_size) - | ||
420 | offsetof(struct debug_line_header, minimum_instruction_length); | ||
421 | |||
422 | emit_lineno_info(be, ent, nr_entry, code_addr); | ||
423 | |||
424 | emit_lne_end_of_sequence(be); | ||
425 | |||
426 | dbg_header = buffer_ext_addr(be) + old_size; | ||
427 | dbg_header->total_length = (buffer_ext_size(be) - old_size) - | ||
428 | offsetof(struct debug_line_header, version); | ||
429 | } | ||
430 | |||
431 | static void | ||
432 | add_debug_abbrev(struct buffer_ext *be) | ||
433 | { | ||
434 | emit_unsigned_LEB128(be, 1); | ||
435 | emit_unsigned_LEB128(be, DW_TAG_compile_unit); | ||
436 | emit_unsigned_LEB128(be, DW_CHILDREN_yes); | ||
437 | emit_unsigned_LEB128(be, DW_AT_stmt_list); | ||
438 | emit_unsigned_LEB128(be, DW_FORM_data4); | ||
439 | emit_unsigned_LEB128(be, 0); | ||
440 | emit_unsigned_LEB128(be, 0); | ||
441 | emit_unsigned_LEB128(be, 0); | ||
442 | } | ||
443 | |||
444 | static void | ||
445 | add_compilation_unit(struct buffer_ext *be, | ||
446 | size_t offset_debug_line) | ||
447 | { | ||
448 | struct compilation_unit_header *comp_unit_header; | ||
449 | size_t old_size = buffer_ext_size(be); | ||
450 | |||
451 | buffer_ext_add(be, &default_comp_unit_header, | ||
452 | sizeof(default_comp_unit_header)); | ||
453 | |||
454 | emit_unsigned_LEB128(be, 1); | ||
455 | emit_uword(be, offset_debug_line); | ||
456 | |||
457 | comp_unit_header = buffer_ext_addr(be) + old_size; | ||
458 | comp_unit_header->total_length = (buffer_ext_size(be) - old_size) - | ||
459 | offsetof(struct compilation_unit_header, version); | ||
460 | } | ||
461 | |||
462 | static int | ||
463 | jit_process_debug_info(uint64_t code_addr, | ||
464 | void *debug, int nr_debug_entries, | ||
465 | struct buffer_ext *dl, | ||
466 | struct buffer_ext *da, | ||
467 | struct buffer_ext *di) | ||
468 | { | ||
469 | struct debug_entry *ent = debug; | ||
470 | int i; | ||
471 | |||
472 | for (i = 0; i < nr_debug_entries; i++) { | ||
473 | ent->addr = ent->addr - code_addr; | ||
474 | ent = debug_entry_next(ent); | ||
475 | } | ||
476 | add_compilation_unit(di, buffer_ext_size(dl)); | ||
477 | add_debug_line(dl, debug, nr_debug_entries, 0); | ||
478 | add_debug_abbrev(da); | ||
479 | if (0) buffer_ext_dump(da, "abbrev"); | ||
480 | |||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | int | ||
485 | jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries) | ||
486 | { | ||
487 | Elf_Data *d; | ||
488 | Elf_Scn *scn; | ||
489 | Elf_Shdr *shdr; | ||
490 | struct buffer_ext dl, di, da; | ||
491 | int ret; | ||
492 | |||
493 | buffer_ext_init(&dl); | ||
494 | buffer_ext_init(&di); | ||
495 | buffer_ext_init(&da); | ||
496 | |||
497 | ret = jit_process_debug_info(code_addr, debug, nr_debug_entries, &dl, &da, &di); | ||
498 | if (ret) | ||
499 | return -1; | ||
500 | /* | ||
501 | * setup .debug_line section | ||
502 | */ | ||
503 | scn = elf_newscn(e); | ||
504 | if (!scn) { | ||
505 | warnx("cannot create section"); | ||
506 | return -1; | ||
507 | } | ||
508 | |||
509 | d = elf_newdata(scn); | ||
510 | if (!d) { | ||
511 | warnx("cannot get new data"); | ||
512 | return -1; | ||
513 | } | ||
514 | |||
515 | d->d_align = 1; | ||
516 | d->d_off = 0LL; | ||
517 | d->d_buf = buffer_ext_addr(&dl); | ||
518 | d->d_type = ELF_T_BYTE; | ||
519 | d->d_size = buffer_ext_size(&dl); | ||
520 | d->d_version = EV_CURRENT; | ||
521 | |||
522 | shdr = elf_getshdr(scn); | ||
523 | if (!shdr) { | ||
524 | warnx("cannot get section header"); | ||
525 | return -1; | ||
526 | } | ||
527 | |||
528 | shdr->sh_name = 52; /* .debug_line */ | ||
529 | shdr->sh_type = SHT_PROGBITS; | ||
530 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | ||
531 | shdr->sh_flags = 0; | ||
532 | shdr->sh_entsize = 0; | ||
533 | |||
534 | /* | ||
535 | * setup .debug_info section | ||
536 | */ | ||
537 | scn = elf_newscn(e); | ||
538 | if (!scn) { | ||
539 | warnx("cannot create section"); | ||
540 | return -1; | ||
541 | } | ||
542 | |||
543 | d = elf_newdata(scn); | ||
544 | if (!d) { | ||
545 | warnx("cannot get new data"); | ||
546 | return -1; | ||
547 | } | ||
548 | |||
549 | d->d_align = 1; | ||
550 | d->d_off = 0LL; | ||
551 | d->d_buf = buffer_ext_addr(&di); | ||
552 | d->d_type = ELF_T_BYTE; | ||
553 | d->d_size = buffer_ext_size(&di); | ||
554 | d->d_version = EV_CURRENT; | ||
555 | |||
556 | shdr = elf_getshdr(scn); | ||
557 | if (!shdr) { | ||
558 | warnx("cannot get section header"); | ||
559 | return -1; | ||
560 | } | ||
561 | |||
562 | shdr->sh_name = 64; /* .debug_info */ | ||
563 | shdr->sh_type = SHT_PROGBITS; | ||
564 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | ||
565 | shdr->sh_flags = 0; | ||
566 | shdr->sh_entsize = 0; | ||
567 | |||
568 | /* | ||
569 | * setup .debug_abbrev section | ||
570 | */ | ||
571 | scn = elf_newscn(e); | ||
572 | if (!scn) { | ||
573 | warnx("cannot create section"); | ||
574 | return -1; | ||
575 | } | ||
576 | |||
577 | d = elf_newdata(scn); | ||
578 | if (!d) { | ||
579 | warnx("cannot get new data"); | ||
580 | return -1; | ||
581 | } | ||
582 | |||
583 | d->d_align = 1; | ||
584 | d->d_off = 0LL; | ||
585 | d->d_buf = buffer_ext_addr(&da); | ||
586 | d->d_type = ELF_T_BYTE; | ||
587 | d->d_size = buffer_ext_size(&da); | ||
588 | d->d_version = EV_CURRENT; | ||
589 | |||
590 | shdr = elf_getshdr(scn); | ||
591 | if (!shdr) { | ||
592 | warnx("cannot get section header"); | ||
593 | return -1; | ||
594 | } | ||
595 | |||
596 | shdr->sh_name = 76; /* .debug_info */ | ||
597 | shdr->sh_type = SHT_PROGBITS; | ||
598 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | ||
599 | shdr->sh_flags = 0; | ||
600 | shdr->sh_entsize = 0; | ||
601 | |||
602 | /* | ||
603 | * now we update the ELF image with all the sections | ||
604 | */ | ||
605 | if (elf_update(e, ELF_C_WRITE) < 0) { | ||
606 | warnx("elf_update debug failed"); | ||
607 | return -1; | ||
608 | } | ||
609 | return 0; | ||
610 | } | ||
diff --git a/tools/perf/util/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..99fa5eee9fe0 --- /dev/null +++ b/tools/perf/util/jitdump.c | |||
@@ -0,0 +1,672 @@ | |||
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 | void *debug, | ||
68 | int nr_debug_entries) | ||
69 | { | ||
70 | int ret, fd; | ||
71 | |||
72 | if (verbose > 0) | ||
73 | fprintf(stderr, "write ELF image %s\n", filename); | ||
74 | |||
75 | fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0644); | ||
76 | if (fd == -1) { | ||
77 | pr_warning("cannot create jit ELF %s: %s\n", filename, strerror(errno)); | ||
78 | return -1; | ||
79 | } | ||
80 | |||
81 | ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries); | ||
82 | |||
83 | close(fd); | ||
84 | |||
85 | if (ret) | ||
86 | unlink(filename); | ||
87 | |||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | static void | ||
92 | jit_close(struct jit_buf_desc *jd) | ||
93 | { | ||
94 | if (!(jd && jd->in)) | ||
95 | return; | ||
96 | funlockfile(jd->in); | ||
97 | fclose(jd->in); | ||
98 | jd->in = NULL; | ||
99 | } | ||
100 | |||
101 | static int | ||
102 | jit_open(struct jit_buf_desc *jd, const char *name) | ||
103 | { | ||
104 | struct jitheader header; | ||
105 | struct jr_prefix *prefix; | ||
106 | ssize_t bs, bsz = 0; | ||
107 | void *n, *buf = NULL; | ||
108 | int ret, retval = -1; | ||
109 | |||
110 | jd->in = fopen(name, "r"); | ||
111 | if (!jd->in) | ||
112 | return -1; | ||
113 | |||
114 | bsz = hmax(sizeof(header), sizeof(*prefix)); | ||
115 | |||
116 | buf = malloc(bsz); | ||
117 | if (!buf) | ||
118 | goto error; | ||
119 | |||
120 | /* | ||
121 | * protect from writer modifying the file while we are reading it | ||
122 | */ | ||
123 | flockfile(jd->in); | ||
124 | |||
125 | ret = fread(buf, sizeof(header), 1, jd->in); | ||
126 | if (ret != 1) | ||
127 | goto error; | ||
128 | |||
129 | memcpy(&header, buf, sizeof(header)); | ||
130 | |||
131 | if (header.magic != JITHEADER_MAGIC) { | ||
132 | if (header.magic != JITHEADER_MAGIC_SW) | ||
133 | goto error; | ||
134 | jd->needs_bswap = true; | ||
135 | } | ||
136 | |||
137 | if (jd->needs_bswap) { | ||
138 | header.version = bswap_32(header.version); | ||
139 | header.total_size = bswap_32(header.total_size); | ||
140 | header.pid = bswap_32(header.pid); | ||
141 | header.elf_mach = bswap_32(header.elf_mach); | ||
142 | header.timestamp = bswap_64(header.timestamp); | ||
143 | header.flags = bswap_64(header.flags); | ||
144 | } | ||
145 | |||
146 | if (verbose > 2) | ||
147 | pr_debug("version=%u\nhdr.size=%u\nts=0x%llx\npid=%d\nelf_mach=%d\n", | ||
148 | header.version, | ||
149 | header.total_size, | ||
150 | (unsigned long long)header.timestamp, | ||
151 | header.pid, | ||
152 | header.elf_mach); | ||
153 | |||
154 | if (header.flags & JITDUMP_FLAGS_RESERVED) { | ||
155 | pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", | ||
156 | (unsigned long long)header.flags & JITDUMP_FLAGS_RESERVED); | ||
157 | goto error; | ||
158 | } | ||
159 | |||
160 | bs = header.total_size - sizeof(header); | ||
161 | |||
162 | if (bs > bsz) { | ||
163 | n = realloc(buf, bs); | ||
164 | if (!n) | ||
165 | goto error; | ||
166 | bsz = bs; | ||
167 | buf = n; | ||
168 | /* read extra we do not know about */ | ||
169 | ret = fread(buf, bs - bsz, 1, jd->in); | ||
170 | if (ret != 1) | ||
171 | goto error; | ||
172 | } | ||
173 | /* | ||
174 | * keep dirname for generating files and mmap records | ||
175 | */ | ||
176 | strcpy(jd->dir, name); | ||
177 | dirname(jd->dir); | ||
178 | |||
179 | return 0; | ||
180 | error: | ||
181 | funlockfile(jd->in); | ||
182 | fclose(jd->in); | ||
183 | return retval; | ||
184 | } | ||
185 | |||
186 | static union jr_entry * | ||
187 | jit_get_next_entry(struct jit_buf_desc *jd) | ||
188 | { | ||
189 | struct jr_prefix *prefix; | ||
190 | union jr_entry *jr; | ||
191 | void *addr; | ||
192 | size_t bs, size; | ||
193 | int id, ret; | ||
194 | |||
195 | if (!(jd && jd->in)) | ||
196 | return NULL; | ||
197 | |||
198 | if (jd->buf == NULL) { | ||
199 | size_t sz = getpagesize(); | ||
200 | if (sz < sizeof(*prefix)) | ||
201 | sz = sizeof(*prefix); | ||
202 | |||
203 | jd->buf = malloc(sz); | ||
204 | if (jd->buf == NULL) | ||
205 | return NULL; | ||
206 | |||
207 | jd->bufsize = sz; | ||
208 | } | ||
209 | |||
210 | prefix = jd->buf; | ||
211 | |||
212 | /* | ||
213 | * file is still locked at this point | ||
214 | */ | ||
215 | ret = fread(prefix, sizeof(*prefix), 1, jd->in); | ||
216 | if (ret != 1) | ||
217 | return NULL; | ||
218 | |||
219 | if (jd->needs_bswap) { | ||
220 | prefix->id = bswap_32(prefix->id); | ||
221 | prefix->total_size = bswap_32(prefix->total_size); | ||
222 | prefix->timestamp = bswap_64(prefix->timestamp); | ||
223 | } | ||
224 | id = prefix->id; | ||
225 | size = prefix->total_size; | ||
226 | |||
227 | bs = (size_t)size; | ||
228 | if (bs < sizeof(*prefix)) | ||
229 | return NULL; | ||
230 | |||
231 | if (id >= JIT_CODE_MAX) { | ||
232 | pr_warning("next_entry: unknown prefix %d, skipping\n", id); | ||
233 | return NULL; | ||
234 | } | ||
235 | if (bs > jd->bufsize) { | ||
236 | void *n; | ||
237 | n = realloc(jd->buf, bs); | ||
238 | if (!n) | ||
239 | return NULL; | ||
240 | jd->buf = n; | ||
241 | jd->bufsize = bs; | ||
242 | } | ||
243 | |||
244 | addr = ((void *)jd->buf) + sizeof(*prefix); | ||
245 | |||
246 | ret = fread(addr, bs - sizeof(*prefix), 1, jd->in); | ||
247 | if (ret != 1) | ||
248 | return NULL; | ||
249 | |||
250 | jr = (union jr_entry *)jd->buf; | ||
251 | |||
252 | switch(id) { | ||
253 | case JIT_CODE_DEBUG_INFO: | ||
254 | if (jd->needs_bswap) { | ||
255 | uint64_t n; | ||
256 | jr->info.code_addr = bswap_64(jr->info.code_addr); | ||
257 | jr->info.nr_entry = bswap_64(jr->info.nr_entry); | ||
258 | for (n = 0 ; n < jr->info.nr_entry; n++) { | ||
259 | jr->info.entries[n].addr = bswap_64(jr->info.entries[n].addr); | ||
260 | jr->info.entries[n].lineno = bswap_32(jr->info.entries[n].lineno); | ||
261 | jr->info.entries[n].discrim = bswap_32(jr->info.entries[n].discrim); | ||
262 | } | ||
263 | } | ||
264 | break; | ||
265 | case JIT_CODE_CLOSE: | ||
266 | break; | ||
267 | case JIT_CODE_LOAD: | ||
268 | if (jd->needs_bswap) { | ||
269 | jr->load.pid = bswap_32(jr->load.pid); | ||
270 | jr->load.tid = bswap_32(jr->load.tid); | ||
271 | jr->load.vma = bswap_64(jr->load.vma); | ||
272 | jr->load.code_addr = bswap_64(jr->load.code_addr); | ||
273 | jr->load.code_size = bswap_64(jr->load.code_size); | ||
274 | jr->load.code_index= bswap_64(jr->load.code_index); | ||
275 | } | ||
276 | jd->code_load_count++; | ||
277 | break; | ||
278 | case JIT_CODE_MOVE: | ||
279 | if (jd->needs_bswap) { | ||
280 | jr->move.pid = bswap_32(jr->move.pid); | ||
281 | jr->move.tid = bswap_32(jr->move.tid); | ||
282 | jr->move.vma = bswap_64(jr->move.vma); | ||
283 | jr->move.old_code_addr = bswap_64(jr->move.old_code_addr); | ||
284 | jr->move.new_code_addr = bswap_64(jr->move.new_code_addr); | ||
285 | jr->move.code_size = bswap_64(jr->move.code_size); | ||
286 | jr->move.code_index = bswap_64(jr->move.code_index); | ||
287 | } | ||
288 | break; | ||
289 | case JIT_CODE_MAX: | ||
290 | default: | ||
291 | return NULL; | ||
292 | } | ||
293 | return jr; | ||
294 | } | ||
295 | |||
296 | static int | ||
297 | jit_inject_event(struct jit_buf_desc *jd, union perf_event *event) | ||
298 | { | ||
299 | ssize_t size; | ||
300 | |||
301 | size = perf_data_file__write(jd->output, event, event->header.size); | ||
302 | if (size < 0) | ||
303 | return -1; | ||
304 | |||
305 | jd->bytes_written += size; | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | ||
310 | { | ||
311 | struct perf_sample sample; | ||
312 | union perf_event *event; | ||
313 | struct perf_tool *tool = jd->session->tool; | ||
314 | uint64_t code, addr; | ||
315 | uintptr_t uaddr; | ||
316 | char *filename; | ||
317 | struct stat st; | ||
318 | size_t size; | ||
319 | u16 idr_size; | ||
320 | const char *sym; | ||
321 | uint32_t count; | ||
322 | int ret, csize; | ||
323 | pid_t pid, tid; | ||
324 | struct { | ||
325 | u32 pid, tid; | ||
326 | u64 time; | ||
327 | } *id; | ||
328 | |||
329 | pid = jr->load.pid; | ||
330 | tid = jr->load.tid; | ||
331 | csize = jr->load.code_size; | ||
332 | addr = jr->load.code_addr; | ||
333 | sym = (void *)((unsigned long)jr + sizeof(jr->load)); | ||
334 | code = (unsigned long)jr + jr->load.p.total_size - csize; | ||
335 | count = jr->load.code_index; | ||
336 | idr_size = jd->machine->id_hdr_size; | ||
337 | |||
338 | event = calloc(1, sizeof(*event) + idr_size); | ||
339 | if (!event) | ||
340 | return -1; | ||
341 | |||
342 | filename = event->mmap2.filename; | ||
343 | size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%u.so", | ||
344 | jd->dir, | ||
345 | pid, | ||
346 | count); | ||
347 | |||
348 | size++; /* for \0 */ | ||
349 | |||
350 | size = PERF_ALIGN(size, sizeof(u64)); | ||
351 | uaddr = (uintptr_t)code; | ||
352 | ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries); | ||
353 | |||
354 | if (jd->debug_data && jd->nr_debug_entries) { | ||
355 | free(jd->debug_data); | ||
356 | jd->debug_data = NULL; | ||
357 | jd->nr_debug_entries = 0; | ||
358 | } | ||
359 | |||
360 | if (ret) { | ||
361 | free(event); | ||
362 | return -1; | ||
363 | } | ||
364 | if (stat(filename, &st)) | ||
365 | memset(&st, 0, sizeof(stat)); | ||
366 | |||
367 | event->mmap2.header.type = PERF_RECORD_MMAP2; | ||
368 | event->mmap2.header.misc = PERF_RECORD_MISC_USER; | ||
369 | event->mmap2.header.size = (sizeof(event->mmap2) - | ||
370 | (sizeof(event->mmap2.filename) - size) + idr_size); | ||
371 | |||
372 | event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; | ||
373 | event->mmap2.start = addr; | ||
374 | event->mmap2.len = csize; | ||
375 | event->mmap2.pid = pid; | ||
376 | event->mmap2.tid = tid; | ||
377 | event->mmap2.ino = st.st_ino; | ||
378 | event->mmap2.maj = major(st.st_dev); | ||
379 | event->mmap2.min = minor(st.st_dev); | ||
380 | event->mmap2.prot = st.st_mode; | ||
381 | event->mmap2.flags = MAP_SHARED; | ||
382 | event->mmap2.ino_generation = 1; | ||
383 | |||
384 | id = (void *)((unsigned long)event + event->mmap.header.size - idr_size); | ||
385 | if (jd->sample_type & PERF_SAMPLE_TID) { | ||
386 | id->pid = pid; | ||
387 | id->tid = tid; | ||
388 | } | ||
389 | if (jd->sample_type & PERF_SAMPLE_TIME) | ||
390 | id->time = jr->load.p.timestamp; | ||
391 | |||
392 | /* | ||
393 | * create pseudo sample to induce dso hit increment | ||
394 | * use first address as sample address | ||
395 | */ | ||
396 | memset(&sample, 0, sizeof(sample)); | ||
397 | sample.pid = pid; | ||
398 | sample.tid = tid; | ||
399 | sample.time = id->time; | ||
400 | sample.ip = addr; | ||
401 | |||
402 | ret = perf_event__process_mmap2(tool, event, &sample, jd->machine); | ||
403 | if (ret) | ||
404 | return ret; | ||
405 | |||
406 | ret = jit_inject_event(jd, event); | ||
407 | /* | ||
408 | * mark dso as use to generate buildid in the header | ||
409 | */ | ||
410 | if (!ret) | ||
411 | build_id__mark_dso_hit(tool, event, &sample, NULL, jd->machine); | ||
412 | |||
413 | return ret; | ||
414 | } | ||
415 | |||
416 | static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) | ||
417 | { | ||
418 | struct perf_sample sample; | ||
419 | union perf_event *event; | ||
420 | struct perf_tool *tool = jd->session->tool; | ||
421 | char *filename; | ||
422 | size_t size; | ||
423 | struct stat st; | ||
424 | u16 idr_size; | ||
425 | int ret; | ||
426 | pid_t pid, tid; | ||
427 | struct { | ||
428 | u32 pid, tid; | ||
429 | u64 time; | ||
430 | } *id; | ||
431 | |||
432 | pid = jr->move.pid; | ||
433 | tid = jr->move.tid; | ||
434 | idr_size = jd->machine->id_hdr_size; | ||
435 | |||
436 | /* | ||
437 | * +16 to account for sample_id_all (hack) | ||
438 | */ | ||
439 | event = calloc(1, sizeof(*event) + 16); | ||
440 | if (!event) | ||
441 | return -1; | ||
442 | |||
443 | filename = event->mmap2.filename; | ||
444 | size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%"PRIu64, | ||
445 | jd->dir, | ||
446 | pid, | ||
447 | jr->move.code_index); | ||
448 | |||
449 | size++; /* for \0 */ | ||
450 | |||
451 | if (stat(filename, &st)) | ||
452 | memset(&st, 0, sizeof(stat)); | ||
453 | |||
454 | size = PERF_ALIGN(size, sizeof(u64)); | ||
455 | |||
456 | event->mmap2.header.type = PERF_RECORD_MMAP2; | ||
457 | event->mmap2.header.misc = PERF_RECORD_MISC_USER; | ||
458 | event->mmap2.header.size = (sizeof(event->mmap2) - | ||
459 | (sizeof(event->mmap2.filename) - size) + idr_size); | ||
460 | event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; | ||
461 | event->mmap2.start = jr->move.new_code_addr; | ||
462 | event->mmap2.len = jr->move.code_size; | ||
463 | event->mmap2.pid = pid; | ||
464 | event->mmap2.tid = tid; | ||
465 | event->mmap2.ino = st.st_ino; | ||
466 | event->mmap2.maj = major(st.st_dev); | ||
467 | event->mmap2.min = minor(st.st_dev); | ||
468 | event->mmap2.prot = st.st_mode; | ||
469 | event->mmap2.flags = MAP_SHARED; | ||
470 | event->mmap2.ino_generation = 1; | ||
471 | |||
472 | id = (void *)((unsigned long)event + event->mmap.header.size - idr_size); | ||
473 | if (jd->sample_type & PERF_SAMPLE_TID) { | ||
474 | id->pid = pid; | ||
475 | id->tid = tid; | ||
476 | } | ||
477 | if (jd->sample_type & PERF_SAMPLE_TIME) | ||
478 | id->time = jr->load.p.timestamp; | ||
479 | |||
480 | /* | ||
481 | * create pseudo sample to induce dso hit increment | ||
482 | * use first address as sample address | ||
483 | */ | ||
484 | memset(&sample, 0, sizeof(sample)); | ||
485 | sample.pid = pid; | ||
486 | sample.tid = tid; | ||
487 | sample.time = id->time; | ||
488 | sample.ip = jr->move.new_code_addr; | ||
489 | |||
490 | ret = perf_event__process_mmap2(tool, event, &sample, jd->machine); | ||
491 | if (ret) | ||
492 | return ret; | ||
493 | |||
494 | ret = jit_inject_event(jd, event); | ||
495 | if (!ret) | ||
496 | build_id__mark_dso_hit(tool, event, &sample, NULL, jd->machine); | ||
497 | |||
498 | return ret; | ||
499 | } | ||
500 | |||
501 | static int jit_repipe_debug_info(struct jit_buf_desc *jd, union jr_entry *jr) | ||
502 | { | ||
503 | void *data; | ||
504 | size_t sz; | ||
505 | |||
506 | if (!(jd && jr)) | ||
507 | return -1; | ||
508 | |||
509 | sz = jr->prefix.total_size - sizeof(jr->info); | ||
510 | data = malloc(sz); | ||
511 | if (!data) | ||
512 | return -1; | ||
513 | |||
514 | memcpy(data, &jr->info.entries, sz); | ||
515 | |||
516 | jd->debug_data = data; | ||
517 | |||
518 | /* | ||
519 | * we must use nr_entry instead of size here because | ||
520 | * we cannot distinguish actual entry from padding otherwise | ||
521 | */ | ||
522 | jd->nr_debug_entries = jr->info.nr_entry; | ||
523 | |||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | static int | ||
528 | jit_process_dump(struct jit_buf_desc *jd) | ||
529 | { | ||
530 | union jr_entry *jr; | ||
531 | int ret; | ||
532 | |||
533 | while ((jr = jit_get_next_entry(jd))) { | ||
534 | switch(jr->prefix.id) { | ||
535 | case JIT_CODE_LOAD: | ||
536 | ret = jit_repipe_code_load(jd, jr); | ||
537 | break; | ||
538 | case JIT_CODE_MOVE: | ||
539 | ret = jit_repipe_code_move(jd, jr); | ||
540 | break; | ||
541 | case JIT_CODE_DEBUG_INFO: | ||
542 | ret = jit_repipe_debug_info(jd, jr); | ||
543 | break; | ||
544 | default: | ||
545 | ret = 0; | ||
546 | continue; | ||
547 | } | ||
548 | } | ||
549 | return ret; | ||
550 | } | ||
551 | |||
552 | static int | ||
553 | jit_inject(struct jit_buf_desc *jd, char *path) | ||
554 | { | ||
555 | int ret; | ||
556 | |||
557 | if (verbose > 0) | ||
558 | fprintf(stderr, "injecting: %s\n", path); | ||
559 | |||
560 | ret = jit_open(jd, path); | ||
561 | if (ret) | ||
562 | return -1; | ||
563 | |||
564 | ret = jit_process_dump(jd); | ||
565 | |||
566 | jit_close(jd); | ||
567 | |||
568 | if (verbose > 0) | ||
569 | fprintf(stderr, "injected: %s (%d)\n", path, ret); | ||
570 | |||
571 | return 0; | ||
572 | } | ||
573 | |||
574 | /* | ||
575 | * File must be with pattern .../jit-XXXX.dump | ||
576 | * where XXXX is the PID of the process which did the mmap() | ||
577 | * as captured in the RECORD_MMAP record | ||
578 | */ | ||
579 | static int | ||
580 | jit_detect(char *mmap_name, pid_t pid) | ||
581 | { | ||
582 | char *p; | ||
583 | char *end = NULL; | ||
584 | pid_t pid2; | ||
585 | |||
586 | if (verbose > 2) | ||
587 | fprintf(stderr, "jit marker trying : %s\n", mmap_name); | ||
588 | /* | ||
589 | * get file name | ||
590 | */ | ||
591 | p = strrchr(mmap_name, '/'); | ||
592 | if (!p) | ||
593 | return -1; | ||
594 | |||
595 | /* | ||
596 | * match prefix | ||
597 | */ | ||
598 | if (strncmp(p, "/jit-", 5)) | ||
599 | return -1; | ||
600 | |||
601 | /* | ||
602 | * skip prefix | ||
603 | */ | ||
604 | p += 5; | ||
605 | |||
606 | /* | ||
607 | * must be followed by a pid | ||
608 | */ | ||
609 | if (!isdigit(*p)) | ||
610 | return -1; | ||
611 | |||
612 | pid2 = (int)strtol(p, &end, 10); | ||
613 | if (!end) | ||
614 | return -1; | ||
615 | |||
616 | /* | ||
617 | * pid does not match mmap pid | ||
618 | * pid==0 in system-wide mode (synthesized) | ||
619 | */ | ||
620 | if (pid && pid2 != pid) | ||
621 | return -1; | ||
622 | /* | ||
623 | * validate suffix | ||
624 | */ | ||
625 | if (strcmp(end, ".dump")) | ||
626 | return -1; | ||
627 | |||
628 | if (verbose > 0) | ||
629 | fprintf(stderr, "jit marker found: %s\n", mmap_name); | ||
630 | |||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | int | ||
635 | jit_process(struct perf_session *session, | ||
636 | struct perf_data_file *output, | ||
637 | struct machine *machine, | ||
638 | char *filename, | ||
639 | pid_t pid, | ||
640 | u64 *nbytes) | ||
641 | { | ||
642 | struct perf_evsel *first; | ||
643 | struct jit_buf_desc jd; | ||
644 | int ret; | ||
645 | |||
646 | /* | ||
647 | * first, detect marker mmap (i.e., the jitdump mmap) | ||
648 | */ | ||
649 | if (jit_detect(filename, pid)) | ||
650 | return -1; | ||
651 | |||
652 | memset(&jd, 0, sizeof(jd)); | ||
653 | |||
654 | jd.session = session; | ||
655 | jd.output = output; | ||
656 | jd.machine = machine; | ||
657 | |||
658 | /* | ||
659 | * track sample_type to compute id_all layout | ||
660 | * perf sets the same sample type to all events as of now | ||
661 | */ | ||
662 | first = perf_evlist__first(session->evlist); | ||
663 | jd.sample_type = first->attr.sample_type; | ||
664 | |||
665 | *nbytes = 0; | ||
666 | |||
667 | ret = jit_inject(&jd, filename); | ||
668 | if (!ret) | ||
669 | *nbytes = jd.bytes_written; | ||
670 | |||
671 | return ret; | ||
672 | } | ||
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 */ | ||
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 562b8ebeae5b..b1dd68f358fc 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <inttypes.h> | 6 | #include <inttypes.h> |
7 | 7 | ||
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | #include "demangle-java.h" | ||
9 | #include "machine.h" | 10 | #include "machine.h" |
10 | #include "vdso.h" | 11 | #include "vdso.h" |
11 | #include <symbol/kallsyms.h> | 12 | #include <symbol/kallsyms.h> |
@@ -1077,6 +1078,8 @@ new_symbol: | |||
1077 | demangle_flags = DMGL_PARAMS | DMGL_ANSI; | 1078 | demangle_flags = DMGL_PARAMS | DMGL_ANSI; |
1078 | 1079 | ||
1079 | demangled = bfd_demangle(NULL, elf_name, demangle_flags); | 1080 | demangled = bfd_demangle(NULL, elf_name, demangle_flags); |
1081 | if (demangled == NULL) | ||
1082 | demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET); | ||
1080 | if (demangled != NULL) | 1083 | if (demangled != NULL) |
1081 | elf_name = demangled; | 1084 | elf_name = demangled; |
1082 | } | 1085 | } |