linux/tools/perf/ui/gtk/annotate.c
Ian Rogers 035f0c279b perf annotate: Prefer passing evsel to evsel->core.idx
An evsel idx may not be stable due to sorting, evlist removal,
etc. Try to reduce it being part of APIs by explicitly passing the
evsel in annotate code. Internally the code just reads evsel->core.idx
so behavior is unchanged.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Chen Ni <nichen@iscas.ac.cn>
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Link: https://lore.kernel.org/r/20250117181848.690474-1-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
2025-01-18 10:02:10 -08:00

278 lines
6.5 KiB
C

// 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>
enum {
ANN_COL__PERCENT,
ANN_COL__OFFSET,
ANN_COL__LINE,
MAX_ANN_COLS
};
static const char *const col_names[] = {
"Overhead",
"Offset",
"Line"
};
static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym,
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->al.offset == (s64) -1)
return 0;
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 * nr_samples / symhist->nr_samples;
markup = perf_gtk__get_percent_color(percent);
if (markup)
ret += scnprintf(buf, size, "%s", markup);
ret += scnprintf(buf + ret, size - ret, "%6.2f%%", percent);
if (markup)
ret += scnprintf(buf + ret, size - ret, "</span>");
return ret;
}
static int perf_gtk__get_offset(char *buf, size_t size, struct map_symbol *ms,
struct disasm_line *dl)
{
u64 start = map__rip_2objdump(ms->map, ms->sym->start);
strcpy(buf, "");
if (dl->al.offset == (s64) -1)
return 0;
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->al.line, -1);
const char *markup = "<span fgcolor='gray'>";
strcpy(buf, "");
if (!line)
return 0;
if (dl->al.offset != (s64) -1)
markup = NULL;
if (markup)
ret += scnprintf(buf, size, "%s", markup);
ret += scnprintf(buf + ret, size - ret, "%s", line);
if (markup)
ret += scnprintf(buf + ret, size - ret, "</span>");
g_free(line);
return ret;
}
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];
GtkCellRenderer *renderer;
GtkListStore *store;
GtkWidget *view;
int i;
char s[512];
notes = symbol__annotation(sym);
for (i = 0; i < MAX_ANN_COLS; i++) {
col_types[i] = G_TYPE_STRING;
}
store = gtk_list_store_newv(MAX_ANN_COLS, col_types);
view = gtk_tree_view_new();
renderer = gtk_cell_renderer_text_new();
for (i = 0; i < MAX_ANN_COLS; i++) {
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
-1, col_names[i], renderer, "markup",
i, NULL);
}
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, al.node) {
GtkTreeIter iter;
int ret = 0;
gtk_list_store_append(store, &iter);
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,
cur_evsel);
ret += scnprintf(s + ret, sizeof(s) - ret, " ");
}
} else {
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), 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);
}
gtk_container_add(GTK_CONTAINER(window), view);
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 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 (dso__annotate_warned(dso))
return -1;
err = symbol__annotate(ms, evsel, NULL);
if (err) {
char msg[BUFSIZ];
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;
} else {
GtkWidget *vbox;
GtkWidget *infobar;
GtkWidget *statbar;
signal(SIGSEGV, perf_gtk__signal);
signal(SIGFPE, perf_gtk__signal);
signal(SIGINT, perf_gtk__signal);
signal(SIGQUIT, perf_gtk__signal);
signal(SIGTERM, perf_gtk__signal);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "perf annotate");
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
pgctx = perf_gtk__activate_context(window);
if (!pgctx)
return -1;
vbox = gtk_vbox_new(FALSE, 0);
notebook = gtk_notebook_new();
pgctx->notebook = notebook;
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
infobar = perf_gtk__setup_info_bar();
if (infobar) {
gtk_box_pack_start(GTK_BOX(vbox), infobar,
FALSE, FALSE, 0);
}
statbar = perf_gtk__setup_statusbar();
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
}
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
tab_label = gtk_label_new(sym->name);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window,
tab_label);
perf_gtk__annotate_symbol(scrolled_window, ms, evsel, hbt);
return 0;
}
int hist_entry__gtk_annotate(struct hist_entry *he,
struct evsel *evsel,
struct hist_browser_timer *hbt)
{
return symbol__gtk_annotate(&he->ms, evsel, hbt);
}
void perf_gtk__show_annotations(void)
{
GtkWidget *window;
if (!perf_gtk__is_active_context(pgctx))
return;
window = pgctx->main_window;
gtk_widget_show_all(window);
perf_gtk__resize_window(window);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_main();
perf_gtk__deactivate_context(&pgctx);
}