Re: [PATCH v6 3/4] perf inject: add jitdump mmap injection support

From: Adrian Hunter
Date: Wed Apr 01 2015 - 03:01:14 EST


On 31/03/15 01:19, Stephane Eranian wrote:
> 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.
>
> Signed-off-by: Stephane Eranian <eranian@xxxxxxxxxx>
> ---
> tools/build/Makefile.feature | 2 +
> tools/build/feature/Makefile | 4 +
> tools/build/feature/test-libcrypto.c | 9 +
> tools/perf/Documentation/perf-inject.txt | 6 +
> tools/perf/builtin-inject.c | 60 +++-
> tools/perf/config/Makefile | 11 +
> tools/perf/util/Build | 2 +
> tools/perf/util/genelf.c | 479 +++++++++++++++++++++++++
> tools/perf/util/genelf.h | 6 +
> tools/perf/util/jit.h | 15 +
> tools/perf/util/jitdump.c | 588 +++++++++++++++++++++++++++++++
> tools/perf/util/jitdump.h | 92 +++++
> 12 files changed, 1261 insertions(+), 13 deletions(-)
> create mode 100644 tools/build/feature/test-libcrypto.c
> create mode 100644 tools/perf/util/genelf.c
> create mode 100644 tools/perf/util/genelf.h
> create mode 100644 tools/perf/util/jit.h
> create mode 100644 tools/perf/util/jitdump.c
> create mode 100644 tools/perf/util/jitdump.h
>
> diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
> index 3a0b0ca..8b468a3 100644
> --- a/tools/build/Makefile.feature
> +++ b/tools/build/Makefile.feature
> @@ -45,6 +45,7 @@ FEATURE_TESTS = \
> libpython \
> libpython-version \
> libslang \
> + libcrypto \
> libunwind \
> pthread-attr-setaffinity-np \
> stackprotector-all \
> @@ -64,6 +65,7 @@ FEATURE_DISPLAY = \
> libperl \
> libpython \
> libslang \
> + libcrypto \
> libunwind \
> libdw-dwarf-unwind \
> zlib \
> diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
> index 463ed8f..ed86700 100644
> --- a/tools/build/feature/Makefile
> +++ b/tools/build/feature/Makefile
> @@ -23,6 +23,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 \
> @@ -93,6 +94,9 @@ __BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@)
> 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)
>
> diff --git a/tools/build/feature/test-libcrypto.c b/tools/build/feature/test-libcrypto.c
> new file mode 100644
> index 0000000..8580c40
> --- /dev/null
> +++ b/tools/build/feature/test-libcrypto.c
> @@ -0,0 +1,9 @@
> +#include <openssl/md5.h>
> +
> +int main(void)
> +{
> + MD5_CTX context;
> +
> + MD5_Init(&context);
> + return 0;
> +}
> diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt
> index dc7442c..bb33fe2 100644
> --- a/tools/perf/Documentation/perf-inject.txt
> +++ b/tools/perf/Documentation/perf-inject.txt
> @@ -44,6 +44,12 @@ OPTIONS
> --kallsyms=<file>::
> kallsyms pathname
>
> +-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]
> diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
> index ea46df25..af25d1a 100644
> --- a/tools/perf/builtin-inject.c
> +++ b/tools/perf/builtin-inject.c
> @@ -16,6 +16,7 @@
> #include "util/debug.h"
> #include "util/build-id.h"
> #include "util/data.h"
> +#include "util/jit.h"
>
> #include "util/parse-options.h"
>
> @@ -26,6 +27,7 @@ struct perf_inject {
> struct perf_session *session;
> bool build_ids;
> bool sched_stat;
> + bool jit_mode;
> const char *input_name;
> struct perf_data_file output;
> u64 bytes_written;
> @@ -121,12 +123,19 @@ static int perf_event__repipe_mmap(struct perf_tool *tool,
> struct perf_sample *sample,
> struct machine *machine)
> {
> - int err;
> -
> - err = perf_event__process_mmap(tool, event, sample, machine);
> - perf_event__repipe(tool, event, sample, machine);
> -
> - return err;
> + struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
> + u64 n = 0;
> +
> + if (inject->jit_mode) {
> + /*
> + * if jit marker, then inject jit mmaps and generate ELF images
> + */
> + if (!jit_process(&inject->tool, &inject->output, machine, event->mmap.filename, sample->pid, &n)) {
> + inject->bytes_written += n;
> + return 0;
> + }
> + }

You have dropped perf_event__process_mmap() from the !inject->jit_mode case.
But it would be nicer for jit_mode to have its own function.

> + return perf_event__repipe(tool, event, sample, machine);
> }
>
> static int perf_event__repipe_mmap2(struct perf_tool *tool,
> @@ -134,12 +143,19 @@ static int perf_event__repipe_mmap2(struct perf_tool *tool,
> struct perf_sample *sample,
> struct machine *machine)
> {
> - int err;
> -
> - err = perf_event__process_mmap2(tool, event, sample, machine);
> - perf_event__repipe(tool, event, sample, machine);
> -
> - return err;
> + struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
> + u64 n = 0;
> +
> + if (inject->jit_mode) {
> + /*
> + * if jit marker, then inject jit mmaps and generate ELF images
> + */
> + if (!jit_process(&inject->tool, &inject->output, machine, event->mmap2.filename, sample->pid, &n)) {
> + inject->bytes_written += n;
> + return 0;
> + }
> + }

Ditto

> + return perf_event__repipe(tool, event, sample, machine);
> }
>
> static int perf_event__repipe_fork(struct perf_tool *tool,
> @@ -341,7 +357,6 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel,
> name, sample_msg);
> return -EINVAL;
> }
> -
> return 0;
> }
>
> @@ -439,6 +454,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
> OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat,
> "Merge sched-stat and sched-switch for getting events "
> "where and how long tasks slept"),
> + OPT_BOOLEAN('j', "jit", &inject.jit_mode, "merge jitdump files into perf.data file"),
> OPT_INCR('v', "verbose", &verbose,
> "be more verbose (show build ids, etc)"),
> OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file",
> @@ -470,6 +486,24 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
> if (inject.session == NULL)
> return -1;
>
> + if (inject.build_ids) {
> + /*
> + * to make sure the mmap records are ordered correctly
> + * and so that the correct especially due to jitted code
> + * mmaps. We cannot generate the buildid hit list and
> + * inject the jit mmaps at the same time for now.
> + */
> + inject.tool.ordered_events = true;
> + inject.tool.ordering_requires_timestamps = true;
> + }
> +
> + if (inject.jit_mode) {
> + inject.tool.mmap2 = perf_event__repipe_mmap2;
> + inject.tool.mmap = perf_event__repipe_mmap;

As suggested above, why not make your own tool fns e.g.

inject.tool.mmap2 = perf_event__jit_mode_mmap2;
inject.tool.mmap = perf_event__jit_mode_mmap;


> + inject.tool.ordered_events = true;
> + inject.tool.ordering_requires_timestamps = true;

You are taking advantage of a bug in perf-inject, that is the
"finished_round" events are not being processed. Really they should be
processed and you should inject in time order.

> + }
> +
> if (symbol__init(&inject.session->header.env) < 0)
> return -1;
>
> diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
> index cd121df..e3cf5e4 100644
> --- a/tools/perf/config/Makefile
> +++ b/tools/perf/config/Makefile
> @@ -366,6 +366,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
> diff --git a/tools/perf/util/Build b/tools/perf/util/Build
> index 9bff65e..8bc62b4 100644
> --- a/tools/perf/util/Build
> +++ b/tools/perf/util/Build
> @@ -75,6 +75,8 @@ libperf-$(CONFIG_X86) += tsc.o
> libperf-y += cloexec.o
> libperf-y += thread-stack.o
> libperf-y += demangle-java.o
> +libperf-y += jitdump.o
> +libperf-y += genelf.o
>
> libperf-$(CONFIG_LIBELF) += symbol-elf.o
> libperf-$(CONFIG_LIBELF) += probe-event.o
> diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c
> new file mode 100644
> index 0000000..a5beebe
> --- /dev/null
> +++ b/tools/perf/util/genelf.c
> @@ -0,0 +1,479 @@
> +/*
> + * genelf.c
> + * Copyright (C) 2014, Google, Inc
> + *
> + * Contributed by:
> + * Stephane Eranian <eranian@xxxxxxxxx>
> + *
> + * Released under the GPL v2. (and only v2, not any later version)
> + */
> +
> +#include <sys/types.h>
> +#include <stdio.h>
> +#include <getopt.h>
> +#include <stddef.h>
> +#include <libelf.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <fcntl.h>
> +#include <err.h>
> +
> +#include "perf.h"
> +#include "genelf.h"
> +
> +#define JVMTI
> +
> +#define BUILD_ID_URANDOM /* different uuid for each run */
> +
> +#ifdef HAVE_LIBCRYPTO
> +
> +#define BUILD_ID_MD5
> +#undef BUILD_ID_SHA /* does not seem to work well when linked with Java */
> +#undef BUILD_ID_URANDOM /* different uuid for each run */
> +
> +#ifdef BUILD_ID_SHA
> +#include <openssl/sha.h>
> +#endif
> +
> +#ifdef BUILD_ID_MD5
> +#include <openssl/md5.h>
> +#endif
> +#endif
> +
> +#if defined(__arm__)
> +#define GEN_ELF_ARCH EM_ARM
> +#define GEN_ELF_ENDIAN ELFDATA2LSB
> +#define GEN_ELF_CLASS ELFCLASS32
> +#elif defined(__x86_64__)
> +#define GEN_ELF_ARCH EM_X86_64
> +#define GEN_ELF_ENDIAN ELFDATA2LSB
> +#define GEN_ELF_CLASS ELFCLASS64
> +#elif defined(__i386__)
> +#define GEN_ELF_ARCH EM_386
> +#define GEN_ELF_ENDIAN ELFDATA2LSB
> +#define GEN_ELF_CLASS ELFCLASS32
> +#elif defined(__ppcle__)
> +#define GEN_ELF_ARCH EM_PPC
> +#define GEN_ELF_ENDIAN ELFDATA2LSB
> +#define GEN_ELF_CLASS ELFCLASS64
> +#elif defined(__powerpc__)
> +#define GEN_ELF_ARCH EM_PPC64
> +#define GEN_ELF_ENDIAN ELFDATA2MSB
> +#define GEN_ELF_CLASS ELFCLASS64
> +#elif defined(__powerpcle__)
> +#define GEN_ELF_ARCH EM_PPC64
> +#define GEN_ELF_ENDIAN ELFDATA2LSB
> +#define GEN_ELF_CLASS ELFCLASS64
> +#else
> +#error "unsupported architecture"
> +#endif
> +
> +#if GEN_ELF_CLASS == ELFCLASS64
> +#define elf_newehdr elf64_newehdr
> +#define elf_getshdr elf64_getshdr
> +#define Elf_Ehdr Elf64_Ehdr
> +#define Elf_Shdr Elf64_Shdr
> +#define Elf_Sym Elf64_Sym
> +#define ELF_ST_TYPE(a) ELF64_ST_TYPE(a)
> +#define ELF_ST_BIND(a) ELF64_ST_BIND(a)
> +#define ELF_ST_VIS(a) ELF64_ST_VISIBILITY(a)
> +#else
> +#define elf_newehdr elf32_newehdr
> +#define elf_getshdr elf32_getshdr
> +#define Elf_Ehdr Elf32_Ehdr
> +#define Elf_Shdr Elf32_Shdr
> +#define Elf_Sym Elf32_Sym
> +#define ELF_ST_TYPE(a) ELF32_ST_TYPE(a)
> +#define ELF_ST_BIND(a) ELF32_ST_BIND(a)
> +#define ELF_ST_VIS(a) ELF32_ST_VISIBILITY(a)
> +#endif
> +
> +typedef struct {
> + unsigned int namesz; /* Size of entry's owner string */
> + unsigned int descsz; /* Size of the note descriptor */
> + unsigned int type; /* Interpretation of the descriptor */
> + char name[0]; /* Start of the name+desc data */
> +} Elf_Note;
> +
> +struct options {
> + char *output;
> + int fd;
> +};
> +
> +static char shd_string_table[] = {
> + 0,
> + '.', 't', 'e', 'x', 't', 0, /* 1 */
> + '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0, /* 7 */
> + '.', 's', 'y', 'm', 't', 'a', 'b', 0, /* 17 */
> + '.', 's', 't', 'r', 't', 'a', 'b', 0, /* 25 */
> + '.', 'n', 'o', 't', 'e', '.', 'g', 'n', 'u', '.', 'b', 'u', 'i', 'l', 'd', '-', 'i', 'd', 0, /* 33 */
> +};
> +
> +
> +static struct buildid_note {
> + Elf_Note desc; /* descsz: size of build-id, must be multiple of 4 */
> + char name[4]; /* GNU\0 */
> + char build_id[20];
> +} bnote;
> +
> +static Elf_Sym symtab[]={
> + /* symbol 0 MUST be the undefined symbol */
> + { .st_name = 0, /* index in sym_string table */
> + .st_info = ELF_ST_TYPE(STT_NOTYPE),
> + .st_shndx = 0, /* for now */
> + .st_value = 0x0,
> + .st_other = ELF_ST_VIS(STV_DEFAULT),
> + .st_size = 0,
> + },
> + { .st_name = 1, /* index in sym_string table */
> + .st_info = ELF_ST_BIND(STB_LOCAL) | ELF_ST_TYPE(STT_FUNC),
> + .st_shndx = 1,
> + .st_value = 0, /* for now */
> + .st_other = ELF_ST_VIS(STV_DEFAULT),
> + .st_size = 0, /* for now */
> + }
> +};
> +
> +#ifdef BUILD_ID_URANDOM
> +static void
> +gen_build_id(struct buildid_note *note,
> + unsigned long load_addr __maybe_unused,
> + const void *code __maybe_unused,
> + size_t csize __maybe_unused)
> +{
> + int fd;
> + size_t sz = sizeof(note->build_id);
> + ssize_t sret;
> +
> + fd = open("/dev/urandom", O_RDONLY);
> + if (fd == -1)
> + err(1, "cannot access /dev/urandom for builid");
> +
> + sret = read(fd, note->build_id, sz);
> +
> + close(fd);
> +
> + if (sret != (ssize_t)sz)
> + memset(note->build_id, 0, sz);
> +}
> +#endif
> +
> +#ifdef BUILD_ID_SHA
> +static void
> +gen_build_id(struct buildid_note *note,
> + unsigned long load_addr __maybe_unused,
> + const void *code,
> + size_t csize)
> +{
> + if (sizeof(note->build_id) < SHA_DIGEST_LENGTH)
> + errx(1, "build_id too small for SHA1");
> +
> + SHA1(code, csize, (unsigned char *)note->build_id);
> +}
> +#endif
> +
> +#ifdef BUILD_ID_MD5
> +static void
> +gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *code, size_t csize)
> +{
> + MD5_CTX context;
> +
> + if (sizeof(note->build_id) < 16)
> + errx(1, "build_id too small for MD5");
> +
> + MD5_Init(&context);
> + MD5_Update(&context, &load_addr, sizeof(load_addr));
> + MD5_Update(&context, code, csize);
> + MD5_Final((unsigned char *)note->build_id, &context);
> +}
> +#endif
> +
> +/*
> + * fd: file descriptor open for writing for the output file
> + * load_addr: code load address (could be zero, just used for buildid)
> + * sym: function name (for native code - used as the symbol)
> + * code: the native code
> + * csize: the code size in bytes
> + */
> +int
> +jit_write_elf(int fd, unsigned long load_addr, const char *sym, const void *code, int csize)
> +{
> + Elf *e;
> + Elf_Data *d;
> + Elf_Scn *scn;
> + Elf_Ehdr *ehdr;
> + Elf_Shdr *shdr;
> + char *strsym = NULL;
> + int symlen;
> + int retval = -1;
> +
> + if (elf_version(EV_CURRENT) == EV_NONE) {
> + warnx("ELF initialization failed");
> + return -1;
> + }
> +
> + e = elf_begin(fd, ELF_C_WRITE, NULL);
> + if (!e) {
> + warnx("elf_begin failed");
> + goto error;
> + }
> +
> + /*
> + * setup ELF header
> + */
> + ehdr = elf_newehdr(e);
> + if (!ehdr) {
> + warnx("cannot get ehdr");
> + goto error;
> + }
> +
> + ehdr->e_ident[EI_DATA] = GEN_ELF_ENDIAN;
> + ehdr->e_ident[EI_CLASS] = GEN_ELF_CLASS;
> + ehdr->e_machine = GEN_ELF_ARCH;
> + ehdr->e_type = ET_DYN;
> + ehdr->e_entry = 0x0;
> + ehdr->e_version = EV_CURRENT;
> + ehdr->e_shstrndx= 2; /* shdr index for section name */
> +
> + /*
> + * setup text section
> + */
> + scn = elf_newscn(e);
> + if (!scn) {
> + warnx("cannot create section");
> + goto error;
> + }
> +
> + d = elf_newdata(scn);
> + if (!d) {
> + warnx("cannot get new data");
> + goto error;
> + }
> +
> + d->d_align = 16;
> + d->d_off = 0LL;
> + d->d_buf = (void *)code;
> + d->d_type = ELF_T_BYTE;
> + d->d_size = csize;
> + d->d_version = EV_CURRENT;
> +
> + shdr = elf_getshdr(scn);
> + if (!shdr) {
> + warnx("cannot get section header");
> + goto error;
> + }
> +
> + shdr->sh_name = 1;
> + shdr->sh_type = SHT_PROGBITS;
> + shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */
> + shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
> + shdr->sh_entsize = 0;
> +
> + /*
> + * setup section headers string table
> + */
> + scn = elf_newscn(e);
> + if (!scn) {
> + warnx("cannot create section");
> + goto error;
> + }
> +
> + d = elf_newdata(scn);
> + if (!d) {
> + warnx("cannot get new data");
> + goto error;
> + }
> +
> + d->d_align = 1;
> + d->d_off = 0LL;
> + d->d_buf = shd_string_table;
> + d->d_type = ELF_T_BYTE;
> + d->d_size = sizeof(shd_string_table);
> + d->d_version = EV_CURRENT;
> +
> + shdr = elf_getshdr(scn);
> + if (!shdr) {
> + warnx("cannot get section header");
> + goto error;
> + }
> +
> + shdr->sh_name = 7; /* offset of '.shstrtab' in shd_string_table */
> + shdr->sh_type = SHT_STRTAB;
> + shdr->sh_flags = 0;
> + shdr->sh_entsize = 0;
> +
> + /*
> + * setup symtab section
> + */
> + symtab[1].st_size = csize;
> +
> + scn = elf_newscn(e);
> + if (!scn) {
> + warnx("cannot create section");
> + goto error;
> + }
> +
> + d = elf_newdata(scn);
> + if (!d) {
> + warnx("cannot get new data");
> + goto error;
> + }
> +
> + d->d_align = 8;
> + d->d_off = 0LL;
> + d->d_buf = symtab;
> + d->d_type = ELF_T_SYM;
> + d->d_size = sizeof(symtab);
> + d->d_version = EV_CURRENT;
> +
> + shdr = elf_getshdr(scn);
> + if (!shdr) {
> + warnx("cannot get section header");
> + goto error;
> + }
> +
> + shdr->sh_name = 17; /* offset of '.symtab' in shd_string_table */
> + shdr->sh_type = SHT_SYMTAB;
> + shdr->sh_flags = 0;
> + shdr->sh_entsize = sizeof(Elf_Sym);
> + shdr->sh_link = 4; /* index of .strtab section */
> +
> + /*
> + * setup symbols string table
> + * 2 = 1 for 0 in 1st entry, 1 for the 0 at end of symbol for 2nd entry
> + */
> + symlen = 2 + strlen(sym);
> + strsym = calloc(1, symlen);
> + if (!strsym) {
> + warnx("cannot allocate strsym");
> + goto error;
> + }
> + strcpy(strsym + 1, sym);
> +
> + scn = elf_newscn(e);
> + if (!scn) {
> + warnx("cannot create section");
> + goto error;
> + }
> +
> + d = elf_newdata(scn);
> + if (!d) {
> + warnx("cannot get new data");
> + goto error;
> + }
> +
> + d->d_align = 1;
> + d->d_off = 0LL;
> + d->d_buf = strsym;
> + d->d_type = ELF_T_BYTE;
> + d->d_size = symlen;
> + d->d_version = EV_CURRENT;
> +
> + shdr = elf_getshdr(scn);
> + if (!shdr) {
> + warnx("cannot get section header");
> + goto error;
> + }
> +
> + shdr->sh_name = 25; /* offset in shd_string_table */
> + shdr->sh_type = SHT_STRTAB;
> + shdr->sh_flags = 0;
> + shdr->sh_entsize = 0;
> +
> + /*
> + * setup build-id section
> + */
> + scn = elf_newscn(e);
> + if (!scn) {
> + warnx("cannot create section");
> + goto error;
> + }
> +
> + d = elf_newdata(scn);
> + if (!d) {
> + warnx("cannot get new data");
> + goto error;
> + }
> +
> + /*
> + * build-id generation
> + */
> + gen_build_id(&bnote, load_addr, code, csize);
> + bnote.desc.namesz = sizeof(bnote.name); /* must include 0 termination */
> + bnote.desc.descsz = sizeof(bnote.build_id);
> + bnote.desc.type = NT_GNU_BUILD_ID;
> + strcpy(bnote.name, "GNU");
> +
> + d->d_align = 4;
> + d->d_off = 0LL;
> + d->d_buf = &bnote;
> + d->d_type = ELF_T_BYTE;
> + d->d_size = sizeof(bnote);
> + d->d_version = EV_CURRENT;
> +
> + shdr = elf_getshdr(scn);
> + if (!shdr) {
> + warnx("cannot get section header");
> + goto error;
> + }
> +
> + shdr->sh_name = 33; /* offset in shd_string_table */
> + shdr->sh_type = SHT_NOTE;
> + shdr->sh_addr = 0x0;
> + shdr->sh_flags = SHF_ALLOC;
> + shdr->sh_size = sizeof(bnote);
> + shdr->sh_entsize = 0;
> +
> + if (elf_update(e, ELF_C_WRITE) < 0) {
> + warnx("elf_update 4 failed");
> + goto error;
> + }
> + (void)elf_end(e);
> +
> + retval = 0;
> +error:
> + free(strsym);
> +
> + return retval;
> +}
> +
> +#ifndef JVMTI
> +
> +static unsigned char x86_code[] = {
> + 0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */
> + 0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */
> + 0xCD, 0x80 /* int $0x80 */
> +};
> +
> +static struct options options;
> +
> +int main(int argc, char **argv)
> +{
> + int c, fd, ret;
> +
> + while ((c = getopt(argc, argv, "o:h")) != -1) {
> + switch (c) {
> + case 'o':
> + options.output = optarg;
> + break;
> + case 'h':
> + printf("Usage: genelf -o output_file [-h]\n");
> + return 0;
> + default:
> + errx(1, "unknown option");
> + }
> + }
> +
> + fd = open(options.output, O_CREAT|O_TRUNC|O_RDWR, 0666);
> + if (fd == -1)
> + err(1, "cannot create file %s", options.output);
> +
> + ret = jit_write_elf(fd, "main", x86_code, sizeof(x86_code));
> + close(fd);
> +
> + if (ret != 0)
> + unlink(options.output);
> +
> + return ret;
> +}
> +#endif
> diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h
> new file mode 100644
> index 0000000..11307a2
> --- /dev/null
> +++ b/tools/perf/util/genelf.h
> @@ -0,0 +1,6 @@
> +#ifndef __GENELF_H__
> +#define __GENELF_H__
> +
> +extern int jit_write_elf(int fd, unsigned long code_addr, const char *sym, const void *code, int csize);
> +
> +#endif
> diff --git a/tools/perf/util/jit.h b/tools/perf/util/jit.h
> new file mode 100644
> index 0000000..2a812c0
> --- /dev/null
> +++ b/tools/perf/util/jit.h
> @@ -0,0 +1,15 @@
> +#ifndef __JIT_H__
> +#define __JIT_H__
> +
> +#include <data.h>
> +
> +extern int jit_process(struct perf_tool *tool,
> + struct perf_data_file *output,
> + struct machine *machine,
> + char *filename,
> + pid_t pid,
> + u64 *nbytes);
> +
> +extern int jit_inject_record(const char *filename);
> +
> +#endif /* __JIT_H__ */
> diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
> new file mode 100644
> index 0000000..9c5fafe
> --- /dev/null
> +++ b/tools/perf/util/jitdump.c
> @@ -0,0 +1,588 @@
> +#include <sys/types.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <inttypes.h>
> +#include <byteswap.h>
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +
> +#include "util.h"
> +#include "event.h"
> +#include "debug.h"
> +#include "symbol.h"
> +#include "strlist.h"
> +#include <elf.h>
> +
> +#include "session.h"
> +#include "jit.h"
> +#include "jitdump.h"
> +#include "genelf.h"
> +#include "../builtin.h"
> +
> +struct jit_buf_desc {
> + struct perf_data_file *output;
> + struct perf_tool *tool;
> + struct machine *machine;
> + union jr_entry *entry;
> + void *buf;
> + size_t bufsize;
> + FILE *in;
> + int needs_bswap; /* handles cross-endianess */
> + uint32_t code_load_count;
> + u64 bytes_written;
> + struct rb_root code_root;
> + char dir[PATH_MAX];
> +};
> +
> +struct debug_line_info {
> + unsigned long vma;
> + unsigned int lineno;
> + /* The filename format is unspecified, absolute path, relative etc. */
> + char const filename[0];
> +};
> +
> +struct jit_tool {
> + struct perf_tool tool;
> + struct perf_data_file output;
> + struct perf_data_file input;
> + u64 bytes_written;
> +};
> +
> +#define hmax(a, b) ((a) > (b) ? (a) : (b))
> +#define get_jit_tool(t) (container_of(tool, struct jit_tool, tool))
> +
> +static int
> +jit_emit_elf(char *filename,
> + const char *sym,
> + unsigned long code,
> + int csize)
> +{
> + int ret, fd;
> + unsigned long addr = (unsigned long)code;
> +
> + if (verbose > 0)
> + fprintf(stderr, "write ELF image %s\n", filename);
> +
> + fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0644);
> + if (fd == -1) {
> + pr_warning("cannot create jit ELF %s: %s\n", filename, strerror(errno));
> + return -1;
> + }
> +
> + ret = jit_write_elf(fd, addr, sym, (const void *)code, csize);
> +
> + close(fd);
> +
> + if (ret)
> + unlink(filename);
> +
> + return ret;
> +}
> +
> +static void
> +jit_close(struct jit_buf_desc *jd)
> +{
> + if (!(jd && jd->in))
> + return;
> + fclose(jd->in);
> + jd->in = NULL;
> +}
> +
> +static int
> +jit_open(struct jit_buf_desc *jd, const char *name)
> +{
> + struct jitheader header;
> + struct jr_prefix *prefix;
> + ssize_t bs, bsz = 0;
> + void *n, *buf = NULL;
> + int ret, retval = -1;
> +
> + jd->in = fopen(name, "r");
> + if (!jd->in)
> + return -1;
> +
> + bsz = hmax(sizeof(header), sizeof(*prefix));
> +
> + buf = malloc(bsz);
> + if (!buf)
> + goto error;
> +
> + ret = fread(buf, sizeof(header), 1, jd->in);
> + if (ret != 1)
> + goto error;
> +
> + memcpy(&header, buf, sizeof(header));
> +
> + if (header.magic != JITHEADER_MAGIC) {
> + if (header.magic != JITHEADER_MAGIC_SW)
> + goto error;
> + jd->needs_bswap = 1;
> + }
> +
> + if (jd->needs_bswap) {
> + header.version = bswap_32(header.version);
> + header.total_size = bswap_32(header.total_size);
> + header.pid = bswap_32(header.pid);
> + header.elf_mach = bswap_32(header.elf_mach);
> + header.timestamp = bswap_64(header.timestamp);
> + }
> +
> + if (verbose > 2)
> + pr_debug("version=%u\nhdr.size=%u\nts=0x%llx\npid=%d\nelf_mach=%d\n",
> + header.version,
> + header.total_size,
> + (unsigned long long)header.timestamp,
> + header.pid,
> + header.elf_mach);
> +
> + bs = header.total_size - sizeof(header);
> +
> + if (bs > bsz) {
> + n = realloc(buf, bs);
> + if (!n)
> + goto error;
> + bsz = bs;
> + buf = n;
> + /* read extra we do not know about */
> + ret = fread(buf, bs - bsz, 1, jd->in);
> + if (ret != 1)
> + goto error;
> + }
> + /*
> + * keep dirname for generating files and mmap records
> + */
> + strcpy(jd->dir, name);
> + dirname(jd->dir);
> +
> + return 0;
> +error:
> + fclose(jd->in);
> + return retval;
> +}
> +
> +static union jr_entry *
> +jit_get_next_entry(struct jit_buf_desc *jd)
> +{
> + struct jr_prefix *prefix;
> + union jr_entry *jr;
> + void *addr;
> + size_t bs, size;
> + int id, ret;
> +
> + if (!(jd && jd->in))
> + return NULL;
> +
> + if (jd->buf == NULL) {
> + size_t sz = getpagesize();
> + if (sz < sizeof(*prefix))
> + sz = sizeof(*prefix);
> +
> + jd->buf = malloc(sz);
> + if (jd->buf == NULL)
> + return NULL;
> +
> + jd->bufsize = sz;
> + }
> +
> + prefix = jd->buf;
> +
> + ret = fread(prefix, sizeof(*prefix), 1, jd->in);
> + if (ret != 1)
> + return NULL;
> +
> + if (jd->needs_bswap) {
> + prefix->id = bswap_32(prefix->id);
> + prefix->total_size = bswap_32(prefix->total_size);
> + prefix->timestamp = bswap_64(prefix->timestamp);
> + }
> + id = prefix->id;
> + size = prefix->total_size;
> +
> + bs = (size_t)size;
> + if (bs < sizeof(*prefix))
> + return NULL;
> +
> + if (id >= JIT_CODE_MAX) {
> + pr_warning("next_entry: unknown prefix %d, skipping\n", id);
> + return NULL;
> + }
> + if (bs > jd->bufsize) {
> + void *n;
> + n = realloc(jd->buf, bs);
> + if (!n)
> + return NULL;
> + jd->buf = n;
> + jd->bufsize = bs;
> + }
> +
> + addr = ((void *)jd->buf) + sizeof(*prefix);
> +
> + ret = fread(addr, bs - sizeof(*prefix), 1, jd->in);
> + if (ret != 1)
> + return NULL;
> +
> + jr = (union jr_entry *)jd->buf;
> +
> + switch(id) {
> + case JIT_CODE_DEBUG_INFO:
> + case JIT_CODE_CLOSE:
> + break;
> + case JIT_CODE_LOAD:
> + if (jd->needs_bswap) {
> + jr->load.pid = bswap_32(jr->load.pid);
> + jr->load.tid = bswap_32(jr->load.tid);
> + jr->load.vma = bswap_64(jr->load.vma);
> + jr->load.code_addr = bswap_64(jr->load.code_addr);
> + jr->load.code_size = bswap_64(jr->load.code_size);
> + jr->load.code_index= bswap_64(jr->load.code_index);
> + }
> + jd->code_load_count++;
> + break;
> + case JIT_CODE_MOVE:
> + if (jd->needs_bswap) {
> + jr->move.pid = bswap_32(jr->move.pid);
> + jr->move.tid = bswap_32(jr->move.tid);
> + jr->move.vma = bswap_64(jr->move.vma);
> + jr->move.old_code_addr = bswap_64(jr->move.old_code_addr);
> + jr->move.new_code_addr = bswap_64(jr->move.new_code_addr);
> + jr->move.code_size = bswap_64(jr->move.code_size);
> + jr->move.code_index = bswap_64(jr->move.code_index);
> + }
> + break;
> + case JIT_CODE_MAX:
> + default:
> + return NULL;
> + }
> + return jr;
> +}
> +
> +static int
> +jit_inject_event(struct jit_buf_desc *jd, union perf_event *event)
> +{
> + ssize_t size;
> +
> + size = perf_data_file__write(jd->output, event, event->header.size);
> + if (size < 0)
> + return -1;
> +
> + jd->bytes_written += size;
> + return 0;
> +}
> +
> +static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
> +{
> + struct perf_sample sample;
> + union perf_event *event;
> + unsigned long 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) + 16);
> + if (!event)
> + return -1;
> +
> + filename = event->mmap2.filename;
> + size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%u",
> + jd->dir,
> + pid,
> + count);
> +
> + size++; /* for \0 */
> +
> + size = PERF_ALIGN(size, sizeof(u64));
> +
> + ret = jit_emit_elf(filename, sym, code, csize);
> + if (ret) {
> + free(event);
> + return -1;
> + }
> +
> + 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;
> + 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);
> + id->pid = pid;
> + id->tid = tid;
> + id->time = jr->load.p.timestamp;

