From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752335AbbDAHBJ (ORCPT ); Wed, 1 Apr 2015 03:01:09 -0400 Received: from mga03.intel.com ([134.134.136.65]:15612 "EHLO mga03.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751055AbbDAHBE (ORCPT ); Wed, 1 Apr 2015 03:01:04 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.11,503,1422950400"; d="scan'208";a="673578539" Message-ID: <551B9731.7080505@intel.com> Date: Wed, 01 Apr 2015 09:58:57 +0300 From: Adrian Hunter Organization: Intel Finland Oy, Registered Address: PL 281, 00181 Helsinki, Business Identity Code: 0357606 - 4, Domiciled in Helsinki User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.4.0 MIME-Version: 1.0 To: Stephane Eranian , linux-kernel@vger.kernel.org CC: acme@redhat.com, peterz@infradead.org, mingo@elte.hu, ak@linux.intel.com, jolsa@redhat.com, namhyung@kernel.org, cel@us.ibm.com, sukadev@linux.vnet.ibm.com, sonnyrao@chromium.org, johnmccutchan@google.com, dsahern@gmail.com, pawel.moll@arm.com Subject: Re: [PATCH v6 3/4] perf inject: add jitdump mmap injection support References: <1427753974-13380-1-git-send-email-eranian@google.com> <1427753974-13380-4-git-send-email-eranian@google.com> In-Reply-To: <1427753974-13380-4-git-send-email-eranian@google.com> Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 > --- > 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 > + > +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=:: > 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 > + * > + * Released under the GPL v2. (and only v2, not any later version) > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 > +#endif > + > +#ifdef BUILD_ID_MD5 > +#include > +#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 > + > +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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "util.h" > +#include "event.h" > +#include "debug.h" > +#include "symbol.h" > +#include "strlist.h" > +#include > + > +#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 > +#include > +#include > + > +/* 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 */ >