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 perf_gtk__add_callchain_flat(struct rb_root *root, GtkTreeStore *store, 93 GtkTreeIter *parent, int col, u64 total) 94 { 95 struct rb_node *nd; 96 bool has_single_node = (rb_first(root) == rb_last(root)); 97 98 for (nd = rb_first(root); nd; nd = rb_next(nd)) { 99 struct callchain_node *node; 100 struct callchain_list *chain; 101 GtkTreeIter iter, new_parent; 102 bool need_new_parent; 103 104 node = rb_entry(nd, struct callchain_node, rb_node); 105 106 new_parent = *parent; 107 need_new_parent = !has_single_node; 108 109 callchain_node__make_parent_list(node); 110 111 list_for_each_entry(chain, &node->parent_val, list) { 112 char buf[128]; 113 114 gtk_tree_store_append(store, &iter, &new_parent); 115 116 callchain_node__scnprintf_value(node, buf, sizeof(buf), total); 117 gtk_tree_store_set(store, &iter, 0, buf, -1); 118 119 callchain_list__sym_name(chain, buf, sizeof(buf), false); 120 gtk_tree_store_set(store, &iter, col, buf, -1); 121 122 if (need_new_parent) { 123 /* 124 * Only show the top-most symbol in a callchain 125 * if it's not the only callchain. 126 */ 127 new_parent = iter; 128 need_new_parent = false; 129 } 130 } 131 132 list_for_each_entry(chain, &node->val, list) { 133 char buf[128]; 134 135 gtk_tree_store_append(store, &iter, &new_parent); 136 137 callchain_node__scnprintf_value(node, buf, sizeof(buf), total); 138 gtk_tree_store_set(store, &iter, 0, buf, -1); 139 140 callchain_list__sym_name(chain, buf, sizeof(buf), false); 141 gtk_tree_store_set(store, &iter, col, buf, -1); 142 143 if (need_new_parent) { 144 /* 145 * Only show the top-most symbol in a callchain 146 * if it's not the only callchain. 147 */ 148 new_parent = iter; 149 need_new_parent = false; 150 } 151 } 152 } 153 } 154 155 static void perf_gtk__add_callchain_folded(struct rb_root *root, GtkTreeStore *store, 156 GtkTreeIter *parent, int col, u64 total) 157 { 158 struct rb_node *nd; 159 160 for (nd = rb_first(root); nd; nd = rb_next(nd)) { 161 struct callchain_node *node; 162 struct callchain_list *chain; 163 GtkTreeIter iter; 164 char buf[64]; 165 char *str, *str_alloc = NULL; 166 bool first = true; 167 168 node = rb_entry(nd, struct callchain_node, rb_node); 169 170 callchain_node__make_parent_list(node); 171 172 list_for_each_entry(chain, &node->parent_val, list) { 173 char name[1024]; 174 175 callchain_list__sym_name(chain, name, sizeof(name), false); 176 177 if (asprintf(&str, "%s%s%s", 178 first ? "" : str_alloc, 179 first ? "" : symbol_conf.field_sep ?: "; ", 180 name) < 0) 181 return; 182 183 first = false; 184 free(str_alloc); 185 str_alloc = str; 186 } 187 188 list_for_each_entry(chain, &node->val, list) { 189 char name[1024]; 190 191 callchain_list__sym_name(chain, name, sizeof(name), false); 192 193 if (asprintf(&str, "%s%s%s", 194 first ? "" : str_alloc, 195 first ? "" : symbol_conf.field_sep ?: "; ", 196 name) < 0) 197 return; 198 199 first = false; 200 free(str_alloc); 201 str_alloc = str; 202 } 203 204 gtk_tree_store_append(store, &iter, parent); 205 206 callchain_node__scnprintf_value(node, buf, sizeof(buf), total); 207 gtk_tree_store_set(store, &iter, 0, buf, -1); 208 209 gtk_tree_store_set(store, &iter, col, str, -1); 210 211 free(str_alloc); 212 } 213 } 214 215 static void perf_gtk__add_callchain_graph(struct rb_root *root, GtkTreeStore *store, 216 GtkTreeIter *parent, int col, u64 total) 217 { 218 struct rb_node *nd; 219 bool has_single_node = (rb_first(root) == rb_last(root)); 220 221 for (nd = rb_first(root); nd; nd = rb_next(nd)) { 222 struct callchain_node *node; 223 struct callchain_list *chain; 224 GtkTreeIter iter, new_parent; 225 bool need_new_parent; 226 u64 child_total; 227 228 node = rb_entry(nd, struct callchain_node, rb_node); 229 230 new_parent = *parent; 231 need_new_parent = !has_single_node && (node->val_nr > 1); 232 233 list_for_each_entry(chain, &node->val, list) { 234 char buf[128]; 235 236 gtk_tree_store_append(store, &iter, &new_parent); 237 238 callchain_node__scnprintf_value(node, buf, sizeof(buf), total); 239 gtk_tree_store_set(store, &iter, 0, buf, -1); 240 241 callchain_list__sym_name(chain, buf, sizeof(buf), false); 242 gtk_tree_store_set(store, &iter, col, buf, -1); 243 244 if (need_new_parent) { 245 /* 246 * Only show the top-most symbol in a callchain 247 * if it's not the only callchain. 248 */ 249 new_parent = iter; 250 need_new_parent = false; 251 } 252 } 253 254 if (callchain_param.mode == CHAIN_GRAPH_REL) 255 child_total = node->children_hit; 256 else 257 child_total = total; 258 259 /* Now 'iter' contains info of the last callchain_list */ 260 perf_gtk__add_callchain_graph(&node->rb_root, store, &iter, col, 261 child_total); 262 } 263 } 264 265 static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, 266 GtkTreeIter *parent, int col, u64 total) 267 { 268 if (callchain_param.mode == CHAIN_FLAT) 269 perf_gtk__add_callchain_flat(root, store, parent, col, total); 270 else if (callchain_param.mode == CHAIN_FOLDED) 271 perf_gtk__add_callchain_folded(root, store, parent, col, total); 272 else 273 perf_gtk__add_callchain_graph(root, store, parent, col, total); 274 } 275 276 static void on_row_activated(GtkTreeView *view, GtkTreePath *path, 277 GtkTreeViewColumn *col __maybe_unused, 278 gpointer user_data __maybe_unused) 279 { 280 bool expanded = gtk_tree_view_row_expanded(view, path); 281 282 if (expanded) 283 gtk_tree_view_collapse_row(view, path); 284 else 285 gtk_tree_view_expand_row(view, path, FALSE); 286 } 287 288 static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, 289 float min_pcnt) 290 { 291 struct perf_hpp_fmt *fmt; 292 GType col_types[MAX_COLUMNS]; 293 GtkCellRenderer *renderer; 294 GtkTreeStore *store; 295 struct rb_node *nd; 296 GtkWidget *view; 297 int col_idx; 298 int sym_col = -1; 299 int nr_cols; 300 char s[512]; 301 302 struct perf_hpp hpp = { 303 .buf = s, 304 .size = sizeof(s), 305 }; 306 307 nr_cols = 0; 308 309 perf_hpp__for_each_format(fmt) 310 col_types[nr_cols++] = G_TYPE_STRING; 311 312 store = gtk_tree_store_newv(nr_cols, col_types); 313 314 view = gtk_tree_view_new(); 315 316 renderer = gtk_cell_renderer_text_new(); 317 318 col_idx = 0; 319 320 perf_hpp__for_each_format(fmt) { 321 if (perf_hpp__should_skip(fmt, hists)) 322 continue; 323 324 /* 325 * XXX no way to determine where symcol column is.. 326 * Just use last column for now. 327 */ 328 if (perf_hpp__is_sort_entry(fmt)) 329 sym_col = col_idx; 330 331 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), 332 -1, fmt->name, 333 renderer, "markup", 334 col_idx++, NULL); 335 } 336 337 for (col_idx = 0; col_idx < nr_cols; col_idx++) { 338 GtkTreeViewColumn *column; 339 340 column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx); 341 gtk_tree_view_column_set_resizable(column, TRUE); 342 343 if (col_idx == sym_col) { 344 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view), 345 column); 346 } 347 } 348 349 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); 350 351 g_object_unref(GTK_TREE_MODEL(store)); 352 353 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 354 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 355 GtkTreeIter iter; 356 u64 total = hists__total_period(h->hists); 357 float percent; 358 359 if (h->filtered) 360 continue; 361 362 percent = hist_entry__get_percent_limit(h); 363 if (percent < min_pcnt) 364 continue; 365 366 gtk_tree_store_append(store, &iter, NULL); 367 368 col_idx = 0; 369 370 perf_hpp__for_each_format(fmt) { 371 if (perf_hpp__should_skip(fmt, h->hists)) 372 continue; 373 374 if (fmt->color) 375 fmt->color(fmt, &hpp, h); 376 else 377 fmt->entry(fmt, &hpp, h); 378 379 gtk_tree_store_set(store, &iter, col_idx++, s, -1); 380 } 381 382 if (symbol_conf.use_callchain && sort__has_sym) { 383 if (callchain_param.mode == CHAIN_GRAPH_REL) 384 total = symbol_conf.cumulate_callchain ? 385 h->stat_acc->period : h->stat.period; 386 387 perf_gtk__add_callchain(&h->sorted_chain, store, &iter, 388 sym_col, total); 389 } 390 } 391 392 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE); 393 394 g_signal_connect(view, "row-activated", 395 G_CALLBACK(on_row_activated), NULL); 396 gtk_container_add(GTK_CONTAINER(window), view); 397 } 398 399 int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, 400 const char *help, 401 struct hist_browser_timer *hbt __maybe_unused, 402 float min_pcnt) 403 { 404 struct perf_evsel *pos; 405 GtkWidget *vbox; 406 GtkWidget *notebook; 407 GtkWidget *info_bar; 408 GtkWidget *statbar; 409 GtkWidget *window; 410 411 signal(SIGSEGV, perf_gtk__signal); 412 signal(SIGFPE, perf_gtk__signal); 413 signal(SIGINT, perf_gtk__signal); 414 signal(SIGQUIT, perf_gtk__signal); 415 signal(SIGTERM, perf_gtk__signal); 416 417 window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 418 419 gtk_window_set_title(GTK_WINDOW(window), "perf report"); 420 421 g_signal_connect(window, "delete_event", gtk_main_quit, NULL); 422 423 pgctx = perf_gtk__activate_context(window); 424 if (!pgctx) 425 return -1; 426 427 vbox = gtk_vbox_new(FALSE, 0); 428 429 notebook = gtk_notebook_new(); 430 431 gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); 432 433 info_bar = perf_gtk__setup_info_bar(); 434 if (info_bar) 435 gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0); 436 437 statbar = perf_gtk__setup_statusbar(); 438 gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); 439 440 gtk_container_add(GTK_CONTAINER(window), vbox); 441 442 evlist__for_each(evlist, pos) { 443 struct hists *hists = evsel__hists(pos); 444 const char *evname = perf_evsel__name(pos); 445 GtkWidget *scrolled_window; 446 GtkWidget *tab_label; 447 char buf[512]; 448 size_t size = sizeof(buf); 449 450 if (symbol_conf.event_group) { 451 if (!perf_evsel__is_group_leader(pos)) 452 continue; 453 454 if (pos->nr_members > 1) { 455 perf_evsel__group_desc(pos, buf, size); 456 evname = buf; 457 } 458 } 459 460 scrolled_window = gtk_scrolled_window_new(NULL, NULL); 461 462 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), 463 GTK_POLICY_AUTOMATIC, 464 GTK_POLICY_AUTOMATIC); 465 466 perf_gtk__show_hists(scrolled_window, hists, min_pcnt); 467 468 tab_label = gtk_label_new(evname); 469 470 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); 471 } 472 473 gtk_widget_show_all(window); 474 475 perf_gtk__resize_window(window); 476 477 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); 478 479 ui_helpline__push(help); 480 481 gtk_main(); 482 483 perf_gtk__deactivate_context(&pgctx); 484 485 return 0; 486 } 487