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 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) \ 37 static u64 he_get_##_field(struct hist_entry *he) \ 38 { \ 39 return he->stat._field; \ 40 } \ 41 \ 42 static 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, " %6.2f%%", \ 47 __percent_color_snprintf, true); \ 48 } 49 50 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ 51 static u64 he_get_acc_##_field(struct hist_entry *he) \ 52 { \ 53 return he->stat_acc->_field; \ 54 } \ 55 \ 56 static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 57 struct perf_hpp *hpp, \ 58 struct hist_entry *he) \ 59 { \ 60 return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \ 61 __percent_color_snprintf, true); \ 62 } 63 64 __HPP_COLOR_PERCENT_FN(overhead, period) 65 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) 66 __HPP_COLOR_PERCENT_FN(overhead_us, period_us) 67 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) 68 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) 69 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period) 70 71 #undef __HPP_COLOR_PERCENT_FN 72 73 74 void perf_gtk__init_hpp(void) 75 { 76 perf_hpp__format[PERF_HPP__OVERHEAD].color = 77 perf_gtk__hpp_color_overhead; 78 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 79 perf_gtk__hpp_color_overhead_sys; 80 perf_hpp__format[PERF_HPP__OVERHEAD_US].color = 81 perf_gtk__hpp_color_overhead_us; 82 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = 83 perf_gtk__hpp_color_overhead_guest_sys; 84 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = 85 perf_gtk__hpp_color_overhead_guest_us; 86 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = 87 perf_gtk__hpp_color_overhead_acc; 88 } 89 90 static void callchain_list__sym_name(struct callchain_list *cl, 91 char *bf, size_t bfsize) 92 { 93 if (cl->ms.sym) 94 scnprintf(bf, bfsize, "%s", cl->ms.sym->name); 95 else 96 scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); 97 } 98 99 static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, 100 GtkTreeIter *parent, int col, u64 total) 101 { 102 struct rb_node *nd; 103 bool has_single_node = (rb_first(root) == rb_last(root)); 104 105 for (nd = rb_first(root); nd; nd = rb_next(nd)) { 106 struct callchain_node *node; 107 struct callchain_list *chain; 108 GtkTreeIter iter, new_parent; 109 bool need_new_parent; 110 double percent; 111 u64 hits, child_total; 112 113 node = rb_entry(nd, struct callchain_node, rb_node); 114 115 hits = callchain_cumul_hits(node); 116 percent = 100.0 * hits / total; 117 118 new_parent = *parent; 119 need_new_parent = !has_single_node && (node->val_nr > 1); 120 121 list_for_each_entry(chain, &node->val, list) { 122 char buf[128]; 123 124 gtk_tree_store_append(store, &iter, &new_parent); 125 126 scnprintf(buf, sizeof(buf), "%5.2f%%", percent); 127 gtk_tree_store_set(store, &iter, 0, buf, -1); 128 129 callchain_list__sym_name(chain, buf, sizeof(buf)); 130 gtk_tree_store_set(store, &iter, col, buf, -1); 131 132 if (need_new_parent) { 133 /* 134 * Only show the top-most symbol in a callchain 135 * if it's not the only callchain. 136 */ 137 new_parent = iter; 138 need_new_parent = false; 139 } 140 } 141 142 if (callchain_param.mode == CHAIN_GRAPH_REL) 143 child_total = node->children_hit; 144 else 145 child_total = total; 146 147 /* Now 'iter' contains info of the last callchain_list */ 148 perf_gtk__add_callchain(&node->rb_root, store, &iter, col, 149 child_total); 150 } 151 } 152 153 static void on_row_activated(GtkTreeView *view, GtkTreePath *path, 154 GtkTreeViewColumn *col __maybe_unused, 155 gpointer user_data __maybe_unused) 156 { 157 bool expanded = gtk_tree_view_row_expanded(view, path); 158 159 if (expanded) 160 gtk_tree_view_collapse_row(view, path); 161 else 162 gtk_tree_view_expand_row(view, path, FALSE); 163 } 164 165 static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, 166 float min_pcnt) 167 { 168 struct perf_hpp_fmt *fmt; 169 GType col_types[MAX_COLUMNS]; 170 GtkCellRenderer *renderer; 171 GtkTreeStore *store; 172 struct rb_node *nd; 173 GtkWidget *view; 174 int col_idx; 175 int sym_col = -1; 176 int nr_cols; 177 char s[512]; 178 179 struct perf_hpp hpp = { 180 .buf = s, 181 .size = sizeof(s), 182 }; 183 184 nr_cols = 0; 185 186 perf_hpp__for_each_format(fmt) 187 col_types[nr_cols++] = G_TYPE_STRING; 188 189 store = gtk_tree_store_newv(nr_cols, col_types); 190 191 view = gtk_tree_view_new(); 192 193 renderer = gtk_cell_renderer_text_new(); 194 195 col_idx = 0; 196 197 perf_hpp__for_each_format(fmt) { 198 if (perf_hpp__should_skip(fmt)) 199 continue; 200 201 /* 202 * XXX no way to determine where symcol column is.. 203 * Just use last column for now. 204 */ 205 if (perf_hpp__is_sort_entry(fmt)) 206 sym_col = col_idx; 207 208 fmt->header(fmt, &hpp, hists_to_evsel(hists)); 209 210 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), 211 -1, ltrim(s), 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 = &pos->hists; 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