1 #include "../browser.h" 2 #include "../helpline.h" 3 #include "../libslang.h" 4 #include "../../annotate.h" 5 #include "../../hist.h" 6 #include "../../sort.h" 7 #include "../../symbol.h" 8 #include <pthread.h> 9 10 static void ui__error_window(const char *fmt, ...) 11 { 12 va_list ap; 13 14 va_start(ap, fmt); 15 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); 16 va_end(ap); 17 } 18 19 struct annotate_browser { 20 struct ui_browser b; 21 struct rb_root entries; 22 struct rb_node *curr_hot; 23 }; 24 25 struct objdump_line_rb_node { 26 struct rb_node rb_node; 27 double percent; 28 u32 idx; 29 }; 30 31 static inline 32 struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) 33 { 34 return (struct objdump_line_rb_node *)(self + 1); 35 } 36 37 static void annotate_browser__write(struct ui_browser *self, void *entry, int row) 38 { 39 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); 40 bool current_entry = ui_browser__is_current_entry(self, row); 41 int width = self->width; 42 43 if (ol->offset != -1) { 44 struct objdump_line_rb_node *olrb = objdump_line__rb(ol); 45 ui_browser__set_percent_color(self, olrb->percent, current_entry); 46 slsmg_printf(" %7.2f ", olrb->percent); 47 } else { 48 ui_browser__set_percent_color(self, 0, current_entry); 49 slsmg_write_nstring(" ", 9); 50 } 51 52 SLsmg_write_char(':'); 53 slsmg_write_nstring(" ", 8); 54 if (!*ol->line) 55 slsmg_write_nstring(" ", width - 18); 56 else 57 slsmg_write_nstring(ol->line, width - 18); 58 59 if (!current_entry) 60 ui_browser__set_color(self, HE_COLORSET_CODE); 61 } 62 63 static double objdump_line__calc_percent(struct objdump_line *self, 64 struct symbol *sym, int evidx) 65 { 66 double percent = 0.0; 67 68 if (self->offset != -1) { 69 int len = sym->end - sym->start; 70 unsigned int hits = 0; 71 struct annotation *notes = symbol__annotation(sym); 72 struct source_line *src_line = notes->src->lines; 73 struct sym_hist *h = annotation__histogram(notes, evidx); 74 s64 offset = self->offset; 75 struct objdump_line *next; 76 77 next = objdump__get_next_ip_line(¬es->src->source, self); 78 while (offset < (s64)len && 79 (next == NULL || offset < next->offset)) { 80 if (src_line) { 81 percent += src_line[offset].percent; 82 } else 83 hits += h->addr[offset]; 84 85 ++offset; 86 } 87 /* 88 * If the percentage wasn't already calculated in 89 * symbol__get_source_line, do it now: 90 */ 91 if (src_line == NULL && h->sum) 92 percent = 100.0 * hits / h->sum; 93 } 94 95 return percent; 96 } 97 98 static void objdump__insert_line(struct rb_root *self, 99 struct objdump_line_rb_node *line) 100 { 101 struct rb_node **p = &self->rb_node; 102 struct rb_node *parent = NULL; 103 struct objdump_line_rb_node *l; 104 105 while (*p != NULL) { 106 parent = *p; 107 l = rb_entry(parent, struct objdump_line_rb_node, rb_node); 108 if (line->percent < l->percent) 109 p = &(*p)->rb_left; 110 else 111 p = &(*p)->rb_right; 112 } 113 rb_link_node(&line->rb_node, parent, p); 114 rb_insert_color(&line->rb_node, self); 115 } 116 117 static void annotate_browser__set_top(struct annotate_browser *self, 118 struct rb_node *nd) 119 { 120 struct objdump_line_rb_node *rbpos; 121 struct objdump_line *pos; 122 unsigned back; 123 124 ui_browser__refresh_dimensions(&self->b); 125 back = self->b.height / 2; 126 rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node); 127 pos = ((struct objdump_line *)rbpos) - 1; 128 self->b.top_idx = self->b.index = rbpos->idx; 129 130 while (self->b.top_idx != 0 && back != 0) { 131 pos = list_entry(pos->node.prev, struct objdump_line, node); 132 133 --self->b.top_idx; 134 --back; 135 } 136 137 self->b.top = pos; 138 self->curr_hot = nd; 139 } 140 141 static void annotate_browser__calc_percent(struct annotate_browser *browser, 142 int evidx) 143 { 144 struct symbol *sym = browser->b.priv; 145 struct annotation *notes = symbol__annotation(sym); 146 struct objdump_line *pos; 147 148 browser->entries = RB_ROOT; 149 150 pthread_mutex_lock(¬es->lock); 151 152 list_for_each_entry(pos, ¬es->src->source, node) { 153 struct objdump_line_rb_node *rbpos = objdump_line__rb(pos); 154 rbpos->percent = objdump_line__calc_percent(pos, sym, evidx); 155 if (rbpos->percent < 0.01) { 156 RB_CLEAR_NODE(&rbpos->rb_node); 157 continue; 158 } 159 objdump__insert_line(&browser->entries, rbpos); 160 } 161 pthread_mutex_unlock(¬es->lock); 162 163 browser->curr_hot = rb_last(&browser->entries); 164 } 165 166 static int annotate_browser__run(struct annotate_browser *self, int evidx, 167 int refresh) 168 { 169 struct rb_node *nd = NULL; 170 struct symbol *sym = self->b.priv; 171 /* 172 * RIGHT To allow builtin-annotate to cycle thru multiple symbols by 173 * examining the exit key for this function. 174 */ 175 int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, 176 NEWT_KEY_RIGHT, 0 }; 177 int key; 178 179 if (ui_browser__show(&self->b, sym->name, 180 "<-, -> or ESC: exit, TAB/shift+TAB: " 181 "cycle hottest lines, H: Hottest") < 0) 182 return -1; 183 184 ui_browser__add_exit_keys(&self->b, exit_keys); 185 annotate_browser__calc_percent(self, evidx); 186 187 if (self->curr_hot) 188 annotate_browser__set_top(self, self->curr_hot); 189 190 nd = self->curr_hot; 191 192 if (refresh != 0) 193 newtFormSetTimer(self->b.form, refresh); 194 195 while (1) { 196 key = ui_browser__run(&self->b); 197 198 if (refresh != 0) { 199 annotate_browser__calc_percent(self, evidx); 200 /* 201 * Current line focus got out of the list of most active 202 * lines, NULL it so that if TAB|UNTAB is pressed, we 203 * move to curr_hot (current hottest line). 204 */ 205 if (nd != NULL && RB_EMPTY_NODE(nd)) 206 nd = NULL; 207 } 208 209 switch (key) { 210 case -1: 211 /* 212 * FIXME we need to check if it was 213 * es.reason == NEWT_EXIT_TIMER 214 */ 215 if (refresh != 0) 216 symbol__annotate_decay_histogram(sym, evidx); 217 continue; 218 case NEWT_KEY_TAB: 219 if (nd != NULL) { 220 nd = rb_prev(nd); 221 if (nd == NULL) 222 nd = rb_last(&self->entries); 223 } else 224 nd = self->curr_hot; 225 break; 226 case NEWT_KEY_UNTAB: 227 if (nd != NULL) 228 nd = rb_next(nd); 229 if (nd == NULL) 230 nd = rb_first(&self->entries); 231 else 232 nd = self->curr_hot; 233 break; 234 case 'H': 235 nd = self->curr_hot; 236 break; 237 default: 238 goto out; 239 } 240 241 if (nd != NULL) 242 annotate_browser__set_top(self, nd); 243 } 244 out: 245 ui_browser__hide(&self->b); 246 return key; 247 } 248 249 int hist_entry__tui_annotate(struct hist_entry *he, int evidx) 250 { 251 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); 252 } 253 254 int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, 255 int refresh) 256 { 257 struct objdump_line *pos, *n; 258 struct annotation *notes; 259 struct annotate_browser browser = { 260 .b = { 261 .refresh = ui_browser__list_head_refresh, 262 .seek = ui_browser__list_head_seek, 263 .write = annotate_browser__write, 264 .priv = sym, 265 }, 266 }; 267 int ret; 268 269 if (sym == NULL) 270 return -1; 271 272 if (map->dso->annotate_warned) 273 return -1; 274 275 if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { 276 ui__error_window(ui_helpline__last_msg); 277 return -1; 278 } 279 280 ui_helpline__push("Press <- or ESC to exit"); 281 282 notes = symbol__annotation(sym); 283 284 list_for_each_entry(pos, ¬es->src->source, node) { 285 struct objdump_line_rb_node *rbpos; 286 size_t line_len = strlen(pos->line); 287 288 if (browser.b.width < line_len) 289 browser.b.width = line_len; 290 rbpos = objdump_line__rb(pos); 291 rbpos->idx = browser.b.nr_entries++; 292 } 293 294 browser.b.entries = ¬es->src->source, 295 browser.b.width += 18; /* Percentage */ 296 ret = annotate_browser__run(&browser, evidx, refresh); 297 list_for_each_entry_safe(pos, n, ¬es->src->source, node) { 298 list_del(&pos->node); 299 objdump_line__free(pos); 300 } 301 return ret; 302 } 303