| 1 | |
| 2 | #include "../browser.h"
|
| 3 | #include "../helpline.h"
|
| 4 | #include "../ui.h"
|
| 5 | #include "../../util/annotate.h"
|
| 6 | #include "../../util/debug.h"
|
| 7 | #include "../../util/debuginfo.h"
|
| 8 | #include "../../util/dso.h"
|
| 9 | #include "../../util/hashmap.h"
|
| 10 | #include "../../util/hist.h"
|
| 11 | #include "../../util/sort.h"
|
| 12 | #include "../../util/map.h"
|
| 13 | #include "../../util/mutex.h"
|
| 14 | #include "../../util/symbol.h"
|
| 15 | #include "../../util/evsel.h"
|
| 16 | #include "../../util/evlist.h"
|
| 17 | #include "../../util/thread.h"
|
| 18 | #include <inttypes.h>
|
| 19 | #include <linux/err.h>
|
| 20 | #include <linux/kernel.h>
|
| 21 | #include <linux/string.h>
|
| 22 | #include <linux/zalloc.h>
|
| 23 | #include <sys/ttydefaults.h>
|
| 24 | #include <asm/bug.h>
|
| 25 |
|
| 26 | struct arch;
|
| 27 |
|
| 28 | struct annotate_browser {
|
| 29 | struct ui_browser b;
|
| 30 | struct rb_root entries;
|
| 31 | struct rb_node *curr_hot;
|
| 32 | struct annotation_line *selection;
|
| 33 | const struct arch *arch;
|
| 34 | |
| 35 | * perf top can delete hist_entry anytime. Callers should make sure
|
| 36 | * its lifetime.
|
| 37 |
|
| 38 | struct hist_entry *he;
|
| 39 | struct debuginfo *dbg;
|
| 40 | struct evsel *evsel;
|
| 41 | struct hashmap *type_hash;
|
| 42 | bool searching_backwards;
|
| 43 | char search_bf[128];
|
| 44 | };
|
| 45 |
|
| 46 |
|
| 47 | static struct hist_entry annotate_he;
|
| 48 |
|
| 49 | static size_t type_hash(long key, void *ctx __maybe_unused)
|
| 50 | {
|
| 51 | return key;
|
| 52 | }
|
| 53 |
|
| 54 | static bool type_equal(long key1, long key2, void *ctx __maybe_unused)
|
| 55 | {
|
| 56 | return key1 == key2;
|
| 57 | }
|
| 58 |
|
| 59 | static inline struct annotation *browser__annotation(struct ui_browser *browser)
|
| 60 | {
|
| 61 | struct map_symbol *ms = browser->priv;
|
| 62 | return symbol__annotation(ms->sym);
|
| 63 | }
|
| 64 |
|
| 65 | static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, void *entry)
|
| 66 | {
|
| 67 | struct annotation_line *al = list_entry(entry, struct annotation_line, node);
|
| 68 | return annotation_line__filter(al);
|
| 69 | }
|
| 70 |
|
| 71 | static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
|
| 72 | {
|
| 73 | struct annotation *notes = browser__annotation(browser);
|
| 74 |
|
| 75 | if (current && (!browser->use_navkeypressed || browser->navkeypressed))
|
| 76 | return HE_COLORSET_SELECTED;
|
| 77 | if (nr == notes->src->max_jump_sources)
|
| 78 | return HE_COLORSET_TOP;
|
| 79 | if (nr > 1)
|
| 80 | return HE_COLORSET_MEDIUM;
|
| 81 | return HE_COLORSET_NORMAL;
|
| 82 | }
|
| 83 |
|
| 84 | static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
|
| 85 | {
|
| 86 | int color = ui_browser__jumps_percent_color(browser, nr, current);
|
| 87 | return ui_browser__set_color(browser, color);
|
| 88 | }
|
| 89 |
|
| 90 | static int annotate_browser__set_color(void *browser, int color)
|
| 91 | {
|
| 92 | return ui_browser__set_color(browser, color);
|
| 93 | }
|
| 94 |
|
| 95 | static void annotate_browser__write_graph(void *browser, int graph)
|
| 96 | {
|
| 97 | ui_browser__write_graph(browser, graph);
|
| 98 | }
|
| 99 |
|
| 100 | static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
|
| 101 | {
|
| 102 | ui_browser__set_percent_color(browser, percent, current);
|
| 103 | }
|
| 104 |
|
| 105 | static void annotate_browser__printf(void *browser, const char *fmt, ...)
|
| 106 | {
|
| 107 | va_list args;
|
| 108 |
|
| 109 | va_start(args, fmt);
|
| 110 | ui_browser__vprintf(browser, fmt, args);
|
| 111 | va_end(args);
|
| 112 | }
|
| 113 |
|
| 114 | static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
|
| 115 | {
|
| 116 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
|
| 117 | struct annotation *notes = browser__annotation(browser);
|
| 118 | struct annotation_line *al = list_entry(entry, struct annotation_line, node);
|
| 119 | const bool is_current_entry = ui_browser__is_current_entry(browser, row);
|
| 120 | struct annotation_write_ops ops = {
|
| 121 | .first_line = row == 0,
|
| 122 | .current_entry = is_current_entry,
|
| 123 | .change_color = (!annotate_opts.hide_src_code &&
|
| 124 | (!is_current_entry ||
|
| 125 | (browser->use_navkeypressed &&
|
| 126 | !browser->navkeypressed))),
|
| 127 | .width = browser->width,
|
| 128 | .obj = browser,
|
| 129 | .set_color = annotate_browser__set_color,
|
| 130 | .set_percent_color = annotate_browser__set_percent_color,
|
| 131 | .set_jumps_percent_color = ui_browser__set_jumps_percent_color,
|
| 132 | .printf = annotate_browser__printf,
|
| 133 | .write_graph = annotate_browser__write_graph,
|
| 134 | };
|
| 135 | struct annotation_print_data apd = {
|
| 136 | .he = ab->he,
|
| 137 | .arch = ab->arch,
|
| 138 | .evsel = ab->evsel,
|
| 139 | .dbg = ab->dbg,
|
| 140 | };
|
| 141 |
|
| 142 |
|
| 143 | if (!browser->navkeypressed)
|
| 144 | ops.width += 1;
|
| 145 |
|
| 146 | if (!IS_ERR_OR_NULL(ab->type_hash))
|
| 147 | apd.type_hash = ab->type_hash;
|
| 148 |
|
| 149 | annotation_line__write(al, notes, &ops, &apd);
|
| 150 |
|
| 151 | if (ops.current_entry)
|
| 152 | ab->selection = al;
|
| 153 | }
|
| 154 |
|
| 155 | static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
|
| 156 | {
|
| 157 | struct disasm_line *pos = list_prev_entry(cursor, al.node);
|
| 158 | const char *name;
|
| 159 | int diff = 1;
|
| 160 |
|
| 161 | while (pos && pos->al.offset == -1) {
|
| 162 | pos = list_prev_entry(pos, al.node);
|
| 163 | if (!annotate_opts.hide_src_code)
|
| 164 | diff++;
|
| 165 | }
|
| 166 |
|
| 167 | if (!pos)
|
| 168 | return 0;
|
| 169 |
|
| 170 | if (ins__is_lock(&pos->ins))
|
| 171 | name = pos->ops.locked.ins.name;
|
| 172 | else
|
| 173 | name = pos->ins.name;
|
| 174 |
|
| 175 | if (!name || !cursor->ins.name)
|
| 176 | return 0;
|
| 177 |
|
| 178 | if (ins__is_fused(ab->arch, name, cursor->ins.name))
|
| 179 | return diff;
|
| 180 | return 0;
|
| 181 | }
|
| 182 |
|
| 183 | static void annotate_browser__draw_current_jump(struct ui_browser *browser)
|
| 184 | {
|
| 185 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
|
| 186 | struct disasm_line *cursor = disasm_line(ab->selection);
|
| 187 | struct annotation_line *target;
|
| 188 | unsigned int from, to;
|
| 189 | struct map_symbol *ms = ab->b.priv;
|
| 190 | struct symbol *sym = ms->sym;
|
| 191 | struct annotation *notes = symbol__annotation(sym);
|
| 192 | u8 pcnt_width = annotation__pcnt_width(notes);
|
| 193 | u8 cntr_width = annotation__br_cntr_width();
|
| 194 | int width;
|
| 195 | int diff = 0;
|
| 196 |
|
| 197 |
|
| 198 | if (strstr(sym->name, "@plt"))
|
| 199 | return;
|
| 200 |
|
| 201 | if (!disasm_line__is_valid_local_jump(cursor, sym))
|
| 202 | return;
|
| 203 |
|
| 204 | |
| 205 | * This first was seen with a gcc function, _cpp_lex_token, that
|
| 206 | * has the usual jumps:
|
| 207 | *
|
| 208 | * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92>
|
| 209 | *
|
| 210 | * I.e. jumps to a label inside that function (_cpp_lex_token), and
|
| 211 | * those works, but also this kind:
|
| 212 | *
|
| 213 | * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72>
|
| 214 | *
|
| 215 | * I.e. jumps to another function, outside _cpp_lex_token, which
|
| 216 | * are not being correctly handled generating as a side effect references
|
| 217 | * to ab->offset[] entries that are set to NULL, so to make this code
|
| 218 | * more robust, check that here.
|
| 219 | *
|
| 220 | * A proper fix for will be put in place, looking at the function
|
| 221 | * name right after the '<' token and probably treating this like a
|
| 222 | * 'call' instruction.
|
| 223 |
|
| 224 | target = annotated_source__get_line(notes->src, cursor->ops.target.offset);
|
| 225 | if (target == NULL) {
|
| 226 | ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
|
| 227 | cursor->ops.target.offset);
|
| 228 | return;
|
| 229 | }
|
| 230 |
|
| 231 | if (annotate_opts.hide_src_code) {
|
| 232 | from = cursor->al.idx_asm;
|
| 233 | to = target->idx_asm;
|
| 234 | } else {
|
| 235 | from = (u64)cursor->al.idx;
|
| 236 | to = (u64)target->idx;
|
| 237 | }
|
| 238 |
|
| 239 | width = annotation__cycles_width(notes);
|
| 240 |
|
| 241 | ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
|
| 242 | __ui_browser__line_arrow(browser,
|
| 243 | pcnt_width + 2 + notes->src->widths.addr + width + cntr_width,
|
| 244 | from, to);
|
| 245 |
|
| 246 | diff = is_fused(ab, cursor);
|
| 247 | if (diff > 0) {
|
| 248 | ui_browser__mark_fused(browser,
|
| 249 | pcnt_width + 3 + notes->src->widths.addr + width + cntr_width,
|
| 250 | from - diff, diff, to > from);
|
| 251 | }
|
| 252 | }
|
| 253 |
|
| 254 | static unsigned int annotate_browser__refresh(struct ui_browser *browser)
|
| 255 | {
|
| 256 | struct annotation *notes = browser__annotation(browser);
|
| 257 | int ret = ui_browser__list_head_refresh(browser);
|
| 258 | int pcnt_width = annotation__pcnt_width(notes);
|
| 259 |
|
| 260 | if (annotate_opts.jump_arrows)
|
| 261 | annotate_browser__draw_current_jump(browser);
|
| 262 |
|
| 263 | ui_browser__set_color(browser, HE_COLORSET_NORMAL);
|
| 264 | __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
|
| 265 | return ret;
|
| 266 | }
|
| 267 |
|
| 268 | static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
|
| 269 | int percent_type)
|
| 270 | {
|
| 271 | int i;
|
| 272 |
|
| 273 | for (i = 0; i < a->data_nr; i++) {
|
| 274 | if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
|
| 275 | continue;
|
| 276 | return a->data[i].percent[percent_type] -
|
| 277 | b->data[i].percent[percent_type];
|
| 278 | }
|
| 279 | return 0;
|
| 280 | }
|
| 281 |
|
| 282 | static void disasm_rb_tree__insert(struct annotate_browser *browser,
|
| 283 | struct annotation_line *al)
|
| 284 | {
|
| 285 | struct rb_root *root = &browser->entries;
|
| 286 | struct rb_node **p = &root->rb_node;
|
| 287 | struct rb_node *parent = NULL;
|
| 288 | struct annotation_line *l;
|
| 289 |
|
| 290 | while (*p != NULL) {
|
| 291 | parent = *p;
|
| 292 | l = rb_entry(parent, struct annotation_line, rb_node);
|
| 293 |
|
| 294 | if (disasm__cmp(al, l, annotate_opts.percent_type) < 0)
|
| 295 | p = &(*p)->rb_left;
|
| 296 | else
|
| 297 | p = &(*p)->rb_right;
|
| 298 | }
|
| 299 | rb_link_node(&al->rb_node, parent, p);
|
| 300 | rb_insert_color(&al->rb_node, root);
|
| 301 | }
|
| 302 |
|
| 303 | static void annotate_browser__set_top(struct annotate_browser *browser,
|
| 304 | struct annotation_line *pos, u32 idx)
|
| 305 | {
|
| 306 | unsigned back;
|
| 307 |
|
| 308 | ui_browser__refresh_dimensions(&browser->b);
|
| 309 | back = browser->b.height / 2;
|
| 310 | browser->b.top_idx = browser->b.index = idx;
|
| 311 |
|
| 312 | while (browser->b.top_idx != 0 && back != 0) {
|
| 313 | pos = list_entry(pos->node.prev, struct annotation_line, node);
|
| 314 |
|
| 315 | if (annotation_line__filter(pos))
|
| 316 | continue;
|
| 317 |
|
| 318 | --browser->b.top_idx;
|
| 319 | --back;
|
| 320 | }
|
| 321 |
|
| 322 | browser->b.top = pos;
|
| 323 | browser->b.navkeypressed = true;
|
| 324 | }
|
| 325 |
|
| 326 | static void annotate_browser__set_rb_top(struct annotate_browser *browser,
|
| 327 | struct rb_node *nd)
|
| 328 | {
|
| 329 | struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
|
| 330 | u32 idx = pos->idx;
|
| 331 |
|
| 332 | if (annotate_opts.hide_src_code)
|
| 333 | idx = pos->idx_asm;
|
| 334 | annotate_browser__set_top(browser, pos, idx);
|
| 335 | browser->curr_hot = nd;
|
| 336 | }
|
| 337 |
|
| 338 | static void annotate_browser__calc_percent(struct annotate_browser *browser,
|
| 339 | struct evsel *evsel)
|
| 340 | {
|
| 341 | struct map_symbol *ms = browser->b.priv;
|
| 342 | struct symbol *sym = ms->sym;
|
| 343 | struct annotation *notes = symbol__annotation(sym);
|
| 344 | struct disasm_line *pos;
|
| 345 |
|
| 346 | browser->entries = RB_ROOT;
|
| 347 |
|
| 348 | annotation__lock(notes);
|
| 349 |
|
| 350 | symbol__calc_percent(sym, evsel);
|
| 351 |
|
| 352 | list_for_each_entry(pos, ¬es->src->source, al.node) {
|
| 353 | double max_percent = 0.0;
|
| 354 | int i;
|
| 355 |
|
| 356 | if (pos->al.offset == -1) {
|
| 357 | RB_CLEAR_NODE(&pos->al.rb_node);
|
| 358 | continue;
|
| 359 | }
|
| 360 |
|
| 361 | for (i = 0; i < pos->al.data_nr; i++) {
|
| 362 | double percent;
|
| 363 |
|
| 364 | percent = annotation_data__percent(&pos->al.data[i],
|
| 365 | annotate_opts.percent_type);
|
| 366 |
|
| 367 | if (max_percent < percent)
|
| 368 | max_percent = percent;
|
| 369 | }
|
| 370 |
|
| 371 | if (max_percent < 0.01 && (!pos->al.cycles || pos->al.cycles->ipc == 0)) {
|
| 372 | RB_CLEAR_NODE(&pos->al.rb_node);
|
| 373 | continue;
|
| 374 | }
|
| 375 | disasm_rb_tree__insert(browser, &pos->al);
|
| 376 | }
|
| 377 | annotation__unlock(notes);
|
| 378 |
|
| 379 | browser->curr_hot = rb_last(&browser->entries);
|
| 380 | }
|
| 381 |
|
| 382 | static struct annotation_line *annotate_browser__find_new_asm_line(
|
| 383 | struct annotate_browser *browser,
|
| 384 | int idx_asm)
|
| 385 | {
|
| 386 | struct annotation_line *al;
|
| 387 | struct list_head *head = browser->b.entries;
|
| 388 |
|
| 389 |
|
| 390 | list_for_each_entry(al, head, node) {
|
| 391 | if (al->idx_asm == idx_asm)
|
| 392 | return al;
|
| 393 | }
|
| 394 |
|
| 395 |
|
| 396 | return NULL;
|
| 397 | }
|
| 398 |
|
| 399 | static struct annotation_line *annotate_browser__find_next_asm_line(
|
| 400 | struct annotate_browser *browser,
|
| 401 | struct annotation_line *al)
|
| 402 | {
|
| 403 | struct annotation_line *it = al;
|
| 404 |
|
| 405 |
|
| 406 | list_for_each_entry_continue(it, browser->b.entries, node) {
|
| 407 | if (it->idx_asm >= 0)
|
| 408 | return it;
|
| 409 | }
|
| 410 |
|
| 411 |
|
| 412 | it = al;
|
| 413 | list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
|
| 414 | if (it->idx_asm >= 0)
|
| 415 | return it;
|
| 416 | }
|
| 417 |
|
| 418 |
|
| 419 | return NULL;
|
| 420 | }
|
| 421 |
|
| 422 | static bool annotation__has_source(struct annotation *notes)
|
| 423 | {
|
| 424 | struct annotation_line *al;
|
| 425 | bool found_asm = false;
|
| 426 |
|
| 427 |
|
| 428 | list_for_each_entry(al, ¬es->src->source, node) {
|
| 429 | if (al->offset >= 0) {
|
| 430 | found_asm = true;
|
| 431 | break;
|
| 432 | }
|
| 433 | }
|
| 434 |
|
| 435 | if (found_asm) {
|
| 436 |
|
| 437 | list_for_each_entry_continue(al, ¬es->src->source, node) {
|
| 438 | if (al->offset == -1)
|
| 439 | return true;
|
| 440 | }
|
| 441 | }
|
| 442 | return false;
|
| 443 | }
|
| 444 |
|
| 445 | static bool annotate_browser__toggle_source(struct annotate_browser *browser,
|
| 446 | struct evsel *evsel)
|
| 447 | {
|
| 448 | struct annotation *notes = browser__annotation(&browser->b);
|
| 449 | struct annotation_line *al;
|
| 450 | off_t offset = browser->b.index - browser->b.top_idx;
|
| 451 |
|
| 452 | if (browser->b.nr_entries == 0)
|
| 453 | return false;
|
| 454 |
|
| 455 | browser->b.seek(&browser->b, offset, SEEK_CUR);
|
| 456 | al = list_entry(browser->b.top, struct annotation_line, node);
|
| 457 |
|
| 458 | if (!annotate_opts.annotate_src)
|
| 459 | annotate_opts.annotate_src = true;
|
| 460 |
|
| 461 | |
| 462 | * It's about to get source code annotation for the first time.
|
| 463 | * Drop the existing annotation_lines and get the new one with source.
|
| 464 | * And then move to the original line at the same asm index.
|
| 465 |
|
| 466 | if (annotate_opts.hide_src_code && !notes->src->tried_source) {
|
| 467 | struct map_symbol *ms = browser->b.priv;
|
| 468 | int orig_idx_asm = al->idx_asm;
|
| 469 |
|
| 470 |
|
| 471 | annotate_opts.hide_src_code = false;
|
| 472 | annotated_source__purge(notes->src);
|
| 473 | symbol__annotate2(ms, evsel, &browser->arch);
|
| 474 | annotate_opts.hide_src_code = true;
|
| 475 |
|
| 476 |
|
| 477 | notes->src->tried_source = true;
|
| 478 |
|
| 479 | if (!annotation__has_source(notes))
|
| 480 | ui__warning("Annotation has no source code.");
|
| 481 |
|
| 482 | browser->b.entries = ¬es->src->source;
|
| 483 | al = annotate_browser__find_new_asm_line(browser, orig_idx_asm);
|
| 484 | if (unlikely(al == NULL)) {
|
| 485 | al = list_first_entry(¬es->src->source,
|
| 486 | struct annotation_line, node);
|
| 487 | }
|
| 488 | browser->b.seek(&browser->b, al->idx_asm, SEEK_SET);
|
| 489 | }
|
| 490 |
|
| 491 | if (annotate_opts.hide_src_code) {
|
| 492 | if (al->idx_asm < offset)
|
| 493 | offset = al->idx;
|
| 494 |
|
| 495 | browser->b.nr_entries = notes->src->nr_entries;
|
| 496 | annotate_opts.hide_src_code = false;
|
| 497 | browser->b.seek(&browser->b, -offset, SEEK_CUR);
|
| 498 | browser->b.top_idx = al->idx - offset;
|
| 499 | browser->b.index = al->idx;
|
| 500 | } else {
|
| 501 | if (al->idx_asm < 0) {
|
| 502 |
|
| 503 | al = annotate_browser__find_next_asm_line(browser, al);
|
| 504 | if (!al) {
|
| 505 | browser->b.seek(&browser->b, -offset, SEEK_CUR);
|
| 506 | return false;
|
| 507 | }
|
| 508 | }
|
| 509 |
|
| 510 | if (al->idx_asm < offset)
|
| 511 | offset = al->idx_asm;
|
| 512 |
|
| 513 | browser->b.nr_entries = notes->src->nr_asm_entries;
|
| 514 | annotate_opts.hide_src_code = true;
|
| 515 | browser->b.seek(&browser->b, -offset, SEEK_CUR);
|
| 516 | browser->b.top_idx = al->idx_asm - offset;
|
| 517 | browser->b.index = al->idx_asm;
|
| 518 | }
|
| 519 |
|
| 520 | if (annotate_opts.hide_src_code_on_title)
|
| 521 | annotate_opts.hide_src_code_on_title = false;
|
| 522 |
|
| 523 | return true;
|
| 524 | }
|
| 525 |
|
| 526 | #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
|
| 527 |
|
| 528 | static void annotate_browser__show_full_location(struct ui_browser *browser)
|
| 529 | {
|
| 530 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
|
| 531 | struct disasm_line *cursor = disasm_line(ab->selection);
|
| 532 | struct annotation_line *al = &cursor->al;
|
| 533 |
|
| 534 | if (al->offset != -1)
|
| 535 | ui_helpline__puts("Only available for source code lines.");
|
| 536 | else if (al->fileloc == NULL)
|
| 537 | ui_helpline__puts("No source file location.");
|
| 538 | else {
|
| 539 | char help_line[SYM_TITLE_MAX_SIZE];
|
| 540 | sprintf (help_line, "Source file location: %s", al->fileloc);
|
| 541 | ui_helpline__puts(help_line);
|
| 542 | }
|
| 543 | }
|
| 544 |
|
| 545 | static void ui_browser__init_asm_mode(struct ui_browser *browser)
|
| 546 | {
|
| 547 | struct annotation *notes = browser__annotation(browser);
|
| 548 | browser->nr_entries = notes->src->nr_asm_entries;
|
| 549 | ui_browser__reset_index(browser);
|
| 550 | }
|
| 551 |
|
| 552 | static int sym_title(struct symbol *sym, struct map *map, char *title,
|
| 553 | size_t sz, int percent_type)
|
| 554 | {
|
| 555 | return snprintf(title, sz, "%s %s [Percent: %s] %s", sym->name,
|
| 556 | dso__long_name(map__dso(map)),
|
| 557 | percent_type_str(percent_type),
|
| 558 | annotate_opts.code_with_type ? "[Type]" : "");
|
| 559 | }
|
| 560 |
|
| 561 | static void annotate_browser__show_function_title(struct annotate_browser *browser)
|
| 562 | {
|
| 563 | struct ui_browser *b = &browser->b;
|
| 564 | struct map_symbol *ms = b->priv;
|
| 565 | struct symbol *sym = ms->sym;
|
| 566 | char title[SYM_TITLE_MAX_SIZE];
|
| 567 |
|
| 568 | sym_title(sym, ms->map, title, sizeof(title), annotate_opts.percent_type);
|
| 569 |
|
| 570 | ui_browser__gotorc_title(b, 0, 0);
|
| 571 | ui_browser__set_color(b, HE_COLORSET_ROOT);
|
| 572 | ui_browser__write_nstring(b, title, b->width + 1);
|
| 573 | }
|
| 574 |
|
| 575 | |
| 576 | * This can be called from external jumps, i.e. jumps from one function
|
| 577 | * to another, like from the kernel's entry_SYSCALL_64 function to the
|
| 578 | * swapgs_restore_regs_and_return_to_usermode() function.
|
| 579 | *
|
| 580 | * So all we check here is that dl->ops.target.sym is set, if it is, just
|
| 581 | * go to that function and when exiting from its disassembly, come back
|
| 582 | * to the calling function.
|
| 583 |
|
| 584 | static bool annotate_browser__callq(struct annotate_browser *browser,
|
| 585 | struct evsel *evsel,
|
| 586 | struct hist_browser_timer *hbt)
|
| 587 | {
|
| 588 | struct map_symbol *ms = browser->b.priv, target_ms;
|
| 589 | struct disasm_line *dl = disasm_line(browser->selection);
|
| 590 | struct annotation *notes;
|
| 591 |
|
| 592 | if (!dl->ops.target.sym) {
|
| 593 | ui_helpline__puts("The called function was not found.");
|
| 594 | return true;
|
| 595 | }
|
| 596 |
|
| 597 | notes = symbol__annotation(dl->ops.target.sym);
|
| 598 | annotation__lock(notes);
|
| 599 |
|
| 600 | if (!symbol__hists(dl->ops.target.sym, evlist__nr_entries(evsel->evlist))) {
|
| 601 | annotation__unlock(notes);
|
| 602 | ui__warning("Not enough memory for annotating '%s' symbol!\n",
|
| 603 | dl->ops.target.sym->name);
|
| 604 | return true;
|
| 605 | }
|
| 606 |
|
| 607 | target_ms.thread = ms->thread;
|
| 608 | target_ms.map = ms->map;
|
| 609 | target_ms.sym = dl->ops.target.sym;
|
| 610 | annotation__unlock(notes);
|
| 611 | __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_ADDR);
|
| 612 |
|
| 613 | |
| 614 | * The annotate_browser above changed the title with the target function
|
| 615 | * and now it's back to the original function. Refresh the header line
|
| 616 | * for the original function again.
|
| 617 |
|
| 618 | annotate_browser__show_function_title(browser);
|
| 619 | return true;
|
| 620 | }
|
| 621 |
|
| 622 | static
|
| 623 | struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
|
| 624 | s64 offset, s64 *idx)
|
| 625 | {
|
| 626 | struct annotation *notes = browser__annotation(&browser->b);
|
| 627 | struct disasm_line *pos;
|
| 628 |
|
| 629 | *idx = 0;
|
| 630 | list_for_each_entry(pos, ¬es->src->source, al.node) {
|
| 631 | if (pos->al.offset == offset)
|
| 632 | return pos;
|
| 633 | if (!annotation_line__filter(&pos->al))
|
| 634 | ++*idx;
|
| 635 | }
|
| 636 |
|
| 637 | return NULL;
|
| 638 | }
|
| 639 |
|
| 640 | static bool annotate_browser__jump(struct annotate_browser *browser,
|
| 641 | struct evsel *evsel,
|
| 642 | struct hist_browser_timer *hbt)
|
| 643 | {
|
| 644 | struct disasm_line *dl = disasm_line(browser->selection);
|
| 645 | u64 offset;
|
| 646 | s64 idx;
|
| 647 |
|
| 648 | if (!ins__is_jump(&dl->ins))
|
| 649 | return false;
|
| 650 |
|
| 651 | if (dl->ops.target.outside) {
|
| 652 | annotate_browser__callq(browser, evsel, hbt);
|
| 653 | return true;
|
| 654 | }
|
| 655 |
|
| 656 | offset = dl->ops.target.offset;
|
| 657 | dl = annotate_browser__find_offset(browser, offset, &idx);
|
| 658 | if (dl == NULL) {
|
| 659 | ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
|
| 660 | return true;
|
| 661 | }
|
| 662 |
|
| 663 | annotate_browser__set_top(browser, &dl->al, idx);
|
| 664 |
|
| 665 | return true;
|
| 666 | }
|
| 667 |
|
| 668 | static
|
| 669 | struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
|
| 670 | char *s, s64 *idx)
|
| 671 | {
|
| 672 | struct annotation *notes = browser__annotation(&browser->b);
|
| 673 | struct annotation_line *al = browser->selection;
|
| 674 |
|
| 675 | *idx = browser->b.index;
|
| 676 | list_for_each_entry_continue(al, ¬es->src->source, node) {
|
| 677 | if (annotation_line__filter(al))
|
| 678 | continue;
|
| 679 |
|
| 680 | ++*idx;
|
| 681 |
|
| 682 | if (al->line && strstr(al->line, s) != NULL)
|
| 683 | return al;
|
| 684 | }
|
| 685 |
|
| 686 | return NULL;
|
| 687 | }
|
| 688 |
|
| 689 | static bool __annotate_browser__search(struct annotate_browser *browser)
|
| 690 | {
|
| 691 | struct annotation_line *al;
|
| 692 | s64 idx;
|
| 693 |
|
| 694 | al = annotate_browser__find_string(browser, browser->search_bf, &idx);
|
| 695 | if (al == NULL) {
|
| 696 | ui_helpline__puts("String not found!");
|
| 697 | return false;
|
| 698 | }
|
| 699 |
|
| 700 | annotate_browser__set_top(browser, al, idx);
|
| 701 | browser->searching_backwards = false;
|
| 702 | return true;
|
| 703 | }
|
| 704 |
|
| 705 | static
|
| 706 | struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
|
| 707 | char *s, s64 *idx)
|
| 708 | {
|
| 709 | struct annotation *notes = browser__annotation(&browser->b);
|
| 710 | struct annotation_line *al = browser->selection;
|
| 711 |
|
| 712 | *idx = browser->b.index;
|
| 713 | list_for_each_entry_continue_reverse(al, ¬es->src->source, node) {
|
| 714 | if (annotation_line__filter(al))
|
| 715 | continue;
|
| 716 |
|
| 717 | --*idx;
|
| 718 |
|
| 719 | if (al->line && strstr(al->line, s) != NULL)
|
| 720 | return al;
|
| 721 | }
|
| 722 |
|
| 723 | return NULL;
|
| 724 | }
|
| 725 |
|
| 726 | static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
|
| 727 | {
|
| 728 | struct annotation_line *al;
|
| 729 | s64 idx;
|
| 730 |
|
| 731 | al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
|
| 732 | if (al == NULL) {
|
| 733 | ui_helpline__puts("String not found!");
|
| 734 | return false;
|
| 735 | }
|
| 736 |
|
| 737 | annotate_browser__set_top(browser, al, idx);
|
| 738 | browser->searching_backwards = true;
|
| 739 | return true;
|
| 740 | }
|
| 741 |
|
| 742 | static bool annotate_browser__search_window(struct annotate_browser *browser,
|
| 743 | int delay_secs)
|
| 744 | {
|
| 745 | if (ui_browser__input_window("Search", "String: ", browser->search_bf,
|
| 746 | "ENTER: OK, ESC: Cancel",
|
| 747 | delay_secs * 2) != K_ENTER ||
|
| 748 | !*browser->search_bf)
|
| 749 | return false;
|
| 750 |
|
| 751 | return true;
|
| 752 | }
|
| 753 |
|
| 754 | static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
|
| 755 | {
|
| 756 | if (annotate_browser__search_window(browser, delay_secs))
|
| 757 | return __annotate_browser__search(browser);
|
| 758 |
|
| 759 | return false;
|
| 760 | }
|
| 761 |
|
| 762 | static bool annotate_browser__continue_search(struct annotate_browser *browser,
|
| 763 | int delay_secs)
|
| 764 | {
|
| 765 | if (!*browser->search_bf)
|
| 766 | return annotate_browser__search(browser, delay_secs);
|
| 767 |
|
| 768 | return __annotate_browser__search(browser);
|
| 769 | }
|
| 770 |
|
| 771 | static bool annotate_browser__search_reverse(struct annotate_browser *browser,
|
| 772 | int delay_secs)
|
| 773 | {
|
| 774 | if (annotate_browser__search_window(browser, delay_secs))
|
| 775 | return __annotate_browser__search_reverse(browser);
|
| 776 |
|
| 777 | return false;
|
| 778 | }
|
| 779 |
|
| 780 | static
|
| 781 | bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
|
| 782 | int delay_secs)
|
| 783 | {
|
| 784 | if (!*browser->search_bf)
|
| 785 | return annotate_browser__search_reverse(browser, delay_secs);
|
| 786 |
|
| 787 | return __annotate_browser__search_reverse(browser);
|
| 788 | }
|
| 789 |
|
| 790 | static int annotate_browser__show(struct annotate_browser *browser, char *title, const char *help)
|
| 791 | {
|
| 792 | if (ui_browser__show(&browser->b, title, help) < 0)
|
| 793 | return -1;
|
| 794 |
|
| 795 | annotate_browser__show_function_title(browser);
|
| 796 | return 0;
|
| 797 | }
|
| 798 |
|
| 799 | static void
|
| 800 | switch_percent_type(struct annotation_options *opts, bool base)
|
| 801 | {
|
| 802 | switch (opts->percent_type) {
|
| 803 | case PERCENT_HITS_LOCAL:
|
| 804 | if (base)
|
| 805 | opts->percent_type = PERCENT_PERIOD_LOCAL;
|
| 806 | else
|
| 807 | opts->percent_type = PERCENT_HITS_GLOBAL;
|
| 808 | break;
|
| 809 | case PERCENT_HITS_GLOBAL:
|
| 810 | if (base)
|
| 811 | opts->percent_type = PERCENT_PERIOD_GLOBAL;
|
| 812 | else
|
| 813 | opts->percent_type = PERCENT_HITS_LOCAL;
|
| 814 | break;
|
| 815 | case PERCENT_PERIOD_LOCAL:
|
| 816 | if (base)
|
| 817 | opts->percent_type = PERCENT_HITS_LOCAL;
|
| 818 | else
|
| 819 | opts->percent_type = PERCENT_PERIOD_GLOBAL;
|
| 820 | break;
|
| 821 | case PERCENT_PERIOD_GLOBAL:
|
| 822 | if (base)
|
| 823 | opts->percent_type = PERCENT_HITS_GLOBAL;
|
| 824 | else
|
| 825 | opts->percent_type = PERCENT_PERIOD_LOCAL;
|
| 826 | break;
|
| 827 | default:
|
| 828 | WARN_ON(1);
|
| 829 | }
|
| 830 | }
|
| 831 |
|
| 832 | static int annotate__scnprintf_title(struct hists *hists, char *bf, size_t size)
|
| 833 | {
|
| 834 | int printed = hists__scnprintf_title(hists, bf, size);
|
| 835 |
|
| 836 | if (!annotate_opts.hide_src_code_on_title) {
|
| 837 | printed += scnprintf(bf + printed, size - printed, " [source: %s]",
|
| 838 | annotate_opts.hide_src_code ? "OFF" : "On");
|
| 839 | }
|
| 840 |
|
| 841 | return printed;
|
| 842 | }
|
| 843 |
|
| 844 | static void annotate_browser__debuginfo_warning(struct annotate_browser *browser)
|
| 845 | {
|
| 846 | struct map_symbol *ms = browser->b.priv;
|
| 847 | struct dso *dso = map__dso(ms->map);
|
| 848 |
|
| 849 | if (browser->dbg == NULL && annotate_opts.code_with_type &&
|
| 850 | !dso__debuginfo_warned(dso)) {
|
| 851 | ui__warning("DWARF debuginfo not found.\n\n"
|
| 852 | "Data-type in this DSO will not be displayed.\n"
|
| 853 | "Please make sure to have debug information.");
|
| 854 | dso__set_debuginfo_warned(dso);
|
| 855 | }
|
| 856 | }
|
| 857 |
|
| 858 | static s64 annotate_browser__curr_hot_offset(struct annotate_browser *browser)
|
| 859 | {
|
| 860 | struct annotation_line *al = NULL;
|
| 861 |
|
| 862 | if (browser->curr_hot)
|
| 863 | al = rb_entry(browser->curr_hot, struct annotation_line, rb_node);
|
| 864 |
|
| 865 | return al ? al->offset : 0;
|
| 866 | }
|
| 867 |
|
| 868 | static void annotate_browser__symbol_annotate_error(struct annotate_browser *browser, int err)
|
| 869 | {
|
| 870 | struct map_symbol *ms = browser->b.priv;
|
| 871 | struct symbol *sym = ms->sym;
|
| 872 | struct dso *dso = map__dso(ms->map);
|
| 873 | char msg[BUFSIZ];
|
| 874 |
|
| 875 | dso__set_annotate_warned(dso);
|
| 876 | symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
|
| 877 | ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
|
| 878 | }
|
| 879 |
|
| 880 | static int annotate_browser__run(struct annotate_browser *browser,
|
| 881 | struct evsel *evsel,
|
| 882 | struct hist_browser_timer *hbt)
|
| 883 | {
|
| 884 | struct rb_node *nd = NULL;
|
| 885 | struct hists *hists = evsel__hists(evsel);
|
| 886 | struct map_symbol *ms = browser->b.priv;
|
| 887 | struct symbol *sym = ms->sym;
|
| 888 | struct annotation *notes = symbol__annotation(ms->sym);
|
| 889 | const char *help = "Press 'h' for help on key bindings";
|
| 890 | int delay_secs = hbt ? hbt->refresh : 0;
|
| 891 | char *br_cntr_text = NULL;
|
| 892 | char title[256];
|
| 893 | int key;
|
| 894 |
|
| 895 | annotate__scnprintf_title(hists, title, sizeof(title));
|
| 896 | if (annotate_browser__show(browser, title, help) < 0)
|
| 897 | return -1;
|
| 898 |
|
| 899 | annotate_browser__calc_percent(browser, evsel);
|
| 900 |
|
| 901 | if (browser->selection != NULL) {
|
| 902 | browser->curr_hot = &browser->selection->rb_node;
|
| 903 | browser->b.use_navkeypressed = false;
|
| 904 | }
|
| 905 |
|
| 906 | if (browser->curr_hot) {
|
| 907 | annotate_browser__set_rb_top(browser, browser->curr_hot);
|
| 908 | browser->b.navkeypressed = false;
|
| 909 | }
|
| 910 |
|
| 911 | nd = browser->curr_hot;
|
| 912 |
|
| 913 | annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false);
|
| 914 |
|
| 915 | annotate_browser__debuginfo_warning(browser);
|
| 916 |
|
| 917 | while (1) {
|
| 918 | key = ui_browser__run(&browser->b, delay_secs);
|
| 919 |
|
| 920 | if (delay_secs != 0) {
|
| 921 | annotate_browser__calc_percent(browser, evsel);
|
| 922 | |
| 923 | * Current line focus got out of the list of most active
|
| 924 | * lines, NULL it so that if TAB|UNTAB is pressed, we
|
| 925 | * move to curr_hot (current hottest line).
|
| 926 |
|
| 927 | if (nd != NULL && RB_EMPTY_NODE(nd))
|
| 928 | nd = NULL;
|
| 929 | }
|
| 930 |
|
| 931 | switch (key) {
|
| 932 | case K_TIMER:
|
| 933 | if (hbt)
|
| 934 | hbt->timer(hbt->arg);
|
| 935 |
|
| 936 | if (delay_secs != 0) {
|
| 937 | symbol__annotate_decay_histogram(sym, evsel);
|
| 938 | annotate__scnprintf_title(hists, title, sizeof(title));
|
| 939 | annotate_browser__show(browser, title, help);
|
| 940 | }
|
| 941 | continue;
|
| 942 | case K_TAB:
|
| 943 | if (nd != NULL) {
|
| 944 | nd = rb_prev(nd);
|
| 945 | if (nd == NULL)
|
| 946 | nd = rb_last(&browser->entries);
|
| 947 | } else
|
| 948 | nd = browser->curr_hot;
|
| 949 | break;
|
| 950 | case K_UNTAB:
|
| 951 | if (nd != NULL) {
|
| 952 | nd = rb_next(nd);
|
| 953 | if (nd == NULL)
|
| 954 | nd = rb_first(&browser->entries);
|
| 955 | } else
|
| 956 | nd = browser->curr_hot;
|
| 957 | break;
|
| 958 | case K_F1:
|
| 959 | case 'h':
|
| 960 | ui_browser__help_window(&browser->b,
|
| 961 | "UP/DOWN/PGUP\n"
|
| 962 | "PGDN/SPACE Navigate\n"
|
| 963 | "</> Move to prev/next symbol\n"
|
| 964 | "q/ESC/CTRL+C Exit\n\n"
|
| 965 | "ENTER Go to target\n"
|
| 966 | "H Go to hottest instruction\n"
|
| 967 | "TAB/shift+TAB Cycle thru hottest instructions\n"
|
| 968 | "j Toggle showing jump to target arrows\n"
|
| 969 | "J Toggle showing number of jump sources on targets\n"
|
| 970 | "n Search next string\n"
|
| 971 | "o Toggle disassembler output/simplified view\n"
|
| 972 | "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
|
| 973 | "s Toggle source code view\n"
|
| 974 | "t Circulate percent, total period, samples view\n"
|
| 975 | "c Show min/max cycle\n"
|
| 976 | "/ Search string\n"
|
| 977 | "k Toggle line numbers\n"
|
| 978 | "l Show full source file location\n"
|
| 979 | "P Print to [symbol_name].annotation file.\n"
|
| 980 | "r Run available scripts\n"
|
| 981 | "p Toggle percent type [local/global]\n"
|
| 982 | "b Toggle percent base [period/hits]\n"
|
| 983 | "B Branch counter abbr list (Optional)\n"
|
| 984 | "? Search string backwards\n"
|
| 985 | "f Toggle showing offsets to full address\n"
|
| 986 | "T Toggle data type display\n");
|
| 987 | continue;
|
| 988 | case 'r':
|
| 989 | script_browse(NULL, NULL);
|
| 990 | annotate_browser__show(browser, title, help);
|
| 991 | continue;
|
| 992 | case 'k':
|
| 993 | annotate_opts.show_linenr = !annotate_opts.show_linenr;
|
| 994 | continue;
|
| 995 | case 'l':
|
| 996 | annotate_browser__show_full_location (&browser->b);
|
| 997 | continue;
|
| 998 | case 'H':
|
| 999 | nd = browser->curr_hot;
|
| 1000 | break;
|
| 1001 | case 's': {
|
| 1002 | struct annotation_line *al = NULL;
|
| 1003 | s64 offset = annotate_browser__curr_hot_offset(browser);
|
| 1004 |
|
| 1005 | if (annotate_browser__toggle_source(browser, evsel))
|
| 1006 | ui_helpline__puts(help);
|
| 1007 |
|
| 1008 |
|
| 1009 | annotate_browser__calc_percent(browser, evsel);
|
| 1010 |
|
| 1011 | al = annotated_source__get_line(notes->src, offset);
|
| 1012 | browser->curr_hot = al ? &al->rb_node : NULL;
|
| 1013 | nd = browser->curr_hot;
|
| 1014 |
|
| 1015 | annotate__scnprintf_title(hists, title, sizeof(title));
|
| 1016 | annotate_browser__show(browser, title, help);
|
| 1017 | continue;
|
| 1018 | }
|
| 1019 | case 'o':
|
| 1020 | annotate_opts.use_offset = !annotate_opts.use_offset;
|
| 1021 | annotation__update_column_widths(notes);
|
| 1022 | continue;
|
| 1023 | case 'O':
|
| 1024 | if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
|
| 1025 | annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
|
| 1026 | continue;
|
| 1027 | case 'j':
|
| 1028 | annotate_opts.jump_arrows = !annotate_opts.jump_arrows;
|
| 1029 | continue;
|
| 1030 | case 'J':
|
| 1031 | annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps;
|
| 1032 | annotation__update_column_widths(notes);
|
| 1033 | continue;
|
| 1034 | case '/':
|
| 1035 | if (annotate_browser__search(browser, delay_secs)) {
|
| 1036 | show_help:
|
| 1037 | ui_helpline__puts(help);
|
| 1038 | }
|
| 1039 | continue;
|
| 1040 | case 'n':
|
| 1041 | if (browser->searching_backwards ?
|
| 1042 | annotate_browser__continue_search_reverse(browser, delay_secs) :
|
| 1043 | annotate_browser__continue_search(browser, delay_secs))
|
| 1044 | goto show_help;
|
| 1045 | continue;
|
| 1046 | case '?':
|
| 1047 | if (annotate_browser__search_reverse(browser, delay_secs))
|
| 1048 | goto show_help;
|
| 1049 | continue;
|
| 1050 | case 'D': {
|
| 1051 | static int seq;
|
| 1052 | ui_helpline__pop();
|
| 1053 | ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
|
| 1054 | seq++, browser->b.nr_entries,
|
| 1055 | browser->b.height,
|
| 1056 | browser->b.index,
|
| 1057 | browser->b.top_idx,
|
| 1058 | notes->src->nr_asm_entries);
|
| 1059 | }
|
| 1060 | continue;
|
| 1061 | case K_ENTER:
|
| 1062 | case K_RIGHT:
|
| 1063 | {
|
| 1064 | struct disasm_line *dl = disasm_line(browser->selection);
|
| 1065 |
|
| 1066 | if (browser->selection == NULL)
|
| 1067 | ui_helpline__puts("Huh? No selection. Report to [email protected]");
|
| 1068 | else if (browser->selection->offset == -1)
|
| 1069 | ui_helpline__puts("Actions are only available for assembly lines.");
|
| 1070 | else if (!dl->ins.ops)
|
| 1071 | goto show_sup_ins;
|
| 1072 | else if (ins__is_ret(&dl->ins))
|
| 1073 | goto out;
|
| 1074 | else if (!(annotate_browser__jump(browser, evsel, hbt) ||
|
| 1075 | annotate_browser__callq(browser, evsel, hbt))) {
|
| 1076 | show_sup_ins:
|
| 1077 | ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
|
| 1078 | }
|
| 1079 | continue;
|
| 1080 | }
|
| 1081 | case 'P':
|
| 1082 | map_symbol__annotation_dump(ms, evsel, browser->he);
|
| 1083 | continue;
|
| 1084 | case 't':
|
| 1085 | if (symbol_conf.show_total_period) {
|
| 1086 | symbol_conf.show_total_period = false;
|
| 1087 | symbol_conf.show_nr_samples = true;
|
| 1088 | } else if (symbol_conf.show_nr_samples)
|
| 1089 | symbol_conf.show_nr_samples = false;
|
| 1090 | else
|
| 1091 | symbol_conf.show_total_period = true;
|
| 1092 | annotation__update_column_widths(notes);
|
| 1093 | continue;
|
| 1094 | case 'c':
|
| 1095 | if (annotate_opts.show_minmax_cycle)
|
| 1096 | annotate_opts.show_minmax_cycle = false;
|
| 1097 | else
|
| 1098 | annotate_opts.show_minmax_cycle = true;
|
| 1099 | annotation__update_column_widths(notes);
|
| 1100 | continue;
|
| 1101 | case 'p':
|
| 1102 | case 'b':
|
| 1103 | switch_percent_type(&annotate_opts, key == 'b');
|
| 1104 | annotate__scnprintf_title(hists, title, sizeof(title));
|
| 1105 | annotate_browser__show(browser, title, help);
|
| 1106 | continue;
|
| 1107 | case 'B':
|
| 1108 | if (br_cntr_text)
|
| 1109 | ui_browser__help_window(&browser->b, br_cntr_text);
|
| 1110 | else {
|
| 1111 | ui_browser__help_window(&browser->b,
|
| 1112 | "\n The branch counter is not available.\n");
|
| 1113 | }
|
| 1114 | continue;
|
| 1115 | case 'f':
|
| 1116 | annotation__toggle_full_addr(notes, ms);
|
| 1117 | continue;
|
| 1118 | case 'T':
|
| 1119 | annotate_opts.code_with_type ^= 1;
|
| 1120 | if (browser->dbg == NULL)
|
| 1121 | browser->dbg = dso__debuginfo(map__dso(ms->map));
|
| 1122 | if (browser->type_hash == NULL) {
|
| 1123 | browser->type_hash = hashmap__new(type_hash, type_equal,
|
| 1124 | NULL);
|
| 1125 | }
|
| 1126 | annotate_browser__show(browser, title, help);
|
| 1127 | annotate_browser__debuginfo_warning(browser);
|
| 1128 | continue;
|
| 1129 | case K_LEFT:
|
| 1130 | case '<':
|
| 1131 | case '>':
|
| 1132 | case K_ESC:
|
| 1133 | case 'q':
|
| 1134 | case CTRL('c'):
|
| 1135 | goto out;
|
| 1136 | default:
|
| 1137 | ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions");
|
| 1138 | continue;
|
| 1139 | }
|
| 1140 |
|
| 1141 | if (nd != NULL)
|
| 1142 | annotate_browser__set_rb_top(browser, nd);
|
| 1143 | }
|
| 1144 | out:
|
| 1145 | ui_browser__hide(&browser->b);
|
| 1146 | free(br_cntr_text);
|
| 1147 | return key;
|
| 1148 | }
|
| 1149 |
|
| 1150 | int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
|
| 1151 | struct hist_browser_timer *hbt, u64 al_addr)
|
| 1152 | {
|
| 1153 |
|
| 1154 | SLang_reset_tty();
|
| 1155 | SLang_init_tty(0, 0, 0);
|
| 1156 | SLtty_set_suspend_state(true);
|
| 1157 |
|
| 1158 | return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, al_addr);
|
| 1159 | }
|
| 1160 |
|
| 1161 | int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
|
| 1162 | struct evsel *evsel,
|
| 1163 | struct hist_browser_timer *hbt, u64 al_addr)
|
| 1164 | {
|
| 1165 | struct symbol *sym = ms->sym;
|
| 1166 | struct annotation *notes = symbol__annotation(sym);
|
| 1167 | struct annotate_browser browser = {
|
| 1168 | .b = {
|
| 1169 | .refresh = annotate_browser__refresh,
|
| 1170 | .seek = ui_browser__list_head_seek,
|
| 1171 | .write = annotate_browser__write,
|
| 1172 | .filter = disasm_line__filter,
|
| 1173 | .extra_title_lines = 1,
|
| 1174 | .priv = ms,
|
| 1175 | .use_navkeypressed = true,
|
| 1176 | },
|
| 1177 | .he = he,
|
| 1178 | .evsel = evsel,
|
| 1179 | };
|
| 1180 | struct dso *dso;
|
| 1181 | int ret = -1, err;
|
| 1182 | int not_annotated = list_empty(¬es->src->source);
|
| 1183 |
|
| 1184 | if (sym == NULL)
|
| 1185 | return -1;
|
| 1186 |
|
| 1187 | dso = map__dso(ms->map);
|
| 1188 | if (dso__annotate_warned(dso))
|
| 1189 | return -1;
|
| 1190 |
|
| 1191 | if (not_annotated || !symbol__is_annotate2(sym)) {
|
| 1192 | err = symbol__annotate2(ms, evsel, &browser.arch);
|
| 1193 | if (err) {
|
| 1194 | annotate_browser__symbol_annotate_error(&browser, err);
|
| 1195 | return -1;
|
| 1196 | }
|
| 1197 |
|
| 1198 | if (!annotate_opts.hide_src_code) {
|
| 1199 | notes->src->tried_source = true;
|
| 1200 | if (!annotation__has_source(notes))
|
| 1201 | ui__warning("Annotation has no source code.");
|
| 1202 | }
|
| 1203 | } else {
|
| 1204 | err = map_symbol__get_arch(ms, &browser.arch);
|
| 1205 | if (err) {
|
| 1206 | annotate_browser__symbol_annotate_error(&browser, err);
|
| 1207 | return -1;
|
| 1208 | }
|
| 1209 | }
|
| 1210 |
|
| 1211 |
|
| 1212 | if (hbt != NULL && he != &annotate_he) {
|
| 1213 | annotate_he.hists = he->hists;
|
| 1214 | annotate_he.thread = thread__get(he->thread);
|
| 1215 | annotate_he.cpumode = he->cpumode;
|
| 1216 | map_symbol__copy(&annotate_he.ms, ms);
|
| 1217 |
|
| 1218 | browser.he = &annotate_he;
|
| 1219 | }
|
| 1220 |
|
| 1221 | ui_helpline__push("Press ESC to exit");
|
| 1222 |
|
| 1223 | if (annotate_opts.code_with_type) {
|
| 1224 | browser.dbg = dso__debuginfo(dso);
|
| 1225 | browser.type_hash = hashmap__new(type_hash, type_equal, NULL);
|
| 1226 | }
|
| 1227 |
|
| 1228 | browser.b.width = notes->src->widths.max_line_len;
|
| 1229 | browser.b.nr_entries = notes->src->nr_entries;
|
| 1230 | browser.b.entries = ¬es->src->source;
|
| 1231 | browser.b.width += 18;
|
| 1232 |
|
| 1233 | if (annotate_opts.hide_src_code)
|
| 1234 | ui_browser__init_asm_mode(&browser.b);
|
| 1235 |
|
| 1236 | |
| 1237 | * If al_addr is set, it means that there should be a line
|
| 1238 | * intentionally selected, not based on the percentages
|
| 1239 | * which caculated by the event sampling. In this case, we
|
| 1240 | * convey this information into the browser selection, where
|
| 1241 | * the selection in other cases should be empty.
|
| 1242 |
|
| 1243 | if (al_addr != NO_ADDR) {
|
| 1244 | struct annotation_line *al = annotated_source__get_line(notes->src,
|
| 1245 | al_addr - sym->start);
|
| 1246 |
|
| 1247 | browser.selection = al;
|
| 1248 | }
|
| 1249 |
|
| 1250 | ret = annotate_browser__run(&browser, evsel, hbt);
|
| 1251 |
|
| 1252 | debuginfo__delete(browser.dbg);
|
| 1253 |
|
| 1254 | if (!IS_ERR_OR_NULL(browser.type_hash)) {
|
| 1255 | struct hashmap_entry *cur;
|
| 1256 | size_t bkt;
|
| 1257 |
|
| 1258 | hashmap__for_each_entry(browser.type_hash, cur, bkt)
|
| 1259 | zfree(&cur->pvalue);
|
| 1260 | hashmap__free(browser.type_hash);
|
| 1261 | }
|
| 1262 |
|
| 1263 | if (not_annotated && !notes->src->tried_source)
|
| 1264 | annotated_source__purge(notes->src);
|
| 1265 |
|
| 1266 | if (hbt != NULL && he != &annotate_he) {
|
| 1267 | thread__zput(annotate_he.thread);
|
| 1268 | map_symbol__exit(&annotate_he.ms);
|
| 1269 | }
|
| 1270 |
|
| 1271 | return ret;
|
| 1272 | }
|