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