Skip to contentSkip to conversation

perf: add support for profiling jitted code

PATCH Superseded Stephane Eranian 6 participants 14 messages Oct 1, 2015 – Oct 9, 2015 perf events

v7 of 8 Superseded Latest is v8 →

lore
Share & tools
lore
#0 lore raw

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
#1 lore raw

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
index 4bc7a9a..495a27a 100644
@@ -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
new file mode 100644
index 0000000..a981c1f
@@ -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
index 475d88d..3fce39f 100644
@@ -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
#2 lore raw

PATCH v7 2/5 perf tools: pass session to mmap processing code

In reply to Stephane Eranian

This 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
index 0a945d2..db5393e 100644
@@ -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
index 8ce1c6b..93d09f2 100644
@@ -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
index b1bb348..555f876 100644
@@ -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
index a0dbcbd..a33d50d 100644
@@ -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
index cab8cc2..b4f94fe 100644
@@ -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
#3 lore raw

PATCH v7 3/5 perf inject: add jitdump mmap injection support

In reply to 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.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
index 72817e4..a245539 100644
@@ -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
index e43a297..fa3b6c0 100644
@@ -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
index 0b1cede..87b2588 100644
@@ -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
index ab09ada..2858424 100644
@@ -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
index 495a27a..9fd4906 100644
@@ -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
reviewer
#4 lore raw

Re: [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.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.

---

<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;
#5 lore raw

PATCH v7 4/5 perf tools: add JVMTI agent library

In reply to Stephane Eranian

This 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
new file mode 100644
index 0000000..22efd13
@@ -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
new file mode 100644
index 0000000..8251a1c
@@ -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
#6 lore raw

PATCH v7 5/5 perf/jit: add source line info support

In reply to Stephane Eranian

This 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
index 7d96581..ca064be 100644
@@ -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
index 8251a1c..bedf5d0 100644
@@ -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
index 9fd4906..b6f8c96 100644
@@ -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
index 3832cd0..6ce8cf3 100644
@@ -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
index 79b89e2..957e3de 100644
@@ -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
index e910130..42b967b 100644
@@ -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
maintainer
#7 lore raw
In reply to Stephane Eranian
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 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.

#8 lore raw
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

#9 lore raw

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

#10 lore raw
In reply to Peter Zijlstra

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 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.

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.

#11 lore raw
In reply to Stephane Eranian

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 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.

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.h header 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

[1] http://techblog.netflix.com/2015/07/java-in-flames.html

#12 lore raw

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 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.

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.h header 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_events and 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_events on Java or Node.js, which we currently can profile with perf_events effectively. 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

[1] http://techblog.netflix.com/2015/07/java-in-flames.html

#13 lore raw
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 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.

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.h header 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_events and 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