|
@@ -14,6 +14,7 @@
|
|
|
#include "symbol.h"
|
|
|
#include "debug.h"
|
|
|
#include "annotate.h"
|
|
|
+#include "evsel.h"
|
|
|
#include <pthread.h>
|
|
|
#include <linux/bitops.h>
|
|
|
|
|
@@ -602,8 +603,42 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset,
|
|
|
+ s64 end, const char **path)
|
|
|
+{
|
|
|
+ struct source_line *src_line = notes->src->lines;
|
|
|
+ double percent = 0.0;
|
|
|
+
|
|
|
+ if (src_line) {
|
|
|
+ size_t sizeof_src_line = sizeof(*src_line) +
|
|
|
+ sizeof(src_line->p) * (src_line->nr_pcnt - 1);
|
|
|
+
|
|
|
+ while (offset < end) {
|
|
|
+ src_line = (void *)notes->src->lines +
|
|
|
+ (sizeof_src_line * offset);
|
|
|
+
|
|
|
+ if (*path == NULL)
|
|
|
+ *path = src_line->path;
|
|
|
+
|
|
|
+ percent += src_line->p[evidx].percent;
|
|
|
+ offset++;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ struct sym_hist *h = annotation__histogram(notes, evidx);
|
|
|
+ unsigned int hits = 0;
|
|
|
+
|
|
|
+ while (offset < end)
|
|
|
+ hits += h->addr[offset++];
|
|
|
+
|
|
|
+ if (h->sum)
|
|
|
+ percent = 100.0 * hits / h->sum;
|
|
|
+ }
|
|
|
+
|
|
|
+ return percent;
|
|
|
+}
|
|
|
+
|
|
|
static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start,
|
|
|
- int evidx, u64 len, int min_pcnt, int printed,
|
|
|
+ struct perf_evsel *evsel, u64 len, int min_pcnt, int printed,
|
|
|
int max_lines, struct disasm_line *queue)
|
|
|
{
|
|
|
static const char *prev_line;
|
|
@@ -611,34 +646,37 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
|
|
|
|
|
|
if (dl->offset != -1) {
|
|
|
const char *path = NULL;
|
|
|
- unsigned int hits = 0;
|
|
|
- double percent = 0.0;
|
|
|
+ double percent, max_percent = 0.0;
|
|
|
+ double *ppercents = &percent;
|
|
|
+ int i, nr_percent = 1;
|
|
|
const char *color;
|
|
|
struct annotation *notes = symbol__annotation(sym);
|
|
|
- struct source_line *src_line = notes->src->lines;
|
|
|
- struct sym_hist *h = annotation__histogram(notes, evidx);
|
|
|
s64 offset = dl->offset;
|
|
|
const u64 addr = start + offset;
|
|
|
struct disasm_line *next;
|
|
|
|
|
|
next = disasm__get_next_ip_line(¬es->src->source, dl);
|
|
|
|
|
|
- while (offset < (s64)len &&
|
|
|
- (next == NULL || offset < next->offset)) {
|
|
|
- if (src_line) {
|
|
|
- if (path == NULL)
|
|
|
- path = src_line[offset].path;
|
|
|
- percent += src_line[offset].percent;
|
|
|
- } else
|
|
|
- hits += h->addr[offset];
|
|
|
-
|
|
|
- ++offset;
|
|
|
+ if (perf_evsel__is_group_event(evsel)) {
|
|
|
+ nr_percent = evsel->nr_members;
|
|
|
+ ppercents = calloc(nr_percent, sizeof(double));
|
|
|
+ if (ppercents == NULL)
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
- if (src_line == NULL && h->sum)
|
|
|
- percent = 100.0 * hits / h->sum;
|
|
|
+ for (i = 0; i < nr_percent; i++) {
|
|
|
+ percent = disasm__calc_percent(notes,
|
|
|
+ notes->src->lines ? i : evsel->idx + i,
|
|
|
+ offset,
|
|
|
+ next ? next->offset : (s64) len,
|
|
|
+ &path);
|
|
|
+
|
|
|
+ ppercents[i] = percent;
|
|
|
+ if (percent > max_percent)
|
|
|
+ max_percent = percent;
|
|
|
+ }
|
|
|
|
|
|
- if (percent < min_pcnt)
|
|
|
+ if (max_percent < min_pcnt)
|
|
|
return -1;
|
|
|
|
|
|
if (max_lines && printed >= max_lines)
|
|
@@ -648,12 +686,12 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
|
|
|
list_for_each_entry_from(queue, ¬es->src->source, node) {
|
|
|
if (queue == dl)
|
|
|
break;
|
|
|
- disasm_line__print(queue, sym, start, evidx, len,
|
|
|
+ disasm_line__print(queue, sym, start, evsel, len,
|
|
|
0, 0, 1, NULL);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- color = get_percent_color(percent);
|
|
|
+ color = get_percent_color(max_percent);
|
|
|
|
|
|
/*
|
|
|
* Also color the filename and line if needed, with
|
|
@@ -669,25 +707,59 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- color_fprintf(stdout, color, " %7.2f", percent);
|
|
|
+ for (i = 0; i < nr_percent; i++) {
|
|
|
+ percent = ppercents[i];
|
|
|
+ color = get_percent_color(percent);
|
|
|
+ color_fprintf(stdout, color, " %7.2f", percent);
|
|
|
+ }
|
|
|
+
|
|
|
printf(" : ");
|
|
|
color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr);
|
|
|
color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line);
|
|
|
+
|
|
|
+ if (ppercents != &percent)
|
|
|
+ free(ppercents);
|
|
|
+
|
|
|
} else if (max_lines && printed >= max_lines)
|
|
|
return 1;
|
|
|
else {
|
|
|
+ int width = 8;
|
|
|
+
|
|
|
if (queue)
|
|
|
return -1;
|
|
|
|
|
|
+ if (perf_evsel__is_group_event(evsel))
|
|
|
+ width *= evsel->nr_members;
|
|
|
+
|
|
|
if (!*dl->line)
|
|
|
- printf(" :\n");
|
|
|
+ printf(" %*s:\n", width, " ");
|
|
|
else
|
|
|
- printf(" : %s\n", dl->line);
|
|
|
+ printf(" %*s: %s\n", width, " ", dl->line);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw)
|
|
|
+ * which looks like following
|
|
|
+ *
|
|
|
+ * 0000000000415500 <_init>:
|
|
|
+ * 415500: sub $0x8,%rsp
|
|
|
+ * 415504: mov 0x2f5ad5(%rip),%rax # 70afe0 <_DYNAMIC+0x2f8>
|
|
|
+ * 41550b: test %rax,%rax
|
|
|
+ * 41550e: je 415515 <_init+0x15>
|
|
|
+ * 415510: callq 416e70 <__gmon_start__@plt>
|
|
|
+ * 415515: add $0x8,%rsp
|
|
|
+ * 415519: retq
|
|
|
+ *
|
|
|
+ * it will be parsed and saved into struct disasm_line as
|
|
|
+ * <offset> <name> <ops.raw>
|
|
|
+ *
|
|
|
+ * The offset will be a relative offset from the start of the symbol and -1
|
|
|
+ * means that it's not a disassembly line so should be treated differently.
|
|
|
+ * The ops.raw part will be parsed further according to type of the instruction.
|
|
|
+ */
|
|
|
static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
|
|
|
FILE *file, size_t privsize)
|
|
|
{
|
|
@@ -858,7 +930,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
|
|
|
struct source_line *iter;
|
|
|
struct rb_node **p = &root->rb_node;
|
|
|
struct rb_node *parent = NULL;
|
|
|
- int ret;
|
|
|
+ int i, ret;
|
|
|
|
|
|
while (*p != NULL) {
|
|
|
parent = *p;
|
|
@@ -866,7 +938,8 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
|
|
|
|
|
|
ret = strcmp(iter->path, src_line->path);
|
|
|
if (ret == 0) {
|
|
|
- iter->percent_sum += src_line->percent;
|
|
|
+ for (i = 0; i < src_line->nr_pcnt; i++)
|
|
|
+ iter->p[i].percent_sum += src_line->p[i].percent;
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -876,12 +949,26 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
|
|
|
p = &(*p)->rb_right;
|
|
|
}
|
|
|
|
|
|
- src_line->percent_sum = src_line->percent;
|
|
|
+ for (i = 0; i < src_line->nr_pcnt; i++)
|
|
|
+ src_line->p[i].percent_sum = src_line->p[i].percent;
|
|
|
|
|
|
rb_link_node(&src_line->node, parent, p);
|
|
|
rb_insert_color(&src_line->node, root);
|
|
|
}
|
|
|
|
|
|
+static int cmp_source_line(struct source_line *a, struct source_line *b)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < a->nr_pcnt; i++) {
|
|
|
+ if (a->p[i].percent_sum == b->p[i].percent_sum)
|
|
|
+ continue;
|
|
|
+ return a->p[i].percent_sum > b->p[i].percent_sum;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static void __resort_source_line(struct rb_root *root, struct source_line *src_line)
|
|
|
{
|
|
|
struct source_line *iter;
|
|
@@ -892,7 +979,7 @@ static void __resort_source_line(struct rb_root *root, struct source_line *src_l
|
|
|
parent = *p;
|
|
|
iter = rb_entry(parent, struct source_line, node);
|
|
|
|
|
|
- if (src_line->percent_sum > iter->percent_sum)
|
|
|
+ if (cmp_source_line(src_line, iter))
|
|
|
p = &(*p)->rb_left;
|
|
|
else
|
|
|
p = &(*p)->rb_right;
|
|
@@ -924,32 +1011,52 @@ static void symbol__free_source_line(struct symbol *sym, int len)
|
|
|
{
|
|
|
struct annotation *notes = symbol__annotation(sym);
|
|
|
struct source_line *src_line = notes->src->lines;
|
|
|
+ size_t sizeof_src_line;
|
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i < len; i++)
|
|
|
- free(src_line[i].path);
|
|
|
+ sizeof_src_line = sizeof(*src_line) +
|
|
|
+ (sizeof(src_line->p) * (src_line->nr_pcnt - 1));
|
|
|
+
|
|
|
+ for (i = 0; i < len; i++) {
|
|
|
+ free(src_line->path);
|
|
|
+ src_line = (void *)src_line + sizeof_src_line;
|
|
|
+ }
|
|
|
|
|
|
- free(src_line);
|
|
|
+ free(notes->src->lines);
|
|
|
notes->src->lines = NULL;
|
|
|
}
|
|
|
|
|
|
/* Get the filename:line for the colored entries */
|
|
|
static int symbol__get_source_line(struct symbol *sym, struct map *map,
|
|
|
- int evidx, struct rb_root *root, int len,
|
|
|
+ struct perf_evsel *evsel,
|
|
|
+ struct rb_root *root, int len,
|
|
|
const char *filename)
|
|
|
{
|
|
|
u64 start;
|
|
|
- int i;
|
|
|
+ int i, k;
|
|
|
+ int evidx = evsel->idx;
|
|
|
char cmd[PATH_MAX * 2];
|
|
|
struct source_line *src_line;
|
|
|
struct annotation *notes = symbol__annotation(sym);
|
|
|
struct sym_hist *h = annotation__histogram(notes, evidx);
|
|
|
struct rb_root tmp_root = RB_ROOT;
|
|
|
+ int nr_pcnt = 1;
|
|
|
+ u64 h_sum = h->sum;
|
|
|
+ size_t sizeof_src_line = sizeof(struct source_line);
|
|
|
+
|
|
|
+ if (perf_evsel__is_group_event(evsel)) {
|
|
|
+ for (i = 1; i < evsel->nr_members; i++) {
|
|
|
+ h = annotation__histogram(notes, evidx + i);
|
|
|
+ h_sum += h->sum;
|
|
|
+ }
|
|
|
+ nr_pcnt = evsel->nr_members;
|
|
|
+ sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->p);
|
|
|
+ }
|
|
|
|
|
|
- if (!h->sum)
|
|
|
+ if (!h_sum)
|
|
|
return 0;
|
|
|
|
|
|
- src_line = notes->src->lines = calloc(len, sizeof(struct source_line));
|
|
|
+ src_line = notes->src->lines = calloc(len, sizeof_src_line);
|
|
|
if (!notes->src->lines)
|
|
|
return -1;
|
|
|
|
|
@@ -960,29 +1067,41 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
|
|
|
size_t line_len;
|
|
|
u64 offset;
|
|
|
FILE *fp;
|
|
|
+ double percent_max = 0.0;
|
|
|
|
|
|
- src_line[i].percent = 100.0 * h->addr[i] / h->sum;
|
|
|
- if (src_line[i].percent <= 0.5)
|
|
|
- continue;
|
|
|
+ src_line->nr_pcnt = nr_pcnt;
|
|
|
+
|
|
|
+ for (k = 0; k < nr_pcnt; k++) {
|
|
|
+ h = annotation__histogram(notes, evidx + k);
|
|
|
+ src_line->p[k].percent = 100.0 * h->addr[i] / h->sum;
|
|
|
+
|
|
|
+ if (src_line->p[k].percent > percent_max)
|
|
|
+ percent_max = src_line->p[k].percent;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (percent_max <= 0.5)
|
|
|
+ goto next;
|
|
|
|
|
|
offset = start + i;
|
|
|
sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset);
|
|
|
fp = popen(cmd, "r");
|
|
|
if (!fp)
|
|
|
- continue;
|
|
|
+ goto next;
|
|
|
|
|
|
if (getline(&path, &line_len, fp) < 0 || !line_len)
|
|
|
- goto next;
|
|
|
+ goto next_close;
|
|
|
|
|
|
- src_line[i].path = malloc(sizeof(char) * line_len + 1);
|
|
|
- if (!src_line[i].path)
|
|
|
- goto next;
|
|
|
+ src_line->path = malloc(sizeof(char) * line_len + 1);
|
|
|
+ if (!src_line->path)
|
|
|
+ goto next_close;
|
|
|
|
|
|
- strcpy(src_line[i].path, path);
|
|
|
- insert_source_line(&tmp_root, &src_line[i]);
|
|
|
+ strcpy(src_line->path, path);
|
|
|
+ insert_source_line(&tmp_root, src_line);
|
|
|
|
|
|
- next:
|
|
|
+ next_close:
|
|
|
pclose(fp);
|
|
|
+ next:
|
|
|
+ src_line = (void *)src_line + sizeof_src_line;
|
|
|
}
|
|
|
|
|
|
resort_source_line(root, &tmp_root);
|
|
@@ -1004,24 +1123,33 @@ static void print_summary(struct rb_root *root, const char *filename)
|
|
|
|
|
|
node = rb_first(root);
|
|
|
while (node) {
|
|
|
- double percent;
|
|
|
+ double percent, percent_max = 0.0;
|
|
|
const char *color;
|
|
|
char *path;
|
|
|
+ int i;
|
|
|
|
|
|
src_line = rb_entry(node, struct source_line, node);
|
|
|
- percent = src_line->percent_sum;
|
|
|
- color = get_percent_color(percent);
|
|
|
+ for (i = 0; i < src_line->nr_pcnt; i++) {
|
|
|
+ percent = src_line->p[i].percent_sum;
|
|
|
+ color = get_percent_color(percent);
|
|
|
+ color_fprintf(stdout, color, " %7.2f", percent);
|
|
|
+
|
|
|
+ if (percent > percent_max)
|
|
|
+ percent_max = percent;
|
|
|
+ }
|
|
|
+
|
|
|
path = src_line->path;
|
|
|
+ color = get_percent_color(percent_max);
|
|
|
+ color_fprintf(stdout, color, " %s", path);
|
|
|
|
|
|
- color_fprintf(stdout, color, " %7.2f %s", percent, path);
|
|
|
node = rb_next(node);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void symbol__annotate_hits(struct symbol *sym, int evidx)
|
|
|
+static void symbol__annotate_hits(struct symbol *sym, struct perf_evsel *evsel)
|
|
|
{
|
|
|
struct annotation *notes = symbol__annotation(sym);
|
|
|
- struct sym_hist *h = annotation__histogram(notes, evidx);
|
|
|
+ struct sym_hist *h = annotation__histogram(notes, evsel->idx);
|
|
|
u64 len = symbol__size(sym), offset;
|
|
|
|
|
|
for (offset = 0; offset < len; ++offset)
|
|
@@ -1031,9 +1159,9 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx)
|
|
|
printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum);
|
|
|
}
|
|
|
|
|
|
-int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
|
|
|
- bool full_paths, int min_pcnt, int max_lines,
|
|
|
- int context)
|
|
|
+int symbol__annotate_printf(struct symbol *sym, struct map *map,
|
|
|
+ struct perf_evsel *evsel, bool full_paths,
|
|
|
+ int min_pcnt, int max_lines, int context)
|
|
|
{
|
|
|
struct dso *dso = map->dso;
|
|
|
char *filename;
|
|
@@ -1044,6 +1172,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
|
|
|
int printed = 2, queue_len = 0;
|
|
|
int more = 0;
|
|
|
u64 len;
|
|
|
+ int width = 8;
|
|
|
+ int namelen;
|
|
|
|
|
|
filename = strdup(dso->long_name);
|
|
|
if (!filename)
|
|
@@ -1055,12 +1185,18 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
|
|
|
d_filename = basename(filename);
|
|
|
|
|
|
len = symbol__size(sym);
|
|
|
+ namelen = strlen(d_filename);
|
|
|
+
|
|
|
+ if (perf_evsel__is_group_event(evsel))
|
|
|
+ width *= evsel->nr_members;
|
|
|
|
|
|
- printf(" Percent | Source code & Disassembly of %s\n", d_filename);
|
|
|
- printf("------------------------------------------------\n");
|
|
|
+ printf(" %-*.*s| Source code & Disassembly of %s\n",
|
|
|
+ width, width, "Percent", d_filename);
|
|
|
+ printf("-%-*.*s-------------------------------------\n",
|
|
|
+ width+namelen, width+namelen, graph_dotted_line);
|
|
|
|
|
|
if (verbose)
|
|
|
- symbol__annotate_hits(sym, evidx);
|
|
|
+ symbol__annotate_hits(sym, evsel);
|
|
|
|
|
|
list_for_each_entry(pos, ¬es->src->source, node) {
|
|
|
if (context && queue == NULL) {
|
|
@@ -1068,7 +1204,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
|
|
|
queue_len = 0;
|
|
|
}
|
|
|
|
|
|
- switch (disasm_line__print(pos, sym, start, evidx, len,
|
|
|
+ switch (disasm_line__print(pos, sym, start, evsel, len,
|
|
|
min_pcnt, printed, max_lines,
|
|
|
queue)) {
|
|
|
case 0:
|
|
@@ -1163,9 +1299,9 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp)
|
|
|
return printed;
|
|
|
}
|
|
|
|
|
|
-int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
|
|
|
- bool print_lines, bool full_paths, int min_pcnt,
|
|
|
- int max_lines)
|
|
|
+int symbol__tty_annotate(struct symbol *sym, struct map *map,
|
|
|
+ struct perf_evsel *evsel, bool print_lines,
|
|
|
+ bool full_paths, int min_pcnt, int max_lines)
|
|
|
{
|
|
|
struct dso *dso = map->dso;
|
|
|
const char *filename = dso->long_name;
|
|
@@ -1178,12 +1314,12 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
|
|
|
len = symbol__size(sym);
|
|
|
|
|
|
if (print_lines) {
|
|
|
- symbol__get_source_line(sym, map, evidx, &source_line,
|
|
|
+ symbol__get_source_line(sym, map, evsel, &source_line,
|
|
|
len, filename);
|
|
|
print_summary(&source_line, filename);
|
|
|
}
|
|
|
|
|
|
- symbol__annotate_printf(sym, map, evidx, full_paths,
|
|
|
+ symbol__annotate_printf(sym, map, evsel, full_paths,
|
|
|
min_pcnt, max_lines, 0);
|
|
|
if (print_lines)
|
|
|
symbol__free_source_line(sym, len);
|