linux/tools/perf/ui/gtk/hists.c
<<
>>
Prefs
   1#include "../evlist.h"
   2#include "../cache.h"
   3#include "../evsel.h"
   4#include "../sort.h"
   5#include "../hist.h"
   6#include "../helpline.h"
   7#include "gtk.h"
   8
   9#define MAX_COLUMNS                     32
  10
  11static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...)
  12{
  13        int ret = 0;
  14        va_list args;
  15        double percent;
  16        const char *markup;
  17        char *buf = hpp->buf;
  18        size_t size = hpp->size;
  19
  20        va_start(args, fmt);
  21        percent = va_arg(args, double);
  22        va_end(args);
  23
  24        markup = perf_gtk__get_percent_color(percent);
  25        if (markup)
  26                ret += scnprintf(buf, size, markup);
  27
  28        ret += scnprintf(buf + ret, size - ret, fmt, percent);
  29
  30        if (markup)
  31                ret += scnprintf(buf + ret, size - ret, "</span>");
  32
  33        return ret;
  34}
  35
  36#define __HPP_COLOR_PERCENT_FN(_type, _field)                                   \
  37static u64 he_get_##_field(struct hist_entry *he)                               \
  38{                                                                               \
  39        return he->stat._field;                                                 \
  40}                                                                               \
  41                                                                                \
  42static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
  43                                       struct perf_hpp *hpp,                    \
  44                                       struct hist_entry *he)                   \
  45{                                                                               \
  46        return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%",           \
  47                          __percent_color_snprintf, true);                      \
  48}
  49
  50__HPP_COLOR_PERCENT_FN(overhead, period)
  51__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
  52__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
  53__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
  54__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
  55
  56#undef __HPP_COLOR_PERCENT_FN
  57
  58
  59void perf_gtk__init_hpp(void)
  60{
  61        perf_hpp__init();
  62
  63        perf_hpp__format[PERF_HPP__OVERHEAD].color =
  64                                perf_gtk__hpp_color_overhead;
  65        perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
  66                                perf_gtk__hpp_color_overhead_sys;
  67        perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
  68                                perf_gtk__hpp_color_overhead_us;
  69        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
  70                                perf_gtk__hpp_color_overhead_guest_sys;
  71        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
  72                                perf_gtk__hpp_color_overhead_guest_us;
  73}
  74
  75static void callchain_list__sym_name(struct callchain_list *cl,
  76                                     char *bf, size_t bfsize)
  77{
  78        if (cl->ms.sym)
  79                scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
  80        else
  81                scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
  82}
  83
  84static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
  85                                    GtkTreeIter *parent, int col, u64 total)
  86{
  87        struct rb_node *nd;
  88        bool has_single_node = (rb_first(root) == rb_last(root));
  89
  90        for (nd = rb_first(root); nd; nd = rb_next(nd)) {
  91                struct callchain_node *node;
  92                struct callchain_list *chain;
  93                GtkTreeIter iter, new_parent;
  94                bool need_new_parent;
  95                double percent;
  96                u64 hits, child_total;
  97
  98                node = rb_entry(nd, struct callchain_node, rb_node);
  99
 100                hits = callchain_cumul_hits(node);
 101                percent = 100.0 * hits / total;
 102
 103                new_parent = *parent;
 104                need_new_parent = !has_single_node && (node->val_nr > 1);
 105
 106                list_for_each_entry(chain, &node->val, list) {
 107                        char buf[128];
 108
 109                        gtk_tree_store_append(store, &iter, &new_parent);
 110
 111                        scnprintf(buf, sizeof(buf), "%5.2f%%", percent);
 112                        gtk_tree_store_set(store, &iter, 0, buf, -1);
 113
 114                        callchain_list__sym_name(chain, buf, sizeof(buf));
 115                        gtk_tree_store_set(store, &iter, col, buf, -1);
 116
 117                        if (need_new_parent) {
 118                                /*
 119                                 * Only show the top-most symbol in a callchain
 120                                 * if it's not the only callchain.
 121                                 */
 122                                new_parent = iter;
 123                                need_new_parent = false;
 124                        }
 125                }
 126
 127                if (callchain_param.mode == CHAIN_GRAPH_REL)
 128                        child_total = node->children_hit;
 129                else
 130                        child_total = total;
 131
 132                /* Now 'iter' contains info of the last callchain_list */
 133                perf_gtk__add_callchain(&node->rb_root, store, &iter, col,
 134                                        child_total);
 135        }
 136}
 137
 138static void on_row_activated(GtkTreeView *view, GtkTreePath *path,
 139                             GtkTreeViewColumn *col __maybe_unused,
 140                             gpointer user_data __maybe_unused)
 141{
 142        bool expanded = gtk_tree_view_row_expanded(view, path);
 143
 144        if (expanded)
 145                gtk_tree_view_collapse_row(view, path);
 146        else
 147                gtk_tree_view_expand_row(view, path, FALSE);
 148}
 149
 150static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
 151                                 float min_pcnt)
 152{
 153        struct perf_hpp_fmt *fmt;
 154        GType col_types[MAX_COLUMNS];
 155        GtkCellRenderer *renderer;
 156        struct sort_entry *se;
 157        GtkTreeStore *store;
 158        struct rb_node *nd;
 159        GtkWidget *view;
 160        int col_idx;
 161        int sym_col = -1;
 162        int nr_cols;
 163        char s[512];
 164
 165        struct perf_hpp hpp = {
 166                .buf            = s,
 167                .size           = sizeof(s),
 168        };
 169
 170        nr_cols = 0;
 171
 172        perf_hpp__for_each_format(fmt)
 173                col_types[nr_cols++] = G_TYPE_STRING;
 174
 175        list_for_each_entry(se, &hist_entry__sort_list, list) {
 176                if (se->elide)
 177                        continue;
 178
 179                if (se == &sort_sym)
 180                        sym_col = nr_cols;
 181
 182                col_types[nr_cols++] = G_TYPE_STRING;
 183        }
 184
 185        store = gtk_tree_store_newv(nr_cols, col_types);
 186
 187        view = gtk_tree_view_new();
 188
 189        renderer = gtk_cell_renderer_text_new();
 190
 191        col_idx = 0;
 192
 193        perf_hpp__for_each_format(fmt) {
 194                fmt->header(fmt, &hpp, hists_to_evsel(hists));
 195
 196                gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
 197                                                            -1, ltrim(s),
 198                                                            renderer, "markup",
 199                                                            col_idx++, NULL);
 200        }
 201
 202        list_for_each_entry(se, &hist_entry__sort_list, list) {
 203                if (se->elide)
 204                        continue;
 205
 206                gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
 207                                                            -1, se->se_header,
 208                                                            renderer, "text",
 209                                                            col_idx++, NULL);
 210        }
 211
 212        for (col_idx = 0; col_idx < nr_cols; col_idx++) {
 213                GtkTreeViewColumn *column;
 214
 215                column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx);
 216                gtk_tree_view_column_set_resizable(column, TRUE);
 217
 218                if (col_idx == sym_col) {
 219                        gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view),
 220                                                          column);
 221                }
 222        }
 223
 224        gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
 225
 226        g_object_unref(GTK_TREE_MODEL(store));
 227
 228        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 229                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 230                GtkTreeIter iter;
 231                float percent = h->stat.period * 100.0 /
 232                                        hists->stats.total_period;
 233
 234                if (h->filtered)
 235                        continue;
 236
 237                if (percent < min_pcnt)
 238                        continue;
 239
 240                gtk_tree_store_append(store, &iter, NULL);
 241
 242                col_idx = 0;
 243
 244                perf_hpp__for_each_format(fmt) {
 245                        if (fmt->color)
 246                                fmt->color(fmt, &hpp, h);
 247                        else
 248                                fmt->entry(fmt, &hpp, h);
 249
 250                        gtk_tree_store_set(store, &iter, col_idx++, s, -1);
 251                }
 252
 253                list_for_each_entry(se, &hist_entry__sort_list, list) {
 254                        if (se->elide)
 255                                continue;
 256
 257                        se->se_snprintf(h, s, ARRAY_SIZE(s),
 258                                        hists__col_len(hists, se->se_width_idx));
 259
 260                        gtk_tree_store_set(store, &iter, col_idx++, s, -1);
 261                }
 262
 263                if (symbol_conf.use_callchain && sort__has_sym) {
 264                        u64 total;
 265
 266                        if (callchain_param.mode == CHAIN_GRAPH_REL)
 267                                total = h->stat.period;
 268                        else
 269                                total = hists->stats.total_period;
 270
 271                        perf_gtk__add_callchain(&h->sorted_chain, store, &iter,
 272                                                sym_col, total);
 273                }
 274        }
 275
 276        gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
 277
 278        g_signal_connect(view, "row-activated",
 279                         G_CALLBACK(on_row_activated), NULL);
 280        gtk_container_add(GTK_CONTAINER(window), view);
 281}
 282
 283int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
 284                                  const char *help,
 285                                  struct hist_browser_timer *hbt __maybe_unused,
 286                                  float min_pcnt)
 287{
 288        struct perf_evsel *pos;
 289        GtkWidget *vbox;
 290        GtkWidget *notebook;
 291        GtkWidget *info_bar;
 292        GtkWidget *statbar;
 293        GtkWidget *window;
 294
 295        signal(SIGSEGV, perf_gtk__signal);
 296        signal(SIGFPE,  perf_gtk__signal);
 297        signal(SIGINT,  perf_gtk__signal);
 298        signal(SIGQUIT, perf_gtk__signal);
 299        signal(SIGTERM, perf_gtk__signal);
 300
 301        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 302
 303        gtk_window_set_title(GTK_WINDOW(window), "perf report");
 304
 305        g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
 306
 307        pgctx = perf_gtk__activate_context(window);
 308        if (!pgctx)
 309                return -1;
 310
 311        vbox = gtk_vbox_new(FALSE, 0);
 312
 313        notebook = gtk_notebook_new();
 314
 315        gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
 316
 317        info_bar = perf_gtk__setup_info_bar();
 318        if (info_bar)
 319                gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
 320
 321        statbar = perf_gtk__setup_statusbar();
 322        gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
 323
 324        gtk_container_add(GTK_CONTAINER(window), vbox);
 325
 326        evlist__for_each(evlist, pos) {
 327                struct hists *hists = &pos->hists;
 328                const char *evname = perf_evsel__name(pos);
 329                GtkWidget *scrolled_window;
 330                GtkWidget *tab_label;
 331                char buf[512];
 332                size_t size = sizeof(buf);
 333
 334                if (symbol_conf.event_group) {
 335                        if (!perf_evsel__is_group_leader(pos))
 336                                continue;
 337
 338                        if (pos->nr_members > 1) {
 339                                perf_evsel__group_desc(pos, buf, size);
 340                                evname = buf;
 341                        }
 342                }
 343
 344                scrolled_window = gtk_scrolled_window_new(NULL, NULL);
 345
 346                gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
 347                                                        GTK_POLICY_AUTOMATIC,
 348                                                        GTK_POLICY_AUTOMATIC);
 349
 350                perf_gtk__show_hists(scrolled_window, hists, min_pcnt);
 351
 352                tab_label = gtk_label_new(evname);
 353
 354                gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
 355        }
 356
 357        gtk_widget_show_all(window);
 358
 359        perf_gtk__resize_window(window);
 360
 361        gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
 362
 363        ui_helpline__push(help);
 364
 365        gtk_main();
 366
 367        perf_gtk__deactivate_context(&pgctx);
 368
 369        return 0;
 370}
 371