diff options
Diffstat (limited to 'tools/perf/builtin-annotate.c')
| -rw-r--r-- | tools/perf/builtin-annotate.c | 262 |
1 files changed, 214 insertions, 48 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index b1ed5f766cb3..7e58e3ad1508 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -25,6 +25,10 @@ | |||
| 25 | #define SHOW_USER 2 | 25 | #define SHOW_USER 2 |
| 26 | #define SHOW_HV 4 | 26 | #define SHOW_HV 4 |
| 27 | 27 | ||
| 28 | #define MIN_GREEN 0.5 | ||
| 29 | #define MIN_RED 5.0 | ||
| 30 | |||
| 31 | |||
| 28 | static char const *input_name = "perf.data"; | 32 | static char const *input_name = "perf.data"; |
| 29 | static char *vmlinux = "vmlinux"; | 33 | static char *vmlinux = "vmlinux"; |
| 30 | 34 | ||
| @@ -39,40 +43,42 @@ static int dump_trace = 0; | |||
| 39 | 43 | ||
| 40 | static int verbose; | 44 | static int verbose; |
| 41 | 45 | ||
| 46 | static int print_line; | ||
| 47 | |||
| 42 | static unsigned long page_size; | 48 | static unsigned long page_size; |
| 43 | static unsigned long mmap_window = 32; | 49 | static unsigned long mmap_window = 32; |
| 44 | 50 | ||
| 45 | struct ip_event { | 51 | struct ip_event { |
| 46 | struct perf_event_header header; | 52 | struct perf_event_header header; |
| 47 | __u64 ip; | 53 | u64 ip; |
| 48 | __u32 pid, tid; | 54 | u32 pid, tid; |
| 49 | }; | 55 | }; |
| 50 | 56 | ||
| 51 | struct mmap_event { | 57 | struct mmap_event { |
| 52 | struct perf_event_header header; | 58 | struct perf_event_header header; |
| 53 | __u32 pid, tid; | 59 | u32 pid, tid; |
| 54 | __u64 start; | 60 | u64 start; |
| 55 | __u64 len; | 61 | u64 len; |
| 56 | __u64 pgoff; | 62 | u64 pgoff; |
| 57 | char filename[PATH_MAX]; | 63 | char filename[PATH_MAX]; |
| 58 | }; | 64 | }; |
| 59 | 65 | ||
| 60 | struct comm_event { | 66 | struct comm_event { |
| 61 | struct perf_event_header header; | 67 | struct perf_event_header header; |
| 62 | __u32 pid, tid; | 68 | u32 pid, tid; |
| 63 | char comm[16]; | 69 | char comm[16]; |
| 64 | }; | 70 | }; |
| 65 | 71 | ||
| 66 | struct fork_event { | 72 | struct fork_event { |
| 67 | struct perf_event_header header; | 73 | struct perf_event_header header; |
| 68 | __u32 pid, ppid; | 74 | u32 pid, ppid; |
| 69 | }; | 75 | }; |
| 70 | 76 | ||
| 71 | struct period_event { | 77 | struct period_event { |
| 72 | struct perf_event_header header; | 78 | struct perf_event_header header; |
| 73 | __u64 time; | 79 | u64 time; |
| 74 | __u64 id; | 80 | u64 id; |
| 75 | __u64 sample_period; | 81 | u64 sample_period; |
| 76 | }; | 82 | }; |
| 77 | 83 | ||
| 78 | typedef union event_union { | 84 | typedef union event_union { |
| @@ -84,6 +90,13 @@ typedef union event_union { | |||
| 84 | struct period_event period; | 90 | struct period_event period; |
| 85 | } event_t; | 91 | } event_t; |
| 86 | 92 | ||
| 93 | |||
| 94 | struct sym_ext { | ||
| 95 | struct rb_node node; | ||
| 96 | double percent; | ||
| 97 | char *path; | ||
| 98 | }; | ||
| 99 | |||
| 87 | static LIST_HEAD(dsos); | 100 | static LIST_HEAD(dsos); |
| 88 | static struct dso *kernel_dso; | 101 | static struct dso *kernel_dso; |
| 89 | static struct dso *vdso; | 102 | static struct dso *vdso; |
| @@ -145,7 +158,7 @@ static void dsos__fprintf(FILE *fp) | |||
| 145 | dso__fprintf(pos, fp); | 158 | dso__fprintf(pos, fp); |
| 146 | } | 159 | } |
| 147 | 160 | ||
| 148 | static struct symbol *vdso__find_symbol(struct dso *dso, __u64 ip) | 161 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) |
| 149 | { | 162 | { |
| 150 | return dso__find_symbol(kernel_dso, ip); | 163 | return dso__find_symbol(kernel_dso, ip); |
| 151 | } | 164 | } |
| @@ -178,19 +191,19 @@ static int load_kernel(void) | |||
| 178 | 191 | ||
| 179 | struct map { | 192 | struct map { |
| 180 | struct list_head node; | 193 | struct list_head node; |
| 181 | __u64 start; | 194 | u64 start; |
| 182 | __u64 end; | 195 | u64 end; |
| 183 | __u64 pgoff; | 196 | u64 pgoff; |
| 184 | __u64 (*map_ip)(struct map *, __u64); | 197 | u64 (*map_ip)(struct map *, u64); |
| 185 | struct dso *dso; | 198 | struct dso *dso; |
| 186 | }; | 199 | }; |
| 187 | 200 | ||
| 188 | static __u64 map__map_ip(struct map *map, __u64 ip) | 201 | static u64 map__map_ip(struct map *map, u64 ip) |
| 189 | { | 202 | { |
| 190 | return ip - map->start + map->pgoff; | 203 | return ip - map->start + map->pgoff; |
| 191 | } | 204 | } |
| 192 | 205 | ||
| 193 | static __u64 vdso__map_ip(struct map *map, __u64 ip) | 206 | static u64 vdso__map_ip(struct map *map, u64 ip) |
| 194 | { | 207 | { |
| 195 | return ip; | 208 | return ip; |
| 196 | } | 209 | } |
| @@ -373,7 +386,7 @@ static int thread__fork(struct thread *self, struct thread *parent) | |||
| 373 | return 0; | 386 | return 0; |
| 374 | } | 387 | } |
| 375 | 388 | ||
| 376 | static struct map *thread__find_map(struct thread *self, __u64 ip) | 389 | static struct map *thread__find_map(struct thread *self, u64 ip) |
| 377 | { | 390 | { |
| 378 | struct map *pos; | 391 | struct map *pos; |
| 379 | 392 | ||
| @@ -414,7 +427,7 @@ struct hist_entry { | |||
| 414 | struct map *map; | 427 | struct map *map; |
| 415 | struct dso *dso; | 428 | struct dso *dso; |
| 416 | struct symbol *sym; | 429 | struct symbol *sym; |
| 417 | __u64 ip; | 430 | u64 ip; |
| 418 | char level; | 431 | char level; |
| 419 | 432 | ||
| 420 | uint32_t count; | 433 | uint32_t count; |
| @@ -519,7 +532,7 @@ sort__dso_print(FILE *fp, struct hist_entry *self) | |||
| 519 | if (self->dso) | 532 | if (self->dso) |
| 520 | return fprintf(fp, "%-25s", self->dso->name); | 533 | return fprintf(fp, "%-25s", self->dso->name); |
| 521 | 534 | ||
| 522 | return fprintf(fp, "%016llx ", (__u64)self->ip); | 535 | return fprintf(fp, "%016llx ", (u64)self->ip); |
| 523 | } | 536 | } |
| 524 | 537 | ||
| 525 | static struct sort_entry sort_dso = { | 538 | static struct sort_entry sort_dso = { |
| @@ -533,7 +546,7 @@ static struct sort_entry sort_dso = { | |||
| 533 | static int64_t | 546 | static int64_t |
| 534 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 547 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
| 535 | { | 548 | { |
| 536 | __u64 ip_l, ip_r; | 549 | u64 ip_l, ip_r; |
| 537 | 550 | ||
| 538 | if (left->sym == right->sym) | 551 | if (left->sym == right->sym) |
| 539 | return 0; | 552 | return 0; |
| @@ -550,13 +563,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self) | |||
| 550 | size_t ret = 0; | 563 | size_t ret = 0; |
| 551 | 564 | ||
| 552 | if (verbose) | 565 | if (verbose) |
| 553 | ret += fprintf(fp, "%#018llx ", (__u64)self->ip); | 566 | ret += fprintf(fp, "%#018llx ", (u64)self->ip); |
| 554 | 567 | ||
| 555 | if (self->sym) { | 568 | if (self->sym) { |
| 556 | ret += fprintf(fp, "[%c] %s", | 569 | ret += fprintf(fp, "[%c] %s", |
| 557 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); | 570 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); |
| 558 | } else { | 571 | } else { |
| 559 | ret += fprintf(fp, "%#016llx", (__u64)self->ip); | 572 | ret += fprintf(fp, "%#016llx", (u64)self->ip); |
| 560 | } | 573 | } |
| 561 | 574 | ||
| 562 | return ret; | 575 | return ret; |
| @@ -647,7 +660,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
| 647 | /* | 660 | /* |
| 648 | * collect histogram counts | 661 | * collect histogram counts |
| 649 | */ | 662 | */ |
| 650 | static void hist_hit(struct hist_entry *he, __u64 ip) | 663 | static void hist_hit(struct hist_entry *he, u64 ip) |
| 651 | { | 664 | { |
| 652 | unsigned int sym_size, offset; | 665 | unsigned int sym_size, offset; |
| 653 | struct symbol *sym = he->sym; | 666 | struct symbol *sym = he->sym; |
| @@ -676,7 +689,7 @@ static void hist_hit(struct hist_entry *he, __u64 ip) | |||
| 676 | 689 | ||
| 677 | static int | 690 | static int |
| 678 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | 691 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, |
| 679 | struct symbol *sym, __u64 ip, char level) | 692 | struct symbol *sym, u64 ip, char level) |
| 680 | { | 693 | { |
| 681 | struct rb_node **p = &hist.rb_node; | 694 | struct rb_node **p = &hist.rb_node; |
| 682 | struct rb_node *parent = NULL; | 695 | struct rb_node *parent = NULL; |
| @@ -848,7 +861,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 848 | int show = 0; | 861 | int show = 0; |
| 849 | struct dso *dso = NULL; | 862 | struct dso *dso = NULL; |
| 850 | struct thread *thread = threads__findnew(event->ip.pid); | 863 | struct thread *thread = threads__findnew(event->ip.pid); |
| 851 | __u64 ip = event->ip.ip; | 864 | u64 ip = event->ip.ip; |
| 852 | struct map *map = NULL; | 865 | struct map *map = NULL; |
| 853 | 866 | ||
| 854 | dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", | 867 | dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", |
| @@ -1030,13 +1043,33 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1030 | return 0; | 1043 | return 0; |
| 1031 | } | 1044 | } |
| 1032 | 1045 | ||
| 1046 | static char *get_color(double percent) | ||
| 1047 | { | ||
| 1048 | char *color = PERF_COLOR_NORMAL; | ||
| 1049 | |||
| 1050 | /* | ||
| 1051 | * We color high-overhead entries in red, mid-overhead | ||
| 1052 | * entries in green - and keep the low overhead places | ||
| 1053 | * normal: | ||
| 1054 | */ | ||
| 1055 | if (percent >= MIN_RED) | ||
| 1056 | color = PERF_COLOR_RED; | ||
| 1057 | else { | ||
| 1058 | if (percent > MIN_GREEN) | ||
| 1059 | color = PERF_COLOR_GREEN; | ||
| 1060 | } | ||
| 1061 | return color; | ||
| 1062 | } | ||
| 1063 | |||
| 1033 | static int | 1064 | static int |
| 1034 | parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | 1065 | parse_line(FILE *file, struct symbol *sym, u64 start, u64 len) |
| 1035 | { | 1066 | { |
| 1036 | char *line = NULL, *tmp, *tmp2; | 1067 | char *line = NULL, *tmp, *tmp2; |
| 1068 | static const char *prev_line; | ||
| 1069 | static const char *prev_color; | ||
| 1037 | unsigned int offset; | 1070 | unsigned int offset; |
| 1038 | size_t line_len; | 1071 | size_t line_len; |
| 1039 | __u64 line_ip; | 1072 | u64 line_ip; |
| 1040 | int ret; | 1073 | int ret; |
| 1041 | char *c; | 1074 | char *c; |
| 1042 | 1075 | ||
| @@ -1073,27 +1106,36 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | |||
| 1073 | } | 1106 | } |
| 1074 | 1107 | ||
| 1075 | if (line_ip != -1) { | 1108 | if (line_ip != -1) { |
| 1109 | const char *path = NULL; | ||
| 1076 | unsigned int hits = 0; | 1110 | unsigned int hits = 0; |
| 1077 | double percent = 0.0; | 1111 | double percent = 0.0; |
| 1078 | char *color = PERF_COLOR_NORMAL; | 1112 | char *color; |
| 1113 | struct sym_ext *sym_ext = sym->priv; | ||
| 1079 | 1114 | ||
| 1080 | offset = line_ip - start; | 1115 | offset = line_ip - start; |
| 1081 | if (offset < len) | 1116 | if (offset < len) |
| 1082 | hits = sym->hist[offset]; | 1117 | hits = sym->hist[offset]; |
| 1083 | 1118 | ||
| 1084 | if (sym->hist_sum) | 1119 | if (offset < len && sym_ext) { |
| 1120 | path = sym_ext[offset].path; | ||
| 1121 | percent = sym_ext[offset].percent; | ||
| 1122 | } else if (sym->hist_sum) | ||
| 1085 | percent = 100.0 * hits / sym->hist_sum; | 1123 | percent = 100.0 * hits / sym->hist_sum; |
| 1086 | 1124 | ||
| 1125 | color = get_color(percent); | ||
| 1126 | |||
| 1087 | /* | 1127 | /* |
| 1088 | * We color high-overhead entries in red, mid-overhead | 1128 | * Also color the filename and line if needed, with |
| 1089 | * entries in green - and keep the low overhead places | 1129 | * the same color than the percentage. Don't print it |
| 1090 | * normal: | 1130 | * twice for close colored ip with the same filename:line |
| 1091 | */ | 1131 | */ |
| 1092 | if (percent >= 5.0) | 1132 | if (path) { |
| 1093 | color = PERF_COLOR_RED; | 1133 | if (!prev_line || strcmp(prev_line, path) |
| 1094 | else { | 1134 | || color != prev_color) { |
| 1095 | if (percent > 0.5) | 1135 | color_fprintf(stdout, color, " %s", path); |
| 1096 | color = PERF_COLOR_GREEN; | 1136 | prev_line = path; |
| 1137 | prev_color = color; | ||
| 1138 | } | ||
| 1097 | } | 1139 | } |
| 1098 | 1140 | ||
| 1099 | color_fprintf(stdout, color, " %7.2f", percent); | 1141 | color_fprintf(stdout, color, " %7.2f", percent); |
| @@ -1109,10 +1151,125 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | |||
| 1109 | return 0; | 1151 | return 0; |
| 1110 | } | 1152 | } |
| 1111 | 1153 | ||
| 1154 | static struct rb_root root_sym_ext; | ||
| 1155 | |||
| 1156 | static void insert_source_line(struct sym_ext *sym_ext) | ||
| 1157 | { | ||
| 1158 | struct sym_ext *iter; | ||
| 1159 | struct rb_node **p = &root_sym_ext.rb_node; | ||
| 1160 | struct rb_node *parent = NULL; | ||
| 1161 | |||
| 1162 | while (*p != NULL) { | ||
| 1163 | parent = *p; | ||
| 1164 | iter = rb_entry(parent, struct sym_ext, node); | ||
| 1165 | |||
| 1166 | if (sym_ext->percent > iter->percent) | ||
| 1167 | p = &(*p)->rb_left; | ||
| 1168 | else | ||
| 1169 | p = &(*p)->rb_right; | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | rb_link_node(&sym_ext->node, parent, p); | ||
| 1173 | rb_insert_color(&sym_ext->node, &root_sym_ext); | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | static void free_source_line(struct symbol *sym, int len) | ||
| 1177 | { | ||
| 1178 | struct sym_ext *sym_ext = sym->priv; | ||
| 1179 | int i; | ||
| 1180 | |||
| 1181 | if (!sym_ext) | ||
| 1182 | return; | ||
| 1183 | |||
| 1184 | for (i = 0; i < len; i++) | ||
| 1185 | free(sym_ext[i].path); | ||
| 1186 | free(sym_ext); | ||
| 1187 | |||
| 1188 | sym->priv = NULL; | ||
| 1189 | root_sym_ext = RB_ROOT; | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | /* Get the filename:line for the colored entries */ | ||
| 1193 | static void | ||
| 1194 | get_source_line(struct symbol *sym, u64 start, int len, char *filename) | ||
| 1195 | { | ||
| 1196 | int i; | ||
| 1197 | char cmd[PATH_MAX * 2]; | ||
| 1198 | struct sym_ext *sym_ext; | ||
| 1199 | |||
| 1200 | if (!sym->hist_sum) | ||
| 1201 | return; | ||
| 1202 | |||
| 1203 | sym->priv = calloc(len, sizeof(struct sym_ext)); | ||
| 1204 | if (!sym->priv) | ||
| 1205 | return; | ||
| 1206 | |||
| 1207 | sym_ext = sym->priv; | ||
| 1208 | |||
| 1209 | for (i = 0; i < len; i++) { | ||
| 1210 | char *path = NULL; | ||
| 1211 | size_t line_len; | ||
| 1212 | u64 offset; | ||
| 1213 | FILE *fp; | ||
| 1214 | |||
| 1215 | sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum; | ||
| 1216 | if (sym_ext[i].percent <= 0.5) | ||
| 1217 | continue; | ||
| 1218 | |||
| 1219 | offset = start + i; | ||
| 1220 | sprintf(cmd, "addr2line -e %s %016llx", filename, offset); | ||
| 1221 | fp = popen(cmd, "r"); | ||
| 1222 | if (!fp) | ||
| 1223 | continue; | ||
| 1224 | |||
| 1225 | if (getline(&path, &line_len, fp) < 0 || !line_len) | ||
| 1226 | goto next; | ||
| 1227 | |||
| 1228 | sym_ext[i].path = malloc(sizeof(char) * line_len + 1); | ||
| 1229 | if (!sym_ext[i].path) | ||
| 1230 | goto next; | ||
| 1231 | |||
| 1232 | strcpy(sym_ext[i].path, path); | ||
| 1233 | insert_source_line(&sym_ext[i]); | ||
| 1234 | |||
| 1235 | next: | ||
| 1236 | pclose(fp); | ||
| 1237 | } | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | static void print_summary(char *filename) | ||
| 1241 | { | ||
| 1242 | struct sym_ext *sym_ext; | ||
| 1243 | struct rb_node *node; | ||
| 1244 | |||
| 1245 | printf("\nSorted summary for file %s\n", filename); | ||
| 1246 | printf("----------------------------------------------\n\n"); | ||
| 1247 | |||
| 1248 | if (RB_EMPTY_ROOT(&root_sym_ext)) { | ||
| 1249 | printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); | ||
| 1250 | return; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | node = rb_first(&root_sym_ext); | ||
| 1254 | while (node) { | ||
| 1255 | double percent; | ||
| 1256 | char *color; | ||
| 1257 | char *path; | ||
| 1258 | |||
| 1259 | sym_ext = rb_entry(node, struct sym_ext, node); | ||
| 1260 | percent = sym_ext->percent; | ||
| 1261 | color = get_color(percent); | ||
| 1262 | path = sym_ext->path; | ||
| 1263 | |||
| 1264 | color_fprintf(stdout, color, " %7.2f %s", percent, path); | ||
| 1265 | node = rb_next(node); | ||
| 1266 | } | ||
| 1267 | } | ||
| 1268 | |||
| 1112 | static void annotate_sym(struct dso *dso, struct symbol *sym) | 1269 | static void annotate_sym(struct dso *dso, struct symbol *sym) |
| 1113 | { | 1270 | { |
| 1114 | char *filename = dso->name; | 1271 | char *filename = dso->name; |
| 1115 | __u64 start, end, len; | 1272 | u64 start, end, len; |
| 1116 | char command[PATH_MAX*2]; | 1273 | char command[PATH_MAX*2]; |
| 1117 | FILE *file; | 1274 | FILE *file; |
| 1118 | 1275 | ||
| @@ -1121,13 +1278,6 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
| 1121 | if (dso == kernel_dso) | 1278 | if (dso == kernel_dso) |
| 1122 | filename = vmlinux; | 1279 | filename = vmlinux; |
| 1123 | 1280 | ||
| 1124 | printf("\n------------------------------------------------\n"); | ||
| 1125 | printf(" Percent | Source code & Disassembly of %s\n", filename); | ||
| 1126 | printf("------------------------------------------------\n"); | ||
| 1127 | |||
| 1128 | if (verbose >= 2) | ||
| 1129 | printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); | ||
| 1130 | |||
| 1131 | start = sym->obj_start; | 1281 | start = sym->obj_start; |
| 1132 | if (!start) | 1282 | if (!start) |
| 1133 | start = sym->start; | 1283 | start = sym->start; |
| @@ -1135,7 +1285,19 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
| 1135 | end = start + sym->end - sym->start + 1; | 1285 | end = start + sym->end - sym->start + 1; |
| 1136 | len = sym->end - sym->start; | 1286 | len = sym->end - sym->start; |
| 1137 | 1287 | ||
| 1138 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (__u64)start, (__u64)end, filename); | 1288 | if (print_line) { |
| 1289 | get_source_line(sym, start, len, filename); | ||
| 1290 | print_summary(filename); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | printf("\n\n------------------------------------------------\n"); | ||
| 1294 | printf(" Percent | Source code & Disassembly of %s\n", filename); | ||
| 1295 | printf("------------------------------------------------\n"); | ||
| 1296 | |||
| 1297 | if (verbose >= 2) | ||
| 1298 | printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); | ||
| 1299 | |||
| 1300 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (u64)start, (u64)end, filename); | ||
| 1139 | 1301 | ||
| 1140 | if (verbose >= 3) | 1302 | if (verbose >= 3) |
| 1141 | printf("doing: %s\n", command); | 1303 | printf("doing: %s\n", command); |
| @@ -1150,6 +1312,8 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
| 1150 | } | 1312 | } |
| 1151 | 1313 | ||
| 1152 | pclose(file); | 1314 | pclose(file); |
| 1315 | if (print_line) | ||
| 1316 | free_source_line(sym, len); | ||
| 1153 | } | 1317 | } |
| 1154 | 1318 | ||
| 1155 | static void find_annotations(void) | 1319 | static void find_annotations(void) |
| @@ -1308,6 +1472,8 @@ static const struct option options[] = { | |||
| 1308 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1472 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
| 1309 | "dump raw trace in ASCII"), | 1473 | "dump raw trace in ASCII"), |
| 1310 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 1474 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
| 1475 | OPT_BOOLEAN('l', "print-line", &print_line, | ||
| 1476 | "print matching source lines (may be slow)"), | ||
| 1311 | OPT_END() | 1477 | OPT_END() |
| 1312 | }; | 1478 | }; |
| 1313 | 1479 | ||