You need to take acount of the sample_type here instead of hard-coding the
format of the id sample.

> +
> + /*
> + * 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(jd->tool, event, &sample, jd->machine);
> + 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(jd->tool, event, &sample, NULL, jd->machine);
> +
> + return ret;
> +}
> +
> +static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)
> +{
> + struct perf_sample sample;
> + union perf_event *event;
> + char *filename;
> + size_t size;
> + struct stat st;
> + u16 idr_size;
> + int ret;
> + pid_t pid, tid;
> + struct {
> + u32 pid, tid;
> + u64 time;
> + } *id;
> +
> + pid = jr->move.pid;
> + tid = jr->move.tid;
> + idr_size = jd->machine->id_hdr_size;
> +
> + /*
> + * +16 to account for sample_id_all (hack)
> + */
> + event = calloc(1, sizeof(*event) + 16);
> + if (!event)
> + return -1;
> +
> + filename = event->mmap2.filename;
> + size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%"PRIu64,
> + jd->dir,
> + pid,
> + jr->move.code_index);
> +
> + size++; /* for \0 */
> +
> + if (stat(filename, &st))
> + memset(&st, 0, sizeof(stat));
> +
> + size = PERF_ALIGN(size, sizeof(u64));
> +
> + 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;
> + event->mmap2.start = jr->move.new_code_addr;
> + event->mmap2.len = jr->move.code_size;
> + 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);
> + id->pid = pid;
> + id->tid = tid;
> + id->time = jr->move.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 = jr->move.new_code_addr;
> +
> + ret = perf_event__process_mmap2(jd->tool, event, &sample, jd->machine);
> + if (ret)
> + return ret;
> +
> + ret = jit_inject_event(jd, event);
> + if (!ret)
> + build_id__mark_dso_hit(jd->tool, event, &sample, NULL, jd->machine);
> +
> + return ret;
> +}
> +
> +static int
> +jit_process_dump(struct jit_buf_desc *jd)
> +{
> + union jr_entry *jr;
> + int ret;
> +
> + while ((jr = jit_get_next_entry(jd))) {
> + switch(jr->prefix.id) {
> + case JIT_CODE_LOAD:
> + ret = jit_repipe_code_load(jd, jr);
> + break;
> + case JIT_CODE_MOVE:
> + ret = jit_repipe_code_move(jd, jr);
> + break;
> + default:
> + ret = 0;
> + continue;
> + }
> + }
> + return ret;
> +}
> +
> +static int
> +jit_inject(struct jit_buf_desc *jd, char *path)
> +{
> + int ret;
> +
> + if (verbose > 0)
> + fprintf(stderr, "injecting: %s\n", path);
> +
> + ret = jit_open(jd, path);
> + if (ret)
> + return -1;
> +
> + ret = jit_process_dump(jd);
> +
> + jit_close(jd);
> +
> + if (verbose > 0)
> + fprintf(stderr, "injected: %s (%d)\n", path, ret);
> +
> + return 0;
> +}
> +
> +/*
> + * File must be with pattern .../jit-XXXX.dump
> + * where XXXX is the PID of the process which did the mmap()
> + * as captured in the RECORD_MMAP record
> + */
> +static int
> +jit_detect(char *mmap_name, pid_t pid)
> + {
> + char *p;
> + char *end = NULL;
> + pid_t pid2;
> +
> + if (verbose > 2)
> + fprintf(stderr, "jit marker trying : %s\n", mmap_name);
> + /*
> + * get file name
> + */
> + p = strrchr(mmap_name, '/');
> + if (!p)
> + return -1;
> +
> + /*
> + * match prefix
> + */
> + if (strncmp(p, "/jit-", 5))
> + return -1;
> +
> + /*
> + * skip prefix
> + */
> + p += 5;
> +
> + /*
> + * must be followed by a pid
> + */
> + if (!isdigit(*p))
> + return -1;
> +
> + pid2 = (int)strtol(p, &end, 10);
> + if (!end)
> + return -1;
> +
> + /*
> + * pid does not match mmap pid
> + * pid==0 in system-wide mode (synthesized)
> + */
> + if (pid && pid2 != pid)
> + return -1;
> + /*
> + * validate suffix
> + */
> + if (strcmp(end, ".dump"))
> + return -1;
> +
> + if (verbose > 0)
> + fprintf(stderr, "jit marker found: %s\n", mmap_name);
> +
> + return 0;
> +}
> +
> +int
> +jit_process(struct perf_tool *tool,
> + struct perf_data_file *output,
> + struct machine *machine,
> + char *filename,
> + pid_t pid,
> + u64 *nbytes)
> +{
> + struct jit_buf_desc jd;
> + int ret;
> +
> + memset(&jd, 0, sizeof(jd));
> +
> + jd.tool = tool;
> + jd.output = output;
> + jd.machine = machine;
> +
> + *nbytes = 0;
> +
> + /*
> + * detect marker mmap (i.e., the jitdump mmap)
> + */
> + if (jit_detect(filename, pid))
> + return -1;
> +
> + ret = jit_inject(&jd, filename);
> + if (!ret)
> + *nbytes = jd.bytes_written;
> +
> + return ret;
> +}
> diff --git a/tools/perf/util/jitdump.h b/tools/perf/util/jitdump.h
> new file mode 100644
> index 0000000..120bdcf
> --- /dev/null
> +++ b/tools/perf/util/jitdump.h
> @@ -0,0 +1,92 @@
> +/*
> + * jitdump.h: jitted code info encapsulation file format
> + *
> + * Adapted from OProfile GPLv2 support jidump.h:
> + * Copyright 2007 OProfile authors
> + * Jens Wilke
> + * Daniel Hansel
> + * Copyright IBM Corporation 2007
> + */
> +#ifndef JITDUMP_H
> +#define JITDUMP_H
> +
> +#include <sys/time.h>
> +#include <time.h>
> +#include <stdint.h>
> +
> +/* JiTD */
> +#define JITHEADER_MAGIC 0x4A695444
> +#define JITHEADER_MAGIC_SW 0x4454694A
> +
> +#define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7)
> +
> +#define JITHEADER_VERSION 1
> +
> +struct jitheader {
> + uint32_t magic; /* characters "jItD" */
> + uint32_t version; /* header version */
> + uint32_t total_size; /* total size of header */
> + uint32_t elf_mach; /* elf mach target */
> + uint32_t pad1; /* reserved */
> + uint32_t pid; /* JIT process id */
> + uint64_t timestamp; /* timestamp */
> +};
> +
> +enum jit_record_type {
> + JIT_CODE_LOAD = 0,
> + JIT_CODE_MOVE = 1,
> + JIT_CODE_DEBUG_INFO = 2,
> + JIT_CODE_CLOSE = 3,
> +
> + JIT_CODE_MAX,
> +};
> +
> +/* record prefix (mandatory in each record) */
> +struct jr_prefix {
> + uint32_t id;
> + uint32_t total_size;
> + uint64_t timestamp;
> +};
> +
> +struct jr_code_load {
> + struct jr_prefix p;
> +
> + uint32_t pid;
> + uint32_t tid;
> + uint64_t vma;
> + uint64_t code_addr;
> + uint64_t code_size;
> + uint64_t code_index;
> +};
> +
> +struct jr_code_close {
> + struct jr_prefix p;
> +};
> +
> +struct jr_code_move {
> + struct jr_prefix p;
> +
> + uint32_t pid;
> + uint32_t tid;
> + uint64_t vma;
> + uint64_t old_code_addr;
> + uint64_t new_code_addr;
> + uint64_t code_size;
> + uint64_t code_index;
> +};
> +
> +struct jr_code_debug_info {
> + struct jr_prefix p;
> +
> + uint64_t code_addr;
> + uint64_t nr_entry;
> +};
> +
> +union jr_entry {
> + struct jr_code_debug_info info;
> + struct jr_code_close close;
> + struct jr_code_load load;
> + struct jr_code_move move;
> + struct jr_prefix prefix;
> +};
> +#endif /* !JITDUMP_H */
>

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/