|
@@ -39,6 +39,8 @@ static int dump_trace = 0;
|
|
|
|
|
|
static int verbose;
|
|
static int verbose;
|
|
|
|
|
|
|
|
+static int print_line;
|
|
|
|
+
|
|
static unsigned long page_size;
|
|
static unsigned long page_size;
|
|
static unsigned long mmap_window = 32;
|
|
static unsigned long mmap_window = 32;
|
|
|
|
|
|
@@ -84,6 +86,12 @@ typedef union event_union {
|
|
struct period_event period;
|
|
struct period_event period;
|
|
} event_t;
|
|
} event_t;
|
|
|
|
|
|
|
|
+
|
|
|
|
+struct sym_ext {
|
|
|
|
+ double percent;
|
|
|
|
+ char *path;
|
|
|
|
+};
|
|
|
|
+
|
|
static LIST_HEAD(dsos);
|
|
static LIST_HEAD(dsos);
|
|
static struct dso *kernel_dso;
|
|
static struct dso *kernel_dso;
|
|
static struct dso *vdso;
|
|
static struct dso *vdso;
|
|
@@ -1034,6 +1042,8 @@ static int
|
|
parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
|
|
parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
|
|
{
|
|
{
|
|
char *line = NULL, *tmp, *tmp2;
|
|
char *line = NULL, *tmp, *tmp2;
|
|
|
|
+ static const char *prev_line;
|
|
|
|
+ static const char *prev_color;
|
|
unsigned int offset;
|
|
unsigned int offset;
|
|
size_t line_len;
|
|
size_t line_len;
|
|
__u64 line_ip;
|
|
__u64 line_ip;
|
|
@@ -1073,15 +1083,20 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
|
|
}
|
|
}
|
|
|
|
|
|
if (line_ip != -1) {
|
|
if (line_ip != -1) {
|
|
|
|
+ const char *path = NULL;
|
|
unsigned int hits = 0;
|
|
unsigned int hits = 0;
|
|
double percent = 0.0;
|
|
double percent = 0.0;
|
|
char *color = PERF_COLOR_NORMAL;
|
|
char *color = PERF_COLOR_NORMAL;
|
|
|
|
+ struct sym_ext *sym_ext = sym->priv;
|
|
|
|
|
|
offset = line_ip - start;
|
|
offset = line_ip - start;
|
|
if (offset < len)
|
|
if (offset < len)
|
|
hits = sym->hist[offset];
|
|
hits = sym->hist[offset];
|
|
|
|
|
|
- if (sym->hist_sum)
|
|
|
|
|
|
+ if (sym_ext) {
|
|
|
|
+ path = sym_ext[offset].path;
|
|
|
|
+ percent = sym_ext[offset].percent;
|
|
|
|
+ } else if (sym->hist_sum)
|
|
percent = 100.0 * hits / sym->hist_sum;
|
|
percent = 100.0 * hits / sym->hist_sum;
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1096,6 +1111,20 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
|
|
color = PERF_COLOR_GREEN;
|
|
color = PERF_COLOR_GREEN;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Also color the filename and line if needed, with
|
|
|
|
+ * the same color than the percentage. Don't print it
|
|
|
|
+ * twice for close colored ip with the same filename:line
|
|
|
|
+ */
|
|
|
|
+ if (path) {
|
|
|
|
+ if (!prev_line || strcmp(prev_line, path)
|
|
|
|
+ || color != prev_color) {
|
|
|
|
+ color_fprintf(stdout, color, " %s", path);
|
|
|
|
+ prev_line = path;
|
|
|
|
+ prev_color = color;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
color_fprintf(stdout, color, " %7.2f", percent);
|
|
color_fprintf(stdout, color, " %7.2f", percent);
|
|
printf(" : ");
|
|
printf(" : ");
|
|
color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
|
|
color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
|
|
@@ -1109,6 +1138,67 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void free_source_line(struct symbol *sym, int len)
|
|
|
|
+{
|
|
|
|
+ struct sym_ext *sym_ext = sym->priv;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if (!sym_ext)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < len; i++)
|
|
|
|
+ free(sym_ext[i].path);
|
|
|
|
+ free(sym_ext);
|
|
|
|
+
|
|
|
|
+ sym->priv = NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Get the filename:line for the colored entries */
|
|
|
|
+static void get_source_line(struct symbol *sym, __u64 start, int len)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+ char cmd[PATH_MAX * 2];
|
|
|
|
+ struct sym_ext *sym_ext;
|
|
|
|
+
|
|
|
|
+ if (!sym->hist_sum)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ sym->priv = calloc(len, sizeof(struct sym_ext));
|
|
|
|
+ if (!sym->priv)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ sym_ext = sym->priv;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < len; i++) {
|
|
|
|
+ char *path = NULL;
|
|
|
|
+ size_t line_len;
|
|
|
|
+ __u64 offset;
|
|
|
|
+ FILE *fp;
|
|
|
|
+
|
|
|
|
+ sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
|
|
|
|
+ if (sym_ext[i].percent <= 0.5)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ offset = start + i;
|
|
|
|
+ sprintf(cmd, "addr2line -e %s %016llx", vmlinux, offset);
|
|
|
|
+ fp = popen(cmd, "r");
|
|
|
|
+ if (!fp)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (getline(&path, &line_len, fp) < 0 || !line_len)
|
|
|
|
+ goto next;
|
|
|
|
+
|
|
|
|
+ sym_ext[i].path = malloc(sizeof(char) * line_len);
|
|
|
|
+ if (!sym_ext[i].path)
|
|
|
|
+ goto next;
|
|
|
|
+
|
|
|
|
+ strcpy(sym_ext[i].path, path);
|
|
|
|
+
|
|
|
|
+ next:
|
|
|
|
+ pclose(fp);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static void annotate_sym(struct dso *dso, struct symbol *sym)
|
|
static void annotate_sym(struct dso *dso, struct symbol *sym)
|
|
{
|
|
{
|
|
char *filename = dso->name;
|
|
char *filename = dso->name;
|
|
@@ -1135,6 +1225,9 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
|
|
end = start + sym->end - sym->start + 1;
|
|
end = start + sym->end - sym->start + 1;
|
|
len = sym->end - sym->start;
|
|
len = sym->end - sym->start;
|
|
|
|
|
|
|
|
+ if (print_line)
|
|
|
|
+ get_source_line(sym, start, len);
|
|
|
|
+
|
|
sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (__u64)start, (__u64)end, filename);
|
|
sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (__u64)start, (__u64)end, filename);
|
|
|
|
|
|
if (verbose >= 3)
|
|
if (verbose >= 3)
|
|
@@ -1150,6 +1243,7 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
|
|
}
|
|
}
|
|
|
|
|
|
pclose(file);
|
|
pclose(file);
|
|
|
|
+ free_source_line(sym, len);
|
|
}
|
|
}
|
|
|
|
|
|
static void find_annotations(void)
|
|
static void find_annotations(void)
|
|
@@ -1308,6 +1402,8 @@ static const struct option options[] = {
|
|
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
|
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
|
"dump raw trace in ASCII"),
|
|
"dump raw trace in ASCII"),
|
|
OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
|
|
OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
|
|
|
|
+ OPT_BOOLEAN('l', "print-line", &print_line,
|
|
|
|
+ "print matching source lines (may be slow)"),
|
|
OPT_END()
|
|
OPT_END()
|
|
};
|
|
};
|
|
|
|
|