perf: Use PERF_SAMPLE_IDENTIFIER
[akaros.git] / tools / profile / perf / perfconv.c
index 4b064fb..fdcd2f5 100644 (file)
@@ -29,6 +29,7 @@
 #include "xlib.h"
 #include "perf_core.h"
 #include "perfconv.h"
+#include "elf.h"
 
 #define MAX_PERF_RECORD_SIZE (32 * 1024 * 1024)
 #define PERF_RECORD_BUFFER_SIZE 1024
@@ -37,6 +38,8 @@
 
 #define MBWR_SOLID (1 << 0)
 
+char *cmd_line_save;
+
 struct perf_record {
        uint64_t type;
        uint64_t size;
@@ -372,6 +375,131 @@ static void hdr_do_nrcpus(struct perf_header *ph, struct perf_headers *hdrs)
        headers_add_header(ph, hdrs, HEADER_NRCPUS, mb);
 }
 
+/* Unfortunately, HEADER_CMDLINE doesn't just take a string.  It takes a list of
+ * null-terminated perf_header_strings (i.e. argv), with the a u32 for the
+ * number of strings.  We can just send one string for the entire cmd line. */
+static void hdr_do_cmdline(struct perf_header *ph, struct perf_headers *hdrs)
+{
+       struct perf_header_string *hdr;
+       struct mem_block *mb;
+       size_t str_sz = strlen(cmd_line_save) + 1;
+       size_t hdr_sz = sizeof(uint32_t) +
+                       ROUNDUP(str_sz + sizeof(struct perf_header_string),
+                               PERF_STRING_ALIGN);
+
+       mb = mem_block_alloc(hdr_sz);
+       /* emit the nr strings (1) */
+       *(uint32_t*)mb->wptr = 1;
+       mb->wptr += sizeof(uint32_t);
+       /* emit the perf_header_string, as usual */
+       hdr = (struct perf_header_string*)mb->wptr;
+       mb->wptr += hdr_sz - sizeof(uint32_t);
+       hdr->len = str_sz;
+       memcpy(hdr->string, cmd_line_save, str_sz);
+
+       headers_add_header(ph, hdrs, HEADER_CMDLINE, mb);
+}
+
+/* Returns TRUE if we already emitted a build-id for path.  If this gets too
+ * slow (which we'll know because of perf!) we can use a hash table (don't hash
+ * on the first few bytes btw - they are usually either /bin or /lib). */
+static bool lookup_buildid(struct perfconv_context *cctx, const char *path)
+{
+       struct build_id_event *b_evt;
+       struct mem_block *mb;
+       size_t b_evt_path_sz;
+
+       mb = cctx->hdrs.headers[HEADER_BUILD_ID];
+       while (mb) {
+               b_evt = (struct build_id_event*)mb->base;
+               b_evt_path_sz = b_evt->header.size -
+                               offsetof(struct build_id_event, filename);
+               /* ignoring the last byte since we forced it to be \0 earlier. */
+               if (!strncmp(b_evt->filename, path, b_evt_path_sz - 1))
+                       return TRUE;
+               mb = mb->next;
+       }
+       return FALSE;
+}
+
+/* Helper: given a path, allocs and inits a build_id_event within a mem_block,
+ * returning both via parameters.  Caller needs to set header.misc and fill in
+ * the actual build_id. */
+static void build_id_alloc(const char *path, struct build_id_event **b_evt_p,
+                           struct mem_block **mb_p)
+{
+       struct build_id_event *b_evt;
+       struct mem_block *mb;
+       size_t path_sz, b_evt_sz;
+
+       path_sz = strlen(path) + 1;
+       b_evt_sz = path_sz + sizeof(struct build_id_event);
+
+       mb = mem_block_alloc(b_evt_sz);
+       b_evt = (struct build_id_event*)mb->wptr;
+       mb->wptr += b_evt_sz;
+
+       b_evt->header.type = 0; /* if this fails, try 67 (synthetic build id) */
+       /* header.size filled in by the caller, depending on the type */
+       b_evt->header.size = b_evt_sz;
+       strlcpy(b_evt->filename, path, path_sz);
+
+       *b_evt_p = b_evt;
+       *mb_p = mb;
+}
+
+/* Add a build-id header.  Unlike many of the other headers, this one is built
+ * on the fly as we emit other records. */
+static void hdr_add_buildid(struct perfconv_context *cctx, const char *path,
+                            int pid)
+{
+       struct build_id_event *b_evt;
+       struct mem_block *mb;
+       int ret;
+
+       if (lookup_buildid(cctx, path))
+               return;
+
+       build_id_alloc(path, &b_evt, &mb);
+       b_evt->header.misc = PERF_RECORD_MISC_USER;
+       b_evt->pid = pid;
+       ret = filename__read_build_id(path, b_evt->build_id, BUILD_ID_SIZE);
+       if (ret <= 0)
+               free(mb);
+       else
+               headers_add_header(&cctx->ph, &cctx->hdrs, HEADER_BUILD_ID, mb);
+}
+
+static void convert_str_to_binary(char *b_id_str, uint8_t *b_id_raw)
+{
+       char *c = b_id_str;
+
+       for (int i = 0; i < BUILD_ID_SIZE; i++) {
+               b_id_raw[i] = nibble_to_num(*c) << 4 | nibble_to_num(*(c + 1));
+               c += 2;
+       }
+}
+
+void perfconv_add_kernel_buildid(struct perfconv_context *cctx)
+{
+       struct build_id_event *b_evt;
+       struct mem_block *mb;
+       int ret, fd;
+       char build_id[BUILD_ID_SIZE * 2 + 1] = {0};
+
+       build_id_alloc("[kernel.kallsyms]", &b_evt, &mb);
+       b_evt->header.misc = PERF_RECORD_MISC_KERNEL;
+       b_evt->pid = -1;
+       fd = xopen("#version/build_id", O_RDONLY, 0);
+       ret = read(fd, build_id, sizeof(build_id));
+       if (ret <= 0) {
+               free(mb);
+       } else {
+               convert_str_to_binary(build_id, b_evt->build_id);
+               headers_add_header(&cctx->ph, &cctx->hdrs, HEADER_BUILD_ID, mb);
+       }
+}
+
 /* Helper: adds all the headers, marking them in PH and storing them in
  * feat_hdrs. */
 static void headers_build(struct perf_header *ph, struct perf_headers *hdrs,
@@ -379,6 +507,7 @@ static void headers_build(struct perf_header *ph, struct perf_headers *hdrs,
 {
        hdr_do_osrelease(ph, hdrs);
        hdr_do_nrcpus(ph, hdrs);
+       hdr_do_cmdline(ph, hdrs);
 
        headers_finalize(hdrs, feat_hdrs);
 }
@@ -443,8 +572,8 @@ static uint64_t perfconv_get_event_id(struct perfconv_context *cctx,
        attr.sample_period = sel->ev.trigger_count;
        /* Closely coupled with struct perf_record_sample */
        attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
-                          PERF_SAMPLE_ADDR | PERF_SAMPLE_ID | PERF_SAMPLE_CPU |
-                          PERF_SAMPLE_CALLCHAIN;
+                          PERF_SAMPLE_ADDR | PERF_SAMPLE_IDENTIFIER |
+                          PERF_SAMPLE_CPU | PERF_SAMPLE_CALLCHAIN;
        attr.exclude_guest = 1; /* we can't trace VMs yet */
        attr.exclude_hv = 1;    /* we aren't tracing our hypervisor, AFAIK */
        attr.exclude_user = !PMEV_GET_USR(raw_event);
@@ -505,6 +634,8 @@ static void emit_pid_mmap64(struct perf_record *pr,
                      strlen((char*)rec->path) + 1;
        struct perf_record_mmap *xrec = xzmalloc(size);
 
+       hdr_add_buildid(cctx, (char*)rec->path, rec->pid);
+
        xrec->header.type = PERF_RECORD_MMAP;
        xrec->header.misc = PERF_RECORD_MISC_USER;
        xrec->header.size = size;
@@ -542,7 +673,7 @@ static void emit_kernel_trace64(struct perf_record *pr,
        xrec->tid = 0;
        xrec->time = rec->tstamp;
        xrec->addr = rec->trace[0];
-       xrec->id = perfconv_get_event_id(cctx, rec->info);
+       xrec->identifier = perfconv_get_event_id(cctx, rec->info);
        xrec->cpu = rec->cpu;
        xrec->nr = rec->num_traces - 1;
        memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
@@ -568,7 +699,7 @@ static void emit_user_trace64(struct perf_record *pr,
        xrec->pid = xrec->tid = rec->pid;
        xrec->time = rec->tstamp;
        xrec->addr = rec->trace[0];
-       xrec->id = perfconv_get_event_id(cctx, rec->info);
+       xrec->identifier = perfconv_get_event_id(cctx, rec->info);
        xrec->cpu = rec->cpu;
        xrec->nr = rec->num_traces - 1;
        memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
@@ -582,8 +713,11 @@ static void emit_new_process(struct perf_record *pr,
                                                         struct perfconv_context *cctx)
 {
        struct proftype_new_process *rec = (struct proftype_new_process *) pr->data;
-       const char *comm = strrchr((char*)rec->path, '/');
+       const char *comm;
+
+       hdr_add_buildid(cctx, (char*)rec->path, rec->pid);
 
+       comm = strrchr((char*)rec->path, '/');
        if (!comm)
                comm = (char*)rec->path;
        else