1*aca7a94dSNamhyung Kim #include <stdio.h> 2*aca7a94dSNamhyung Kim #include "../libslang.h" 3*aca7a94dSNamhyung Kim #include <stdlib.h> 4*aca7a94dSNamhyung Kim #include <string.h> 5*aca7a94dSNamhyung Kim #include <newt.h> 6*aca7a94dSNamhyung Kim #include <linux/rbtree.h> 7*aca7a94dSNamhyung Kim 8*aca7a94dSNamhyung Kim #include "../../util/evsel.h" 9*aca7a94dSNamhyung Kim #include "../../util/evlist.h" 10*aca7a94dSNamhyung Kim #include "../../util/hist.h" 11*aca7a94dSNamhyung Kim #include "../../util/pstack.h" 12*aca7a94dSNamhyung Kim #include "../../util/sort.h" 13*aca7a94dSNamhyung Kim #include "../../util/util.h" 14*aca7a94dSNamhyung Kim 15*aca7a94dSNamhyung Kim #include "../browser.h" 16*aca7a94dSNamhyung Kim #include "../helpline.h" 17*aca7a94dSNamhyung Kim #include "../util.h" 18*aca7a94dSNamhyung Kim #include "../ui.h" 19*aca7a94dSNamhyung Kim #include "map.h" 20*aca7a94dSNamhyung Kim 21*aca7a94dSNamhyung Kim struct hist_browser { 22*aca7a94dSNamhyung Kim struct ui_browser b; 23*aca7a94dSNamhyung Kim struct hists *hists; 24*aca7a94dSNamhyung Kim struct hist_entry *he_selection; 25*aca7a94dSNamhyung Kim struct map_symbol *selection; 26*aca7a94dSNamhyung Kim bool has_symbols; 27*aca7a94dSNamhyung Kim }; 28*aca7a94dSNamhyung Kim 29*aca7a94dSNamhyung Kim static int hists__browser_title(struct hists *self, char *bf, size_t size, 30*aca7a94dSNamhyung Kim const char *ev_name); 31*aca7a94dSNamhyung Kim 32*aca7a94dSNamhyung Kim static void hist_browser__refresh_dimensions(struct hist_browser *self) 33*aca7a94dSNamhyung Kim { 34*aca7a94dSNamhyung Kim /* 3 == +/- toggle symbol before actual hist_entry rendering */ 35*aca7a94dSNamhyung Kim self->b.width = 3 + (hists__sort_list_width(self->hists) + 36*aca7a94dSNamhyung Kim sizeof("[k]")); 37*aca7a94dSNamhyung Kim } 38*aca7a94dSNamhyung Kim 39*aca7a94dSNamhyung Kim static void hist_browser__reset(struct hist_browser *self) 40*aca7a94dSNamhyung Kim { 41*aca7a94dSNamhyung Kim self->b.nr_entries = self->hists->nr_entries; 42*aca7a94dSNamhyung Kim hist_browser__refresh_dimensions(self); 43*aca7a94dSNamhyung Kim ui_browser__reset_index(&self->b); 44*aca7a94dSNamhyung Kim } 45*aca7a94dSNamhyung Kim 46*aca7a94dSNamhyung Kim static char tree__folded_sign(bool unfolded) 47*aca7a94dSNamhyung Kim { 48*aca7a94dSNamhyung Kim return unfolded ? '-' : '+'; 49*aca7a94dSNamhyung Kim } 50*aca7a94dSNamhyung Kim 51*aca7a94dSNamhyung Kim static char map_symbol__folded(const struct map_symbol *self) 52*aca7a94dSNamhyung Kim { 53*aca7a94dSNamhyung Kim return self->has_children ? tree__folded_sign(self->unfolded) : ' '; 54*aca7a94dSNamhyung Kim } 55*aca7a94dSNamhyung Kim 56*aca7a94dSNamhyung Kim static char hist_entry__folded(const struct hist_entry *self) 57*aca7a94dSNamhyung Kim { 58*aca7a94dSNamhyung Kim return map_symbol__folded(&self->ms); 59*aca7a94dSNamhyung Kim } 60*aca7a94dSNamhyung Kim 61*aca7a94dSNamhyung Kim static char callchain_list__folded(const struct callchain_list *self) 62*aca7a94dSNamhyung Kim { 63*aca7a94dSNamhyung Kim return map_symbol__folded(&self->ms); 64*aca7a94dSNamhyung Kim } 65*aca7a94dSNamhyung Kim 66*aca7a94dSNamhyung Kim static void map_symbol__set_folding(struct map_symbol *self, bool unfold) 67*aca7a94dSNamhyung Kim { 68*aca7a94dSNamhyung Kim self->unfolded = unfold ? self->has_children : false; 69*aca7a94dSNamhyung Kim } 70*aca7a94dSNamhyung Kim 71*aca7a94dSNamhyung Kim static int callchain_node__count_rows_rb_tree(struct callchain_node *self) 72*aca7a94dSNamhyung Kim { 73*aca7a94dSNamhyung Kim int n = 0; 74*aca7a94dSNamhyung Kim struct rb_node *nd; 75*aca7a94dSNamhyung Kim 76*aca7a94dSNamhyung Kim for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { 77*aca7a94dSNamhyung Kim struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 78*aca7a94dSNamhyung Kim struct callchain_list *chain; 79*aca7a94dSNamhyung Kim char folded_sign = ' '; /* No children */ 80*aca7a94dSNamhyung Kim 81*aca7a94dSNamhyung Kim list_for_each_entry(chain, &child->val, list) { 82*aca7a94dSNamhyung Kim ++n; 83*aca7a94dSNamhyung Kim /* We need this because we may not have children */ 84*aca7a94dSNamhyung Kim folded_sign = callchain_list__folded(chain); 85*aca7a94dSNamhyung Kim if (folded_sign == '+') 86*aca7a94dSNamhyung Kim break; 87*aca7a94dSNamhyung Kim } 88*aca7a94dSNamhyung Kim 89*aca7a94dSNamhyung Kim if (folded_sign == '-') /* Have children and they're unfolded */ 90*aca7a94dSNamhyung Kim n += callchain_node__count_rows_rb_tree(child); 91*aca7a94dSNamhyung Kim } 92*aca7a94dSNamhyung Kim 93*aca7a94dSNamhyung Kim return n; 94*aca7a94dSNamhyung Kim } 95*aca7a94dSNamhyung Kim 96*aca7a94dSNamhyung Kim static int callchain_node__count_rows(struct callchain_node *node) 97*aca7a94dSNamhyung Kim { 98*aca7a94dSNamhyung Kim struct callchain_list *chain; 99*aca7a94dSNamhyung Kim bool unfolded = false; 100*aca7a94dSNamhyung Kim int n = 0; 101*aca7a94dSNamhyung Kim 102*aca7a94dSNamhyung Kim list_for_each_entry(chain, &node->val, list) { 103*aca7a94dSNamhyung Kim ++n; 104*aca7a94dSNamhyung Kim unfolded = chain->ms.unfolded; 105*aca7a94dSNamhyung Kim } 106*aca7a94dSNamhyung Kim 107*aca7a94dSNamhyung Kim if (unfolded) 108*aca7a94dSNamhyung Kim n += callchain_node__count_rows_rb_tree(node); 109*aca7a94dSNamhyung Kim 110*aca7a94dSNamhyung Kim return n; 111*aca7a94dSNamhyung Kim } 112*aca7a94dSNamhyung Kim 113*aca7a94dSNamhyung Kim static int callchain__count_rows(struct rb_root *chain) 114*aca7a94dSNamhyung Kim { 115*aca7a94dSNamhyung Kim struct rb_node *nd; 116*aca7a94dSNamhyung Kim int n = 0; 117*aca7a94dSNamhyung Kim 118*aca7a94dSNamhyung Kim for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 119*aca7a94dSNamhyung Kim struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 120*aca7a94dSNamhyung Kim n += callchain_node__count_rows(node); 121*aca7a94dSNamhyung Kim } 122*aca7a94dSNamhyung Kim 123*aca7a94dSNamhyung Kim return n; 124*aca7a94dSNamhyung Kim } 125*aca7a94dSNamhyung Kim 126*aca7a94dSNamhyung Kim static bool map_symbol__toggle_fold(struct map_symbol *self) 127*aca7a94dSNamhyung Kim { 128*aca7a94dSNamhyung Kim if (!self) 129*aca7a94dSNamhyung Kim return false; 130*aca7a94dSNamhyung Kim 131*aca7a94dSNamhyung Kim if (!self->has_children) 132*aca7a94dSNamhyung Kim return false; 133*aca7a94dSNamhyung Kim 134*aca7a94dSNamhyung Kim self->unfolded = !self->unfolded; 135*aca7a94dSNamhyung Kim return true; 136*aca7a94dSNamhyung Kim } 137*aca7a94dSNamhyung Kim 138*aca7a94dSNamhyung Kim static void callchain_node__init_have_children_rb_tree(struct callchain_node *self) 139*aca7a94dSNamhyung Kim { 140*aca7a94dSNamhyung Kim struct rb_node *nd = rb_first(&self->rb_root); 141*aca7a94dSNamhyung Kim 142*aca7a94dSNamhyung Kim for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { 143*aca7a94dSNamhyung Kim struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 144*aca7a94dSNamhyung Kim struct callchain_list *chain; 145*aca7a94dSNamhyung Kim bool first = true; 146*aca7a94dSNamhyung Kim 147*aca7a94dSNamhyung Kim list_for_each_entry(chain, &child->val, list) { 148*aca7a94dSNamhyung Kim if (first) { 149*aca7a94dSNamhyung Kim first = false; 150*aca7a94dSNamhyung Kim chain->ms.has_children = chain->list.next != &child->val || 151*aca7a94dSNamhyung Kim !RB_EMPTY_ROOT(&child->rb_root); 152*aca7a94dSNamhyung Kim } else 153*aca7a94dSNamhyung Kim chain->ms.has_children = chain->list.next == &child->val && 154*aca7a94dSNamhyung Kim !RB_EMPTY_ROOT(&child->rb_root); 155*aca7a94dSNamhyung Kim } 156*aca7a94dSNamhyung Kim 157*aca7a94dSNamhyung Kim callchain_node__init_have_children_rb_tree(child); 158*aca7a94dSNamhyung Kim } 159*aca7a94dSNamhyung Kim } 160*aca7a94dSNamhyung Kim 161*aca7a94dSNamhyung Kim static void callchain_node__init_have_children(struct callchain_node *self) 162*aca7a94dSNamhyung Kim { 163*aca7a94dSNamhyung Kim struct callchain_list *chain; 164*aca7a94dSNamhyung Kim 165*aca7a94dSNamhyung Kim list_for_each_entry(chain, &self->val, list) 166*aca7a94dSNamhyung Kim chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root); 167*aca7a94dSNamhyung Kim 168*aca7a94dSNamhyung Kim callchain_node__init_have_children_rb_tree(self); 169*aca7a94dSNamhyung Kim } 170*aca7a94dSNamhyung Kim 171*aca7a94dSNamhyung Kim static void callchain__init_have_children(struct rb_root *self) 172*aca7a94dSNamhyung Kim { 173*aca7a94dSNamhyung Kim struct rb_node *nd; 174*aca7a94dSNamhyung Kim 175*aca7a94dSNamhyung Kim for (nd = rb_first(self); nd; nd = rb_next(nd)) { 176*aca7a94dSNamhyung Kim struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 177*aca7a94dSNamhyung Kim callchain_node__init_have_children(node); 178*aca7a94dSNamhyung Kim } 179*aca7a94dSNamhyung Kim } 180*aca7a94dSNamhyung Kim 181*aca7a94dSNamhyung Kim static void hist_entry__init_have_children(struct hist_entry *self) 182*aca7a94dSNamhyung Kim { 183*aca7a94dSNamhyung Kim if (!self->init_have_children) { 184*aca7a94dSNamhyung Kim self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain); 185*aca7a94dSNamhyung Kim callchain__init_have_children(&self->sorted_chain); 186*aca7a94dSNamhyung Kim self->init_have_children = true; 187*aca7a94dSNamhyung Kim } 188*aca7a94dSNamhyung Kim } 189*aca7a94dSNamhyung Kim 190*aca7a94dSNamhyung Kim static bool hist_browser__toggle_fold(struct hist_browser *self) 191*aca7a94dSNamhyung Kim { 192*aca7a94dSNamhyung Kim if (map_symbol__toggle_fold(self->selection)) { 193*aca7a94dSNamhyung Kim struct hist_entry *he = self->he_selection; 194*aca7a94dSNamhyung Kim 195*aca7a94dSNamhyung Kim hist_entry__init_have_children(he); 196*aca7a94dSNamhyung Kim self->hists->nr_entries -= he->nr_rows; 197*aca7a94dSNamhyung Kim 198*aca7a94dSNamhyung Kim if (he->ms.unfolded) 199*aca7a94dSNamhyung Kim he->nr_rows = callchain__count_rows(&he->sorted_chain); 200*aca7a94dSNamhyung Kim else 201*aca7a94dSNamhyung Kim he->nr_rows = 0; 202*aca7a94dSNamhyung Kim self->hists->nr_entries += he->nr_rows; 203*aca7a94dSNamhyung Kim self->b.nr_entries = self->hists->nr_entries; 204*aca7a94dSNamhyung Kim 205*aca7a94dSNamhyung Kim return true; 206*aca7a94dSNamhyung Kim } 207*aca7a94dSNamhyung Kim 208*aca7a94dSNamhyung Kim /* If it doesn't have children, no toggling performed */ 209*aca7a94dSNamhyung Kim return false; 210*aca7a94dSNamhyung Kim } 211*aca7a94dSNamhyung Kim 212*aca7a94dSNamhyung Kim static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold) 213*aca7a94dSNamhyung Kim { 214*aca7a94dSNamhyung Kim int n = 0; 215*aca7a94dSNamhyung Kim struct rb_node *nd; 216*aca7a94dSNamhyung Kim 217*aca7a94dSNamhyung Kim for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { 218*aca7a94dSNamhyung Kim struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 219*aca7a94dSNamhyung Kim struct callchain_list *chain; 220*aca7a94dSNamhyung Kim bool has_children = false; 221*aca7a94dSNamhyung Kim 222*aca7a94dSNamhyung Kim list_for_each_entry(chain, &child->val, list) { 223*aca7a94dSNamhyung Kim ++n; 224*aca7a94dSNamhyung Kim map_symbol__set_folding(&chain->ms, unfold); 225*aca7a94dSNamhyung Kim has_children = chain->ms.has_children; 226*aca7a94dSNamhyung Kim } 227*aca7a94dSNamhyung Kim 228*aca7a94dSNamhyung Kim if (has_children) 229*aca7a94dSNamhyung Kim n += callchain_node__set_folding_rb_tree(child, unfold); 230*aca7a94dSNamhyung Kim } 231*aca7a94dSNamhyung Kim 232*aca7a94dSNamhyung Kim return n; 233*aca7a94dSNamhyung Kim } 234*aca7a94dSNamhyung Kim 235*aca7a94dSNamhyung Kim static int callchain_node__set_folding(struct callchain_node *node, bool unfold) 236*aca7a94dSNamhyung Kim { 237*aca7a94dSNamhyung Kim struct callchain_list *chain; 238*aca7a94dSNamhyung Kim bool has_children = false; 239*aca7a94dSNamhyung Kim int n = 0; 240*aca7a94dSNamhyung Kim 241*aca7a94dSNamhyung Kim list_for_each_entry(chain, &node->val, list) { 242*aca7a94dSNamhyung Kim ++n; 243*aca7a94dSNamhyung Kim map_symbol__set_folding(&chain->ms, unfold); 244*aca7a94dSNamhyung Kim has_children = chain->ms.has_children; 245*aca7a94dSNamhyung Kim } 246*aca7a94dSNamhyung Kim 247*aca7a94dSNamhyung Kim if (has_children) 248*aca7a94dSNamhyung Kim n += callchain_node__set_folding_rb_tree(node, unfold); 249*aca7a94dSNamhyung Kim 250*aca7a94dSNamhyung Kim return n; 251*aca7a94dSNamhyung Kim } 252*aca7a94dSNamhyung Kim 253*aca7a94dSNamhyung Kim static int callchain__set_folding(struct rb_root *chain, bool unfold) 254*aca7a94dSNamhyung Kim { 255*aca7a94dSNamhyung Kim struct rb_node *nd; 256*aca7a94dSNamhyung Kim int n = 0; 257*aca7a94dSNamhyung Kim 258*aca7a94dSNamhyung Kim for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 259*aca7a94dSNamhyung Kim struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 260*aca7a94dSNamhyung Kim n += callchain_node__set_folding(node, unfold); 261*aca7a94dSNamhyung Kim } 262*aca7a94dSNamhyung Kim 263*aca7a94dSNamhyung Kim return n; 264*aca7a94dSNamhyung Kim } 265*aca7a94dSNamhyung Kim 266*aca7a94dSNamhyung Kim static void hist_entry__set_folding(struct hist_entry *self, bool unfold) 267*aca7a94dSNamhyung Kim { 268*aca7a94dSNamhyung Kim hist_entry__init_have_children(self); 269*aca7a94dSNamhyung Kim map_symbol__set_folding(&self->ms, unfold); 270*aca7a94dSNamhyung Kim 271*aca7a94dSNamhyung Kim if (self->ms.has_children) { 272*aca7a94dSNamhyung Kim int n = callchain__set_folding(&self->sorted_chain, unfold); 273*aca7a94dSNamhyung Kim self->nr_rows = unfold ? n : 0; 274*aca7a94dSNamhyung Kim } else 275*aca7a94dSNamhyung Kim self->nr_rows = 0; 276*aca7a94dSNamhyung Kim } 277*aca7a94dSNamhyung Kim 278*aca7a94dSNamhyung Kim static void hists__set_folding(struct hists *self, bool unfold) 279*aca7a94dSNamhyung Kim { 280*aca7a94dSNamhyung Kim struct rb_node *nd; 281*aca7a94dSNamhyung Kim 282*aca7a94dSNamhyung Kim self->nr_entries = 0; 283*aca7a94dSNamhyung Kim 284*aca7a94dSNamhyung Kim for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 285*aca7a94dSNamhyung Kim struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 286*aca7a94dSNamhyung Kim hist_entry__set_folding(he, unfold); 287*aca7a94dSNamhyung Kim self->nr_entries += 1 + he->nr_rows; 288*aca7a94dSNamhyung Kim } 289*aca7a94dSNamhyung Kim } 290*aca7a94dSNamhyung Kim 291*aca7a94dSNamhyung Kim static void hist_browser__set_folding(struct hist_browser *self, bool unfold) 292*aca7a94dSNamhyung Kim { 293*aca7a94dSNamhyung Kim hists__set_folding(self->hists, unfold); 294*aca7a94dSNamhyung Kim self->b.nr_entries = self->hists->nr_entries; 295*aca7a94dSNamhyung Kim /* Go to the start, we may be way after valid entries after a collapse */ 296*aca7a94dSNamhyung Kim ui_browser__reset_index(&self->b); 297*aca7a94dSNamhyung Kim } 298*aca7a94dSNamhyung Kim 299*aca7a94dSNamhyung Kim static void ui_browser__warn_lost_events(struct ui_browser *browser) 300*aca7a94dSNamhyung Kim { 301*aca7a94dSNamhyung Kim ui_browser__warning(browser, 4, 302*aca7a94dSNamhyung Kim "Events are being lost, check IO/CPU overload!\n\n" 303*aca7a94dSNamhyung Kim "You may want to run 'perf' using a RT scheduler policy:\n\n" 304*aca7a94dSNamhyung Kim " perf top -r 80\n\n" 305*aca7a94dSNamhyung Kim "Or reduce the sampling frequency."); 306*aca7a94dSNamhyung Kim } 307*aca7a94dSNamhyung Kim 308*aca7a94dSNamhyung Kim static int hist_browser__run(struct hist_browser *self, const char *ev_name, 309*aca7a94dSNamhyung Kim void(*timer)(void *arg), void *arg, int delay_secs) 310*aca7a94dSNamhyung Kim { 311*aca7a94dSNamhyung Kim int key; 312*aca7a94dSNamhyung Kim char title[160]; 313*aca7a94dSNamhyung Kim 314*aca7a94dSNamhyung Kim self->b.entries = &self->hists->entries; 315*aca7a94dSNamhyung Kim self->b.nr_entries = self->hists->nr_entries; 316*aca7a94dSNamhyung Kim 317*aca7a94dSNamhyung Kim hist_browser__refresh_dimensions(self); 318*aca7a94dSNamhyung Kim hists__browser_title(self->hists, title, sizeof(title), ev_name); 319*aca7a94dSNamhyung Kim 320*aca7a94dSNamhyung Kim if (ui_browser__show(&self->b, title, 321*aca7a94dSNamhyung Kim "Press '?' for help on key bindings") < 0) 322*aca7a94dSNamhyung Kim return -1; 323*aca7a94dSNamhyung Kim 324*aca7a94dSNamhyung Kim while (1) { 325*aca7a94dSNamhyung Kim key = ui_browser__run(&self->b, delay_secs); 326*aca7a94dSNamhyung Kim 327*aca7a94dSNamhyung Kim switch (key) { 328*aca7a94dSNamhyung Kim case K_TIMER: 329*aca7a94dSNamhyung Kim timer(arg); 330*aca7a94dSNamhyung Kim ui_browser__update_nr_entries(&self->b, self->hists->nr_entries); 331*aca7a94dSNamhyung Kim 332*aca7a94dSNamhyung Kim if (self->hists->stats.nr_lost_warned != 333*aca7a94dSNamhyung Kim self->hists->stats.nr_events[PERF_RECORD_LOST]) { 334*aca7a94dSNamhyung Kim self->hists->stats.nr_lost_warned = 335*aca7a94dSNamhyung Kim self->hists->stats.nr_events[PERF_RECORD_LOST]; 336*aca7a94dSNamhyung Kim ui_browser__warn_lost_events(&self->b); 337*aca7a94dSNamhyung Kim } 338*aca7a94dSNamhyung Kim 339*aca7a94dSNamhyung Kim hists__browser_title(self->hists, title, sizeof(title), ev_name); 340*aca7a94dSNamhyung Kim ui_browser__show_title(&self->b, title); 341*aca7a94dSNamhyung Kim continue; 342*aca7a94dSNamhyung Kim case 'D': { /* Debug */ 343*aca7a94dSNamhyung Kim static int seq; 344*aca7a94dSNamhyung Kim struct hist_entry *h = rb_entry(self->b.top, 345*aca7a94dSNamhyung Kim struct hist_entry, rb_node); 346*aca7a94dSNamhyung Kim ui_helpline__pop(); 347*aca7a94dSNamhyung Kim ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", 348*aca7a94dSNamhyung Kim seq++, self->b.nr_entries, 349*aca7a94dSNamhyung Kim self->hists->nr_entries, 350*aca7a94dSNamhyung Kim self->b.height, 351*aca7a94dSNamhyung Kim self->b.index, 352*aca7a94dSNamhyung Kim self->b.top_idx, 353*aca7a94dSNamhyung Kim h->row_offset, h->nr_rows); 354*aca7a94dSNamhyung Kim } 355*aca7a94dSNamhyung Kim break; 356*aca7a94dSNamhyung Kim case 'C': 357*aca7a94dSNamhyung Kim /* Collapse the whole world. */ 358*aca7a94dSNamhyung Kim hist_browser__set_folding(self, false); 359*aca7a94dSNamhyung Kim break; 360*aca7a94dSNamhyung Kim case 'E': 361*aca7a94dSNamhyung Kim /* Expand the whole world. */ 362*aca7a94dSNamhyung Kim hist_browser__set_folding(self, true); 363*aca7a94dSNamhyung Kim break; 364*aca7a94dSNamhyung Kim case K_ENTER: 365*aca7a94dSNamhyung Kim if (hist_browser__toggle_fold(self)) 366*aca7a94dSNamhyung Kim break; 367*aca7a94dSNamhyung Kim /* fall thru */ 368*aca7a94dSNamhyung Kim default: 369*aca7a94dSNamhyung Kim goto out; 370*aca7a94dSNamhyung Kim } 371*aca7a94dSNamhyung Kim } 372*aca7a94dSNamhyung Kim out: 373*aca7a94dSNamhyung Kim ui_browser__hide(&self->b); 374*aca7a94dSNamhyung Kim return key; 375*aca7a94dSNamhyung Kim } 376*aca7a94dSNamhyung Kim 377*aca7a94dSNamhyung Kim static char *callchain_list__sym_name(struct callchain_list *self, 378*aca7a94dSNamhyung Kim char *bf, size_t bfsize) 379*aca7a94dSNamhyung Kim { 380*aca7a94dSNamhyung Kim if (self->ms.sym) 381*aca7a94dSNamhyung Kim return self->ms.sym->name; 382*aca7a94dSNamhyung Kim 383*aca7a94dSNamhyung Kim snprintf(bf, bfsize, "%#" PRIx64, self->ip); 384*aca7a94dSNamhyung Kim return bf; 385*aca7a94dSNamhyung Kim } 386*aca7a94dSNamhyung Kim 387*aca7a94dSNamhyung Kim #define LEVEL_OFFSET_STEP 3 388*aca7a94dSNamhyung Kim 389*aca7a94dSNamhyung Kim static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, 390*aca7a94dSNamhyung Kim struct callchain_node *chain_node, 391*aca7a94dSNamhyung Kim u64 total, int level, 392*aca7a94dSNamhyung Kim unsigned short row, 393*aca7a94dSNamhyung Kim off_t *row_offset, 394*aca7a94dSNamhyung Kim bool *is_current_entry) 395*aca7a94dSNamhyung Kim { 396*aca7a94dSNamhyung Kim struct rb_node *node; 397*aca7a94dSNamhyung Kim int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; 398*aca7a94dSNamhyung Kim u64 new_total, remaining; 399*aca7a94dSNamhyung Kim 400*aca7a94dSNamhyung Kim if (callchain_param.mode == CHAIN_GRAPH_REL) 401*aca7a94dSNamhyung Kim new_total = chain_node->children_hit; 402*aca7a94dSNamhyung Kim else 403*aca7a94dSNamhyung Kim new_total = total; 404*aca7a94dSNamhyung Kim 405*aca7a94dSNamhyung Kim remaining = new_total; 406*aca7a94dSNamhyung Kim node = rb_first(&chain_node->rb_root); 407*aca7a94dSNamhyung Kim while (node) { 408*aca7a94dSNamhyung Kim struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 409*aca7a94dSNamhyung Kim struct rb_node *next = rb_next(node); 410*aca7a94dSNamhyung Kim u64 cumul = callchain_cumul_hits(child); 411*aca7a94dSNamhyung Kim struct callchain_list *chain; 412*aca7a94dSNamhyung Kim char folded_sign = ' '; 413*aca7a94dSNamhyung Kim int first = true; 414*aca7a94dSNamhyung Kim int extra_offset = 0; 415*aca7a94dSNamhyung Kim 416*aca7a94dSNamhyung Kim remaining -= cumul; 417*aca7a94dSNamhyung Kim 418*aca7a94dSNamhyung Kim list_for_each_entry(chain, &child->val, list) { 419*aca7a94dSNamhyung Kim char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; 420*aca7a94dSNamhyung Kim const char *str; 421*aca7a94dSNamhyung Kim int color; 422*aca7a94dSNamhyung Kim bool was_first = first; 423*aca7a94dSNamhyung Kim 424*aca7a94dSNamhyung Kim if (first) 425*aca7a94dSNamhyung Kim first = false; 426*aca7a94dSNamhyung Kim else 427*aca7a94dSNamhyung Kim extra_offset = LEVEL_OFFSET_STEP; 428*aca7a94dSNamhyung Kim 429*aca7a94dSNamhyung Kim folded_sign = callchain_list__folded(chain); 430*aca7a94dSNamhyung Kim if (*row_offset != 0) { 431*aca7a94dSNamhyung Kim --*row_offset; 432*aca7a94dSNamhyung Kim goto do_next; 433*aca7a94dSNamhyung Kim } 434*aca7a94dSNamhyung Kim 435*aca7a94dSNamhyung Kim alloc_str = NULL; 436*aca7a94dSNamhyung Kim str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); 437*aca7a94dSNamhyung Kim if (was_first) { 438*aca7a94dSNamhyung Kim double percent = cumul * 100.0 / new_total; 439*aca7a94dSNamhyung Kim 440*aca7a94dSNamhyung Kim if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) 441*aca7a94dSNamhyung Kim str = "Not enough memory!"; 442*aca7a94dSNamhyung Kim else 443*aca7a94dSNamhyung Kim str = alloc_str; 444*aca7a94dSNamhyung Kim } 445*aca7a94dSNamhyung Kim 446*aca7a94dSNamhyung Kim color = HE_COLORSET_NORMAL; 447*aca7a94dSNamhyung Kim width = self->b.width - (offset + extra_offset + 2); 448*aca7a94dSNamhyung Kim if (ui_browser__is_current_entry(&self->b, row)) { 449*aca7a94dSNamhyung Kim self->selection = &chain->ms; 450*aca7a94dSNamhyung Kim color = HE_COLORSET_SELECTED; 451*aca7a94dSNamhyung Kim *is_current_entry = true; 452*aca7a94dSNamhyung Kim } 453*aca7a94dSNamhyung Kim 454*aca7a94dSNamhyung Kim ui_browser__set_color(&self->b, color); 455*aca7a94dSNamhyung Kim ui_browser__gotorc(&self->b, row, 0); 456*aca7a94dSNamhyung Kim slsmg_write_nstring(" ", offset + extra_offset); 457*aca7a94dSNamhyung Kim slsmg_printf("%c ", folded_sign); 458*aca7a94dSNamhyung Kim slsmg_write_nstring(str, width); 459*aca7a94dSNamhyung Kim free(alloc_str); 460*aca7a94dSNamhyung Kim 461*aca7a94dSNamhyung Kim if (++row == self->b.height) 462*aca7a94dSNamhyung Kim goto out; 463*aca7a94dSNamhyung Kim do_next: 464*aca7a94dSNamhyung Kim if (folded_sign == '+') 465*aca7a94dSNamhyung Kim break; 466*aca7a94dSNamhyung Kim } 467*aca7a94dSNamhyung Kim 468*aca7a94dSNamhyung Kim if (folded_sign == '-') { 469*aca7a94dSNamhyung Kim const int new_level = level + (extra_offset ? 2 : 1); 470*aca7a94dSNamhyung Kim row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, 471*aca7a94dSNamhyung Kim new_level, row, row_offset, 472*aca7a94dSNamhyung Kim is_current_entry); 473*aca7a94dSNamhyung Kim } 474*aca7a94dSNamhyung Kim if (row == self->b.height) 475*aca7a94dSNamhyung Kim goto out; 476*aca7a94dSNamhyung Kim node = next; 477*aca7a94dSNamhyung Kim } 478*aca7a94dSNamhyung Kim out: 479*aca7a94dSNamhyung Kim return row - first_row; 480*aca7a94dSNamhyung Kim } 481*aca7a94dSNamhyung Kim 482*aca7a94dSNamhyung Kim static int hist_browser__show_callchain_node(struct hist_browser *self, 483*aca7a94dSNamhyung Kim struct callchain_node *node, 484*aca7a94dSNamhyung Kim int level, unsigned short row, 485*aca7a94dSNamhyung Kim off_t *row_offset, 486*aca7a94dSNamhyung Kim bool *is_current_entry) 487*aca7a94dSNamhyung Kim { 488*aca7a94dSNamhyung Kim struct callchain_list *chain; 489*aca7a94dSNamhyung Kim int first_row = row, 490*aca7a94dSNamhyung Kim offset = level * LEVEL_OFFSET_STEP, 491*aca7a94dSNamhyung Kim width = self->b.width - offset; 492*aca7a94dSNamhyung Kim char folded_sign = ' '; 493*aca7a94dSNamhyung Kim 494*aca7a94dSNamhyung Kim list_for_each_entry(chain, &node->val, list) { 495*aca7a94dSNamhyung Kim char ipstr[BITS_PER_LONG / 4 + 1], *s; 496*aca7a94dSNamhyung Kim int color; 497*aca7a94dSNamhyung Kim 498*aca7a94dSNamhyung Kim folded_sign = callchain_list__folded(chain); 499*aca7a94dSNamhyung Kim 500*aca7a94dSNamhyung Kim if (*row_offset != 0) { 501*aca7a94dSNamhyung Kim --*row_offset; 502*aca7a94dSNamhyung Kim continue; 503*aca7a94dSNamhyung Kim } 504*aca7a94dSNamhyung Kim 505*aca7a94dSNamhyung Kim color = HE_COLORSET_NORMAL; 506*aca7a94dSNamhyung Kim if (ui_browser__is_current_entry(&self->b, row)) { 507*aca7a94dSNamhyung Kim self->selection = &chain->ms; 508*aca7a94dSNamhyung Kim color = HE_COLORSET_SELECTED; 509*aca7a94dSNamhyung Kim *is_current_entry = true; 510*aca7a94dSNamhyung Kim } 511*aca7a94dSNamhyung Kim 512*aca7a94dSNamhyung Kim s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); 513*aca7a94dSNamhyung Kim ui_browser__gotorc(&self->b, row, 0); 514*aca7a94dSNamhyung Kim ui_browser__set_color(&self->b, color); 515*aca7a94dSNamhyung Kim slsmg_write_nstring(" ", offset); 516*aca7a94dSNamhyung Kim slsmg_printf("%c ", folded_sign); 517*aca7a94dSNamhyung Kim slsmg_write_nstring(s, width - 2); 518*aca7a94dSNamhyung Kim 519*aca7a94dSNamhyung Kim if (++row == self->b.height) 520*aca7a94dSNamhyung Kim goto out; 521*aca7a94dSNamhyung Kim } 522*aca7a94dSNamhyung Kim 523*aca7a94dSNamhyung Kim if (folded_sign == '-') 524*aca7a94dSNamhyung Kim row += hist_browser__show_callchain_node_rb_tree(self, node, 525*aca7a94dSNamhyung Kim self->hists->stats.total_period, 526*aca7a94dSNamhyung Kim level + 1, row, 527*aca7a94dSNamhyung Kim row_offset, 528*aca7a94dSNamhyung Kim is_current_entry); 529*aca7a94dSNamhyung Kim out: 530*aca7a94dSNamhyung Kim return row - first_row; 531*aca7a94dSNamhyung Kim } 532*aca7a94dSNamhyung Kim 533*aca7a94dSNamhyung Kim static int hist_browser__show_callchain(struct hist_browser *self, 534*aca7a94dSNamhyung Kim struct rb_root *chain, 535*aca7a94dSNamhyung Kim int level, unsigned short row, 536*aca7a94dSNamhyung Kim off_t *row_offset, 537*aca7a94dSNamhyung Kim bool *is_current_entry) 538*aca7a94dSNamhyung Kim { 539*aca7a94dSNamhyung Kim struct rb_node *nd; 540*aca7a94dSNamhyung Kim int first_row = row; 541*aca7a94dSNamhyung Kim 542*aca7a94dSNamhyung Kim for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 543*aca7a94dSNamhyung Kim struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 544*aca7a94dSNamhyung Kim 545*aca7a94dSNamhyung Kim row += hist_browser__show_callchain_node(self, node, level, 546*aca7a94dSNamhyung Kim row, row_offset, 547*aca7a94dSNamhyung Kim is_current_entry); 548*aca7a94dSNamhyung Kim if (row == self->b.height) 549*aca7a94dSNamhyung Kim break; 550*aca7a94dSNamhyung Kim } 551*aca7a94dSNamhyung Kim 552*aca7a94dSNamhyung Kim return row - first_row; 553*aca7a94dSNamhyung Kim } 554*aca7a94dSNamhyung Kim 555*aca7a94dSNamhyung Kim static int hist_browser__show_entry(struct hist_browser *self, 556*aca7a94dSNamhyung Kim struct hist_entry *entry, 557*aca7a94dSNamhyung Kim unsigned short row) 558*aca7a94dSNamhyung Kim { 559*aca7a94dSNamhyung Kim char s[256]; 560*aca7a94dSNamhyung Kim double percent; 561*aca7a94dSNamhyung Kim int printed = 0; 562*aca7a94dSNamhyung Kim int width = self->b.width - 6; /* The percentage */ 563*aca7a94dSNamhyung Kim char folded_sign = ' '; 564*aca7a94dSNamhyung Kim bool current_entry = ui_browser__is_current_entry(&self->b, row); 565*aca7a94dSNamhyung Kim off_t row_offset = entry->row_offset; 566*aca7a94dSNamhyung Kim 567*aca7a94dSNamhyung Kim if (current_entry) { 568*aca7a94dSNamhyung Kim self->he_selection = entry; 569*aca7a94dSNamhyung Kim self->selection = &entry->ms; 570*aca7a94dSNamhyung Kim } 571*aca7a94dSNamhyung Kim 572*aca7a94dSNamhyung Kim if (symbol_conf.use_callchain) { 573*aca7a94dSNamhyung Kim hist_entry__init_have_children(entry); 574*aca7a94dSNamhyung Kim folded_sign = hist_entry__folded(entry); 575*aca7a94dSNamhyung Kim } 576*aca7a94dSNamhyung Kim 577*aca7a94dSNamhyung Kim if (row_offset == 0) { 578*aca7a94dSNamhyung Kim hist_entry__snprintf(entry, s, sizeof(s), self->hists); 579*aca7a94dSNamhyung Kim percent = (entry->period * 100.0) / self->hists->stats.total_period; 580*aca7a94dSNamhyung Kim 581*aca7a94dSNamhyung Kim ui_browser__set_percent_color(&self->b, percent, current_entry); 582*aca7a94dSNamhyung Kim ui_browser__gotorc(&self->b, row, 0); 583*aca7a94dSNamhyung Kim if (symbol_conf.use_callchain) { 584*aca7a94dSNamhyung Kim slsmg_printf("%c ", folded_sign); 585*aca7a94dSNamhyung Kim width -= 2; 586*aca7a94dSNamhyung Kim } 587*aca7a94dSNamhyung Kim 588*aca7a94dSNamhyung Kim slsmg_printf(" %5.2f%%", percent); 589*aca7a94dSNamhyung Kim 590*aca7a94dSNamhyung Kim /* The scroll bar isn't being used */ 591*aca7a94dSNamhyung Kim if (!self->b.navkeypressed) 592*aca7a94dSNamhyung Kim width += 1; 593*aca7a94dSNamhyung Kim 594*aca7a94dSNamhyung Kim if (!current_entry || !self->b.navkeypressed) 595*aca7a94dSNamhyung Kim ui_browser__set_color(&self->b, HE_COLORSET_NORMAL); 596*aca7a94dSNamhyung Kim 597*aca7a94dSNamhyung Kim if (symbol_conf.show_nr_samples) { 598*aca7a94dSNamhyung Kim slsmg_printf(" %11u", entry->nr_events); 599*aca7a94dSNamhyung Kim width -= 12; 600*aca7a94dSNamhyung Kim } 601*aca7a94dSNamhyung Kim 602*aca7a94dSNamhyung Kim if (symbol_conf.show_total_period) { 603*aca7a94dSNamhyung Kim slsmg_printf(" %12" PRIu64, entry->period); 604*aca7a94dSNamhyung Kim width -= 13; 605*aca7a94dSNamhyung Kim } 606*aca7a94dSNamhyung Kim 607*aca7a94dSNamhyung Kim slsmg_write_nstring(s, width); 608*aca7a94dSNamhyung Kim ++row; 609*aca7a94dSNamhyung Kim ++printed; 610*aca7a94dSNamhyung Kim } else 611*aca7a94dSNamhyung Kim --row_offset; 612*aca7a94dSNamhyung Kim 613*aca7a94dSNamhyung Kim if (folded_sign == '-' && row != self->b.height) { 614*aca7a94dSNamhyung Kim printed += hist_browser__show_callchain(self, &entry->sorted_chain, 615*aca7a94dSNamhyung Kim 1, row, &row_offset, 616*aca7a94dSNamhyung Kim ¤t_entry); 617*aca7a94dSNamhyung Kim if (current_entry) 618*aca7a94dSNamhyung Kim self->he_selection = entry; 619*aca7a94dSNamhyung Kim } 620*aca7a94dSNamhyung Kim 621*aca7a94dSNamhyung Kim return printed; 622*aca7a94dSNamhyung Kim } 623*aca7a94dSNamhyung Kim 624*aca7a94dSNamhyung Kim static void ui_browser__hists_init_top(struct ui_browser *browser) 625*aca7a94dSNamhyung Kim { 626*aca7a94dSNamhyung Kim if (browser->top == NULL) { 627*aca7a94dSNamhyung Kim struct hist_browser *hb; 628*aca7a94dSNamhyung Kim 629*aca7a94dSNamhyung Kim hb = container_of(browser, struct hist_browser, b); 630*aca7a94dSNamhyung Kim browser->top = rb_first(&hb->hists->entries); 631*aca7a94dSNamhyung Kim } 632*aca7a94dSNamhyung Kim } 633*aca7a94dSNamhyung Kim 634*aca7a94dSNamhyung Kim static unsigned int hist_browser__refresh(struct ui_browser *self) 635*aca7a94dSNamhyung Kim { 636*aca7a94dSNamhyung Kim unsigned row = 0; 637*aca7a94dSNamhyung Kim struct rb_node *nd; 638*aca7a94dSNamhyung Kim struct hist_browser *hb = container_of(self, struct hist_browser, b); 639*aca7a94dSNamhyung Kim 640*aca7a94dSNamhyung Kim ui_browser__hists_init_top(self); 641*aca7a94dSNamhyung Kim 642*aca7a94dSNamhyung Kim for (nd = self->top; nd; nd = rb_next(nd)) { 643*aca7a94dSNamhyung Kim struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 644*aca7a94dSNamhyung Kim 645*aca7a94dSNamhyung Kim if (h->filtered) 646*aca7a94dSNamhyung Kim continue; 647*aca7a94dSNamhyung Kim 648*aca7a94dSNamhyung Kim row += hist_browser__show_entry(hb, h, row); 649*aca7a94dSNamhyung Kim if (row == self->height) 650*aca7a94dSNamhyung Kim break; 651*aca7a94dSNamhyung Kim } 652*aca7a94dSNamhyung Kim 653*aca7a94dSNamhyung Kim return row; 654*aca7a94dSNamhyung Kim } 655*aca7a94dSNamhyung Kim 656*aca7a94dSNamhyung Kim static struct rb_node *hists__filter_entries(struct rb_node *nd) 657*aca7a94dSNamhyung Kim { 658*aca7a94dSNamhyung Kim while (nd != NULL) { 659*aca7a94dSNamhyung Kim struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 660*aca7a94dSNamhyung Kim if (!h->filtered) 661*aca7a94dSNamhyung Kim return nd; 662*aca7a94dSNamhyung Kim 663*aca7a94dSNamhyung Kim nd = rb_next(nd); 664*aca7a94dSNamhyung Kim } 665*aca7a94dSNamhyung Kim 666*aca7a94dSNamhyung Kim return NULL; 667*aca7a94dSNamhyung Kim } 668*aca7a94dSNamhyung Kim 669*aca7a94dSNamhyung Kim static struct rb_node *hists__filter_prev_entries(struct rb_node *nd) 670*aca7a94dSNamhyung Kim { 671*aca7a94dSNamhyung Kim while (nd != NULL) { 672*aca7a94dSNamhyung Kim struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 673*aca7a94dSNamhyung Kim if (!h->filtered) 674*aca7a94dSNamhyung Kim return nd; 675*aca7a94dSNamhyung Kim 676*aca7a94dSNamhyung Kim nd = rb_prev(nd); 677*aca7a94dSNamhyung Kim } 678*aca7a94dSNamhyung Kim 679*aca7a94dSNamhyung Kim return NULL; 680*aca7a94dSNamhyung Kim } 681*aca7a94dSNamhyung Kim 682*aca7a94dSNamhyung Kim static void ui_browser__hists_seek(struct ui_browser *self, 683*aca7a94dSNamhyung Kim off_t offset, int whence) 684*aca7a94dSNamhyung Kim { 685*aca7a94dSNamhyung Kim struct hist_entry *h; 686*aca7a94dSNamhyung Kim struct rb_node *nd; 687*aca7a94dSNamhyung Kim bool first = true; 688*aca7a94dSNamhyung Kim 689*aca7a94dSNamhyung Kim if (self->nr_entries == 0) 690*aca7a94dSNamhyung Kim return; 691*aca7a94dSNamhyung Kim 692*aca7a94dSNamhyung Kim ui_browser__hists_init_top(self); 693*aca7a94dSNamhyung Kim 694*aca7a94dSNamhyung Kim switch (whence) { 695*aca7a94dSNamhyung Kim case SEEK_SET: 696*aca7a94dSNamhyung Kim nd = hists__filter_entries(rb_first(self->entries)); 697*aca7a94dSNamhyung Kim break; 698*aca7a94dSNamhyung Kim case SEEK_CUR: 699*aca7a94dSNamhyung Kim nd = self->top; 700*aca7a94dSNamhyung Kim goto do_offset; 701*aca7a94dSNamhyung Kim case SEEK_END: 702*aca7a94dSNamhyung Kim nd = hists__filter_prev_entries(rb_last(self->entries)); 703*aca7a94dSNamhyung Kim first = false; 704*aca7a94dSNamhyung Kim break; 705*aca7a94dSNamhyung Kim default: 706*aca7a94dSNamhyung Kim return; 707*aca7a94dSNamhyung Kim } 708*aca7a94dSNamhyung Kim 709*aca7a94dSNamhyung Kim /* 710*aca7a94dSNamhyung Kim * Moves not relative to the first visible entry invalidates its 711*aca7a94dSNamhyung Kim * row_offset: 712*aca7a94dSNamhyung Kim */ 713*aca7a94dSNamhyung Kim h = rb_entry(self->top, struct hist_entry, rb_node); 714*aca7a94dSNamhyung Kim h->row_offset = 0; 715*aca7a94dSNamhyung Kim 716*aca7a94dSNamhyung Kim /* 717*aca7a94dSNamhyung Kim * Here we have to check if nd is expanded (+), if it is we can't go 718*aca7a94dSNamhyung Kim * the next top level hist_entry, instead we must compute an offset of 719*aca7a94dSNamhyung Kim * what _not_ to show and not change the first visible entry. 720*aca7a94dSNamhyung Kim * 721*aca7a94dSNamhyung Kim * This offset increments when we are going from top to bottom and 722*aca7a94dSNamhyung Kim * decreases when we're going from bottom to top. 723*aca7a94dSNamhyung Kim * 724*aca7a94dSNamhyung Kim * As we don't have backpointers to the top level in the callchains 725*aca7a94dSNamhyung Kim * structure, we need to always print the whole hist_entry callchain, 726*aca7a94dSNamhyung Kim * skipping the first ones that are before the first visible entry 727*aca7a94dSNamhyung Kim * and stop when we printed enough lines to fill the screen. 728*aca7a94dSNamhyung Kim */ 729*aca7a94dSNamhyung Kim do_offset: 730*aca7a94dSNamhyung Kim if (offset > 0) { 731*aca7a94dSNamhyung Kim do { 732*aca7a94dSNamhyung Kim h = rb_entry(nd, struct hist_entry, rb_node); 733*aca7a94dSNamhyung Kim if (h->ms.unfolded) { 734*aca7a94dSNamhyung Kim u16 remaining = h->nr_rows - h->row_offset; 735*aca7a94dSNamhyung Kim if (offset > remaining) { 736*aca7a94dSNamhyung Kim offset -= remaining; 737*aca7a94dSNamhyung Kim h->row_offset = 0; 738*aca7a94dSNamhyung Kim } else { 739*aca7a94dSNamhyung Kim h->row_offset += offset; 740*aca7a94dSNamhyung Kim offset = 0; 741*aca7a94dSNamhyung Kim self->top = nd; 742*aca7a94dSNamhyung Kim break; 743*aca7a94dSNamhyung Kim } 744*aca7a94dSNamhyung Kim } 745*aca7a94dSNamhyung Kim nd = hists__filter_entries(rb_next(nd)); 746*aca7a94dSNamhyung Kim if (nd == NULL) 747*aca7a94dSNamhyung Kim break; 748*aca7a94dSNamhyung Kim --offset; 749*aca7a94dSNamhyung Kim self->top = nd; 750*aca7a94dSNamhyung Kim } while (offset != 0); 751*aca7a94dSNamhyung Kim } else if (offset < 0) { 752*aca7a94dSNamhyung Kim while (1) { 753*aca7a94dSNamhyung Kim h = rb_entry(nd, struct hist_entry, rb_node); 754*aca7a94dSNamhyung Kim if (h->ms.unfolded) { 755*aca7a94dSNamhyung Kim if (first) { 756*aca7a94dSNamhyung Kim if (-offset > h->row_offset) { 757*aca7a94dSNamhyung Kim offset += h->row_offset; 758*aca7a94dSNamhyung Kim h->row_offset = 0; 759*aca7a94dSNamhyung Kim } else { 760*aca7a94dSNamhyung Kim h->row_offset += offset; 761*aca7a94dSNamhyung Kim offset = 0; 762*aca7a94dSNamhyung Kim self->top = nd; 763*aca7a94dSNamhyung Kim break; 764*aca7a94dSNamhyung Kim } 765*aca7a94dSNamhyung Kim } else { 766*aca7a94dSNamhyung Kim if (-offset > h->nr_rows) { 767*aca7a94dSNamhyung Kim offset += h->nr_rows; 768*aca7a94dSNamhyung Kim h->row_offset = 0; 769*aca7a94dSNamhyung Kim } else { 770*aca7a94dSNamhyung Kim h->row_offset = h->nr_rows + offset; 771*aca7a94dSNamhyung Kim offset = 0; 772*aca7a94dSNamhyung Kim self->top = nd; 773*aca7a94dSNamhyung Kim break; 774*aca7a94dSNamhyung Kim } 775*aca7a94dSNamhyung Kim } 776*aca7a94dSNamhyung Kim } 777*aca7a94dSNamhyung Kim 778*aca7a94dSNamhyung Kim nd = hists__filter_prev_entries(rb_prev(nd)); 779*aca7a94dSNamhyung Kim if (nd == NULL) 780*aca7a94dSNamhyung Kim break; 781*aca7a94dSNamhyung Kim ++offset; 782*aca7a94dSNamhyung Kim self->top = nd; 783*aca7a94dSNamhyung Kim if (offset == 0) { 784*aca7a94dSNamhyung Kim /* 785*aca7a94dSNamhyung Kim * Last unfiltered hist_entry, check if it is 786*aca7a94dSNamhyung Kim * unfolded, if it is then we should have 787*aca7a94dSNamhyung Kim * row_offset at its last entry. 788*aca7a94dSNamhyung Kim */ 789*aca7a94dSNamhyung Kim h = rb_entry(nd, struct hist_entry, rb_node); 790*aca7a94dSNamhyung Kim if (h->ms.unfolded) 791*aca7a94dSNamhyung Kim h->row_offset = h->nr_rows; 792*aca7a94dSNamhyung Kim break; 793*aca7a94dSNamhyung Kim } 794*aca7a94dSNamhyung Kim first = false; 795*aca7a94dSNamhyung Kim } 796*aca7a94dSNamhyung Kim } else { 797*aca7a94dSNamhyung Kim self->top = nd; 798*aca7a94dSNamhyung Kim h = rb_entry(nd, struct hist_entry, rb_node); 799*aca7a94dSNamhyung Kim h->row_offset = 0; 800*aca7a94dSNamhyung Kim } 801*aca7a94dSNamhyung Kim } 802*aca7a94dSNamhyung Kim 803*aca7a94dSNamhyung Kim static struct hist_browser *hist_browser__new(struct hists *hists) 804*aca7a94dSNamhyung Kim { 805*aca7a94dSNamhyung Kim struct hist_browser *self = zalloc(sizeof(*self)); 806*aca7a94dSNamhyung Kim 807*aca7a94dSNamhyung Kim if (self) { 808*aca7a94dSNamhyung Kim self->hists = hists; 809*aca7a94dSNamhyung Kim self->b.refresh = hist_browser__refresh; 810*aca7a94dSNamhyung Kim self->b.seek = ui_browser__hists_seek; 811*aca7a94dSNamhyung Kim self->b.use_navkeypressed = true; 812*aca7a94dSNamhyung Kim if (sort__branch_mode == 1) 813*aca7a94dSNamhyung Kim self->has_symbols = sort_sym_from.list.next != NULL; 814*aca7a94dSNamhyung Kim else 815*aca7a94dSNamhyung Kim self->has_symbols = sort_sym.list.next != NULL; 816*aca7a94dSNamhyung Kim } 817*aca7a94dSNamhyung Kim 818*aca7a94dSNamhyung Kim return self; 819*aca7a94dSNamhyung Kim } 820*aca7a94dSNamhyung Kim 821*aca7a94dSNamhyung Kim static void hist_browser__delete(struct hist_browser *self) 822*aca7a94dSNamhyung Kim { 823*aca7a94dSNamhyung Kim free(self); 824*aca7a94dSNamhyung Kim } 825*aca7a94dSNamhyung Kim 826*aca7a94dSNamhyung Kim static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) 827*aca7a94dSNamhyung Kim { 828*aca7a94dSNamhyung Kim return self->he_selection; 829*aca7a94dSNamhyung Kim } 830*aca7a94dSNamhyung Kim 831*aca7a94dSNamhyung Kim static struct thread *hist_browser__selected_thread(struct hist_browser *self) 832*aca7a94dSNamhyung Kim { 833*aca7a94dSNamhyung Kim return self->he_selection->thread; 834*aca7a94dSNamhyung Kim } 835*aca7a94dSNamhyung Kim 836*aca7a94dSNamhyung Kim static int hists__browser_title(struct hists *self, char *bf, size_t size, 837*aca7a94dSNamhyung Kim const char *ev_name) 838*aca7a94dSNamhyung Kim { 839*aca7a94dSNamhyung Kim char unit; 840*aca7a94dSNamhyung Kim int printed; 841*aca7a94dSNamhyung Kim const struct dso *dso = self->dso_filter; 842*aca7a94dSNamhyung Kim const struct thread *thread = self->thread_filter; 843*aca7a94dSNamhyung Kim unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; 844*aca7a94dSNamhyung Kim u64 nr_events = self->stats.total_period; 845*aca7a94dSNamhyung Kim 846*aca7a94dSNamhyung Kim nr_samples = convert_unit(nr_samples, &unit); 847*aca7a94dSNamhyung Kim printed = scnprintf(bf, size, 848*aca7a94dSNamhyung Kim "Samples: %lu%c of event '%s', Event count (approx.): %lu", 849*aca7a94dSNamhyung Kim nr_samples, unit, ev_name, nr_events); 850*aca7a94dSNamhyung Kim 851*aca7a94dSNamhyung Kim 852*aca7a94dSNamhyung Kim if (self->uid_filter_str) 853*aca7a94dSNamhyung Kim printed += snprintf(bf + printed, size - printed, 854*aca7a94dSNamhyung Kim ", UID: %s", self->uid_filter_str); 855*aca7a94dSNamhyung Kim if (thread) 856*aca7a94dSNamhyung Kim printed += scnprintf(bf + printed, size - printed, 857*aca7a94dSNamhyung Kim ", Thread: %s(%d)", 858*aca7a94dSNamhyung Kim (thread->comm_set ? thread->comm : ""), 859*aca7a94dSNamhyung Kim thread->pid); 860*aca7a94dSNamhyung Kim if (dso) 861*aca7a94dSNamhyung Kim printed += scnprintf(bf + printed, size - printed, 862*aca7a94dSNamhyung Kim ", DSO: %s", dso->short_name); 863*aca7a94dSNamhyung Kim return printed; 864*aca7a94dSNamhyung Kim } 865*aca7a94dSNamhyung Kim 866*aca7a94dSNamhyung Kim static inline void free_popup_options(char **options, int n) 867*aca7a94dSNamhyung Kim { 868*aca7a94dSNamhyung Kim int i; 869*aca7a94dSNamhyung Kim 870*aca7a94dSNamhyung Kim for (i = 0; i < n; ++i) { 871*aca7a94dSNamhyung Kim free(options[i]); 872*aca7a94dSNamhyung Kim options[i] = NULL; 873*aca7a94dSNamhyung Kim } 874*aca7a94dSNamhyung Kim } 875*aca7a94dSNamhyung Kim 876*aca7a94dSNamhyung Kim static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 877*aca7a94dSNamhyung Kim const char *helpline, const char *ev_name, 878*aca7a94dSNamhyung Kim bool left_exits, 879*aca7a94dSNamhyung Kim void(*timer)(void *arg), void *arg, 880*aca7a94dSNamhyung Kim int delay_secs) 881*aca7a94dSNamhyung Kim { 882*aca7a94dSNamhyung Kim struct hists *self = &evsel->hists; 883*aca7a94dSNamhyung Kim struct hist_browser *browser = hist_browser__new(self); 884*aca7a94dSNamhyung Kim struct branch_info *bi; 885*aca7a94dSNamhyung Kim struct pstack *fstack; 886*aca7a94dSNamhyung Kim char *options[16]; 887*aca7a94dSNamhyung Kim int nr_options = 0; 888*aca7a94dSNamhyung Kim int key = -1; 889*aca7a94dSNamhyung Kim char buf[64]; 890*aca7a94dSNamhyung Kim 891*aca7a94dSNamhyung Kim if (browser == NULL) 892*aca7a94dSNamhyung Kim return -1; 893*aca7a94dSNamhyung Kim 894*aca7a94dSNamhyung Kim fstack = pstack__new(2); 895*aca7a94dSNamhyung Kim if (fstack == NULL) 896*aca7a94dSNamhyung Kim goto out; 897*aca7a94dSNamhyung Kim 898*aca7a94dSNamhyung Kim ui_helpline__push(helpline); 899*aca7a94dSNamhyung Kim 900*aca7a94dSNamhyung Kim memset(options, 0, sizeof(options)); 901*aca7a94dSNamhyung Kim 902*aca7a94dSNamhyung Kim while (1) { 903*aca7a94dSNamhyung Kim const struct thread *thread = NULL; 904*aca7a94dSNamhyung Kim const struct dso *dso = NULL; 905*aca7a94dSNamhyung Kim int choice = 0, 906*aca7a94dSNamhyung Kim annotate = -2, zoom_dso = -2, zoom_thread = -2, 907*aca7a94dSNamhyung Kim annotate_f = -2, annotate_t = -2, browse_map = -2; 908*aca7a94dSNamhyung Kim 909*aca7a94dSNamhyung Kim nr_options = 0; 910*aca7a94dSNamhyung Kim 911*aca7a94dSNamhyung Kim key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); 912*aca7a94dSNamhyung Kim 913*aca7a94dSNamhyung Kim if (browser->he_selection != NULL) { 914*aca7a94dSNamhyung Kim thread = hist_browser__selected_thread(browser); 915*aca7a94dSNamhyung Kim dso = browser->selection->map ? browser->selection->map->dso : NULL; 916*aca7a94dSNamhyung Kim } 917*aca7a94dSNamhyung Kim switch (key) { 918*aca7a94dSNamhyung Kim case K_TAB: 919*aca7a94dSNamhyung Kim case K_UNTAB: 920*aca7a94dSNamhyung Kim if (nr_events == 1) 921*aca7a94dSNamhyung Kim continue; 922*aca7a94dSNamhyung Kim /* 923*aca7a94dSNamhyung Kim * Exit the browser, let hists__browser_tree 924*aca7a94dSNamhyung Kim * go to the next or previous 925*aca7a94dSNamhyung Kim */ 926*aca7a94dSNamhyung Kim goto out_free_stack; 927*aca7a94dSNamhyung Kim case 'a': 928*aca7a94dSNamhyung Kim if (!browser->has_symbols) { 929*aca7a94dSNamhyung Kim ui_browser__warning(&browser->b, delay_secs * 2, 930*aca7a94dSNamhyung Kim "Annotation is only available for symbolic views, " 931*aca7a94dSNamhyung Kim "include \"sym*\" in --sort to use it."); 932*aca7a94dSNamhyung Kim continue; 933*aca7a94dSNamhyung Kim } 934*aca7a94dSNamhyung Kim 935*aca7a94dSNamhyung Kim if (browser->selection == NULL || 936*aca7a94dSNamhyung Kim browser->selection->sym == NULL || 937*aca7a94dSNamhyung Kim browser->selection->map->dso->annotate_warned) 938*aca7a94dSNamhyung Kim continue; 939*aca7a94dSNamhyung Kim goto do_annotate; 940*aca7a94dSNamhyung Kim case 'd': 941*aca7a94dSNamhyung Kim goto zoom_dso; 942*aca7a94dSNamhyung Kim case 't': 943*aca7a94dSNamhyung Kim goto zoom_thread; 944*aca7a94dSNamhyung Kim case 's': 945*aca7a94dSNamhyung Kim if (ui_browser__input_window("Symbol to show", 946*aca7a94dSNamhyung Kim "Please enter the name of symbol you want to see", 947*aca7a94dSNamhyung Kim buf, "ENTER: OK, ESC: Cancel", 948*aca7a94dSNamhyung Kim delay_secs * 2) == K_ENTER) { 949*aca7a94dSNamhyung Kim self->symbol_filter_str = *buf ? buf : NULL; 950*aca7a94dSNamhyung Kim hists__filter_by_symbol(self); 951*aca7a94dSNamhyung Kim hist_browser__reset(browser); 952*aca7a94dSNamhyung Kim } 953*aca7a94dSNamhyung Kim continue; 954*aca7a94dSNamhyung Kim case K_F1: 955*aca7a94dSNamhyung Kim case 'h': 956*aca7a94dSNamhyung Kim case '?': 957*aca7a94dSNamhyung Kim ui_browser__help_window(&browser->b, 958*aca7a94dSNamhyung Kim "h/?/F1 Show this window\n" 959*aca7a94dSNamhyung Kim "UP/DOWN/PGUP\n" 960*aca7a94dSNamhyung Kim "PGDN/SPACE Navigate\n" 961*aca7a94dSNamhyung Kim "q/ESC/CTRL+C Exit browser\n\n" 962*aca7a94dSNamhyung Kim "For multiple event sessions:\n\n" 963*aca7a94dSNamhyung Kim "TAB/UNTAB Switch events\n\n" 964*aca7a94dSNamhyung Kim "For symbolic views (--sort has sym):\n\n" 965*aca7a94dSNamhyung Kim "-> Zoom into DSO/Threads & Annotate current symbol\n" 966*aca7a94dSNamhyung Kim "<- Zoom out\n" 967*aca7a94dSNamhyung Kim "a Annotate current symbol\n" 968*aca7a94dSNamhyung Kim "C Collapse all callchains\n" 969*aca7a94dSNamhyung Kim "E Expand all callchains\n" 970*aca7a94dSNamhyung Kim "d Zoom into current DSO\n" 971*aca7a94dSNamhyung Kim "t Zoom into current Thread\n" 972*aca7a94dSNamhyung Kim "s Filter symbol by name"); 973*aca7a94dSNamhyung Kim continue; 974*aca7a94dSNamhyung Kim case K_ENTER: 975*aca7a94dSNamhyung Kim case K_RIGHT: 976*aca7a94dSNamhyung Kim /* menu */ 977*aca7a94dSNamhyung Kim break; 978*aca7a94dSNamhyung Kim case K_LEFT: { 979*aca7a94dSNamhyung Kim const void *top; 980*aca7a94dSNamhyung Kim 981*aca7a94dSNamhyung Kim if (pstack__empty(fstack)) { 982*aca7a94dSNamhyung Kim /* 983*aca7a94dSNamhyung Kim * Go back to the perf_evsel_menu__run or other user 984*aca7a94dSNamhyung Kim */ 985*aca7a94dSNamhyung Kim if (left_exits) 986*aca7a94dSNamhyung Kim goto out_free_stack; 987*aca7a94dSNamhyung Kim continue; 988*aca7a94dSNamhyung Kim } 989*aca7a94dSNamhyung Kim top = pstack__pop(fstack); 990*aca7a94dSNamhyung Kim if (top == &browser->hists->dso_filter) 991*aca7a94dSNamhyung Kim goto zoom_out_dso; 992*aca7a94dSNamhyung Kim if (top == &browser->hists->thread_filter) 993*aca7a94dSNamhyung Kim goto zoom_out_thread; 994*aca7a94dSNamhyung Kim continue; 995*aca7a94dSNamhyung Kim } 996*aca7a94dSNamhyung Kim case K_ESC: 997*aca7a94dSNamhyung Kim if (!left_exits && 998*aca7a94dSNamhyung Kim !ui_browser__dialog_yesno(&browser->b, 999*aca7a94dSNamhyung Kim "Do you really want to exit?")) 1000*aca7a94dSNamhyung Kim continue; 1001*aca7a94dSNamhyung Kim /* Fall thru */ 1002*aca7a94dSNamhyung Kim case 'q': 1003*aca7a94dSNamhyung Kim case CTRL('c'): 1004*aca7a94dSNamhyung Kim goto out_free_stack; 1005*aca7a94dSNamhyung Kim default: 1006*aca7a94dSNamhyung Kim continue; 1007*aca7a94dSNamhyung Kim } 1008*aca7a94dSNamhyung Kim 1009*aca7a94dSNamhyung Kim if (!browser->has_symbols) 1010*aca7a94dSNamhyung Kim goto add_exit_option; 1011*aca7a94dSNamhyung Kim 1012*aca7a94dSNamhyung Kim if (sort__branch_mode == 1) { 1013*aca7a94dSNamhyung Kim bi = browser->he_selection->branch_info; 1014*aca7a94dSNamhyung Kim if (browser->selection != NULL && 1015*aca7a94dSNamhyung Kim bi && 1016*aca7a94dSNamhyung Kim bi->from.sym != NULL && 1017*aca7a94dSNamhyung Kim !bi->from.map->dso->annotate_warned && 1018*aca7a94dSNamhyung Kim asprintf(&options[nr_options], "Annotate %s", 1019*aca7a94dSNamhyung Kim bi->from.sym->name) > 0) 1020*aca7a94dSNamhyung Kim annotate_f = nr_options++; 1021*aca7a94dSNamhyung Kim 1022*aca7a94dSNamhyung Kim if (browser->selection != NULL && 1023*aca7a94dSNamhyung Kim bi && 1024*aca7a94dSNamhyung Kim bi->to.sym != NULL && 1025*aca7a94dSNamhyung Kim !bi->to.map->dso->annotate_warned && 1026*aca7a94dSNamhyung Kim (bi->to.sym != bi->from.sym || 1027*aca7a94dSNamhyung Kim bi->to.map->dso != bi->from.map->dso) && 1028*aca7a94dSNamhyung Kim asprintf(&options[nr_options], "Annotate %s", 1029*aca7a94dSNamhyung Kim bi->to.sym->name) > 0) 1030*aca7a94dSNamhyung Kim annotate_t = nr_options++; 1031*aca7a94dSNamhyung Kim } else { 1032*aca7a94dSNamhyung Kim 1033*aca7a94dSNamhyung Kim if (browser->selection != NULL && 1034*aca7a94dSNamhyung Kim browser->selection->sym != NULL && 1035*aca7a94dSNamhyung Kim !browser->selection->map->dso->annotate_warned && 1036*aca7a94dSNamhyung Kim asprintf(&options[nr_options], "Annotate %s", 1037*aca7a94dSNamhyung Kim browser->selection->sym->name) > 0) 1038*aca7a94dSNamhyung Kim annotate = nr_options++; 1039*aca7a94dSNamhyung Kim } 1040*aca7a94dSNamhyung Kim 1041*aca7a94dSNamhyung Kim if (thread != NULL && 1042*aca7a94dSNamhyung Kim asprintf(&options[nr_options], "Zoom %s %s(%d) thread", 1043*aca7a94dSNamhyung Kim (browser->hists->thread_filter ? "out of" : "into"), 1044*aca7a94dSNamhyung Kim (thread->comm_set ? thread->comm : ""), 1045*aca7a94dSNamhyung Kim thread->pid) > 0) 1046*aca7a94dSNamhyung Kim zoom_thread = nr_options++; 1047*aca7a94dSNamhyung Kim 1048*aca7a94dSNamhyung Kim if (dso != NULL && 1049*aca7a94dSNamhyung Kim asprintf(&options[nr_options], "Zoom %s %s DSO", 1050*aca7a94dSNamhyung Kim (browser->hists->dso_filter ? "out of" : "into"), 1051*aca7a94dSNamhyung Kim (dso->kernel ? "the Kernel" : dso->short_name)) > 0) 1052*aca7a94dSNamhyung Kim zoom_dso = nr_options++; 1053*aca7a94dSNamhyung Kim 1054*aca7a94dSNamhyung Kim if (browser->selection != NULL && 1055*aca7a94dSNamhyung Kim browser->selection->map != NULL && 1056*aca7a94dSNamhyung Kim asprintf(&options[nr_options], "Browse map details") > 0) 1057*aca7a94dSNamhyung Kim browse_map = nr_options++; 1058*aca7a94dSNamhyung Kim add_exit_option: 1059*aca7a94dSNamhyung Kim options[nr_options++] = (char *)"Exit"; 1060*aca7a94dSNamhyung Kim retry_popup_menu: 1061*aca7a94dSNamhyung Kim choice = ui__popup_menu(nr_options, options); 1062*aca7a94dSNamhyung Kim 1063*aca7a94dSNamhyung Kim if (choice == nr_options - 1) 1064*aca7a94dSNamhyung Kim break; 1065*aca7a94dSNamhyung Kim 1066*aca7a94dSNamhyung Kim if (choice == -1) { 1067*aca7a94dSNamhyung Kim free_popup_options(options, nr_options - 1); 1068*aca7a94dSNamhyung Kim continue; 1069*aca7a94dSNamhyung Kim } 1070*aca7a94dSNamhyung Kim 1071*aca7a94dSNamhyung Kim if (choice == annotate || choice == annotate_t || choice == annotate_f) { 1072*aca7a94dSNamhyung Kim struct hist_entry *he; 1073*aca7a94dSNamhyung Kim int err; 1074*aca7a94dSNamhyung Kim do_annotate: 1075*aca7a94dSNamhyung Kim he = hist_browser__selected_entry(browser); 1076*aca7a94dSNamhyung Kim if (he == NULL) 1077*aca7a94dSNamhyung Kim continue; 1078*aca7a94dSNamhyung Kim 1079*aca7a94dSNamhyung Kim /* 1080*aca7a94dSNamhyung Kim * we stash the branch_info symbol + map into the 1081*aca7a94dSNamhyung Kim * the ms so we don't have to rewrite all the annotation 1082*aca7a94dSNamhyung Kim * code to use branch_info. 1083*aca7a94dSNamhyung Kim * in branch mode, the ms struct is not used 1084*aca7a94dSNamhyung Kim */ 1085*aca7a94dSNamhyung Kim if (choice == annotate_f) { 1086*aca7a94dSNamhyung Kim he->ms.sym = he->branch_info->from.sym; 1087*aca7a94dSNamhyung Kim he->ms.map = he->branch_info->from.map; 1088*aca7a94dSNamhyung Kim } else if (choice == annotate_t) { 1089*aca7a94dSNamhyung Kim he->ms.sym = he->branch_info->to.sym; 1090*aca7a94dSNamhyung Kim he->ms.map = he->branch_info->to.map; 1091*aca7a94dSNamhyung Kim } 1092*aca7a94dSNamhyung Kim 1093*aca7a94dSNamhyung Kim /* 1094*aca7a94dSNamhyung Kim * Don't let this be freed, say, by hists__decay_entry. 1095*aca7a94dSNamhyung Kim */ 1096*aca7a94dSNamhyung Kim he->used = true; 1097*aca7a94dSNamhyung Kim err = hist_entry__tui_annotate(he, evsel->idx, 1098*aca7a94dSNamhyung Kim timer, arg, delay_secs); 1099*aca7a94dSNamhyung Kim he->used = false; 1100*aca7a94dSNamhyung Kim /* 1101*aca7a94dSNamhyung Kim * offer option to annotate the other branch source or target 1102*aca7a94dSNamhyung Kim * (if they exists) when returning from annotate 1103*aca7a94dSNamhyung Kim */ 1104*aca7a94dSNamhyung Kim if ((err == 'q' || err == CTRL('c')) 1105*aca7a94dSNamhyung Kim && annotate_t != -2 && annotate_f != -2) 1106*aca7a94dSNamhyung Kim goto retry_popup_menu; 1107*aca7a94dSNamhyung Kim 1108*aca7a94dSNamhyung Kim ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 1109*aca7a94dSNamhyung Kim if (err) 1110*aca7a94dSNamhyung Kim ui_browser__handle_resize(&browser->b); 1111*aca7a94dSNamhyung Kim 1112*aca7a94dSNamhyung Kim } else if (choice == browse_map) 1113*aca7a94dSNamhyung Kim map__browse(browser->selection->map); 1114*aca7a94dSNamhyung Kim else if (choice == zoom_dso) { 1115*aca7a94dSNamhyung Kim zoom_dso: 1116*aca7a94dSNamhyung Kim if (browser->hists->dso_filter) { 1117*aca7a94dSNamhyung Kim pstack__remove(fstack, &browser->hists->dso_filter); 1118*aca7a94dSNamhyung Kim zoom_out_dso: 1119*aca7a94dSNamhyung Kim ui_helpline__pop(); 1120*aca7a94dSNamhyung Kim browser->hists->dso_filter = NULL; 1121*aca7a94dSNamhyung Kim sort_dso.elide = false; 1122*aca7a94dSNamhyung Kim } else { 1123*aca7a94dSNamhyung Kim if (dso == NULL) 1124*aca7a94dSNamhyung Kim continue; 1125*aca7a94dSNamhyung Kim ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", 1126*aca7a94dSNamhyung Kim dso->kernel ? "the Kernel" : dso->short_name); 1127*aca7a94dSNamhyung Kim browser->hists->dso_filter = dso; 1128*aca7a94dSNamhyung Kim sort_dso.elide = true; 1129*aca7a94dSNamhyung Kim pstack__push(fstack, &browser->hists->dso_filter); 1130*aca7a94dSNamhyung Kim } 1131*aca7a94dSNamhyung Kim hists__filter_by_dso(self); 1132*aca7a94dSNamhyung Kim hist_browser__reset(browser); 1133*aca7a94dSNamhyung Kim } else if (choice == zoom_thread) { 1134*aca7a94dSNamhyung Kim zoom_thread: 1135*aca7a94dSNamhyung Kim if (browser->hists->thread_filter) { 1136*aca7a94dSNamhyung Kim pstack__remove(fstack, &browser->hists->thread_filter); 1137*aca7a94dSNamhyung Kim zoom_out_thread: 1138*aca7a94dSNamhyung Kim ui_helpline__pop(); 1139*aca7a94dSNamhyung Kim browser->hists->thread_filter = NULL; 1140*aca7a94dSNamhyung Kim sort_thread.elide = false; 1141*aca7a94dSNamhyung Kim } else { 1142*aca7a94dSNamhyung Kim ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", 1143*aca7a94dSNamhyung Kim thread->comm_set ? thread->comm : "", 1144*aca7a94dSNamhyung Kim thread->pid); 1145*aca7a94dSNamhyung Kim browser->hists->thread_filter = thread; 1146*aca7a94dSNamhyung Kim sort_thread.elide = true; 1147*aca7a94dSNamhyung Kim pstack__push(fstack, &browser->hists->thread_filter); 1148*aca7a94dSNamhyung Kim } 1149*aca7a94dSNamhyung Kim hists__filter_by_thread(self); 1150*aca7a94dSNamhyung Kim hist_browser__reset(browser); 1151*aca7a94dSNamhyung Kim } 1152*aca7a94dSNamhyung Kim } 1153*aca7a94dSNamhyung Kim out_free_stack: 1154*aca7a94dSNamhyung Kim pstack__delete(fstack); 1155*aca7a94dSNamhyung Kim out: 1156*aca7a94dSNamhyung Kim hist_browser__delete(browser); 1157*aca7a94dSNamhyung Kim free_popup_options(options, nr_options - 1); 1158*aca7a94dSNamhyung Kim return key; 1159*aca7a94dSNamhyung Kim } 1160*aca7a94dSNamhyung Kim 1161*aca7a94dSNamhyung Kim struct perf_evsel_menu { 1162*aca7a94dSNamhyung Kim struct ui_browser b; 1163*aca7a94dSNamhyung Kim struct perf_evsel *selection; 1164*aca7a94dSNamhyung Kim bool lost_events, lost_events_warned; 1165*aca7a94dSNamhyung Kim }; 1166*aca7a94dSNamhyung Kim 1167*aca7a94dSNamhyung Kim static void perf_evsel_menu__write(struct ui_browser *browser, 1168*aca7a94dSNamhyung Kim void *entry, int row) 1169*aca7a94dSNamhyung Kim { 1170*aca7a94dSNamhyung Kim struct perf_evsel_menu *menu = container_of(browser, 1171*aca7a94dSNamhyung Kim struct perf_evsel_menu, b); 1172*aca7a94dSNamhyung Kim struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); 1173*aca7a94dSNamhyung Kim bool current_entry = ui_browser__is_current_entry(browser, row); 1174*aca7a94dSNamhyung Kim unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; 1175*aca7a94dSNamhyung Kim const char *ev_name = event_name(evsel); 1176*aca7a94dSNamhyung Kim char bf[256], unit; 1177*aca7a94dSNamhyung Kim const char *warn = " "; 1178*aca7a94dSNamhyung Kim size_t printed; 1179*aca7a94dSNamhyung Kim 1180*aca7a94dSNamhyung Kim ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 1181*aca7a94dSNamhyung Kim HE_COLORSET_NORMAL); 1182*aca7a94dSNamhyung Kim 1183*aca7a94dSNamhyung Kim nr_events = convert_unit(nr_events, &unit); 1184*aca7a94dSNamhyung Kim printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 1185*aca7a94dSNamhyung Kim unit, unit == ' ' ? "" : " ", ev_name); 1186*aca7a94dSNamhyung Kim slsmg_printf("%s", bf); 1187*aca7a94dSNamhyung Kim 1188*aca7a94dSNamhyung Kim nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST]; 1189*aca7a94dSNamhyung Kim if (nr_events != 0) { 1190*aca7a94dSNamhyung Kim menu->lost_events = true; 1191*aca7a94dSNamhyung Kim if (!current_entry) 1192*aca7a94dSNamhyung Kim ui_browser__set_color(browser, HE_COLORSET_TOP); 1193*aca7a94dSNamhyung Kim nr_events = convert_unit(nr_events, &unit); 1194*aca7a94dSNamhyung Kim printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", 1195*aca7a94dSNamhyung Kim nr_events, unit, unit == ' ' ? "" : " "); 1196*aca7a94dSNamhyung Kim warn = bf; 1197*aca7a94dSNamhyung Kim } 1198*aca7a94dSNamhyung Kim 1199*aca7a94dSNamhyung Kim slsmg_write_nstring(warn, browser->width - printed); 1200*aca7a94dSNamhyung Kim 1201*aca7a94dSNamhyung Kim if (current_entry) 1202*aca7a94dSNamhyung Kim menu->selection = evsel; 1203*aca7a94dSNamhyung Kim } 1204*aca7a94dSNamhyung Kim 1205*aca7a94dSNamhyung Kim static int perf_evsel_menu__run(struct perf_evsel_menu *menu, 1206*aca7a94dSNamhyung Kim int nr_events, const char *help, 1207*aca7a94dSNamhyung Kim void(*timer)(void *arg), void *arg, int delay_secs) 1208*aca7a94dSNamhyung Kim { 1209*aca7a94dSNamhyung Kim struct perf_evlist *evlist = menu->b.priv; 1210*aca7a94dSNamhyung Kim struct perf_evsel *pos; 1211*aca7a94dSNamhyung Kim const char *ev_name, *title = "Available samples"; 1212*aca7a94dSNamhyung Kim int key; 1213*aca7a94dSNamhyung Kim 1214*aca7a94dSNamhyung Kim if (ui_browser__show(&menu->b, title, 1215*aca7a94dSNamhyung Kim "ESC: exit, ENTER|->: Browse histograms") < 0) 1216*aca7a94dSNamhyung Kim return -1; 1217*aca7a94dSNamhyung Kim 1218*aca7a94dSNamhyung Kim while (1) { 1219*aca7a94dSNamhyung Kim key = ui_browser__run(&menu->b, delay_secs); 1220*aca7a94dSNamhyung Kim 1221*aca7a94dSNamhyung Kim switch (key) { 1222*aca7a94dSNamhyung Kim case K_TIMER: 1223*aca7a94dSNamhyung Kim timer(arg); 1224*aca7a94dSNamhyung Kim 1225*aca7a94dSNamhyung Kim if (!menu->lost_events_warned && menu->lost_events) { 1226*aca7a94dSNamhyung Kim ui_browser__warn_lost_events(&menu->b); 1227*aca7a94dSNamhyung Kim menu->lost_events_warned = true; 1228*aca7a94dSNamhyung Kim } 1229*aca7a94dSNamhyung Kim continue; 1230*aca7a94dSNamhyung Kim case K_RIGHT: 1231*aca7a94dSNamhyung Kim case K_ENTER: 1232*aca7a94dSNamhyung Kim if (!menu->selection) 1233*aca7a94dSNamhyung Kim continue; 1234*aca7a94dSNamhyung Kim pos = menu->selection; 1235*aca7a94dSNamhyung Kim browse_hists: 1236*aca7a94dSNamhyung Kim perf_evlist__set_selected(evlist, pos); 1237*aca7a94dSNamhyung Kim /* 1238*aca7a94dSNamhyung Kim * Give the calling tool a chance to populate the non 1239*aca7a94dSNamhyung Kim * default evsel resorted hists tree. 1240*aca7a94dSNamhyung Kim */ 1241*aca7a94dSNamhyung Kim if (timer) 1242*aca7a94dSNamhyung Kim timer(arg); 1243*aca7a94dSNamhyung Kim ev_name = event_name(pos); 1244*aca7a94dSNamhyung Kim key = perf_evsel__hists_browse(pos, nr_events, help, 1245*aca7a94dSNamhyung Kim ev_name, true, timer, 1246*aca7a94dSNamhyung Kim arg, delay_secs); 1247*aca7a94dSNamhyung Kim ui_browser__show_title(&menu->b, title); 1248*aca7a94dSNamhyung Kim switch (key) { 1249*aca7a94dSNamhyung Kim case K_TAB: 1250*aca7a94dSNamhyung Kim if (pos->node.next == &evlist->entries) 1251*aca7a94dSNamhyung Kim pos = list_entry(evlist->entries.next, struct perf_evsel, node); 1252*aca7a94dSNamhyung Kim else 1253*aca7a94dSNamhyung Kim pos = list_entry(pos->node.next, struct perf_evsel, node); 1254*aca7a94dSNamhyung Kim goto browse_hists; 1255*aca7a94dSNamhyung Kim case K_UNTAB: 1256*aca7a94dSNamhyung Kim if (pos->node.prev == &evlist->entries) 1257*aca7a94dSNamhyung Kim pos = list_entry(evlist->entries.prev, struct perf_evsel, node); 1258*aca7a94dSNamhyung Kim else 1259*aca7a94dSNamhyung Kim pos = list_entry(pos->node.prev, struct perf_evsel, node); 1260*aca7a94dSNamhyung Kim goto browse_hists; 1261*aca7a94dSNamhyung Kim case K_ESC: 1262*aca7a94dSNamhyung Kim if (!ui_browser__dialog_yesno(&menu->b, 1263*aca7a94dSNamhyung Kim "Do you really want to exit?")) 1264*aca7a94dSNamhyung Kim continue; 1265*aca7a94dSNamhyung Kim /* Fall thru */ 1266*aca7a94dSNamhyung Kim case 'q': 1267*aca7a94dSNamhyung Kim case CTRL('c'): 1268*aca7a94dSNamhyung Kim goto out; 1269*aca7a94dSNamhyung Kim default: 1270*aca7a94dSNamhyung Kim continue; 1271*aca7a94dSNamhyung Kim } 1272*aca7a94dSNamhyung Kim case K_LEFT: 1273*aca7a94dSNamhyung Kim continue; 1274*aca7a94dSNamhyung Kim case K_ESC: 1275*aca7a94dSNamhyung Kim if (!ui_browser__dialog_yesno(&menu->b, 1276*aca7a94dSNamhyung Kim "Do you really want to exit?")) 1277*aca7a94dSNamhyung Kim continue; 1278*aca7a94dSNamhyung Kim /* Fall thru */ 1279*aca7a94dSNamhyung Kim case 'q': 1280*aca7a94dSNamhyung Kim case CTRL('c'): 1281*aca7a94dSNamhyung Kim goto out; 1282*aca7a94dSNamhyung Kim default: 1283*aca7a94dSNamhyung Kim continue; 1284*aca7a94dSNamhyung Kim } 1285*aca7a94dSNamhyung Kim } 1286*aca7a94dSNamhyung Kim 1287*aca7a94dSNamhyung Kim out: 1288*aca7a94dSNamhyung Kim ui_browser__hide(&menu->b); 1289*aca7a94dSNamhyung Kim return key; 1290*aca7a94dSNamhyung Kim } 1291*aca7a94dSNamhyung Kim 1292*aca7a94dSNamhyung Kim static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 1293*aca7a94dSNamhyung Kim const char *help, 1294*aca7a94dSNamhyung Kim void(*timer)(void *arg), void *arg, 1295*aca7a94dSNamhyung Kim int delay_secs) 1296*aca7a94dSNamhyung Kim { 1297*aca7a94dSNamhyung Kim struct perf_evsel *pos; 1298*aca7a94dSNamhyung Kim struct perf_evsel_menu menu = { 1299*aca7a94dSNamhyung Kim .b = { 1300*aca7a94dSNamhyung Kim .entries = &evlist->entries, 1301*aca7a94dSNamhyung Kim .refresh = ui_browser__list_head_refresh, 1302*aca7a94dSNamhyung Kim .seek = ui_browser__list_head_seek, 1303*aca7a94dSNamhyung Kim .write = perf_evsel_menu__write, 1304*aca7a94dSNamhyung Kim .nr_entries = evlist->nr_entries, 1305*aca7a94dSNamhyung Kim .priv = evlist, 1306*aca7a94dSNamhyung Kim }, 1307*aca7a94dSNamhyung Kim }; 1308*aca7a94dSNamhyung Kim 1309*aca7a94dSNamhyung Kim ui_helpline__push("Press ESC to exit"); 1310*aca7a94dSNamhyung Kim 1311*aca7a94dSNamhyung Kim list_for_each_entry(pos, &evlist->entries, node) { 1312*aca7a94dSNamhyung Kim const char *ev_name = event_name(pos); 1313*aca7a94dSNamhyung Kim size_t line_len = strlen(ev_name) + 7; 1314*aca7a94dSNamhyung Kim 1315*aca7a94dSNamhyung Kim if (menu.b.width < line_len) 1316*aca7a94dSNamhyung Kim menu.b.width = line_len; 1317*aca7a94dSNamhyung Kim /* 1318*aca7a94dSNamhyung Kim * Cache the evsel name, tracepoints have a _high_ cost per 1319*aca7a94dSNamhyung Kim * event_name() call. 1320*aca7a94dSNamhyung Kim */ 1321*aca7a94dSNamhyung Kim if (pos->name == NULL) 1322*aca7a94dSNamhyung Kim pos->name = strdup(ev_name); 1323*aca7a94dSNamhyung Kim } 1324*aca7a94dSNamhyung Kim 1325*aca7a94dSNamhyung Kim return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, 1326*aca7a94dSNamhyung Kim arg, delay_secs); 1327*aca7a94dSNamhyung Kim } 1328*aca7a94dSNamhyung Kim 1329*aca7a94dSNamhyung Kim int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 1330*aca7a94dSNamhyung Kim void(*timer)(void *arg), void *arg, 1331*aca7a94dSNamhyung Kim int delay_secs) 1332*aca7a94dSNamhyung Kim { 1333*aca7a94dSNamhyung Kim 1334*aca7a94dSNamhyung Kim if (evlist->nr_entries == 1) { 1335*aca7a94dSNamhyung Kim struct perf_evsel *first = list_entry(evlist->entries.next, 1336*aca7a94dSNamhyung Kim struct perf_evsel, node); 1337*aca7a94dSNamhyung Kim const char *ev_name = event_name(first); 1338*aca7a94dSNamhyung Kim return perf_evsel__hists_browse(first, evlist->nr_entries, help, 1339*aca7a94dSNamhyung Kim ev_name, false, timer, arg, 1340*aca7a94dSNamhyung Kim delay_secs); 1341*aca7a94dSNamhyung Kim } 1342*aca7a94dSNamhyung Kim 1343*aca7a94dSNamhyung Kim return __perf_evlist__tui_browse_hists(evlist, help, 1344*aca7a94dSNamhyung Kim timer, arg, delay_secs); 1345*aca7a94dSNamhyung Kim } 1346