2013-02-07 17:02:08 +08:00
|
|
|
#include "gtk.h"
|
|
|
|
#include "util/debug.h"
|
|
|
|
#include "util/annotate.h"
|
2013-03-05 13:53:21 +08:00
|
|
|
#include "util/evsel.h"
|
2013-02-07 17:02:08 +08:00
|
|
|
#include "ui/helpline.h"
|
2017-04-18 02:23:08 +08:00
|
|
|
#include <inttypes.h>
|
2017-04-20 02:49:18 +08:00
|
|
|
#include <signal.h>
|
2013-02-07 17:02:08 +08:00
|
|
|
|
|
|
|
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, int evidx)
|
|
|
|
{
|
|
|
|
struct sym_hist *symhist;
|
|
|
|
double percent = 0.0;
|
|
|
|
const char *markup;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
strcpy(buf, "");
|
|
|
|
|
|
|
|
if (dl->offset == (s64) -1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
symhist = annotation__histogram(symbol__annotation(sym), evidx);
|
2017-07-20 05:36:45 +08:00
|
|
|
if (!symbol_conf.event_group && !symhist->addr[dl->offset].nr_samples)
|
2013-02-07 17:02:08 +08:00
|
|
|
return 0;
|
|
|
|
|
2017-07-20 05:36:51 +08:00
|
|
|
percent = 100.0 * symhist->addr[dl->offset].nr_samples / symhist->nr_samples;
|
2013-02-07 17:02:08 +08:00
|
|
|
|
|
|
|
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 symbol *sym,
|
|
|
|
struct map *map, struct disasm_line *dl)
|
|
|
|
{
|
|
|
|
u64 start = map__rip_2objdump(map, sym->start);
|
|
|
|
|
|
|
|
strcpy(buf, "");
|
|
|
|
|
|
|
|
if (dl->offset == (s64) -1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return scnprintf(buf, size, "%"PRIx64, start + dl->offset);
|
|
|
|
}
|
|
|
|
|
2013-02-07 17:02:10 +08:00
|
|
|
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);
|
|
|
|
const char *markup = "<span fgcolor='gray'>";
|
|
|
|
|
|
|
|
strcpy(buf, "");
|
|
|
|
|
|
|
|
if (!line)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (dl->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;
|
|
|
|
}
|
|
|
|
|
2013-02-07 17:02:08 +08:00
|
|
|
static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
|
2013-03-05 13:53:21 +08:00
|
|
|
struct map *map, struct perf_evsel *evsel,
|
2013-02-07 17:02:08 +08:00
|
|
|
struct hist_browser_timer *hbt __maybe_unused)
|
|
|
|
{
|
|
|
|
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),
|
2013-02-07 17:02:10 +08:00
|
|
|
-1, col_names[i], renderer, "markup",
|
2013-02-07 17:02:08 +08:00
|
|
|
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, ¬es->src->source, node) {
|
|
|
|
GtkTreeIter iter;
|
2013-03-05 13:53:32 +08:00
|
|
|
int ret = 0;
|
2013-02-07 17:02:08 +08:00
|
|
|
|
|
|
|
gtk_list_store_append(store, &iter);
|
|
|
|
|
2013-03-05 13:53:32 +08:00
|
|
|
if (perf_evsel__is_group_event(evsel)) {
|
|
|
|
for (i = 0; i < evsel->nr_members; i++) {
|
|
|
|
ret += perf_gtk__get_percent(s + ret,
|
|
|
|
sizeof(s) - ret,
|
|
|
|
sym, pos,
|
|
|
|
evsel->idx + i);
|
|
|
|
ret += scnprintf(s + ret, sizeof(s) - ret, " ");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret = perf_gtk__get_percent(s, sizeof(s), sym, pos,
|
|
|
|
evsel->idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret)
|
2013-02-07 17:02:08 +08:00
|
|
|
gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1);
|
|
|
|
if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos))
|
|
|
|
gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1);
|
2013-02-07 17:02:10 +08:00
|
|
|
if (perf_gtk__get_line(s, sizeof(s), pos))
|
|
|
|
gtk_list_store_set(store, &iter, ANN_COL__LINE, s, -1);
|
2013-02-07 17:02:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
gtk_container_add(GTK_CONTAINER(window), view);
|
|
|
|
|
|
|
|
list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
|
|
|
|
list_del(&pos->node);
|
|
|
|
disasm_line__free(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-09-13 14:27:43 +08:00
|
|
|
static int symbol__gtk_annotate(struct symbol *sym, struct map *map,
|
|
|
|
struct perf_evsel *evsel,
|
|
|
|
struct hist_browser_timer *hbt)
|
2013-02-07 17:02:08 +08:00
|
|
|
{
|
|
|
|
GtkWidget *window;
|
2013-02-07 17:02:09 +08:00
|
|
|
GtkWidget *notebook;
|
2013-02-07 17:02:08 +08:00
|
|
|
GtkWidget *scrolled_window;
|
|
|
|
GtkWidget *tab_label;
|
2016-07-30 03:27:18 +08:00
|
|
|
int err;
|
2013-02-07 17:02:08 +08:00
|
|
|
|
2013-02-07 17:02:13 +08:00
|
|
|
if (map->dso->annotate_warned)
|
|
|
|
return -1;
|
|
|
|
|
2017-06-19 10:55:56 +08:00
|
|
|
err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
|
perf annotate: Check for fused instructions
Macro fusion merges two instructions to a single micro-op. Intel core
platform performs this hardware optimization under limited
circumstances.
For example, CMP + JCC can be "fused" and executed /retired together.
While with sampling this can result in the sample sometimes being on the
JCC and sometimes on the CMP. So for the fused instruction pair, they
could be considered together.
On Nehalem, fused instruction pairs:
cmp/test + jcc.
On other new CPU:
cmp/test/add/sub/and/inc/dec + jcc.
This patch adds an x86-specific function which checks if 2 instructions
are in a "fused" pair. For non-x86 arch, the function is just NULL.
Changelog:
v4: Move the CPU model checking to symbol__disassemble and save the CPU
family/model in arch structure.
It avoids checking every time when jump arrow printed.
v3: Add checking for Nehalem (CMP, TEST). For other newer Intel CPUs
just check it by default (CMP, TEST, ADD, SUB, AND, INC, DEC).
v2: Remove the original weak function. Arnaldo points out that doing it
as a weak function that will be overridden by the host arch doesn't
work. So now it's implemented as an arch-specific function.
Committer fix:
Do not access evsel->evlist->env->cpuid, ->env can be null, introduce
perf_evsel__env_cpuid(), just like perf_evsel__env_arch(), also used in
this function call.
The original patch was segfaulting 'perf top' + annotation.
But this essentially disables this fused instructions augmentation in
'perf top', the right thing is to get the cpuid from the running kernel,
left for a later patch tho.
Signed-off-by: Yao Jin <yao.jin@linux.intel.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/1499403995-19857-2-git-send-email-yao.jin@linux.intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-07-07 13:06:34 +08:00
|
|
|
0, NULL, NULL);
|
2016-07-30 03:27:18 +08:00
|
|
|
if (err) {
|
|
|
|
char msg[BUFSIZ];
|
|
|
|
symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
|
|
|
|
ui__error("Couldn't annotate %s: %s\n", sym->name, msg);
|
2013-02-07 17:02:13 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-02-07 17:02:09 +08:00
|
|
|
if (perf_gtk__is_active_context(pgctx)) {
|
|
|
|
window = pgctx->main_window;
|
|
|
|
notebook = pgctx->notebook;
|
|
|
|
} else {
|
|
|
|
GtkWidget *vbox;
|
|
|
|
GtkWidget *infobar;
|
|
|
|
GtkWidget *statbar;
|
2013-02-07 17:02:08 +08:00
|
|
|
|
2013-02-07 17:02:09 +08:00
|
|
|
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);
|
2013-02-07 17:02:08 +08:00
|
|
|
|
2013-02-07 17:02:09 +08:00
|
|
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_window_set_title(GTK_WINDOW(window), "perf annotate");
|
2013-02-07 17:02:08 +08:00
|
|
|
|
2013-02-07 17:02:09 +08:00
|
|
|
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);
|
|
|
|
}
|
2013-02-07 17:02:08 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2013-03-05 13:53:21 +08:00
|
|
|
perf_gtk__annotate_symbol(scrolled_window, sym, map, evsel, hbt);
|
2013-02-07 17:02:09 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2013-02-07 17:02:08 +08:00
|
|
|
|
2013-09-13 14:27:43 +08:00
|
|
|
int hist_entry__gtk_annotate(struct hist_entry *he,
|
|
|
|
struct perf_evsel *evsel,
|
|
|
|
struct hist_browser_timer *hbt)
|
|
|
|
{
|
|
|
|
return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt);
|
|
|
|
}
|
|
|
|
|
2013-02-07 17:02:09 +08:00
|
|
|
void perf_gtk__show_annotations(void)
|
|
|
|
{
|
|
|
|
GtkWidget *window;
|
2013-02-07 17:02:08 +08:00
|
|
|
|
2013-02-07 17:02:09 +08:00
|
|
|
if (!perf_gtk__is_active_context(pgctx))
|
|
|
|
return;
|
2013-02-07 17:02:08 +08:00
|
|
|
|
2013-02-07 17:02:09 +08:00
|
|
|
window = pgctx->main_window;
|
2013-02-07 17:02:08 +08:00
|
|
|
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);
|
|
|
|
}
|