perf: add support for profiling jitted code
v7 cover perf: add support for profiling jitted code
This patch series extends perf record/report/annotate to enable profiling of jitted (just-in-time compiled) code. The current perf tool provides very limited support for profiling jitted code for some runtime environments. But the support is experimental and cannot be used in complex environments. It relies on files in /tmp, for instance. It does not support annotate mode or rejitted code.
This patch series adds a better way of profiling jitted code with the following advantages:
- support any jitted code environment (some with modifications)
- support Java runtime with JVMTI interface with no modifications
- provides a portable JVMTI agent library
- known to support V8 runtime
- known to support DART runtime
- supports code rejitting and code movements
- no files in /tmp
- meta-data file is unique to each run
- no changes to perf report/annotate
- support per-thread and system-wide profiling
- support monitoring of multiple simultaneous Jit runtimes
- source level view in perf annotate
The support is based on cooperation with the runtime. For Java runtimes, supporting the JVMTI interface, there is no change necessary. For other runtimes, modifications are necessary to emit the meta-data to support symbolization, annotation, source lines correlation of the samples. Those modifications are relatively straighforward, some have been implemented in V8 and DART.
The jit environment emits a binary dump file which contains the jitted code (in raw format) and meta-data describing the mapping of functions. The binary format is documented in the jitdump.h header file. It is adapted from the OProfile jitdump format.
To enable synchronization of the runtime MMAPs with those recorded by the kernel on behalf of the perf tool, the runtime needs to timestamp any record in the dump file using the same time source. This is possible since Linux 4.1 where the kernel supports per event timestamp clock source. In the case of the JVMTI agent, the clock used is CLOCK_MONOTONIC, thus perf record is invoked with -k mono such that it matches the agent.
The current support only works when the runtime is monitored from start to finish: perf record java --agentpath:libpfmjvmti.so my_class.
Once the run is completed, the jitdump file needs to be injected into the perf.data file. This is accomplished by using the perf inject command. This will also generate an ELF image for each jitted function. The injected MMAP records will point to these ELF images. The reasoning behind using ELF images is that it makes processing for perf report and annotate automatic and transparent. It also makes it easier to package and analyze on a remote machine. Binutils tools can decode the ELF images easily.
The reporting is unchanged, simply invoke perf report or perf annotate on the modified perf.data file. The jitted code will appear symbolized and the assembly view will display the instruction level profile and source level profile.
As an added bonus, the series includes support for demangling function signature from OpenJDK.
Furthermore, we believe there is a way to skip the perf inject phase and have perf report/annotate directly inject the MMAP records on the fly during processing of the perf.data file. Perf report would also generate the ELF files if necessary. Such optimization, would make using this extension seamless in system-wide mode and larger environments. This will be added in a later update as well.
In V2, we have switched to Pawell Moll and David Ahern posix clock kernel module instead. We have dropped the patch which modified the arguments to map_init() because the change was not used. We are not printing the return type of Java methods anymore and have made the Java demangler a separate module. We also rebased to 3.19.0+ from tip.git.
In V3, we switched to Pawel Moll's CLOCK_MONOTONIC perf clock patches. This patch switch perf_events from sched_clock to CLOCK_MONOTONIC, a clock source which is available to users.
In V4, we rebased to 4.0-rc5. We also simplified the process by getting rid of the requirement to pass the jitdump file name to perf inject. Now, perf injects automtically detects if jitdumps were generated and it merges the relevant meta-data. This is accomplished by having the jit runtime mmap the jitdump file for the purpose of creating a MMAP record in the perf.data file. That MMAP contains all the info to locate the jitdump file and generate the ELF images for jitted functions.
In V5, we rebase to acme's perf/core branch (instead of tip.git). We fixed some bswap issues, switched to using scnprintf() and fixed formatting issues. Also made sure all the files were included in the patches. We also fix one error message in the JVMTI agent.
In V6, we switched back to using tip.git to leverage PeterZ's clockid patch for perf_events in 4.0.0-rc6. Clock source can now be specified per event and they are connected with the MONOTONIC Posix clock. We leverage this extension to timestamp samples in the jit runtime and correlate them with perf samples. Notice the -k mono option in perf record example below.
In V7, we rebased to 4.3.0-rc3 using tip.git (at commit 0dc7757). We fixed several issues in the agent. We also added source line information in the jitdump file from the JVMTI agent. This is still experimental and probably has some issues. The source line info is encoded in DWARF2 format in each ELF image. The code to do this is leveraged from Oprofile with some fixes and cleanups.
To use the new feature:
- need to run with 4.1 or later
- compile perf
- cd tools/perf/jvmti; make; install wherever is appropriate
Example using openJDK:
$ perf record -k mono java -agentpath:libjvmti.so my_class $ perf inject -i perf.data -jit -o perf.data.jitted $ perf report -i perf.data.jitted
Thanks to all the contributors and testers. Special thanks to PeterZ for adding the clock source to perf_events and solving the problem of common timesource for user and kernel level samples. Thanks to the Oprofile authors for the DWARF2 source line code generation.
Enjoy,
Stephane Eranian (5):
perf tools: add Java demangling support perf tools: pass session to mmap processing code perf inject: add jitdump mmap injection support perf tools: add JVMTI agent library perf/jit: add source line info support
tools/build/Makefile.feature | 2 + tools/build/feature/Makefile | 4 + tools/perf/Documentation/perf-inject.txt | 7 + tools/perf/builtin-inject.c | 97 ++++- tools/perf/builtin-script.c | 14 +- tools/perf/config/Makefile | 11 + tools/perf/jvmti/Makefile | 70 ++++ tools/perf/jvmti/jvmti_agent.c | 464 +++++++++++++++++++++ tools/perf/jvmti/jvmti_agent.h | 36 ++ tools/perf/jvmti/libjvmti.c | 304 ++++++++++++++ tools/perf/util/Build | 4 + tools/perf/util/demangle-java.c | 199 +++++++++ tools/perf/util/demangle-java.h | 10 + tools/perf/util/event.c | 6 +- tools/perf/util/event.h | 6 +- tools/perf/util/genelf.c | 448 +++++++++++++++++++++ tools/perf/util/genelf.h | 60 +++ tools/perf/util/genelf_debug.c | 610 ++++++++++++++++++++++++++++ tools/perf/util/jitdump.c | 666 +++++++++++++++++++++++++++++++ tools/perf/util/jitdump.h | 116 ++++++ tools/perf/util/session.c | 43 +- tools/perf/util/symbol-elf.c | 3 + tools/perf/util/tool.h | 11 +- 23 files changed, 3152 insertions(+), 39 deletions(-) create mode 100644 tools/perf/jvmti/Makefile create mode 100644 tools/perf/jvmti/jvmti_agent.c create mode 100644 tools/perf/jvmti/jvmti_agent.h create mode 100644 tools/perf/jvmti/libjvmti.c create mode 100644 tools/perf/util/demangle-java.c create mode 100644 tools/perf/util/demangle-java.h create mode 100644 tools/perf/util/genelf.c create mode 100644 tools/perf/util/genelf.h create mode 100644 tools/perf/util/genelf_debug.c create mode 100644 tools/perf/util/jitdump.c create mode 100644 tools/perf/util/jitdump.h
Signature · 1 line
1.9.1
PATCH v7 1/5 perf tools: add Java demangling support
Add Java function descriptor demangling support.
Something bfd cannot do.
Use the JAVA_DEMANGLE_NORET flag to avoid decoding the return type of functions.
--- tools/perf/util/Build | 1 + tools/perf/util/demangle-java.c | 199 ++++++++++++++++++++++++++++++++++++++++ tools/perf/util/demangle-java.h | 10 ++ tools/perf/util/symbol-elf.c | 3 + 4 files changed, 213 insertions(+) create mode 100644 tools/perf/util/demangle-java.c create mode 100644 tools/perf/util/demangle-java.h
tools/perf/util/Build +1 -0
@@ -107,6 +107,7 @@ libperf-y += scripting-engines/ libperf-$(CONFIG_PERF_REGS) += perf_regs.o libperf-$(CONFIG_ZLIB) += zlib.o libperf-$(CONFIG_LZMA) += lzma.o +libperf-y += demangle-java.o CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_exec_cmd.o += -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" -DPREFIX="BUILD_STR($(prefix_SQ))"
tools/perf/util/demangle-java.c new file +199 -0
199 lines not on this page · raw message
tools/perf/util/demangle-java.h new file +10 -0
@@ -0,0 +1,10 @@ +#ifndef __PERF_DEMANGLE_JAVA +#define __PERF_DEMANGLE_JAVA 1 +/* +* demangle function flags +*/ +#define JAVA_DEMANGLE_NORET 0x1/* do not process return type */ + +char *java_demangle_sym (const char *str,int flags); + +#endif /* __PERF_DEMANGLE_JAVA */
tools/perf/util/symbol-elf.c +3 -0
@@ -6,6 +6,7 @@#include <inttypes.h> #include "symbol.h" +#include "demangle-java.h" #include "machine.h" #include "vdso.h" #include <symbol/kallsyms.h>
@@ -1070,6 +1071,8 @@ int dso__load_sym(struct dso *dso, struct map *map, demangle_flags =DMGL_PARAMS |DMGL_ANSI ; demangled =bfd_demangle (NULL , elf_name, demangle_flags); +if (demangled ==NULL ) + demangled =java_demangle_sym (elf_name,JAVA_DEMANGLE_NORET );if (demangled !=NULL ) elf_name = demangled; }
Signature · 1 line
1.9.1
PATCH v7 2/5 perf tools: pass session to mmap processing code
In reply to Stephane EranianThis patch passes the perf_session to the mmap processing callbacks. This is needed in a later patch to get to the event list and sample_type.
A consequence of the patch is that the number of argument to machine_deliver_event() is reduced.
--- tools/perf/builtin-inject.c | 14 ++++++++------ tools/perf/builtin-script.c | 14 ++++++-------- tools/perf/util/event.c | 6 ++++-- tools/perf/util/event.h | 6 ++++-- tools/perf/util/session.c | 43 +++++++++++++++++++++++++------------------ tools/perf/util/tool.h | 11 ++++++++--- 6 files changed, 55 insertions(+), 39 deletions(-)
tools/perf/builtin-inject.c +8 -6
@@ -224,11 +224,12 @@ static int perf_event__repipe_sample(struct perf_tool *tool,static int perf_event__repipe_mmap (struct perf_tool *tool,union perf_event *event,struct perf_sample *sample, -struct machine *machine) +struct machine *machine, +struct perf_session *session) {int err; - err =perf_event__process_mmap (tool, event, sample, machine); + err =perf_event__process_mmap (tool, event, sample, machine, session);perf_event__repipe (tool, event, sample, machine);return err;
@@ -237,11 +238,12 @@ static int perf_event__repipe_mmap(struct perf_tool *tool,static int perf_event__repipe_mmap2 (struct perf_tool *tool,union perf_event *event,struct perf_sample *sample, -struct machine *machine) +struct machine *machine, +struct perf_session *session) {int err; - err =perf_event__process_mmap2 (tool, event, sample, machine); + err =perf_event__process_mmap2 (tool, event, sample, machine, session);perf_event__repipe (tool, event, sample, machine);return err;
@@ -669,8 +671,8 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)struct perf_inject inject = { .tool = { .sample = perf_event__repipe_sample, - .mmap = perf_event__repipe, - .mmap2 = perf_event__repipe, + .mmap = perf_event__repipe_mmap, + .mmap2 = perf_event__repipe_mmap, .comm = perf_event__repipe, .fork = perf_event__repipe, .exit = perf_event__repipe,
tools/perf/builtin-script.c +6 -8
@@ -790,14 +790,13 @@ static int process_exit_event(struct perf_tool *tool,static int process_mmap_event (struct perf_tool *tool,union perf_event *event,struct perf_sample *sample, -struct machine *machine) +struct machine *machine, +struct perf_session *session) {struct thread *thread; - struct perf_script *script = container_of(tool, struct perf_script, tool); -struct perf_session *session = script->session;struct perf_evsel *evsel =perf_evlist__id2evsel (session->evlist, sample->id); -if (perf_event__process_mmap (tool, event, sample, machine) <0 ) +if (perf_event__process_mmap (tool, event, sample, machine, session) <0 )return -1 ; thread =machine__findnew_thread (machine, event->mmap.pid, event->mmap.tid);
@@ -821,14 +820,13 @@ static int process_mmap_event(struct perf_tool *tool,static int process_mmap2_event (struct perf_tool *tool,union perf_event *event,struct perf_sample *sample, -struct machine *machine) +struct machine *machine, +struct perf_session *session) {struct thread *thread; - struct perf_script *script = container_of(tool, struct perf_script, tool); -struct perf_session *session = script->session;struct perf_evsel *evsel =perf_evlist__id2evsel (session->evlist, sample->id); -if (perf_event__process_mmap2 (tool, event, sample, machine) <0 ) +if (perf_event__process_mmap2 (tool, event, sample, machine, session) <0 )return -1 ; thread =machine__findnew_thread (machine, event->mmap2.pid, event->mmap2.tid);
tools/perf/util/event.c +4 -2
@@ -786,7 +786,8 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)int perf_event__process_mmap (struct perf_tool *tool__maybe_unused ,union perf_event *event,struct perf_sample *sample, -struct machine *machine) +struct machine *machine, +struct perf_session *session__maybe_unused ) {return machine__process_mmap_event (machine, event, sample); }
@@ -794,7 +795,8 @@ int perf_event__process_mmap(struct perf_tool *tool __maybe_unused,int perf_event__process_mmap2 (struct perf_tool *tool__maybe_unused ,union perf_event *event,struct perf_sample *sample, -struct machine *machine) +struct machine *machine, +struct perf_session *session__maybe_unused ) {return machine__process_mmap2_event (machine, event, sample); }
tools/perf/util/event.h +4 -2
@@ -434,11 +434,13 @@ int perf_event__process_switch(struct perf_tool *tool,int perf_event__process_mmap (struct perf_tool *tool,union perf_event *event,struct perf_sample *sample, -struct machine *machine); +struct machine *machine, +struct perf_session *session);int perf_event__process_mmap2 (struct perf_tool *tool,union perf_event *event,struct perf_sample *sample, -struct machine *machine); +struct machine *machine, +struct perf_session *session); int perf_event__process_fork(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample,
tools/perf/util/session.c +25 -18
125 lines not on this page · raw message
tools/perf/util/tool.h +8 -3
@@ -34,12 +34,15 @@ typedef int (*event_oe)(struct perf_tool *tool, union perf_event *event,typedef s64 (*event_op3 )(struct perf_tool *tool,union perf_event *event,struct perf_session *session); + +typedef int (*event_op4 )(struct perf_tool *tool,union perf_event *event, +struct perf_sample *sample,struct machine *machine, +struct perf_session *session); +struct perf_tool {event_sample sample, read; -event_op mmap, - mmap2, - comm, +event_op comm, fork, exit, lost,
@@ -57,6 +60,8 @@ struct perf_tool { auxtrace_info, auxtrace_error;event_op3 auxtrace; +event_op4 mmap, + mmap2;bool ordered_events;bool ordering_requires_timestamps; };
Signature · 1 line
1.9.1
PATCH v7 3/5 perf inject: add jitdump mmap injection support
In reply to Stephane EranianThis patch adds a --jit option to perf inject.
This options injects MMAP records into the perf.data file to cover the jitted code mmaps. It also emits ELF images for each function in the jidump file. Those images are created where the jitdump file is. The MMAP records point to that location as well.
Typical flow:
$ perf record -k mono -- java -agentpath:libpjvmti.so java_class $ perf inject --jit -i perf.data -o perf.data.jitted $ perf report -i perf.data.jitted
Note that jitdump.h support is not limited to Java, it works with any jitted environment modified to emit the jitdump file format, include those where code can be jitted multiple times and moved around.
The jitdump.h format is adapted from the Oprofile project.
The genelf.c (ELF binary generation) depends on MD5 hash encoding for the buildid. To enable this, libssl-dev must be installed. If not, then genelf.c defaults to using urandom to generate the buildid, which is not ideal. The Makefile auto-detects the presence on libssl-dev.
This version mmaps the jitdump file to create a marker MMAP record in the perf.data file. The marker is used to detect jitdump and cause perf inject to inject the jitted mmaps and generate ELF images for jitted functions.
--- tools/build/Makefile.feature | 2 + tools/build/feature/Makefile | 4 + tools/perf/Documentation/perf-inject.txt | 7 + tools/perf/builtin-inject.c | 83 ++++ tools/perf/config/Makefile | 11 + tools/perf/util/Build | 2 + tools/perf/util/genelf.c | 441 ++++++++++++++++++++ tools/perf/util/genelf.h | 56 +++ tools/perf/util/jitdump.c | 664 +++++++++++++++++++++++++++++++ tools/perf/util/jitdump.h | 116 ++++++ 10 files changed, 1386 insertions(+) create mode 100644 tools/perf/util/genelf.c create mode 100644 tools/perf/util/genelf.h create mode 100644 tools/perf/util/jitdump.c create mode 100644 tools/perf/util/jitdump.h
tools/build/Makefile.feature +2 -0
@@ -46,6 +46,7 @@ FEATURE_TESTS ?= \ libpython \ libpython-version \ libslang \ + libcrypto \ libunwind \ pthread-attr-setaffinity-np \ stackprotector-all \
@@ -67,6 +68,7 @@ FEATURE_DISPLAY ?= \ libperl \ libpython \ libslang \ + libcrypto \ libunwind \ libdw-dwarf-unwind \ zlib \
tools/build/feature/Makefile +4 -0
@@ -24,6 +24,7 @@ FILES= \ test-libpython.bin \ test-libpython-version.bin \ test-libslang.bin \ + test-libcrypto.bin \ test-libunwind.bin \ test-libunwind-debug-frame.bin \ test-pthread-attr-setaffinity-np.bin \
@@ -104,6 +105,9 @@ endif test-libslang.bin: $(BUILD) -I/usr/include/slang -lslang +test-libcrypto.bin: + $(BUILD) -lcrypto + test-gtk2.bin: $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null)
tools/perf/Documentation/perf-inject.txt +7 -0
@@ -53,6 +53,13 @@ include::itrace.txt[] --strip:: Use with --itrace to strip out non-synthesized events. +-j:: +--jit:: + Process jitdump files by injecting the mmap records corresponding to jitted + functions. This option also generates the ELF images for each jitted function + found in the jitdumps files captured in the input perf.data file. Use this option + if you are monitoring environment using JIT runtimes, such as Java, DART or V8. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1]
tools/perf/builtin-inject.c +83 -0
125 lines not on this page · raw message
tools/perf/config/Makefile +11 -0
@@ -380,6 +380,17 @@ ifndef NO_LIBAUDIT endif endif +ifndef NO_LIBCRYPTO + ifneq ($(feature-libcrypto), 1) + msg := $(warning No libcrypto.h found, disables jitted code injection, please install libssl-devel or libssl-dev); + NO_LIBCRYPTO := 1 + else + CFLAGS += -DHAVE_LIBCRYPTO_SUPPORT + EXTLIBS += -lcrypto + $(call detected,CONFIG_CRYPTO) + endif +endif + ifdef NO_NEWT NO_SLANG=1 endif
tools/perf/util/Build +2 -0
@@ -108,6 +108,8 @@ libperf-$(CONFIG_PERF_REGS) += perf_regs.o libperf-$(CONFIG_ZLIB) += zlib.o libperf-$(CONFIG_LZMA) += lzma.o libperf-y += demangle-java.o +libperf-y += jitdump.o +libperf-y += genelf.o CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_exec_cmd.o += -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" -DPREFIX="BUILD_STR($(prefix_SQ))"
tools/perf/util/genelf.c new file +441 -0
441 lines not on this page · raw message
tools/perf/util/genelf.h new file +56 -0
56 lines not on this page · raw message
tools/perf/util/jitdump.c new file +664 -0
664 lines not on this page · raw message
tools/perf/util/jitdump.h new file +116 -0
116 lines not on this page · raw message
Signature · 1 line
1.9.1
Adrian Hunter
reviewerRe: [PATCH v7 3/5] perf inject: add jitdump mmap injection support
Quoted 33 lines · Stephane Eranian
This patch adds a --jit option to perf inject.
This options injects MMAP records into the perf.data file to cover the jitted code mmaps. It also emits ELF images for each function in the jidump file. Those images are created where the jitdump file is. The MMAP records point to that location as well.
Typical flow:
$ perf record -k mono -- java -agentpath:libpjvmti.so java_class $ perf inject --jit -i perf.data -o perf.data.jitted $ perf report -i perf.data.jittedNote that
jitdump.hsupport is not limited to Java, it works with any jitted environment modified to emit the jitdump file format, include those where code can be jitted multiple times and moved around.The
jitdump.hformat is adapted from the Oprofile project.The
genelf.c(ELF binary generation) depends on MD5 hash encoding for the buildid. To enable this, libssl-dev must be installed. If not, thengenelf.cdefaults to using urandom to generate the buildid, which is not ideal. TheMakefileauto-detects the presence on libssl-dev.This version mmaps the jitdump file to create a marker MMAP record in the perf.data file. The marker is used to detect jitdump and cause perf inject to inject the jitted mmaps and generate ELF images for jitted functions.
Signed-off-by Stephane Eranian <[email protected]>---
<SNIP>
Quoted 55 lines
tools/perf/util/jitdump.c new file +55
-0+static int jit_repipe_code_load (struct jit_buf_desc *jd,union jr_entry *jr) +{ +struct perf_sample sample; +union perf_event *event; +struct perf_tool *tool = jd->session->tool; +uint64_t code, addr; +char *filename; +struct stat st; +size_t size; +u16 idr_size; +const char *sym; +uint32_t count; +int ret, csize; +pid_t pid, tid; +struct { +u32 pid, tid; +u64 time; + } *id; + + pid = jr->load.pid; + tid = jr->load.tid; + csize = jr->load.code_size; + addr = jr->load.code_addr; + sym = (void *)((unsigned long )jr +sizeof (jr->load)); + code = (unsigned long )jr + jr->load.p.total_size - csize; + count = jr->load.code_index; + idr_size = jd->machine->id_hdr_size; +/* +* +16 to account for sample_id_all (hack) +*/ + event =calloc (1 ,sizeof (*event) + idr_size); +if (!event) +return -1 ; + + filename = event->mmap2.filename; + size =snprintf (filename,PATH_MAX ,"%s/jitted-%d-%u.so" , + jd->dir, + pid, + count); + + size++;/* for \0 */ + + size =PERF_ALIGN (size,sizeof (u64)); + + ret =jit_emit_elf (filename, sym, addr, (const void *)code, csize); + +if (jd->debug_data && jd->nr_debug_entries) { +free (jd->debug_data); + jd->debug_data =NULL ; + jd->nr_debug_entries =0 ; + } + +if (ret) { +free (event); +return -1 ;
tools/perf/util/jitdump.c line 364
Quoted 10 lines
tools/perf/util/jitdump.c new file +10
-0+ } +if (stat (filename, &st)) +memset (&st,0 ,sizeof (stat)); + + event->mmap2.header.type =PERF_RECORD_MMAP2 ; + event->mmap2.header.misc =PERF_RECORD_MISC_USER ; + event->mmap2.header.size = (sizeof (event->mmap2) - + (sizeof (event->mmap2.filename) - size) + idr_size); + + event->mmap2.pgoff =0 ;
I was testing this with Intel PT but getting decoding errors.
I think the problem is here, as described below, but I will look at it more next week.
event->mmap2.pgoff = 0 says that the code running at 'addr' can be found at offset 0 in the elf file, which is not correct. The MMAP event has to inform where in the file the code is.
Quoted 41 lines
tools/perf/util/jitdump.c new file +41
-0+ event->mmap2.start = addr; + event->mmap2.len = csize; + event->mmap2.pid = pid; + event->mmap2.tid = tid; + event->mmap2.ino = st.st_ino; + event->mmap2.maj =major (st.st_dev); + event->mmap2.min =minor (st.st_dev); + event->mmap2.prot = st.st_mode; + event->mmap2.flags =MAP_SHARED ; + event->mmap2.ino_generation =1 ; + + id = (void *)((unsigned long )event + event->mmap.header.size - idr_size); +if (jd->sample_type &PERF_SAMPLE_TID ) { + id->pid = pid; + id->tid = tid; + } +if (jd->sample_type &PERF_SAMPLE_TIME ) + id->time = jr->load.p.timestamp; + +/* +* create pseudo sample to induce dso hit increment +* use first address as sample address +*/ +memset (&sample,0 ,sizeof (sample)); + sample.pid = pid; + sample.tid = tid; + sample.time = id->time; + sample.ip = addr; + + ret =perf_event__process_mmap2 (tool, event, &sample, jd->machine, jd->session); +if (ret) +return ret; + + ret =jit_inject_event (jd, event); +/* +* mark dso as use to generate buildid in the header +*/ +if (!ret) +build_id__mark_dso_hit (tool, event, &sample,NULL , jd->machine); + +return ret;
PATCH v7 4/5 perf tools: add JVMTI agent library
In reply to Stephane EranianThis is a standalone JVMTI library to help profile Java jitted code with perf record/perf report. The library is not installed or compiled automatically by perf Makefile. It is not used directly by perf. It is arch agnostic and has been tested on X86 and ARM. It needs to be used with a Java runtime, such as OpenJDK, as follows:
$ java -agentpath:libjvmti.so .......
When used this way, java will generate a jitdump binary file in $HOME/.debug/java/jit/java-jit-*
This binary dump file contains information to help symbolize and annotate jitted code.
The jitdump information must be injected into the perf.data file using:
$ perf inject --jit -i perf.data -o perf.data.jitted
This injects the MMAP records to cover the jitted code and also generates one ELF image for each jitted function. The ELF images are created in the same subdir as the jitdump file. The MMAP records point there too.
Then, to visualize the function or asm profile, simply use the regular perf commands:
$ perf report -i perf.data.jitted or $ perf annotate -i perf.data.jitted
JVMTI agent code adapted from the OProfile's opagent code.
This version of the JVMTI agent is using the CLOCK_MONOTIC as the time source to timestamp jit samples. To correlate with perf_events samples, it needs to run on kernel 4.0.0-rc5+ or later with the following commit from Peter Zijlstra:
34f4392 perf: Add per event clockid support
With this patch recording jitted code is done as follows:
$ perf record -k mono -- java -agentpath:libjvmti.so .......
--- tools/perf/jvmti/Makefile | 70 +++++++ tools/perf/jvmti/jvmti_agent.c | 464 +++++++++++++++++++++++++++++++++++++++++ tools/perf/jvmti/jvmti_agent.h | 29 +++ tools/perf/jvmti/libjvmti.c | 208 ++++++++++++++++++ 4 files changed, 771 insertions(+) create mode 100644 tools/perf/jvmti/Makefile create mode 100644 tools/perf/jvmti/jvmti_agent.c create mode 100644 tools/perf/jvmti/jvmti_agent.h create mode 100644 tools/perf/jvmti/libjvmti.c
tools/perf/jvmti/Makefile new file +70 -0
@@ -0,0 +1,70 @@ +ARCH=$(shell uname -m) + +ifeq ($(ARCH), x86_64) +JARCH=amd64 +endif +ifeq ($(ARCH), armv7l) +JARCH=armhf +endif +ifeq ($(ARCH), armv6l) +JARCH=armhf +endif +ifeq ($(ARCH), ppc64) +JARCH=powerpc +endif +ifeq ($(ARCH), ppc64le) +JARCH=powerpc +endif + +DESTDIR=/usr/local + +VERSION=1 +REVISION=0 +AGE=0 + +LN=ln -sf +RM=rm + +SLIBJVMTI=libjvmti.so.$(VERSION).$(REVISION).$(AGE) +VLIBJVMTI=libjvmti.so.$(VERSION) +SLDFLAGS=-shared -Wl,-soname -Wl,$(VLIBJVMTI) +SOLIBEXT=so + +JDIR=$(shell /usr/sbin/update-java-alternatives -l | head -1 | cut -d ' ' -f 3) +# -lrt required in 32-bit mode for clock_gettime() +LIBS=-lelf -lrt +INCDIR=-I $(JDIR)/include -I $(JDIR)/include/linux + +TARGETS=$(SLIBJVMTI) + +SRCS=libjvmti.c jvmti_agent.c +OBJS=$(SRCS:.c=.o) +SOBJS=$(OBJS:.o=.lo) +OPT=-O2 -g -Werror -Wall + +CFLAGS=$(INCDIR) $(OPT) + +all: $(TARGETS) + +.c.o: + $(CC) $(CFLAGS) -c $*.c +.c.lo: + $(CC) -fPIC -DPIC $(CFLAGS) -c $*.c -o $*.lo + +$(OBJS) $(SOBJS): Makefile jvmti_agent.h ../util/jitdump.h + +$(SLIBJVMTI): $(SOBJS) + $(CC) $(CFLAGS) $(SLDFLAGS) -o $@ $(SOBJS) $(LIBS) + $(LN) $@ libjvmti.$(SOLIBEXT) + +clean: + $(RM) -f *.o *.so.* *.so *.lo + +install: + -mkdir -p $(DESTDIR)/lib + install -m 755 $(SLIBJVMTI) $(DESTDIR)/lib/ + (cd $(DESTDIR)/lib; $(LN) $(SLIBJVMTI) $(VLIBJVMTI)) + (cd $(DESTDIR)/lib; $(LN) $(SLIBJVMTI) libjvmti.$(SOLIBEXT)) + ldconfig + +.SUFFIXES: .c .S .o .lo
tools/perf/jvmti/jvmti_agent.c new file +464 -0
464 lines not on this page · raw message
tools/perf/jvmti/jvmti_agent.h new file +29 -0
@@ -0,0 +1,29 @@ +#ifndef __JVMTI_AGENT_H__ +#define __JVMTI_AGENT_H__ + +#include <sys/types.h> +#include <stdint.h> +#include <jvmti.h> + +#define __unused __attribute__((unused)) + +#if defined(__cplusplus) +extern "C" { +#endif + +void *jvmti_open (void ); +int jvmti_close (void *agent); +int jvmti_write_code (void *agent,char const *symbol_name, +uint64_t vma,void const *code, +const unsigned int code_size); +int jvmti_write_debug_info (void *agent, +uint64_t code, +const char *file, +jvmtiAddrLocationMap const *map, +jvmtiLineNumberEntry *tab,jint nr); + +#if defined(__cplusplus) +} + +#endif +#endif /* __JVMTI_H__ */
tools/perf/jvmti/libjvmti.c new file +208 -0
208 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH v7 5/5 perf/jit: add source line info support
In reply to Stephane EranianThis patch adds source line information support to perf for jitted code. The source line info must be emitted by the runtime, such as JVMTI. Perf injects extract the source line info from the jitdump file and adds the corresponding .debug_lines section in the ELF image generated for each jitted function. The source line enables matching any address in the profile with a source file and line number. The improvement is visible in perf annotate with the source code displayed alongside the assembly code.
The dwarf code leverages the support from OProfile which is also released under GPLv2. Copyright 2007 OProfile authors.
--- tools/perf/jvmti/jvmti_agent.c | 32 +-- tools/perf/jvmti/jvmti_agent.h | 11 +- tools/perf/jvmti/libjvmti.c | 122 ++++++++- tools/perf/util/Build | 1 + tools/perf/util/genelf.c | 15 +- tools/perf/util/genelf.h | 6 +- tools/perf/util/genelf_debug.c | 610 +++++++++++++++++++++++++++++++++++++++++ tools/perf/util/jitdump.c | 8 +- 8 files changed, 766 insertions(+), 39 deletions(-) create mode 100644 tools/perf/util/genelf_debug.c
tools/perf/jvmti/jvmti_agent.c +16 -16
@@ -373,20 +373,20 @@ jvmti_write_code(void *agent, char const *sym,int jvmti_write_debug_info (void *agent,uint64_t code,const char *file, -jvmtiAddrLocationMap const *map, -jvmtiLineNumberEntry *li,jint num) +jvmti_line_info_t *li,int nr_lines) { -static const char *prev_str ="\xff" ;struct jr_code_debug_info rec;size_t sret, len, size, flen;size_t padding_count; +uint64_t addr; +const char *fn = file;FILE *fp = agent;int i;/* * no entry to write */ -if (!num) +if (!nr_lines)return 0 ;if (!fp) {
@@ -400,17 +400,18 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file, size =sizeof (rec); rec.p.timestamp =perf_get_timestamp (); rec.code_addr = (uint64_t )(uintptr_t )code; - rec.nr_entry = num; + rec.nr_entry = nr_lines;/* * on disk source line info layout: * uint64_t : addr * int : line number +* int : column discriminator * file[] : source file name * padding : pad to multiple of 8 bytes */ - size += num * (sizeof (uint64_t ) +sizeof (int )); - size += flen + (num -1 ) *2 ; + size += nr_lines *sizeof (struct debug_entry ); + size += flen * nr_lines;/* * pad to 8 bytes */
@@ -428,28 +429,27 @@ jvmti_write_debug_info(void *agent, uint64_t code, const char *file,if (sret !=1 ) goto error; -for (i =0 ; i < num; i++) { -uint64_t addr; +for (i =0 ; i < nr_lines; i++) { - addr = (uint64_t )map[i].start_address; + addr = (uint64_t )li[i].pc; len =sizeof (addr); sret =fwrite_unlocked (&addr, len,1 , fp);if (sret !=1 ) goto error; - len =sizeof (int ); + len =sizeof (li[0 ].line_number); sret =fwrite_unlocked (&li[i].line_number, len,1 , fp);if (sret !=1 ) goto error; -if (i ==0 ) { - sret =fwrite_unlocked (file, flen,1 , fp); - }else { - sret =fwrite_unlocked (prev_str,2 ,1 , fp); - } + len =sizeof (li[0 ].discrim); + sret =fwrite_unlocked (&li[i].discrim, len,1 , fp);if (sret !=1 ) goto error; + sret =fwrite_unlocked (fn, flen,1 , fp); +if (sret !=1 ) + goto error; }if (padding_count) sret =fwrite_unlocked (pad_bytes, padding_count,1 , fp);
tools/perf/jvmti/jvmti_agent.h +9 -2
@@ -11,16 +11,23 @@extern "C" {#endif +typedef struct { +unsigned long pc; +int line_number; +int discrim;/* discriminator -- 0 for now */ +}jvmti_line_info_t ; +void *jvmti_open (void );int jvmti_close (void *agent);int jvmti_write_code (void *agent,char const *symbol_name,uint64_t vma,void const *code,const unsigned int code_size); +int jvmti_write_debug_info (void *agent,uint64_t code,const char *file, -jvmtiAddrLocationMap const *map, -jvmtiLineNumberEntry *tab,jint nr); +jvmti_line_info_t *li, +int nr_lines);#if defined(__cplusplus) }
tools/perf/jvmti/libjvmti.c +109 -13
177 lines not on this page · raw message
tools/perf/util/Build +1 -0
@@ -110,6 +110,7 @@ libperf-$(CONFIG_LZMA) += lzma.o libperf-y += demangle-java.o libperf-y += jitdump.o libperf-y += genelf.o +libperf-y += genelf_debug.o CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_exec_cmd.o += -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" -DPREFIX="BUILD_STR($(prefix_SQ))"
tools/perf/util/genelf.c +11 -4
@@ -156,7 +156,8 @@ gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *cod*/ int jit_write_elf (int fd,uint64_t load_addr,const char *sym, -const void *code,int csize) +const void *code,int csize, +void *debug,int nr_debug_entries) {Elf *e;Elf_Data *d;
@@ -384,9 +385,15 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, shdr->sh_size =sizeof (bnote); shdr->sh_entsize =0 ; -if (elf_update (e,ELF_C_WRITE ) <0 ) { -warnx ("elf_update 4 failed" ); - goto error; +if (debug && nr_debug_entries) { + retval =jit_add_debug_info (e, load_addr, debug, nr_debug_entries); +if (retval) + goto error; + }else { +if (elf_update (e,ELF_C_WRITE ) <0 ) { +warnx ("elf_update 4 failed" ); + goto error; + } } retval =0 ;
tools/perf/util/genelf.h +5 -1
@@ -3,7 +3,11 @@/* genelf.c */ extern int jit_write_elf (int fd,uint64_t code_addr,const char *sym, -const void *code,int csize); +const void *code,int csize, +void *debug,int nr_debug_entries); +/* genelf_debug.c */ +extern int jit_add_debug_info (Elf *e,uint64_t code_addr, +void *debug,int nr_debug_entries);#if defined(__arm__)#define GEN_ELF_ARCH EM_ARM
tools/perf/util/genelf_debug.c new file +610 -0
610 lines not on this page · raw message
tools/perf/util/jitdump.c +5 -3
@@ -63,7 +63,9 @@ jit_emit_elf(char *filename,const char *sym, uint64_t code_addr, const void *code, - int csize) + int csize, + void *debug, + int nr_debug_entries) { int ret, fd;
@@ -76,7 +78,7 @@ jit_emit_elf(char *filename,return -1 ; } - ret =jit_write_elf (fd, code_addr, sym, (const void *)code, csize); + ret =jit_write_elf (fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries);close (fd);
@@ -341,7 +343,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) size =PERF_ALIGN (size,sizeof (u64)); - ret =jit_emit_elf (filename, sym, addr, (const void *)code, csize); + ret =jit_emit_elf (filename, sym, addr, (const void *)code, csize, jd->debug_data, jd->nr_debug_entries);if (jd->debug_data && jd->nr_debug_entries) {free (jd->debug_data);
Signature · 1 line
1.9.1
Peter Zijlstra
maintainerQuoted 23 lines · Stephane Eranian
This patch series extends perf record/report/annotate to enable profiling of jitted (just-in-time compiled) code. The current perf tool provides very limited support for profiling jitted code for some runtime environments. But the support is experimental and cannot be used in complex environments. It relies on files in /tmp, for instance. It does not support annotate mode or rejitted code.
This patch series adds a better way of profiling jitted code with the following advantages:
- support any jitted code environment (some with modifications)
- support Java runtime with JVMTI interface with no modifications
- provides a portable JVMTI agent library
- known to support V8 runtime
- known to support DART runtime
- supports code rejitting and code movements
- no files in /tmp
- meta-data file is unique to each run
- no changes to perf report/annotate
- support per-thread and system-wide profiling
- support monitoring of multiple simultaneous Jit runtimes
- source level view in perf annotate
Quoted 6 lines
The support is based on cooperation with the runtime. For Java runtimes, supporting the JVMTI interface, there is no change necessary. For other runtimes, modifications are necessary to emit the meta-data to support symbolization, annotation, source lines correlation of the samples. Those modifications are relatively straighforward, some have been implemented in V8 and DART.
Do V8 and DART come with these bits or will that be a future contribution?
Quoted 6 lines
This will also generate an ELF image for each jitted function. The injected MMAP records will point to these ELF images. The reasoning behind using ELF images is that it makes processing for perf report and annotate automatic and transparent. It also makes it easier to package and analyze on a remote machine. Binutils tools can decode the ELF images easily.
The generation of ELF files is really nice!
All in all this looks really nice.
Thanks for doing this.
Quoted 11 lines · Peter Zijlstra
Quoted 5 lines
This will also generate an ELF image for each jitted function. The injected MMAP records will point to these ELF images. The reasoning behind using ELF images is that it makes processing for perf report and annotate automatic and transparent. It also makes it easier to package and analyze on a remote machine. Binutils tools can decode the ELF images easily.
The generation of ELF files is really nice!
All in all this looks really nice.
Thanks for doing this.
Seconded, nice stuff!
Ingo
Em Thu, Oct 01, 2015 at 11:39:14AM +0200, Ingo Molnar escreveu:
Quoted 7 lines · Peter Zijlstra
Quoted 5 lines
Quoted 5 lines
This will also generate an ELF image for each jitted function. The injected MMAP records will point to these ELF images. The reasoning behind using ELF images is that it makes processing for perf report and annotate automatic and transparent. It also makes it easier to package and analyze on a remote machine. Binutils tools can decode the ELF images easily.
Quoted 7 lines
Quoted 5 lines
The generation of ELF files is really nice!
All in all this looks really nice.
Thanks for doing this.
Seconded, nice stuff!
Thirded! ;-)
Gave it a quick look, the ELF generation thing could later be librarized somehow for other uses, the passing of perf_session for sample_type can maybe use evsel->attr.sample_type instead, no?
Will try to try it after lunch :-)
- Arnaldo
Hi Peter,
Quoted 31 lines · Peter Zijlstra
Quoted 23 lines · Stephane Eranian
This patch series extends perf record/report/annotate to enable profiling of jitted (just-in-time compiled) code. The current perf tool provides very limited support for profiling jitted code for some runtime environments. But the support is experimental and cannot be used in complex environments. It relies on files in /tmp, for instance. It does not support annotate mode or rejitted code.
This patch series adds a better way of profiling jitted code with the following advantages:
- support any jitted code environment (some with modifications)
- support Java runtime with JVMTI interface with no modifications
- provides a portable JVMTI agent library
- known to support V8 runtime
- known to support DART runtime
- supports code rejitting and code movements
- no files in /tmp
- meta-data file is unique to each run
- no changes to perf report/annotate
- support per-thread and system-wide profiling
- support monitoring of multiple simultaneous Jit runtimes
- source level view in perf annotateQuoted 6 lines
The support is based on cooperation with the runtime. For Java runtimes, supporting the JVMTI interface, there is no change necessary. For other runtimes, modifications are necessary to emit the meta-data to support symbolization, annotation, source lines correlation of the samples. Those modifications are relatively straighforward, some have been implemented in V8 and DART.
Quoted 3 lines
Do V8 and DART come with these bits or will that be a future contribution?
I think both had this working some time ago. I have not had a chance to verify if this new version still works. I did not change the format of the jitdump
file, so if they use the same clock source for their timestamp as perf record, it
should still work. All of this hinges on the fact that V8 & DART developers run with 4.1 to get your clock changes.
Quoted 11 lines
Quoted 6 lines
This will also generate an ELF image for each jitted function. The injected MMAP records will point to these ELF images. The reasoning behind using ELF images is that it makes processing for perf report and annotate automatic and transparent. It also makes it easier to package and analyze on a remote machine. Binutils tools can decode the ELF images easily.
The generation of ELF files is really nice!
All in all this looks really nice.
Thanks!
Quoted 1 line
Thanks for doing this.
G'Day,
Quoted 31 lines · Stephane Eranian
This patch series extends perf record/report/annotate to enable profiling of jitted (just-in-time compiled) code. The current perf tool provides very limited support for profiling jitted code for some runtime environments. But the support is experimental and cannot be used in complex environments. It relies on files in /tmp, for instance. It does not support annotate mode or rejitted code.
This patch series adds a better way of profiling jitted code with the following advantages:
- support any jitted code environment (some with modifications)
- support Java runtime with JVMTI interface with no modifications
- provides a portable JVMTI agent library
- known to support V8 runtime
- known to support DART runtime
- supports code rejitting and code movements
- no files in /tmp
- meta-data file is unique to each run
- no changes to perf report/annotate
- support per-thread and system-wide profiling
- support monitoring of multiple simultaneous Jit runtimes
- source level view in perf annotateThe support is based on cooperation with the runtime. For Java runtimes, supporting the JVMTI interface, there is no change necessary. For other runtimes, modifications are necessary to emit the meta-data to support symbolization, annotation, source lines correlation of the samples. Those modifications are relatively straighforward, some have been implemented in V8 and DART.
Quoted 4 lines
The jit environment emits a binary dump file which contains the jitted code (in raw format) and meta-data describing the mapping of functions. The binary format is documented in the
jitdump.hheader file. It is adapted from the OProfile jitdump format.
While this is impressive work, I don't think I'd use it very much, and I wouldn't encourage others too either. Is it right that this approach needs to be turned on from runtime start, and will constantly emit timestamped JIT records? I'd use that as a backup for existing techniques for perf_events and jitted runtimes.
Right now we (Netflix) can profile Java in production using perf_events, using an on-demand symbol dump: the perf-map-agent JVMTI[1]. This agent used to be loaded on startup, like this patchset, which cost overhead: CPU, filesystem I/O, storage. The problem was that we didn't know which of the tens of thousands of instances we'd want to profile, so loading an always-on JIT symbol dumper on all instances would cost resources. With on-demand, we only dump symbols on the few instances needed. On-demand has caveats, of course, and symbols can change during the profile. But so far that's not been a problem.
So this seems like a lot of changes for what won't be our primary way of using perf_events on Java or Node.js, which we currently can profile with perf_events effectively. So long as we're aware of that.
Brendan
Brendan,
Quoted 39 lines · Brendan Gregg
G'Day,
Quoted 31 lines · Stephane Eranian
This patch series extends perf record/report/annotate to enable profiling of jitted (just-in-time compiled) code. The current perf tool provides very limited support for profiling jitted code for some runtime environments. But the support is experimental and cannot be used in complex environments. It relies on files in /tmp, for instance. It does not support annotate mode or rejitted code.
This patch series adds a better way of profiling jitted code with the following advantages:
- support any jitted code environment (some with modifications)
- support Java runtime with JVMTI interface with no modifications
- provides a portable JVMTI agent library
- known to support V8 runtime
- known to support DART runtime
- supports code rejitting and code movements
- no files in /tmp
- meta-data file is unique to each run
- no changes to perf report/annotate
- support per-thread and system-wide profiling
- support monitoring of multiple simultaneous Jit runtimes
- source level view in perf annotateThe support is based on cooperation with the runtime. For Java runtimes, supporting the JVMTI interface, there is no change necessary. For other runtimes, modifications are necessary to emit the meta-data to support symbolization, annotation, source lines correlation of the samples. Those modifications are relatively straighforward, some have been implemented in V8 and DART.
Quoted 4 lines
The jit environment emits a binary dump file which contains the jitted code (in raw format) and meta-data describing the mapping of functions. The binary format is documented in the
jitdump.hheader file. It is adapted from the OProfile jitdump format.
Quoted 6 lines
While this is impressive work, I don't think I'd use it very much, and I wouldn't encourage others too either. Is it right that this approach needs to be turned on from runtime start, and will constantly emit timestamped JIT records? I'd use that as a backup for existing techniques for
perf_eventsand jitted runtimes.
This boils down that when does the JIT runtime emit the jitdump data? In the case of openJDK and given how I wrote the agent, it needs to run from start to finish. In order for perf to have full visibility, it needs to get info about all the code that has been jitted so far. This could be done after start if there was somehow a protocol to handle this in the runtime, like a signal. It would just need to dump the current status, including all the code. The rest of the support would work. In other words, if there was a way to signal to the runtime that it is being monitored and that it needs to dump its state, then everything would work. To avoid generating more dumps, the runtime would also have to be informed that monitoring has stopped.
Quoted 11 lines
Right now we (Netflix) can profile Java in production using
perf_events, using an on-demand symbol dump: the perf-map-agent JVMTI[1]. This agent used to be loaded on startup, like this patchset, which cost overhead: CPU, filesystem I/O, storage. The problem was that we didn't know which of the tens of thousands of instances we'd want to profile, so loading an always-on JIT symbol dumper on all instances would cost resources. With on-demand, we only dump symbols on the few instances needed. On-demand has caveats, of course, and symbols can change during the profile. But so far that's not been a problem.
I am guessing that for what you are doing getting the jitted code symbol names are sufficient. This patch series is not really needed for this, you already have the/tmp/perf-map approach. This is mostly for the user program developers. This series
addresses the needs of the other category, namely the jit compiler and runtime developers who need to study the quality of the generated code. And there, they need to see the profile of the jitted native code. This is the major value-add of this
series. I would also add the support for re-jitting and code movements.
Quoted 4 lines
So this seems like a lot of changes for what won't be our primary way of using
perf_eventson Java or Node.js, which we currently can profile withperf_eventseffectively. So long as we're aware of that.
The changes to perf are small. There are no change to perf record, report, annotate. just the injection code with the jitdump reader and elf creation.
Quoted 3 lines
Brendan
Quoted 49 lines · Stephane Eranian
Brendan,
Quoted 39 lines · Brendan Gregg
G'Day,
Quoted 31 lines · Stephane Eranian
This patch series extends perf record/report/annotate to enable profiling of jitted (just-in-time compiled) code. The current perf tool provides very limited support for profiling jitted code for some runtime environments. But the support is experimental and cannot be used in complex environments. It relies on files in /tmp, for instance. It does not support annotate mode or rejitted code.
This patch series adds a better way of profiling jitted code with the following advantages:
- support any jitted code environment (some with modifications)
- support Java runtime with JVMTI interface with no modifications
- provides a portable JVMTI agent library
- known to support V8 runtime
- known to support DART runtime
- supports code rejitting and code movements
- no files in /tmp
- meta-data file is unique to each run
- no changes to perf report/annotate
- support per-thread and system-wide profiling
- support monitoring of multiple simultaneous Jit runtimes
- source level view in perf annotateThe support is based on cooperation with the runtime. For Java runtimes, supporting the JVMTI interface, there is no change necessary. For other runtimes, modifications are necessary to emit the meta-data to support symbolization, annotation, source lines correlation of the samples. Those modifications are relatively straighforward, some have been implemented in V8 and DART.
Quoted 4 lines
The jit environment emits a binary dump file which contains the jitted code (in raw format) and meta-data describing the mapping of functions. The binary format is documented in the
jitdump.hheader file. It is adapted from the OProfile jitdump format.Quoted 6 lines
While this is impressive work, I don't think I'd use it very much, and I wouldn't encourage others too either. Is it right that this approach needs to be turned on from runtime start, and will constantly emit timestamped JIT records? I'd use that as a backup for existing techniques for
perf_eventsand jitted runtimes.
Quoted 10 lines
This boils down that when does the JIT runtime emit the jitdump data? In the case of openJDK and given how I wrote the agent, it needs to run from start to finish. In order for perf to have full visibility, it needs to get info about all the code that has been jitted so far. This could be done after start if there was somehow a protocol to handle this in the runtime, like a signal. It would just need to dump the current status, including all the code. The rest of the support would work. In other words, if there was a way to signal to the runtime that it is being monitored and that it needs to dump its state, then everything would work. To avoid generating more dumps, the runtime would also have to be informed that monitoring has stopped.
Ok, so it's possible that an on-demand approach could work, if the agent can be modified. And since perf-map-agent made this transition
(https://github.com/jrudolph/perf-map-agent), examples of how to do this are probably in its git history. :) The outcome could well be something like:
perf record -F 99 -a -g -- perf_java_agent --sleep 10
Where a perf_java_agent tool attached to all Javas, dumped initial symbols, and then logged timestamped symbols during the run (--sleep 10), then detached at the end. That would be ideal. Which just means changing the Java agent, not the perf implementation.
Brendan