summaryrefslogtreecommitdiff
path: root/tools/perf/ui
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/ui')
-rw-r--r--tools/perf/ui/Build21
-rw-r--r--tools/perf/ui/browser.c134
-rw-r--r--tools/perf/ui/browser.h17
-rw-r--r--tools/perf/ui/browsers/Build17
-rw-r--r--tools/perf/ui/browsers/annotate-data.c614
-rw-r--r--tools/perf/ui/browsers/annotate.c1391
-rw-r--r--tools/perf/ui/browsers/header.c10
-rw-r--r--tools/perf/ui/browsers/hists.c1356
-rw-r--r--tools/perf/ui/browsers/hists.h7
-rw-r--r--tools/perf/ui/browsers/map.c15
-rw-r--r--tools/perf/ui/browsers/map.h1
-rw-r--r--tools/perf/ui/browsers/res_sample.c96
-rw-r--r--tools/perf/ui/browsers/scripts.c452
-rw-r--r--tools/perf/ui/gtk/Build7
-rw-r--r--tools/perf/ui/gtk/annotate.c84
-rw-r--r--tools/perf/ui/gtk/browser.c5
-rw-r--r--tools/perf/ui/gtk/gtk.h12
-rw-r--r--tools/perf/ui/gtk/helpline.c5
-rw-r--r--tools/perf/ui/gtk/hists.c38
-rw-r--r--tools/perf/ui/gtk/progress.c2
-rw-r--r--tools/perf/ui/gtk/setup.c5
-rw-r--r--tools/perf/ui/gtk/util.c6
-rw-r--r--tools/perf/ui/helpline.c3
-rw-r--r--tools/perf/ui/helpline.h3
-rw-r--r--tools/perf/ui/hist.c753
-rw-r--r--tools/perf/ui/keysyms.c44
-rw-r--r--tools/perf/ui/keysyms.h4
-rw-r--r--tools/perf/ui/libslang.h22
-rw-r--r--tools/perf/ui/progress.c17
-rw-r--r--tools/perf/ui/progress.h13
-rw-r--r--tools/perf/ui/setup.c32
-rw-r--r--tools/perf/ui/stdio/hist.c264
-rw-r--r--tools/perf/ui/tui/Build8
-rw-r--r--tools/perf/ui/tui/helpline.c13
-rw-r--r--tools/perf/ui/tui/progress.c42
-rw-r--r--tools/perf/ui/tui/setup.c42
-rw-r--r--tools/perf/ui/tui/tui.h1
-rw-r--r--tools/perf/ui/tui/util.c74
-rw-r--r--tools/perf/ui/ui.h8
-rw-r--r--tools/perf/ui/util.c10
-rw-r--r--tools/perf/ui/util.h5
41 files changed, 3819 insertions, 1834 deletions
diff --git a/tools/perf/ui/Build b/tools/perf/ui/Build
index 0a73538c0441..6005f813c9e3 100644
--- a/tools/perf/ui/Build
+++ b/tools/perf/ui/Build
@@ -1,14 +1,13 @@
-libperf-y += setup.o
-libperf-y += helpline.o
-libperf-y += progress.o
-libperf-y += util.o
-libperf-y += hist.o
-libperf-y += stdio/hist.o
+perf-ui-y += setup.o
+perf-ui-y += helpline.o
+perf-ui-y += progress.o
+perf-ui-y += util.o
+perf-ui-y += hist.o
+perf-ui-y += stdio/hist.o
CFLAGS_setup.o += -DLIBDIR="BUILD_STR($(LIBDIR))"
-libperf-$(CONFIG_SLANG) += browser.o
-libperf-$(CONFIG_SLANG) += browsers/
-libperf-$(CONFIG_SLANG) += tui/
-
-CFLAGS_browser.o += -DENABLE_SLFUTURE_CONST
+perf-ui-$(CONFIG_SLANG) += browser.o
+perf-ui-$(CONFIG_SLANG) += keysyms.o
+perf-ui-$(CONFIG_SLANG) += browsers/
+perf-ui-$(CONFIG_SLANG) += tui/
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
index 83874b0e266c..dc88427b4ae5 100644
--- a/tools/perf/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -1,20 +1,21 @@
-#include "../util.h"
-#include "../string2.h"
-#include "../config.h"
-#include "../../perf.h"
+// SPDX-License-Identifier: GPL-2.0
+#include "../util/string2.h"
+#include "../util/config.h"
#include "libslang.h"
#include "ui.h"
#include "util.h"
#include <linux/compiler.h>
#include <linux/list.h>
#include <linux/rbtree.h>
+#include <linux/string.h>
#include <stdlib.h>
#include <sys/ttydefaults.h>
#include "browser.h"
#include "helpline.h"
#include "keysyms.h"
-#include "../color.h"
-#include "sane_ctype.h"
+#include "../util/color.h"
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
static int ui_browser__percent_color(struct ui_browser *browser,
double percent, bool current)
@@ -43,15 +44,25 @@ void ui_browser__set_percent_color(struct ui_browser *browser,
ui_browser__set_color(browser, color);
}
-void ui_browser__gotorc(struct ui_browser *browser, int y, int x)
+void ui_browser__gotorc_title(struct ui_browser *browser, int y, int x)
{
SLsmg_gotorc(browser->y + y, browser->x + x);
}
+void ui_browser__gotorc(struct ui_browser *browser, int y, int x)
+{
+ SLsmg_gotorc(browser->y + y + browser->extra_title_lines, browser->x + x);
+}
+
void ui_browser__write_nstring(struct ui_browser *browser __maybe_unused, const char *msg,
unsigned int width)
{
- slsmg_write_nstring(msg, width);
+ SLsmg_write_nstring(msg, width);
+}
+
+void ui_browser__vprintf(struct ui_browser *browser __maybe_unused, const char *fmt, va_list args)
+{
+ SLsmg_vprintf(fmt, args);
}
void ui_browser__printf(struct ui_browser *browser __maybe_unused, const char *fmt, ...)
@@ -59,7 +70,7 @@ void ui_browser__printf(struct ui_browser *browser __maybe_unused, const char *f
va_list args;
va_start(args, fmt);
- slsmg_vprintf(fmt, args);
+ ui_browser__vprintf(browser, fmt, args);
va_end(args);
}
@@ -184,6 +195,7 @@ void ui_browser__refresh_dimensions(struct ui_browser *browser)
{
browser->width = SLtt_Screen_Cols - 1;
browser->height = browser->rows = SLtt_Screen_Rows - 2;
+ browser->rows -= browser->extra_title_lines;
browser->y = 1;
browser->x = 0;
}
@@ -191,7 +203,7 @@ void ui_browser__refresh_dimensions(struct ui_browser *browser)
void ui_browser__handle_resize(struct ui_browser *browser)
{
ui__refresh_dimensions(false);
- ui_browser__show(browser, browser->title, ui_helpline__current);
+ ui_browser__show(browser, browser->title ?: "", ui_helpline__current);
ui_browser__refresh(browser);
}
@@ -221,6 +233,14 @@ int ui_browser__warning(struct ui_browser *browser, int timeout,
return key;
}
+int ui_browser__warn_unhandled_hotkey(struct ui_browser *browser, int key, int timeout, const char *help)
+{
+ char kname[32];
+
+ key_name(key, kname, sizeof(kname));
+ return ui_browser__warning(browser, timeout, "\n'%s' key not associated%s!\n", kname, help ?: "");
+}
+
int ui_browser__help_window(struct ui_browser *browser, const char *text)
{
int key;
@@ -256,9 +276,9 @@ void __ui_browser__show_title(struct ui_browser *browser, const char *title)
void ui_browser__show_title(struct ui_browser *browser, const char *title)
{
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
__ui_browser__show_title(browser, title);
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
}
int ui_browser__show(struct ui_browser *browser, const char *title,
@@ -272,10 +292,11 @@ int ui_browser__show(struct ui_browser *browser, const char *title,
browser->refresh_dimensions(browser);
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
__ui_browser__show_title(browser, title);
- browser->title = title;
+ free(browser->title);
+ browser->title = strdup(title);
zfree(&browser->helpline);
va_start(ap, helpline);
@@ -283,16 +304,17 @@ int ui_browser__show(struct ui_browser *browser, const char *title,
va_end(ap);
if (err > 0)
ui_helpline__push(browser->helpline);
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
return err ? 0 : -1;
}
void ui_browser__hide(struct ui_browser *browser)
{
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
ui_helpline__pop();
zfree(&browser->helpline);
- pthread_mutex_unlock(&ui__lock);
+ zfree(&browser->title);
+ mutex_unlock(&ui__lock);
}
static void ui_browser__scrollbar_set(struct ui_browser *browser)
@@ -330,17 +352,19 @@ static int __ui_browser__refresh(struct ui_browser *browser)
else
width += 1;
- SLsmg_fill_region(browser->y + row, browser->x,
- browser->height - row, width, ' ');
+ SLsmg_fill_region(browser->y + row + browser->extra_title_lines, browser->x,
+ browser->rows - row, width, ' ');
+ if (browser->nr_entries == 0 && browser->no_samples_msg)
+ __ui__info_window(NULL, browser->no_samples_msg, NULL);
return 0;
}
int ui_browser__refresh(struct ui_browser *browser)
{
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
__ui_browser__refresh(browser);
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
return 0;
}
@@ -376,10 +400,10 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs)
while (1) {
off_t offset;
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
err = __ui_browser__refresh(browser);
SLsmg_refresh();
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
if (err < 0)
break;
@@ -435,6 +459,8 @@ int ui_browser__run(struct ui_browser *browser, int delay_secs)
goto out;
if (browser->horiz_scroll != 0)
--browser->horiz_scroll;
+ else
+ goto out;
break;
case K_PGDN:
case ' ':
@@ -563,7 +589,7 @@ static int ui_browser__color_config(const char *var, const char *value,
int i;
/* same dir for all commands */
- if (prefixcmp(var, "colors.") != 0)
+ if (!strstarts(var, "colors.") != 0)
return 0;
for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
@@ -581,7 +607,7 @@ static int ui_browser__color_config(const char *var, const char *value,
break;
*bg = '\0';
- bg = ltrim(++bg);
+ bg = skip_spaces(bg + 1);
ui_browser__colorsets[i].bg = bg;
ui_browser__colorsets[i].fg = fg;
return 0;
@@ -598,14 +624,16 @@ void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
browser->top = browser->entries;
break;
case SEEK_CUR:
- browser->top = browser->top + browser->top_idx + offset;
+ browser->top = (char **)browser->top + offset;
break;
case SEEK_END:
- browser->top = browser->top + browser->nr_entries - 1 + offset;
+ browser->top = (char **)browser->entries + browser->nr_entries - 1 + offset;
break;
default:
return;
}
+ assert((char **)browser->top < (char **)browser->entries + browser->nr_entries);
+ assert((char **)browser->top >= (char **)browser->entries);
}
unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
@@ -617,7 +645,9 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
browser->top = browser->entries;
pos = (char **)browser->top;
- while (idx < browser->nr_entries) {
+ while (idx < browser->nr_entries &&
+ row < (unsigned)SLtt_Screen_Rows - 1) {
+ assert(pos < (char **)browser->entries + browser->nr_entries);
if (!browser->filter || !browser->filter(browser, *pos)) {
ui_browser__gotorc(browser, row, 0);
browser->write(browser, pos, row);
@@ -738,6 +768,50 @@ void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
__ui_browser__line_arrow_down(browser, column, start, end);
}
+void ui_browser__mark_fused(struct ui_browser *browser, unsigned int column,
+ unsigned int row, int diff, bool arrow_down)
+{
+ int end_row;
+
+ if (diff <= 0)
+ return;
+
+ SLsmg_set_char_set(1);
+
+ if (arrow_down) {
+ if (row + diff <= browser->top_idx)
+ return;
+
+ end_row = row + diff - browser->top_idx;
+ ui_browser__gotorc(browser, end_row, column - 1);
+ SLsmg_write_char(SLSMG_LTEE_CHAR);
+
+ while (--end_row >= 0 && end_row > (int)(row - browser->top_idx)) {
+ ui_browser__gotorc(browser, end_row, column - 1);
+ SLsmg_draw_vline(1);
+ }
+
+ end_row = (int)(row - browser->top_idx);
+ if (end_row >= 0) {
+ ui_browser__gotorc(browser, end_row, column - 1);
+ SLsmg_write_char(SLSMG_ULCORN_CHAR);
+ ui_browser__gotorc(browser, end_row, column);
+ SLsmg_draw_hline(2);
+ }
+ } else {
+ if (row < browser->top_idx)
+ return;
+
+ end_row = row - browser->top_idx;
+ ui_browser__gotorc(browser, end_row, column - 1);
+ SLsmg_write_char(SLSMG_LTEE_CHAR);
+ ui_browser__gotorc(browser, end_row, column);
+ SLsmg_draw_hline(2);
+ }
+
+ SLsmg_set_char_set(0);
+}
+
void ui_browser__init(void)
{
int i = 0;
@@ -746,8 +820,6 @@ void ui_browser__init(void)
while (ui_browser__colorsets[i].name) {
struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
- sltt_set_color(c->colorset, c->name, c->fg, c->bg);
+ SLtt_set_color(c->colorset, c->name, c->fg, c->bg);
}
-
- annotate_browser__init();
}
diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h
index be3b70eb5fca..9d4404f9b87f 100644
--- a/tools/perf/ui/browser.h
+++ b/tools/perf/ui/browser.h
@@ -1,7 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_UI_BROWSER_H_
#define _PERF_UI_BROWSER_H_ 1
#include <linux/types.h>
+#include <stdarg.h>
+#include <sys/types.h>
#define HE_COLORSET_TOP 50
#define HE_COLORSET_MEDIUM 51
@@ -15,10 +18,12 @@ struct ui_browser {
u64 index, top_idx;
void *top, *entries;
u16 y, x, width, height, rows, columns, horiz_scroll;
+ u8 extra_title_lines;
int current_color;
void *priv;
- const char *title;
+ char *title;
char *helpline;
+ const char *no_samples_msg;
void (*refresh_dimensions)(struct ui_browser *browser);
unsigned int (*refresh)(struct ui_browser *browser);
void (*write)(struct ui_browser *browser, void *entry, int row);
@@ -36,13 +41,17 @@ bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row);
void ui_browser__refresh_dimensions(struct ui_browser *browser);
void ui_browser__reset_index(struct ui_browser *browser);
+void ui_browser__gotorc_title(struct ui_browser *browser, int y, int x);
void ui_browser__gotorc(struct ui_browser *browser, int y, int x);
void ui_browser__write_nstring(struct ui_browser *browser, const char *msg,
unsigned int width);
+void ui_browser__vprintf(struct ui_browser *browser, const char *fmt, va_list args);
void ui_browser__printf(struct ui_browser *browser, const char *fmt, ...);
void ui_browser__write_graph(struct ui_browser *browser, int graph);
void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
u64 start, u64 end);
+void ui_browser__mark_fused(struct ui_browser *browser, unsigned int column,
+ unsigned int row, int diff, bool arrow_down);
void __ui_browser__show_title(struct ui_browser *browser, const char *title);
void ui_browser__show_title(struct ui_browser *browser, const char *title);
int ui_browser__show(struct ui_browser *browser, const char *title,
@@ -57,12 +66,13 @@ void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
int ui_browser__warning(struct ui_browser *browser, int timeout,
const char *format, ...);
+int ui_browser__warn_unhandled_hotkey(struct ui_browser *browser, int key, int timeout, const char *help);
int ui_browser__help_window(struct ui_browser *browser, const char *text);
bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text);
int ui_browser__input_window(const char *title, const char *text, char *input,
const char *exit_msg, int delay_sec);
-struct perf_env;
-int tui__header_window(struct perf_env *env);
+struct perf_session;
+int tui__header_window(struct perf_session *session);
void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence);
unsigned int ui_browser__argv_refresh(struct ui_browser *browser);
@@ -74,5 +84,4 @@ void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int wh
unsigned int ui_browser__list_head_refresh(struct ui_browser *browser);
void ui_browser__init(void);
-void annotate_browser__init(void);
#endif /* _PERF_UI_BROWSER_H_ */
diff --git a/tools/perf/ui/browsers/Build b/tools/perf/ui/browsers/Build
index de223f5bed58..a07489e44765 100644
--- a/tools/perf/ui/browsers/Build
+++ b/tools/perf/ui/browsers/Build
@@ -1,10 +1,7 @@
-libperf-y += annotate.o
-libperf-y += hists.o
-libperf-y += map.o
-libperf-y += scripts.o
-libperf-y += header.o
-
-CFLAGS_annotate.o += -DENABLE_SLFUTURE_CONST
-CFLAGS_hists.o += -DENABLE_SLFUTURE_CONST
-CFLAGS_map.o += -DENABLE_SLFUTURE_CONST
-CFLAGS_scripts.o += -DENABLE_SLFUTURE_CONST
+perf-ui-y += annotate.o
+perf-ui-y += annotate-data.o
+perf-ui-y += hists.o
+perf-ui-y += map.o
+perf-ui-y += scripts.o
+perf-ui-y += header.o
+perf-ui-y += res_sample.o
diff --git a/tools/perf/ui/browsers/annotate-data.c b/tools/perf/ui/browsers/annotate-data.c
new file mode 100644
index 000000000000..aa8c89fe2e82
--- /dev/null
+++ b/tools/perf/ui/browsers/annotate-data.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <inttypes.h>
+#include <string.h>
+#include <linux/zalloc.h>
+#include <sys/ttydefaults.h>
+
+#include "ui/browser.h"
+#include "ui/helpline.h"
+#include "ui/keysyms.h"
+#include "ui/ui.h"
+#include "util/annotate.h"
+#include "util/annotate-data.h"
+#include "util/evsel.h"
+#include "util/evlist.h"
+#include "util/sort.h"
+
+#define FOLDED_SIGN '+'
+#define UNFOLD_SIGN '-'
+#define NOCHLD_SIGN ' '
+
+struct browser_entry {
+ struct list_head node;
+ struct annotated_member *data;
+ struct type_hist_entry *hists;
+ struct browser_entry *parent;
+ struct list_head children;
+ int indent; /*indentation level, starts from 0 */
+ int nr_entries; /* # of visible entries: self + descendents */
+ bool folded; /* only can be false when it has children */
+};
+
+struct annotated_data_browser {
+ struct ui_browser b;
+ struct list_head entries;
+ struct browser_entry *curr;
+ int nr_events;
+};
+
+static struct annotated_data_browser *get_browser(struct ui_browser *uib)
+{
+ return container_of(uib, struct annotated_data_browser, b);
+}
+
+static void update_hist_entry(struct type_hist_entry *dst,
+ struct type_hist_entry *src)
+{
+ dst->nr_samples += src->nr_samples;
+ dst->period += src->period;
+}
+
+static int get_member_overhead(struct annotated_data_type *adt,
+ struct browser_entry *entry,
+ struct evsel *leader)
+{
+ struct annotated_member *member = entry->data;
+ int i, k;
+
+ for (i = 0; i < member->size; i++) {
+ struct type_hist *h;
+ struct evsel *evsel;
+ int offset = member->offset + i;
+
+ k = 0;
+ for_each_group_evsel(evsel, leader) {
+ if (symbol_conf.skip_empty &&
+ evsel__hists(evsel)->stats.nr_samples == 0)
+ continue;
+
+ h = adt->histograms[evsel->core.idx];
+ update_hist_entry(&entry->hists[k++], &h->addr[offset]);
+ }
+ }
+ return 0;
+}
+
+static int add_child_entries(struct annotated_data_browser *browser,
+ struct browser_entry *parent,
+ struct annotated_data_type *adt,
+ struct annotated_member *member,
+ struct evsel *evsel, int indent)
+{
+ struct annotated_member *pos;
+ struct browser_entry *entry;
+ struct list_head *parent_list;
+
+ entry = zalloc(sizeof(*entry));
+ if (entry == NULL)
+ return -1;
+
+ entry->hists = calloc(browser->nr_events, sizeof(*entry->hists));
+ if (entry->hists == NULL) {
+ free(entry);
+ return -1;
+ }
+
+ entry->data = member;
+ entry->parent = parent;
+ entry->indent = indent;
+ if (get_member_overhead(adt, entry, evsel) < 0) {
+ free(entry);
+ return -1;
+ }
+
+ INIT_LIST_HEAD(&entry->children);
+ if (parent)
+ parent_list = &parent->children;
+ else
+ parent_list = &browser->entries;
+
+ list_add_tail(&entry->node, parent_list);
+
+ list_for_each_entry(pos, &member->children, node) {
+ int nr = add_child_entries(browser, entry, adt, pos, evsel,
+ indent + 1);
+ if (nr < 0)
+ return nr;
+ }
+
+ /* add an entry for the closing bracket ("}") */
+ if (!list_empty(&member->children)) {
+ struct browser_entry *bracket;
+
+ bracket = zalloc(sizeof(*bracket));
+ if (bracket == NULL)
+ return -1;
+
+ bracket->indent = indent;
+ bracket->parent = entry;
+ bracket->folded = true;
+ bracket->nr_entries = 1;
+
+ INIT_LIST_HEAD(&bracket->children);
+ list_add_tail(&bracket->node, &entry->children);
+ }
+
+ /* fold child entries by default */
+ entry->folded = true;
+ entry->nr_entries = 1;
+ return 0;
+}
+
+static u32 count_visible_entries(struct annotated_data_browser *browser)
+{
+ int nr = 0;
+ struct browser_entry *entry;
+
+ list_for_each_entry(entry, &browser->entries, node)
+ nr += entry->nr_entries;
+
+ return nr;
+}
+
+static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser)
+{
+ struct hist_entry *he = browser->b.priv;
+ struct annotated_data_type *adt = he->mem_type;
+ struct evsel *evsel = hists_to_evsel(he->hists);
+
+ INIT_LIST_HEAD(&browser->entries);
+
+ add_child_entries(browser, /*parent=*/NULL, adt, &adt->self, evsel,
+ /*indent=*/0);
+
+ browser->b.entries = &browser->entries;
+ browser->b.nr_entries = count_visible_entries(browser);
+ return 0;
+}
+
+static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser)
+{
+ struct browser_entry *pos, *tmp;
+
+ list_for_each_entry_safe(pos, tmp, &browser->entries, node) {
+ list_del_init(&pos->node);
+ zfree(&pos->hists);
+ free(pos);
+ }
+}
+
+static struct browser_entry *get_first_child(struct browser_entry *entry)
+{
+ if (list_empty(&entry->children))
+ return NULL;
+
+ return list_first_entry(&entry->children, struct browser_entry, node);
+}
+
+static struct browser_entry *get_last_child(struct browser_entry *entry)
+{
+ if (list_empty(&entry->children))
+ return NULL;
+
+ return list_last_entry(&entry->children, struct browser_entry, node);
+}
+
+static bool is_first_child(struct browser_entry *entry)
+{
+ /* This will be checked in a different way */
+ if (entry->parent == NULL)
+ return false;
+
+ return get_first_child(entry->parent) == entry;
+}
+
+static bool is_last_child(struct browser_entry *entry)
+{
+ /* This will be checked in a different way */
+ if (entry->parent == NULL)
+ return false;
+
+ return get_last_child(entry->parent) == entry;
+}
+
+static struct browser_entry *browser__prev_entry(struct ui_browser *uib,
+ struct browser_entry *entry)
+{
+ struct annotated_data_browser *browser = get_browser(uib);
+ struct browser_entry *first;
+
+ first = list_first_entry(&browser->entries, struct browser_entry, node);
+
+ while (entry != first) {
+ if (is_first_child(entry))
+ entry = entry->parent;
+ else {
+ entry = list_prev_entry(entry, node);
+ while (!entry->folded)
+ entry = get_last_child(entry);
+ }
+
+ if (!uib->filter || !uib->filter(uib, &entry->node))
+ return entry;
+ }
+ return first;
+}
+
+static struct browser_entry *browser__next_entry(struct ui_browser *uib,
+ struct browser_entry *entry)
+{
+ struct annotated_data_browser *browser = get_browser(uib);
+ struct browser_entry *last;
+
+ last = list_last_entry(&browser->entries, struct browser_entry, node);
+ while (!last->folded)
+ last = get_last_child(last);
+
+ while (entry != last) {
+ if (!entry->folded)
+ entry = get_first_child(entry);
+ else {
+ while (is_last_child(entry))
+ entry = entry->parent;
+
+ entry = list_next_entry(entry, node);
+ }
+
+ if (!uib->filter || !uib->filter(uib, &entry->node))
+ return entry;
+ }
+ return last;
+}
+
+static void browser__seek(struct ui_browser *uib, off_t offset, int whence)
+{
+ struct annotated_data_browser *browser = get_browser(uib);
+ struct browser_entry *entry;
+
+ if (uib->nr_entries == 0)
+ return;
+
+ switch (whence) {
+ case SEEK_SET:
+ entry = list_first_entry(&browser->entries, typeof(*entry), node);
+ if (uib->filter && uib->filter(uib, &entry->node))
+ entry = browser__next_entry(uib, entry);
+ break;
+ case SEEK_CUR:
+ entry = list_entry(uib->top, typeof(*entry), node);
+ break;
+ case SEEK_END:
+ entry = list_last_entry(&browser->entries, typeof(*entry), node);
+ while (!entry->folded)
+ entry = get_last_child(entry);
+ if (uib->filter && uib->filter(uib, &entry->node))
+ entry = browser__prev_entry(uib, entry);
+ break;
+ default:
+ return;
+ }
+
+ assert(entry != NULL);
+
+ if (offset > 0) {
+ while (offset-- != 0)
+ entry = browser__next_entry(uib, entry);
+ } else {
+ while (offset++ != 0)
+ entry = browser__prev_entry(uib, entry);
+ }
+
+ uib->top = &entry->node;
+}
+
+static unsigned int browser__refresh(struct ui_browser *uib)
+{
+ struct annotated_data_browser *browser = get_browser(uib);
+ struct browser_entry *entry, *next;
+ int row = 0;
+
+ if (uib->top == NULL || uib->top == uib->entries)
+ browser__seek(uib, SEEK_SET, 0);
+
+ entry = list_entry(uib->top, typeof(*entry), node);
+
+ while (true) {
+ if (!uib->filter || !uib->filter(uib, &entry->node)) {
+ ui_browser__gotorc(uib, row, 0);
+ uib->write(uib, entry, row);
+ if (uib->top_idx + row == uib->index)
+ browser->curr = entry;
+ if (++row == uib->rows)
+ break;
+ }
+ next = browser__next_entry(uib, entry);
+ if (next == entry)
+ break;
+
+ entry = next;
+ }
+
+ return row;
+}
+
+static int browser__show(struct ui_browser *uib)
+{
+ struct hist_entry *he = uib->priv;
+ struct annotated_data_type *adt = he->mem_type;
+ struct annotated_data_browser *browser = get_browser(uib);
+ const char *help = "Press 'h' for help on key bindings";
+ char title[256];
+
+ snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)",
+ adt->self.type_name, he->stat.nr_events);
+
+ if (ui_browser__show(uib, title, help) < 0)
+ return -1;
+
+ /* second line header */
+ ui_browser__gotorc_title(uib, 0, 0);
+ ui_browser__set_color(uib, HE_COLORSET_ROOT);
+
+ if (symbol_conf.show_total_period)
+ strcpy(title, "Period");
+ else if (symbol_conf.show_nr_samples)
+ strcpy(title, "Samples");
+ else
+ strcpy(title, "Percent");
+
+ ui_browser__printf(uib, "%*s %10s %10s %10s %s",
+ 2 + 11 * (browser->nr_events - 1), "",
+ title, "Offset", "Size", "Field");
+ ui_browser__write_nstring(uib, "", uib->width);
+ return 0;
+}
+
+static void browser__write_overhead(struct ui_browser *uib,
+ struct type_hist *total,
+ struct type_hist_entry *hist, int row)
+{
+ u64 period = hist->period;
+ double percent = total->period ? (100.0 * period / total->period) : 0;
+ bool current = ui_browser__is_current_entry(uib, row);
+ int nr_samples = 0;
+
+ ui_browser__set_percent_color(uib, percent, current);
+
+ if (symbol_conf.show_total_period)
+ ui_browser__printf(uib, " %10" PRIu64, period);
+ else if (symbol_conf.show_nr_samples)
+ ui_browser__printf(uib, " %10d", nr_samples);
+ else
+ ui_browser__printf(uib, " %10.2f", percent);
+
+ ui_browser__set_percent_color(uib, 0, current);
+}
+
+static void browser__write(struct ui_browser *uib, void *entry, int row)
+{
+ struct annotated_data_browser *browser = get_browser(uib);
+ struct browser_entry *be = entry;
+ struct annotated_member *member = be->data;
+ struct hist_entry *he = uib->priv;
+ struct annotated_data_type *adt = he->mem_type;
+ struct evsel *leader = hists_to_evsel(he->hists);
+ struct evsel *evsel;
+ int idx = 0;
+ bool current = ui_browser__is_current_entry(uib, row);
+
+ if (member == NULL) {
+ /* print the closing bracket */
+ ui_browser__set_percent_color(uib, 0, current);
+ ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
+ ui_browser__write_nstring(uib, "", 11 * browser->nr_events);
+ ui_browser__printf(uib, " %10s %10s %*s};",
+ "", "", be->indent * 4, "");
+ ui_browser__write_nstring(uib, "", uib->width);
+ return;
+ }
+
+ ui_browser__set_percent_color(uib, 0, current);
+
+ if (!list_empty(&be->children))
+ ui_browser__printf(uib, "%c ", be->folded ? FOLDED_SIGN : UNFOLD_SIGN);
+ else
+ ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
+
+ /* print the number */
+ for_each_group_evsel(evsel, leader) {
+ struct type_hist *h = adt->histograms[evsel->core.idx];
+
+ if (symbol_conf.skip_empty &&
+ evsel__hists(evsel)->stats.nr_samples == 0)
+ continue;
+
+ browser__write_overhead(uib, h, &be->hists[idx++], row);
+ }
+
+ /* print type info */
+ if (be->indent == 0 && !member->var_name) {
+ ui_browser__printf(uib, " %#10x %#10x %s%s",
+ member->offset, member->size,
+ member->type_name,
+ list_empty(&member->children) || be->folded? ";" : " {");
+ } else {
+ ui_browser__printf(uib, " %#10x %#10x %*s%s\t%s%s",
+ member->offset, member->size,
+ be->indent * 4, "", member->type_name,
+ member->var_name ?: "",
+ list_empty(&member->children) || be->folded ? ";" : " {");
+ }
+ /* fill the rest */
+ ui_browser__write_nstring(uib, "", uib->width);
+}
+
+static void annotated_data_browser__fold(struct annotated_data_browser *browser,
+ struct browser_entry *entry,
+ bool recursive)
+{
+ struct browser_entry *child;
+
+ if (list_empty(&entry->children))
+ return;
+ if (entry->folded && !recursive)
+ return;
+
+ if (recursive) {
+ list_for_each_entry(child, &entry->children, node)
+ annotated_data_browser__fold(browser, child, true);
+ }
+
+ entry->nr_entries = 1;
+ entry->folded = true;
+}
+
+static void annotated_data_browser__unfold(struct annotated_data_browser *browser,
+ struct browser_entry *entry,
+ bool recursive)
+{
+ struct browser_entry *child;
+ int nr_entries;
+
+ if (list_empty(&entry->children))
+ return;
+ if (!entry->folded && !recursive)
+ return;
+
+ nr_entries = 1; /* for self */
+ list_for_each_entry(child, &entry->children, node) {
+ if (recursive)
+ annotated_data_browser__unfold(browser, child, true);
+
+ nr_entries += child->nr_entries;
+ }
+
+ entry->nr_entries = nr_entries;
+ entry->folded = false;
+}
+
+static void annotated_data_browser__toggle_fold(struct annotated_data_browser *browser,
+ bool recursive)
+{
+ struct browser_entry *curr = browser->curr;
+ struct browser_entry *parent;
+
+ parent = curr->parent;
+ while (parent) {
+ parent->nr_entries -= curr->nr_entries;
+ parent = parent->parent;
+ }
+ browser->b.nr_entries -= curr->nr_entries;
+
+ if (curr->folded)
+ annotated_data_browser__unfold(browser, curr, recursive);
+ else
+ annotated_data_browser__fold(browser, curr, recursive);
+
+ parent = curr->parent;
+ while (parent) {
+ parent->nr_entries += curr->nr_entries;
+ parent = parent->parent;
+ }
+ browser->b.nr_entries += curr->nr_entries;
+
+ assert(browser->b.nr_entries == count_visible_entries(browser));
+}
+
+static int annotated_data_browser__run(struct annotated_data_browser *browser,
+ struct evsel *evsel __maybe_unused,
+ struct hist_browser_timer *hbt)
+{
+ int delay_secs = hbt ? hbt->refresh : 0;
+ int key;
+
+ if (browser__show(&browser->b) < 0)
+ return -1;
+
+ while (1) {
+ key = ui_browser__run(&browser->b, delay_secs);
+
+ switch (key) {
+ case K_TIMER:
+ if (hbt)
+ hbt->timer(hbt->arg);
+ continue;
+ case K_F1:
+ case 'h':
+ ui_browser__help_window(&browser->b,
+ "UP/DOWN/PGUP\n"
+ "PGDN/SPACE Navigate\n"
+ "</> Move to prev/next symbol\n"
+ "e Expand/Collapse current entry\n"
+ "E Expand/Collapse all children of the current\n"
+ "q/ESC/CTRL+C Exit\n\n");
+ continue;
+ case 'e':
+ annotated_data_browser__toggle_fold(browser,
+ /*recursive=*/false);
+ break;
+ case 'E':
+ annotated_data_browser__toggle_fold(browser,
+ /*recursive=*/true);
+ break;
+ case K_LEFT:
+ case '<':
+ case '>':
+ case K_ESC:
+ case 'q':
+ case CTRL('c'):
+ goto out;
+ default:
+ ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions");
+ continue;
+ }
+ }
+out:
+ ui_browser__hide(&browser->b);
+ return key;
+}
+
+int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel,
+ struct hist_browser_timer *hbt)
+{
+ struct annotated_data_browser browser = {
+ .b = {
+ .refresh = browser__refresh,
+ .seek = browser__seek,
+ .write = browser__write,
+ .priv = he,
+ .extra_title_lines = 1,
+ },
+ .nr_events = 1,
+ };
+ int ret;
+
+ ui_helpline__push("Press ESC to exit");
+
+ if (evsel__is_group_event(evsel)) {
+ struct evsel *pos;
+ int nr = 0;
+
+ for_each_group_evsel(pos, evsel) {
+ if (!symbol_conf.skip_empty ||
+ evsel__hists(pos)->stats.nr_samples)
+ nr++;
+ }
+ browser.nr_events = nr;
+ }
+
+ ret = annotated_data_browser__collect_entries(&browser);
+ if (ret < 0)
+ goto out;
+
+ /* To get the top and current entry */
+ browser__refresh(&browser.b);
+ /* Show the first-level child entries by default */
+ annotated_data_browser__toggle_fold(&browser, /*recursive=*/false);
+
+ ret = annotated_data_browser__run(&browser, evsel, hbt);
+
+out:
+ annotated_data_browser__delete_entries(&browser);
+
+ return ret;
+}
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 27f41f28dcb4..36aca8d6d003 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -1,363 +1,307 @@
-#include "../../util/util.h"
+// SPDX-License-Identifier: GPL-2.0
#include "../browser.h"
#include "../helpline.h"
#include "../ui.h"
-#include "../util.h"
#include "../../util/annotate.h"
+#include "../../util/debug.h"
+#include "../../util/debuginfo.h"
+#include "../../util/dso.h"
+#include "../../util/hashmap.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
+#include "../../util/map.h"
+#include "../../util/mutex.h"
#include "../../util/symbol.h"
#include "../../util/evsel.h"
-#include "../../util/config.h"
+#include "../../util/evlist.h"
+#include "../../util/thread.h"
#include <inttypes.h>
-#include <pthread.h>
+#include <linux/err.h>
#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
#include <sys/ttydefaults.h>
+#include <asm/bug.h>
-struct disasm_line_samples {
- double percent;
- u64 nr;
-};
-
-#define IPC_WIDTH 6
-#define CYCLES_WIDTH 6
+struct arch;
-struct browser_disasm_line {
- struct rb_node rb_node;
- u32 idx;
- int idx_asm;
- int jump_sources;
+struct annotate_browser {
+ struct ui_browser b;
+ struct rb_root entries;
+ struct rb_node *curr_hot;
+ struct annotation_line *selection;
+ struct arch *arch;
/*
- * actual length of this array is saved on the nr_events field
- * of the struct annotate_browser
+ * perf top can delete hist_entry anytime. Callers should make sure
+ * its lifetime.
*/
- struct disasm_line_samples samples[1];
-};
-
-static struct annotate_browser_opt {
- bool hide_src_code,
- use_offset,
- jump_arrows,
- show_linenr,
- show_nr_jumps,
- show_total_period;
-} annotate_browser__opts = {
- .use_offset = true,
- .jump_arrows = true,
+ struct hist_entry *he;
+ struct debuginfo *dbg;
+ struct evsel *evsel;
+ struct hashmap *type_hash;
+ bool searching_backwards;
+ char search_bf[128];
};
-struct arch;
+/* A copy of target hist_entry for perf top. */
+static struct hist_entry annotate_he;
-struct annotate_browser {
- struct ui_browser b;
- struct rb_root entries;
- struct rb_node *curr_hot;
- struct disasm_line *selection;
- struct disasm_line **offsets;
- struct arch *arch;
- int nr_events;
- u64 start;
- int nr_asm_entries;
- int nr_entries;
- int max_jump_sources;
- int nr_jumps;
- bool searching_backwards;
- bool have_cycles;
- u8 addr_width;
- u8 jumps_width;
- u8 target_width;
- u8 min_addr_width;
- u8 max_addr_width;
- char search_bf[128];
-};
+static size_t type_hash(long key, void *ctx __maybe_unused)
+{
+ return key;
+}
-static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
+static bool type_equal(long key1, long key2, void *ctx __maybe_unused)
{
- return (struct browser_disasm_line *)(dl + 1);
+ return key1 == key2;
}
-static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
- void *entry)
+static inline struct annotation *browser__annotation(struct ui_browser *browser)
{
- if (annotate_browser__opts.hide_src_code) {
- struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
- return dl->offset == -1;
- }
+ struct map_symbol *ms = browser->priv;
+ return symbol__annotation(ms->sym);
+}
- return false;
+static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, void *entry)
+{
+ struct annotation_line *al = list_entry(entry, struct annotation_line, node);
+ return annotation_line__filter(al);
}
-static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
- int nr, bool current)
+static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
{
- if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
+ struct annotation *notes = browser__annotation(browser);
+
+ if (current && (!browser->use_navkeypressed || browser->navkeypressed))
return HE_COLORSET_SELECTED;
- if (nr == browser->max_jump_sources)
+ if (nr == notes->src->max_jump_sources)
return HE_COLORSET_TOP;
if (nr > 1)
return HE_COLORSET_MEDIUM;
return HE_COLORSET_NORMAL;
}
-static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
- int nr, bool current)
+static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
{
- int color = annotate_browser__jumps_percent_color(browser, nr, current);
- return ui_browser__set_color(&browser->b, color);
+ int color = ui_browser__jumps_percent_color(browser, nr, current);
+ return ui_browser__set_color(browser, color);
}
-static int annotate_browser__pcnt_width(struct annotate_browser *ab)
+static int annotate_browser__set_color(void *browser, int color)
{
- int w = 7 * ab->nr_events;
-
- if (ab->have_cycles)
- w += IPC_WIDTH + CYCLES_WIDTH;
- return w;
+ return ui_browser__set_color(browser, color);
}
-static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
+static void annotate_browser__write_graph(void *browser, int graph)
{
- 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)));
- int width = browser->width, printed;
- int i, pcnt_width = annotate_browser__pcnt_width(ab);
- double percent_max = 0.0;
- char bf[256];
- bool show_title = false;
-
- for (i = 0; i < ab->nr_events; i++) {
- if (bdl->samples[i].percent > percent_max)
- percent_max = bdl->samples[i].percent;
- }
-
- if ((row == 0) && (dl->offset == -1 || percent_max == 0.0)) {
- if (ab->have_cycles) {
- if (dl->ipc == 0.0 && dl->cycles == 0)
- show_title = true;
- } else
- show_title = true;
- }
+ ui_browser__write_graph(browser, graph);
+}
- if (dl->offset != -1 && percent_max != 0.0) {
- for (i = 0; i < ab->nr_events; i++) {
- ui_browser__set_percent_color(browser,
- bdl->samples[i].percent,
- current_entry);
- if (annotate_browser__opts.show_total_period) {
- ui_browser__printf(browser, "%6" PRIu64 " ",
- bdl->samples[i].nr);
- } else {
- ui_browser__printf(browser, "%6.2f ",
- bdl->samples[i].percent);
- }
- }
- } else {
- ui_browser__set_percent_color(browser, 0, current_entry);
+static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
+{
+ ui_browser__set_percent_color(browser, percent, current);
+}
- if (!show_title)
- ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
- else
- ui_browser__printf(browser, "%*s", 7, "Percent");
- }
- if (ab->have_cycles) {
- if (dl->ipc)
- ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
- else if (!show_title)
- ui_browser__write_nstring(browser, " ", IPC_WIDTH);
- else
- ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");
+static void annotate_browser__printf(void *browser, const char *fmt, ...)
+{
+ va_list args;
- if (dl->cycles)
- ui_browser__printf(browser, "%*" PRIu64 " ",
- CYCLES_WIDTH - 1, dl->cycles);
- else if (!show_title)
- ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
- else
- ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
- }
+ va_start(args, fmt);
+ ui_browser__vprintf(browser, fmt, args);
+ va_end(args);
+}
- SLsmg_write_char(' ');
+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 annotation *notes = browser__annotation(browser);
+ struct annotation_line *al = list_entry(entry, struct annotation_line, node);
+ const bool is_current_entry = ui_browser__is_current_entry(browser, row);
+ struct annotation_write_ops ops = {
+ .first_line = row == 0,
+ .current_entry = is_current_entry,
+ .change_color = (!annotate_opts.hide_src_code &&
+ (!is_current_entry ||
+ (browser->use_navkeypressed &&
+ !browser->navkeypressed))),
+ .width = browser->width,
+ .obj = browser,
+ .set_color = annotate_browser__set_color,
+ .set_percent_color = annotate_browser__set_percent_color,
+ .set_jumps_percent_color = ui_browser__set_jumps_percent_color,
+ .printf = annotate_browser__printf,
+ .write_graph = annotate_browser__write_graph,
+ };
+ struct annotation_print_data apd = {
+ .he = ab->he,
+ .arch = ab->arch,
+ .evsel = ab->evsel,
+ .dbg = ab->dbg,
+ };
/* The scroll bar isn't being used */
if (!browser->navkeypressed)
- width += 1;
-
- if (!*dl->line)
- ui_browser__write_nstring(browser, " ", width - pcnt_width);
- else if (dl->offset == -1) {
- if (dl->line_nr && annotate_browser__opts.show_linenr)
- printed = scnprintf(bf, sizeof(bf), "%-*d ",
- ab->addr_width + 1, dl->line_nr);
- else
- printed = scnprintf(bf, sizeof(bf), "%*s ",
- ab->addr_width, " ");
- ui_browser__write_nstring(browser, bf, printed);
- ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1);
- } else {
- u64 addr = dl->offset;
- int color = -1;
-
- if (!annotate_browser__opts.use_offset)
- addr += ab->start;
-
- if (!annotate_browser__opts.use_offset) {
- printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
- } else {
- if (bdl->jump_sources) {
- if (annotate_browser__opts.show_nr_jumps) {
- int prev;
- printed = scnprintf(bf, sizeof(bf), "%*d ",
- ab->jumps_width,
- bdl->jump_sources);
- prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
- current_entry);
- ui_browser__write_nstring(browser, bf, printed);
- ui_browser__set_color(browser, prev);
- }
-
- printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
- ab->target_width, addr);
- } else {
- printed = scnprintf(bf, sizeof(bf), "%*s ",
- ab->addr_width, " ");
- }
- }
+ ops.width += 1;
- if (change_color)
- color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
- ui_browser__write_nstring(browser, bf, printed);
- if (change_color)
- ui_browser__set_color(browser, color);
- if (dl->ins.ops && dl->ins.ops->scnprintf) {
- if (ins__is_jump(&dl->ins)) {
- bool fwd = dl->ops.target.offset > dl->offset;
-
- ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
- SLSMG_UARROW_CHAR);
- SLsmg_write_char(' ');
- } else if (ins__is_call(&dl->ins)) {
- ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
- SLsmg_write_char(' ');
- } else if (ins__is_ret(&dl->ins)) {
- ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
- SLsmg_write_char(' ');
- } else {
- ui_browser__write_nstring(browser, " ", 2);
- }
- } else {
- ui_browser__write_nstring(browser, " ", 2);
- }
+ if (!IS_ERR_OR_NULL(ab->type_hash))
+ apd.type_hash = ab->type_hash;
- disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
- ui_browser__write_nstring(browser, bf, width - pcnt_width - 3 - printed);
- }
+ annotation_line__write(al, notes, &ops, &apd);
- if (current_entry)
- ab->selection = dl;
+ if (ops.current_entry)
+ ab->selection = al;
}
-static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
+static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
- if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
- || !disasm_line__has_offset(dl)
- || dl->ops.target.offset < 0
- || dl->ops.target.offset >= (s64)symbol__size(sym))
- return false;
+ struct disasm_line *pos = list_prev_entry(cursor, al.node);
+ const char *name;
+ int diff = 1;
- return true;
+ while (pos && pos->al.offset == -1) {
+ pos = list_prev_entry(pos, al.node);
+ if (!annotate_opts.hide_src_code)
+ diff++;
+ }
+
+ if (!pos)
+ return 0;
+
+ if (ins__is_lock(&pos->ins))
+ name = pos->ops.locked.ins.name;
+ else
+ name = pos->ins.name;
+
+ if (!name || !cursor->ins.name)
+ return 0;
+
+ if (ins__is_fused(ab->arch, name, cursor->ins.name))
+ return diff;
+ return 0;
}
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
{
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
- struct disasm_line *cursor = ab->selection, *target;
- struct browser_disasm_line *btarget, *bcursor;
+ struct disasm_line *cursor = disasm_line(ab->selection);
+ struct annotation_line *target;
unsigned int from, to;
struct map_symbol *ms = ab->b.priv;
struct symbol *sym = ms->sym;
- u8 pcnt_width = annotate_browser__pcnt_width(ab);
+ struct annotation *notes = symbol__annotation(sym);
+ u8 pcnt_width = annotation__pcnt_width(notes);
+ u8 cntr_width = annotation__br_cntr_width();
+ int width;
+ int diff = 0;
/* PLT symbols contain external offsets */
if (strstr(sym->name, "@plt"))
return;
- if (!disasm_line__is_valid_jump(cursor, sym))
+ if (!disasm_line__is_valid_local_jump(cursor, sym))
return;
- target = ab->offsets[cursor->ops.target.offset];
- if (!target)
+ /*
+ * This first was seen with a gcc function, _cpp_lex_token, that
+ * has the usual jumps:
+ *
+ * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92>
+ *
+ * I.e. jumps to a label inside that function (_cpp_lex_token), and
+ * those works, but also this kind:
+ *
+ * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72>
+ *
+ * I.e. jumps to another function, outside _cpp_lex_token, which
+ * are not being correctly handled generating as a side effect references
+ * to ab->offset[] entries that are set to NULL, so to make this code
+ * more robust, check that here.
+ *
+ * A proper fix for will be put in place, looking at the function
+ * name right after the '<' token and probably treating this like a
+ * 'call' instruction.
+ */
+ target = annotated_source__get_line(notes->src, cursor->ops.target.offset);
+ if (target == NULL) {
+ ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
+ cursor->ops.target.offset);
return;
+ }
- 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;
+ if (annotate_opts.hide_src_code) {
+ from = cursor->al.idx_asm;
+ to = target->idx_asm;
} else {
- from = (u64)bcursor->idx;
- to = (u64)btarget->idx;
+ from = (u64)cursor->al.idx;
+ to = (u64)target->idx;
}
+ width = annotation__cycles_width(notes);
+
ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
- __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
+ __ui_browser__line_arrow(browser,
+ pcnt_width + 2 + notes->src->widths.addr + width + cntr_width,
from, to);
+
+ diff = is_fused(ab, cursor);
+ if (diff > 0) {
+ ui_browser__mark_fused(browser,
+ pcnt_width + 3 + notes->src->widths.addr + width + cntr_width,
+ from - diff, diff, to > from);
+ }
}
static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
- struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
+ struct annotation *notes = browser__annotation(browser);
int ret = ui_browser__list_head_refresh(browser);
- int pcnt_width = annotate_browser__pcnt_width(ab);
+ int pcnt_width = annotation__pcnt_width(notes);
- if (annotate_browser__opts.jump_arrows)
+ if (annotate_opts.jump_arrows)
annotate_browser__draw_current_jump(browser);
ui_browser__set_color(browser, HE_COLORSET_NORMAL);
- __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
+ __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
return ret;
}
-static int disasm__cmp(struct browser_disasm_line *a,
- struct browser_disasm_line *b, int nr_pcnt)
+static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
+ int percent_type)
{
int i;
- for (i = 0; i < nr_pcnt; i++) {
- if (a->samples[i].percent == b->samples[i].percent)
+ for (i = 0; i < a->data_nr; i++) {
+ if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
continue;
- return a->samples[i].percent < b->samples[i].percent;
+ return a->data[i].percent[percent_type] -
+ b->data[i].percent[percent_type];
}
return 0;
}
-static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
- int nr_events)
+static void disasm_rb_tree__insert(struct annotate_browser *browser,
+ struct annotation_line *al)
{
+ struct rb_root *root = &browser->entries;
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
- struct browser_disasm_line *l;
+ struct annotation_line *l;
while (*p != NULL) {
parent = *p;
- l = rb_entry(parent, struct browser_disasm_line, rb_node);
+ l = rb_entry(parent, struct annotation_line, rb_node);
- if (disasm__cmp(bdl, l, nr_events))
+ if (disasm__cmp(al, l, annotate_opts.percent_type) < 0)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
- rb_link_node(&bdl->rb_node, parent, p);
- rb_insert_color(&bdl->rb_node, root);
+ rb_link_node(&al->rb_node, parent, p);
+ rb_insert_color(&al->rb_node, root);
}
static void annotate_browser__set_top(struct annotate_browser *browser,
- struct disasm_line *pos, u32 idx)
+ struct annotation_line *pos, u32 idx)
{
unsigned back;
@@ -366,9 +310,9 @@ static void annotate_browser__set_top(struct annotate_browser *browser,
browser->b.top_idx = browser->b.index = idx;
while (browser->b.top_idx != 0 && back != 0) {
- pos = list_entry(pos->node.prev, struct disasm_line, node);
+ pos = list_entry(pos->node.prev, struct annotation_line, node);
- if (disasm_line__filter(&browser->b, &pos->node))
+ if (annotation_line__filter(pos))
continue;
--browser->b.top_idx;
@@ -382,162 +326,293 @@ static void annotate_browser__set_top(struct annotate_browser *browser,
static void annotate_browser__set_rb_top(struct annotate_browser *browser,
struct rb_node *nd)
{
- struct browser_disasm_line *bpos;
- struct disasm_line *pos;
- u32 idx;
+ struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
+ u32 idx = pos->idx;
- 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;
+ if (annotate_opts.hide_src_code)
+ idx = pos->idx_asm;
annotate_browser__set_top(browser, pos, idx);
browser->curr_hot = nd;
}
static void annotate_browser__calc_percent(struct annotate_browser *browser,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
- struct disasm_line *pos, *next;
- s64 len = symbol__size(sym);
+ struct disasm_line *pos;
browser->entries = RB_ROOT;
- pthread_mutex_lock(&notes->lock);
+ annotation__lock(notes);
- list_for_each_entry(pos, &notes->src->source, node) {
- struct browser_disasm_line *bpos = disasm_line__browser(pos);
- const char *path = NULL;
+ symbol__calc_percent(sym, evsel);
+
+ list_for_each_entry(pos, &notes->src->source, al.node) {
double max_percent = 0.0;
int i;
- if (pos->offset == -1) {
- RB_CLEAR_NODE(&bpos->rb_node);
+ if (pos->al.offset == -1) {
+ RB_CLEAR_NODE(&pos->al.rb_node);
continue;
}
- next = disasm__get_next_ip_line(&notes->src->source, pos);
-
- for (i = 0; i < browser->nr_events; i++) {
- u64 nr_samples;
+ for (i = 0; i < pos->al.data_nr; i++) {
+ double percent;
- bpos->samples[i].percent = disasm__calc_percent(notes,
- evsel->idx + i,
- pos->offset,
- next ? next->offset : len,
- &path, &nr_samples);
- bpos->samples[i].nr = nr_samples;
+ percent = annotation_data__percent(&pos->al.data[i],
+ annotate_opts.percent_type);
- if (max_percent < bpos->samples[i].percent)
- max_percent = bpos->samples[i].percent;
+ if (max_percent < percent)
+ max_percent = percent;
}
- if (max_percent < 0.01 && pos->ipc == 0) {
- RB_CLEAR_NODE(&bpos->rb_node);
+ if (max_percent < 0.01 && (!pos->al.cycles || pos->al.cycles->ipc == 0)) {
+ RB_CLEAR_NODE(&pos->al.rb_node);
continue;
}
- disasm_rb_tree__insert(&browser->entries, bpos,
- browser->nr_events);
+ disasm_rb_tree__insert(browser, &pos->al);
}
- pthread_mutex_unlock(&notes->lock);
+ annotation__unlock(notes);
browser->curr_hot = rb_last(&browser->entries);
}
-static bool annotate_browser__toggle_source(struct annotate_browser *browser)
+static struct annotation_line *annotate_browser__find_new_asm_line(
+ struct annotate_browser *browser,
+ int idx_asm)
{
- struct disasm_line *dl;
- struct browser_disasm_line *bdl;
+ struct annotation_line *al;
+ struct list_head *head = browser->b.entries;
+
+ /* find an annotation line in the new list with the same idx_asm */
+ list_for_each_entry(al, head, node) {
+ if (al->idx_asm == idx_asm)
+ return al;
+ }
+
+ /* There are no asm lines */
+ return NULL;
+}
+
+static struct annotation_line *annotate_browser__find_next_asm_line(
+ struct annotate_browser *browser,
+ struct annotation_line *al)
+{
+ struct annotation_line *it = al;
+
+ /* find next asm line */
+ list_for_each_entry_continue(it, browser->b.entries, node) {
+ if (it->idx_asm >= 0)
+ return it;
+ }
+
+ /* no asm line found forwards, try backwards */
+ it = al;
+ list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
+ if (it->idx_asm >= 0)
+ return it;
+ }
+
+ /* There are no asm lines */
+ return NULL;
+}
+
+static bool annotation__has_source(struct annotation *notes)
+{
+ struct annotation_line *al;
+ bool found_asm = false;
+
+ /* Let's skip the first non-asm lines which present regardless of source. */
+ list_for_each_entry(al, &notes->src->source, node) {
+ if (al->offset >= 0) {
+ found_asm = true;
+ break;
+ }
+ }
+
+ if (found_asm) {
+ /* After assembly lines, any line without offset means source. */
+ list_for_each_entry_continue(al, &notes->src->source, node) {
+ if (al->offset == -1)
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool annotate_browser__toggle_source(struct annotate_browser *browser,
+ struct evsel *evsel)
+{
+ struct annotation *notes = browser__annotation(&browser->b);
+ struct annotation_line *al;
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);
+ al = list_entry(browser->b.top, struct annotation_line, node);
+
+ if (!annotate_opts.annotate_src)
+ annotate_opts.annotate_src = true;
+
+ /*
+ * It's about to get source code annotation for the first time.
+ * Drop the existing annotation_lines and get the new one with source.
+ * And then move to the original line at the same asm index.
+ */
+ if (annotate_opts.hide_src_code && !notes->src->tried_source) {
+ struct map_symbol *ms = browser->b.priv;
+ int orig_idx_asm = al->idx_asm;
+
+ /* annotate again with source code info */
+ annotate_opts.hide_src_code = false;
+ annotated_source__purge(notes->src);
+ symbol__annotate2(ms, evsel, &browser->arch);
+ annotate_opts.hide_src_code = true;
+
+ /* should be after annotated_source__purge() */
+ notes->src->tried_source = true;
+
+ if (!annotation__has_source(notes))
+ ui__warning("Annotation has no source code.");
+
+ browser->b.entries = &notes->src->source;
+ al = annotate_browser__find_new_asm_line(browser, orig_idx_asm);
+ if (unlikely(al == NULL)) {
+ al = list_first_entry(&notes->src->source,
+ struct annotation_line, node);
+ }
+ browser->b.seek(&browser->b, al->idx_asm, SEEK_SET);
+ }
- if (annotate_browser__opts.hide_src_code) {
- if (bdl->idx_asm < offset)
- offset = bdl->idx;
+ if (annotate_opts.hide_src_code) {
+ if (al->idx_asm < offset)
+ offset = al->idx;
- browser->b.nr_entries = browser->nr_entries;
- annotate_browser__opts.hide_src_code = false;
+ browser->b.nr_entries = notes->src->nr_entries;
+ annotate_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;
+ browser->b.top_idx = al->idx - offset;
+ browser->b.index = al->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 (al->idx_asm < 0) {
+ /* move cursor to next asm line */
+ al = annotate_browser__find_next_asm_line(browser, al);
+ if (!al) {
+ browser->b.seek(&browser->b, -offset, SEEK_CUR);
+ return false;
+ }
}
- if (bdl->idx_asm < offset)
- offset = bdl->idx_asm;
+ if (al->idx_asm < offset)
+ offset = al->idx_asm;
- browser->b.nr_entries = browser->nr_asm_entries;
- annotate_browser__opts.hide_src_code = true;
+ browser->b.nr_entries = notes->src->nr_asm_entries;
+ annotate_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;
+ browser->b.top_idx = al->idx_asm - offset;
+ browser->b.index = al->idx_asm;
}
+ if (annotate_opts.hide_src_code_on_title)
+ annotate_opts.hide_src_code_on_title = false;
+
return true;
}
-static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
+#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
+
+static void annotate_browser__show_full_location(struct ui_browser *browser)
{
- ui_browser__reset_index(&browser->b);
- browser->b.nr_entries = browser->nr_asm_entries;
+ struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
+ struct disasm_line *cursor = disasm_line(ab->selection);
+ struct annotation_line *al = &cursor->al;
+
+ if (al->offset != -1)
+ ui_helpline__puts("Only available for source code lines.");
+ else if (al->fileloc == NULL)
+ ui_helpline__puts("No source file location.");
+ else {
+ char help_line[SYM_TITLE_MAX_SIZE];
+ sprintf (help_line, "Source file location: %s", al->fileloc);
+ ui_helpline__puts(help_line);
+ }
}
-#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
+static void ui_browser__init_asm_mode(struct ui_browser *browser)
+{
+ struct annotation *notes = browser__annotation(browser);
+ ui_browser__reset_index(browser);
+ browser->nr_entries = notes->src->nr_asm_entries;
+}
static int sym_title(struct symbol *sym, struct map *map, char *title,
- size_t sz)
+ size_t sz, int percent_type)
{
- return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name);
+ return snprintf(title, sz, "%s %s [Percent: %s] %s", sym->name,
+ dso__long_name(map__dso(map)),
+ percent_type_str(percent_type),
+ annotate_opts.code_with_type ? "[Type]" : "");
}
+static void annotate_browser__show_function_title(struct annotate_browser *browser)
+{
+ struct ui_browser *b = &browser->b;
+ struct map_symbol *ms = b->priv;
+ struct symbol *sym = ms->sym;
+ char title[SYM_TITLE_MAX_SIZE];
+
+ sym_title(sym, ms->map, title, sizeof(title), annotate_opts.percent_type);
+
+ ui_browser__gotorc_title(b, 0, 0);
+ ui_browser__set_color(b, HE_COLORSET_ROOT);
+ ui_browser__write_nstring(b, title, b->width + 1);
+}
+
+/*
+ * This can be called from external jumps, i.e. jumps from one function
+ * to another, like from the kernel's entry_SYSCALL_64 function to the
+ * swapgs_restore_regs_and_return_to_usermode() function.
+ *
+ * So all we check here is that dl->ops.target.sym is set, if it is, just
+ * go to that function and when exiting from its disassembly, come back
+ * to the calling function.
+ */
static bool annotate_browser__callq(struct annotate_browser *browser,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt)
{
- struct map_symbol *ms = browser->b.priv;
- struct disasm_line *dl = browser->selection;
+ struct map_symbol *ms = browser->b.priv, target_ms;
+ struct disasm_line *dl = disasm_line(browser->selection);
struct annotation *notes;
- struct addr_map_symbol target = {
- .map = ms->map,
- .addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
- };
- char title[SYM_TITLE_MAX_SIZE];
- if (!ins__is_call(&dl->ins))
- return false;
-
- if (map_groups__find_ams(&target) ||
- map__rip_2objdump(target.map, target.map->map_ip(target.map,
- target.addr)) !=
- dl->ops.target.addr) {
+ if (!dl->ops.target.sym) {
ui_helpline__puts("The called function was not found.");
return true;
}
- notes = symbol__annotation(target.sym);
- pthread_mutex_lock(&notes->lock);
+ notes = symbol__annotation(dl->ops.target.sym);
+ annotation__lock(notes);
- if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
- pthread_mutex_unlock(&notes->lock);
+ if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
+ annotation__unlock(notes);
ui__warning("Not enough memory for annotating '%s' symbol!\n",
- target.sym->name);
+ dl->ops.target.sym->name);
return true;
}
- pthread_mutex_unlock(&notes->lock);
- symbol__tui_annotate(target.sym, target.map, evsel, hbt);
- sym_title(ms->sym, ms->map, title, sizeof(title));
- ui_browser__show_title(&browser->b, title);
+ target_ms.maps = ms->maps;
+ target_ms.map = ms->map;
+ target_ms.sym = dl->ops.target.sym;
+ annotation__unlock(notes);
+ __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_ADDR);
+
+ /*
+ * The annotate_browser above changed the title with the target function
+ * and now it's back to the original function. Refresh the header line
+ * for the original function again.
+ */
+ annotate_browser__show_function_title(browser);
return true;
}
@@ -545,31 +620,36 @@ static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
s64 offset, s64 *idx)
{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes = symbol__annotation(sym);
+ struct annotation *notes = browser__annotation(&browser->b);
struct disasm_line *pos;
*idx = 0;
- list_for_each_entry(pos, &notes->src->source, node) {
- if (pos->offset == offset)
+ list_for_each_entry(pos, &notes->src->source, al.node) {
+ if (pos->al.offset == offset)
return pos;
- if (!disasm_line__filter(&browser->b, &pos->node))
+ if (!annotation_line__filter(&pos->al))
++*idx;
}
return NULL;
}
-static bool annotate_browser__jump(struct annotate_browser *browser)
+static bool annotate_browser__jump(struct annotate_browser *browser,
+ struct evsel *evsel,
+ struct hist_browser_timer *hbt)
{
- struct disasm_line *dl = browser->selection;
+ struct disasm_line *dl = disasm_line(browser->selection);
u64 offset;
s64 idx;
if (!ins__is_jump(&dl->ins))
return false;
+ if (dl->ops.target.outside) {
+ annotate_browser__callq(browser, evsel, hbt);
+ return true;
+ }
+
offset = dl->ops.target.offset;
dl = annotate_browser__find_offset(browser, offset, &idx);
if (dl == NULL) {
@@ -577,29 +657,27 @@ static bool annotate_browser__jump(struct annotate_browser *browser)
return true;
}
- annotate_browser__set_top(browser, dl, idx);
+ annotate_browser__set_top(browser, &dl->al, idx);
return true;
}
static
-struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
+struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
char *s, s64 *idx)
{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes = symbol__annotation(sym);
- struct disasm_line *pos = browser->selection;
+ struct annotation *notes = browser__annotation(&browser->b);
+ struct annotation_line *al = browser->selection;
*idx = browser->b.index;
- list_for_each_entry_continue(pos, &notes->src->source, node) {
- if (disasm_line__filter(&browser->b, &pos->node))
+ list_for_each_entry_continue(al, &notes->src->source, node) {
+ if (annotation_line__filter(al))
continue;
++*idx;
- if (pos->line && strstr(pos->line, s) != NULL)
- return pos;
+ if (al->line && strstr(al->line, s) != NULL)
+ return al;
}
return NULL;
@@ -607,38 +685,36 @@ struct disasm_line *annotate_browser__find_string(struct annotate_browser *brows
static bool __annotate_browser__search(struct annotate_browser *browser)
{
- struct disasm_line *dl;
+ struct annotation_line *al;
s64 idx;
- dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
- if (dl == NULL) {
+ al = annotate_browser__find_string(browser, browser->search_bf, &idx);
+ if (al == NULL) {
ui_helpline__puts("String not found!");
return false;
}
- annotate_browser__set_top(browser, dl, idx);
+ annotate_browser__set_top(browser, al, idx);
browser->searching_backwards = false;
return true;
}
static
-struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
+struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
char *s, s64 *idx)
{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes = symbol__annotation(sym);
- struct disasm_line *pos = browser->selection;
+ struct annotation *notes = browser__annotation(&browser->b);
+ struct annotation_line *al = browser->selection;
*idx = browser->b.index;
- list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
- if (disasm_line__filter(&browser->b, &pos->node))
+ list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
+ if (annotation_line__filter(al))
continue;
--*idx;
- if (pos->line && strstr(pos->line, s) != NULL)
- return pos;
+ if (al->line && strstr(al->line, s) != NULL)
+ return al;
}
return NULL;
@@ -646,16 +722,16 @@ struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browse
static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
- struct disasm_line *dl;
+ struct annotation_line *al;
s64 idx;
- dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
- if (dl == NULL) {
+ al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
+ if (al == NULL) {
ui_helpline__puts("String not found!");
return false;
}
- annotate_browser__set_top(browser, dl, idx);
+ annotate_browser__set_top(browser, al, idx);
browser->searching_backwards = true;
return true;
}
@@ -708,37 +784,122 @@ bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
return __annotate_browser__search_reverse(browser);
}
-static void annotate_browser__update_addr_width(struct annotate_browser *browser)
+static int annotate_browser__show(struct annotate_browser *browser, char *title, const char *help)
{
- if (annotate_browser__opts.use_offset)
- browser->target_width = browser->min_addr_width;
- else
- browser->target_width = browser->max_addr_width;
+ if (ui_browser__show(&browser->b, title, help) < 0)
+ return -1;
+
+ annotate_browser__show_function_title(browser);
+ return 0;
+}
+
+static void
+switch_percent_type(struct annotation_options *opts, bool base)
+{
+ switch (opts->percent_type) {
+ case PERCENT_HITS_LOCAL:
+ if (base)
+ opts->percent_type = PERCENT_PERIOD_LOCAL;
+ else
+ opts->percent_type = PERCENT_HITS_GLOBAL;
+ break;
+ case PERCENT_HITS_GLOBAL:
+ if (base)
+ opts->percent_type = PERCENT_PERIOD_GLOBAL;
+ else
+ opts->percent_type = PERCENT_HITS_LOCAL;
+ break;
+ case PERCENT_PERIOD_LOCAL:
+ if (base)
+ opts->percent_type = PERCENT_HITS_LOCAL;
+ else
+ opts->percent_type = PERCENT_PERIOD_GLOBAL;
+ break;
+ case PERCENT_PERIOD_GLOBAL:
+ if (base)
+ opts->percent_type = PERCENT_HITS_GLOBAL;
+ else
+ opts->percent_type = PERCENT_PERIOD_LOCAL;
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+
+static int annotate__scnprintf_title(struct hists *hists, char *bf, size_t size)
+{
+ int printed = hists__scnprintf_title(hists, bf, size);
- browser->addr_width = browser->target_width;
+ if (!annotate_opts.hide_src_code_on_title) {
+ printed += scnprintf(bf + printed, size - printed, " [source: %s]",
+ annotate_opts.hide_src_code ? "OFF" : "On");
+ }
- if (annotate_browser__opts.show_nr_jumps)
- browser->addr_width += browser->jumps_width + 1;
+ return printed;
+}
+
+static void annotate_browser__debuginfo_warning(struct annotate_browser *browser)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct dso *dso = map__dso(ms->map);
+
+ if (browser->dbg == NULL && annotate_opts.code_with_type &&
+ !dso__debuginfo_warned(dso)) {
+ ui__warning("DWARF debuginfo not found.\n\n"
+ "Data-type in this DSO will not be displayed.\n"
+ "Please make sure to have debug information.");
+ dso__set_debuginfo_warned(dso);
+ }
+}
+
+static s64 annotate_browser__curr_hot_offset(struct annotate_browser *browser)
+{
+ struct annotation_line *al = NULL;
+
+ if (browser->curr_hot)
+ al = rb_entry(browser->curr_hot, struct annotation_line, rb_node);
+
+ return al ? al->offset : 0;
+}
+
+static void annotate_browser__symbol_annotate_error(struct annotate_browser *browser, int err)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct dso *dso = map__dso(ms->map);
+ char msg[BUFSIZ];
+
+ dso__set_annotate_warned(dso);
+ symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
+ ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
}
static int annotate_browser__run(struct annotate_browser *browser,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt)
{
struct rb_node *nd = NULL;
+ struct hists *hists = evsel__hists(evsel);
struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym;
+ struct annotation *notes = symbol__annotation(ms->sym);
const char *help = "Press 'h' for help on key bindings";
int delay_secs = hbt ? hbt->refresh : 0;
+ char *br_cntr_text = NULL;
+ char title[256];
int key;
- char title[SYM_TITLE_MAX_SIZE];
- sym_title(sym, ms->map, title, sizeof(title));
- if (ui_browser__show(&browser->b, title, help) < 0)
+ annotate__scnprintf_title(hists, title, sizeof(title));
+ if (annotate_browser__show(browser, title, help) < 0)
return -1;
annotate_browser__calc_percent(browser, evsel);
+ if (browser->selection != NULL) {
+ browser->curr_hot = &browser->selection->rb_node;
+ browser->b.use_navkeypressed = false;
+ }
+
if (browser->curr_hot) {
annotate_browser__set_rb_top(browser, browser->curr_hot);
browser->b.navkeypressed = false;
@@ -746,6 +907,10 @@ static int annotate_browser__run(struct annotate_browser *browser,
nd = browser->curr_hot;
+ annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false);
+
+ annotate_browser__debuginfo_warning(browser);
+
while (1) {
key = ui_browser__run(&browser->b, delay_secs);
@@ -765,8 +930,11 @@ static int annotate_browser__run(struct annotate_browser *browser,
if (hbt)
hbt->timer(hbt->arg);
- if (delay_secs != 0)
- symbol__annotate_decay_histogram(sym, evsel->idx);
+ if (delay_secs != 0) {
+ symbol__annotate_decay_histogram(sym, evsel);
+ annotate__scnprintf_title(hists, title, sizeof(title));
+ annotate_browser__show(browser, title, help);
+ }
continue;
case K_TAB:
if (nd != NULL) {
@@ -789,47 +957,76 @@ static int annotate_browser__run(struct annotate_browser *browser,
ui_browser__help_window(&browser->b,
"UP/DOWN/PGUP\n"
"PGDN/SPACE Navigate\n"
+ "</> Move to prev/next symbol\n"
"q/ESC/CTRL+C Exit\n\n"
"ENTER Go to target\n"
- "ESC Exit\n"
- "H Cycle thru hottest instructions\n"
+ "H Go to hottest instruction\n"
+ "TAB/shift+TAB Cycle thru hottest instructions\n"
"j Toggle showing jump to target arrows\n"
"J Toggle showing number of jump sources on targets\n"
"n Search next string\n"
"o Toggle disassembler output/simplified view\n"
+ "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
"s Toggle source code view\n"
- "t Toggle total period view\n"
+ "t Circulate percent, total period, samples view\n"
+ "c Show min/max cycle\n"
"/ Search string\n"
"k Toggle line numbers\n"
+ "l Show full source file location\n"
+ "P Print to [symbol_name].annotation file.\n"
"r Run available scripts\n"
- "? Search string backwards\n");
+ "p Toggle percent type [local/global]\n"
+ "b Toggle percent base [period/hits]\n"
+ "B Branch counter abbr list (Optional)\n"
+ "? Search string backwards\n"
+ "f Toggle showing offsets to full address\n"
+ "T Toggle data type display\n");
continue;
case 'r':
- {
- script_browse(NULL);
- continue;
- }
+ script_browse(NULL, NULL);
+ annotate_browser__show(browser, title, help);
+ continue;
case 'k':
- annotate_browser__opts.show_linenr =
- !annotate_browser__opts.show_linenr;
- break;
+ annotate_opts.show_linenr = !annotate_opts.show_linenr;
+ continue;
+ case 'l':
+ annotate_browser__show_full_location (&browser->b);
+ continue;
case 'H':
nd = browser->curr_hot;
break;
- case 's':
- if (annotate_browser__toggle_source(browser))
+ case 's': {
+ struct annotation_line *al = NULL;
+ s64 offset = annotate_browser__curr_hot_offset(browser);
+
+ if (annotate_browser__toggle_source(browser, evsel))
ui_helpline__puts(help);
+
+ /* Update the annotation browser's rb_tree, and reset the nd */
+ annotate_browser__calc_percent(browser, evsel);
+ /* Try to find the same asm line as before */
+ al = annotated_source__get_line(notes->src, offset);
+ browser->curr_hot = al ? &al->rb_node : NULL;
+ nd = browser->curr_hot;
+
+ annotate__scnprintf_title(hists, title, sizeof(title));
+ annotate_browser__show(browser, title, help);
continue;
+ }
case 'o':
- annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
- annotate_browser__update_addr_width(browser);
+ annotate_opts.use_offset = !annotate_opts.use_offset;
+ annotation__update_column_widths(notes);
+ continue;
+ case 'O':
+ if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
+ annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
continue;
case 'j':
- annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
+ annotate_opts.jump_arrows = !annotate_opts.jump_arrows;
continue;
case 'J':
- annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
- annotate_browser__update_addr_width(browser);
+ annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps;
+ annotation__update_column_widths(notes);
continue;
case '/':
if (annotate_browser__search(browser, delay_secs)) {
@@ -855,36 +1052,86 @@ show_help:
browser->b.height,
browser->b.index,
browser->b.top_idx,
- browser->nr_asm_entries);
+ notes->src->nr_asm_entries);
}
continue;
case K_ENTER:
case K_RIGHT:
+ {
+ struct disasm_line *dl = disasm_line(browser->selection);
+
if (browser->selection == NULL)
ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
else if (browser->selection->offset == -1)
ui_helpline__puts("Actions are only available for assembly lines.");
- else if (!browser->selection->ins.ops)
+ else if (!dl->ins.ops)
goto show_sup_ins;
- else if (ins__is_ret(&browser->selection->ins))
+ else if (ins__is_ret(&dl->ins))
goto out;
- else if (!(annotate_browser__jump(browser) ||
+ else if (!(annotate_browser__jump(browser, evsel, hbt) ||
annotate_browser__callq(browser, evsel, hbt))) {
show_sup_ins:
ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
}
continue;
+ }
+ case 'P':
+ map_symbol__annotation_dump(ms, evsel, browser->he);
+ continue;
case 't':
- annotate_browser__opts.show_total_period =
- !annotate_browser__opts.show_total_period;
- annotate_browser__update_addr_width(browser);
+ if (symbol_conf.show_total_period) {
+ symbol_conf.show_total_period = false;
+ symbol_conf.show_nr_samples = true;
+ } else if (symbol_conf.show_nr_samples)
+ symbol_conf.show_nr_samples = false;
+ else
+ symbol_conf.show_total_period = true;
+ annotation__update_column_widths(notes);
+ continue;
+ case 'c':
+ if (annotate_opts.show_minmax_cycle)
+ annotate_opts.show_minmax_cycle = false;
+ else
+ annotate_opts.show_minmax_cycle = true;
+ annotation__update_column_widths(notes);
+ continue;
+ case 'p':
+ case 'b':
+ switch_percent_type(&annotate_opts, key == 'b');
+ annotate__scnprintf_title(hists, title, sizeof(title));
+ annotate_browser__show(browser, title, help);
+ continue;
+ case 'B':
+ if (br_cntr_text)
+ ui_browser__help_window(&browser->b, br_cntr_text);
+ else {
+ ui_browser__help_window(&browser->b,
+ "\n The branch counter is not available.\n");
+ }
+ continue;
+ case 'f':
+ annotation__toggle_full_addr(notes, ms);
+ continue;
+ case 'T':
+ annotate_opts.code_with_type ^= 1;
+ if (browser->dbg == NULL)
+ browser->dbg = dso__debuginfo(map__dso(ms->map));
+ if (browser->type_hash == NULL) {
+ browser->type_hash = hashmap__new(type_hash, type_equal,
+ /*ctx=*/NULL);
+ }
+ annotate_browser__show(browser, title, help);
+ annotate_browser__debuginfo_warning(browser);
continue;
case K_LEFT:
+ case '<':
+ case '>':
case K_ESC:
case 'q':
case CTRL('c'):
goto out;
default:
+ ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions");
continue;
}
@@ -893,298 +1140,130 @@ show_sup_ins:
}
out:
ui_browser__hide(&browser->b);
+ free(br_cntr_text);
return key;
}
-int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
- struct hist_browser_timer *hbt)
-{
- /* Set default value for show_total_period. */
- annotate_browser__opts.show_total_period =
- symbol_conf.show_total_period;
-
- return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
-}
-
-int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
- struct hist_browser_timer *hbt)
+int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
+ struct hist_browser_timer *hbt, u64 al_addr)
{
/* reset abort key so that it can get Ctrl-C as a key */
SLang_reset_tty();
SLang_init_tty(0, 0, 0);
+ SLtty_set_suspend_state(true);
- return map_symbol__tui_annotate(&he->ms, evsel, hbt);
-}
-
-
-static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
-{
- unsigned n_insn = 0;
- u64 offset;
-
- for (offset = start; offset <= end; offset++) {
- if (browser->offsets[offset])
- n_insn++;
- }
- return n_insn;
+ return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, al_addr);
}
-static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
- struct cyc_hist *ch)
+int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
+ struct evsel *evsel,
+ struct hist_browser_timer *hbt, u64 al_addr)
{
- unsigned n_insn;
- u64 offset;
-
- n_insn = count_insn(browser, start, end);
- if (n_insn && ch->num && ch->cycles) {
- float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
-
- /* Hide data when there are too many overlaps. */
- if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
- return;
-
- for (offset = start; offset <= end; offset++) {
- struct disasm_line *dl = browser->offsets[offset];
-
- if (dl)
- dl->ipc = ipc;
- }
- }
-}
-
-/*
- * This should probably be in util/annotate.c to share with the tty
- * annotate, but right now we need the per byte offsets arrays,
- * which are only here.
- */
-static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
- struct symbol *sym)
-{
- u64 offset;
- struct annotation *notes = symbol__annotation(sym);
-
- if (!notes->src || !notes->src->cycles_hist)
- return;
-
- pthread_mutex_lock(&notes->lock);
- for (offset = 0; offset < size; ++offset) {
- struct cyc_hist *ch;
-
- ch = &notes->src->cycles_hist[offset];
- if (ch && ch->cycles) {
- struct disasm_line *dl;
-
- if (ch->have_start)
- count_and_fill(browser, ch->start, offset, ch);
- dl = browser->offsets[offset];
- if (dl && ch->num_aggr)
- dl->cycles = ch->cycles_aggr / ch->num_aggr;
- browser->have_cycles = true;
- }
- }
- pthread_mutex_unlock(&notes->lock);
-}
-
-static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
- size_t size)
-{
- u64 offset;
- struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym;
-
- /* PLT symbols contain external offsets */
- if (strstr(sym->name, "@plt"))
- return;
-
- for (offset = 0; offset < size; ++offset) {
- struct disasm_line *dl = browser->offsets[offset], *dlt;
- struct browser_disasm_line *bdlt;
-
- if (!disasm_line__is_valid_jump(dl, sym))
- continue;
-
- dlt = browser->offsets[dl->ops.target.offset];
- /*
- * FIXME: Oops, no jump target? Buggy disassembler? Or do we
- * have to adjust to the previous offset?
- */
- if (dlt == NULL)
- continue;
-
- bdlt = disasm_line__browser(dlt);
- if (++bdlt->jump_sources > browser->max_jump_sources)
- browser->max_jump_sources = bdlt->jump_sources;
-
- ++browser->nr_jumps;
- }
-}
-
-static inline int width_jumps(int n)
-{
- if (n >= 100)
- return 5;
- if (n / 10)
- return 2;
- return 1;
-}
-
-int symbol__tui_annotate(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel,
- struct hist_browser_timer *hbt)
-{
- struct disasm_line *pos, *n;
- struct annotation *notes;
- size_t size;
- struct map_symbol ms = {
- .map = map,
- .sym = sym,
- };
+ struct annotation *notes = symbol__annotation(sym);
struct annotate_browser browser = {
.b = {
.refresh = annotate_browser__refresh,
.seek = ui_browser__list_head_seek,
.write = annotate_browser__write,
.filter = disasm_line__filter,
- .priv = &ms,
+ .extra_title_lines = 1, /* for hists__scnprintf_title() */
+ .priv = ms,
.use_navkeypressed = true,
},
+ .he = he,
+ .evsel = evsel,
};
+ struct dso *dso;
int ret = -1, err;
- int nr_pcnt = 1;
- size_t sizeof_bdl = sizeof(struct browser_disasm_line);
+ int not_annotated = list_empty(&notes->src->source);
if (sym == NULL)
return -1;
- size = symbol__size(sym);
-
- if (map->dso->annotate_warned)
+ dso = map__dso(ms->map);
+ if (dso__annotate_warned(dso))
return -1;
- browser.offsets = zalloc(size * sizeof(struct disasm_line *));
- if (browser.offsets == NULL) {
- ui__error("Not enough memory!");
- return -1;
- }
+ if (not_annotated || !sym->annotate2) {
+ err = symbol__annotate2(ms, evsel, &browser.arch);
+ if (err) {
+ annotate_browser__symbol_annotate_error(&browser, err);
+ return -1;
+ }
- if (perf_evsel__is_group_event(evsel)) {
- nr_pcnt = evsel->nr_members;
- sizeof_bdl += sizeof(struct disasm_line_samples) *
- (nr_pcnt - 1);
+ if (!annotate_opts.hide_src_code) {
+ notes->src->tried_source = true;
+ if (!annotation__has_source(notes))
+ ui__warning("Annotation has no source code.");
+ }
+ } else {
+ err = evsel__get_arch(evsel, &browser.arch);
+ if (err) {
+ annotate_browser__symbol_annotate_error(&browser, err);
+ return -1;
+ }
}
- err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
- sizeof_bdl, &browser.arch);
- if (err) {
- char msg[BUFSIZ];
- symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
- goto out_free_offsets;
+ /* Copy necessary information when it's called from perf top */
+ if (hbt != NULL && he != &annotate_he) {
+ annotate_he.hists = he->hists;
+ annotate_he.thread = thread__get(he->thread);
+ annotate_he.cpumode = he->cpumode;
+ map_symbol__copy(&annotate_he.ms, ms);
+
+ browser.he = &annotate_he;
}
ui_helpline__push("Press ESC to exit");
- notes = symbol__annotation(sym);
- browser.start = map__rip_2objdump(map, sym->start);
-
- list_for_each_entry(pos, &notes->src->source, node) {
- struct browser_disasm_line *bpos;
- size_t line_len = strlen(pos->line);
-
- if (browser.b.width < line_len)
- browser.b.width = line_len;
- bpos = disasm_line__browser(pos);
- bpos->idx = browser.nr_entries++;
- if (pos->offset != -1) {
- bpos->idx_asm = browser.nr_asm_entries++;
- /*
- * FIXME: short term bandaid to cope with assembly
- * routines that comes with labels in the same column
- * as the address in objdump, sigh.
- *
- * E.g. copy_user_generic_unrolled
- */
- if (pos->offset < (s64)size)
- browser.offsets[pos->offset] = pos;
- } else
- bpos->idx_asm = -1;
+ if (annotate_opts.code_with_type) {
+ browser.dbg = dso__debuginfo(dso);
+ browser.type_hash = hashmap__new(type_hash, type_equal, /*ctx=*/NULL);
}
- annotate_browser__mark_jump_targets(&browser, size);
- annotate__compute_ipc(&browser, size, sym);
-
- browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
- browser.max_addr_width = hex_width(sym->end);
- browser.jumps_width = width_jumps(browser.max_jump_sources);
- browser.nr_events = nr_pcnt;
- browser.b.nr_entries = browser.nr_entries;
- browser.b.entries = &notes->src->source,
+ browser.b.width = notes->src->widths.max_line_len;
+ browser.b.nr_entries = notes->src->nr_entries;
+ 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 (annotate_opts.hide_src_code)
+ ui_browser__init_asm_mode(&browser.b);
- annotate_browser__update_addr_width(&browser);
+ /*
+ * If al_addr is set, it means that there should be a line
+ * intentionally selected, not based on the percentages
+ * which caculated by the event sampling. In this case, we
+ * convey this information into the browser selection, where
+ * the selection in other cases should be empty.
+ */
+ if (al_addr != NO_ADDR) {
+ struct annotation_line *al = annotated_source__get_line(notes->src,
+ al_addr - sym->start);
- ret = annotate_browser__run(&browser, evsel, hbt);
- list_for_each_entry_safe(pos, n, &notes->src->source, node) {
- list_del(&pos->node);
- disasm_line__free(pos);
+ browser.selection = al;
}
-out_free_offsets:
- free(browser.offsets);
- return ret;
-}
-
-#define ANNOTATE_CFG(n) \
- { .name = #n, .value = &annotate_browser__opts.n, }
-
-/*
- * Keep the entries sorted, they are bsearch'ed
- */
-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),
- ANNOTATE_CFG(show_total_period),
- ANNOTATE_CFG(use_offset),
-};
-
-#undef ANNOTATE_CFG
+ ret = annotate_browser__run(&browser, evsel, hbt);
-static int annotate_config__cmp(const void *name, const void *cfgp)
-{
- const struct annotate_config *cfg = cfgp;
+ debuginfo__delete(browser.dbg);
- return strcmp(name, cfg->name);
-}
+ if (!IS_ERR_OR_NULL(browser.type_hash)) {
+ struct hashmap_entry *cur;
+ size_t bkt;
-static int annotate__config(const char *var, const char *value,
- void *data __maybe_unused)
-{
- struct annotate_config *cfg;
- const char *name;
-
- if (prefixcmp(var, "annotate.") != 0)
- return 0;
+ hashmap__for_each_entry(browser.type_hash, cur, bkt)
+ zfree(&cur->pvalue);
+ hashmap__free(browser.type_hash);
+ }
- name = var + 9;
- cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
- sizeof(struct annotate_config), annotate_config__cmp);
+ if (not_annotated && !notes->src->tried_source)
+ annotated_source__purge(notes->src);
- if (cfg == NULL)
- ui__warning("%s variable unknown, ignoring...", var);
- else
- *cfg->value = perf_config_bool(name, value);
- return 0;
-}
+ if (hbt != NULL && he != &annotate_he) {
+ thread__zput(annotate_he.thread);
+ map_symbol__exit(&annotate_he.ms);
+ }
-void annotate_browser__init(void)
-{
- perf_config(annotate__config, NULL);
+ return ret;
}
diff --git a/tools/perf/ui/browsers/header.c b/tools/perf/ui/browsers/header.c
index e2c9390ff4c5..5b5ca32e3eef 100644
--- a/tools/perf/ui/browsers/header.c
+++ b/tools/perf/ui/browsers/header.c
@@ -1,5 +1,4 @@
-#include "util/cache.h"
-#include "util/debug.h"
+// SPDX-License-Identifier: GPL-2.0
#include "ui/browser.h"
#include "ui/keysyms.h"
#include "ui/ui.h"
@@ -34,7 +33,7 @@ static int list_menu__run(struct ui_browser *menu)
{
int key;
unsigned long offset;
- const char help[] =
+ static const char help[] =
"h/?/F1 Show this window\n"
"UP/DOWN/PGUP\n"
"PGDN/SPACE\n"
@@ -70,6 +69,7 @@ static int list_menu__run(struct ui_browser *menu)
key = -1;
break;
default:
+ ui_browser__warn_unhandled_hotkey(menu, key, 0, ", use 'h'/'?'/F1 to see actions");
continue;
}
@@ -93,16 +93,14 @@ static int ui__list_menu(int argc, char * const argv[])
return list_menu__run(&menu);
}
-int tui__header_window(struct perf_env *env)
+int tui__header_window(struct perf_session *session)
{
int i, argc = 0;
char **argv;
- struct perf_session *session;
char *ptr, *pos;
size_t size;
FILE *fp = open_memstream(&ptr, &size);
- session = container_of(env, struct perf_session, header.env);
perf_header__fprintf_info(session, fp, true);
fclose(fp);
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 69f4570bd4f9..08fecbe28a52 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
@@ -5,16 +6,30 @@
#include <stdlib.h>
#include <string.h>
#include <linux/rbtree.h>
+#include <linux/string.h>
#include <sys/ttydefaults.h>
+#include <linux/time64.h>
+#include <linux/zalloc.h>
+#include "../../util/debug.h"
+#include "../../util/dso.h"
+#include "../../util/callchain.h"
#include "../../util/evsel.h"
#include "../../util/evlist.h"
+#include "../../util/header.h"
#include "../../util/hist.h"
+#include "../../util/machine.h"
+#include "../../util/map.h"
+#include "../../util/maps.h"
+#include "../../util/symbol.h"
+#include "../../util/map_symbol.h"
+#include "../../util/branch.h"
#include "../../util/pstack.h"
#include "../../util/sort.h"
-#include "../../util/util.h"
#include "../../util/top.h"
#include "../../util/thread.h"
+#include "../../util/block-info.h"
+#include "../../util/util.h"
#include "../../arch/common.h"
#include "../browsers/hists.h"
@@ -23,16 +38,17 @@
#include "../ui.h"
#include "map.h"
#include "annotate.h"
+#include "annotate-data.h"
#include "srcline.h"
#include "string2.h"
#include "units.h"
+#include "time-utils.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
extern void hist_browser__init_hpp(void);
-static int perf_evsel_browser_title(struct hist_browser *browser,
- char *bf, size_t size);
+static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
static void hist_browser__update_nr_entries(struct hist_browser *hb);
static struct rb_node *hists__filter_entries(struct rb_node *nd,
@@ -49,7 +65,7 @@ static int hist_browser__get_folding(struct hist_browser *browser)
struct hists *hists = browser->hists;
int unfolded_rows = 0;
- for (nd = rb_first(&hists->entries);
+ for (nd = rb_first_cached(&hists->entries);
(nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
nd = rb_hierarchy_next(nd)) {
struct hist_entry *he =
@@ -61,6 +77,15 @@ static int hist_browser__get_folding(struct hist_browser *browser)
return unfolded_rows;
}
+static void hist_browser__set_title_space(struct hist_browser *hb)
+{
+ struct ui_browser *browser = &hb->b;
+ struct hists *hists = hb->hists;
+ struct perf_hpp_list *hpp_list = hists->hpp_list;
+
+ browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
+}
+
static u32 hist_browser__nr_entries(struct hist_browser *hb)
{
u32 nr_entries;
@@ -81,13 +106,19 @@ static void hist_browser__update_rows(struct hist_browser *hb)
struct ui_browser *browser = &hb->b;
struct hists *hists = hb->hists;
struct perf_hpp_list *hpp_list = hists->hpp_list;
- u16 header_offset, index_row;
+ u16 index_row;
+
+ if (!hb->show_headers) {
+ browser->rows += browser->extra_title_lines;
+ browser->extra_title_lines = 0;
+ return;
+ }
- header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
- browser->rows = browser->height - header_offset;
+ browser->extra_title_lines = hpp_list->nr_header_lines;
+ browser->rows -= browser->extra_title_lines;
/*
* Verify if we were at the last line and that line isn't
- * visibe because we now show the header line(s).
+ * visible because we now show the header line(s).
*/
index_row = browser->index - browser->top_idx;
if (index_row >= browser->rows)
@@ -107,17 +138,6 @@ static void hist_browser__refresh_dimensions(struct ui_browser *browser)
* changeset.
*/
ui_browser__refresh_dimensions(browser);
- hist_browser__update_rows(hb);
-}
-
-static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
-{
- struct hists *hists = browser->hists;
- struct perf_hpp_list *hpp_list = hists->hpp_list;
- u16 header_offset;
-
- header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
- ui_browser__gotorc(&browser->b, row + header_offset, column);
}
static void hist_browser__reset(struct hist_browser *browser)
@@ -154,60 +174,9 @@ static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
cl->unfolded = unfold ? cl->has_children : false;
}
-static struct inline_node *inline_node__create(struct map *map, u64 ip)
-{
- struct dso *dso;
- struct inline_node *node;
-
- if (map == NULL)
- return NULL;
-
- dso = map->dso;
- if (dso == NULL)
- return NULL;
-
- if (dso->kernel != DSO_TYPE_USER)
- return NULL;
-
- node = dso__parse_addr_inlines(dso,
- map__rip_2objdump(map, ip));
-
- return node;
-}
-
-static int inline__count_rows(struct inline_node *node)
-{
- struct inline_list *ilist;
- int i = 0;
-
- if (node == NULL)
- return 0;
-
- list_for_each_entry(ilist, &node->val, list) {
- if ((ilist->filename != NULL) || (ilist->funcname != NULL))
- i++;
- }
-
- return i;
-}
-
-static int callchain_list__inline_rows(struct callchain_list *chain)
-{
- struct inline_node *node;
- int rows;
-
- node = inline_node__create(chain->ms.map, chain->ip);
- if (node == NULL)
- return 0;
-
- rows = inline__count_rows(node);
- inline_node__delete(node);
- return rows;
-}
-
static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
{
- int n = 0, inline_rows;
+ int n = 0;
struct rb_node *nd;
for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
@@ -218,12 +187,6 @@ static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
list_for_each_entry(chain, &child->val, list) {
++n;
- if (symbol_conf.inline_name) {
- inline_rows =
- callchain_list__inline_rows(chain);
- n += inline_rows;
- }
-
/* We need this because we may not have children */
folded_sign = callchain_list__folded(chain);
if (folded_sign == '+')
@@ -275,7 +238,7 @@ static int callchain_node__count_rows(struct callchain_node *node)
{
struct callchain_list *chain;
bool unfolded = false;
- int n = 0, inline_rows;
+ int n = 0;
if (callchain_param.mode == CHAIN_FLAT)
return callchain_node__count_flat_rows(node);
@@ -284,10 +247,6 @@ static int callchain_node__count_rows(struct callchain_node *node)
list_for_each_entry(chain, &node->val, list) {
++n;
- if (symbol_conf.inline_name) {
- inline_rows = callchain_list__inline_rows(chain);
- n += inline_rows;
- }
unfolded = chain->unfolded;
}
@@ -324,7 +283,7 @@ static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
if (he->has_no_entry)
return 1;
- node = rb_first(&he->hroot_out);
+ node = rb_first_cached(&he->hroot_out);
while (node) {
float percent;
@@ -429,23 +388,56 @@ static void hist_entry__init_have_children(struct hist_entry *he)
he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
callchain__init_have_children(&he->sorted_chain);
} else {
- he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
+ he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
}
he->init_have_children = true;
}
-static void hist_entry_init_inline_node(struct hist_entry *he)
+static bool hist_browser__selection_has_children(struct hist_browser *browser)
{
- if (he->inline_node)
- return;
+ struct hist_entry *he = browser->he_selection;
+ struct map_symbol *ms = browser->selection;
- he->inline_node = inline_node__create(he->ms.map, he->ip);
+ if (!he || !ms)
+ return false;
- if (he->inline_node == NULL)
- return;
+ if (ms == &he->ms)
+ return he->has_children;
+
+ return container_of(ms, struct callchain_list, ms)->has_children;
+}
+
+static bool hist_browser__selection_unfolded(struct hist_browser *browser)
+{
+ struct hist_entry *he = browser->he_selection;
+ struct map_symbol *ms = browser->selection;
+
+ if (!he || !ms)
+ return false;
+
+ if (ms == &he->ms)
+ return he->unfolded;
+
+ return container_of(ms, struct callchain_list, ms)->unfolded;
+}
+
+static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
+{
+ struct hist_entry *he = browser->he_selection;
+ struct map_symbol *ms = browser->selection;
+ struct callchain_list *callchain_entry;
+
+ if (!he || !ms)
+ return NULL;
- he->has_children = true;
+ if (ms == &he->ms) {
+ hist_entry__sym_snprintf(he, bf, size, 0);
+ return bf + 4; // skip the level, e.g. '[k] '
+ }
+
+ callchain_entry = container_of(ms, struct callchain_list, ms);
+ return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
}
static bool hist_browser__toggle_fold(struct hist_browser *browser)
@@ -479,12 +471,8 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser)
if (he->unfolded) {
if (he->leaf)
- if (he->inline_node)
- he->nr_rows = inline__count_rows(
- he->inline_node);
- else
- he->nr_rows = callchain__count_rows(
- &he->sorted_chain);
+ he->nr_rows = callchain__count_rows(
+ &he->sorted_chain);
else
he->nr_rows = hierarchy_count_rows(browser, he, false);
@@ -582,7 +570,7 @@ static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
struct hist_entry *child;
int n = 0;
- for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
+ for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
child = rb_entry(nd, struct hist_entry, rb_node);
percent = hist_entry__get_percent_limit(child);
if (!child->filtered && percent >= hb->min_pcnt)
@@ -592,8 +580,8 @@ static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
return n;
}
-static void __hist_entry__set_folding(struct hist_entry *he,
- struct hist_browser *hb, bool unfold)
+static void hist_entry__set_folding(struct hist_entry *he,
+ struct hist_browser *hb, bool unfold)
{
hist_entry__init_have_children(he);
he->unfolded = unfold ? he->has_children : false;
@@ -611,36 +599,14 @@ static void __hist_entry__set_folding(struct hist_entry *he,
he->nr_rows = 0;
}
-static void hist_entry__set_folding(struct hist_entry *he,
- struct hist_browser *browser, bool unfold)
-{
- double percent;
-
- percent = hist_entry__get_percent_limit(he);
- if (he->filtered || percent < browser->min_pcnt)
- return;
-
- __hist_entry__set_folding(he, browser, unfold);
-
- if (!he->depth || unfold)
- browser->nr_hierarchy_entries++;
- if (he->leaf)
- browser->nr_callchain_rows += he->nr_rows;
- else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
- browser->nr_hierarchy_entries++;
- he->has_no_entry = true;
- he->nr_rows = 1;
- } else
- he->has_no_entry = false;
-}
-
static void
__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
{
struct rb_node *nd;
struct hist_entry *he;
+ double percent;
- nd = rb_first(&browser->hists->entries);
+ nd = rb_first_cached(&browser->hists->entries);
while (nd) {
he = rb_entry(nd, struct hist_entry, rb_node);
@@ -648,6 +614,21 @@ __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
hist_entry__set_folding(he, browser, unfold);
+
+ percent = hist_entry__get_percent_limit(he);
+ if (he->filtered || percent < browser->min_pcnt)
+ continue;
+
+ if (!he->depth || unfold)
+ browser->nr_hierarchy_entries++;
+ if (he->leaf)
+ browser->nr_callchain_rows += he->nr_rows;
+ else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
+ browser->nr_hierarchy_entries++;
+ he->has_no_entry = true;
+ he->nr_rows = 1;
+ } else
+ he->has_no_entry = false;
}
}
@@ -667,8 +648,10 @@ static void hist_browser__set_folding_selected(struct hist_browser *browser, boo
if (!browser->he_selection)
return;
- hist_entry__set_folding(browser->he_selection, browser, unfold);
- browser->b.nr_entries = hist_browser__nr_entries(browser);
+ if (unfold == browser->he_selection->unfolded)
+ return;
+
+ hist_browser__toggle_fold(browser);
}
static void ui_browser__warn_lost_events(struct ui_browser *browser)
@@ -685,9 +668,82 @@ static int hist_browser__title(struct hist_browser *browser, char *bf, size_t si
return browser->title ? browser->title(browser, bf, size) : 0;
}
-int hist_browser__run(struct hist_browser *browser, const char *help)
+static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
+{
+ switch (key) {
+ case K_TIMER: {
+ struct hist_browser_timer *hbt = browser->hbt;
+ struct evsel *evsel = hists_to_evsel(browser->hists);
+ u64 nr_entries;
+
+ WARN_ON_ONCE(!hbt);
+
+ if (hbt)
+ hbt->timer(hbt->arg);
+
+ if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
+ hist_browser__update_nr_entries(browser);
+
+ nr_entries = hist_browser__nr_entries(browser);
+ ui_browser__update_nr_entries(&browser->b, nr_entries);
+
+ if (warn_lost_event &&
+ (evsel->evlist->stats.nr_lost_warned !=
+ evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) {
+ evsel->evlist->stats.nr_lost_warned =
+ evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
+ ui_browser__warn_lost_events(&browser->b);
+ }
+
+ hist_browser__title(browser, title, size);
+ ui_browser__show_title(&browser->b, title);
+ break;
+ }
+ case 'D': { /* Debug */
+ struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
+ static int seq;
+
+ ui_helpline__pop();
+ ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
+ seq++, browser->b.nr_entries, browser->hists->nr_entries,
+ browser->b.extra_title_lines, browser->b.rows,
+ browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
+ }
+ break;
+ case 'C':
+ /* Collapse the whole world. */
+ hist_browser__set_folding(browser, false);
+ break;
+ case 'c':
+ /* Collapse the selected entry. */
+ hist_browser__set_folding_selected(browser, false);
+ break;
+ case 'E':
+ /* Expand the whole world. */
+ hist_browser__set_folding(browser, true);
+ break;
+ case 'e':
+ /* Toggle expand/collapse the selected entry. */
+ hist_browser__toggle_fold(browser);
+ break;
+ case 'H':
+ browser->show_headers = !browser->show_headers;
+ hist_browser__update_rows(browser);
+ break;
+ case '+':
+ if (hist_browser__toggle_fold(browser))
+ break;
+ /* fall thru */
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int hist_browser__run(struct hist_browser *browser, const char *help,
+ bool warn_lost_event, int key)
{
- int key;
char title[160];
struct hist_browser_timer *hbt = browser->hbt;
int delay_secs = hbt ? hbt->refresh : 0;
@@ -700,73 +756,14 @@ int hist_browser__run(struct hist_browser *browser, const char *help)
if (ui_browser__show(&browser->b, title, "%s", help) < 0)
return -1;
+ if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
+ goto out;
+
while (1) {
key = ui_browser__run(&browser->b, delay_secs);
- switch (key) {
- case K_TIMER: {
- u64 nr_entries;
- hbt->timer(hbt->arg);
-
- if (hist_browser__has_filter(browser) ||
- symbol_conf.report_hierarchy)
- hist_browser__update_nr_entries(browser);
-
- nr_entries = hist_browser__nr_entries(browser);
- ui_browser__update_nr_entries(&browser->b, nr_entries);
-
- if (browser->hists->stats.nr_lost_warned !=
- browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
- browser->hists->stats.nr_lost_warned =
- browser->hists->stats.nr_events[PERF_RECORD_LOST];
- ui_browser__warn_lost_events(&browser->b);
- }
-
- hist_browser__title(browser, title, sizeof(title));
- ui_browser__show_title(&browser->b, title);
- continue;
- }
- case 'D': { /* Debug */
- static int seq;
- struct hist_entry *h = rb_entry(browser->b.top,
- struct hist_entry, rb_node);
- ui_helpline__pop();
- ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
- seq++, browser->b.nr_entries,
- browser->hists->nr_entries,
- browser->b.rows,
- browser->b.index,
- browser->b.top_idx,
- h->row_offset, h->nr_rows);
- }
- break;
- case 'C':
- /* Collapse the whole world. */
- hist_browser__set_folding(browser, false);
- break;
- case 'c':
- /* Collapse the selected entry. */
- hist_browser__set_folding_selected(browser, false);
- break;
- case 'E':
- /* Expand the whole world. */
- hist_browser__set_folding(browser, true);
- break;
- case 'e':
- /* Expand the selected entry. */
- hist_browser__set_folding_selected(browser, true);
- break;
- case 'H':
- browser->show_headers = !browser->show_headers;
- hist_browser__update_rows(browser);
+ if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
break;
- case K_ENTER:
- if (hist_browser__toggle_fold(browser))
- break;
- /* fall thru */
- default:
- goto out;
- }
}
out:
ui_browser__hide(&browser->b);
@@ -808,7 +805,7 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser,
}
ui_browser__set_color(&browser->b, color);
- hist_browser__gotorc(browser, row, 0);
+ ui_browser__gotorc(&browser->b, row, 0);
ui_browser__write_nstring(&browser->b, " ", offset);
ui_browser__printf(&browser->b, "%c", folded_sign);
ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
@@ -844,71 +841,6 @@ static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_u
#define LEVEL_OFFSET_STEP 3
-static int hist_browser__show_inline(struct hist_browser *browser,
- struct inline_node *node,
- unsigned short row,
- int offset)
-{
- struct inline_list *ilist;
- char buf[1024];
- int color, width, first_row;
-
- first_row = row;
- width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
- list_for_each_entry(ilist, &node->val, list) {
- if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
- color = HE_COLORSET_NORMAL;
- if (ui_browser__is_current_entry(&browser->b, row))
- color = HE_COLORSET_SELECTED;
-
- if (callchain_param.key == CCKEY_ADDRESS ||
- callchain_param.key == CCKEY_SRCLINE) {
- if (ilist->filename != NULL)
- scnprintf(buf, sizeof(buf),
- "%s:%d (inline)",
- ilist->filename,
- ilist->line_nr);
- else
- scnprintf(buf, sizeof(buf), "??");
- } else if (ilist->funcname != NULL)
- scnprintf(buf, sizeof(buf), "%s (inline)",
- ilist->funcname);
- else if (ilist->filename != NULL)
- scnprintf(buf, sizeof(buf),
- "%s:%d (inline)",
- ilist->filename,
- ilist->line_nr);
- else
- scnprintf(buf, sizeof(buf), "??");
-
- ui_browser__set_color(&browser->b, color);
- hist_browser__gotorc(browser, row, 0);
- ui_browser__write_nstring(&browser->b, " ",
- LEVEL_OFFSET_STEP + offset);
- ui_browser__write_nstring(&browser->b, buf, width);
- row++;
- }
- }
-
- return row - first_row;
-}
-
-static size_t show_inline_list(struct hist_browser *browser, struct map *map,
- u64 ip, int row, int offset)
-{
- struct inline_node *node;
- int ret;
-
- node = inline_node__create(map, ip);
- if (node == NULL)
- return 0;
-
- ret = hist_browser__show_inline(browser, node, row, offset);
-
- inline_node__delete(node);
- return ret;
-}
-
static int hist_browser__show_callchain_list(struct hist_browser *browser,
struct callchain_node *node,
struct callchain_list *chain,
@@ -920,7 +852,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
char bf[1024], *alloc_str;
char buf[64], *alloc_str2;
const char *str;
- int inline_rows = 0, ret = 1;
+ int ret = 1;
if (arg->row_offset != 0) {
arg->row_offset--;
@@ -934,12 +866,8 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
browser->show_dso);
if (symbol_conf.show_branchflag_count) {
- if (need_percent)
- callchain_list_counts__printf_value(node, chain, NULL,
- buf, sizeof(buf));
- else
- callchain_list_counts__printf_value(NULL, chain, NULL,
- buf, sizeof(buf));
+ callchain_list_counts__printf_value(chain, NULL,
+ buf, sizeof(buf));
if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
str = "Not enough memory!";
@@ -961,12 +889,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
free(alloc_str);
free(alloc_str2);
- if (symbol_conf.inline_name) {
- inline_rows = show_inline_list(browser, chain->ms.map,
- chain->ip, row + 1, offset);
- }
-
- return ret + inline_rows;
+ return ret;
}
static bool check_percent_display(struct rb_node *node, u64 parent_total)
@@ -1303,7 +1226,7 @@ int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
return ret;
}
-#define __HPP_COLOR_PERCENT_FN(_type, _field) \
+#define __HPP_COLOR_PERCENT_FN(_type, _field, _fmttype) \
static u64 __hpp_get_##_field(struct hist_entry *he) \
{ \
return he->stat._field; \
@@ -1315,10 +1238,10 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
struct hist_entry *he) \
{ \
return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
- __hpp__slsmg_color_printf, true); \
+ __hpp__slsmg_color_printf, _fmttype); \
}
-#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
+#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field, _fmttype) \
static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
{ \
return he->stat_acc->_field; \
@@ -1339,23 +1262,44 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
return ret; \
} \
return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
- " %*.2f%%", __hpp__slsmg_color_printf, true); \
+ " %*.2f%%", __hpp__slsmg_color_printf, \
+ _fmttype); \
}
-__HPP_COLOR_PERCENT_FN(overhead, period)
-__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
-__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
-__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
-__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
-__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
+#define __HPP_COLOR_MEM_STAT_FN(_name, _type) \
+static int \
+hist_browser__hpp_color_mem_stat_##_name(struct perf_hpp_fmt *fmt, \
+ struct perf_hpp *hpp, \
+ struct hist_entry *he) \
+{ \
+ return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type, \
+ " %5.1f%%", __hpp__slsmg_color_printf);\
+}
+
+__HPP_COLOR_PERCENT_FN(overhead, period, PERF_HPP_FMT_TYPE__PERCENT)
+__HPP_COLOR_PERCENT_FN(latency, latency, PERF_HPP_FMT_TYPE__LATENCY)
+__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT)
+__HPP_COLOR_PERCENT_FN(overhead_us, period_us, PERF_HPP_FMT_TYPE__PERCENT)
+__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, PERF_HPP_FMT_TYPE__PERCENT)
+__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, PERF_HPP_FMT_TYPE__PERCENT)
+__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period, PERF_HPP_FMT_TYPE__PERCENT)
+__HPP_COLOR_ACC_PERCENT_FN(latency_acc, latency, PERF_HPP_FMT_TYPE__LATENCY)
+__HPP_COLOR_MEM_STAT_FN(op, OP)
+__HPP_COLOR_MEM_STAT_FN(cache, CACHE)
+__HPP_COLOR_MEM_STAT_FN(memory, MEMORY)
+__HPP_COLOR_MEM_STAT_FN(snoop, SNOOP)
+__HPP_COLOR_MEM_STAT_FN(dtlb, DTLB)
#undef __HPP_COLOR_PERCENT_FN
#undef __HPP_COLOR_ACC_PERCENT_FN
+#undef __HPP_COLOR_MEM_STAT_FN
void hist_browser__init_hpp(void)
{
perf_hpp__format[PERF_HPP__OVERHEAD].color =
hist_browser__hpp_color_overhead;
+ perf_hpp__format[PERF_HPP__LATENCY].color =
+ hist_browser__hpp_color_latency;
perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
hist_browser__hpp_color_overhead_sys;
perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
@@ -1366,6 +1310,20 @@ void hist_browser__init_hpp(void)
hist_browser__hpp_color_overhead_guest_us;
perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
hist_browser__hpp_color_overhead_acc;
+ perf_hpp__format[PERF_HPP__LATENCY_ACC].color =
+ hist_browser__hpp_color_latency_acc;
+ perf_hpp__format[PERF_HPP__MEM_STAT_OP].color =
+ hist_browser__hpp_color_mem_stat_op;
+ perf_hpp__format[PERF_HPP__MEM_STAT_CACHE].color =
+ hist_browser__hpp_color_mem_stat_cache;
+ perf_hpp__format[PERF_HPP__MEM_STAT_MEMORY].color =
+ hist_browser__hpp_color_mem_stat_memory;
+ perf_hpp__format[PERF_HPP__MEM_STAT_SNOOP].color =
+ hist_browser__hpp_color_mem_stat_snoop;
+ perf_hpp__format[PERF_HPP__MEM_STAT_DTLB].color =
+ hist_browser__hpp_color_mem_stat_dtlb;
+
+ res_sample_init();
}
static int hist_browser__show_entry(struct hist_browser *browser,
@@ -1376,6 +1334,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
int width = browser->b.width;
char folded_sign = ' ';
bool current_entry = ui_browser__is_current_entry(&browser->b, row);
+ bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
off_t row_offset = entry->row_offset;
bool first = true;
struct perf_hpp_fmt *fmt;
@@ -1385,17 +1344,11 @@ static int hist_browser__show_entry(struct hist_browser *browser,
browser->selection = &entry->ms;
}
- if (symbol_conf.use_callchain) {
+ if (use_callchain) {
hist_entry__init_have_children(entry);
folded_sign = hist_entry__folded(entry);
}
- if (symbol_conf.inline_name &&
- (!entry->has_children)) {
- hist_entry_init_inline_node(entry);
- folded_sign = hist_entry__folded(entry);
- }
-
if (row_offset == 0) {
struct hpp_arg arg = {
.b = &browser->b,
@@ -1404,7 +1357,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
};
int column = 0;
- hist_browser__gotorc(browser, row, 0);
+ ui_browser__gotorc(&browser->b, row, 0);
hists__for_each_format(browser->hists, fmt) {
char s[2048];
@@ -1427,8 +1380,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
}
if (first) {
- if (symbol_conf.use_callchain ||
- symbol_conf.inline_name) {
+ if (use_callchain) {
ui_browser__printf(&browser->b, "%c ", folded_sign);
width -= 2;
}
@@ -1470,15 +1422,11 @@ static int hist_browser__show_entry(struct hist_browser *browser,
.is_current_entry = current_entry,
};
- if (entry->inline_node)
- printed += hist_browser__show_inline(browser,
- entry->inline_node, row, 0);
- else
- printed += hist_browser__show_callchain(browser,
- entry, 1, row,
- hist_browser__show_callchain_entry,
- &arg,
- hist_browser__check_output_full);
+ printed += hist_browser__show_callchain(browser,
+ entry, 1, row,
+ hist_browser__show_callchain_entry,
+ &arg,
+ hist_browser__check_output_full);
}
return printed;
@@ -1518,7 +1466,7 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
goto show_callchain;
}
- hist_browser__gotorc(browser, row, 0);
+ ui_browser__gotorc(&browser->b, row, 0);
if (current_entry && browser->b.navkeypressed)
ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
@@ -1618,7 +1566,7 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
int i = 0;
width -= fmt->entry(fmt, &hpp, entry);
- ui_browser__printf(&browser->b, "%s", ltrim(s));
+ ui_browser__printf(&browser->b, "%s", skip_spaces(s));
while (isspace(s[i++]))
width++;
@@ -1667,7 +1615,7 @@ static int hist_browser__show_no_entry(struct hist_browser *browser,
browser->selection = NULL;
}
- hist_browser__gotorc(browser, row, 0);
+ ui_browser__gotorc(&browser->b, row, 0);
if (current_entry && browser->b.navkeypressed)
ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
@@ -1739,7 +1687,7 @@ hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
int column = 0;
int span = 0;
- if (symbol_conf.use_callchain) {
+ if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
ret = scnprintf(buf, size, " ");
if (advance_hpp_check(&dummy_hpp, ret))
return ret;
@@ -1764,7 +1712,8 @@ hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
return ret;
}
-static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
+static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser,
+ char *buf, size_t size, int line)
{
struct hists *hists = browser->hists;
struct perf_hpp dummy_hpp = {
@@ -1790,7 +1739,7 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
if (column++ < browser->b.horiz_scroll)
continue;
- ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
+ ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL);
if (advance_hpp_check(&dummy_hpp, ret))
break;
@@ -1801,6 +1750,9 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
first_node = false;
}
+ if (line < hists->hpp_list->nr_header_lines - 1)
+ return ret;
+
if (!first_node) {
ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
indent * HIERARCHY_INDENT, "");
@@ -1831,10 +1783,10 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
}
first_col = false;
- ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
+ ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL);
dummy_hpp.buf[ret] = '\0';
- start = trim(dummy_hpp.buf);
+ start = strim(dummy_hpp.buf);
ret = strlen(start);
if (start != dummy_hpp.buf)
@@ -1850,14 +1802,18 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
static void hists_browser__hierarchy_headers(struct hist_browser *browser)
{
+ struct perf_hpp_list *hpp_list = browser->hists->hpp_list;
char headers[1024];
+ int line;
- hists_browser__scnprintf_hierarchy_headers(browser, headers,
- sizeof(headers));
+ for (line = 0; line < hpp_list->nr_header_lines; line++) {
+ hists_browser__scnprintf_hierarchy_headers(browser, headers,
+ sizeof(headers), line);
- ui_browser__gotorc(&browser->b, 0, 0);
- ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
- ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
+ ui_browser__gotorc_title(&browser->b, line, 0);
+ ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
+ ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
+ }
}
static void hists_browser__headers(struct hist_browser *browser)
@@ -1873,7 +1829,7 @@ static void hists_browser__headers(struct hist_browser *browser)
hists_browser__scnprintf_headers(browser, headers,
sizeof(headers), line);
- ui_browser__gotorc(&browser->b, line, 0);
+ ui_browser__gotorc_title(&browser->b, line, 0);
ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
}
@@ -1893,24 +1849,18 @@ static void ui_browser__hists_init_top(struct ui_browser *browser)
struct hist_browser *hb;
hb = container_of(browser, struct hist_browser, b);
- browser->top = rb_first(&hb->hists->entries);
+ browser->top = rb_first_cached(&hb->hists->entries);
}
}
static unsigned int hist_browser__refresh(struct ui_browser *browser)
{
unsigned row = 0;
- u16 header_offset = 0;
struct rb_node *nd;
struct hist_browser *hb = container_of(browser, struct hist_browser, b);
- struct hists *hists = hb->hists;
-
- if (hb->show_headers) {
- struct perf_hpp_list *hpp_list = hists->hpp_list;
+ if (hb->show_headers)
hist_browser__show_headers(hb);
- header_offset = hpp_list->nr_header_lines;
- }
ui_browser__hists_init_top(browser);
hb->he_selection = NULL;
@@ -1926,7 +1876,11 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
continue;
}
- percent = hist_entry__get_percent_limit(h);
+ if (symbol_conf.report_individual_block)
+ percent = block_info__total_cycles_percent(h);
+ else
+ percent = hist_entry__get_percent_limit(h);
+
if (percent < hb->min_pcnt)
continue;
@@ -1948,7 +1902,7 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
break;
}
- return row + header_offset;
+ return row;
}
static struct rb_node *hists__filter_entries(struct rb_node *nd,
@@ -2149,7 +2103,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
bool first = true;
int ret;
- if (symbol_conf.use_callchain) {
+ if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
folded_sign = hist_entry__folded(he);
printed += fprintf(fp, "%c ", folded_sign);
}
@@ -2224,7 +2178,8 @@ static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
advance_hpp(&hpp, ret);
}
- printed += fprintf(fp, "%s\n", rtrim(s));
+ strim(s);
+ printed += fprintf(fp, "%s\n", s);
if (he->leaf && folded_sign == '-') {
printed += hist_browser__fprintf_callchain(browser, he, fp,
@@ -2303,6 +2258,7 @@ void hist_browser__init(struct hist_browser *browser,
browser->b.seek = ui_browser__hists_seek;
browser->b.use_navkeypressed = true;
browser->show_headers = symbol_conf.show_hist_headers;
+ hist_browser__set_title_space(browser);
if (symbol_conf.report_hierarchy) {
struct perf_hpp_list_node *fmt_node;
@@ -2334,7 +2290,7 @@ struct hist_browser *hist_browser__new(struct hists *hists)
}
static struct hist_browser *
-perf_evsel_browser__new(struct perf_evsel *evsel,
+perf_evsel_browser__new(struct evsel *evsel,
struct hist_browser_timer *hbt,
struct perf_env *env)
{
@@ -2343,7 +2299,7 @@ perf_evsel_browser__new(struct perf_evsel *evsel,
if (browser) {
browser->hbt = hbt;
browser->env = env;
- browser->title = perf_evsel_browser_title;
+ browser->title = hists_browser__scnprintf_title;
}
return browser;
}
@@ -2363,92 +2319,40 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *browser
return browser->he_selection->thread;
}
+static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
+{
+ return browser->he_selection ? browser->he_selection->res_samples : NULL;
+}
+
/* Check whether the browser is for 'top' or 'report' */
static inline bool is_report_browser(void *timer)
{
return timer == NULL;
}
-static int perf_evsel_browser_title(struct hist_browser *browser,
- char *bf, size_t size)
+static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
{
struct hist_browser_timer *hbt = browser->hbt;
- struct hists *hists = browser->hists;
- char unit;
- int printed;
- const struct dso *dso = hists->dso_filter;
- const struct thread *thread = hists->thread_filter;
- int socket_id = hists->socket_filter;
- unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
- u64 nr_events = hists->stats.total_period;
- struct perf_evsel *evsel = hists_to_evsel(hists);
- const char *ev_name = perf_evsel__name(evsel);
- char buf[512];
- size_t buflen = sizeof(buf);
- char ref[30] = " show reference callgraph, ";
- bool enable_ref = false;
-
- if (symbol_conf.filter_relative) {
- nr_samples = hists->stats.nr_non_filtered_samples;
- nr_events = hists->stats.total_non_filtered_period;
- }
-
- if (perf_evsel__is_group_event(evsel)) {
- struct perf_evsel *pos;
-
- perf_evsel__group_desc(evsel, buf, buflen);
- ev_name = buf;
-
- for_each_group_member(pos, evsel) {
- struct hists *pos_hists = evsel__hists(pos);
-
- if (symbol_conf.filter_relative) {
- nr_samples += pos_hists->stats.nr_non_filtered_samples;
- nr_events += pos_hists->stats.total_non_filtered_period;
- } else {
- nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
- nr_events += pos_hists->stats.total_period;
- }
- }
- }
-
- if (symbol_conf.show_ref_callgraph &&
- strstr(ev_name, "call-graph=no"))
- enable_ref = true;
- nr_samples = convert_unit(nr_samples, &unit);
- printed = scnprintf(bf, size,
- "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
- nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
+ int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
+ if (!is_report_browser(hbt)) {
+ struct perf_top *top = hbt->arg;
- if (hists->uid_filter_str)
- printed += snprintf(bf + printed, size - printed,
- ", UID: %s", hists->uid_filter_str);
- if (thread) {
- if (hists__has(hists, thread)) {
- printed += scnprintf(bf + printed, size - printed,
- ", Thread: %s(%d)",
- (thread->comm_set ? thread__comm_str(thread) : ""),
- thread->tid);
- } else {
- printed += scnprintf(bf + printed, size - printed,
- ", Thread: %s",
- (thread->comm_set ? thread__comm_str(thread) : ""));
- }
- }
- if (dso)
printed += scnprintf(bf + printed, size - printed,
- ", DSO: %s", dso->short_name);
- if (socket_id > -1)
+ " lost: %" PRIu64 "/%" PRIu64,
+ top->lost, top->lost_total);
+
printed += scnprintf(bf + printed, size - printed,
- ", Processor Socket: %d", socket_id);
- if (!is_report_browser(hbt)) {
- struct perf_top *top = hbt->arg;
+ " drop: %" PRIu64 "/%" PRIu64,
+ top->drop, top->drop_total);
if (top->zero)
printed += scnprintf(bf + printed, size - printed, " [z]");
+
+ perf_top__reset_sample_counters(top);
}
+
return printed;
}
@@ -2530,7 +2434,7 @@ close_file_and_continue:
closedir(pwd_dir);
if (nr_options) {
- choice = ui__popup_menu(nr_options, options);
+ choice = ui__popup_menu(nr_options, options, NULL);
if (choice < nr_options && choice >= 0) {
tmp = strdup(abs_path[choice]);
if (tmp) {
@@ -2550,31 +2454,38 @@ close_file_and_continue:
}
struct popup_action {
+ unsigned long time;
struct thread *thread;
+ int (*fn)(struct hist_browser *browser, struct popup_action *act);
struct map_symbol ms;
int socket;
+ enum rstype rstype;
- int (*fn)(struct hist_browser *browser, struct popup_action *act);
};
static int
do_annotate(struct hist_browser *browser, struct popup_action *act)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct annotation *notes;
struct hist_entry *he;
int err;
- if (!objdump_path && perf_env__lookup_objdump(browser->env))
+ if (!annotate_opts.objdump_path &&
+ perf_env__lookup_objdump(browser->env, &annotate_opts.objdump_path))
return 0;
notes = symbol__annotation(act->ms.sym);
if (!notes->src)
return 0;
- evsel = hists_to_evsel(browser->hists);
- err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
+ if (browser->block_evsel)
+ evsel = browser->block_evsel;
+ else
+ evsel = hists_to_evsel(browser->hists);
+
he = hist_browser__selected_entry(browser);
+ err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_ADDR);
/*
* offer option to annotate the other branch source or target
* (if they exists) when returning from annotate
@@ -2588,24 +2499,77 @@ do_annotate(struct hist_browser *browser, struct popup_action *act)
return 0;
}
+static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
+{
+ struct annotated_source *src;
+ struct symbol *sym;
+ char name[64];
+
+ snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
+
+ sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
+ if (sym) {
+ src = symbol__hists(sym, 1);
+ if (!src) {
+ symbol__delete(sym);
+ return NULL;
+ }
+
+ dso__insert_symbol(map__dso(map), sym);
+ }
+
+ return sym;
+}
+
static int
-add_annotate_opt(struct hist_browser *browser __maybe_unused,
- struct popup_action *act, char **optstr,
- struct map *map, struct symbol *sym)
+add_annotate_opt(struct popup_action *act, char **optstr,
+ struct map_symbol *ms,
+ u64 addr)
{
- if (sym == NULL || map->dso->annotate_warned)
+ struct dso *dso;
+
+ if (!ms->map || (dso = map__dso(ms->map)) == NULL || dso__annotate_warned(dso))
return 0;
- if (asprintf(optstr, "Annotate %s", sym->name) < 0)
+ if (!ms->sym)
+ ms->sym = symbol__new_unresolved(addr, ms->map);
+
+ if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
return 0;
- act->ms.map = map;
- act->ms.sym = sym;
+ if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
+ return 0;
+
+ act->ms = *ms;
act->fn = do_annotate;
return 1;
}
static int
+do_annotate_type(struct hist_browser *browser, struct popup_action *act __maybe_unused)
+{
+ struct hist_entry *he = browser->he_selection;
+
+ hist_entry__annotate_data_tui(he, hists_to_evsel(browser->hists), browser->hbt);
+ ui_browser__handle_resize(&browser->b);
+ return 0;
+}
+
+static int
+add_annotate_type_opt(struct popup_action *act, char **optstr,
+ struct hist_entry *he)
+{
+ if (he == NULL || he->mem_type == NULL || he->mem_type->histograms == NULL)
+ return 0;
+
+ if (asprintf(optstr, "Annotate type %s", he->mem_type->self.type_name) < 0)
+ return 0;
+
+ act->fn = do_annotate_type;
+ return 1;
+}
+
+static int
do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
{
struct thread *thread = act->thread;
@@ -2620,13 +2584,15 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
thread__zput(browser->hists->thread_filter);
ui_helpline__pop();
} else {
+ const char *comm_set_str =
+ thread__comm_set(thread) ? thread__comm_str(thread) : "";
+
if (hists__has(browser->hists, thread)) {
ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
- thread->comm_set ? thread__comm_str(thread) : "",
- thread->tid);
+ comm_set_str, thread__tid(thread));
} else {
ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
- thread->comm_set ? thread__comm_str(thread) : "");
+ comm_set_str);
}
browser->hists->thread_filter = thread__get(thread);
@@ -2644,20 +2610,19 @@ add_thread_opt(struct hist_browser *browser, struct popup_action *act,
char **optstr, struct thread *thread)
{
int ret;
+ const char *comm_set_str, *in_out;
if ((!hists__has(browser->hists, thread) &&
!hists__has(browser->hists, comm)) || thread == NULL)
return 0;
+ in_out = browser->hists->thread_filter ? "out of" : "into";
+ comm_set_str = thread__comm_set(thread) ? thread__comm_str(thread) : "";
if (hists__has(browser->hists, thread)) {
ret = asprintf(optstr, "Zoom %s %s(%d) thread",
- browser->hists->thread_filter ? "out of" : "into",
- thread->comm_set ? thread__comm_str(thread) : "",
- thread->tid);
+ in_out, comm_set_str, thread__tid(thread));
} else {
- ret = asprintf(optstr, "Zoom %s %s thread",
- browser->hists->thread_filter ? "out of" : "into",
- thread->comm_set ? thread__comm_str(thread) : "");
+ ret = asprintf(optstr, "Zoom %s %s thread", in_out, comm_set_str);
}
if (ret < 0)
return 0;
@@ -2667,11 +2632,8 @@ add_thread_opt(struct hist_browser *browser, struct popup_action *act,
return 1;
}
-static int
-do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
+static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
{
- struct map *map = act->ms.map;
-
if (!hists__has(browser->hists, dso) || map == NULL)
return 0;
@@ -2681,9 +2643,10 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
browser->hists->dso_filter = NULL;
ui_helpline__pop();
} else {
+ struct dso *dso = map__dso(map);
ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
- __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
- browser->hists->dso_filter = map->dso;
+ __map__is_kernel(map) ? "the Kernel" : dso__short_name(dso));
+ browser->hists->dso_filter = dso;
perf_hpp__set_elide(HISTC_DSO, true);
pstack__push(browser->pstack, &browser->hists->dso_filter);
}
@@ -2694,15 +2657,21 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
}
static int
+do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
+{
+ return hists_browser__zoom_map(browser, act->ms.map);
+}
+
+static int
add_dso_opt(struct hist_browser *browser, struct popup_action *act,
char **optstr, struct map *map)
{
if (!hists__has(browser->hists, dso) || map == NULL)
return 0;
- if (asprintf(optstr, "Zoom %s %s DSO",
+ if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
browser->hists->dso_filter ? "out of" : "into",
- __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
+ __map__is_kernel(map) ? "the Kernel" : dso__short_name(map__dso(map))) < 0)
return 0;
act->ms.map = map;
@@ -2710,6 +2679,28 @@ add_dso_opt(struct hist_browser *browser, struct popup_action *act,
return 1;
}
+static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
+{
+ hist_browser__toggle_fold(browser);
+ return 0;
+}
+
+static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
+{
+ char sym_name[512];
+
+ if (!hist_browser__selection_has_children(browser))
+ return 0;
+
+ if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
+ hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
+ hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
+ return 0;
+
+ act->fn = do_toggle_callchain;
+ return 1;
+}
+
static int
do_browse_map(struct hist_browser *browser __maybe_unused,
struct popup_action *act)
@@ -2734,39 +2725,77 @@ add_map_opt(struct hist_browser *browser,
}
static int
-do_run_script(struct hist_browser *browser __maybe_unused,
+do_run_script(struct hist_browser *browser,
struct popup_action *act)
{
- char script_opt[64];
- memset(script_opt, 0, sizeof(script_opt));
+ char *script_opt;
+ int len;
+ int n = 0;
+ len = 100;
+ if (act->thread)
+ len += strlen(thread__comm_str(act->thread));
+ else if (act->ms.sym)
+ len += strlen(act->ms.sym->name);
+ script_opt = malloc(len);
+ if (!script_opt)
+ return -1;
+
+ script_opt[0] = 0;
if (act->thread) {
- scnprintf(script_opt, sizeof(script_opt), " -c %s ",
+ n = scnprintf(script_opt, len, " -c %s ",
thread__comm_str(act->thread));
} else if (act->ms.sym) {
- scnprintf(script_opt, sizeof(script_opt), " -S %s ",
+ n = scnprintf(script_opt, len, " -S %s ",
act->ms.sym->name);
}
- script_browse(script_opt);
+ if (act->time) {
+ char start[32], end[32];
+ unsigned long starttime = act->time;
+ unsigned long endtime = act->time + symbol_conf.time_quantum;
+
+ if (starttime == endtime) { /* Display 1ms as fallback */
+ starttime -= 1*NSEC_PER_MSEC;
+ endtime += 1*NSEC_PER_MSEC;
+ }
+ timestamp__scnprintf_usec(starttime, start, sizeof start);
+ timestamp__scnprintf_usec(endtime, end, sizeof end);
+ n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
+ }
+
+ script_browse(script_opt, hists_to_evsel(browser->hists));
+ free(script_opt);
return 0;
}
static int
-add_script_opt(struct hist_browser *browser __maybe_unused,
- struct popup_action *act, char **optstr,
- struct thread *thread, struct symbol *sym)
+do_res_sample_script(struct hist_browser *browser,
+ struct popup_action *act)
{
+ struct hist_entry *he;
+
+ he = hist_browser__selected_entry(browser);
+ res_sample_browse(he->res_samples, he->num_res, hists_to_evsel(browser->hists), act->rstype);
+ return 0;
+}
+
+static int
+add_script_opt_2(struct popup_action *act, char **optstr,
+ struct thread *thread, struct symbol *sym,
+ const char *tstr)
+{
+
if (thread) {
- if (asprintf(optstr, "Run scripts for samples of thread [%s]",
- thread__comm_str(thread)) < 0)
+ if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
+ thread__comm_str(thread), tstr) < 0)
return 0;
} else if (sym) {
- if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
- sym->name) < 0)
+ if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
+ sym->name, tstr) < 0)
return 0;
} else {
- if (asprintf(optstr, "Run scripts for all samples") < 0)
+ if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
return 0;
}
@@ -2777,6 +2806,53 @@ add_script_opt(struct hist_browser *browser __maybe_unused,
}
static int
+add_script_opt(struct hist_browser *browser,
+ struct popup_action *act, char **optstr,
+ struct thread *thread, struct symbol *sym)
+{
+ int n, j;
+ struct hist_entry *he;
+
+ n = add_script_opt_2(act, optstr, thread, sym, "");
+
+ he = hist_browser__selected_entry(browser);
+ if (sort_order && strstr(sort_order, "time")) {
+ char tstr[128];
+
+ optstr++;
+ act++;
+ j = sprintf(tstr, " in ");
+ j += timestamp__scnprintf_usec(he->time, tstr + j,
+ sizeof tstr - j);
+ j += sprintf(tstr + j, "-");
+ timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
+ tstr + j, sizeof tstr - j);
+ n += add_script_opt_2(act, optstr, thread, sym, tstr);
+ act->time = he->time;
+ }
+ return n;
+}
+
+static int
+add_res_sample_opt(struct hist_browser *browser __maybe_unused,
+ struct popup_action *act, char **optstr,
+ struct res_sample *res_sample,
+ enum rstype type)
+{
+ if (!res_sample)
+ return 0;
+
+ if (asprintf(optstr, "Show context for individual samples %s",
+ type == A_ASM ? "with assembler" :
+ type == A_SOURCE ? "with source" : "") < 0)
+ return 0;
+
+ act->fn = do_res_sample_script;
+ act->rstype = type;
+ return 1;
+}
+
+static int
do_switch_data(struct hist_browser *browser __maybe_unused,
struct popup_action *act __maybe_unused)
{
@@ -2862,7 +2938,7 @@ add_socket_opt(struct hist_browser *browser, struct popup_action *act,
static void hist_browser__update_nr_entries(struct hist_browser *hb)
{
u64 nr_entries = 0;
- struct rb_node *nd = rb_first(&hb->hists->entries);
+ struct rb_node *nd = rb_first_cached(&hb->hists->entries);
if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
@@ -2882,7 +2958,7 @@ static void hist_browser__update_percent_limit(struct hist_browser *hb,
double percent)
{
struct hist_entry *he;
- struct rb_node *nd = rb_first(&hb->hists->entries);
+ struct rb_node *nd = rb_first_cached(&hb->hists->entries);
u64 total = hists__total_period(hb->hists);
u64 min_callchain_hits = total * (percent / 100);
@@ -2896,7 +2972,7 @@ static void hist_browser__update_percent_limit(struct hist_browser *hb,
he->nr_rows = 0;
}
- if (!he->leaf || !symbol_conf.use_callchain)
+ if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
goto next;
if (callchain_param.mode == CHAIN_GRAPH_REL) {
@@ -2920,54 +2996,55 @@ next:
}
}
-static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
- const char *helpline,
- bool left_exits,
- struct hist_browser_timer *hbt,
- float min_pcnt,
- struct perf_env *env)
+static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
+ bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
+ struct perf_env *env, bool warn_lost_event)
{
struct hists *hists = evsel__hists(evsel);
struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
- struct branch_info *bi;
+ struct branch_info *bi = NULL;
#define MAX_OPTIONS 16
char *options[MAX_OPTIONS];
struct popup_action actions[MAX_OPTIONS];
int nr_options = 0;
int key = -1;
- char buf[64];
+ char buf[128];
int delay_secs = hbt ? hbt->refresh : 0;
#define HIST_BROWSER_HELP_COMMON \
"h/?/F1 Show this window\n" \
"UP/DOWN/PGUP\n" \
"PGDN/SPACE Navigate\n" \
- "q/ESC/CTRL+C Exit browser\n\n" \
+ "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \
"For multiple event sessions:\n\n" \
"TAB/UNTAB Switch events\n\n" \
"For symbolic views (--sort has sym):\n\n" \
"ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
"ESC Zoom out\n" \
+ "+ Expand/Collapse one callchain level\n" \
"a Annotate current symbol\n" \
"C Collapse all callchains\n" \
"d Zoom into current DSO\n" \
+ "e Expand/Collapse main entry callchains\n" \
"E Expand all callchains\n" \
"F Toggle percentage of filtered entries\n" \
"H Display column headers\n" \
+ "k Zoom into the kernel map\n" \
"L Change percent limit\n" \
"m Display context menu\n" \
"S Zoom into current Processor Socket\n" \
/* help messages are sorted by lexical order of the hotkey */
- const char report_help[] = HIST_BROWSER_HELP_COMMON
+ static const char report_help[] = HIST_BROWSER_HELP_COMMON
"i Show header information\n"
"P Print histograms to perf.hist.N\n"
"r Run available scripts\n"
"s Switch to another data file in PWD\n"
"t Zoom into current Thread\n"
"V Verbose (DSO names in callchains, etc)\n"
- "/ Filter symbol by name";
- const char top_help[] = HIST_BROWSER_HELP_COMMON
+ "/ Filter symbol by name\n"
+ "0-9 Sort by event n in group";
+ static const char top_help[] = HIST_BROWSER_HELP_COMMON
"P Print histograms to perf.hist.N\n"
"t Zoom into current Thread\n"
"V Verbose (DSO names in callchains, etc)\n"
@@ -2981,6 +3058,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
/* reset abort key so that it can get Ctrl-C as a key */
SLang_reset_tty();
SLang_init_tty(0, 0, 0);
+ SLtty_set_suspend_state(true);
if (min_pcnt)
browser->min_pcnt = min_pcnt;
@@ -2998,15 +3076,19 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
if (symbol_conf.col_width_list_str)
perf_hpp__set_user_width(symbol_conf.col_width_list_str);
+ if (!is_report_browser(hbt))
+ browser->b.no_samples_msg = "Collecting samples...";
+
while (1) {
struct thread *thread = NULL;
struct map *map = NULL;
- int choice = 0;
+ int choice;
int socked_id = -1;
- nr_options = 0;
-
- key = hist_browser__run(browser, helpline);
+ key = 0; // reset key
+do_hotkey: // key came straight from options ui__popup_menu()
+ choice = nr_options = 0;
+ key = hist_browser__run(browser, helpline, warn_lost_event, key);
if (browser->he_selection != NULL) {
thread = hist_browser__selected_thread(browser);
@@ -3023,6 +3105,31 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
* go to the next or previous
*/
goto out_free_stack;
+ case '0' ... '9':
+ if (!symbol_conf.event_group ||
+ evsel->core.nr_members < 2) {
+ snprintf(buf, sizeof(buf),
+ "Sort by index only available with group events!");
+ helpline = buf;
+ continue;
+ }
+
+ if (key - '0' == symbol_conf.group_sort_idx)
+ continue;
+
+ symbol_conf.group_sort_idx = key - '0';
+
+ if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
+ snprintf(buf, sizeof(buf),
+ "Max event group index to sort is %d (index from 0 to %d)",
+ evsel->core.nr_members - 1,
+ evsel->core.nr_members - 1);
+ helpline = buf;
+ continue;
+ }
+
+ key = K_RELOAD;
+ goto out_free_stack;
case 'a':
if (!hists__has(hists, sym)) {
ui_browser__warning(&browser->b, delay_secs * 2,
@@ -3031,13 +3138,45 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
continue;
}
- if (browser->selection == NULL ||
- browser->selection->sym == NULL ||
- browser->selection->map->dso->annotate_warned)
+ if (!browser->selection ||
+ !browser->selection->map ||
+ !map__dso(browser->selection->map) ||
+ dso__annotate_warned(map__dso(browser->selection->map))) {
continue;
+ }
+
+ if (!browser->selection->sym) {
+ if (!browser->he_selection)
+ continue;
+
+ if (sort__mode == SORT_MODE__BRANCH) {
+ bi = browser->he_selection->branch_info;
+ if (!bi || !bi->to.ms.map)
+ continue;
+
+ actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
+ actions->ms.map = bi->to.ms.map;
+ } else {
+ actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
+ browser->selection->map);
+ actions->ms.map = browser->selection->map;
+ }
+
+ if (!actions->ms.sym)
+ continue;
+ } else {
+ if (symbol__annotation(browser->selection->sym)->src == NULL) {
+ ui_browser__warning(&browser->b, delay_secs * 2,
+ "No samples for the \"%s\" symbol.\n\n"
+ "Probably appeared just in a callchain",
+ browser->selection->sym->name);
+ continue;
+ }
+
+ actions->ms.map = browser->selection->map;
+ actions->ms.sym = browser->selection->sym;
+ }
- actions->ms.map = browser->selection->map;
- actions->ms.sym = browser->selection->sym;
do_annotate(browser, actions);
continue;
case 'P':
@@ -3047,6 +3186,11 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
actions->ms.map = map;
do_zoom_dso(browser, actions);
continue;
+ case 'k':
+ if (browser->selection != NULL)
+ hists_browser__zoom_map(browser,
+ maps__machine(browser->selection->maps)->vmlinux_map);
+ continue;
case 'V':
verbose = (verbose + 1) % 4;
browser->show_dso = verbose > 0;
@@ -3089,7 +3233,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
case 'i':
/* env->arch is NULL for live-mode (i.e. perf top) */
if (env->arch)
- tui__header_window(env);
+ tui__header_window(evsel__session(evsel));
continue;
case 'F':
symbol_conf.filter_relative ^= 1;
@@ -3148,15 +3292,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
continue;
}
+ actions->ms.map = map;
top = pstack__peek(browser->pstack);
if (top == &browser->hists->dso_filter) {
/*
* No need to set actions->dso here since
* it's just to remove the current filter.
- * Ditto for thread below.
*/
do_zoom_dso(browser, actions);
} else if (top == &browser->hists->thread_filter) {
+ actions->thread = thread;
do_zoom_thread(browser, actions);
} else if (top == &browser->hists->socket_filter) {
do_zoom_socket(browser, actions);
@@ -3170,7 +3315,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
if (!is_report_browser(hbt)) {
struct perf_top *top = hbt->arg;
- perf_evlist__toggle_enable(top->evlist);
+ evlist__toggle_enable(top->evlist);
/*
* No need to refresh, resort/decay histogram
* entries if we are not collecting samples:
@@ -3187,6 +3332,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
/* Fall thru */
default:
helpline = "Press '?' for help on key bindings";
+ ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs,
+ ", use 'h'/'?'/F1 to see actions");
continue;
}
@@ -3194,34 +3341,37 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
goto skip_annotation;
if (sort__mode == SORT_MODE__BRANCH) {
- bi = browser->he_selection->branch_info;
+
+ if (browser->he_selection)
+ bi = browser->he_selection->branch_info;
if (bi == NULL)
goto skip_annotation;
- nr_options += add_annotate_opt(browser,
- &actions[nr_options],
+ nr_options += add_annotate_opt(&actions[nr_options],
&options[nr_options],
- bi->from.map,
- bi->from.sym);
- if (bi->to.sym != bi->from.sym)
- nr_options += add_annotate_opt(browser,
- &actions[nr_options],
+ &bi->from.ms,
+ bi->from.al_addr);
+ if (bi->to.ms.sym != bi->from.ms.sym)
+ nr_options += add_annotate_opt(&actions[nr_options],
&options[nr_options],
- bi->to.map,
- bi->to.sym);
- } else {
- nr_options += add_annotate_opt(browser,
- &actions[nr_options],
+ &bi->to.ms,
+ bi->to.al_addr);
+ } else if (browser->he_selection) {
+ nr_options += add_annotate_opt(&actions[nr_options],
&options[nr_options],
- browser->selection->map,
- browser->selection->sym);
+ browser->selection,
+ browser->he_selection->ip);
}
skip_annotation:
+ nr_options += add_annotate_type_opt(&actions[nr_options],
+ &options[nr_options],
+ browser->he_selection);
nr_options += add_thread_opt(browser, &actions[nr_options],
&options[nr_options], thread);
nr_options += add_dso_opt(browser, &actions[nr_options],
&options[nr_options], map);
+ nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
nr_options += add_map_opt(browser, &actions[nr_options],
&options[nr_options],
browser->selection ?
@@ -3258,6 +3408,18 @@ skip_annotation:
}
nr_options += add_script_opt(browser, &actions[nr_options],
&options[nr_options], NULL, NULL);
+ nr_options += add_res_sample_opt(browser, &actions[nr_options],
+ &options[nr_options],
+ hist_browser__selected_res_sample(browser),
+ A_NORMAL);
+ nr_options += add_res_sample_opt(browser, &actions[nr_options],
+ &options[nr_options],
+ hist_browser__selected_res_sample(browser),
+ A_ASM);
+ nr_options += add_res_sample_opt(browser, &actions[nr_options],
+ &options[nr_options],
+ hist_browser__selected_res_sample(browser),
+ A_SOURCE);
nr_options += add_switch_opt(browser, &actions[nr_options],
&options[nr_options]);
skip_scripting:
@@ -3267,10 +3429,13 @@ skip_scripting:
do {
struct popup_action *act;
- choice = ui__popup_menu(nr_options, options);
- if (choice == -1 || choice >= nr_options)
+ choice = ui__popup_menu(nr_options, options, &key);
+ if (choice == -1)
break;
+ if (choice == nr_options)
+ goto do_hotkey;
+
act = &actions[choice];
key = act->fn(browser, act);
} while (key == 1);
@@ -3286,9 +3451,9 @@ out:
return key;
}
-struct perf_evsel_menu {
+struct evsel_menu {
struct ui_browser b;
- struct perf_evsel *selection;
+ struct evsel *selection;
bool lost_events, lost_events_warned;
float min_pcnt;
struct perf_env *env;
@@ -3297,13 +3462,13 @@ struct perf_evsel_menu {
static void perf_evsel_menu__write(struct ui_browser *browser,
void *entry, int row)
{
- struct perf_evsel_menu *menu = container_of(browser,
- struct perf_evsel_menu, b);
- struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
+ struct evsel_menu *menu = container_of(browser,
+ struct evsel_menu, b);
+ struct evsel *evsel = list_entry(entry, struct evsel, core.node);
struct hists *hists = evsel__hists(evsel);
bool current_entry = ui_browser__is_current_entry(browser, row);
- unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
- const char *ev_name = perf_evsel__name(evsel);
+ unsigned long nr_events = hists->stats.nr_samples;
+ const char *ev_name = evsel__name(evsel);
char bf[256], unit;
const char *warn = " ";
size_t printed;
@@ -3311,14 +3476,14 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
HE_COLORSET_NORMAL);
- if (perf_evsel__is_group_event(evsel)) {
- struct perf_evsel *pos;
+ if (evsel__is_group_event(evsel)) {
+ struct evsel *pos;
- ev_name = perf_evsel__group_name(evsel);
+ ev_name = evsel__group_name(evsel);
for_each_group_member(pos, evsel) {
struct hists *pos_hists = evsel__hists(pos);
- nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
+ nr_events += pos_hists->stats.nr_samples;
}
}
@@ -3327,7 +3492,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
unit, unit == ' ' ? "" : " ", ev_name);
ui_browser__printf(browser, "%s", bf);
- nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
+ nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
if (nr_events != 0) {
menu->lost_events = true;
if (!current_entry)
@@ -3344,12 +3509,13 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
menu->selection = evsel;
}
-static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
+static int perf_evsel_menu__run(struct evsel_menu *menu,
int nr_events, const char *help,
- struct hist_browser_timer *hbt)
+ struct hist_browser_timer *hbt,
+ bool warn_lost_event)
{
- struct perf_evlist *evlist = menu->b.priv;
- struct perf_evsel *pos;
+ struct evlist *evlist = menu->b.priv;
+ struct evsel *pos;
const char *title = "Available samples";
int delay_secs = hbt ? hbt->refresh : 0;
int key;
@@ -3363,9 +3529,12 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
switch (key) {
case K_TIMER:
- hbt->timer(hbt->arg);
+ if (hbt)
+ hbt->timer(hbt->arg);
- if (!menu->lost_events_warned && menu->lost_events) {
+ if (!menu->lost_events_warned &&
+ menu->lost_events &&
+ warn_lost_event) {
ui_browser__warn_lost_events(&menu->b);
menu->lost_events_warned = true;
}
@@ -3376,32 +3545,32 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
continue;
pos = menu->selection;
browse_hists:
- perf_evlist__set_selected(evlist, pos);
+ evlist__set_selected(evlist, pos);
/*
* Give the calling tool a chance to populate the non
* default evsel resorted hists tree.
*/
if (hbt)
hbt->timer(hbt->arg);
- key = perf_evsel__hists_browse(pos, nr_events, help,
- true, hbt,
- menu->min_pcnt,
- menu->env);
+ key = evsel__hists_browse(pos, nr_events, help, true, hbt,
+ menu->min_pcnt, menu->env,
+ warn_lost_event);
ui_browser__show_title(&menu->b, title);
switch (key) {
case K_TAB:
- if (pos->node.next == &evlist->entries)
- pos = perf_evlist__first(evlist);
+ if (pos->core.node.next == &evlist->core.entries)
+ pos = evlist__first(evlist);
else
- pos = perf_evsel__next(pos);
+ pos = evsel__next(pos);
goto browse_hists;
case K_UNTAB:
- if (pos->node.prev == &evlist->entries)
- pos = perf_evlist__last(evlist);
+ if (pos->core.node.prev == &evlist->core.entries)
+ pos = evlist__last(evlist);
else
- pos = perf_evsel__prev(pos);
+ pos = evsel__prev(pos);
goto browse_hists;
case K_SWITCH_INPUT_DATA:
+ case K_RELOAD:
case 'q':
case CTRL('c'):
goto out;
@@ -3420,6 +3589,7 @@ browse_hists:
case CTRL('c'):
goto out;
default:
+ ui_browser__warn_unhandled_hotkey(&menu->b, key, delay_secs, NULL);
continue;
}
}
@@ -3432,24 +3602,22 @@ out:
static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
void *entry)
{
- struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
+ struct evsel *evsel = list_entry(entry, struct evsel, core.node);
- if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
+ if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
return true;
return false;
}
-static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
- int nr_entries, const char *help,
- struct hist_browser_timer *hbt,
- float min_pcnt,
- struct perf_env *env)
+static int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
+ struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
+ bool warn_lost_event)
{
- struct perf_evsel *pos;
- struct perf_evsel_menu menu = {
+ struct evsel *pos;
+ struct evsel_menu menu = {
.b = {
- .entries = &evlist->entries,
+ .entries = &evlist->core.entries,
.refresh = ui_browser__list_head_refresh,
.seek = ui_browser__list_head_seek,
.write = perf_evsel_menu__write,
@@ -3464,38 +3632,54 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
ui_helpline__push("Press ESC to exit");
evlist__for_each_entry(evlist, pos) {
- const char *ev_name = perf_evsel__name(pos);
+ const char *ev_name = evsel__name(pos);
size_t line_len = strlen(ev_name) + 7;
if (menu.b.width < line_len)
menu.b.width = line_len;
}
- return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
+ return perf_evsel_menu__run(&menu, nr_entries, help,
+ hbt, warn_lost_event);
}
-int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
- struct hist_browser_timer *hbt,
- float min_pcnt,
- struct perf_env *env)
+static bool evlist__single_entry(struct evlist *evlist)
{
- int nr_entries = evlist->nr_entries;
+ int nr_entries = evlist->core.nr_entries;
+
+ if (nr_entries == 1)
+ return true;
-single_entry:
- if (nr_entries == 1) {
- struct perf_evsel *first = perf_evlist__first(evlist);
+ if (nr_entries == 2) {
+ struct evsel *last = evlist__last(evlist);
- return perf_evsel__hists_browse(first, nr_entries, help,
- false, hbt, min_pcnt,
- env);
+ if (evsel__is_dummy_event(last))
+ return true;
+ }
+
+ return false;
+}
+
+int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
+ float min_pcnt, struct perf_env *env, bool warn_lost_event)
+{
+ int nr_entries = evlist->core.nr_entries;
+
+ if (evlist__single_entry(evlist)) {
+single_entry: {
+ struct evsel *first = evlist__first(evlist);
+
+ return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
+ env, warn_lost_event);
+ }
}
if (symbol_conf.event_group) {
- struct perf_evsel *pos;
+ struct evsel *pos;
nr_entries = 0;
evlist__for_each_entry(evlist, pos) {
- if (perf_evsel__is_group_leader(pos))
+ if (evsel__is_group_leader(pos))
nr_entries++;
}
@@ -3503,6 +3687,96 @@ single_entry:
goto single_entry;
}
- return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
- hbt, min_pcnt, env);
+ return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
+ warn_lost_event);
+}
+
+static int block_hists_browser__title(struct hist_browser *browser, char *bf,
+ size_t size)
+{
+ struct hists *hists = evsel__hists(browser->block_evsel);
+ const char *evname = evsel__name(browser->block_evsel);
+ unsigned long nr_samples = hists->stats.nr_samples;
+ int ret;
+
+ ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
+ if (evname)
+ scnprintf(bf + ret, size - ret, " of event '%s'", evname);
+
+ return 0;
+}
+
+int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
+ float min_percent, struct perf_env *env)
+{
+ struct hists *hists = &bh->block_hists;
+ struct hist_browser *browser;
+ int key = -1;
+ struct popup_action action;
+ char *br_cntr_text = NULL;
+ static const char help[] =
+ " q/ESC Quit \n"
+ " B Branch counter abbr list (Optional)\n";
+
+ browser = hist_browser__new(hists);
+ if (!browser)
+ return -1;
+
+ browser->block_evsel = evsel;
+ browser->title = block_hists_browser__title;
+ browser->min_pcnt = min_percent;
+ browser->env = env;
+
+ /* reset abort key so that it can get Ctrl-C as a key */
+ SLang_reset_tty();
+ SLang_init_tty(0, 0, 0);
+ SLtty_set_suspend_state(true);
+
+ memset(&action, 0, sizeof(action));
+
+ if (!annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false))
+ annotate_opts.show_br_cntr = true;
+
+ while (1) {
+ key = hist_browser__run(browser, "? - help", true, 0);
+
+ switch (key) {
+ case 'q':
+ case K_ESC:
+ goto out;
+ case '?':
+ ui_browser__help_window(&browser->b, help);
+ break;
+ case 'a':
+ case K_ENTER:
+ if (!browser->selection ||
+ !browser->selection->sym) {
+ continue;
+ }
+
+ action.ms.map = browser->selection->map;
+ action.ms.sym = browser->selection->sym;
+ do_annotate(browser, &action);
+ continue;
+ case 'B':
+ if (br_cntr_text) {
+ ui__question_window("Branch counter abbr list",
+ br_cntr_text, "Press any key...", 0);
+ } else {
+ ui__question_window("Branch counter abbr list",
+ "\n The branch counter is not available.\n",
+ "Press any key...", 0);
+ }
+ continue;
+ default:
+ ui_browser__warn_unhandled_hotkey(&browser->b, key, 0,
+ ", use '?' to see actions");
+ continue;
+ }
+ }
+
+out:
+ hist_browser__delete(browser);
+ free(br_cntr_text);
+ return 0;
}
diff --git a/tools/perf/ui/browsers/hists.h b/tools/perf/ui/browsers/hists.h
index 23d6acb84800..de46f6c56b0e 100644
--- a/tools/perf/ui/browsers/hists.h
+++ b/tools/perf/ui/browsers/hists.h
@@ -1,8 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_UI_BROWSER_HISTS_H_
#define _PERF_UI_BROWSER_HISTS_H_ 1
#include "ui/browser.h"
+struct evsel;
+
struct hist_browser {
struct ui_browser b;
struct hists *hists;
@@ -11,6 +14,7 @@ struct hist_browser {
struct hist_browser_timer *hbt;
struct pstack *pstack;
struct perf_env *env;
+ struct evsel *block_evsel;
int print_seq;
bool show_dso;
bool show_headers;
@@ -27,7 +31,8 @@ struct hist_browser {
struct hist_browser *hist_browser__new(struct hists *hists);
void hist_browser__delete(struct hist_browser *browser);
-int hist_browser__run(struct hist_browser *browser, const char *help);
+int hist_browser__run(struct hist_browser *browser, const char *help,
+ bool warn_lost_event, int key);
void hist_browser__init(struct hist_browser *browser,
struct hists *hists);
#endif /* _PERF_UI_BROWSER_HISTS_H_ */
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index ffa5addf631d..c61ba3174a24 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -1,17 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
#include <elf.h>
#include <inttypes.h>
#include <sys/ttydefaults.h>
+#include <stdlib.h>
#include <string.h>
#include <linux/bitops.h>
-#include "../../util/util.h"
#include "../../util/debug.h"
+#include "../../util/map.h"
+#include "../../util/dso.h"
#include "../../util/symbol.h"
#include "../browser.h"
#include "../helpline.h"
#include "../keysyms.h"
#include "map.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
struct map_browser {
struct ui_browser b;
@@ -73,7 +76,7 @@ static int map_browser__run(struct map_browser *browser)
{
int key;
- if (ui_browser__show(&browser->b, browser->map->dso->long_name,
+ if (ui_browser__show(&browser->b, dso__long_name(map__dso(browser->map)),
"Press ESC to exit, %s / to search",
verbose > 0 ? "" : "restart with -v to use") < 0)
return -1;
@@ -85,8 +88,10 @@ static int map_browser__run(struct map_browser *browser)
case '/':
if (verbose > 0)
map_browser__search(browser);
+ /* fall thru */
default:
- break;
+ ui_browser__warn_unhandled_hotkey(&browser->b, key, 0, NULL);
+ continue;
case K_LEFT:
case K_ESC:
case 'q':
@@ -103,7 +108,7 @@ int map__browse(struct map *map)
{
struct map_browser mb = {
.b = {
- .entries = &map->dso->symbols[map->type],
+ .entries = dso__symbols(map__dso(map)),
.refresh = ui_browser__rb_tree_refresh,
.seek = ui_browser__rb_tree_seek,
.write = map_browser__write,
diff --git a/tools/perf/ui/browsers/map.h b/tools/perf/ui/browsers/map.h
index 2d58e4b3eb6f..0ed7dbb3a373 100644
--- a/tools/perf/ui/browsers/map.h
+++ b/tools/perf/ui/browsers/map.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_UI_MAP_BROWSER_H_
#define _PERF_UI_MAP_BROWSER_H_ 1
struct map;
diff --git a/tools/perf/ui/browsers/res_sample.c b/tools/perf/ui/browsers/res_sample.c
new file mode 100644
index 000000000000..5f60e515b12e
--- /dev/null
+++ b/tools/perf/ui/browsers/res_sample.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Display a menu with individual samples to browse with perf script */
+#include "hist.h"
+#include "evsel.h"
+#include "hists.h"
+#include "sort.h"
+#include "config.h"
+#include "time-utils.h"
+#include "../util.h"
+#include "../../util/util.h" // perf_exe()
+#include "../../perf.h"
+#include <stdlib.h>
+#include <string.h>
+#include <linux/time64.h>
+#include <linux/zalloc.h>
+
+static u64 context_len = 10 * NSEC_PER_MSEC;
+
+static int res_sample_config(const char *var, const char *value, void *data __maybe_unused)
+{
+ if (!strcmp(var, "samples.context"))
+ return perf_config_u64(&context_len, var, value);
+ return 0;
+}
+
+void res_sample_init(void)
+{
+ perf_config(res_sample_config, NULL);
+}
+
+int res_sample_browse(struct res_sample *res_samples, int num_res,
+ struct evsel *evsel, enum rstype rstype)
+{
+ char **names;
+ int i, n;
+ int choice;
+ char *cmd;
+ char pbuf[256], tidbuf[32], cpubuf[32];
+ const char *perf = perf_exe(pbuf, sizeof pbuf);
+ char trange[128], tsample[64];
+ struct res_sample *r;
+ char extra_format[256];
+
+ names = calloc(num_res, sizeof(char *));
+ if (!names)
+ return -1;
+ for (i = 0; i < num_res; i++) {
+ char tbuf[64];
+
+ timestamp__scnprintf_nsec(res_samples[i].time, tbuf, sizeof tbuf);
+ if (asprintf(&names[i], "%s: CPU %d tid %d", tbuf,
+ res_samples[i].cpu, res_samples[i].tid) < 0) {
+ while (--i >= 0)
+ zfree(&names[i]);
+ free(names);
+ return -1;
+ }
+ }
+ choice = ui__popup_menu(num_res, names, NULL);
+ for (i = 0; i < num_res; i++)
+ zfree(&names[i]);
+ free(names);
+
+ if (choice < 0 || choice >= num_res)
+ return -1;
+ r = &res_samples[choice];
+
+ n = timestamp__scnprintf_nsec(r->time - context_len, trange, sizeof trange);
+ trange[n++] = ',';
+ timestamp__scnprintf_nsec(r->time + context_len, trange + n, sizeof trange - n);
+
+ timestamp__scnprintf_nsec(r->time, tsample, sizeof tsample);
+
+ attr_to_script(extra_format, &evsel->core.attr);
+
+ if (asprintf(&cmd, "%s script %s%s --time %s %s%s %s%s --ns %s %s %s %s %s | less +/%s",
+ perf,
+ input_name ? "-i " : "",
+ input_name ? input_name : "",
+ trange,
+ r->cpu >= 0 ? "--cpu " : "",
+ r->cpu >= 0 ? (sprintf(cpubuf, "%d", r->cpu), cpubuf) : "",
+ r->tid ? "--tid " : "",
+ r->tid ? (sprintf(tidbuf, "%d", r->tid), tidbuf) : "",
+ extra_format,
+ rstype == A_ASM ? "-F +disasm" :
+ rstype == A_SOURCE ? "-F +srcline,+srccode" : "",
+ symbol_conf.inline_name ? "--inline" : "",
+ "--show-lost-events ",
+ r->tid ? "--show-switch-events --show-task-events " : "",
+ tsample) < 0)
+ return -1;
+ run_script(cmd);
+ free(cmd);
+ return 0;
+}
diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c
index ad6b6ee3770e..1e8c2c2f952d 100644
--- a/tools/perf/ui/browsers/scripts.c
+++ b/tools/perf/ui/browsers/scripts.c
@@ -1,33 +1,19 @@
-#include <elf.h>
-#include <inttypes.h>
-#include <sys/ttydefaults.h>
-#include <string.h>
-#include "../../util/sort.h"
-#include "../../util/util.h"
+// SPDX-License-Identifier: GPL-2.0
+#include "../../util/util.h" // perf_exe()
+#include "../util.h"
+#include "../../util/evlist.h"
#include "../../util/hist.h"
#include "../../util/debug.h"
+#include "../../util/session.h"
#include "../../util/symbol.h"
#include "../browser.h"
-#include "../helpline.h"
#include "../libslang.h"
-
-/* 2048 lines should be enough for a script output */
-#define MAX_LINES 2048
-
-/* 160 bytes for one output line */
-#define AVERAGE_LINE_LEN 160
-
-struct script_line {
- struct list_head node;
- char line[AVERAGE_LINE_LEN];
-};
-
-struct perf_script_browser {
- struct ui_browser b;
- struct list_head entries;
- const char *script_name;
- int nr_lines;
-};
+#include "config.h"
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
+#include <subcmd/exec-cmd.h>
+#include <stdlib.h>
#define SCRIPT_NAMELEN 128
#define SCRIPT_MAX_NO 64
@@ -39,149 +25,343 @@ struct perf_script_browser {
*/
#define SCRIPT_FULLPATH_LEN 256
-/*
- * When success, will copy the full path of the selected script
- * into the buffer pointed by script_name, and return 0.
- * Return -1 on failure.
- */
-static int list_scripts(char *script_name)
-{
- char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO];
- int i, num, choice, ret = -1;
+struct script_config {
+ const char **names;
+ char **paths;
+ int index;
+ const char *perf;
+ char extra_format[256];
+};
- /* Preset the script name to SCRIPT_NAMELEN */
- buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
- if (!buf)
- return ret;
+void attr_to_script(char *extra_format, struct perf_event_attr *attr)
+{
+ extra_format[0] = 0;
+ if (attr->read_format & PERF_FORMAT_GROUP)
+ strcat(extra_format, " -F +metric");
+ if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK)
+ strcat(extra_format, " -F +brstackinsn --xed");
+ if (attr->sample_type & PERF_SAMPLE_REGS_INTR)
+ strcat(extra_format, " -F +iregs");
+ if (attr->sample_type & PERF_SAMPLE_REGS_USER)
+ strcat(extra_format, " -F +uregs");
+ if (attr->sample_type & PERF_SAMPLE_PHYS_ADDR)
+ strcat(extra_format, " -F +phys_addr");
+}
- for (i = 0; i < SCRIPT_MAX_NO; i++) {
- names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
- paths[i] = names[i] + SCRIPT_NAMELEN;
- }
+static int add_script_option(const char *name, const char *opt,
+ struct script_config *c)
+{
+ c->names[c->index] = name;
+ if (asprintf(&c->paths[c->index],
+ "%s script %s -F +metric %s %s",
+ c->perf, opt, symbol_conf.inline_name ? " --inline" : "",
+ c->extra_format) < 0)
+ return -1;
+ c->index++;
+ return 0;
+}
- num = find_scripts(names, paths);
- if (num > 0) {
- choice = ui__popup_menu(num, names);
- if (choice < num && choice >= 0) {
- strcpy(script_name, paths[choice]);
- ret = 0;
- }
- }
+static int scripts_config(const char *var, const char *value, void *data)
+{
+ struct script_config *c = data;
- free(buf);
- return ret;
+ if (!strstarts(var, "scripts."))
+ return -1;
+ if (c->index >= SCRIPT_MAX_NO)
+ return -1;
+ c->names[c->index] = strdup(var + 7);
+ if (!c->names[c->index])
+ return -1;
+ if (asprintf(&c->paths[c->index], "%s %s", value,
+ c->extra_format) < 0)
+ return -1;
+ c->index++;
+ return 0;
}
-static void script_browser__write(struct ui_browser *browser,
- void *entry, int row)
+/*
+ * Some scripts specify the required events in their "xxx-record" file,
+ * this function will check if the events in perf.data match those
+ * mentioned in the "xxx-record".
+ *
+ * Fixme: All existing "xxx-record" are all in good formats "-e event ",
+ * which is covered well now. And new parsing code should be added to
+ * cover the future complex formats like event groups etc.
+ */
+static int check_ev_match(int dir_fd, const char *scriptname, struct perf_session *session)
{
- struct script_line *sline = list_entry(entry, struct script_line, node);
- bool current_entry = ui_browser__is_current_entry(browser, row);
+ char line[BUFSIZ];
+ FILE *fp;
+
+ {
+ char filename[NAME_MAX + 5];
+ int fd;
+
+ scnprintf(filename, sizeof(filename), "bin/%s-record", scriptname);
+ fd = openat(dir_fd, filename, O_RDONLY);
+ if (fd == -1)
+ return -1;
+ fp = fdopen(fd, "r");
+ if (!fp)
+ return -1;
+ }
- ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
- HE_COLORSET_NORMAL);
+ while (fgets(line, sizeof(line), fp)) {
+ char *p = skip_spaces(line);
+
+ if (*p == '#')
+ continue;
+
+ while (strlen(p)) {
+ int match, len;
+ struct evsel *pos;
+ char evname[128];
+
+ p = strstr(p, "-e");
+ if (!p)
+ break;
+
+ p += 2;
+ p = skip_spaces(p);
+ len = strcspn(p, " \t");
+ if (!len)
+ break;
+
+ snprintf(evname, len + 1, "%s", p);
+
+ match = 0;
+ evlist__for_each_entry(session->evlist, pos) {
+ if (evsel__name_is(pos, evname)) {
+ match = 1;
+ break;
+ }
+ }
+
+ if (!match) {
+ fclose(fp);
+ return -1;
+ }
+ }
+ }
- ui_browser__write_nstring(browser, sline->line, browser->width);
+ fclose(fp);
+ return 0;
}
-static int script_browser__run(struct perf_script_browser *browser)
+/*
+ * Return -1 if none is found, otherwise the actual scripts number.
+ *
+ * Currently the only user of this function is the script browser, which
+ * will list all statically runnable scripts, select one, execute it and
+ * show the output in a perf browser.
+ */
+static int find_scripts(char **scripts_array, char **scripts_path_array, int num,
+ int pathlen)
{
- int key;
-
- if (ui_browser__show(&browser->b, browser->script_name,
- "Press ESC to exit") < 0)
+ struct dirent *script_dirent, *lang_dirent;
+ int scripts_dir_fd, lang_dir_fd;
+ DIR *scripts_dir, *lang_dir;
+ struct perf_session *session;
+ struct perf_data data = {
+ .path = input_name,
+ .mode = PERF_DATA_MODE_READ,
+ };
+ char *temp;
+ int i = 0;
+ const char *exec_path = get_argv_exec_path();
+
+ session = perf_session__new(&data, NULL);
+ if (IS_ERR(session))
+ return PTR_ERR(session);
+
+ {
+ char scripts_path[PATH_MAX];
+
+ snprintf(scripts_path, sizeof(scripts_path), "%s/scripts", exec_path);
+ scripts_dir_fd = open(scripts_path, O_DIRECTORY);
+ pr_err("Failed to open directory '%s'", scripts_path);
+ if (scripts_dir_fd == -1) {
+ perf_session__delete(session);
+ return -1;
+ }
+ }
+ scripts_dir = fdopendir(scripts_dir_fd);
+ if (!scripts_dir) {
+ close(scripts_dir_fd);
+ perf_session__delete(session);
return -1;
+ }
- while (1) {
- key = ui_browser__run(&browser->b, 0);
-
- /* We can add some special key handling here if needed */
- break;
+ while ((lang_dirent = readdir(scripts_dir)) != NULL) {
+ if (lang_dirent->d_type != DT_DIR &&
+ (lang_dirent->d_type == DT_UNKNOWN &&
+ !is_directory_at(scripts_dir_fd, lang_dirent->d_name)))
+ continue;
+ if (!strcmp(lang_dirent->d_name, ".") || !strcmp(lang_dirent->d_name, ".."))
+ continue;
+
+#ifndef HAVE_LIBPERL_SUPPORT
+ if (strstr(lang_dirent->d_name, "perl"))
+ continue;
+#endif
+#ifndef HAVE_LIBPYTHON_SUPPORT
+ if (strstr(lang_dirent->d_name, "python"))
+ continue;
+#endif
+
+ lang_dir_fd = openat(scripts_dir_fd, lang_dirent->d_name, O_DIRECTORY);
+ if (lang_dir_fd == -1)
+ continue;
+ lang_dir = fdopendir(lang_dir_fd);
+ if (!lang_dir) {
+ close(lang_dir_fd);
+ continue;
+ }
+ while ((script_dirent = readdir(lang_dir)) != NULL) {
+ if (script_dirent->d_type == DT_DIR)
+ continue;
+ if (script_dirent->d_type == DT_UNKNOWN &&
+ is_directory_at(lang_dir_fd, script_dirent->d_name))
+ continue;
+ /* Skip those real time scripts: xxxtop.p[yl] */
+ if (strstr(script_dirent->d_name, "top."))
+ continue;
+ if (i >= num)
+ break;
+ scnprintf(scripts_path_array[i], pathlen, "%s/scripts/%s/%s",
+ exec_path,
+ lang_dirent->d_name,
+ script_dirent->d_name);
+ temp = strchr(script_dirent->d_name, '.');
+ snprintf(scripts_array[i],
+ (temp - script_dirent->d_name) + 1,
+ "%s", script_dirent->d_name);
+
+ if (check_ev_match(lang_dir_fd, scripts_array[i], session))
+ continue;
+
+ i++;
+ }
+ closedir(lang_dir);
}
- ui_browser__hide(&browser->b);
- return key;
+ closedir(scripts_dir);
+ perf_session__delete(session);
+ return i;
}
-
-int script_browse(const char *script_opt)
+/*
+ * When success, will copy the full path of the selected script
+ * into the buffer pointed by script_name, and return 0.
+ * Return -1 on failure.
+ */
+static int list_scripts(char *script_name, bool *custom,
+ struct evsel *evsel)
{
- char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN];
- char *line = NULL;
- size_t len = 0;
- ssize_t retlen;
- int ret = -1, nr_entries = 0;
- FILE *fp;
- void *buf;
- struct script_line *sline;
-
- struct perf_script_browser script = {
- .b = {
- .refresh = ui_browser__list_head_refresh,
- .seek = ui_browser__list_head_seek,
- .write = script_browser__write,
- },
- .script_name = script_name,
+ char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO];
+ int i, num, choice;
+ int ret = 0;
+ int max_std, custom_perf;
+ char pbuf[256];
+ const char *perf = perf_exe(pbuf, sizeof pbuf);
+ struct script_config scriptc = {
+ .names = (const char **)names,
+ .paths = paths,
+ .perf = perf
};
- INIT_LIST_HEAD(&script.entries);
+ script_name[0] = 0;
- /* Save each line of the output in one struct script_line object. */
- buf = zalloc((sizeof(*sline)) * MAX_LINES);
+ /* Preset the script name to SCRIPT_NAMELEN */
+ buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
if (!buf)
return -1;
- sline = buf;
-
- memset(script_name, 0, SCRIPT_FULLPATH_LEN);
- if (list_scripts(script_name))
- goto exit;
-
- sprintf(cmd, "perf script -s %s ", script_name);
- if (script_opt)
- strcat(cmd, script_opt);
-
- if (input_name) {
- strcat(cmd, " -i ");
- strcat(cmd, input_name);
+ if (evsel)
+ attr_to_script(scriptc.extra_format, &evsel->core.attr);
+ add_script_option("Show individual samples", "", &scriptc);
+ add_script_option("Show individual samples with assembler", "-F +disasm",
+ &scriptc);
+ add_script_option("Show individual samples with source", "-F +srcline,+srccode",
+ &scriptc);
+ perf_config(scripts_config, &scriptc);
+ custom_perf = scriptc.index;
+ add_script_option("Show samples with custom perf script arguments", "", &scriptc);
+ i = scriptc.index;
+ max_std = i;
+
+ for (; i < SCRIPT_MAX_NO; i++) {
+ names[i] = buf + (i - max_std) * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
+ paths[i] = names[i] + SCRIPT_NAMELEN;
}
- strcat(cmd, " 2>&1");
-
- fp = popen(cmd, "r");
- if (!fp)
- goto exit;
-
- while ((retlen = getline(&line, &len, fp)) != -1) {
- strncpy(sline->line, line, AVERAGE_LINE_LEN);
-
- /* If one output line is very large, just cut it short */
- if (retlen >= AVERAGE_LINE_LEN) {
- sline->line[AVERAGE_LINE_LEN - 1] = '\0';
- sline->line[AVERAGE_LINE_LEN - 2] = '\n';
+ num = find_scripts(names + max_std, paths + max_std, SCRIPT_MAX_NO - max_std,
+ SCRIPT_FULLPATH_LEN);
+ if (num < 0)
+ num = 0;
+ choice = ui__popup_menu(num + max_std, (char * const *)names, NULL);
+ if (choice < 0) {
+ ret = -1;
+ goto out;
+ }
+ if (choice == custom_perf) {
+ char script_args[50];
+ int key = ui_browser__input_window("perf script command",
+ "Enter perf script command line (without perf script prefix)",
+ script_args, "", 0);
+ if (key != K_ENTER) {
+ ret = -1;
+ goto out;
}
- list_add_tail(&sline->node, &script.entries);
+ sprintf(script_name, "%s script %s", perf, script_args);
+ } else if (choice < num + max_std) {
+ strcpy(script_name, paths[choice]);
+ }
+ *custom = choice >= max_std;
+
+out:
+ free(buf);
+ for (i = 0; i < max_std; i++)
+ zfree(&paths[i]);
+ return ret;
+}
- if (script.b.width < retlen)
- script.b.width = retlen;
+void run_script(char *cmd)
+{
+ pr_debug("Running %s\n", cmd);
+ SLang_reset_tty();
+ if (system(cmd) < 0)
+ pr_warning("Cannot run %s\n", cmd);
+ /*
+ * SLang doesn't seem to reset the whole terminal, so be more
+ * forceful to get back to the original state.
+ */
+ printf("\033[c\033[H\033[J");
+ fflush(stdout);
+ SLang_init_tty(0, 0, 0);
+ SLtty_set_suspend_state(true);
+ SLsmg_refresh();
+}
- if (nr_entries++ >= MAX_LINES - 1)
- break;
- sline++;
- }
+int script_browse(const char *script_opt, struct evsel *evsel)
+{
+ char *cmd, script_name[SCRIPT_FULLPATH_LEN];
+ bool custom = false;
- if (script.b.width > AVERAGE_LINE_LEN)
- script.b.width = AVERAGE_LINE_LEN;
+ memset(script_name, 0, SCRIPT_FULLPATH_LEN);
+ if (list_scripts(script_name, &custom, evsel))
+ return -1;
- free(line);
- pclose(fp);
+ if (asprintf(&cmd, "%s%s %s %s%s 2>&1 | less",
+ custom ? "perf script -s " : "",
+ script_name,
+ script_opt ? script_opt : "",
+ input_name ? "-i " : "",
+ input_name ? input_name : "") < 0)
+ return -1;
- script.nr_lines = nr_entries;
- script.b.nr_entries = nr_entries;
- script.b.entries = &script.entries;
+ run_script(cmd);
+ free(cmd);
- ret = script_browser__run(&script);
-exit:
- free(buf);
- return ret;
+ return 0;
}
diff --git a/tools/perf/ui/gtk/Build b/tools/perf/ui/gtk/Build
index ec22e899a224..eef708c502f4 100644
--- a/tools/perf/ui/gtk/Build
+++ b/tools/perf/ui/gtk/Build
@@ -1,4 +1,4 @@
-CFLAGS_gtk += -fPIC $(GTK_CFLAGS)
+CFLAGS_gtk += -fPIC $(GTK_CFLAGS) -Wno-deprecated-declarations
gtk-y += browser.o
gtk-y += hists.o
@@ -7,3 +7,8 @@ gtk-y += util.o
gtk-y += helpline.o
gtk-y += progress.o
gtk-y += annotate.o
+gtk-y += zalloc.o
+
+$(OUTPUT)ui/gtk/zalloc.o: ../lib/zalloc.c FORCE
+ $(call rule_mkdir)
+ $(call if_changed_dep,cc_o_c)
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c
index d903fd493416..8920e298420a 100644
--- a/tools/perf/ui/gtk/annotate.c
+++ b/tools/perf/ui/gtk/annotate.c
@@ -1,7 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
#include "gtk.h"
+#include "util/sort.h"
#include "util/debug.h"
#include "util/annotate.h"
+#include "util/evlist.h"
#include "util/evsel.h"
+#include "util/map.h"
+#include "util/dso.h"
+#include "util/symbol.h"
#include "ui/helpline.h"
#include <inttypes.h>
#include <signal.h>
@@ -21,23 +27,31 @@ static const char *const col_names[] = {
};
static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym,
- struct disasm_line *dl, int evidx)
+ struct disasm_line *dl, const struct evsel *evsel)
{
+ struct annotation *notes;
struct sym_hist *symhist;
+ struct sym_hist_entry *entry;
double percent = 0.0;
const char *markup;
int ret = 0;
+ u64 nr_samples = 0;
strcpy(buf, "");
- if (dl->offset == (s64) -1)
+ if (dl->al.offset == (s64) -1)
return 0;
- symhist = annotation__histogram(symbol__annotation(sym), evidx);
- if (!symbol_conf.event_group && !symhist->addr[dl->offset])
+ notes = symbol__annotation(sym);
+ symhist = annotation__histogram(notes, evsel);
+ entry = annotated_source__hist_entry(notes->src, evsel, dl->al.offset);
+ if (entry)
+ nr_samples = entry->nr_samples;
+
+ if (!symbol_conf.event_group && nr_samples == 0)
return 0;
- percent = 100.0 * symhist->addr[dl->offset] / symhist->sum;
+ percent = 100.0 * nr_samples / symhist->nr_samples;
markup = perf_gtk__get_percent_color(percent);
if (markup)
@@ -49,23 +63,23 @@ static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym,
return ret;
}
-static int perf_gtk__get_offset(char *buf, size_t size, struct symbol *sym,
- struct map *map, struct disasm_line *dl)
+static int perf_gtk__get_offset(char *buf, size_t size, struct map_symbol *ms,
+ struct disasm_line *dl)
{
- u64 start = map__rip_2objdump(map, sym->start);
+ u64 start = map__rip_2objdump(ms->map, ms->sym->start);
strcpy(buf, "");
- if (dl->offset == (s64) -1)
+ if (dl->al.offset == (s64) -1)
return 0;
- return scnprintf(buf, size, "%"PRIx64, start + dl->offset);
+ return scnprintf(buf, size, "%"PRIx64, start + dl->al.offset);
}
static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl)
{
int ret = 0;
- char *line = g_markup_escape_text(dl->line, -1);
+ char *line = g_markup_escape_text(dl->al.line, -1);
const char *markup = "<span fgcolor='gray'>";
strcpy(buf, "");
@@ -73,7 +87,7 @@ static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl)
if (!line)
return 0;
- if (dl->offset != (s64) -1)
+ if (dl->al.offset != (s64) -1)
markup = NULL;
if (markup)
@@ -86,10 +100,11 @@ static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl)
return ret;
}
-static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
- struct map *map, struct perf_evsel *evsel,
+static int perf_gtk__annotate_symbol(GtkWidget *window, struct map_symbol *ms,
+ struct evsel *evsel,
struct hist_browser_timer *hbt __maybe_unused)
{
+ struct symbol *sym = ms->sym;
struct disasm_line *pos, *n;
struct annotation *notes;
GType col_types[MAX_ANN_COLS];
@@ -118,28 +133,29 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
g_object_unref(GTK_TREE_MODEL(store));
- list_for_each_entry(pos, &notes->src->source, node) {
+ list_for_each_entry(pos, &notes->src->source, al.node) {
GtkTreeIter iter;
int ret = 0;
gtk_list_store_append(store, &iter);
- if (perf_evsel__is_group_event(evsel)) {
- for (i = 0; i < evsel->nr_members; i++) {
+ if (evsel__is_group_event(evsel)) {
+ struct evsel *cur_evsel;
+
+ for_each_group_evsel(cur_evsel, evsel__leader(evsel)) {
ret += perf_gtk__get_percent(s + ret,
sizeof(s) - ret,
sym, pos,
- evsel->idx + i);
+ cur_evsel);
ret += scnprintf(s + ret, sizeof(s) - ret, " ");
}
} else {
- ret = perf_gtk__get_percent(s, sizeof(s), sym, pos,
- evsel->idx);
+ ret = perf_gtk__get_percent(s, sizeof(s), sym, pos, evsel);
}
if (ret)
gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1);
- if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos))
+ if (perf_gtk__get_offset(s, sizeof(s), ms, pos))
gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1);
if (perf_gtk__get_line(s, sizeof(s), pos))
gtk_list_store_set(store, &iter, ANN_COL__LINE, s, -1);
@@ -147,36 +163,40 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
gtk_container_add(GTK_CONTAINER(window), view);
- list_for_each_entry_safe(pos, n, &notes->src->source, node) {
- list_del(&pos->node);
+ list_for_each_entry_safe(pos, n, &notes->src->source, al.node) {
+ list_del_init(&pos->al.node);
disasm_line__free(pos);
}
return 0;
}
-static int symbol__gtk_annotate(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel,
+static int symbol__gtk_annotate(struct map_symbol *ms, struct evsel *evsel,
struct hist_browser_timer *hbt)
{
+ struct dso *dso = map__dso(ms->map);
+ struct symbol *sym = ms->sym;
GtkWidget *window;
GtkWidget *notebook;
GtkWidget *scrolled_window;
GtkWidget *tab_label;
int err;
- if (map->dso->annotate_warned)
+ if (dso__annotate_warned(dso))
return -1;
- err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
- 0, NULL);
+ err = symbol__annotate(ms, evsel, NULL);
if (err) {
char msg[BUFSIZ];
- symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
+
+ dso__set_annotate_warned(dso);
+ symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
ui__error("Couldn't annotate %s: %s\n", sym->name, msg);
return -1;
}
+ symbol__calc_percent(sym, evsel);
+
if (perf_gtk__is_active_context(pgctx)) {
window = pgctx->main_window;
notebook = pgctx->notebook;
@@ -228,15 +248,15 @@ static int symbol__gtk_annotate(struct symbol *sym, struct map *map,
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window,
tab_label);
- perf_gtk__annotate_symbol(scrolled_window, sym, map, evsel, hbt);
+ perf_gtk__annotate_symbol(scrolled_window, ms, evsel, hbt);
return 0;
}
int hist_entry__gtk_annotate(struct hist_entry *he,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt)
{
- return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt);
+ return symbol__gtk_annotate(&he->ms, evsel, hbt);
}
void perf_gtk__show_annotations(void)
diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c
index c24d91221290..d2dadf3873fb 100644
--- a/tools/perf/ui/gtk/browser.c
+++ b/tools/perf/ui/gtk/browser.c
@@ -1,10 +1,9 @@
-#include "../evlist.h"
-#include "../cache.h"
+// SPDX-License-Identifier: GPL-2.0
+#include "gtk.h"
#include "../evsel.h"
#include "../sort.h"
#include "../hist.h"
#include "../helpline.h"
-#include "gtk.h"
#include <signal.h>
diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h
index 0a9173ff9a61..a2b497f03fd6 100644
--- a/tools/perf/ui/gtk/gtk.h
+++ b/tools/perf/ui/gtk/gtk.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_GTK_H_
#define _PERF_GTK_H_ 1
@@ -51,16 +52,15 @@ static inline GtkWidget *perf_gtk__setup_info_bar(void)
}
#endif
-struct perf_evsel;
-struct perf_evlist;
+struct evsel;
+struct evlist;
struct hist_entry;
struct hist_browser_timer;
-int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help,
- struct hist_browser_timer *hbt,
- float min_pcnt);
+int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,
+ struct hist_browser_timer *hbt, float min_pcnt);
int hist_entry__gtk_annotate(struct hist_entry *he,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt);
void perf_gtk__show_annotations(void);
diff --git a/tools/perf/ui/gtk/helpline.c b/tools/perf/ui/gtk/helpline.c
index 3388cbd12186..8ea95898a5bd 100644
--- a/tools/perf/ui/gtk/helpline.c
+++ b/tools/perf/ui/gtk/helpline.c
@@ -1,10 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "gtk.h"
#include <stdio.h>
#include <string.h>
+#include <linux/kernel.h>
-#include "gtk.h"
#include "../ui.h"
#include "../helpline.h"
-#include "../../util/debug.h"
static void gtk_helpline_pop(void)
{
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index e24f83957705..bae21f336ae6 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -1,12 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "gtk.h"
#include "../evlist.h"
-#include "../cache.h"
+#include "../callchain.h"
#include "../evsel.h"
#include "../sort.h"
#include "../hist.h"
#include "../helpline.h"
#include "../string2.h"
-#include "gtk.h"
#include <signal.h>
+#include <stdlib.h>
+#include <linux/string.h>
#define MAX_COLUMNS 32
@@ -352,7 +355,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
g_object_unref(GTK_TREE_MODEL(store));
- for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
+ for (nd = rb_first_cached(&hists->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
GtkTreeIter iter;
u64 total = hists__total_period(h->hists);
@@ -381,7 +384,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
gtk_tree_store_set(store, &iter, col_idx++, s, -1);
}
- if (symbol_conf.use_callchain && hists__has(hists, sym)) {
+ if (hist_entry__has_callchains(h) &&
+ symbol_conf.use_callchain && hists__has(hists, sym)) {
if (callchain_param.mode == CHAIN_GRAPH_REL)
total = symbol_conf.cumulate_callchain ?
h->stat_acc->period : h->stat.period;
@@ -399,7 +403,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
}
static void perf_gtk__add_hierarchy_entries(struct hists *hists,
- struct rb_root *root,
+ struct rb_root_cached *root,
GtkTreeStore *store,
GtkTreeIter *parent,
struct perf_hpp *hpp,
@@ -413,7 +417,7 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,
u64 total = hists__total_period(hists);
int size;
- for (node = rb_first(root); node; node = rb_next(node)) {
+ for (node = rb_first_cached(root); node; node = rb_next(node)) {
GtkTreeIter iter;
float percent;
char *bf;
@@ -456,7 +460,7 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,
advance_hpp(hpp, ret + 2);
}
- gtk_tree_store_set(store, &iter, col_idx, ltrim(rtrim(bf)), -1);
+ gtk_tree_store_set(store, &iter, col_idx, strim(bf), -1);
if (!he->leaf) {
hpp->buf = bf;
@@ -478,7 +482,7 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,
}
}
- if (symbol_conf.use_callchain && he->leaf) {
+ if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
if (callchain_param.mode == CHAIN_GRAPH_REL)
total = symbol_conf.cumulate_callchain ?
he->stat_acc->period : he->stat.period;
@@ -552,7 +556,7 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
first_col = false;
fmt->header(fmt, &hpp, hists, 0, NULL);
- strcat(buf, ltrim(rtrim(hpp.buf)));
+ strcat(buf, strim(hpp.buf));
}
}
@@ -586,12 +590,10 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
gtk_container_add(GTK_CONTAINER(window), view);
}
-int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
- const char *help,
- struct hist_browser_timer *hbt __maybe_unused,
- float min_pcnt)
+int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,
+ struct hist_browser_timer *hbt __maybe_unused, float min_pcnt)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
GtkWidget *vbox;
GtkWidget *notebook;
GtkWidget *info_bar;
@@ -631,18 +633,18 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
evlist__for_each_entry(evlist, pos) {
struct hists *hists = evsel__hists(pos);
- const char *evname = perf_evsel__name(pos);
+ const char *evname = evsel__name(pos);
GtkWidget *scrolled_window;
GtkWidget *tab_label;
char buf[512];
size_t size = sizeof(buf);
if (symbol_conf.event_group) {
- if (!perf_evsel__is_group_leader(pos))
+ if (!evsel__is_group_leader(pos))
continue;
- if (pos->nr_members > 1) {
- perf_evsel__group_desc(pos, buf, size);
+ if (pos->core.nr_members > 1) {
+ evsel__group_desc(pos, buf, size);
evname = buf;
}
}
diff --git a/tools/perf/ui/gtk/progress.c b/tools/perf/ui/gtk/progress.c
index b656655fbc39..eea6fcde518a 100644
--- a/tools/perf/ui/gtk/progress.c
+++ b/tools/perf/ui/gtk/progress.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
#include "gtk.h"
#include "../progress.h"
-#include "util.h"
static GtkWidget *dialog;
static GtkWidget *progress;
diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c
index 1d57676f8212..f5eee4d66873 100644
--- a/tools/perf/ui/gtk/setup.c
+++ b/tools/perf/ui/gtk/setup.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
#include "gtk.h"
-#include "../../util/cache.h"
-#include "../../util/debug.h"
+#include <linux/compiler.h>
+#include "../util.h"
extern struct perf_error_ops perf_gtk_eops;
diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c
index 00b91921edb1..c47f5c387838 100644
--- a/tools/perf/ui/gtk/util.c
+++ b/tools/perf/ui/gtk/util.c
@@ -1,10 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
#include "../util.h"
-#include "../../util/util.h"
-#include "../../util/debug.h"
#include "gtk.h"
+#include <stdlib.h>
#include <string.h>
-
+#include <linux/zalloc.h>
struct perf_gtk_context *pgctx;
diff --git a/tools/perf/ui/helpline.c b/tools/perf/ui/helpline.c
index 379039ab00d8..911182b3f5e6 100644
--- a/tools/perf/ui/helpline.c
+++ b/tools/perf/ui/helpline.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "../debug.h"
#include "helpline.h"
#include "ui.h"
-#include "../util.h"
char ui_helpline__current[512];
diff --git a/tools/perf/ui/helpline.h b/tools/perf/ui/helpline.h
index d52d0a1a881b..2165a098dee8 100644
--- a/tools/perf/ui/helpline.h
+++ b/tools/perf/ui/helpline.h
@@ -1,11 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_UI_HELPLINE_H_
#define _PERF_UI_HELPLINE_H_ 1
#include <stdio.h>
#include <stdarg.h>
-#include "../util/cache.h"
-
struct ui_helpline {
void (*pop)(void);
void (*push)(const char *msg);
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index ddb2c6fbdf91..e58327595d37 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -1,12 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
#include <inttypes.h>
#include <math.h>
+#include <stdlib.h>
+#include <string.h>
#include <linux/compiler.h>
+#include "../util/callchain.h"
+#include "../util/debug.h"
#include "../util/hist.h"
-#include "../util/util.h"
#include "../util/sort.h"
#include "../util/evsel.h"
#include "../util/evlist.h"
+#include "../util/mem-events.h"
+#include "../util/string2.h"
+#include "../util/thread.h"
+#include "../util/util.h"
/* hist period print (hpp) functions */
@@ -17,85 +26,90 @@
__ret; \
})
+static int __hpp__fmt_print(struct perf_hpp *hpp, struct hists *hists, u64 val,
+ int nr_samples, const char *fmt, int len,
+ hpp_snprint_fn print_fn, enum perf_hpp_fmt_type fmtype)
+{
+ if (fmtype == PERF_HPP_FMT_TYPE__PERCENT || fmtype == PERF_HPP_FMT_TYPE__LATENCY) {
+ double percent = 0.0;
+ u64 total = fmtype == PERF_HPP_FMT_TYPE__PERCENT ? hists__total_period(hists) :
+ hists__total_latency(hists);
+
+ if (total)
+ percent = 100.0 * val / total;
+
+ return hpp__call_print_fn(hpp, print_fn, fmt, len, percent);
+ }
+
+ if (fmtype == PERF_HPP_FMT_TYPE__AVERAGE) {
+ double avg = nr_samples ? (1.0 * val / nr_samples) : 0;
+
+ return hpp__call_print_fn(hpp, print_fn, fmt, len, avg);
+ }
+
+ return hpp__call_print_fn(hpp, print_fn, fmt, len, val);
+}
+
+struct hpp_fmt_value {
+ struct hists *hists;
+ u64 val;
+ int samples;
+};
+
static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
hpp_field_fn get_field, const char *fmt, int len,
- hpp_snprint_fn print_fn, bool fmt_percent)
+ hpp_snprint_fn print_fn, enum perf_hpp_fmt_type fmtype)
{
- int ret;
+ int ret = 0;
struct hists *hists = he->hists;
- struct perf_evsel *evsel = hists_to_evsel(hists);
+ struct evsel *evsel = hists_to_evsel(hists);
+ struct evsel *pos;
char *buf = hpp->buf;
size_t size = hpp->size;
+ int i = 0, nr_members = 1;
+ struct hpp_fmt_value *values;
- if (fmt_percent) {
- double percent = 0.0;
- u64 total = hists__total_period(hists);
+ if (evsel__is_group_event(evsel))
+ nr_members = evsel->core.nr_members;
- if (total)
- percent = 100.0 * get_field(he) / total;
+ values = calloc(nr_members, sizeof(*values));
+ if (values == NULL)
+ return 0;
- ret = hpp__call_print_fn(hpp, print_fn, fmt, len, percent);
- } else
- ret = hpp__call_print_fn(hpp, print_fn, fmt, len, get_field(he));
+ values[0].hists = evsel__hists(evsel);
+ values[0].val = get_field(he);
+ values[0].samples = he->stat.nr_events;
- if (perf_evsel__is_group_event(evsel)) {
- int prev_idx, idx_delta;
+ if (evsel__is_group_event(evsel)) {
struct hist_entry *pair;
- int nr_members = evsel->nr_members;
- prev_idx = perf_evsel__group_idx(evsel);
+ for_each_group_member(pos, evsel)
+ values[++i].hists = evsel__hists(pos);
list_for_each_entry(pair, &he->pairs.head, pairs.node) {
- u64 period = get_field(pair);
- u64 total = hists__total_period(pair->hists);
-
- if (!total)
- continue;
-
- evsel = hists_to_evsel(pair->hists);
- idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
-
- while (idx_delta--) {
- /*
- * zero-fill group members in the middle which
- * have no sample
- */
- if (fmt_percent) {
- ret += hpp__call_print_fn(hpp, print_fn,
- fmt, len, 0.0);
- } else {
- ret += hpp__call_print_fn(hpp, print_fn,
- fmt, len, 0ULL);
- }
- }
+ for (i = 0; i < nr_members; i++) {
+ if (values[i].hists != pair->hists)
+ continue;
- if (fmt_percent) {
- ret += hpp__call_print_fn(hpp, print_fn, fmt, len,
- 100.0 * period / total);
- } else {
- ret += hpp__call_print_fn(hpp, print_fn, fmt,
- len, period);
+ values[i].val = get_field(pair);
+ values[i].samples = pair->stat.nr_events;
+ break;
}
-
- prev_idx = perf_evsel__group_idx(evsel);
}
+ }
- idx_delta = nr_members - prev_idx - 1;
-
- while (idx_delta--) {
- /*
- * zero-fill group members at last which have no sample
- */
- if (fmt_percent) {
- ret += hpp__call_print_fn(hpp, print_fn,
- fmt, len, 0.0);
- } else {
- ret += hpp__call_print_fn(hpp, print_fn,
- fmt, len, 0ULL);
- }
- }
+ for (i = 0; i < nr_members; i++) {
+ if (symbol_conf.skip_empty &&
+ values[i].hists->stats.nr_samples == 0)
+ continue;
+
+ ret += __hpp__fmt_print(hpp, values[i].hists, values[i].val,
+ values[i].samples, fmt, len,
+ print_fn, fmtype);
}
+ free(values);
+
/*
* Restore original buf and size as it's where caller expects
* the result will be saved.
@@ -108,33 +122,77 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct hist_entry *he, hpp_field_fn get_field,
- const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
+ const char *fmtstr, hpp_snprint_fn print_fn,
+ enum perf_hpp_fmt_type fmtype)
{
- int len = fmt->user_len ?: fmt->len;
+ int len = max(fmt->user_len ?: fmt->len, (int)strlen(fmt->name));
if (symbol_conf.field_sep) {
return __hpp__fmt(hpp, he, get_field, fmtstr, 1,
- print_fn, fmt_percent);
+ print_fn, fmtype);
}
- if (fmt_percent)
+ if (fmtype == PERF_HPP_FMT_TYPE__PERCENT || fmtype == PERF_HPP_FMT_TYPE__LATENCY)
len -= 2; /* 2 for a space and a % sign */
else
len -= 1;
- return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmt_percent);
+ return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmtype);
}
int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct hist_entry *he, hpp_field_fn get_field,
- const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
+ const char *fmtstr, hpp_snprint_fn print_fn,
+ enum perf_hpp_fmt_type fmtype)
{
if (!symbol_conf.cumulate_callchain) {
int len = fmt->user_len ?: fmt->len;
return snprintf(hpp->buf, hpp->size, " %*s", len - 1, "N/A");
}
- return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmt_percent);
+ return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmtype);
+}
+
+int hpp__fmt_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
+ struct hist_entry *he, enum mem_stat_type mst,
+ const char *fmtstr, hpp_snprint_fn print_fn)
+{
+ struct hists *hists = he->hists;
+ int mem_stat_idx = -1;
+ char *buf = hpp->buf;
+ size_t size = hpp->size;
+ u64 total = 0;
+ int ret = 0;
+
+ for (int i = 0; i < hists->nr_mem_stats; i++) {
+ if (hists->mem_stat_types[i] == mst) {
+ mem_stat_idx = i;
+ break;
+ }
+ }
+ assert(mem_stat_idx != -1);
+
+ for (int i = 0; i < MEM_STAT_LEN; i++)
+ total += hists->mem_stat_total[mem_stat_idx].entries[i];
+ assert(total != 0);
+
+ for (int i = 0; i < MEM_STAT_LEN; i++) {
+ u64 val = he->mem_stat[mem_stat_idx].entries[i];
+
+ if (hists->mem_stat_total[mem_stat_idx].entries[i] == 0)
+ continue;
+
+ ret += hpp__call_print_fn(hpp, print_fn, fmtstr, 100.0 * val / total);
+ }
+
+ /*
+ * Restore original buf and size as it's where caller expects
+ * the result will be saved.
+ */
+ hpp->buf = buf;
+ hpp->size = size;
+
+ return ret;
}
static int field_cmp(u64 field_a, u64 field_b)
@@ -146,40 +204,103 @@ static int field_cmp(u64 field_a, u64 field_b)
return 0;
}
+static int hist_entry__new_pair(struct hist_entry *a, struct hist_entry *b,
+ hpp_field_fn get_field, int nr_members,
+ u64 **fields_a, u64 **fields_b)
+{
+ u64 *fa = calloc(nr_members, sizeof(*fa)),
+ *fb = calloc(nr_members, sizeof(*fb));
+ struct hist_entry *pair;
+
+ if (!fa || !fb)
+ goto out_free;
+
+ list_for_each_entry(pair, &a->pairs.head, pairs.node) {
+ struct evsel *evsel = hists_to_evsel(pair->hists);
+ fa[evsel__group_idx(evsel)] = get_field(pair);
+ }
+
+ list_for_each_entry(pair, &b->pairs.head, pairs.node) {
+ struct evsel *evsel = hists_to_evsel(pair->hists);
+ fb[evsel__group_idx(evsel)] = get_field(pair);
+ }
+
+ *fields_a = fa;
+ *fields_b = fb;
+ return 0;
+out_free:
+ free(fa);
+ free(fb);
+ *fields_a = *fields_b = NULL;
+ return -1;
+}
+
+static int __hpp__group_sort_idx(struct hist_entry *a, struct hist_entry *b,
+ hpp_field_fn get_field, int idx)
+{
+ struct evsel *evsel = hists_to_evsel(a->hists);
+ u64 *fields_a, *fields_b;
+ int cmp, nr_members, ret, i;
+
+ cmp = field_cmp(get_field(a), get_field(b));
+ if (!evsel__is_group_event(evsel))
+ return cmp;
+
+ nr_members = evsel->core.nr_members;
+ if (idx < 1 || idx >= nr_members)
+ return cmp;
+
+ ret = hist_entry__new_pair(a, b, get_field, nr_members, &fields_a, &fields_b);
+ if (ret) {
+ ret = cmp;
+ goto out;
+ }
+
+ ret = field_cmp(fields_a[idx], fields_b[idx]);
+ if (ret)
+ goto out;
+
+ for (i = 1; i < nr_members; i++) {
+ if (i != idx) {
+ ret = field_cmp(fields_a[i], fields_b[i]);
+ if (ret)
+ goto out;
+ }
+ }
+
+out:
+ free(fields_a);
+ free(fields_b);
+
+ return ret;
+}
+
static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
hpp_field_fn get_field)
{
s64 ret;
int i, nr_members;
- struct perf_evsel *evsel;
- struct hist_entry *pair;
+ struct evsel *evsel;
u64 *fields_a, *fields_b;
+ if (symbol_conf.group_sort_idx && symbol_conf.event_group) {
+ return __hpp__group_sort_idx(a, b, get_field,
+ symbol_conf.group_sort_idx);
+ }
+
ret = field_cmp(get_field(a), get_field(b));
if (ret || !symbol_conf.event_group)
return ret;
evsel = hists_to_evsel(a->hists);
- if (!perf_evsel__is_group_event(evsel))
+ if (!evsel__is_group_event(evsel))
return ret;
- nr_members = evsel->nr_members;
- fields_a = calloc(nr_members, sizeof(*fields_a));
- fields_b = calloc(nr_members, sizeof(*fields_b));
-
- if (!fields_a || !fields_b)
+ nr_members = evsel->core.nr_members;
+ i = hist_entry__new_pair(a, b, get_field, nr_members, &fields_a, &fields_b);
+ if (i)
goto out;
- list_for_each_entry(pair, &a->pairs.head, pairs.node) {
- evsel = hists_to_evsel(pair->hists);
- fields_a[perf_evsel__group_idx(evsel)] = get_field(pair);
- }
-
- list_for_each_entry(pair, &b->pairs.head, pairs.node) {
- evsel = hists_to_evsel(pair->hists);
- fields_b[perf_evsel__group_idx(evsel)] = get_field(pair);
- }
-
for (i = 1; i < nr_members; i++) {
ret = field_cmp(fields_a[i], fields_b[i]);
if (ret)
@@ -206,7 +327,9 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
if (ret)
return ret;
- if (a->thread != b->thread || !symbol_conf.use_callchain)
+ if ((a->thread == NULL ? NULL : RC_CHK_ACCESS(a->thread)) !=
+ (b->thread == NULL ? NULL : RC_CHK_ACCESS(b->thread)) ||
+ !hist_entry__has_callchains(a) || !symbol_conf.use_callchain)
return 0;
ret = b->callchain->max_depth - a->callchain->max_depth;
@@ -216,15 +339,56 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
return ret;
}
+static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt);
+
+static enum mem_stat_type hpp__mem_stat_type(struct perf_hpp_fmt *fmt)
+{
+ if (!perf_hpp__is_mem_stat_entry(fmt))
+ return -1;
+
+ switch (fmt->idx) {
+ case PERF_HPP__MEM_STAT_OP:
+ return PERF_MEM_STAT_OP;
+ case PERF_HPP__MEM_STAT_CACHE:
+ return PERF_MEM_STAT_CACHE;
+ case PERF_HPP__MEM_STAT_MEMORY:
+ return PERF_MEM_STAT_MEMORY;
+ case PERF_HPP__MEM_STAT_SNOOP:
+ return PERF_MEM_STAT_SNOOP;
+ case PERF_HPP__MEM_STAT_DTLB:
+ return PERF_MEM_STAT_DTLB;
+ default:
+ break;
+ }
+ pr_debug("Should not reach here\n");
+ return -1;
+}
+
+static int64_t hpp__sort_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused,
+ struct hist_entry *a, struct hist_entry *b)
+{
+ return a->stat.period - b->stat.period;
+}
+
static int hpp__width_fn(struct perf_hpp_fmt *fmt,
struct perf_hpp *hpp __maybe_unused,
struct hists *hists)
{
int len = fmt->user_len ?: fmt->len;
- struct perf_evsel *evsel = hists_to_evsel(hists);
+ struct evsel *evsel = hists_to_evsel(hists);
+
+ if (symbol_conf.event_group) {
+ int nr = 0;
+ struct evsel *pos;
- if (symbol_conf.event_group)
- len = max(len, evsel->nr_members * fmt->len);
+ for_each_group_evsel(pos, evsel) {
+ if (!symbol_conf.skip_empty ||
+ evsel__hists(pos)->stats.nr_samples)
+ nr++;
+ }
+
+ len = max(len, nr * fmt->len);
+ }
if (len < (int)strlen(fmt->name))
len = strlen(fmt->name);
@@ -233,11 +397,78 @@ static int hpp__width_fn(struct perf_hpp_fmt *fmt,
}
static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
- struct hists *hists, int line __maybe_unused,
+ struct hists *hists, int line,
int *span __maybe_unused)
{
int len = hpp__width_fn(fmt, hpp, hists);
- return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name);
+ const char *hdr = "";
+
+ if (line == hists->hpp_list->nr_header_lines - 1)
+ hdr = fmt->name;
+
+ return scnprintf(hpp->buf, hpp->size, "%*s", len, hdr);
+}
+
+static int hpp__header_mem_stat_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+ struct hists *hists, int line,
+ int *span __maybe_unused)
+{
+ char *buf = hpp->buf;
+ int ret = 0;
+ int len;
+ enum mem_stat_type mst = hpp__mem_stat_type(fmt);
+ int mem_stat_idx = -1;
+
+ for (int i = 0; i < hists->nr_mem_stats; i++) {
+ if (hists->mem_stat_types[i] == mst) {
+ mem_stat_idx = i;
+ break;
+ }
+ }
+ assert(mem_stat_idx != -1);
+
+ if (line == 0) {
+ int left, right;
+
+ len = 0;
+ /* update fmt->len for acutally used columns only */
+ for (int i = 0; i < MEM_STAT_LEN; i++) {
+ if (hists->mem_stat_total[mem_stat_idx].entries[i])
+ len += MEM_STAT_PRINT_LEN;
+ }
+ fmt->len = len;
+
+ /* print header directly if single column only */
+ if (len == MEM_STAT_PRINT_LEN)
+ return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name);
+
+ left = (len - strlen(fmt->name)) / 2 - 1;
+ right = len - left - strlen(fmt->name) - 2;
+
+ if (left < 0)
+ left = 0;
+ if (right < 0)
+ right = 0;
+
+ return scnprintf(hpp->buf, hpp->size, "%.*s %s %.*s",
+ left, graph_dotted_line, fmt->name, right, graph_dotted_line);
+ }
+
+
+ len = hpp->size;
+ for (int i = 0; i < MEM_STAT_LEN; i++) {
+ int printed;
+
+ if (hists->mem_stat_total[mem_stat_idx].entries[i] == 0)
+ continue;
+
+ printed = scnprintf(buf, len, "%*s", MEM_STAT_PRINT_LEN,
+ mem_stat_name(mst, i));
+ ret += printed;
+ buf += printed;
+ len -= printed;
+ }
+ return ret;
}
int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
@@ -269,7 +500,7 @@ static int hpp_entry_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
return (ret >= ssize) ? (ssize - 1) : ret;
}
-#define __HPP_COLOR_PERCENT_FN(_type, _field) \
+#define __HPP_COLOR_PERCENT_FN(_type, _field, _fmttype) \
static u64 he_get_##_field(struct hist_entry *he) \
{ \
return he->stat._field; \
@@ -279,15 +510,15 @@ static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, struct hist_entry *he) \
{ \
return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
- hpp_color_scnprintf, true); \
+ hpp_color_scnprintf, _fmttype); \
}
-#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
+#define __HPP_ENTRY_PERCENT_FN(_type, _field, _fmttype) \
static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, struct hist_entry *he) \
{ \
return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
- hpp_entry_scnprintf, true); \
+ hpp_entry_scnprintf, _fmttype); \
}
#define __HPP_SORT_FN(_type, _field) \
@@ -297,7 +528,7 @@ static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
return __hpp__sort(a, b, he_get_##_field); \
}
-#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
+#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field, _fmttype) \
static u64 he_get_acc_##_field(struct hist_entry *he) \
{ \
return he->stat_acc->_field; \
@@ -307,15 +538,15 @@ static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, struct hist_entry *he) \
{ \
return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
- hpp_color_scnprintf, true); \
+ hpp_color_scnprintf, _fmttype); \
}
-#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \
+#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field, _fmttype) \
static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, struct hist_entry *he) \
{ \
return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
- hpp_entry_scnprintf, true); \
+ hpp_entry_scnprintf, _fmttype); \
}
#define __HPP_SORT_ACC_FN(_type, _field) \
@@ -335,7 +566,7 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
struct perf_hpp *hpp, struct hist_entry *he) \
{ \
return hpp__fmt(fmt, hpp, he, he_get_raw_##_field, " %*"PRIu64, \
- hpp_entry_scnprintf, false); \
+ hpp_entry_scnprintf, PERF_HPP_FMT_TYPE__RAW); \
}
#define __HPP_SORT_RAW_FN(_type, _field) \
@@ -345,31 +576,88 @@ static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
return __hpp__sort(a, b, he_get_raw_##_field); \
}
+#define __HPP_ENTRY_AVERAGE_FN(_type, _field) \
+static u64 he_get_##_field(struct hist_entry *he) \
+{ \
+ return he->stat._field; \
+} \
+ \
+static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
+ struct perf_hpp *hpp, struct hist_entry *he) \
+{ \
+ return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.1f", \
+ hpp_entry_scnprintf, PERF_HPP_FMT_TYPE__AVERAGE); \
+}
+
+#define __HPP_SORT_AVERAGE_FN(_type, _field) \
+static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
+ struct hist_entry *a, struct hist_entry *b) \
+{ \
+ return __hpp__sort(a, b, he_get_##_field); \
+}
+
+#define __HPP_COLOR_MEM_STAT_FN(_name, _type) \
+static int hpp__color_mem_stat_##_name(struct perf_hpp_fmt *fmt, \
+ struct perf_hpp *hpp, \
+ struct hist_entry *he) \
+{ \
+ return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type, \
+ " %5.1f%%", hpp_color_scnprintf); \
+}
+
+#define __HPP_ENTRY_MEM_STAT_FN(_name, _type) \
+static int hpp__entry_mem_stat_##_name(struct perf_hpp_fmt *fmt, \
+ struct perf_hpp *hpp, \
+ struct hist_entry *he) \
+{ \
+ return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type, \
+ " %5.1f%%", hpp_entry_scnprintf); \
+}
-#define HPP_PERCENT_FNS(_type, _field) \
-__HPP_COLOR_PERCENT_FN(_type, _field) \
-__HPP_ENTRY_PERCENT_FN(_type, _field) \
+#define HPP_PERCENT_FNS(_type, _field, _fmttype) \
+__HPP_COLOR_PERCENT_FN(_type, _field, _fmttype) \
+__HPP_ENTRY_PERCENT_FN(_type, _field, _fmttype) \
__HPP_SORT_FN(_type, _field)
-#define HPP_PERCENT_ACC_FNS(_type, _field) \
-__HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
-__HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \
+#define HPP_PERCENT_ACC_FNS(_type, _field, _fmttype) \
+__HPP_COLOR_ACC_PERCENT_FN(_type, _field, _fmttype) \
+__HPP_ENTRY_ACC_PERCENT_FN(_type, _field, _fmttype) \
__HPP_SORT_ACC_FN(_type, _field)
#define HPP_RAW_FNS(_type, _field) \
__HPP_ENTRY_RAW_FN(_type, _field) \
__HPP_SORT_RAW_FN(_type, _field)
-HPP_PERCENT_FNS(overhead, period)
-HPP_PERCENT_FNS(overhead_sys, period_sys)
-HPP_PERCENT_FNS(overhead_us, period_us)
-HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys)
-HPP_PERCENT_FNS(overhead_guest_us, period_guest_us)
-HPP_PERCENT_ACC_FNS(overhead_acc, period)
+#define HPP_AVERAGE_FNS(_type, _field) \
+__HPP_ENTRY_AVERAGE_FN(_type, _field) \
+__HPP_SORT_AVERAGE_FN(_type, _field)
+
+#define HPP_MEM_STAT_FNS(_name, _type) \
+__HPP_COLOR_MEM_STAT_FN(_name, _type) \
+__HPP_ENTRY_MEM_STAT_FN(_name, _type)
+
+HPP_PERCENT_FNS(overhead, period, PERF_HPP_FMT_TYPE__PERCENT)
+HPP_PERCENT_FNS(latency, latency, PERF_HPP_FMT_TYPE__LATENCY)
+HPP_PERCENT_FNS(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT)
+HPP_PERCENT_FNS(overhead_us, period_us, PERF_HPP_FMT_TYPE__PERCENT)
+HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys, PERF_HPP_FMT_TYPE__PERCENT)
+HPP_PERCENT_FNS(overhead_guest_us, period_guest_us, PERF_HPP_FMT_TYPE__PERCENT)
+HPP_PERCENT_ACC_FNS(overhead_acc, period, PERF_HPP_FMT_TYPE__PERCENT)
+HPP_PERCENT_ACC_FNS(latency_acc, latency, PERF_HPP_FMT_TYPE__LATENCY)
HPP_RAW_FNS(samples, nr_events)
HPP_RAW_FNS(period, period)
+HPP_AVERAGE_FNS(weight1, weight1)
+HPP_AVERAGE_FNS(weight2, weight2)
+HPP_AVERAGE_FNS(weight3, weight3)
+
+HPP_MEM_STAT_FNS(op, OP)
+HPP_MEM_STAT_FNS(cache, CACHE)
+HPP_MEM_STAT_FNS(memory, MEMORY)
+HPP_MEM_STAT_FNS(snoop, SNOOP)
+HPP_MEM_STAT_FNS(dtlb, DTLB)
+
static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
struct hist_entry *a __maybe_unused,
struct hist_entry *b __maybe_unused)
@@ -377,6 +665,11 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
return 0;
}
+static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt)
+{
+ return fmt->sort == hpp__sort_mem_stat;
+}
+
static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a)
{
return a->header == hpp__header_fn;
@@ -390,6 +683,14 @@ static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
return a->idx == b->idx;
}
+static bool hpp__equal_mem_stat(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
+{
+ if (!perf_hpp__is_mem_stat_entry(a) || !perf_hpp__is_mem_stat_entry(b))
+ return false;
+
+ return a->entry == b->entry;
+}
+
#define HPP__COLOR_PRINT_FNS(_name, _fn, _idx) \
{ \
.name = _name, \
@@ -431,15 +732,39 @@ static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
.equal = hpp__equal, \
}
+#define HPP__MEM_STAT_PRINT_FNS(_name, _fn, _type) \
+ { \
+ .name = _name, \
+ .header = hpp__header_mem_stat_fn, \
+ .width = hpp__width_fn, \
+ .color = hpp__color_mem_stat_ ## _fn, \
+ .entry = hpp__entry_mem_stat_ ## _fn, \
+ .cmp = hpp__nop_cmp, \
+ .collapse = hpp__nop_cmp, \
+ .sort = hpp__sort_mem_stat, \
+ .idx = PERF_HPP__MEM_STAT_ ## _type, \
+ .equal = hpp__equal_mem_stat, \
+ }
+
struct perf_hpp_fmt perf_hpp__format[] = {
HPP__COLOR_PRINT_FNS("Overhead", overhead, OVERHEAD),
+ HPP__COLOR_PRINT_FNS("Latency", latency, LATENCY),
HPP__COLOR_PRINT_FNS("sys", overhead_sys, OVERHEAD_SYS),
HPP__COLOR_PRINT_FNS("usr", overhead_us, OVERHEAD_US),
HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys, OVERHEAD_GUEST_SYS),
HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us, OVERHEAD_GUEST_US),
HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc, OVERHEAD_ACC),
+ HPP__COLOR_ACC_PRINT_FNS("Latency", latency_acc, LATENCY_ACC),
HPP__PRINT_FNS("Samples", samples, SAMPLES),
- HPP__PRINT_FNS("Period", period, PERIOD)
+ HPP__PRINT_FNS("Period", period, PERIOD),
+ HPP__PRINT_FNS("Weight1", weight1, WEIGHT1),
+ HPP__PRINT_FNS("Weight2", weight2, WEIGHT2),
+ HPP__PRINT_FNS("Weight3", weight3, WEIGHT3),
+ HPP__MEM_STAT_PRINT_FNS("Mem Op", op, OP),
+ HPP__MEM_STAT_PRINT_FNS("Cache", cache, CACHE),
+ HPP__MEM_STAT_PRINT_FNS("Memory", memory, MEMORY),
+ HPP__MEM_STAT_PRINT_FNS("Snoop", snoop, SNOOP),
+ HPP__MEM_STAT_PRINT_FNS("D-TLB", dtlb, DTLB),
};
struct perf_hpp_list perf_hpp_list = {
@@ -451,10 +776,13 @@ struct perf_hpp_list perf_hpp_list = {
#undef HPP__COLOR_PRINT_FNS
#undef HPP__COLOR_ACC_PRINT_FNS
#undef HPP__PRINT_FNS
+#undef HPP__MEM_STAT_PRINT_FNS
#undef HPP_PERCENT_FNS
#undef HPP_PERCENT_ACC_FNS
#undef HPP_RAW_FNS
+#undef HPP_AVERAGE_FNS
+#undef HPP_MEM_STAT_FNS
#undef __HPP_HEADER_FN
#undef __HPP_WIDTH_FN
@@ -463,10 +791,32 @@ struct perf_hpp_list perf_hpp_list = {
#undef __HPP_COLOR_ACC_PERCENT_FN
#undef __HPP_ENTRY_ACC_PERCENT_FN
#undef __HPP_ENTRY_RAW_FN
+#undef __HPP_ENTRY_AVERAGE_FN
+#undef __HPP_COLOR_MEM_STAT_FN
+#undef __HPP_ENTRY_MEM_STAT_FN
+
#undef __HPP_SORT_FN
#undef __HPP_SORT_ACC_FN
#undef __HPP_SORT_RAW_FN
+#undef __HPP_SORT_AVERAGE_FN
+static void fmt_free(struct perf_hpp_fmt *fmt)
+{
+ /*
+ * At this point fmt should be completely
+ * unhooked, if not it's a bug.
+ */
+ BUG_ON(!list_empty(&fmt->list));
+ BUG_ON(!list_empty(&fmt->sort_list));
+
+ if (fmt->free)
+ fmt->free(fmt);
+}
+
+static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
+{
+ return a->equal && a->equal(a, b);
+}
void perf_hpp__init(void)
{
@@ -488,28 +838,48 @@ void perf_hpp__init(void)
if (is_strict_order(field_order))
return;
+ /*
+ * Overhead and latency columns are added in setup_overhead(),
+ * so they are added implicitly here only if they were added
+ * by setup_overhead() before (have was_taken flag set).
+ * This is required because setup_overhead() has more complex
+ * logic, in particular it does not add "overhead" if user
+ * specified "latency" in sort order, and vise versa.
+ */
if (symbol_conf.cumulate_callchain) {
- hpp_dimension__add_output(PERF_HPP__OVERHEAD_ACC);
+ /*
+ * Addition of fields is idempotent, so we add latency
+ * column twice to get desired order with simpler logic.
+ */
+ if (symbol_conf.prefer_latency)
+ hpp_dimension__add_output(PERF_HPP__LATENCY_ACC, true);
+ hpp_dimension__add_output(PERF_HPP__OVERHEAD_ACC, true);
+ if (symbol_conf.enable_latency)
+ hpp_dimension__add_output(PERF_HPP__LATENCY_ACC, true);
perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self";
}
- hpp_dimension__add_output(PERF_HPP__OVERHEAD);
+ if (symbol_conf.prefer_latency)
+ hpp_dimension__add_output(PERF_HPP__LATENCY, true);
+ hpp_dimension__add_output(PERF_HPP__OVERHEAD, true);
+ if (symbol_conf.enable_latency)
+ hpp_dimension__add_output(PERF_HPP__LATENCY, true);
if (symbol_conf.show_cpu_utilization) {
- hpp_dimension__add_output(PERF_HPP__OVERHEAD_SYS);
- hpp_dimension__add_output(PERF_HPP__OVERHEAD_US);
+ hpp_dimension__add_output(PERF_HPP__OVERHEAD_SYS, false);
+ hpp_dimension__add_output(PERF_HPP__OVERHEAD_US, false);
if (perf_guest) {
- hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_SYS);
- hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_US);
+ hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_SYS, false);
+ hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_US, false);
}
}
if (symbol_conf.show_nr_samples)
- hpp_dimension__add_output(PERF_HPP__SAMPLES);
+ hpp_dimension__add_output(PERF_HPP__SAMPLES, false);
if (symbol_conf.show_total_period)
- hpp_dimension__add_output(PERF_HPP__PERIOD);
+ hpp_dimension__add_output(PERF_HPP__PERIOD, false);
}
void perf_hpp_list__column_register(struct perf_hpp_list *list,
@@ -530,35 +900,82 @@ void perf_hpp_list__prepend_sort_field(struct perf_hpp_list *list,
list_add(&format->sort_list, &list->sorts);
}
-void perf_hpp__column_unregister(struct perf_hpp_fmt *format)
+static void perf_hpp__column_unregister(struct perf_hpp_fmt *format)
{
- list_del(&format->list);
+ list_del_init(&format->list);
+ list_del_init(&format->sort_list);
+ fmt_free(format);
}
-void perf_hpp__cancel_cumulate(void)
+void perf_hpp__cancel_cumulate(struct evlist *evlist)
{
- struct perf_hpp_fmt *fmt, *acc, *ovh, *tmp;
+ struct perf_hpp_fmt *fmt, *acc, *ovh, *acc_lat, *tmp;
+ struct evsel *evsel;
if (is_strict_order(field_order))
return;
ovh = &perf_hpp__format[PERF_HPP__OVERHEAD];
acc = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC];
+ acc_lat = &perf_hpp__format[PERF_HPP__LATENCY_ACC];
perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) {
- if (acc->equal(acc, fmt)) {
+ if (fmt_equal(acc, fmt) || fmt_equal(acc_lat, fmt)) {
perf_hpp__column_unregister(fmt);
continue;
}
- if (ovh->equal(ovh, fmt))
+ if (fmt_equal(ovh, fmt))
fmt->name = "Overhead";
}
+
+ evlist__for_each_entry(evlist, evsel) {
+ struct hists *hists = evsel__hists(evsel);
+ struct perf_hpp_list_node *node;
+
+ list_for_each_entry(node, &hists->hpp_formats, list) {
+ perf_hpp_list__for_each_format_safe(&node->hpp, fmt, tmp) {
+ if (fmt_equal(acc, fmt) || fmt_equal(acc_lat, fmt)) {
+ perf_hpp__column_unregister(fmt);
+ continue;
+ }
+
+ if (fmt_equal(ovh, fmt))
+ fmt->name = "Overhead";
+ }
+ }
+ }
}
-static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
+void perf_hpp__cancel_latency(struct evlist *evlist)
{
- return a->equal && a->equal(a, b);
+ struct perf_hpp_fmt *fmt, *lat, *acc, *tmp;
+ struct evsel *evsel;
+
+ if (is_strict_order(field_order))
+ return;
+ if (sort_order && strstr(sort_order, "latency"))
+ return;
+
+ lat = &perf_hpp__format[PERF_HPP__LATENCY];
+ acc = &perf_hpp__format[PERF_HPP__LATENCY_ACC];
+
+ perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) {
+ if (fmt_equal(lat, fmt) || fmt_equal(acc, fmt))
+ perf_hpp__column_unregister(fmt);
+ }
+
+ evlist__for_each_entry(evlist, evsel) {
+ struct hists *hists = evsel__hists(evsel);
+ struct perf_hpp_list_node *node;
+
+ list_for_each_entry(node, &hists->hpp_formats, list) {
+ perf_hpp_list__for_each_format_safe(&node->hpp, fmt, tmp) {
+ if (fmt_equal(lat, fmt) || fmt_equal(acc, fmt))
+ perf_hpp__column_unregister(fmt);
+ }
+ }
+ }
}
void perf_hpp__setup_output_field(struct perf_hpp_list *list)
@@ -604,29 +1021,17 @@ next:
}
-static void fmt_free(struct perf_hpp_fmt *fmt)
-{
- if (fmt->free)
- fmt->free(fmt);
-}
-
void perf_hpp__reset_output_field(struct perf_hpp_list *list)
{
struct perf_hpp_fmt *fmt, *tmp;
/* reset output fields */
- perf_hpp_list__for_each_format_safe(list, fmt, tmp) {
- list_del_init(&fmt->list);
- list_del_init(&fmt->sort_list);
- fmt_free(fmt);
- }
+ perf_hpp_list__for_each_format_safe(list, fmt, tmp)
+ perf_hpp__column_unregister(fmt);
/* reset sort keys */
- perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp) {
- list_del_init(&fmt->list);
- list_del_init(&fmt->sort_list);
- fmt_free(fmt);
- }
+ perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp)
+ perf_hpp__column_unregister(fmt);
}
/*
@@ -691,6 +1096,7 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
switch (fmt->idx) {
case PERF_HPP__OVERHEAD:
+ case PERF_HPP__LATENCY:
case PERF_HPP__OVERHEAD_SYS:
case PERF_HPP__OVERHEAD_US:
case PERF_HPP__OVERHEAD_ACC:
@@ -707,6 +1113,20 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
fmt->len = 12;
break;
+ case PERF_HPP__WEIGHT1:
+ case PERF_HPP__WEIGHT2:
+ case PERF_HPP__WEIGHT3:
+ fmt->len = 8;
+ break;
+
+ case PERF_HPP__MEM_STAT_OP:
+ case PERF_HPP__MEM_STAT_CACHE:
+ case PERF_HPP__MEM_STAT_MEMORY:
+ case PERF_HPP__MEM_STAT_SNOOP:
+ case PERF_HPP__MEM_STAT_DTLB:
+ fmt->len = MEM_STAT_LEN * MEM_STAT_PRINT_LEN;
+ break;
+
default:
break;
}
@@ -786,9 +1206,9 @@ static int add_hierarchy_fmt(struct hists *hists, struct perf_hpp_fmt *fmt)
}
int perf_hpp__setup_hists_formats(struct perf_hpp_list *list,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct perf_hpp_fmt *fmt;
struct hists *hists;
int ret;
@@ -812,3 +1232,42 @@ int perf_hpp__setup_hists_formats(struct perf_hpp_list *list,
return 0;
}
+
+int perf_hpp__alloc_mem_stats(struct perf_hpp_list *list, struct evlist *evlist)
+{
+ struct perf_hpp_fmt *fmt;
+ struct evsel *evsel;
+ enum mem_stat_type mst[16];
+ unsigned nr_mem_stats = 0;
+
+ perf_hpp_list__for_each_format(list, fmt) {
+ if (!perf_hpp__is_mem_stat_entry(fmt))
+ continue;
+
+ assert(nr_mem_stats < ARRAY_SIZE(mst));
+ mst[nr_mem_stats++] = hpp__mem_stat_type(fmt);
+ }
+
+ if (nr_mem_stats == 0)
+ return 0;
+
+ list->nr_header_lines = 2;
+
+ evlist__for_each_entry(evlist, evsel) {
+ struct hists *hists = evsel__hists(evsel);
+
+ hists->mem_stat_types = calloc(nr_mem_stats,
+ sizeof(*hists->mem_stat_types));
+ if (hists->mem_stat_types == NULL)
+ return -ENOMEM;
+
+ hists->mem_stat_total = calloc(nr_mem_stats,
+ sizeof(*hists->mem_stat_total));
+ if (hists->mem_stat_total == NULL)
+ return -ENOMEM;
+
+ memcpy(hists->mem_stat_types, mst, nr_mem_stats * sizeof(*mst));
+ hists->nr_mem_stats = nr_mem_stats;
+ }
+ return 0;
+}
diff --git a/tools/perf/ui/keysyms.c b/tools/perf/ui/keysyms.c
new file mode 100644
index 000000000000..b64564b07f2f
--- /dev/null
+++ b/tools/perf/ui/keysyms.c
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include "keysyms.h"
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+
+const char *key_name(int key, char *bf, size_t size)
+{
+ if (isprint(key)) {
+ scnprintf(bf, size, "%c", key);
+ } else if (key < 32) {
+ scnprintf(bf, size, "Ctrl+%c", key + '@');
+ } else {
+ const char *name = NULL;
+
+ switch (key) {
+ case K_DOWN: name = "Down"; break;
+ case K_END: name = "End"; break;
+ case K_ENTER: name = "Enter"; break;
+ case K_ESC: name = "ESC"; break;
+ case K_F1: name = "F1"; break;
+ case K_HOME: name = "Home"; break;
+ case K_LEFT: name = "Left"; break;
+ case K_PGDN: name = "PgDown"; break;
+ case K_PGUP: name = "PgUp"; break;
+ case K_RIGHT: name = "Right"; break;
+ case K_TAB: name = "Tab"; break;
+ case K_UNTAB: name = "Untab"; break;
+ case K_UP: name = "Up"; break;
+ case K_BKSPC: name = "Backspace"; break;
+ case K_DEL: name = "Del"; break;
+ default:
+ if (key >= SL_KEY_F(1) && key <= SL_KEY_F(63))
+ scnprintf(bf, size, "F%d", key - SL_KEY_F(0));
+ else
+ scnprintf(bf, size, "Unknown (%d)", key);
+ }
+
+ if (name)
+ scnprintf(bf, size, "%s", name);
+ }
+
+ return bf;
+}
diff --git a/tools/perf/ui/keysyms.h b/tools/perf/ui/keysyms.h
index 65092d576b4e..969060edc362 100644
--- a/tools/perf/ui/keysyms.h
+++ b/tools/perf/ui/keysyms.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_KEYSYMS_H_
#define _PERF_KEYSYMS_H_ 1
@@ -24,5 +25,8 @@
#define K_ERROR -2
#define K_RESIZE -3
#define K_SWITCH_INPUT_DATA -4
+#define K_RELOAD -5
+
+const char *key_name(int key, char *bf, size_t size);
#endif /* _PERF_KEYSYMS_H_ */
diff --git a/tools/perf/ui/libslang.h b/tools/perf/ui/libslang.h
index db816695ad97..6722561e0458 100644
--- a/tools/perf/ui/libslang.h
+++ b/tools/perf/ui/libslang.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_UI_SLANG_H_
#define _PERF_UI_SLANG_H_ 1
/*
@@ -9,23 +10,12 @@
#ifndef HAVE_LONG_LONG
#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
#endif
-#include <slang.h>
-#if SLANG_VERSION < 20104
-#define slsmg_printf(msg, args...) \
- SLsmg_printf((char *)(msg), ##args)
-#define slsmg_vprintf(msg, vargs) \
- SLsmg_vprintf((char *)(msg), vargs)
-#define slsmg_write_nstring(msg, len) \
- SLsmg_write_nstring((char *)(msg), len)
-#define sltt_set_color(obj, name, fg, bg) \
- SLtt_set_color(obj,(char *)(name), (char *)(fg), (char *)(bg))
-#else
-#define slsmg_printf SLsmg_printf
-#define slsmg_vprintf SLsmg_vprintf
-#define slsmg_write_nstring SLsmg_write_nstring
-#define sltt_set_color SLtt_set_color
-#endif
+/* Enable future slang's corrected function prototypes. */
+#define ENABLE_SLFUTURE_CONST 1
+#define ENABLE_SLFUTURE_VOID 1
+
+#include <slang.h>
#define SL_KEY_UNTAB 0x1000
diff --git a/tools/perf/ui/progress.c b/tools/perf/ui/progress.c
index a0f24c7115c5..99d60223c74b 100644
--- a/tools/perf/ui/progress.c
+++ b/tools/perf/ui/progress.c
@@ -1,4 +1,5 @@
-#include "../cache.h"
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
#include "progress.h"
static void null_progress__update(struct ui_progress *p __maybe_unused)
@@ -14,21 +15,29 @@ struct ui_progress_ops *ui_progress__ops = &null_progress__ops;
void ui_progress__update(struct ui_progress *p, u64 adv)
{
+ u64 last = p->curr;
+
p->curr += adv;
if (p->curr >= p->next) {
- p->next += p->step;
+ u64 nr = DIV_ROUND_UP(p->curr - last, p->step);
+
+ p->next += nr * p->step;
ui_progress__ops->update(p);
}
}
-void ui_progress__init(struct ui_progress *p, u64 total, const char *title)
+void __ui_progress__init(struct ui_progress *p, u64 total,
+ const char *title, bool size)
{
p->curr = 0;
- p->next = p->step = total / 16;
+ p->next = p->step = total / 16 ?: 1;
p->total = total;
p->title = title;
+ p->size = size;
+ if (ui_progress__ops->init)
+ ui_progress__ops->init(p);
}
void ui_progress__finish(void)
diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h
index 717d39d3052b..4f52c37b2f09 100644
--- a/tools/perf/ui/progress.h
+++ b/tools/perf/ui/progress.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_UI_PROGRESS_H_
#define _PERF_UI_PROGRESS_H_ 1
@@ -8,12 +9,22 @@ void ui_progress__finish(void);
struct ui_progress {
const char *title;
u64 curr, next, step, total;
+ bool size;
};
-void ui_progress__init(struct ui_progress *p, u64 total, const char *title);
+void __ui_progress__init(struct ui_progress *p, u64 total,
+ const char *title, bool size);
+
+#define ui_progress__init(p, total, title) \
+ __ui_progress__init(p, total, title, false)
+
+#define ui_progress__init_size(p, total, title) \
+ __ui_progress__init(p, total, title, true)
+
void ui_progress__update(struct ui_progress *p, u64 adv);
struct ui_progress_ops {
+ void (*init)(struct ui_progress *p);
void (*update)(struct ui_progress *p);
void (*finish)(void);
};
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c
index caf1ce6f5152..ff800047e697 100644
--- a/tools/perf/ui/setup.c
+++ b/tools/perf/ui/setup.c
@@ -1,12 +1,14 @@
-#include <pthread.h>
+// SPDX-License-Identifier: GPL-2.0
#include <dlfcn.h>
+#include <signal.h>
+#include <unistd.h>
-#include "../util/cache.h"
+#include <subcmd/pager.h>
#include "../util/debug.h"
#include "../util/hist.h"
-#include "../util/util.h"
+#include "ui.h"
-pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
+struct mutex ui__lock;
void *perf_gtk_handle;
int use_browser = -1;
@@ -74,6 +76,7 @@ int stdio__config_color(const struct option *opt __maybe_unused,
void setup_browser(bool fallback_to_pager)
{
+ mutex_init(&ui__lock);
if (use_browser < 2 && (!isatty(1) || dump_trace))
use_browser = 0;
@@ -88,9 +91,9 @@ void setup_browser(bool fallback_to_pager)
printf("GTK browser requested but could not find %s\n",
PERF_GTK_DSO);
sleep(1);
+ use_browser = 1;
/* fall through */
case 1:
- use_browser = 1;
if (ui__init() == 0)
break;
/* fall through */
@@ -116,4 +119,23 @@ void exit_browser(bool wait_for_ok)
default:
break;
}
+ mutex_destroy(&ui__lock);
+}
+
+void pthread__block_sigwinch(void)
+{
+ sigset_t set;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGWINCH);
+ pthread_sigmask(SIG_BLOCK, &set, NULL);
+}
+
+void pthread__unblock_sigwinch(void)
+{
+ sigset_t set;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGWINCH);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
}
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 42e432bd2eb4..8c4c8925df2c 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -1,86 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <limits.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <linux/string.h>
-#include "../../util/util.h"
+#include "../../util/callchain.h"
+#include "../../util/debug.h"
+#include "../../util/event.h"
#include "../../util/hist.h"
+#include "../../util/map.h"
+#include "../../util/maps.h"
+#include "../../util/symbol.h"
#include "../../util/sort.h"
#include "../../util/evsel.h"
#include "../../util/srcline.h"
#include "../../util/string2.h"
#include "../../util/thread.h"
-#include "../../util/sane_ctype.h"
+#include "../../util/block-info.h"
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
{
int i;
int ret = fprintf(fp, " ");
+ if (left_margin > USHRT_MAX)
+ left_margin = USHRT_MAX;
+
for (i = 0; i < left_margin; i++)
ret += fprintf(fp, " ");
return ret;
}
-static size_t inline__fprintf(struct map *map, u64 ip, int left_margin,
- int depth, int depth_mask, FILE *fp)
-{
- struct dso *dso;
- struct inline_node *node;
- struct inline_list *ilist;
- int ret = 0, i;
-
- if (map == NULL)
- return 0;
-
- dso = map->dso;
- if (dso == NULL)
- return 0;
-
- if (dso->kernel != DSO_TYPE_USER)
- return 0;
-
- node = dso__parse_addr_inlines(dso,
- map__rip_2objdump(map, ip));
- if (node == NULL)
- return 0;
-
- list_for_each_entry(ilist, &node->val, list) {
- if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
- ret += callchain__fprintf_left_margin(fp, left_margin);
-
- for (i = 0; i < depth; i++) {
- if (depth_mask & (1 << i))
- ret += fprintf(fp, "|");
- else
- ret += fprintf(fp, " ");
- ret += fprintf(fp, " ");
- }
-
- if (callchain_param.key == CCKEY_ADDRESS ||
- callchain_param.key == CCKEY_SRCLINE) {
- if (ilist->filename != NULL)
- ret += fprintf(fp, "%s:%d (inline)",
- ilist->filename,
- ilist->line_nr);
- else
- ret += fprintf(fp, "??");
- } else if (ilist->funcname != NULL)
- ret += fprintf(fp, "%s (inline)",
- ilist->funcname);
- else if (ilist->filename != NULL)
- ret += fprintf(fp, "%s:%d (inline)",
- ilist->filename,
- ilist->line_nr);
- else
- ret += fprintf(fp, "??");
-
- ret += fprintf(fp, "\n");
- }
- }
-
- inline_node__delete(node);
- return ret;
-}
-
static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
int left_margin)
{
@@ -126,12 +79,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
str = callchain_list__sym_name(chain, bf, sizeof(bf), false);
if (symbol_conf.show_branchflag_count) {
- if (!period)
- callchain_list_counts__printf_value(node, chain, NULL,
- buf, sizeof(buf));
- else
- callchain_list_counts__printf_value(NULL, chain, NULL,
- buf, sizeof(buf));
+ callchain_list_counts__printf_value(chain, NULL,
+ buf, sizeof(buf));
if (asprintf(&alloc_str, "%s%s", str, buf) < 0)
str = "Not enough memory!";
@@ -143,9 +92,6 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
fputc('\n', fp);
free(alloc_str);
- if (symbol_conf.inline_name)
- ret += inline__fprintf(chain->ms.map, chain->ip,
- left_margin, depth, depth_mask, fp);
return ret;
}
@@ -295,7 +241,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
* displayed twice.
*/
if (!i++ && field_order == NULL &&
- sort_order && !prefixcmp(sort_order, "sym"))
+ sort_order && strstarts(sort_order, "sym"))
continue;
if (!printed) {
@@ -315,18 +261,11 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
if (symbol_conf.show_branchflag_count)
ret += callchain_list_counts__printf_value(
- NULL, chain, fp, NULL, 0);
+ chain, fp, NULL, 0);
ret += fprintf(fp, "\n");
if (++entries_printed == callchain_param.print_limit)
break;
-
- if (symbol_conf.inline_name)
- ret += inline__fprintf(chain->ms.map,
- chain->ip,
- left_margin,
- 0, 0,
- fp);
}
root = &cnode->rb_root;
}
@@ -585,11 +524,11 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
* dynamic entries are right-aligned but we want left-aligned
* in the hierarchy mode
*/
- printed += fprintf(fp, "%s%s", sep ?: " ", ltrim(buf));
+ printed += fprintf(fp, "%s%s", sep ?: " ", skip_spaces(buf));
}
printed += putc('\n', fp);
- if (symbol_conf.use_callchain && he->leaf) {
+ if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
u64 total = hists__total_period(hists);
printed += hist_entry_callchain__fprintf(he, total, 0, fp);
@@ -600,13 +539,55 @@ out:
return printed;
}
+static int hist_entry__block_fprintf(struct hist_entry *he,
+ char *bf, size_t size,
+ FILE *fp)
+{
+ struct block_hist *bh = container_of(he, struct block_hist, he);
+ int ret = 0;
+
+ for (unsigned int i = 0; i < bh->block_hists.nr_entries; i++) {
+ struct perf_hpp hpp = {
+ .buf = bf,
+ .size = size,
+ .skip = false,
+ };
+
+ bh->block_idx = i;
+ hist_entry__snprintf(he, &hpp);
+
+ if (!hpp.skip)
+ ret += fprintf(fp, "%s\n", bf);
+ }
+
+ return ret;
+}
+
+static int hist_entry__individual_block_fprintf(struct hist_entry *he,
+ char *bf, size_t size,
+ FILE *fp)
+{
+ int ret = 0;
+
+ struct perf_hpp hpp = {
+ .buf = bf,
+ .size = size,
+ .skip = false,
+ };
+
+ hist_entry__snprintf(he, &hpp);
+ if (!hpp.skip)
+ ret += fprintf(fp, "%s\n", bf);
+
+ return ret;
+}
+
static int hist_entry__fprintf(struct hist_entry *he, size_t size,
char *bf, size_t bfsz, FILE *fp,
- bool use_callchain)
+ bool ignore_callchains)
{
int ret;
int callchain_ret = 0;
- int inline_ret = 0;
struct perf_hpp hpp = {
.buf = bf,
.size = size,
@@ -620,21 +601,21 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
if (symbol_conf.report_hierarchy)
return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);
+ if (symbol_conf.report_block)
+ return hist_entry__block_fprintf(he, bf, size, fp);
+
+ if (symbol_conf.report_individual_block)
+ return hist_entry__individual_block_fprintf(he, bf, size, fp);
+
hist_entry__snprintf(he, &hpp);
ret = fprintf(fp, "%s\n", bf);
- if (use_callchain)
+ if (hist_entry__has_callchains(he) && !ignore_callchains)
callchain_ret = hist_entry_callchain__fprintf(he, total_period,
0, fp);
- if (callchain_ret == 0 && symbol_conf.inline_name) {
- inline_ret = inline__fprintf(he->ms.map, he->ip, 0, 0, 0, fp);
- ret += inline_ret;
- if (inline_ret > 0)
- ret += fprintf(fp, "\n");
- } else
- ret += callchain_ret;
+ ret += callchain_ret;
return ret;
}
@@ -642,10 +623,14 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
static int print_hierarchy_indent(const char *sep, int indent,
const char *line, FILE *fp)
{
+ int width;
+
if (sep != NULL || indent < 2)
return 0;
- return fprintf(fp, "%-.*s", (indent - 2) * HIERARCHY_INDENT, line);
+ width = (indent - 2) * HIERARCHY_INDENT;
+
+ return fprintf(fp, "%-*.*s", width, width, line);
}
static int hists__fprintf_hierarchy_headers(struct hists *hists,
@@ -658,45 +643,58 @@ static int hists__fprintf_hierarchy_headers(struct hists *hists,
unsigned header_width = 0;
struct perf_hpp_fmt *fmt;
struct perf_hpp_list_node *fmt_node;
+ struct perf_hpp_list *hpp_list = hists->hpp_list;
const char *sep = symbol_conf.field_sep;
indent = hists->nr_hpp_node;
- /* preserve max indent depth for column headers */
- print_hierarchy_indent(sep, indent, spaces, fp);
-
/* the first hpp_list_node is for overhead columns */
fmt_node = list_first_entry(&hists->hpp_formats,
struct perf_hpp_list_node, list);
- perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
- fmt->header(fmt, hpp, hists, 0, NULL);
- fprintf(fp, "%s%s", hpp->buf, sep ?: " ");
- }
+ for (int line = 0; line < hpp_list->nr_header_lines; line++) {
+ /* first # is displayed one level up */
+ if (line)
+ fprintf(fp, "# ");
- /* combine sort headers with ' / ' */
- first_node = true;
- list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
- if (!first_node)
- header_width += fprintf(fp, " / ");
- first_node = false;
+ /* preserve max indent depth for column headers */
+ print_hierarchy_indent(sep, indent, " ", fp);
- first_col = true;
perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
- if (perf_hpp__should_skip(fmt, hists))
- continue;
+ fmt->header(fmt, hpp, hists, line, NULL);
+ fprintf(fp, "%s%s", hpp->buf, sep ?: " ");
+ }
- if (!first_col)
- header_width += fprintf(fp, "+");
- first_col = false;
+ if (line < hpp_list->nr_header_lines - 1)
+ goto next_line;
+
+ /* combine sort headers with ' / ' */
+ first_node = true;
+ list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
+ if (!first_node)
+ header_width += fprintf(fp, " / ");
+ first_node = false;
+
+ first_col = true;
+ perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
+ if (perf_hpp__should_skip(fmt, hists))
+ continue;
+
+ if (!first_col)
+ header_width += fprintf(fp, "+");
+ first_col = false;
- fmt->header(fmt, hpp, hists, 0, NULL);
+ fmt->header(fmt, hpp, hists, line, NULL);
- header_width += fprintf(fp, "%s", trim(hpp->buf));
+ header_width += fprintf(fp, "%s", strim(hpp->buf));
+ }
}
+
+next_line:
+ fprintf(fp, "\n");
}
- fprintf(fp, "\n# ");
+ fprintf(fp, "# ");
/* preserve max indent depth for initial dots */
print_hierarchy_indent(sep, indent, dots, fp);
@@ -835,7 +833,7 @@ int hists__fprintf_headers(struct hists *hists, FILE *fp)
size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
int max_cols, float min_pcnt, FILE *fp,
- bool use_callchain)
+ bool ignore_callchains)
{
struct rb_node *nd;
size_t ret = 0;
@@ -868,18 +866,23 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
indent = hists__overhead_width(hists) + 4;
- for (nd = rb_first(&hists->entries); nd; nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) {
+ for (nd = rb_first_cached(&hists->entries); nd;
+ nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
float percent;
if (h->filtered)
continue;
- percent = hist_entry__get_percent_limit(h);
+ if (symbol_conf.report_individual_block)
+ percent = block_info__total_cycles_percent(h);
+ else
+ percent = hist_entry__get_percent_limit(h);
+
if (percent < min_pcnt)
continue;
- ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, use_callchain);
+ ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, ignore_callchains);
if (max_rows && ++nr_rows >= max_rows)
break;
@@ -891,7 +894,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) {
int depth = hists->nr_hpp_node + h->depth + 1;
- print_hierarchy_indent(sep, depth, spaces, fp);
+ print_hierarchy_indent(sep, depth, " ", fp);
fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt);
if (max_rows && ++nr_rows >= max_rows)
@@ -899,8 +902,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
}
if (h->ms.map == NULL && verbose > 1) {
- __map_groups__fprintf_maps(h->thread->mg,
- MAP__FUNCTION, fp);
+ maps__fprintf(thread__maps(h->thread), fp);
fprintf(fp, "%.10s end\n", graph_dotted_line);
}
}
@@ -916,19 +918,25 @@ size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)
{
int i;
size_t ret = 0;
+ u32 total = stats->nr_events[0];
for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
const char *name;
- if (stats->nr_events[i] == 0)
- continue;
-
name = perf_event__name(i);
if (!strcmp(name, "UNKNOWN"))
continue;
+ if (symbol_conf.skip_empty && !stats->nr_events[i])
+ continue;
- ret += fprintf(fp, "%16s events: %10d\n", name,
- stats->nr_events[i]);
+ if (i && total) {
+ ret += fprintf(fp, "%20s events: %10d (%4.1f%%)\n",
+ name, stats->nr_events[i],
+ 100.0 * stats->nr_events[i] / total);
+ } else {
+ ret += fprintf(fp, "%20s events: %10d\n",
+ name, stats->nr_events[i]);
+ }
}
return ret;
diff --git a/tools/perf/ui/tui/Build b/tools/perf/ui/tui/Build
index 9e4c6ca41a9f..2ac058ad1a61 100644
--- a/tools/perf/ui/tui/Build
+++ b/tools/perf/ui/tui/Build
@@ -1,4 +1,4 @@
-libperf-y += setup.o
-libperf-y += util.o
-libperf-y += helpline.o
-libperf-y += progress.o
+perf-ui-y += setup.o
+perf-ui-y += util.o
+perf-ui-y += helpline.o
+perf-ui-y += progress.o
diff --git a/tools/perf/ui/tui/helpline.c b/tools/perf/ui/tui/helpline.c
index 88f5143a5981..b39451314f43 100644
--- a/tools/perf/ui/tui/helpline.c
+++ b/tools/perf/ui/tui/helpline.c
@@ -1,9 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <pthread.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
-#include "../../util/debug.h"
#include "../helpline.h"
#include "../ui.h"
#include "../libslang.h"
@@ -21,9 +22,9 @@ static void tui_helpline__push(const char *msg)
SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
SLsmg_set_color(0);
- SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols);
+ SLsmg_write_nstring(msg, SLtt_Screen_Cols);
SLsmg_refresh();
- strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0';
+ strlcpy(ui_helpline__current, msg, sz);
}
static int tui_helpline__show(const char *format, va_list ap)
@@ -31,7 +32,7 @@ static int tui_helpline__show(const char *format, va_list ap)
int ret;
static int backlog;
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
ret = vscnprintf(ui_helpline__last_msg + backlog,
sizeof(ui_helpline__last_msg) - backlog, format, ap);
backlog += ret;
@@ -43,7 +44,7 @@ static int tui_helpline__show(const char *format, va_list ap)
SLsmg_refresh();
backlog = 0;
}
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
return ret;
}
diff --git a/tools/perf/ui/tui/progress.c b/tools/perf/ui/tui/progress.c
index c4b99008e2c9..71b6c8d9474f 100644
--- a/tools/perf/ui/tui/progress.c
+++ b/tools/perf/ui/tui/progress.c
@@ -1,12 +1,33 @@
-#include "../cache.h"
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
#include "../progress.h"
#include "../libslang.h"
#include "../ui.h"
#include "tui.h"
+#include "units.h"
#include "../browser.h"
+static void __tui_progress__init(struct ui_progress *p)
+{
+ p->next = p->step = p->total / (SLtt_Screen_Cols - 2) ?: 1;
+}
+
+static int get_title(struct ui_progress *p, char *buf, size_t size)
+{
+ char buf_cur[20];
+ char buf_tot[20];
+ int ret;
+
+ ret = unit_number__scnprintf(buf_cur, sizeof(buf_cur), p->curr);
+ ret += unit_number__scnprintf(buf_tot, sizeof(buf_tot), p->total);
+
+ return ret + scnprintf(buf, size, "%s [%s/%s]",
+ p->title, buf_cur, buf_tot);
+}
+
static void tui_progress__update(struct ui_progress *p)
{
+ char buf[100], *title = (char *) p->title;
int bar, y;
/*
* FIXME: We should have a per UI backend way of showing progress,
@@ -18,19 +39,24 @@ static void tui_progress__update(struct ui_progress *p)
if (p->total == 0)
return;
+ if (p->size) {
+ get_title(p, buf, sizeof(buf));
+ title = buf;
+ }
+
ui__refresh_dimensions(false);
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
y = SLtt_Screen_Rows / 2 - 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);
SLsmg_gotorc(y++, 1);
- SLsmg_write_string((char *)p->title);
+ SLsmg_write_string(title);
SLsmg_fill_region(y, 1, 1, SLtt_Screen_Cols - 2, ' ');
SLsmg_set_color(HE_COLORSET_SELECTED);
bar = ((SLtt_Screen_Cols - 2) * p->curr) / p->total;
SLsmg_fill_region(y, 1, 1, bar, ' ');
SLsmg_refresh();
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
}
static void tui_progress__finish(void)
@@ -41,16 +67,16 @@ static void tui_progress__finish(void)
return;
ui__refresh_dimensions(false);
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
y = SLtt_Screen_Rows / 2 - 2;
SLsmg_set_color(0);
SLsmg_fill_region(y, 0, 3, SLtt_Screen_Cols, ' ');
SLsmg_refresh();
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
}
-static struct ui_progress_ops tui_progress__ops =
-{
+static struct ui_progress_ops tui_progress__ops = {
+ .init = __tui_progress__init,
.update = tui_progress__update,
.finish = tui_progress__finish,
};
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c
index d9350a1da48b..022534eed68c 100644
--- a/tools/perf/ui/tui/setup.c
+++ b/tools/perf/ui/tui/setup.c
@@ -1,14 +1,16 @@
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
#include <linux/kernel.h>
#ifdef HAVE_BACKTRACE_SUPPORT
#include <execinfo.h>
#endif
-#include "../../util/cache.h"
+#include "../../util/color.h"
#include "../../util/debug.h"
-#include "../../util/util.h"
#include "../browser.h"
#include "../helpline.h"
#include "../ui.h"
@@ -28,10 +30,10 @@ void ui__refresh_dimensions(bool force)
{
if (force || ui__need_resize) {
ui__need_resize = 0;
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
SLtt_get_screen_size();
SLsmg_reinit_smg();
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
}
}
@@ -106,7 +108,7 @@ static void ui__signal_backtrace(int sig)
printf("-------- backtrace --------\n");
size = backtrace(stackdump, ARRAY_SIZE(stackdump));
- backtrace_symbols_fd(stackdump, size, STDOUT_FILENO);
+ __dump_stack(stdout, stackdump, size);
exit(0);
}
@@ -121,6 +123,23 @@ static void ui__signal(int sig)
exit(0);
}
+static void ui__sigcont(int sig)
+{
+ static struct termios tty;
+
+ if (sig == SIGTSTP) {
+ while (tcgetattr(SLang_TT_Read_FD, &tty) == -1 && errno == EINTR)
+ ;
+ while (write(SLang_TT_Read_FD, PERF_COLOR_RESET, sizeof(PERF_COLOR_RESET) - 1) == -1 && errno == EINTR)
+ ;
+ raise(SIGSTOP);
+ } else {
+ while (tcsetattr(SLang_TT_Read_FD, TCSADRAIN, &tty) == -1 && errno == EINTR)
+ ;
+ raise(SIGWINCH);
+ }
+}
+
int ui__init(void)
{
int err;
@@ -135,6 +154,7 @@ int ui__init(void)
err = SLang_init_tty(-1, 0, 0);
if (err < 0)
goto out;
+ SLtty_set_suspend_state(true);
err = SLkp_init();
if (err < 0) {
@@ -142,13 +162,15 @@ int ui__init(void)
goto out;
}
- SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB);
+ SLkp_define_keysym("^(kB)", SL_KEY_UNTAB);
signal(SIGSEGV, ui__signal_backtrace);
signal(SIGFPE, ui__signal_backtrace);
signal(SIGINT, ui__signal);
signal(SIGQUIT, ui__signal);
signal(SIGTERM, ui__signal);
+ signal(SIGTSTP, ui__sigcont);
+ signal(SIGCONT, ui__sigcont);
perf_error__register(&perf_tui_eops);
@@ -169,9 +191,11 @@ void ui__exit(bool wait_for_ok)
"Press any key...", 0);
SLtt_set_cursor_visibility(1);
- SLsmg_refresh();
- SLsmg_reset_smg();
+ if (mutex_trylock(&ui__lock)) {
+ SLsmg_refresh();
+ SLsmg_reset_smg();
+ mutex_unlock(&ui__lock);
+ }
SLang_reset_tty();
-
perf_error__unregister(&perf_tui_eops);
}
diff --git a/tools/perf/ui/tui/tui.h b/tools/perf/ui/tui/tui.h
index 18961c7b6ec5..8de06f634c56 100644
--- a/tools/perf/ui/tui/tui.h
+++ b/tools/perf/ui/tui/tui.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_TUI_H_
#define _PERF_TUI_H_ 1
diff --git a/tools/perf/ui/tui/util.c b/tools/perf/ui/tui/util.c
index d96ad7c8325d..e4d322ce0b54 100644
--- a/tools/perf/ui/tui/util.c
+++ b/tools/perf/ui/tui/util.c
@@ -1,11 +1,10 @@
-#include "../../util/util.h"
+// SPDX-License-Identifier: GPL-2.0
#include <signal.h>
#include <stdbool.h>
#include <string.h>
+#include <stdlib.h>
#include <sys/ttydefaults.h>
-#include "../../util/cache.h"
-#include "../../util/debug.h"
#include "../browser.h"
#include "../keysyms.h"
#include "../helpline.h"
@@ -24,7 +23,7 @@ static void ui_browser__argv_write(struct ui_browser *browser,
ui_browser__write_nstring(browser, *arg, browser->width);
}
-static int popup_menu__run(struct ui_browser *menu)
+static int popup_menu__run(struct ui_browser *menu, int *keyp)
{
int key;
@@ -46,6 +45,11 @@ static int popup_menu__run(struct ui_browser *menu)
key = -1;
break;
default:
+ if (keyp) {
+ *keyp = key;
+ key = menu->nr_entries;
+ break;
+ }
continue;
}
@@ -56,7 +60,7 @@ static int popup_menu__run(struct ui_browser *menu)
return key;
}
-int ui__popup_menu(int argc, char * const argv[])
+int ui__popup_menu(int argc, char * const argv[], int *keyp)
{
struct ui_browser menu = {
.entries = (void *)argv,
@@ -65,8 +69,7 @@ int ui__popup_menu(int argc, char * const argv[])
.write = ui_browser__argv_write,
.nr_entries = argc,
};
-
- return popup_menu__run(&menu);
+ return popup_menu__run(&menu, keyp);
}
int ui_browser__input_window(const char *title, const char *text, char *input,
@@ -92,7 +95,7 @@ int ui_browser__input_window(const char *title, const char *text, char *input,
t = sep + 1;
}
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
max_len += 2;
nr_lines += 8;
@@ -103,7 +106,7 @@ int ui_browser__input_window(const char *title, const char *text, char *input,
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
- SLsmg_write_string((char *)title);
+ SLsmg_write_string(title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 7;
@@ -114,25 +117,25 @@ int ui_browser__input_window(const char *title, const char *text, char *input,
len = 5;
while (len--) {
SLsmg_gotorc(y + len - 1, x);
- SLsmg_write_nstring((char *)" ", max_len);
+ SLsmg_write_nstring(" ", max_len);
}
SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
SLsmg_gotorc(y + 3, x);
- SLsmg_write_nstring((char *)exit_msg, max_len);
+ SLsmg_write_nstring(exit_msg, max_len);
SLsmg_refresh();
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
x += 2;
len = 0;
key = ui__getch(delay_secs);
while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
- pthread_mutex_lock(&ui__lock);
+ mutex_lock(&ui__lock);
if (key == K_BKSPC) {
if (len == 0) {
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
goto next_key;
}
SLsmg_gotorc(y, x + --len);
@@ -144,7 +147,7 @@ int ui_browser__input_window(const char *title, const char *text, char *input,
}
SLsmg_refresh();
- pthread_mutex_unlock(&ui__lock);
+ mutex_unlock(&ui__lock);
/* XXX more graceful overflow handling needed */
if (len == sizeof(buf) - 1) {
@@ -161,8 +164,7 @@ next_key:
return key;
}
-int ui__question_window(const char *title, const char *text,
- const char *exit_msg, int delay_secs)
+void __ui__info_window(const char *title, const char *text, const char *exit_msg)
{
int x, y;
int max_len = 0, nr_lines = 0;
@@ -184,10 +186,10 @@ int ui__question_window(const char *title, const char *text,
t = sep + 1;
}
- pthread_mutex_lock(&ui__lock);
-
max_len += 2;
- nr_lines += 4;
+ nr_lines += 2;
+ if (exit_msg)
+ nr_lines += 2;
y = SLtt_Screen_Rows / 2 - nr_lines / 2,
x = SLtt_Screen_Cols / 2 - max_len / 2;
@@ -195,21 +197,37 @@ int ui__question_window(const char *title, const char *text,
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
- SLsmg_write_string((char *)title);
+ SLsmg_write_string(title);
}
SLsmg_gotorc(++y, x);
- nr_lines -= 2;
+ if (exit_msg)
+ nr_lines -= 2;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
- SLsmg_gotorc(y + nr_lines - 2, x);
- SLsmg_write_nstring((char *)" ", max_len);
- SLsmg_gotorc(y + nr_lines - 1, x);
- SLsmg_write_nstring((char *)exit_msg, max_len);
- SLsmg_refresh();
+ if (exit_msg) {
+ SLsmg_gotorc(y + nr_lines - 2, x);
+ SLsmg_write_nstring(" ", max_len);
+ SLsmg_gotorc(y + nr_lines - 1, x);
+ SLsmg_write_nstring(exit_msg, max_len);
+ }
+}
- pthread_mutex_unlock(&ui__lock);
+void ui__info_window(const char *title, const char *text)
+{
+ mutex_lock(&ui__lock);
+ __ui__info_window(title, text, NULL);
+ SLsmg_refresh();
+ mutex_unlock(&ui__lock);
+}
+int ui__question_window(const char *title, const char *text,
+ const char *exit_msg, int delay_secs)
+{
+ mutex_lock(&ui__lock);
+ __ui__info_window(title, text, exit_msg);
+ SLsmg_refresh();
+ mutex_unlock(&ui__lock);
return ui__getch(delay_secs);
}
diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h
index 4b6fb6c7a542..d8e911205582 100644
--- a/tools/perf/ui/ui.h
+++ b/tools/perf/ui/ui.h
@@ -1,11 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_UI_H_
#define _PERF_UI_H_ 1
-#include <pthread.h>
+#include "../util/mutex.h"
#include <stdbool.h>
#include <linux/compiler.h>
-extern pthread_mutex_t ui__lock;
+extern struct mutex ui__lock;
extern void *perf_gtk_handle;
extern int use_browser;
@@ -30,4 +31,7 @@ struct option;
int stdio__config_color(const struct option *opt, const char *mode, int unset);
+void pthread__block_sigwinch(void);
+void pthread__unblock_sigwinch(void);
+
#endif /* _PERF_UI_H_ */
diff --git a/tools/perf/ui/util.c b/tools/perf/ui/util.c
index e3e0a963d03a..1d38ddf01b60 100644
--- a/tools/perf/ui/util.c
+++ b/tools/perf/ui/util.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
#include "util.h"
-#include "../debug.h"
-
+#include "../util/debug.h"
+#include <stdio.h>
/*
* Default error logging functions
@@ -14,6 +15,9 @@ static int perf_stdio__error(const char *format, va_list args)
static int perf_stdio__warning(const char *format, va_list args)
{
+ if (quiet)
+ return 0;
+
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
return 0;
@@ -44,6 +48,8 @@ int ui__warning(const char *format, ...)
{
int ret;
va_list args;
+ if (quiet)
+ return 0;
va_start(args, format);
ret = perf_eops->warning(format, args);
diff --git a/tools/perf/ui/util.h b/tools/perf/ui/util.h
index 361f08c52d37..e30cea807564 100644
--- a/tools/perf/ui/util.h
+++ b/tools/perf/ui/util.h
@@ -1,12 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_UI_UTIL_H_
#define _PERF_UI_UTIL_H_ 1
#include <stdarg.h>
int ui__getch(int delay_secs);
-int ui__popup_menu(int argc, char * const argv[]);
+int ui__popup_menu(int argc, char * const argv[], int *keyp);
int ui__help_window(const char *text);
int ui__dialog_yesno(const char *msg);
+void __ui__info_window(const char *title, const char *text, const char *exit_msg);
+void ui__info_window(const char *title, const char *text);
int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs);