Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf tools: Support 'srccode' output

When looking at PT or brstackinsn traces with 'perf script' it can be
very useful to see the source code. This adds a simple facility to print
them with 'perf script', if the information is available through dwarf

% perf record ...
% perf script -F insn,ip,sym,srccode
...

4004c6 main
5 for (i = 0; i < 10000000; i++)
4004cd main
5 for (i = 0; i < 10000000; i++)
4004c6 main
5 for (i = 0; i < 10000000; i++)
4004cd main
5 for (i = 0; i < 10000000; i++)
4004cd main
5 for (i = 0; i < 10000000; i++)
4004cd main
5 for (i = 0; i < 10000000; i++)
4004cd main
5 for (i = 0; i < 10000000; i++)
4004cd main
5 for (i = 0; i < 10000000; i++)
4004b3 main
6 v++;

% perf record -b ...
% perf script -F insn,ip,sym,srccode,brstackinsn

...
main+22:
0000000000400543 insn: e8 ca ff ff ff # PRED
|18 f1();
f1:
0000000000400512 insn: 55
|10 {
0000000000400513 insn: 48 89 e5
0000000000400516 insn: b8 00 00 00 00
|11 f2();
000000000040051b insn: e8 d6 ff ff ff # PRED
f2:
00000000004004f6 insn: 55
|5 {
00000000004004f7 insn: 48 89 e5
00000000004004fa insn: 8b 05 2c 0b 20 00
|6 c = a / b;
0000000000400500 insn: 8b 0d 2a 0b 20 00
0000000000400506 insn: 99
0000000000400507 insn: f7 f9
0000000000400509 insn: 89 05 29 0b 20 00
000000000040050f insn: 90
|7 }
0000000000400510 insn: 5d
0000000000400511 insn: c3 # PRED
f1+14:
0000000000400520 insn: b8 00 00 00 00
|12 f2();
0000000000400525 insn: e8 cc ff ff ff # PRED
f2:
00000000004004f6 insn: 55
|5 {
00000000004004f7 insn: 48 89 e5
00000000004004fa insn: 8b 05 2c 0b 20 00
|6 c = a / b;

Not supported for callchains currently, would need some layout changes
there.

Committer notes:

Fixed the build on Alpine Linux (3.4 .. 3.8) by addressing this
warning:

In file included from util/srccode.c:19:0:
/usr/include/sys/fcntl.h:1:2: error: #warning redirecting incorrect #include <sys/fcntl.h> to <fcntl.h> [-Werror=cpp]
#warning redirecting incorrect #include <sys/fcntl.h> to <fcntl.h>
^~~~~~~
cc1: all warnings being treated as errors

Signed-off-by: Andi Kleen <ak@linux.intel.com>
Tested-by: Jiri Olsa <jolsa@kernel.org>
Link: http://lkml.kernel.org/r/20181204001848.24769-1-andi@firstfloor.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Andi Kleen and committed by
Arnaldo Carvalho de Melo
dd2e18e9 42da438c

+339 -3
+1 -1
tools/perf/Documentation/perf-script.txt
··· 117 117 Comma separated list of fields to print. Options are: 118 118 comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, 119 119 srcline, period, iregs, uregs, brstack, brstacksym, flags, bpf-output, brstackinsn, 120 - brstackoff, callindent, insn, insnlen, synth, phys_addr, metric, misc. 120 + brstackoff, callindent, insn, insnlen, synth, phys_addr, metric, misc, srccode. 121 121 Field list can be prepended with the type, trace, sw or hw, 122 122 to indicate to which event type the field list applies. 123 123 e.g., -F sw:comm,tid,time,ip,sym and -F trace:time,cpu,trace
+45 -2
tools/perf/builtin-script.c
··· 96 96 PERF_OUTPUT_UREGS = 1U << 27, 97 97 PERF_OUTPUT_METRIC = 1U << 28, 98 98 PERF_OUTPUT_MISC = 1U << 29, 99 + PERF_OUTPUT_SRCCODE = 1U << 30, 99 100 }; 100 101 101 102 struct output_option { ··· 133 132 {.str = "phys_addr", .field = PERF_OUTPUT_PHYS_ADDR}, 134 133 {.str = "metric", .field = PERF_OUTPUT_METRIC}, 135 134 {.str = "misc", .field = PERF_OUTPUT_MISC}, 135 + {.str = "srccode", .field = PERF_OUTPUT_SRCCODE}, 136 136 }; 137 137 138 138 enum { ··· 426 424 pr_err("Display of DSO requested but no address to convert.\n"); 427 425 return -EINVAL; 428 426 } 429 - if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) { 427 + if ((PRINT_FIELD(SRCLINE) || PRINT_FIELD(SRCCODE)) && !PRINT_FIELD(IP)) { 430 428 pr_err("Display of source line number requested but sample IP is not\n" 431 429 "selected. Hence, no address to lookup the source line number.\n"); 432 430 return -EINVAL; ··· 909 907 return len; 910 908 } 911 909 910 + static int print_srccode(struct thread *thread, u8 cpumode, uint64_t addr) 911 + { 912 + struct addr_location al; 913 + int ret = 0; 914 + 915 + memset(&al, 0, sizeof(al)); 916 + thread__find_map(thread, cpumode, addr, &al); 917 + if (!al.map) 918 + return 0; 919 + ret = map__fprintf_srccode(al.map, al.addr, stdout, 920 + &thread->srccode_state); 921 + if (ret) 922 + ret += printf("\n"); 923 + return ret; 924 + } 925 + 912 926 static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, 913 927 struct perf_insn *x, u8 *inbuf, int len, 914 928 int insn, FILE *fp, int *total_cycles) ··· 1016 998 x.cpumode, x.cpu, &lastsym, attr, fp); 1017 999 printed += ip__fprintf_jump(br->entries[nr - 1].from, &br->entries[nr - 1], 1018 1000 &x, buffer, len, 0, fp, &total_cycles); 1001 + if (PRINT_FIELD(SRCCODE)) 1002 + printed += print_srccode(thread, x.cpumode, br->entries[nr - 1].from); 1019 1003 } 1020 1004 1021 1005 /* Print all blocks */ ··· 1047 1027 if (ip == end) { 1048 1028 printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, insn, fp, 1049 1029 &total_cycles); 1030 + if (PRINT_FIELD(SRCCODE)) 1031 + printed += print_srccode(thread, x.cpumode, ip); 1050 1032 break; 1051 1033 } else { 1052 1034 printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", ip, 1053 1035 dump_insn(&x, ip, buffer + off, len - off, &ilen)); 1054 1036 if (ilen == 0) 1055 1037 break; 1038 + if (PRINT_FIELD(SRCCODE)) 1039 + print_srccode(thread, x.cpumode, ip); 1056 1040 insn++; 1057 1041 } 1058 1042 } ··· 1087 1063 1088 1064 printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", sample->ip, 1089 1065 dump_insn(&x, sample->ip, buffer, len, NULL)); 1066 + if (PRINT_FIELD(SRCCODE)) 1067 + print_srccode(thread, x.cpumode, sample->ip); 1090 1068 goto out; 1091 1069 } 1092 1070 for (off = 0; off <= end - start; off += ilen) { ··· 1096 1070 dump_insn(&x, start + off, buffer + off, len - off, &ilen)); 1097 1071 if (ilen == 0) 1098 1072 break; 1073 + if (PRINT_FIELD(SRCCODE)) 1074 + print_srccode(thread, x.cpumode, start + off); 1099 1075 } 1100 1076 out: 1101 1077 return printed; ··· 1280 1252 printed += map__fprintf_srcline(al->map, al->addr, "\n ", fp); 1281 1253 1282 1254 printed += perf_sample__fprintf_insn(sample, attr, thread, machine, fp); 1283 - return printed + fprintf(fp, "\n"); 1255 + printed += fprintf(fp, "\n"); 1256 + if (PRINT_FIELD(SRCCODE)) { 1257 + int ret = map__fprintf_srccode(al->map, al->addr, stdout, 1258 + &thread->srccode_state); 1259 + if (ret) { 1260 + printed += ret; 1261 + printed += printf("\n"); 1262 + } 1263 + } 1264 + return printed; 1284 1265 } 1285 1266 1286 1267 static struct { ··· 1828 1791 if (PRINT_FIELD(PHYS_ADDR)) 1829 1792 fprintf(fp, "%16" PRIx64, sample->phys_addr); 1830 1793 fprintf(fp, "\n"); 1794 + 1795 + if (PRINT_FIELD(SRCCODE)) { 1796 + if (map__fprintf_srccode(al->map, al->addr, stdout, 1797 + &thread->srccode_state)) 1798 + printf("\n"); 1799 + } 1831 1800 1832 1801 if (PRINT_FIELD(METRIC)) 1833 1802 perf_sample__fprint_metric(script, thread, evsel, sample, fp);
+1
tools/perf/util/Build
··· 77 77 libperf-y += stat-display.o 78 78 libperf-y += record.o 79 79 libperf-y += srcline.o 80 + libperf-y += srccode.o 80 81 libperf-y += data.o 81 82 libperf-y += tsc.o 82 83 libperf-y += cloexec.o
+1
tools/perf/util/evsel_fprintf.c
··· 173 173 if (!print_oneline) 174 174 printed += fprintf(fp, "\n"); 175 175 176 + /* Add srccode here too? */ 176 177 if (symbol_conf.bt_stop_list && 177 178 node->sym && 178 179 strlist__has_entry(symbol_conf.bt_stop_list,
+49
tools/perf/util/map.c
··· 19 19 #include "srcline.h" 20 20 #include "namespaces.h" 21 21 #include "unwind.h" 22 + #include "srccode.h" 22 23 23 24 static void __maps__insert(struct maps *maps, struct map *map); 24 25 static void __maps__insert_name(struct maps *maps, struct map *map); ··· 420 419 free_srcline(srcline); 421 420 } 422 421 return ret; 422 + } 423 + 424 + int map__fprintf_srccode(struct map *map, u64 addr, 425 + FILE *fp, 426 + struct srccode_state *state) 427 + { 428 + char *srcfile; 429 + int ret = 0; 430 + unsigned line; 431 + int len; 432 + char *srccode; 433 + 434 + if (!map || !map->dso) 435 + return 0; 436 + srcfile = get_srcline_split(map->dso, 437 + map__rip_2objdump(map, addr), 438 + &line); 439 + if (!srcfile) 440 + return 0; 441 + 442 + /* Avoid redundant printing */ 443 + if (state && 444 + state->srcfile && 445 + !strcmp(state->srcfile, srcfile) && 446 + state->line == line) { 447 + free(srcfile); 448 + return 0; 449 + } 450 + 451 + srccode = find_sourceline(srcfile, line, &len); 452 + if (!srccode) 453 + goto out_free_line; 454 + 455 + ret = fprintf(fp, "|%-8d %.*s", line, len, srccode); 456 + state->srcfile = srcfile; 457 + state->line = line; 458 + return ret; 459 + 460 + out_free_line: 461 + free(srcfile); 462 + return ret; 463 + } 464 + 465 + 466 + void srccode_state_free(struct srccode_state *state) 467 + { 468 + zfree(&state->srcfile); 469 + state->line = 0; 423 470 } 424 471 425 472 /**
+16
tools/perf/util/map.h
··· 174 174 int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, 175 175 FILE *fp); 176 176 177 + struct srccode_state { 178 + char *srcfile; 179 + unsigned line; 180 + }; 181 + 182 + static inline void srccode_state_init(struct srccode_state *state) 183 + { 184 + state->srcfile = NULL; 185 + state->line = 0; 186 + } 187 + 188 + void srccode_state_free(struct srccode_state *state); 189 + 190 + int map__fprintf_srccode(struct map *map, u64 addr, 191 + FILE *fp, struct srccode_state *state); 192 + 177 193 int map__load(struct map *map); 178 194 struct symbol *map__find_symbol(struct map *map, u64 addr); 179 195 struct symbol *map__find_symbol_by_name(struct map *map, const char *name);
+186
tools/perf/util/srccode.c
··· 1 + /* 2 + * Manage printing of source lines 3 + * Copyright (c) 2017, Intel Corporation. 4 + * Author: Andi Kleen 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms and conditions of the GNU General Public License, 8 + * version 2, as published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope it will be useful, but WITHOUT 11 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 + * more details. 14 + */ 15 + #include "linux/list.h" 16 + #include <stdlib.h> 17 + #include <sys/mman.h> 18 + #include <sys/stat.h> 19 + #include <fcntl.h> 20 + #include <unistd.h> 21 + #include <assert.h> 22 + #include <string.h> 23 + #include "srccode.h" 24 + #include "debug.h" 25 + #include "util.h" 26 + 27 + #define MAXSRCCACHE (32*1024*1024) 28 + #define MAXSRCFILES 64 29 + #define SRC_HTAB_SZ 64 30 + 31 + struct srcfile { 32 + struct hlist_node hash_nd; 33 + struct list_head nd; 34 + char *fn; 35 + char **lines; 36 + char *map; 37 + unsigned numlines; 38 + size_t maplen; 39 + }; 40 + 41 + static struct hlist_head srcfile_htab[SRC_HTAB_SZ]; 42 + static LIST_HEAD(srcfile_list); 43 + static long map_total_sz; 44 + static int num_srcfiles; 45 + 46 + static unsigned shash(unsigned char *s) 47 + { 48 + unsigned h = 0; 49 + while (*s) 50 + h = 65599 * h + *s++; 51 + return h ^ (h >> 16); 52 + } 53 + 54 + static int countlines(char *map, int maplen) 55 + { 56 + int numl; 57 + char *end = map + maplen; 58 + char *p = map; 59 + 60 + if (maplen == 0) 61 + return 0; 62 + numl = 0; 63 + while (p < end && (p = memchr(p, '\n', end - p)) != NULL) { 64 + numl++; 65 + p++; 66 + } 67 + if (p < end) 68 + numl++; 69 + return numl; 70 + } 71 + 72 + static void fill_lines(char **lines, int maxline, char *map, int maplen) 73 + { 74 + int l; 75 + char *end = map + maplen; 76 + char *p = map; 77 + 78 + if (maplen == 0 || maxline == 0) 79 + return; 80 + l = 0; 81 + lines[l++] = map; 82 + while (p < end && (p = memchr(p, '\n', end - p)) != NULL) { 83 + if (l >= maxline) 84 + return; 85 + lines[l++] = ++p; 86 + } 87 + if (p < end) 88 + lines[l] = p; 89 + } 90 + 91 + static void free_srcfile(struct srcfile *sf) 92 + { 93 + list_del(&sf->nd); 94 + hlist_del(&sf->hash_nd); 95 + map_total_sz -= sf->maplen; 96 + munmap(sf->map, sf->maplen); 97 + free(sf->lines); 98 + free(sf->fn); 99 + free(sf); 100 + num_srcfiles--; 101 + } 102 + 103 + static struct srcfile *find_srcfile(char *fn) 104 + { 105 + struct stat st; 106 + struct srcfile *h; 107 + int fd; 108 + unsigned long sz; 109 + unsigned hval = shash((unsigned char *)fn) % SRC_HTAB_SZ; 110 + 111 + hlist_for_each_entry (h, &srcfile_htab[hval], hash_nd) { 112 + if (!strcmp(fn, h->fn)) { 113 + /* Move to front */ 114 + list_del(&h->nd); 115 + list_add(&h->nd, &srcfile_list); 116 + return h; 117 + } 118 + } 119 + 120 + /* Only prune if there is more than one entry */ 121 + while ((num_srcfiles > MAXSRCFILES || map_total_sz > MAXSRCCACHE) && 122 + srcfile_list.next != &srcfile_list) { 123 + assert(!list_empty(&srcfile_list)); 124 + h = list_entry(srcfile_list.prev, struct srcfile, nd); 125 + free_srcfile(h); 126 + } 127 + 128 + fd = open(fn, O_RDONLY); 129 + if (fd < 0 || fstat(fd, &st) < 0) { 130 + pr_debug("cannot open source file %s\n", fn); 131 + return NULL; 132 + } 133 + 134 + h = malloc(sizeof(struct srcfile)); 135 + if (!h) 136 + return NULL; 137 + 138 + h->fn = strdup(fn); 139 + if (!h->fn) 140 + goto out_h; 141 + 142 + h->maplen = st.st_size; 143 + sz = (h->maplen + page_size - 1) & ~(page_size - 1); 144 + h->map = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); 145 + close(fd); 146 + if (h->map == (char *)-1) { 147 + pr_debug("cannot mmap source file %s\n", fn); 148 + goto out_fn; 149 + } 150 + h->numlines = countlines(h->map, h->maplen); 151 + h->lines = calloc(h->numlines, sizeof(char *)); 152 + if (!h->lines) 153 + goto out_map; 154 + fill_lines(h->lines, h->numlines, h->map, h->maplen); 155 + list_add(&h->nd, &srcfile_list); 156 + hlist_add_head(&h->hash_nd, &srcfile_htab[hval]); 157 + map_total_sz += h->maplen; 158 + num_srcfiles++; 159 + return h; 160 + 161 + out_map: 162 + munmap(h->map, sz); 163 + out_fn: 164 + free(h->fn); 165 + out_h: 166 + free(h); 167 + return NULL; 168 + } 169 + 170 + /* Result is not 0 terminated */ 171 + char *find_sourceline(char *fn, unsigned line, int *lenp) 172 + { 173 + char *l, *p; 174 + struct srcfile *sf = find_srcfile(fn); 175 + if (!sf) 176 + return NULL; 177 + line--; 178 + if (line >= sf->numlines) 179 + return NULL; 180 + l = sf->lines[line]; 181 + if (!l) 182 + return NULL; 183 + p = memchr(l, '\n', sf->map + sf->maplen - l); 184 + *lenp = p - l; 185 + return l; 186 + }
+7
tools/perf/util/srccode.h
··· 1 + #ifndef SRCCODE_H 2 + #define SRCCODE_H 1 3 + 4 + /* Result is not 0 terminated */ 5 + char *find_sourceline(char *fn, unsigned line, int *lenp); 6 + 7 + #endif
+28
tools/perf/util/srcline.c
··· 548 548 return srcline; 549 549 } 550 550 551 + /* Returns filename and fills in line number in line */ 552 + char *get_srcline_split(struct dso *dso, u64 addr, unsigned *line) 553 + { 554 + char *file = NULL; 555 + const char *dso_name; 556 + 557 + if (!dso->has_srcline) 558 + goto out; 559 + 560 + dso_name = dso__name(dso); 561 + if (dso_name == NULL) 562 + goto out; 563 + 564 + if (!addr2line(dso_name, addr, &file, line, dso, true, NULL, NULL)) 565 + goto out; 566 + 567 + dso->a2l_fails = 0; 568 + return file; 569 + 570 + out: 571 + if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) { 572 + dso->has_srcline = 0; 573 + dso__free_a2l(dso); 574 + } 575 + 576 + return NULL; 577 + } 578 + 551 579 void free_srcline(char *srcline) 552 580 { 553 581 if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0)
+1
tools/perf/util/srcline.h
··· 16 16 bool show_sym, bool show_addr, bool unwind_inlines, 17 17 u64 ip); 18 18 void free_srcline(char *srcline); 19 + char *get_srcline_split(struct dso *dso, u64 addr, unsigned *line); 19 20 20 21 /* insert the srcline into the DSO, which will take ownership */ 21 22 void srcline__tree_insert(struct rb_root *tree, u64 addr, char *srcline);
+2
tools/perf/util/thread.c
··· 64 64 RB_CLEAR_NODE(&thread->rb_node); 65 65 /* Thread holds first ref to nsdata. */ 66 66 thread->nsinfo = nsinfo__new(pid); 67 + srccode_state_init(&thread->srccode_state); 67 68 } 68 69 69 70 return thread; ··· 104 103 105 104 unwind__finish_access(thread); 106 105 nsinfo__zput(thread->nsinfo); 106 + srccode_state_free(&thread->srccode_state); 107 107 108 108 exit_rwsem(&thread->namespaces_lock); 109 109 exit_rwsem(&thread->comm_lock);
+2
tools/perf/util/thread.h
··· 8 8 #include <unistd.h> 9 9 #include <sys/types.h> 10 10 #include "symbol.h" 11 + #include "map.h" 11 12 #include <strlist.h> 12 13 #include <intlist.h> 13 14 #include "rwsem.h" ··· 39 38 void *priv; 40 39 struct thread_stack *ts; 41 40 struct nsinfo *nsinfo; 41 + struct srccode_state srccode_state; 42 42 #ifdef HAVE_LIBUNWIND_SUPPORT 43 43 void *addr_space; 44 44 struct unwind_libunwind_ops *unwind_libunwind_ops;