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(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 double percent; 104 u64 hits, child_total; 105 106 node = rb_entry(nd, struct callchain_node, rb_node); 107 108 hits = callchain_cumul_hits(node); 109 percent = 100.0 * hits / total; 110 111 new_parent = *parent; 112 need_new_parent = !has_single_node && (node->val_nr > 1); 113 114 list_for_each_entry(chain, &node->val, list) { 115 char buf[128]; 116 117 gtk_tree_store_append(store, &iter, &new_parent); 118 119 scnprintf(buf, sizeof(buf), "%5.2f%%", percent); 120 gtk_tree_store_set(store, &iter, 0, buf, -1); 121 122 callchain_list__sym_name(chain, buf, sizeof(buf), false); 123 gtk_tree_store_set(store, &iter, col, buf, -1); 124 125 if (need_new_parent) { 126 /* 127 * Only show the top-most symbol in a callchain 128 * if it's not the only callchain. 129 */ 130 new_parent = iter; 131 need_new_parent = false; 132 } 133 } 134 135 if (callchain_param.mode == CHAIN_GRAPH_REL) 136 child_total = node->children_hit; 137 else 138 child_total = total; 139 140 /* Now 'iter' contains info of the last callchain_list */ 141 perf_gtk__add_callchain(&node->rb_root, store, &iter, col, 142 child_total); 143 } 144 } 145 146 static void on_row_activated(GtkTreeView *view, GtkTreePath *path, 147 GtkTreeViewColumn *col __maybe_unused, 148 gpointer user_data __maybe_unused) 149 { 150 bool expanded = gtk_tree_view_row_expanded(view, path); 151 152 if (expanded) 153 gtk_tree_view_collapse_row(view, path); 154 else 155 gtk_tree_view_expand_row(view, path, FALSE); 156 } 157 158 static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, 159 float min_pcnt) 160 { 161 struct perf_hpp_fmt *fmt; 162 GType col_types[MAX_COLUMNS]; 163 GtkCellRenderer *renderer; 164 GtkTreeStore *store; 165 struct rb_node *nd; 166 GtkWidget *view; 167 int col_idx; 168 int sym_col = -1; 169 int nr_cols; 170 char s[512]; 171 172 struct perf_hpp hpp = { 173 .buf = s, 174 .size = sizeof(s), 175 }; 176 177 nr_cols = 0; 178 179 perf_hpp__for_each_format(fmt) 180 col_types[nr_cols++] = G_TYPE_STRING; 181 182 store = gtk_tree_store_newv(nr_cols, col_types); 183 184 view = gtk_tree_view_new(); 185 186 renderer = gtk_cell_renderer_text_new(); 187 188 col_idx = 0; 189 190 perf_hpp__for_each_format(fmt) { 191 if (perf_hpp__should_skip(fmt)) 192 continue; 193 194 /* 195 * XXX no way to determine where symcol column is.. 196 * Just use last column for now. 197 */ 198 if (perf_hpp__is_sort_entry(fmt)) 199 sym_col = col_idx; 200 201 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), 202 -1, fmt->name, 203 renderer, "markup", 204 col_idx++, NULL); 205 } 206 207 for (col_idx = 0; col_idx < nr_cols; col_idx++) { 208 GtkTreeViewColumn *column; 209 210 column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx); 211 gtk_tree_view_column_set_resizable(column, TRUE); 212 213 if (col_idx == sym_col) { 214 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view), 215 column); 216 } 217 } 218 219 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); 220 221 g_object_unref(GTK_TREE_MODEL(store)); 222 223 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 224 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 225 GtkTreeIter iter; 226 u64 total = hists__total_period(h->hists); 227 float percent; 228 229 if (h->filtered) 230 continue; 231 232 percent = hist_entry__get_percent_limit(h); 233 if (percent < min_pcnt) 234 continue; 235 236 gtk_tree_store_append(store, &iter, NULL); 237 238 col_idx = 0; 239 240 perf_hpp__for_each_format(fmt) { 241 if (perf_hpp__should_skip(fmt)) 242 continue; 243 244 if (fmt->color) 245 fmt->color(fmt, &hpp, h); 246 else 247 fmt->entry(fmt, &hpp, h); 248 249 gtk_tree_store_set(store, &iter, col_idx++, s, -1); 250 } 251 252 if (symbol_conf.use_callchain && sort__has_sym) { 253 if (callchain_param.mode == CHAIN_GRAPH_REL) 254 total = symbol_conf.cumulate_callchain ? 255 h->stat_acc->period : h->stat.period; 256 257 perf_gtk__add_callchain(&h->sorted_chain, store, &iter, 258 sym_col, total); 259 } 260 } 261 262 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE); 263 264 g_signal_connect(view, "row-activated", 265 G_CALLBACK(on_row_activated), NULL); 266 gtk_container_add(GTK_CONTAINER(window), view); 267 } 268 269 int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, 270 const char *help, 271 struct hist_browser_timer *hbt __maybe_unused, 272 float min_pcnt) 273 { 274 struct perf_evsel *pos; 275 GtkWidget *vbox; 276 GtkWidget *notebook; 277 GtkWidget *info_bar; 278 GtkWidget *statbar; 279 GtkWidget *window; 280 281 signal(SIGSEGV, perf_gtk__signal); 282 signal(SIGFPE, perf_gtk__signal); 283 signal(SIGINT, perf_gtk__signal); 284 signal(SIGQUIT, perf_gtk__signal); 285 signal(SIGTERM, perf_gtk__signal); 286 287 window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 288 289 gtk_window_set_title(GTK_WINDOW(window), "perf report"); 290 291 g_signal_connect(window, "delete_event", gtk_main_quit, NULL); 292 293 pgctx = perf_gtk__activate_context(window); 294 if (!pgctx) 295 return -1; 296 297 vbox = gtk_vbox_new(FALSE, 0); 298 299 notebook = gtk_notebook_new(); 300 301 gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); 302 303 info_bar = perf_gtk__setup_info_bar(); 304 if (info_bar) 305 gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0); 306 307 statbar = perf_gtk__setup_statusbar(); 308 gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); 309 310 gtk_container_add(GTK_CONTAINER(window), vbox); 311 312 evlist__for_each(evlist, pos) { 313 struct hists *hists = evsel__hists(pos); 314 const char *evname = perf_evsel__name(pos); 315 GtkWidget *scrolled_window; 316 GtkWidget *tab_label; 317 char buf[512]; 318 size_t size = sizeof(buf); 319 320 if (symbol_conf.event_group) { 321 if (!perf_evsel__is_group_leader(pos)) 322 continue; 323 324 if (pos->nr_members > 1) { 325 perf_evsel__group_desc(pos, buf, size); 326 evname = buf; 327 } 328 } 329 330 scrolled_window = gtk_scrolled_window_new(NULL, NULL); 331 332 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), 333 GTK_POLICY_AUTOMATIC, 334 GTK_POLICY_AUTOMATIC); 335 336 perf_gtk__show_hists(scrolled_window, hists, min_pcnt); 337 338 tab_label = gtk_label_new(evname); 339 340 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); 341 } 342 343 gtk_widget_show_all(window); 344 345 perf_gtk__resize_window(window); 346 347 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); 348 349 ui_helpline__push(help); 350 351 gtk_main(); 352 353 perf_gtk__deactivate_context(&pgctx); 354 355 return 0; 356 } 357