Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/tools/perf/util/annotate.c
10821 views
1
/*
2
* Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <[email protected]>
3
*
4
* Parts came from builtin-annotate.c, see those files for further
5
* copyright notes.
6
*
7
* Released under the GPL v2. (and only v2, not any later version)
8
*/
9
10
#include "util.h"
11
#include "build-id.h"
12
#include "color.h"
13
#include "cache.h"
14
#include "symbol.h"
15
#include "debug.h"
16
#include "annotate.h"
17
#include <pthread.h>
18
19
int symbol__annotate_init(struct map *map __used, struct symbol *sym)
20
{
21
struct annotation *notes = symbol__annotation(sym);
22
pthread_mutex_init(&notes->lock, NULL);
23
return 0;
24
}
25
26
int symbol__alloc_hist(struct symbol *sym, int nevents)
27
{
28
struct annotation *notes = symbol__annotation(sym);
29
size_t sizeof_sym_hist = (sizeof(struct sym_hist) +
30
(sym->end - sym->start) * sizeof(u64));
31
32
notes->src = zalloc(sizeof(*notes->src) + nevents * sizeof_sym_hist);
33
if (notes->src == NULL)
34
return -1;
35
notes->src->sizeof_sym_hist = sizeof_sym_hist;
36
notes->src->nr_histograms = nevents;
37
INIT_LIST_HEAD(&notes->src->source);
38
return 0;
39
}
40
41
void symbol__annotate_zero_histograms(struct symbol *sym)
42
{
43
struct annotation *notes = symbol__annotation(sym);
44
45
pthread_mutex_lock(&notes->lock);
46
if (notes->src != NULL)
47
memset(notes->src->histograms, 0,
48
notes->src->nr_histograms * notes->src->sizeof_sym_hist);
49
pthread_mutex_unlock(&notes->lock);
50
}
51
52
int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
53
int evidx, u64 addr)
54
{
55
unsigned offset;
56
struct annotation *notes;
57
struct sym_hist *h;
58
59
notes = symbol__annotation(sym);
60
if (notes->src == NULL)
61
return -ENOMEM;
62
63
pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
64
65
if (addr >= sym->end)
66
return 0;
67
68
offset = addr - sym->start;
69
h = annotation__histogram(notes, evidx);
70
h->sum++;
71
h->addr[offset]++;
72
73
pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64
74
", evidx=%d] => %" PRIu64 "\n", sym->start, sym->name,
75
addr, addr - sym->start, evidx, h->addr[offset]);
76
return 0;
77
}
78
79
static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
80
{
81
struct objdump_line *self = malloc(sizeof(*self) + privsize);
82
83
if (self != NULL) {
84
self->offset = offset;
85
self->line = line;
86
}
87
88
return self;
89
}
90
91
void objdump_line__free(struct objdump_line *self)
92
{
93
free(self->line);
94
free(self);
95
}
96
97
static void objdump__add_line(struct list_head *head, struct objdump_line *line)
98
{
99
list_add_tail(&line->node, head);
100
}
101
102
struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
103
struct objdump_line *pos)
104
{
105
list_for_each_entry_continue(pos, head, node)
106
if (pos->offset >= 0)
107
return pos;
108
109
return NULL;
110
}
111
112
static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
113
int evidx, u64 len, int min_pcnt,
114
int printed, int max_lines,
115
struct objdump_line *queue)
116
{
117
static const char *prev_line;
118
static const char *prev_color;
119
120
if (oline->offset != -1) {
121
const char *path = NULL;
122
unsigned int hits = 0;
123
double percent = 0.0;
124
const char *color;
125
struct annotation *notes = symbol__annotation(sym);
126
struct source_line *src_line = notes->src->lines;
127
struct sym_hist *h = annotation__histogram(notes, evidx);
128
s64 offset = oline->offset;
129
struct objdump_line *next;
130
131
next = objdump__get_next_ip_line(&notes->src->source, oline);
132
133
while (offset < (s64)len &&
134
(next == NULL || offset < next->offset)) {
135
if (src_line) {
136
if (path == NULL)
137
path = src_line[offset].path;
138
percent += src_line[offset].percent;
139
} else
140
hits += h->addr[offset];
141
142
++offset;
143
}
144
145
if (src_line == NULL && h->sum)
146
percent = 100.0 * hits / h->sum;
147
148
if (percent < min_pcnt)
149
return -1;
150
151
if (max_lines && printed >= max_lines)
152
return 1;
153
154
if (queue != NULL) {
155
list_for_each_entry_from(queue, &notes->src->source, node) {
156
if (queue == oline)
157
break;
158
objdump_line__print(queue, sym, evidx, len,
159
0, 0, 1, NULL);
160
}
161
}
162
163
color = get_percent_color(percent);
164
165
/*
166
* Also color the filename and line if needed, with
167
* the same color than the percentage. Don't print it
168
* twice for close colored addr with the same filename:line
169
*/
170
if (path) {
171
if (!prev_line || strcmp(prev_line, path)
172
|| color != prev_color) {
173
color_fprintf(stdout, color, " %s", path);
174
prev_line = path;
175
prev_color = color;
176
}
177
}
178
179
color_fprintf(stdout, color, " %7.2f", percent);
180
printf(" : ");
181
color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line);
182
} else if (max_lines && printed >= max_lines)
183
return 1;
184
else {
185
if (queue)
186
return -1;
187
188
if (!*oline->line)
189
printf(" :\n");
190
else
191
printf(" : %s\n", oline->line);
192
}
193
194
return 0;
195
}
196
197
static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
198
FILE *file, size_t privsize)
199
{
200
struct annotation *notes = symbol__annotation(sym);
201
struct objdump_line *objdump_line;
202
char *line = NULL, *tmp, *tmp2, *c;
203
size_t line_len;
204
s64 line_ip, offset = -1;
205
206
if (getline(&line, &line_len, file) < 0)
207
return -1;
208
209
if (!line)
210
return -1;
211
212
while (line_len != 0 && isspace(line[line_len - 1]))
213
line[--line_len] = '\0';
214
215
c = strchr(line, '\n');
216
if (c)
217
*c = 0;
218
219
line_ip = -1;
220
221
/*
222
* Strip leading spaces:
223
*/
224
tmp = line;
225
while (*tmp) {
226
if (*tmp != ' ')
227
break;
228
tmp++;
229
}
230
231
if (*tmp) {
232
/*
233
* Parse hexa addresses followed by ':'
234
*/
235
line_ip = strtoull(tmp, &tmp2, 16);
236
if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0')
237
line_ip = -1;
238
}
239
240
if (line_ip != -1) {
241
u64 start = map__rip_2objdump(map, sym->start),
242
end = map__rip_2objdump(map, sym->end);
243
244
offset = line_ip - start;
245
if (offset < 0 || (u64)line_ip > end)
246
offset = -1;
247
}
248
249
objdump_line = objdump_line__new(offset, line, privsize);
250
if (objdump_line == NULL) {
251
free(line);
252
return -1;
253
}
254
objdump__add_line(&notes->src->source, objdump_line);
255
256
return 0;
257
}
258
259
int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
260
{
261
struct dso *dso = map->dso;
262
char *filename = dso__build_id_filename(dso, NULL, 0);
263
bool free_filename = true;
264
char command[PATH_MAX * 2];
265
FILE *file;
266
int err = 0;
267
char symfs_filename[PATH_MAX];
268
269
if (filename) {
270
snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
271
symbol_conf.symfs, filename);
272
}
273
274
if (filename == NULL) {
275
if (dso->has_build_id) {
276
pr_err("Can't annotate %s: not enough memory\n",
277
sym->name);
278
return -ENOMEM;
279
}
280
goto fallback;
281
} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
282
strstr(command, "[kernel.kallsyms]") ||
283
access(symfs_filename, R_OK)) {
284
free(filename);
285
fallback:
286
/*
287
* If we don't have build-ids or the build-id file isn't in the
288
* cache, or is just a kallsyms file, well, lets hope that this
289
* DSO is the same as when 'perf record' ran.
290
*/
291
filename = dso->long_name;
292
snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
293
symbol_conf.symfs, filename);
294
free_filename = false;
295
}
296
297
if (dso->symtab_type == SYMTAB__KALLSYMS) {
298
char bf[BUILD_ID_SIZE * 2 + 16] = " with build id ";
299
char *build_id_msg = NULL;
300
301
if (dso->annotate_warned)
302
goto out_free_filename;
303
304
if (dso->has_build_id) {
305
build_id__sprintf(dso->build_id,
306
sizeof(dso->build_id), bf + 15);
307
build_id_msg = bf;
308
}
309
err = -ENOENT;
310
dso->annotate_warned = 1;
311
pr_err("Can't annotate %s: No vmlinux file%s was found in the "
312
"path.\nPlease use 'perf buildid-cache -av vmlinux' or "
313
"--vmlinux vmlinux.\n",
314
sym->name, build_id_msg ?: "");
315
goto out_free_filename;
316
}
317
318
pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
319
filename, sym->name, map->unmap_ip(map, sym->start),
320
map->unmap_ip(map, sym->end));
321
322
pr_debug("annotating [%p] %30s : [%p] %30s\n",
323
dso, dso->long_name, sym, sym->name);
324
325
snprintf(command, sizeof(command),
326
"objdump --start-address=0x%016" PRIx64
327
" --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand",
328
map__rip_2objdump(map, sym->start),
329
map__rip_2objdump(map, sym->end),
330
symfs_filename, filename);
331
332
pr_debug("Executing: %s\n", command);
333
334
file = popen(command, "r");
335
if (!file)
336
goto out_free_filename;
337
338
while (!feof(file))
339
if (symbol__parse_objdump_line(sym, map, file, privsize) < 0)
340
break;
341
342
pclose(file);
343
out_free_filename:
344
if (free_filename)
345
free(filename);
346
return err;
347
}
348
349
static void insert_source_line(struct rb_root *root, struct source_line *src_line)
350
{
351
struct source_line *iter;
352
struct rb_node **p = &root->rb_node;
353
struct rb_node *parent = NULL;
354
355
while (*p != NULL) {
356
parent = *p;
357
iter = rb_entry(parent, struct source_line, node);
358
359
if (src_line->percent > iter->percent)
360
p = &(*p)->rb_left;
361
else
362
p = &(*p)->rb_right;
363
}
364
365
rb_link_node(&src_line->node, parent, p);
366
rb_insert_color(&src_line->node, root);
367
}
368
369
static void symbol__free_source_line(struct symbol *sym, int len)
370
{
371
struct annotation *notes = symbol__annotation(sym);
372
struct source_line *src_line = notes->src->lines;
373
int i;
374
375
for (i = 0; i < len; i++)
376
free(src_line[i].path);
377
378
free(src_line);
379
notes->src->lines = NULL;
380
}
381
382
/* Get the filename:line for the colored entries */
383
static int symbol__get_source_line(struct symbol *sym, struct map *map,
384
int evidx, struct rb_root *root, int len,
385
const char *filename)
386
{
387
u64 start;
388
int i;
389
char cmd[PATH_MAX * 2];
390
struct source_line *src_line;
391
struct annotation *notes = symbol__annotation(sym);
392
struct sym_hist *h = annotation__histogram(notes, evidx);
393
394
if (!h->sum)
395
return 0;
396
397
src_line = notes->src->lines = calloc(len, sizeof(struct source_line));
398
if (!notes->src->lines)
399
return -1;
400
401
start = map->unmap_ip(map, sym->start);
402
403
for (i = 0; i < len; i++) {
404
char *path = NULL;
405
size_t line_len;
406
u64 offset;
407
FILE *fp;
408
409
src_line[i].percent = 100.0 * h->addr[i] / h->sum;
410
if (src_line[i].percent <= 0.5)
411
continue;
412
413
offset = start + i;
414
sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset);
415
fp = popen(cmd, "r");
416
if (!fp)
417
continue;
418
419
if (getline(&path, &line_len, fp) < 0 || !line_len)
420
goto next;
421
422
src_line[i].path = malloc(sizeof(char) * line_len + 1);
423
if (!src_line[i].path)
424
goto next;
425
426
strcpy(src_line[i].path, path);
427
insert_source_line(root, &src_line[i]);
428
429
next:
430
pclose(fp);
431
}
432
433
return 0;
434
}
435
436
static void print_summary(struct rb_root *root, const char *filename)
437
{
438
struct source_line *src_line;
439
struct rb_node *node;
440
441
printf("\nSorted summary for file %s\n", filename);
442
printf("----------------------------------------------\n\n");
443
444
if (RB_EMPTY_ROOT(root)) {
445
printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
446
return;
447
}
448
449
node = rb_first(root);
450
while (node) {
451
double percent;
452
const char *color;
453
char *path;
454
455
src_line = rb_entry(node, struct source_line, node);
456
percent = src_line->percent;
457
color = get_percent_color(percent);
458
path = src_line->path;
459
460
color_fprintf(stdout, color, " %7.2f %s", percent, path);
461
node = rb_next(node);
462
}
463
}
464
465
static void symbol__annotate_hits(struct symbol *sym, int evidx)
466
{
467
struct annotation *notes = symbol__annotation(sym);
468
struct sym_hist *h = annotation__histogram(notes, evidx);
469
u64 len = sym->end - sym->start, offset;
470
471
for (offset = 0; offset < len; ++offset)
472
if (h->addr[offset] != 0)
473
printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
474
sym->start + offset, h->addr[offset]);
475
printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum);
476
}
477
478
int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
479
bool full_paths, int min_pcnt, int max_lines,
480
int context)
481
{
482
struct dso *dso = map->dso;
483
const char *filename = dso->long_name, *d_filename;
484
struct annotation *notes = symbol__annotation(sym);
485
struct objdump_line *pos, *queue = NULL;
486
int printed = 2, queue_len = 0;
487
int more = 0;
488
u64 len;
489
490
if (full_paths)
491
d_filename = filename;
492
else
493
d_filename = basename(filename);
494
495
len = sym->end - sym->start;
496
497
printf(" Percent | Source code & Disassembly of %s\n", d_filename);
498
printf("------------------------------------------------\n");
499
500
if (verbose)
501
symbol__annotate_hits(sym, evidx);
502
503
list_for_each_entry(pos, &notes->src->source, node) {
504
if (context && queue == NULL) {
505
queue = pos;
506
queue_len = 0;
507
}
508
509
switch (objdump_line__print(pos, sym, evidx, len, min_pcnt,
510
printed, max_lines, queue)) {
511
case 0:
512
++printed;
513
if (context) {
514
printed += queue_len;
515
queue = NULL;
516
queue_len = 0;
517
}
518
break;
519
case 1:
520
/* filtered by max_lines */
521
++more;
522
break;
523
case -1:
524
default:
525
/*
526
* Filtered by min_pcnt or non IP lines when
527
* context != 0
528
*/
529
if (!context)
530
break;
531
if (queue_len == context)
532
queue = list_entry(queue->node.next, typeof(*queue), node);
533
else
534
++queue_len;
535
break;
536
}
537
}
538
539
return more;
540
}
541
542
void symbol__annotate_zero_histogram(struct symbol *sym, int evidx)
543
{
544
struct annotation *notes = symbol__annotation(sym);
545
struct sym_hist *h = annotation__histogram(notes, evidx);
546
547
memset(h, 0, notes->src->sizeof_sym_hist);
548
}
549
550
void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
551
{
552
struct annotation *notes = symbol__annotation(sym);
553
struct sym_hist *h = annotation__histogram(notes, evidx);
554
struct objdump_line *pos;
555
int len = sym->end - sym->start;
556
557
h->sum = 0;
558
559
list_for_each_entry(pos, &notes->src->source, node) {
560
if (pos->offset != -1 && pos->offset < len) {
561
h->addr[pos->offset] = h->addr[pos->offset] * 7 / 8;
562
h->sum += h->addr[pos->offset];
563
}
564
}
565
}
566
567
void objdump_line_list__purge(struct list_head *head)
568
{
569
struct objdump_line *pos, *n;
570
571
list_for_each_entry_safe(pos, n, head, node) {
572
list_del(&pos->node);
573
objdump_line__free(pos);
574
}
575
}
576
577
int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
578
bool print_lines, bool full_paths, int min_pcnt,
579
int max_lines)
580
{
581
struct dso *dso = map->dso;
582
const char *filename = dso->long_name;
583
struct rb_root source_line = RB_ROOT;
584
u64 len;
585
586
if (symbol__annotate(sym, map, 0) < 0)
587
return -1;
588
589
len = sym->end - sym->start;
590
591
if (print_lines) {
592
symbol__get_source_line(sym, map, evidx, &source_line,
593
len, filename);
594
print_summary(&source_line, filename);
595
}
596
597
symbol__annotate_printf(sym, map, evidx, full_paths,
598
min_pcnt, max_lines, 0);
599
if (print_lines)
600
symbol__free_source_line(sym, len);
601
602
objdump_line_list__purge(&symbol__annotation(sym)->src->source);
603
604
return 0;
605
}
606
607