perf: Treat the kernel like [kernel.kallsyms]
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 13 May 2016 16:08:35 +0000 (12:08 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 16 Jun 2016 15:48:36 +0000 (11:48 -0400)
The perf ABI knows about the kernel, and there are bits in the data format
for kernel mmaps and traces.  When we run Linux's perf report, we can point
it at our symbol table and it can resolve the symbols.

Previously, we were treating the kernel like it was a user binary and had
to hack up Linux's perf to handle it accordingly.  Now we're just emitting
the right thing.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
tools/profile/perf/perf_core.c
tools/profile/perf/perfconv.c
tools/profile/perf/perfconv.h

index 83881b2..ee3d3ea 100644 (file)
@@ -642,35 +642,6 @@ void perf_make_eventsel_from_event_mask(struct perf_eventsel *sel,
        sel->eidx = perf_find_event_by_id(event, mask);
 }
 
-static bool perf_get_kernel_elf_path(char *path, size_t psize, size_t *ksize)
-{
-       int fd;
-       ssize_t rsize = -1;
-
-       fd = open("#version/kernel_path", O_RDONLY);
-       if (fd >= 0) {
-               rsize = read(fd, path, psize);
-               while ((rsize > 0) && (path[rsize - 1] == '\n'))
-                       rsize--;
-               close(fd);
-
-               /* We do not export the real kernel size from the #versions device,
-                * because of cyclic dependency issues. The only reason the size is
-                * needed, is because we generate an MMAP record, which Linux perf
-                * uses to find which ELF should be used to resolve addresses to
-                * symbols. Above the Akaros kernel, hardly other ELF will be loaded,
-                * so the worst it can happen if something above the kernel ELF
-                * proper address gets a hit, is that Linux perf will ask the kernel
-                * ELF to resolve an address, and that will fail.
-                * So here we use a large enough size to cover kernel size expansions
-                * for the next 10 years.
-                */
-               *ksize = 128 * 1024 * 1024;
-       }
-
-       return rsize > 0;
-}
-
 void perf_convert_trace_data(struct perfconv_context *cctx, const char *input,
                                                         const char *output)
 {
@@ -682,12 +653,7 @@ void perf_convert_trace_data(struct perfconv_context *cctx, const char *input,
        if (xfsize(infile) > 0) {
                outfile = xfopen(output, "wb");
 
-               if (perf_get_kernel_elf_path(kpath, sizeof(kpath), &ksize))
-                       perfconv_add_kernel_mmap(kpath, ksize, cctx);
-               else
-                       fprintf(stderr, "Unable to fetch kernel build information!\n"
-                                       "Kernel traces will be missing symbol information.\n");
-
+               perfconv_add_kernel_mmap(cctx);
                perfconv_process_input(cctx, infile, outfile);
 
                fclose(outfile);
index 7453a2b..27377b0 100644 (file)
@@ -200,40 +200,29 @@ static uint64_t perfconv_get_config_type(uint64_t config)
        return (config >> 56) & 0x7f;
 }
 
-static void add_static_mmap(const char *path, uint64_t addr, uint64_t offset,
-                                                       uint64_t size, uint32_t pid,
-                                                       struct perfconv_context *cctx)
+void perfconv_add_kernel_mmap(struct perfconv_context *cctx)
 {
+       char path[] = "[kernel.kallsyms]";
        struct static_mmap64 *mm;
-       struct stat stb;
-
-       if (size == 0) {
-               if (stat(path, &stb)) {
-                       fprintf(stderr, "Unable to stat mmapped file '%s': %s\n",
-                                       path, strerror(errno));
-                       exit(1);
-               }
-               size = (uint64_t) stb.st_size;
-       }
 
        mm = xmem_arena_zalloc(&cctx->ma, sizeof(struct static_mmap64));
-       mm->pid = pid;
-       mm->addr = addr;
-       mm->size = size;
-       mm->offset = offset;
+       mm->pid = -1;                           /* Linux HOST_KERNEL_ID == -1 */
+       mm->tid = 0;                            /* Default thread: swapper */
+       mm->header_misc = PERF_RECORD_MISC_KERNEL;
+       /* Linux sets addr = 0, size = 0xffffffff9fffffff, off = 0xffffffff81000000
+        * Their mmap record is also called [kernel.kallsyms]_text (I think).  They
+        * also have a _text symbol in kallsyms at ffffffff81000000 (equiv to our
+        * KERN_LOAD_ADDR (which is 0xffffffffc0000000)).  Either way, this seems to
+        * work for us; we'll see.  It's also arch-independent (for now). */
+       mm->addr = 0;
+       mm->size = 0xffffffffffffffff;
+       mm->offset = 0x0;
        mm->path = xmem_arena_strdup(&cctx->ma, path);
 
        mm->next = cctx->static_mmaps;
        cctx->static_mmaps = mm;
 }
 
-void perfconv_add_kernel_mmap(const char *path, size_t ksize,
-                                                         struct perfconv_context *cctx)
-{
-       add_static_mmap(path, cctx->kernel_load_address, cctx->kernel_load_address,
-                                       (uint64_t) ksize, 0, cctx);
-}
-
 static void headers_init(struct perf_headers *hdrs)
 {
        ZERO_DATA(*hdrs);
@@ -373,9 +362,10 @@ static void emit_static_mmaps(struct perfconv_context *cctx)
                struct perf_record_mmap *xrec = xzmalloc(size);
 
                xrec->header.type = PERF_RECORD_MMAP;
-               xrec->header.misc = PERF_RECORD_MISC_USER;
+               xrec->header.misc = mm->header_misc;
                xrec->header.size = size;
-               xrec->pid = xrec->tid = mm->pid;
+               xrec->pid = mm->pid;
+               xrec->tid = mm->tid;
                xrec->addr = mm->addr;
                xrec->len = mm->size;
                xrec->pgoff = mm->offset;
@@ -435,9 +425,17 @@ static void emit_kernel_trace64(struct perf_record *pr,
        struct perf_record_sample *xrec = xzmalloc(size);
 
        xrec->header.type = PERF_RECORD_SAMPLE;
-       xrec->header.misc = PERF_RECORD_MISC_USER;
+       xrec->header.misc = PERF_RECORD_MISC_KERNEL;
        xrec->header.size = size;
        xrec->ip = rec->trace[0];
+       /* TODO: We could have pid/tid for kernel tasks during their lifetime.
+        * During syscalls, we could use the pid of the process.  For the kernel
+        * itself, -1 seems to be generic kernel stuff, and tid == 0 is 'swapper'.
+        *
+        * Right now, the kernel doesn't even tell us the pid, so we have no way of
+        * knowing from userspace. */
+       xrec->pid = -1;
+       xrec->tid = 0;
        xrec->time = rec->tstamp;
        xrec->addr = rec->trace[0];
        xrec->id = perfconv_get_event_id(cctx, rec->info);
@@ -506,7 +504,6 @@ struct perfconv_context *perfconv_create_context(void)
        struct perfconv_context *cctx = xzmalloc(sizeof(struct perfconv_context));
 
        xmem_arena_init(&cctx->ma, 0);
-       cctx->kernel_load_address = 0xffffffffc0000000;
        cctx->alloced_events = 128;
        cctx->events = xmem_arena_zalloc(
                &cctx->ma, cctx->alloced_events * sizeof(*cctx->events));
@@ -518,8 +515,6 @@ struct perfconv_context *perfconv_create_context(void)
        mem_file_init(&cctx->data, &cctx->ma);
        mem_file_init(&cctx->event_types, &cctx->ma);
 
-       emit_comm(0, "[kernel]", cctx);
-
        return cctx;
 }
 
index 946f7bd..1071568 100644 (file)
@@ -45,6 +45,8 @@ struct static_mmap64 {
        uint64_t size;
        uint64_t offset;
        uint32_t pid;
+       uint32_t tid;
+       uint16_t header_misc;
        char *path;
 };
 
@@ -60,7 +62,6 @@ struct perf_event_id {
 struct perfconv_context {
        struct mem_arena ma;
        int debug_level;
-       uint64_t kernel_load_address;
        struct static_mmap64 *static_mmaps;
        uint64_t sqnr_id;
        struct perf_event_id *events;
@@ -73,7 +74,6 @@ struct perfconv_context {
 struct perfconv_context *perfconv_create_context(void);
 void perfconv_free_context(struct perfconv_context *cctx);
 void perfconv_set_dbglevel(int level, struct perfconv_context *cctx);
-void perfconv_add_kernel_mmap(const char *path, size_t size,
-                                                         struct perfconv_context *cctx);
+void perfconv_add_kernel_mmap(struct perfconv_context *cctx);
 void perfconv_process_input(struct perfconv_context *cctx, FILE *input,
                                                        FILE *output);