Added kprof to perf converter
authorDavide Libenzi <dlibenzi@google.com>
Tue, 24 Nov 2015 02:47:16 +0000 (18:47 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 18 Nov 2015 17:56:34 +0000 (09:56 -0800)
Added kprof to perf converter.

Signed-off-by: Davide Libenzi <dlibenzi@google.com>
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
Makefile
tools/profile/kprof2perf/Makefile [new file with mode: 0644]
tools/profile/kprof2perf/kprof2perf.c [new file with mode: 0644]
tools/profile/kprof2perf/perf_format.h [new file with mode: 0644]

index 27eb77a..6c2f0c1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -48,11 +48,19 @@ export_parent_env := $(shell export | sed 's/$$/;/')
 # Save the ability to clear the current environment for future use
 clear_current_env := for c in $$(env | cut -d '=' -f 1); do unset $$c; done;
 
+define export_user_variables
+       CROSS_COMPILE="$(CROSS_COMPILE)"\
+       CROSS_INCLUDE="$(XCC_TARGET_INCLUDE)"\
+       ROS_CFLAGS="$(CFLAGS_USER)"\
+       ROS_LDFLAGS="$(LDFLAGS_USER)"
+endef
+
 # Define a set of commands to reset the environment to the parent's environment
 # and then run a local make target
 define make_as_parent
        $(clear_current_env)\
        $(export_parent_env)\
+       $(call export_user_variables)\
        $(MAKE) $(NO_PRINT_DIRECTORY) $(1)
 endef
 
@@ -650,10 +658,12 @@ realclean: userclean mrproper doxyclean objclean
 PHONY += apps-install
 apps-install:
        @$(call make_as_parent, -C tools/apps/busybox)
+       @$(call make_as_parent, -C tools/profile/kprof2perf install)
 
 PHONY += apps-clean
 apps-clean:
        @$(call make_as_parent, -C tools/apps/busybox clean)
+       @$(call make_as_parent, -C tools/profile/kprof2perf clean)
 
 # Cross Compiler
 # =========================================================================
diff --git a/tools/profile/kprof2perf/Makefile b/tools/profile/kprof2perf/Makefile
new file mode 100644 (file)
index 0000000..d08ab7d
--- /dev/null
@@ -0,0 +1,47 @@
+# Do not:
+# o  use make's built-in rules and variables
+#    (this increases performance and avoids hard-to-debug behaviour);
+# o  print "Entering directory ...";
+MAKEFLAGS += -rR --no-print-directory
+
+# Overrides
+BUILDDIR ?= $(shell pwd)
+AKAROS_ROOT ?= $(BUILDDIR)/../../..
+MAKE_JOBS ?= 4
+KFS_ROOT ?= $(AKAROS_ROOT)/kern/kfs
+
+XCC = $(CROSS_COMPILE)gcc
+
+CC = gcc
+CFLAGS = -O2 -g -idirafter $(CROSS_INCLUDE)
+LDFLAGS =
+
+PHONY := all
+all: kprof2perf-ros kprof2perf-linux
+
+
+PHONY += kprof2perf-ros
+kprof2perf-ros: kprof2perf.c
+       @$(XCC) $(ROS_CFLAGS) $(ROS_LDFLAGS) -o kprof2perf-ros kprof2perf.c
+
+
+PHONY += kprof2perf-linux
+kprof2perf-linux: kprof2perf.c
+       @$(CC) $(CFLAGS) $(LDFLAGS) -o kprof2perf-linux kprof2perf.c
+
+
+PHONY += install
+install: all
+       @cp kprof2perf-ros $(KFS_ROOT)/bin/kprof2perf
+
+
+PHONY += clean
+clean:
+       @rm -f kprof2perf-ros kprof2perf-linux
+
+
+PHONY += mrproper
+mrproper: clean
+
+
+.PHONY: $(PHONY)
diff --git a/tools/profile/kprof2perf/kprof2perf.c b/tools/profile/kprof2perf/kprof2perf.c
new file mode 100644 (file)
index 0000000..a95cb8f
--- /dev/null
@@ -0,0 +1,689 @@
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ *
+ * Converts kprof profiler files into Linux perf ones. The Linux Perf file
+ * format has bee illustrated here:
+ *
+ *      https://lwn.net/Articles/644919/
+ *      https://openlab-mu-internal.web.cern.ch/openlab-mu-internal/03_Documents/
+ *                      3_Technical_Documents/Technical_Reports/2011/Urs_Fassler_report.pdf
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <ros/profiler_records.h>
+#include "perf_format.h"
+
+#define MAX_PERF_RECORD_SIZE (32 * 1024 * 1024)
+#define PERF_RECORD_BUFFER_SIZE 1024
+#define OFFSET_NORELOC ((uint64_t) -1)
+
+#define ID_PERF_SAMPLE 0
+
+#define MBWR_SOLID (1 << 0)
+
+#define min(a, b)                                                              \
+       ({ __typeof__(a) _a = (a);                                      \
+               __typeof__(b) _b = (b);                                 \
+               _a < _b ? _a : _b; })
+#define max(a, b)                                                              \
+       ({ __typeof__(a) _a = (a);                                      \
+               __typeof__(b) _b = (b);                                 \
+               _a > _b ? _a : _b; })
+#define always_assert(c)                                                                                               \
+       do {                                                                                                                            \
+               if (!(c))                                                                                                               \
+                       fprintf(stderr, "%s: %d: Assertion failed: " #c "\n",           \
+                                       __FILE__, __LINE__);                                                            \
+       } while (0)
+
+struct perf_record {
+       uint64_t type;
+       uint64_t size;
+       char *data;
+       char buffer[PERF_RECORD_BUFFER_SIZE];
+};
+
+struct mem_file_reloc {
+       struct mem_file_reloc *next;
+       uint64_t *ptr;
+};
+
+struct mem_block {
+       struct mem_block *next;
+       char *base;
+       char *top;
+       char *wptr;
+};
+
+struct mem_file {
+       size_t size;
+       struct mem_block *head;
+       struct mem_block *tail;
+       struct mem_file_reloc *relocs;
+};
+
+struct perf_headers {
+       struct mem_block *headers[HEADER_FEAT_BITS];
+};
+
+struct static_mmap64 {
+       struct static_mmap64 *next;
+       uint64_t addr;
+       uint64_t size;
+       uint64_t offset;
+       uint32_t pid;
+       char *path;
+};
+
+static int debug_level;
+static uint64_t kernel_load_address = 0xffffffffc0000000;
+static size_t std_block_size = 64 * 1024;
+static struct static_mmap64 *static_mmaps;
+
+static inline void set_bitno(void *data, size_t bitno)
+{
+       ((char *) data)[bitno / 8] |= 1 << (bitno % 8);
+}
+
+static inline const char* vb_decode_uint64(const char *data, uint64_t *pval)
+{
+       unsigned int i;
+       uint64_t val = 0;
+
+       for (i = 0; (*data & 0x80) != 0; i += 7, data++)
+               val |= (((uint64_t) *data) & 0x7f) << i;
+       *pval = val | ((uint64_t) *data) << i;
+
+       return data + 1;
+}
+
+static inline int vb_fdecode_uint64(FILE *file, uint64_t *pval)
+{
+       unsigned int i = 0;
+       uint64_t val = 0;
+
+       for (;;) {
+               int c = fgetc(file);
+
+               if (c == EOF)
+                       return EOF;
+               val |= (((uint64_t) c) & 0x7f) << i;
+               i += 7;
+               if ((c & 0x80) == 0)
+                       break;
+       }
+       *pval = val;
+
+       return i / 7;
+}
+
+static void dbg_print(int level, FILE *file, const char *fmt, ...)
+{
+       if (debug_level >= level) {
+               va_list args;
+
+               va_start(args, fmt);
+               vfprintf(file, fmt, args);
+               va_end(args);
+       }
+}
+
+static void *xmalloc(size_t size)
+{
+       void *data = malloc(size);
+
+       if (!data) {
+               fprintf(stderr, "Unable to allocate %lu bytes: %s\n", size,
+                               strerror(errno));
+               exit(1);
+       }
+
+       return data;
+}
+
+static void *xzmalloc(size_t size)
+{
+       void *data = xmalloc(size);
+
+       memset(data, 0, size);
+
+       return data;
+}
+
+static FILE *xfopen(const char *path, const char *mode)
+{
+       FILE *file = fopen(path, mode);
+
+       if (!file) {
+               fprintf(stderr, "Unable to open file '%s' for mode '%s': %s\n",
+                               path, mode, strerror(errno));
+               exit(1);
+       }
+
+       return file;
+}
+
+static void xfwrite(const void *data, size_t size, FILE *file)
+{
+       if (fwrite(data, 1, size, file) != size) {
+               fprintf(stderr, "Unable to write %lu bytes: %s\n", size,
+                               strerror(errno));
+               exit(1);
+       }
+}
+
+static void xfseek(FILE *file, long offset, int whence)
+{
+       if (fseek(file, offset, whence)) {
+               int error = errno;
+
+               fprintf(stderr, "Unable to seek at offset %ld from %s (fpos=%ld): %s\n",
+                               offset, whence == SEEK_SET ? "beginning of file" :
+                               (whence == SEEK_END ? "end of file" : "current position"),
+                               ftell(file), strerror(error));
+               exit(1);
+       }
+}
+
+static void free_record(struct perf_record *pr)
+{
+       if (pr->data != pr->buffer)
+               free(pr->data);
+       pr->data = NULL;
+}
+
+static int read_record(FILE *file, struct perf_record *pr)
+{
+       if (vb_fdecode_uint64(file, &pr->type) == EOF ||
+               vb_fdecode_uint64(file, &pr->size) == EOF)
+               return EOF;
+       if (pr->size > MAX_PERF_RECORD_SIZE) {
+               fprintf(stderr, "Invalid record size: type=%lu size=%lu\n", pr->type,
+                               pr->size);
+               exit(1);
+       }
+       if (pr->size > sizeof(pr->buffer))
+               pr->data = xmalloc((size_t) pr->size);
+       else
+               pr->data = pr->buffer;
+       if (fread(pr->data, 1, (size_t) pr->size, file) != (size_t) pr->size) {
+               fprintf(stderr, "Unable to read record memory: size=%lu\n",
+                               pr->size);
+               return EOF;
+       }
+
+       return 0;
+}
+
+static struct mem_block *mem_block_alloc(size_t size)
+{
+       struct mem_block *mb = xmalloc(sizeof(struct mem_block) + size);
+
+       mb->next = NULL;
+       mb->base = mb->wptr = (char *) mb + sizeof(struct mem_block);
+       mb->top = mb->base + size;
+
+       return mb;
+}
+
+static char *mem_block_write(struct mem_block *mb, const void *data,
+                                                        size_t size)
+{
+       char *wrbase = mb->wptr;
+
+       always_assert(size <= mb->top - mb->wptr);
+
+       memcpy(mb->wptr, data, size);
+       mb->wptr += size;
+
+       return wrbase;
+}
+
+static void mem_file_init(struct mem_file *mf)
+{
+       memset(mf, 0, sizeof(*mf));
+}
+
+static int mem_block_can_write(struct mem_block *mb, size_t size, int flags)
+{
+       size_t space = mb->top - mb->wptr;
+
+       return (flags & MBWR_SOLID) ? (space >= size) : (space > 0);
+}
+
+static void *mem_file_write(struct mem_file *mf, const void *data, size_t size,
+                                                       int flags)
+{
+       void *wrbase = NULL;
+
+       while (size > 0) {
+               size_t space, csize;
+               struct mem_block *mb = mf->tail;
+
+               if (!mb || !mem_block_can_write(mb, size, flags)) {
+                       mb = mem_block_alloc(max(std_block_size, size));
+                       if (!mf->tail)
+                               mf->head = mb;
+                       else
+                               mf->tail->next = mb;
+                       mf->tail = mb;
+               }
+               space = mb->top - mb->wptr;
+               csize = min(size, space);
+
+               wrbase = mem_block_write(mb, data, csize);
+               mf->size += csize;
+
+               size -= csize;
+               data = (const char *) data + csize;
+       }
+
+       return wrbase;
+}
+
+static void mem_file_sync(struct mem_file *mf, FILE *file, uint64_t rel_offset)
+{
+       struct mem_block *mb;
+
+       if (rel_offset != 0) {
+               struct mem_file_reloc *rel;
+
+               always_assert(!mf->relocs || rel_offset != OFFSET_NORELOC);
+
+               for (rel = mf->relocs; rel; rel = rel->next)
+                       *rel->ptr += rel_offset;
+       }
+
+       for (mb = mf->head; mb; mb = mb->next)
+               xfwrite(mb->base, mb->wptr - mb->base, file);
+}
+
+static struct mem_file_reloc *mem_file_add_reloc(struct mem_file *mf,
+                                                                                                uint64_t *ptr)
+{
+       struct mem_file_reloc *rel = xzmalloc(sizeof(struct mem_file_reloc));
+
+       rel->ptr = ptr;
+       rel->next = mf->relocs;
+       mf->relocs = rel;
+
+       return rel;
+}
+
+static void add_static_mmap(const char *path, uint64_t addr, uint64_t offset,
+                                                       uint32_t pid)
+{
+       struct static_mmap64 *mm;
+       struct stat stb;
+
+       if (stat(path, &stb)) {
+               fprintf(stderr, "Unable to stat mmapped file '%s': %s\n",
+                               path, strerror(errno));
+               exit(1);
+       }
+
+       mm = xzmalloc(sizeof(struct static_mmap64));
+       mm->pid = pid;
+       mm->addr = addr;
+       mm->size = stb.st_size;
+       mm->offset = offset;
+       mm->path = strdup(path);
+
+       mm->next = static_mmaps;
+       static_mmaps = mm;
+}
+
+static void add_kernel_mmap(const char *path)
+{
+       add_static_mmap(path, kernel_load_address, kernel_load_address, 0);
+}
+
+static void headers_init(struct perf_headers *hdrs)
+{
+       memset(hdrs, 0, sizeof(*hdrs));
+}
+
+static void headers_add_header(struct perf_headers *hdrs, size_t nhdr,
+                                                          struct mem_block *mb)
+{
+       always_assert(nhdr < HEADER_FEAT_BITS);
+
+       hdrs->headers[nhdr] = mb;
+}
+
+static void headers_write(struct perf_headers *hdrs, struct perf_header *ph,
+                                                 struct mem_file *mf)
+{
+       size_t i;
+
+       for (i = 0; i < HEADER_FEAT_BITS; i++) {
+               struct mem_block *mb = hdrs->headers[i];
+
+               if (mb) {
+                       mem_file_write(mf, mb->base, mb->wptr - mb->base, 0);
+                       set_bitno(ph->adds_features, i);
+               }
+       }
+}
+
+static void perf_header_init(struct perf_header *ph)
+{
+       memset(ph, 0, sizeof(*ph));
+       ph->magic = PERF_MAGIC2;
+       ph->size = sizeof(*ph);
+       ph->attr_size = sizeof(struct perf_event_attr);
+}
+
+static void emit_static_mmaps(struct mem_file *mf)
+{
+       struct static_mmap64 *mm;
+
+       for (mm = static_mmaps; mm; mm = mm->next) {
+               size_t size = sizeof(struct perf_record_mmap) + strlen(mm->path) + 1;
+               struct perf_record_mmap *xrec = xzmalloc(size);
+
+               xrec->header.type = PERF_RECORD_MMAP;
+               xrec->header.misc = PERF_RECORD_MISC_USER;
+               xrec->header.size = size;
+               xrec->pid = xrec->tid = mm->pid;
+               xrec->addr = mm->addr;
+               xrec->len = mm->size;
+               xrec->pgoff = mm->offset;
+               strcpy(xrec->filename, mm->path);
+
+               mem_file_write(mf, xrec, size, 0);
+
+               free(xrec);
+       }
+}
+
+static void emit_comm(uint32_t pid, const char *comm, struct mem_file *mf)
+{
+       size_t size = sizeof(struct perf_record_comm) + strlen(comm) + 1;
+       struct perf_record_comm *xrec = xzmalloc(size);
+
+       xrec->header.type = PERF_RECORD_COMM;
+       xrec->header.misc = PERF_RECORD_MISC_USER;
+       xrec->header.size = size;
+       xrec->pid = xrec->tid = pid;
+       strcpy(xrec->comm, comm);
+
+       mem_file_write(mf, xrec, size, 0);
+
+       free(xrec);
+}
+
+static void emit_pid_mmap64(struct perf_record *pr, struct mem_file *mf)
+{
+       struct proftype_pid_mmap64 *rec = (struct proftype_pid_mmap64 *) pr->data;
+       size_t size = sizeof(struct perf_record_mmap) + strlen(rec->path) + 1;
+       struct perf_record_mmap *xrec = xzmalloc(size);
+
+       xrec->header.type = PERF_RECORD_MMAP;
+       xrec->header.misc = PERF_RECORD_MISC_USER;
+       xrec->header.size = size;
+       xrec->pid = xrec->tid = rec->pid;
+       xrec->addr = rec->addr;
+       xrec->len = rec->size;
+       xrec->pgoff = rec->offset;
+       strcpy(xrec->filename, rec->path);
+
+       mem_file_write(mf, xrec, size, 0);
+
+       free(xrec);
+}
+
+static void emit_kernel_trace64(struct perf_record *pr, struct mem_file *mf)
+{
+       struct proftype_kern_trace64 *rec = (struct proftype_kern_trace64 *)
+               pr->data;
+       size_t size = sizeof(struct perf_record_sample) +
+               (rec->num_traces - 1) * sizeof(uint64_t);
+       struct perf_record_sample *xrec = xzmalloc(size);
+
+       xrec->header.type = PERF_RECORD_SAMPLE;
+       xrec->header.misc = PERF_RECORD_MISC_USER;
+       xrec->header.size = size;
+       xrec->ip = rec->trace[0];
+       xrec->time = rec->tstamp;
+       xrec->addr = rec->trace[0];
+       xrec->id = ID_PERF_SAMPLE;
+       xrec->cpu = rec->cpu;
+       xrec->nr = rec->num_traces - 1;
+       memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
+
+       mem_file_write(mf, xrec, size, 0);
+
+       free(xrec);
+}
+
+static void emit_user_trace64(struct perf_record *pr, struct mem_file *mf)
+{
+       struct proftype_user_trace64 *rec = (struct proftype_user_trace64 *)
+               pr->data;
+       size_t size = sizeof(struct perf_record_sample) +
+               (rec->num_traces - 1) * sizeof(uint64_t);
+       struct perf_record_sample *xrec = xzmalloc(size);
+
+       xrec->header.type = PERF_RECORD_SAMPLE;
+       xrec->header.misc = PERF_RECORD_MISC_USER;
+       xrec->header.size = size;
+       xrec->ip = rec->trace[0];
+       xrec->pid = xrec->tid = rec->pid;
+       xrec->time = rec->tstamp;
+       xrec->addr = rec->trace[0];
+       xrec->id = ID_PERF_SAMPLE;
+       xrec->cpu = rec->cpu;
+       xrec->nr = rec->num_traces - 1;
+       memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
+
+       mem_file_write(mf, xrec, size, 0);
+
+       free(xrec);
+}
+
+static void emit_new_process(struct perf_record *pr, struct mem_file *mf)
+{
+       struct proftype_new_process *rec = (struct proftype_new_process *) pr->data;
+       const char *comm = strrchr(rec->path, '/');
+
+       if (!comm)
+               comm = rec->path;
+       else
+               comm++;
+       emit_comm(rec->pid, comm, mf);
+}
+
+static void add_attribute(struct mem_file *amf, struct mem_file *mmf,
+                                                 const struct perf_event_attr *attr,
+                                                 const uint64_t *ids, size_t nids)
+{
+       struct perf_file_section *psids;
+       struct perf_file_section sids;
+
+       mem_file_write(amf, attr, sizeof(*attr), 0);
+
+       sids.offset = mmf->size;
+       sids.size = nids * sizeof(uint64_t);
+
+       mem_file_write(mmf, ids, nids * sizeof(uint64_t), 0);
+
+       psids = mem_file_write(amf, &sids, sizeof(sids), MBWR_SOLID);
+
+       mem_file_add_reloc(amf, &psids->offset);
+}
+
+static void add_default_attribute(struct mem_file *amf, struct mem_file *mmf,
+                                                                 uint64_t event_id, uint64_t id)
+{
+       struct perf_event_attr attr;
+
+       memset(&attr, 0, sizeof(attr));
+       attr.type = PERF_TYPE_HARDWARE;
+       attr.size = sizeof(attr);
+       attr.config = event_id;
+       attr.mmap = 1;
+       attr.comm = 1;
+       attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
+               PERF_SAMPLE_ADDR | PERF_SAMPLE_ID | PERF_SAMPLE_CPU |
+               PERF_SAMPLE_CALLCHAIN;
+
+       add_attribute(amf, mmf, &attr, &id, 1);
+}
+
+static void add_event_type(struct mem_file *mf, uint64_t id, const char *name)
+{
+       struct perf_trace_event_type evt;
+
+       memset(&evt, 0, sizeof(evt));
+       evt.event_id = id;
+       strncpy(evt.name, name, sizeof(evt.name));
+       evt.name[sizeof(evt.name) - 1] = 0;
+
+       mem_file_write(mf, &evt, sizeof(evt), 0);
+}
+
+static void process_input(FILE *input, FILE *output)
+{
+       size_t processed_records = 0;
+       uint64_t offset, rel_offset;
+       struct perf_record pr;
+       struct perf_header ph;
+       struct perf_headers hdrs;
+       struct mem_file fhdrs, misc, attrs, data, event_types;
+
+       perf_header_init(&ph);
+       headers_init(&hdrs);
+       mem_file_init(&fhdrs);
+       mem_file_init(&misc);
+       mem_file_init(&attrs);
+       mem_file_init(&data);
+       mem_file_init(&event_types);
+
+       add_event_type(&event_types, PERF_COUNT_HW_CPU_CYCLES, "cycles");
+       add_default_attribute(&attrs, &misc, PERF_COUNT_HW_CPU_CYCLES,
+                                                 ID_PERF_SAMPLE);
+
+       emit_comm(0, "[kernel]", &data);
+       emit_static_mmaps(&data);
+
+       while (read_record(input, &pr) == 0) {
+               dbg_print(8, stderr, "Valid record: type=%lu size=%lu\n",
+                                 pr.type, pr.size);
+
+               processed_records++;
+
+               switch (pr.type) {
+               case PROFTYPE_KERN_TRACE64:
+                       emit_kernel_trace64(&pr, &data);
+                       break;
+               case PROFTYPE_USER_TRACE64:
+                       emit_user_trace64(&pr, &data);
+                       break;
+               case PROFTYPE_PID_MMAP64:
+                       emit_pid_mmap64(&pr, &data);
+                       break;
+               case PROFTYPE_NEW_PROCESS:
+                       emit_new_process(&pr, &data);
+                       break;
+               default:
+                       fprintf(stderr, "Unknown record: type=%lu size=%lu\n", pr.type,
+                                       pr.size);
+                       processed_records--;
+               }
+
+               free_record(&pr);
+       }
+
+       headers_write(&hdrs, &ph, &fhdrs);
+       offset = sizeof(ph) + fhdrs.size + misc.size;
+
+       if (event_types.size > 0) {
+               ph.event_types.offset = offset;
+               ph.event_types.size = event_types.size;
+               offset += event_types.size;
+       }
+       if (attrs.size > 0) {
+               ph.attrs.offset = offset;
+               ph.attrs.size = attrs.size;
+               offset += attrs.size;
+       }
+       if (data.size > 0) {
+               ph.data.offset = offset;
+               ph.data.size = data.size;
+               offset += data.size;
+       }
+
+       xfwrite(&ph, sizeof(ph), output);
+       mem_file_sync(&fhdrs, output, OFFSET_NORELOC);
+
+       rel_offset = (uint64_t) ftell(output);
+       mem_file_sync(&misc, output, rel_offset);
+
+       mem_file_sync(&event_types, output, rel_offset);
+       mem_file_sync(&attrs, output, rel_offset);
+       mem_file_sync(&data, output, rel_offset);
+
+       fprintf(stderr, "Conversion succeeded: %lu records converted\n",
+                       processed_records);
+}
+
+static void usage(const char *prg)
+{
+       fprintf(stderr, "Use: %s [-ioskDh]\n"
+                       "\t-i INPUT_FILE                  : Sets the input file path (STDIN).\n"
+                       "\t-o OUTPUT_FILE                 : Sets the output file path (STDOUT).\n"
+                       "\t-k KERN_ELF_FILE               : Sets the kernel file path.\n"
+                       "\t-s SIZE                                : Sets the default memory block size (%lu).\n"
+                       "\t-D DBG_LEVEL                   : Sets the debug level for messages.\n"
+                       "\t-h                                     : Displays this help screen.\n",
+                       prg, std_block_size);
+       exit(1);
+}
+
+int main(int argc, const char **argv)
+{
+       int i;
+       const char *inpath = NULL, *outpath = NULL;
+       FILE *input = stdin, *output = stdout;
+
+       for (i = 1; i < argc; i++) {
+               if (strcmp(argv[i], "-i") == 0) {
+                       if (++i < argc)
+                               inpath = argv[i];
+               } else if (strcmp(argv[i], "-o") == 0) {
+                       if (++i < argc)
+                               outpath = argv[i];
+               } else if (strcmp(argv[i], "-s") == 0) {
+                       if (++i < argc)
+                               std_block_size = atol(argv[i]);
+               } else if (strcmp(argv[i], "-k") == 0) {
+                       if (++i < argc)
+                               add_kernel_mmap(argv[i]);
+               } else if (strcmp(argv[i], "-D") == 0) {
+                       if (++i < argc)
+                               debug_level = atoi(argv[i]);
+               } else
+                       usage(argv[0]);
+       }
+       if (inpath)
+               input = xfopen(inpath, "rb");
+       if (outpath)
+               output = xfopen(outpath, "wb");
+
+       process_input(input, output);
+
+       fflush(output);
+
+       return 0;
+}
diff --git a/tools/profile/kprof2perf/perf_format.h b/tools/profile/kprof2perf/perf_format.h
new file mode 100644 (file)
index 0000000..474cd61
--- /dev/null
@@ -0,0 +1,528 @@
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ *
+ * Parts of this file come, either directly, or as baseline, from the Linux
+ * kernel source file:
+ *
+ *      tools/perf/util/header.h
+ *
+ * Such file is ruled by the general Linux kernel copyright.
+ */
+
+#pragma once
+
+#define BITS_PER_LONG (8 * sizeof(long))
+#define BITS_TO_LONGS(bits) (((bits) + BITS_PER_LONG - 1) / BITS_PER_LONG)
+#define DECLARE_BITMAP(name, bits)                             \
+       unsigned long name[BITS_TO_LONGS(bits)]
+
+enum {
+       HEADER_RESERVED         = 0,    /* Always cleared */
+       HEADER_FIRST_FEATURE    = 1,
+       HEADER_TRACING_DATA     = 1,
+       HEADER_BUILD_ID,
+
+       HEADER_HOSTNAME,
+       HEADER_OSRELEASE,
+       HEADER_VERSION,
+       HEADER_ARCH,
+       HEADER_NRCPUS,
+       HEADER_CPUDESC,
+       HEADER_CPUID,
+       HEADER_TOTAL_MEM,
+       HEADER_CMDLINE,
+       HEADER_EVENT_DESC,
+       HEADER_CPU_TOPOLOGY,
+       HEADER_NUMA_TOPOLOGY,
+       HEADER_BRANCH_STACK,
+       HEADER_PMU_MAPPINGS,
+       HEADER_GROUP_DESC,
+       HEADER_LAST_FEATURE,
+       HEADER_FEAT_BITS        = 256,
+};
+
+/*
+ * Bits that can be set in attr.sample_type to request information
+ * in the overflow packets.
+ */
+enum perf_event_sample_format {
+       PERF_SAMPLE_IP                          = 1U << 0,
+       PERF_SAMPLE_TID                         = 1U << 1,
+       PERF_SAMPLE_TIME                        = 1U << 2,
+       PERF_SAMPLE_ADDR                        = 1U << 3,
+       PERF_SAMPLE_READ                        = 1U << 4,
+       PERF_SAMPLE_CALLCHAIN                   = 1U << 5,
+       PERF_SAMPLE_ID                          = 1U << 6,
+       PERF_SAMPLE_CPU                         = 1U << 7,
+       PERF_SAMPLE_PERIOD                      = 1U << 8,
+       PERF_SAMPLE_STREAM_ID                   = 1U << 9,
+       PERF_SAMPLE_RAW                         = 1U << 10,
+       PERF_SAMPLE_BRANCH_STACK                = 1U << 11,
+       PERF_SAMPLE_REGS_USER                   = 1U << 12,
+       PERF_SAMPLE_STACK_USER                  = 1U << 13,
+       PERF_SAMPLE_WEIGHT                      = 1U << 14,
+       PERF_SAMPLE_DATA_SRC                    = 1U << 15,
+       PERF_SAMPLE_IDENTIFIER                  = 1U << 16,
+       PERF_SAMPLE_TRANSACTION                 = 1U << 17,
+       PERF_SAMPLE_REGS_INTR                   = 1U << 18,
+
+       PERF_SAMPLE_MAX = 1U << 19,             /* non-ABI */
+};
+
+enum perf_event_type {
+       /*
+        * If perf_event_attr.sample_id_all is set then all event types will
+        * have the sample_type selected fields related to where/when
+        * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
+        * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
+        * just after the perf_event_header and the fields already present for
+        * the existing fields, i.e. at the end of the payload. That way a newer
+        * perf.data file will be supported by older perf tools, with these new
+        * optional fields being ignored.
+        *
+        * struct sample_id {
+        *      { u32                   pid, tid; } && PERF_SAMPLE_TID
+        *      { u64                   time;     } && PERF_SAMPLE_TIME
+        *      { u64                   id;               } && PERF_SAMPLE_ID
+        *      { u64                   stream_id;} && PERF_SAMPLE_STREAM_ID
+        *      { u32                   cpu, res; } && PERF_SAMPLE_CPU
+        *      { u64                   id;       } && PERF_SAMPLE_IDENTIFIER
+        * } && perf_event_attr::sample_id_all
+        *
+        * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.  The
+        * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
+        * relative to header.size.
+        */
+
+       /*
+        * The MMAP events record the PROT_EXEC mappings so that we can
+        * correlate userspace IPs to code. They have the following structure:
+        *
+        * struct {
+        *      struct perf_event_header        header;
+        *
+        *      u32                             pid, tid;
+        *      u64                             addr;
+        *      u64                             len;
+        *      u64                             pgoff;
+        *      char                            filename[];
+        * };
+        */
+       PERF_RECORD_MMAP                        = 1,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *      u64                             id;
+        *      u64                             lost;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_LOST                        = 2,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *
+        *      u32                             pid, tid;
+        *      char                            comm[];
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_COMM                        = 3,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *      u32                             pid, ppid;
+        *      u32                             tid, ptid;
+        *      u64                             time;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_EXIT                        = 4,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *      u64                             time;
+        *      u64                             id;
+        *      u64                             stream_id;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_THROTTLE                    = 5,
+       PERF_RECORD_UNTHROTTLE                  = 6,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *      u32                             pid, ppid;
+        *      u32                             tid, ptid;
+        *      u64                             time;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_FORK                        = 7,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *      u32                             pid, tid;
+        *
+        *      struct read_format              values;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_READ                        = 8,
+
+       /*
+        * struct {
+        *      struct perf_event_header        header;
+        *
+        *      #
+        *      # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
+        *      # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
+        *      # is fixed relative to header.
+        *      #
+        *
+        *      { u64                   id;       } && PERF_SAMPLE_IDENTIFIER
+        *      { u64                   ip;       } && PERF_SAMPLE_IP
+        *      { u32                   pid, tid; } && PERF_SAMPLE_TID
+        *      { u64                   time;     } && PERF_SAMPLE_TIME
+        *      { u64                   addr;     } && PERF_SAMPLE_ADDR
+        *      { u64                   id;       } && PERF_SAMPLE_ID
+        *      { u64                   stream_id;} && PERF_SAMPLE_STREAM_ID
+        *      { u32                   cpu, res; } && PERF_SAMPLE_CPU
+        *      { u64                   period;   } && PERF_SAMPLE_PERIOD
+        *
+        *      { struct read_format    values;   } && PERF_SAMPLE_READ
+        *
+        *      { u64                   nr,
+        *        u64                   ips[nr];  } && PERF_SAMPLE_CALLCHAIN
+        *
+        *      #
+        *      # The RAW record below is opaque data wrt the ABI
+        *      #
+        *      # That is, the ABI doesn't make any promises wrt to
+        *      # the stability of its content, it may vary depending
+        *      # on event, hardware, kernel version and phase of
+        *      # the moon.
+        *      #
+        *      # In other words, PERF_SAMPLE_RAW contents are not an ABI.
+        *      #
+        *
+        *      { u32                   size;
+        *        char                                  data[size];}&& PERF_SAMPLE_RAW
+        *
+        *      { u64                                   nr;
+        *                { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+        *
+        *      { u64                   abi; # enum perf_sample_regs_abi
+        *        u64                   regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
+        *
+        *      { u64                   size;
+        *        char                  data[size];
+        *        u64                   dyn_size; } && PERF_SAMPLE_STACK_USER
+        *
+        *      { u64                   weight;   } && PERF_SAMPLE_WEIGHT
+        *      { u64                   data_src; } && PERF_SAMPLE_DATA_SRC
+        *              { u64                                   transaction; } && PERF_SAMPLE_TRANSACTION
+        *      { u64                   abi; # enum perf_sample_regs_abi
+        *        u64                   regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
+        * };
+        */
+       PERF_RECORD_SAMPLE                      = 9,
+
+       /*
+        * The MMAP2 records are an augmented version of MMAP, they add
+        * maj, min, ino numbers to be used to uniquely identify each mapping
+        *
+        * struct {
+        *      struct perf_event_header        header;
+        *
+        *      u32                             pid, tid;
+        *      u64                             addr;
+        *      u64                             len;
+        *      u64                             pgoff;
+        *      u32                             maj;
+        *      u32                             min;
+        *      u64                             ino;
+        *      u64                             ino_generation;
+        *      char                            filename[];
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_MMAP2                       = 10,
+
+       PERF_RECORD_MAX,                        /* non-ABI */
+};
+
+#define PERF_MAX_STACK_DEPTH           127
+
+enum perf_callchain_context {
+       PERF_CONTEXT_HV                         = (__uint64_t) -32,
+       PERF_CONTEXT_KERNEL                     = (__uint64_t) -128,
+       PERF_CONTEXT_USER                       = (__uint64_t) -512,
+
+       PERF_CONTEXT_GUEST                      = (__uint64_t) -2048,
+       PERF_CONTEXT_GUEST_KERNEL       = (__uint64_t) -2176,
+       PERF_CONTEXT_GUEST_USER         = (__uint64_t) -2560,
+
+       PERF_CONTEXT_MAX                        = (__uint64_t) -4095,
+};
+
+/*
+ * attr.type
+ */
+enum perf_type_id {
+       PERF_TYPE_HARDWARE                                              = 0,
+       PERF_TYPE_SOFTWARE                                              = 1,
+       PERF_TYPE_TRACEPOINT                                    = 2,
+       PERF_TYPE_HW_CACHE                                              = 3,
+       PERF_TYPE_RAW                                                   = 4,
+       PERF_TYPE_BREAKPOINT                                    = 5,
+       PERF_TYPE_INTEL_CQM                                             = 6,
+
+       PERF_TYPE_MAX,                                                  /* non-ABI */
+};
+
+/*
+ * Generalized performance event event_id types, used by the
+ * attr.event_id parameter of the sys_perf_event_open()
+ * syscall:
+ */
+enum perf_hw_id {
+       /*
+        * Common hardware events, generalized by the kernel:
+        */
+       PERF_COUNT_HW_CPU_CYCLES                                = 0,
+       PERF_COUNT_HW_INSTRUCTIONS                              = 1,
+       PERF_COUNT_HW_CACHE_REFERENCES                  = 2,
+       PERF_COUNT_HW_CACHE_MISSES                              = 3,
+       PERF_COUNT_HW_BRANCH_INSTRUCTIONS               = 4,
+       PERF_COUNT_HW_BRANCH_MISSES                             = 5,
+       PERF_COUNT_HW_BUS_CYCLES                                = 6,
+       PERF_COUNT_HW_STALLED_CYCLES_FRONTEND   = 7,
+       PERF_COUNT_HW_STALLED_CYCLES_BACKEND    = 8,
+       PERF_COUNT_HW_REF_CPU_CYCLES                    = 9,
+
+       PERF_COUNT_HW_MAX,                                              /* non-ABI */
+};
+
+/*
+ * Hardware event_id to monitor via a performance monitoring event:
+ */
+struct perf_event_attr {
+       /*
+        * Major type: hardware/software/tracepoint/etc.
+        */
+       uint32_t type;
+
+       /*
+        * Size of the attr structure, for fwd/bwd compat.
+        */
+       uint32_t size;
+
+       /*
+        * Type specific configuration information.
+        */
+       uint64_t config;
+
+       union {
+               uint64_t sample_period;
+               uint64_t sample_freq;
+       };
+
+       uint64_t sample_type;
+       uint64_t read_format;
+
+       uint64_t disabled               :  1, /* off by default            */
+               inherit            :  1, /* children inherit it   */
+               pinned             :  1, /* must always be on PMU */
+               exclusive          :  1, /* only group on PMU     */
+               exclude_user   :  1, /* don't count user          */
+               exclude_kernel :  1, /* ditto kernel              */
+               exclude_hv         :  1, /* ditto hypervisor      */
+               exclude_idle   :  1, /* don't count when idle */
+               mmap               :  1, /* include mmap data     */
+               comm               :  1, /* include comm data     */
+               freq               :  1, /* use freq, not period  */
+               inherit_stat   :  1, /* per task counts           */
+               enable_on_exec :  1, /* next exec enables         */
+               task               :  1, /* trace fork/exit               */
+               watermark          :  1, /* wakeup_watermark      */
+       /*
+        * precise_ip:
+        *
+        *      0 - SAMPLE_IP can have arbitrary skid
+        *      1 - SAMPLE_IP must have constant skid
+        *      2 - SAMPLE_IP requested to have 0 skid
+        *      3 - SAMPLE_IP must have 0 skid
+        *
+        *      See also PERF_RECORD_MISC_EXACT_IP
+        */
+               precise_ip         :  2, /* skid constraint               */
+               mmap_data          :  1, /* non-exec mmap data    */
+               sample_id_all  :  1, /* sample_type all events */
+
+               exclude_host   :  1, /* don't count in host       */
+               exclude_guest  :  1, /* don't count in guest  */
+
+               exclude_callchain_kernel : 1, /* exclude kernel callchains */
+               exclude_callchain_user   : 1, /* exclude user callchains */
+               mmap2              :  1, /* include mmap with inode data         */
+
+               __reserved_1   : 40;
+
+       union {
+               uint32_t wakeup_events;   /* wakeup every n events */
+               uint32_t wakeup_watermark; /* bytes before wakeup       */
+       };
+
+       uint32_t bp_type;
+       union {
+               uint64_t bp_addr;
+               uint64_t config1; /* extension of config */
+       };
+       union {
+               uint64_t bp_len;
+               uint64_t config2; /* extension of config1 */
+       };
+       uint64_t branch_sample_type; /* enum perf_branch_sample_type */
+
+       /*
+        * Defines set of user regs to dump on samples.
+        * See asm/perf_regs.h for details.
+        */
+       uint64_t sample_regs_user;
+
+       /*
+        * Defines size of the user stack to dump on samples.
+        */
+       uint32_t sample_stack_user;
+
+       /* Align to u64. */
+       uint32_t __reserved_2;
+
+       /*
+        * Defines set of regs to dump for each sample
+        * state captured on:
+        *      - precise = 0: PMU interrupt
+        *      - precise > 0: sampled instruction
+        *
+        * See asm/perf_regs.h for details.
+        */
+       uint64_t sample_regs_intr;
+} __attribute__((packed));
+
+#define PERF_RECORD_MISC_CPUMODE_MASK          (7 << 0)
+#define PERF_RECORD_MISC_CPUMODE_UNKNOWN       (0 << 0)
+#define PERF_RECORD_MISC_KERNEL                        (1 << 0)
+#define PERF_RECORD_MISC_USER                  (2 << 0)
+#define PERF_RECORD_MISC_HYPERVISOR            (3 << 0)
+#define PERF_RECORD_MISC_GUEST_KERNEL          (4 << 0)
+#define PERF_RECORD_MISC_GUEST_USER            (5 << 0)
+
+#define PERF_RECORD_MISC_MMAP_DATA             (1 << 13)
+/*
+ * Indicates that the content of PERF_SAMPLE_IP points to
+ * the actual instruction that triggered the event. See also
+ * perf_event_attr::precise_ip.
+ */
+#define PERF_RECORD_MISC_EXACT_IP              (1 << 14)
+/*
+ * Reserve the last bit to indicate some extended misc field
+ */
+#define PERF_RECORD_MISC_EXT_RESERVED          (1 << 15)
+
+struct perf_event_header {
+       uint32_t type;
+       uint16_t misc;
+       uint16_t size;
+} __attribute__((packed));
+
+struct perf_file_section {
+       uint64_t offset;        /* Offset from start of file */
+       uint64_t size;          /* Size of the section */
+} __attribute__((packed));
+
+struct perf_header_string {
+       uint32_t len;
+       char string[0];         /* Zero terminated */
+};
+
+struct perf_header_string_list {
+       uint32_t nr;
+       struct perf_header_string strings[0];   /* Variable length records */
+};
+
+#define MAX_EVENT_NAME 64
+
+struct perf_trace_event_type {
+       uint64_t event_id;
+       char name[MAX_EVENT_NAME];
+};
+
+struct perf_file_attr {
+       struct perf_event_attr attr;
+       struct perf_file_section ids;
+};
+
+/* "PERFILE2"
+ */
+static const uint64_t PERF_MAGIC2 = 0x32454c4946524550ULL;
+
+struct perf_pipe_file_header {
+       uint64_t magic;                                 /* PERFILE2 */
+       uint64_t size;
+};
+
+struct perf_header {
+       uint64_t magic;                                 /* PERFILE2 */
+       uint64_t size;                                  /* Size of the header */
+       uint64_t attr_size;                             /* size of an attribute in attrs */
+       struct perf_file_section attrs;
+       struct perf_file_section data;
+       struct perf_file_section event_types;
+       DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+} __attribute__((packed));
+
+/* For type PERF_RECORD_MMAP
+ */
+struct perf_record_mmap {
+       struct perf_event_header header;
+       uint32_t pid;
+       uint32_t tid;
+       uint64_t addr;
+       uint64_t len;
+       uint64_t pgoff;
+       char filename[0];
+} __attribute__((packed));
+
+/* For type PERF_RECORD_COMM
+ */
+struct perf_record_comm {
+       struct perf_event_header header;
+       uint32_t pid;
+       uint32_t tid;
+       char comm[0];
+} __attribute__((packed));
+
+/* For type PERF_RECORD_SAMPLE
+ * Configured with: PERF_SAMPLE_IP | PERF_SAMPLE_TID && PERF_SAMPLE_TIME &&
+ * PERF_SAMPLE_ADDR && PERF_SAMPLE_ID && PERF_SAMPLE_CPU &&
+ * PERF_SAMPLE_CALLCHAIN.
+ */
+struct perf_record_sample {
+       struct perf_event_header header;
+       uint64_t ip;
+       uint32_t pid, tid;
+       uint64_t time;
+       uint64_t addr;
+       uint64_t id;
+       uint32_t cpu, res;
+       uint64_t nr;
+       uint64_t ips[0];
+} __attribute__((packed));