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