[PATCH v3 7/7] perf annotate: Support the new source code view for TUI

From: Taeung Song
Date: Thu Mar 09 2017 - 12:43:41 EST


To make source code view more readable,
add the new source code view for TUI
instead of confusing mixed source code & dissambly view.

This view can show source code per symbol(function).
and it can be toggled by a 's' key.
And support 'k' and 't' key for line numbers and total period.

For example, if target symbol is 'hex2u64' of util/util.c

Before:

| Disassembly of section .text:
|
| 000000000053ef9e <hex2u64>:
| hex2u64():
| /*
| * While we find nice hex chars, build a long_val.
| * Return number of chars processed.
| */
| int hex2u64(const char *ptr, u64 *long_val)
| {
| push %rbp
1.79 | mov %rsp,%rbp
| sub $0x30,%rsp

After:

| * While we find nice hex chars, build a long_val.
| * Return number of chars processed.
| */
| int hex2u64(const char *ptr, u64 *long_val)
1.79 | {
| const char *p = ptr;
| *long_val = 0;
|
28.57 | while (*p) {
30.36 | const int hex_val = hex(*p);
|
12.50 | if (hex_val < 0)
| break;
|
23.21 | *long_val = (*long_val << 4) | hex_val;
| p++;
| }
|
1.79 | return p - ptr;

Cc: Namhyung Kim <namhyung@xxxxxxxxxx>
Cc: Jiri Olsa <jolsa@xxxxxxxxxx>
Signed-off-by: Taeung Song <treeze.taeung@xxxxxxxxx>
---
tools/perf/Documentation/perfconfig.example | 1 -
tools/perf/ui/browsers/annotate.c | 233 ++++++++++++++++++++--------
tools/perf/util/annotate.c | 10 +-
tools/perf/util/annotate.h | 7 +
4 files changed, 180 insertions(+), 71 deletions(-)

diff --git a/tools/perf/Documentation/perfconfig.example b/tools/perf/Documentation/perfconfig.example
index 2b477c1..c9fc41b 100644
--- a/tools/perf/Documentation/perfconfig.example
+++ b/tools/perf/Documentation/perfconfig.example
@@ -23,7 +23,6 @@
[annotate]

# Defaults
- hide_src_code = false
use_offset = true
jump_arrows = true
show_nr_jumps = false
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 03b2012..ac01d75 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -27,8 +27,7 @@ struct browser_disasm_line {
};

static struct annotate_browser_opt {
- bool hide_src_code,
- use_offset,
+ bool use_offset,
jump_arrows,
show_linenr,
show_nr_jumps,
@@ -39,7 +38,7 @@ static struct annotate_browser_opt {
};

struct annotate_browser {
- struct ui_browser b;
+ struct ui_browser b, cb;
struct rb_root entries;
struct rb_node *curr_hot;
struct disasm_line *selection;
@@ -52,6 +51,7 @@ struct annotate_browser {
int nr_jumps;
bool searching_backwards;
bool have_cycles;
+ bool has_src_code;
u8 addr_width;
u8 jumps_width;
u8 target_width;
@@ -68,12 +68,9 @@ static inline struct browser_disasm_line *disasm_line__browser(struct disasm_lin
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
void *entry)
{
- if (annotate_browser__opts.hide_src_code) {
- struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
- return dl->offset == -1;
- }
+ struct disasm_line *dl = list_entry(entry, struct disasm_line, node);

- return false;
+ return dl->offset == -1;
}

static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
@@ -104,15 +101,61 @@ static int annotate_browser__pcnt_width(struct annotate_browser *ab)
return w;
}

+static void annotate_code_browser__write(struct ui_browser *browser, void *entry, int row)
+{
+ struct annotate_browser *ab = container_of(browser, struct annotate_browser, cb);
+ struct code_line *cl = list_entry(entry, struct code_line, node);
+ bool current_entry = ui_browser__is_current_entry(browser, row);
+ int i, printed;
+ double percent, max_percent = 0.0;
+ char line[256];
+
+ for (i = 0; i < ab->nr_events; i++) {
+ if (cl->samples_sum[i].percent > max_percent)
+ max_percent = cl->samples_sum[i].percent;
+ }
+
+ for (i = 0; i < ab->nr_events; i++) {
+ if (max_percent == 0.0) {
+ ui_browser__set_percent_color(browser, 0, current_entry);
+ ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
+ break;
+ }
+
+ percent = cl->samples_sum[i].percent;
+ ui_browser__set_percent_color(browser, percent, current_entry);
+
+ if (annotate_browser__opts.show_total_period)
+ ui_browser__printf(browser, "%6" PRIu64 " ",
+ cl->samples_sum[i].nr);
+ else
+ ui_browser__printf(browser, "%6.2f ", percent);
+
+ if (max_percent < percent)
+ max_percent = percent;
+ }
+
+ SLsmg_write_char(' ');
+
+ if (annotate_browser__opts.show_linenr)
+ printed = scnprintf(line, sizeof(line), "%-*d ",
+ ab->addr_width + 2, cl->line_nr);
+ else
+ printed = scnprintf(line, sizeof(line), "%*s ",
+ ab->addr_width, " ");
+
+ ui_browser__write_nstring(browser, line, printed);
+ ui_browser__write_nstring(browser, cl->line, browser->width);
+}
+
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
{
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
struct browser_disasm_line *bdl = disasm_line__browser(dl);
bool current_entry = ui_browser__is_current_entry(browser, row);
- bool change_color = (!annotate_browser__opts.hide_src_code &&
- (!current_entry || (browser->use_navkeypressed &&
- !browser->navkeypressed)));
+ bool change_color = !current_entry || (browser->use_navkeypressed &&
+ !browser->navkeypressed);
int width = browser->width, printed;
int i, pcnt_width = annotate_browser__pcnt_width(ab);
double percent_max = 0.0;
@@ -271,19 +314,25 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
bcursor = disasm_line__browser(cursor);
btarget = disasm_line__browser(target);

- if (annotate_browser__opts.hide_src_code) {
- from = bcursor->idx_asm;
- to = btarget->idx_asm;
- } else {
- from = (u64)bcursor->idx;
- to = (u64)btarget->idx;
- }
+ from = bcursor->idx_asm;
+ to = btarget->idx_asm;

ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
from, to);
}

+static unsigned int annotate_code_browser__refresh(struct ui_browser *browser)
+{
+ struct annotate_browser *ab = container_of(browser, struct annotate_browser, cb);
+ int ret = ui_browser__list_head_refresh(browser);
+ int pcnt_width = annotate_browser__pcnt_width(ab);
+
+ ui_browser__set_color(browser, HE_COLORSET_NORMAL);
+ __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
+ return ret;
+}
+
static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
@@ -364,12 +413,37 @@ static void annotate_browser__set_rb_top(struct annotate_browser *browser,
bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
pos = ((struct disasm_line *)bpos) - 1;
idx = bpos->idx;
- if (annotate_browser__opts.hide_src_code)
- idx = bpos->idx_asm;
+ idx = bpos->idx_asm;
annotate_browser__set_top(browser, pos, idx);
browser->curr_hot = nd;
}

+static void annotate_code_browser__calc_percent(struct annotate_browser *browser,
+ struct perf_evsel *evsel)
+{
+ int i;
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct annotation *notes = symbol__annotation(sym);
+ struct code_line *cl;
+ struct list_head *code_lines = browser->cb.entries;
+
+ pthread_mutex_lock(&notes->lock);
+
+ list_for_each_entry(cl, code_lines, node) {
+ for (i = 0; i < browser->nr_events; i++) {
+ cl->samples_sum[i].percent = 0.0;
+ cl->samples_sum[i].nr = 0;
+ }
+
+ for (i = 0; i < cl->nr_matched_dl; i++) {
+ code_line__sum_samples(cl, cl->matched_dl_arr[i], notes, evsel);
+ }
+ }
+
+ pthread_mutex_unlock(&notes->lock);
+}
+
static void annotate_browser__calc_percent(struct annotate_browser *browser,
struct perf_evsel *evsel)
{
@@ -422,45 +496,6 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
browser->curr_hot = rb_last(&browser->entries);
}

-static bool annotate_browser__toggle_source(struct annotate_browser *browser)
-{
- struct disasm_line *dl;
- struct browser_disasm_line *bdl;
- off_t offset = browser->b.index - browser->b.top_idx;
-
- browser->b.seek(&browser->b, offset, SEEK_CUR);
- dl = list_entry(browser->b.top, struct disasm_line, node);
- bdl = disasm_line__browser(dl);
-
- if (annotate_browser__opts.hide_src_code) {
- if (bdl->idx_asm < offset)
- offset = bdl->idx;
-
- browser->b.nr_entries = browser->nr_entries;
- annotate_browser__opts.hide_src_code = false;
- browser->b.seek(&browser->b, -offset, SEEK_CUR);
- browser->b.top_idx = bdl->idx - offset;
- browser->b.index = bdl->idx;
- } else {
- if (bdl->idx_asm < 0) {
- ui_helpline__puts("Only available for assembly lines.");
- browser->b.seek(&browser->b, -offset, SEEK_CUR);
- return false;
- }
-
- if (bdl->idx_asm < offset)
- offset = bdl->idx_asm;
-
- browser->b.nr_entries = browser->nr_asm_entries;
- annotate_browser__opts.hide_src_code = true;
- browser->b.seek(&browser->b, -offset, SEEK_CUR);
- browser->b.top_idx = bdl->idx_asm - offset;
- browser->b.index = bdl->idx_asm;
- }
-
- return true;
-}
-
static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
{
ui_browser__reset_index(&browser->b);
@@ -696,6 +731,54 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser
browser->addr_width += browser->jumps_width + 1;
}

+static int annotate_code_browser__run(struct annotate_browser *browser,
+ struct perf_evsel *evsel, int delay_secs)
+{
+ int key;
+
+ if (ui_browser__show(&browser->cb, browser->cb.title, ui_helpline__current) < 0)
+ return -1;
+ annotate_code_browser__calc_percent(browser, evsel);
+
+ while (1) {
+
+ key = ui_browser__run(&browser->cb, delay_secs);
+ if (delay_secs != 0) {
+ annotate_code_browser__calc_percent(browser, evsel);
+ }
+
+ switch (key) {
+ case K_F1:
+ case 'h':
+ ui_browser__help_window(&browser->cb,
+ "UP/DOWN/PGUP\n"
+ "PGDN/SPACE Navigate\n"
+ "q/ESC/CTRL+C Return to dissembly view\n\n"
+ "s Toggle source code view\n"
+ "t Toggle total period view\n"
+ "k Toggle line numbers\n");
+ continue;
+ case 't':
+ annotate_browser__opts.show_total_period =
+ !annotate_browser__opts.show_total_period;
+ continue;
+ case 'k':
+ annotate_browser__opts.show_linenr =
+ !annotate_browser__opts.show_linenr;
+ continue;
+ case 's':
+ case K_LEFT:
+ case K_ESC:
+ case 'q':
+ case CTRL('c'):
+ return 0;
+ default:
+ continue;
+ }
+ }
+ return 0;
+}
+
static int annotate_browser__run(struct annotate_browser *browser,
struct perf_evsel *evsel,
struct hist_browser_timer *hbt)
@@ -775,7 +858,6 @@ static int annotate_browser__run(struct annotate_browser *browser,
"s Toggle source code view\n"
"t Toggle total period view\n"
"/ Search string\n"
- "k Toggle line numbers\n"
"r Run available scripts\n"
"? Search string backwards\n");
continue;
@@ -792,8 +874,11 @@ static int annotate_browser__run(struct annotate_browser *browser,
nd = browser->curr_hot;
break;
case 's':
- if (annotate_browser__toggle_source(browser))
- ui_helpline__puts(help);
+ if (browser->has_src_code) {
+ browser->cb.title = title;
+ annotate_code_browser__run(browser, evsel, delay_secs);
+ } else
+ ui_helpline__puts("No source code for the symbol");
continue;
case 'o':
annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
@@ -1095,9 +1180,27 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map,
browser.b.entries = &notes->src->source,
browser.b.width += 18; /* Percentage */

- if (annotate_browser__opts.hide_src_code)
- annotate_browser__init_asm_mode(&browser);
+ if (symbol__get_source_code(sym, map, evsel) == 0) {
+ struct source_code *code = notes->src->code;
+ struct code_line *cl;
+
+ browser.has_src_code = true;
+ browser.cb.refresh = annotate_code_browser__refresh;
+ browser.cb.seek = ui_browser__list_head_seek;
+ browser.cb.write = annotate_code_browser__write;
+ browser.cb.use_navkeypressed = true;
+ browser.cb.entries = &code->lines;
+
+ list_for_each_entry(cl, &code->lines, node) {
+ size_t line_len = strlen(cl->line);
+
+ if (browser.cb.width < line_len)
+ browser.cb.width = line_len;
+ browser.cb.nr_entries++;
+ }
+ }

+ annotate_browser__init_asm_mode(&browser);
annotate_browser__update_addr_width(&browser);

ret = annotate_browser__run(&browser, evsel, hbt);
@@ -1105,6 +1208,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map,
list_del(&pos->node);
disasm_line__free(pos);
}
+ symbol__free_source_code(sym);

out_free_offsets:
free(browser.offsets);
@@ -1121,7 +1225,6 @@ static struct annotate_config {
const char *name;
bool *value;
} annotate__configs[] = {
- ANNOTATE_CFG(hide_src_code),
ANNOTATE_CFG(jump_arrows),
ANNOTATE_CFG(show_linenr),
ANNOTATE_CFG(show_nr_jumps),
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 7d1c7cc..d8a3a50 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1396,7 +1396,7 @@ static void code_lines__free(struct list_head *code_lines)
}
}

-static int symbol__free_source_code(struct symbol *sym)
+int symbol__free_source_code(struct symbol *sym)
{
struct annotation *notes = symbol__annotation(sym);
struct source_code *code = notes->src->code;
@@ -1410,8 +1410,8 @@ static int symbol__free_source_code(struct symbol *sym)
return 0;
}

-static void code_line__sum_samples(struct code_line *cl, struct disasm_line *dl,
- struct annotation *notes, struct perf_evsel *evsel)
+void code_line__sum_samples(struct code_line *cl, struct disasm_line *dl,
+ struct annotation *notes, struct perf_evsel *evsel)
{
int i;
u64 nr_samples;
@@ -1569,8 +1569,8 @@ static int source_code__collect(struct source_code *code,
return ret;
}

-static int symbol__get_source_code(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel)
+int symbol__get_source_code(struct symbol *sym, struct map *map,
+ struct perf_evsel *evsel)
{
struct annotation *notes = symbol__annotation(sym);
struct source_code *code;
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index dd7ddae..e125eca 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -109,6 +109,9 @@ struct code_line {
struct disasm_line_samples *samples_sum;
};

+void code_line__sum_samples(struct code_line *cl, struct disasm_line *dl,
+ struct annotation *notes, struct perf_evsel *evsel);
+
struct source_code {
char *path;
int nr_events;
@@ -180,6 +183,10 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr);
int symbol__alloc_hist(struct symbol *sym);
void symbol__annotate_zero_histograms(struct symbol *sym);

+int symbol__free_source_code(struct symbol *sym);
+int symbol__get_source_code(struct symbol *sym, struct map *map,
+ struct perf_evsel *evsel);
+
int symbol__disassemble(struct symbol *sym, struct map *map, const char *arch_name, size_t privsize);

enum symbol_disassemble_errno {
--
2.7.4