linux/tools/perf/ui/browsers/annotate.c
<<
>>
Prefs
   1#include "../../util/util.h"
   2#include "../browser.h"
   3#include "../helpline.h"
   4#include "../libslang.h"
   5#include "../ui.h"
   6#include "../util.h"
   7#include "../../util/annotate.h"
   8#include "../../util/hist.h"
   9#include "../../util/sort.h"
  10#include "../../util/symbol.h"
  11#include "../../util/evsel.h"
  12#include <pthread.h>
  13
  14struct browser_disasm_line {
  15        struct rb_node  rb_node;
  16        u32             idx;
  17        int             idx_asm;
  18        int             jump_sources;
  19        /*
  20         * actual length of this array is saved on the nr_events field
  21         * of the struct annotate_browser
  22         */
  23        double          percent[1];
  24};
  25
  26static struct annotate_browser_opt {
  27        bool hide_src_code,
  28             use_offset,
  29             jump_arrows,
  30             show_linenr,
  31             show_nr_jumps;
  32} annotate_browser__opts = {
  33        .use_offset     = true,
  34        .jump_arrows    = true,
  35};
  36
  37struct annotate_browser {
  38        struct ui_browser b;
  39        struct rb_root    entries;
  40        struct rb_node    *curr_hot;
  41        struct disasm_line  *selection;
  42        struct disasm_line  **offsets;
  43        int                 nr_events;
  44        u64                 start;
  45        int                 nr_asm_entries;
  46        int                 nr_entries;
  47        int                 max_jump_sources;
  48        int                 nr_jumps;
  49        bool                searching_backwards;
  50        u8                  addr_width;
  51        u8                  jumps_width;
  52        u8                  target_width;
  53        u8                  min_addr_width;
  54        u8                  max_addr_width;
  55        char                search_bf[128];
  56};
  57
  58static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
  59{
  60        return (struct browser_disasm_line *)(dl + 1);
  61}
  62
  63static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
  64                                void *entry)
  65{
  66        if (annotate_browser__opts.hide_src_code) {
  67                struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
  68                return dl->offset == -1;
  69        }
  70
  71        return false;
  72}
  73
  74static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
  75                                                 int nr, bool current)
  76{
  77        if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
  78                return HE_COLORSET_SELECTED;
  79        if (nr == browser->max_jump_sources)
  80                return HE_COLORSET_TOP;
  81        if (nr > 1)
  82                return HE_COLORSET_MEDIUM;
  83        return HE_COLORSET_NORMAL;
  84}
  85
  86static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
  87                                                     int nr, bool current)
  88{
  89         int color = annotate_browser__jumps_percent_color(browser, nr, current);
  90         return ui_browser__set_color(&browser->b, color);
  91}
  92
  93static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
  94{
  95        struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
  96        struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
  97        struct browser_disasm_line *bdl = disasm_line__browser(dl);
  98        bool current_entry = ui_browser__is_current_entry(browser, row);
  99        bool change_color = (!annotate_browser__opts.hide_src_code &&
 100                             (!current_entry || (browser->use_navkeypressed &&
 101                                                 !browser->navkeypressed)));
 102        int width = browser->width, printed;
 103        int i, pcnt_width = 7 * ab->nr_events;
 104        double percent_max = 0.0;
 105        char bf[256];
 106
 107        for (i = 0; i < ab->nr_events; i++) {
 108                if (bdl->percent[i] > percent_max)
 109                        percent_max = bdl->percent[i];
 110        }
 111
 112        if (dl->offset != -1 && percent_max != 0.0) {
 113                for (i = 0; i < ab->nr_events; i++) {
 114                        ui_browser__set_percent_color(browser, bdl->percent[i],
 115                                                      current_entry);
 116                        slsmg_printf("%6.2f ", bdl->percent[i]);
 117                }
 118        } else {
 119                ui_browser__set_percent_color(browser, 0, current_entry);
 120                slsmg_write_nstring(" ", pcnt_width);
 121        }
 122
 123        SLsmg_write_char(' ');
 124
 125        /* The scroll bar isn't being used */
 126        if (!browser->navkeypressed)
 127                width += 1;
 128
 129        if (!*dl->line)
 130                slsmg_write_nstring(" ", width - pcnt_width);
 131        else if (dl->offset == -1) {
 132                if (dl->line_nr && annotate_browser__opts.show_linenr)
 133                        printed = scnprintf(bf, sizeof(bf), "%-*d ",
 134                                        ab->addr_width + 1, dl->line_nr);
 135                else
 136                        printed = scnprintf(bf, sizeof(bf), "%*s  ",
 137                                    ab->addr_width, " ");
 138                slsmg_write_nstring(bf, printed);
 139                slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1);
 140        } else {
 141                u64 addr = dl->offset;
 142                int color = -1;
 143
 144                if (!annotate_browser__opts.use_offset)
 145                        addr += ab->start;
 146
 147                if (!annotate_browser__opts.use_offset) {
 148                        printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
 149                } else {
 150                        if (bdl->jump_sources) {
 151                                if (annotate_browser__opts.show_nr_jumps) {
 152                                        int prev;
 153                                        printed = scnprintf(bf, sizeof(bf), "%*d ",
 154                                                            ab->jumps_width,
 155                                                            bdl->jump_sources);
 156                                        prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
 157                                                                                         current_entry);
 158                                        slsmg_write_nstring(bf, printed);
 159                                        ui_browser__set_color(browser, prev);
 160                                }
 161
 162                                printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
 163                                                    ab->target_width, addr);
 164                        } else {
 165                                printed = scnprintf(bf, sizeof(bf), "%*s  ",
 166                                                    ab->addr_width, " ");
 167                        }
 168                }
 169
 170                if (change_color)
 171                        color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
 172                slsmg_write_nstring(bf, printed);
 173                if (change_color)
 174                        ui_browser__set_color(browser, color);
 175                if (dl->ins && dl->ins->ops->scnprintf) {
 176                        if (ins__is_jump(dl->ins)) {
 177                                bool fwd = dl->ops.target.offset > (u64)dl->offset;
 178
 179                                ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
 180                                                                    SLSMG_UARROW_CHAR);
 181                                SLsmg_write_char(' ');
 182                        } else if (ins__is_call(dl->ins)) {
 183                                ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
 184                                SLsmg_write_char(' ');
 185                        } else {
 186                                slsmg_write_nstring(" ", 2);
 187                        }
 188                } else {
 189                        if (strcmp(dl->name, "retq")) {
 190                                slsmg_write_nstring(" ", 2);
 191                        } else {
 192                                ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
 193                                SLsmg_write_char(' ');
 194                        }
 195                }
 196
 197                disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
 198                slsmg_write_nstring(bf, width - pcnt_width - 3 - printed);
 199        }
 200
 201        if (current_entry)
 202                ab->selection = dl;
 203}
 204
 205static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
 206{
 207        if (!dl || !dl->ins || !ins__is_jump(dl->ins)
 208            || !disasm_line__has_offset(dl)
 209            || dl->ops.target.offset >= symbol__size(sym))
 210                return false;
 211
 212        return true;
 213}
 214
 215static void annotate_browser__draw_current_jump(struct ui_browser *browser)
 216{
 217        struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
 218        struct disasm_line *cursor = ab->selection, *target;
 219        struct browser_disasm_line *btarget, *bcursor;
 220        unsigned int from, to;
 221        struct map_symbol *ms = ab->b.priv;
 222        struct symbol *sym = ms->sym;
 223        u8 pcnt_width = 7;
 224
 225        /* PLT symbols contain external offsets */
 226        if (strstr(sym->name, "@plt"))
 227                return;
 228
 229        if (!disasm_line__is_valid_jump(cursor, sym))
 230                return;
 231
 232        target = ab->offsets[cursor->ops.target.offset];
 233        if (!target)
 234                return;
 235
 236        bcursor = disasm_line__browser(cursor);
 237        btarget = disasm_line__browser(target);
 238
 239        if (annotate_browser__opts.hide_src_code) {
 240                from = bcursor->idx_asm;
 241                to = btarget->idx_asm;
 242        } else {
 243                from = (u64)bcursor->idx;
 244                to = (u64)btarget->idx;
 245        }
 246
 247        pcnt_width *= ab->nr_events;
 248
 249        ui_browser__set_color(browser, HE_COLORSET_CODE);
 250        __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
 251                                 from, to);
 252}
 253
 254static unsigned int annotate_browser__refresh(struct ui_browser *browser)
 255{
 256        struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
 257        int ret = ui_browser__list_head_refresh(browser);
 258        int pcnt_width;
 259
 260        pcnt_width = 7 * ab->nr_events;
 261
 262        if (annotate_browser__opts.jump_arrows)
 263                annotate_browser__draw_current_jump(browser);
 264
 265        ui_browser__set_color(browser, HE_COLORSET_NORMAL);
 266        __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
 267        return ret;
 268}
 269
 270static int disasm__cmp(struct browser_disasm_line *a,
 271                       struct browser_disasm_line *b, int nr_pcnt)
 272{
 273        int i;
 274
 275        for (i = 0; i < nr_pcnt; i++) {
 276                if (a->percent[i] == b->percent[i])
 277                        continue;
 278                return a->percent[i] < b->percent[i];
 279        }
 280        return 0;
 281}
 282
 283static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
 284                                   int nr_events)
 285{
 286        struct rb_node **p = &root->rb_node;
 287        struct rb_node *parent = NULL;
 288        struct browser_disasm_line *l;
 289
 290        while (*p != NULL) {
 291                parent = *p;
 292                l = rb_entry(parent, struct browser_disasm_line, rb_node);
 293
 294                if (disasm__cmp(bdl, l, nr_events))
 295                        p = &(*p)->rb_left;
 296                else
 297                        p = &(*p)->rb_right;
 298        }
 299        rb_link_node(&bdl->rb_node, parent, p);
 300        rb_insert_color(&bdl->rb_node, root);
 301}
 302
 303static void annotate_browser__set_top(struct annotate_browser *browser,
 304                                      struct disasm_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 disasm_line, node);
 314
 315                if (disasm_line__filter(&browser->b, &pos->node))
 316                        continue;
 317
 318                --browser->b.top_idx;
 319                --back;
 320        }
 321
 322        browser->b.top = pos;
 323        browser->b.navkeypressed = true;
 324}
 325
 326static void annotate_browser__set_rb_top(struct annotate_browser *browser,
 327                                         struct rb_node *nd)
 328{
 329        struct browser_disasm_line *bpos;
 330        struct disasm_line *pos;
 331        u32 idx;
 332
 333        bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
 334        pos = ((struct disasm_line *)bpos) - 1;
 335        idx = bpos->idx;
 336        if (annotate_browser__opts.hide_src_code)
 337                idx = bpos->idx_asm;
 338        annotate_browser__set_top(browser, pos, idx);
 339        browser->curr_hot = nd;
 340}
 341
 342static void annotate_browser__calc_percent(struct annotate_browser *browser,
 343                                           struct perf_evsel *evsel)
 344{
 345        struct map_symbol *ms = browser->b.priv;
 346        struct symbol *sym = ms->sym;
 347        struct annotation *notes = symbol__annotation(sym);
 348        struct disasm_line *pos, *next;
 349        s64 len = symbol__size(sym);
 350
 351        browser->entries = RB_ROOT;
 352
 353        pthread_mutex_lock(&notes->lock);
 354
 355        list_for_each_entry(pos, &notes->src->source, node) {
 356                struct browser_disasm_line *bpos = disasm_line__browser(pos);
 357                const char *path = NULL;
 358                double max_percent = 0.0;
 359                int i;
 360
 361                if (pos->offset == -1) {
 362                        RB_CLEAR_NODE(&bpos->rb_node);
 363                        continue;
 364                }
 365
 366                next = disasm__get_next_ip_line(&notes->src->source, pos);
 367
 368                for (i = 0; i < browser->nr_events; i++) {
 369                        bpos->percent[i] = disasm__calc_percent(notes,
 370                                                evsel->idx + i,
 371                                                pos->offset,
 372                                                next ? next->offset : len,
 373                                                &path);
 374
 375                        if (max_percent < bpos->percent[i])
 376                                max_percent = bpos->percent[i];
 377                }
 378
 379                if (max_percent < 0.01) {
 380                        RB_CLEAR_NODE(&bpos->rb_node);
 381                        continue;
 382                }
 383                disasm_rb_tree__insert(&browser->entries, bpos,
 384                                       browser->nr_events);
 385        }
 386        pthread_mutex_unlock(&notes->lock);
 387
 388        browser->curr_hot = rb_last(&browser->entries);
 389}
 390
 391static bool annotate_browser__toggle_source(struct annotate_browser *browser)
 392{
 393        struct disasm_line *dl;
 394        struct browser_disasm_line *bdl;
 395        off_t offset = browser->b.index - browser->b.top_idx;
 396
 397        browser->b.seek(&browser->b, offset, SEEK_CUR);
 398        dl = list_entry(browser->b.top, struct disasm_line, node);
 399        bdl = disasm_line__browser(dl);
 400
 401        if (annotate_browser__opts.hide_src_code) {
 402                if (bdl->idx_asm < offset)
 403                        offset = bdl->idx;
 404
 405                browser->b.nr_entries = browser->nr_entries;
 406                annotate_browser__opts.hide_src_code = false;
 407                browser->b.seek(&browser->b, -offset, SEEK_CUR);
 408                browser->b.top_idx = bdl->idx - offset;
 409                browser->b.index = bdl->idx;
 410        } else {
 411                if (bdl->idx_asm < 0) {
 412                        ui_helpline__puts("Only available for assembly lines.");
 413                        browser->b.seek(&browser->b, -offset, SEEK_CUR);
 414                        return false;
 415                }
 416
 417                if (bdl->idx_asm < offset)
 418                        offset = bdl->idx_asm;
 419
 420                browser->b.nr_entries = browser->nr_asm_entries;
 421                annotate_browser__opts.hide_src_code = true;
 422                browser->b.seek(&browser->b, -offset, SEEK_CUR);
 423                browser->b.top_idx = bdl->idx_asm - offset;
 424                browser->b.index = bdl->idx_asm;
 425        }
 426
 427        return true;
 428}
 429
 430static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
 431{
 432        ui_browser__reset_index(&browser->b);
 433        browser->b.nr_entries = browser->nr_asm_entries;
 434}
 435
 436#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
 437
 438static int sym_title(struct symbol *sym, struct map *map, char *title,
 439                     size_t sz)
 440{
 441        return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
 442}
 443
 444static bool annotate_browser__callq(struct annotate_browser *browser,
 445                                    struct perf_evsel *evsel,
 446                                    struct hist_browser_timer *hbt)
 447{
 448        struct map_symbol *ms = browser->b.priv;
 449        struct disasm_line *dl = browser->selection;
 450        struct annotation *notes;
 451        struct addr_map_symbol target = {
 452                .map = ms->map,
 453                .addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
 454        };
 455        char title[SYM_TITLE_MAX_SIZE];
 456
 457        if (!ins__is_call(dl->ins))
 458                return false;
 459
 460        if (map_groups__find_ams(&target, NULL) ||
 461            map__rip_2objdump(target.map, target.map->map_ip(target.map,
 462                                                             target.addr)) !=
 463            dl->ops.target.addr) {
 464                ui_helpline__puts("The called function was not found.");
 465                return true;
 466        }
 467
 468        notes = symbol__annotation(target.sym);
 469        pthread_mutex_lock(&notes->lock);
 470
 471        if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
 472                pthread_mutex_unlock(&notes->lock);
 473                ui__warning("Not enough memory for annotating '%s' symbol!\n",
 474                            target.sym->name);
 475                return true;
 476        }
 477
 478        pthread_mutex_unlock(&notes->lock);
 479        symbol__tui_annotate(target.sym, target.map, evsel, hbt);
 480        sym_title(ms->sym, ms->map, title, sizeof(title));
 481        ui_browser__show_title(&browser->b, title);
 482        return true;
 483}
 484
 485static
 486struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
 487                                          s64 offset, s64 *idx)
 488{
 489        struct map_symbol *ms = browser->b.priv;
 490        struct symbol *sym = ms->sym;
 491        struct annotation *notes = symbol__annotation(sym);
 492        struct disasm_line *pos;
 493
 494        *idx = 0;
 495        list_for_each_entry(pos, &notes->src->source, node) {
 496                if (pos->offset == offset)
 497                        return pos;
 498                if (!disasm_line__filter(&browser->b, &pos->node))
 499                        ++*idx;
 500        }
 501
 502        return NULL;
 503}
 504
 505static bool annotate_browser__jump(struct annotate_browser *browser)
 506{
 507        struct disasm_line *dl = browser->selection;
 508        s64 idx;
 509
 510        if (!ins__is_jump(dl->ins))
 511                return false;
 512
 513        dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
 514        if (dl == NULL) {
 515                ui_helpline__puts("Invalid jump offset");
 516                return true;
 517        }
 518
 519        annotate_browser__set_top(browser, dl, idx);
 520
 521        return true;
 522}
 523
 524static
 525struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
 526                                          char *s, s64 *idx)
 527{
 528        struct map_symbol *ms = browser->b.priv;
 529        struct symbol *sym = ms->sym;
 530        struct annotation *notes = symbol__annotation(sym);
 531        struct disasm_line *pos = browser->selection;
 532
 533        *idx = browser->b.index;
 534        list_for_each_entry_continue(pos, &notes->src->source, node) {
 535                if (disasm_line__filter(&browser->b, &pos->node))
 536                        continue;
 537
 538                ++*idx;
 539
 540                if (pos->line && strstr(pos->line, s) != NULL)
 541                        return pos;
 542        }
 543
 544        return NULL;
 545}
 546
 547static bool __annotate_browser__search(struct annotate_browser *browser)
 548{
 549        struct disasm_line *dl;
 550        s64 idx;
 551
 552        dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
 553        if (dl == NULL) {
 554                ui_helpline__puts("String not found!");
 555                return false;
 556        }
 557
 558        annotate_browser__set_top(browser, dl, idx);
 559        browser->searching_backwards = false;
 560        return true;
 561}
 562
 563static
 564struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
 565                                                  char *s, s64 *idx)
 566{
 567        struct map_symbol *ms = browser->b.priv;
 568        struct symbol *sym = ms->sym;
 569        struct annotation *notes = symbol__annotation(sym);
 570        struct disasm_line *pos = browser->selection;
 571
 572        *idx = browser->b.index;
 573        list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
 574                if (disasm_line__filter(&browser->b, &pos->node))
 575                        continue;
 576
 577                --*idx;
 578
 579                if (pos->line && strstr(pos->line, s) != NULL)
 580                        return pos;
 581        }
 582
 583        return NULL;
 584}
 585
 586static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
 587{
 588        struct disasm_line *dl;
 589        s64 idx;
 590
 591        dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
 592        if (dl == NULL) {
 593                ui_helpline__puts("String not found!");
 594                return false;
 595        }
 596
 597        annotate_browser__set_top(browser, dl, idx);
 598        browser->searching_backwards = true;
 599        return true;
 600}
 601
 602static bool annotate_browser__search_window(struct annotate_browser *browser,
 603                                            int delay_secs)
 604{
 605        if (ui_browser__input_window("Search", "String: ", browser->search_bf,
 606                                     "ENTER: OK, ESC: Cancel",
 607                                     delay_secs * 2) != K_ENTER ||
 608            !*browser->search_bf)
 609                return false;
 610
 611        return true;
 612}
 613
 614static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
 615{
 616        if (annotate_browser__search_window(browser, delay_secs))
 617                return __annotate_browser__search(browser);
 618
 619        return false;
 620}
 621
 622static bool annotate_browser__continue_search(struct annotate_browser *browser,
 623                                              int delay_secs)
 624{
 625        if (!*browser->search_bf)
 626                return annotate_browser__search(browser, delay_secs);
 627
 628        return __annotate_browser__search(browser);
 629}
 630
 631static bool annotate_browser__search_reverse(struct annotate_browser *browser,
 632                                           int delay_secs)
 633{
 634        if (annotate_browser__search_window(browser, delay_secs))
 635                return __annotate_browser__search_reverse(browser);
 636
 637        return false;
 638}
 639
 640static
 641bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
 642                                               int delay_secs)
 643{
 644        if (!*browser->search_bf)
 645                return annotate_browser__search_reverse(browser, delay_secs);
 646
 647        return __annotate_browser__search_reverse(browser);
 648}
 649
 650static void annotate_browser__update_addr_width(struct annotate_browser *browser)
 651{
 652        if (annotate_browser__opts.use_offset)
 653                browser->target_width = browser->min_addr_width;
 654        else
 655                browser->target_width = browser->max_addr_width;
 656
 657        browser->addr_width = browser->target_width;
 658
 659        if (annotate_browser__opts.show_nr_jumps)
 660                browser->addr_width += browser->jumps_width + 1;
 661}
 662
 663static int annotate_browser__run(struct annotate_browser *browser,
 664                                 struct perf_evsel *evsel,
 665                                 struct hist_browser_timer *hbt)
 666{
 667        struct rb_node *nd = NULL;
 668        struct map_symbol *ms = browser->b.priv;
 669        struct symbol *sym = ms->sym;
 670        const char *help = "Press 'h' for help on key bindings";
 671        int delay_secs = hbt ? hbt->refresh : 0;
 672        int key;
 673        char title[SYM_TITLE_MAX_SIZE];
 674
 675        sym_title(sym, ms->map, title, sizeof(title));
 676        if (ui_browser__show(&browser->b, title, help) < 0)
 677                return -1;
 678
 679        annotate_browser__calc_percent(browser, evsel);
 680
 681        if (browser->curr_hot) {
 682                annotate_browser__set_rb_top(browser, browser->curr_hot);
 683                browser->b.navkeypressed = false;
 684        }
 685
 686        nd = browser->curr_hot;
 687
 688        while (1) {
 689                key = ui_browser__run(&browser->b, delay_secs);
 690
 691                if (delay_secs != 0) {
 692                        annotate_browser__calc_percent(browser, evsel);
 693                        /*
 694                         * Current line focus got out of the list of most active
 695                         * lines, NULL it so that if TAB|UNTAB is pressed, we
 696                         * move to curr_hot (current hottest line).
 697                         */
 698                        if (nd != NULL && RB_EMPTY_NODE(nd))
 699                                nd = NULL;
 700                }
 701
 702                switch (key) {
 703                case K_TIMER:
 704                        if (hbt)
 705                                hbt->timer(hbt->arg);
 706
 707                        if (delay_secs != 0)
 708                                symbol__annotate_decay_histogram(sym, evsel->idx);
 709                        continue;
 710                case K_TAB:
 711                        if (nd != NULL) {
 712                                nd = rb_prev(nd);
 713                                if (nd == NULL)
 714                                        nd = rb_last(&browser->entries);
 715                        } else
 716                                nd = browser->curr_hot;
 717                        break;
 718                case K_UNTAB:
 719                        if (nd != NULL)
 720                                nd = rb_next(nd);
 721                                if (nd == NULL)
 722                                        nd = rb_first(&browser->entries);
 723                        else
 724                                nd = browser->curr_hot;
 725                        break;
 726                case K_F1:
 727                case 'h':
 728                        ui_browser__help_window(&browser->b,
 729                "UP/DOWN/PGUP\n"
 730                "PGDN/SPACE    Navigate\n"
 731                "q/ESC/CTRL+C  Exit\n\n"
 732                "->            Go to target\n"
 733                "<-            Exit\n"
 734                "H             Cycle thru hottest instructions\n"
 735                "j             Toggle showing jump to target arrows\n"
 736                "J             Toggle showing number of jump sources on targets\n"
 737                "n             Search next string\n"
 738                "o             Toggle disassembler output/simplified view\n"
 739                "s             Toggle source code view\n"
 740                "/             Search string\n"
 741                "k             Toggle line numbers\n"
 742                "r             Run available scripts\n"
 743                "?             Search string backwards\n");
 744                        continue;
 745                case 'r':
 746                        {
 747                                script_browse(NULL);
 748                                continue;
 749                        }
 750                case 'k':
 751                        annotate_browser__opts.show_linenr =
 752                                !annotate_browser__opts.show_linenr;
 753                        break;
 754                case 'H':
 755                        nd = browser->curr_hot;
 756                        break;
 757                case 's':
 758                        if (annotate_browser__toggle_source(browser))
 759                                ui_helpline__puts(help);
 760                        continue;
 761                case 'o':
 762                        annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
 763                        annotate_browser__update_addr_width(browser);
 764                        continue;
 765                case 'j':
 766                        annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
 767                        continue;
 768                case 'J':
 769                        annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
 770                        annotate_browser__update_addr_width(browser);
 771                        continue;
 772                case '/':
 773                        if (annotate_browser__search(browser, delay_secs)) {
 774show_help:
 775                                ui_helpline__puts(help);
 776                        }
 777                        continue;
 778                case 'n':
 779                        if (browser->searching_backwards ?
 780                            annotate_browser__continue_search_reverse(browser, delay_secs) :
 781                            annotate_browser__continue_search(browser, delay_secs))
 782                                goto show_help;
 783                        continue;
 784                case '?':
 785                        if (annotate_browser__search_reverse(browser, delay_secs))
 786                                goto show_help;
 787                        continue;
 788                case 'D': {
 789                        static int seq;
 790                        ui_helpline__pop();
 791                        ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
 792                                           seq++, browser->b.nr_entries,
 793                                           browser->b.height,
 794                                           browser->b.index,
 795                                           browser->b.top_idx,
 796                                           browser->nr_asm_entries);
 797                }
 798                        continue;
 799                case K_ENTER:
 800                case K_RIGHT:
 801                        if (browser->selection == NULL)
 802                                ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
 803                        else if (browser->selection->offset == -1)
 804                                ui_helpline__puts("Actions are only available for assembly lines.");
 805                        else if (!browser->selection->ins) {
 806                                if (strcmp(browser->selection->name, "retq"))
 807                                        goto show_sup_ins;
 808                                goto out;
 809                        } else if (!(annotate_browser__jump(browser) ||
 810                                     annotate_browser__callq(browser, evsel, hbt))) {
 811show_sup_ins:
 812                                ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
 813                        }
 814                        continue;
 815                case K_LEFT:
 816                case K_ESC:
 817                case 'q':
 818                case CTRL('c'):
 819                        goto out;
 820                default:
 821                        continue;
 822                }
 823
 824                if (nd != NULL)
 825                        annotate_browser__set_rb_top(browser, nd);
 826        }
 827out:
 828        ui_browser__hide(&browser->b);
 829        return key;
 830}
 831
 832int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
 833                             struct hist_browser_timer *hbt)
 834{
 835        return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
 836}
 837
 838int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
 839                             struct hist_browser_timer *hbt)
 840{
 841        return map_symbol__tui_annotate(&he->ms, evsel, hbt);
 842}
 843
 844static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
 845                                                size_t size)
 846{
 847        u64 offset;
 848        struct map_symbol *ms = browser->b.priv;
 849        struct symbol *sym = ms->sym;
 850
 851        /* PLT symbols contain external offsets */
 852        if (strstr(sym->name, "@plt"))
 853                return;
 854
 855        for (offset = 0; offset < size; ++offset) {
 856                struct disasm_line *dl = browser->offsets[offset], *dlt;
 857                struct browser_disasm_line *bdlt;
 858
 859                if (!disasm_line__is_valid_jump(dl, sym))
 860                        continue;
 861
 862                dlt = browser->offsets[dl->ops.target.offset];
 863                /*
 864                 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 865                 * have to adjust to the previous offset?
 866                 */
 867                if (dlt == NULL)
 868                        continue;
 869
 870                bdlt = disasm_line__browser(dlt);
 871                if (++bdlt->jump_sources > browser->max_jump_sources)
 872                        browser->max_jump_sources = bdlt->jump_sources;
 873
 874                ++browser->nr_jumps;
 875        }
 876}
 877
 878static inline int width_jumps(int n)
 879{
 880        if (n >= 100)
 881                return 5;
 882        if (n / 10)
 883                return 2;
 884        return 1;
 885}
 886
 887int symbol__tui_annotate(struct symbol *sym, struct map *map,
 888                         struct perf_evsel *evsel,
 889                         struct hist_browser_timer *hbt)
 890{
 891        struct disasm_line *pos, *n;
 892        struct annotation *notes;
 893        size_t size;
 894        struct map_symbol ms = {
 895                .map = map,
 896                .sym = sym,
 897        };
 898        struct annotate_browser browser = {
 899                .b = {
 900                        .refresh = annotate_browser__refresh,
 901                        .seek    = ui_browser__list_head_seek,
 902                        .write   = annotate_browser__write,
 903                        .filter  = disasm_line__filter,
 904                        .priv    = &ms,
 905                        .use_navkeypressed = true,
 906                },
 907        };
 908        int ret = -1;
 909        int nr_pcnt = 1;
 910        size_t sizeof_bdl = sizeof(struct browser_disasm_line);
 911
 912        if (sym == NULL)
 913                return -1;
 914
 915        size = symbol__size(sym);
 916
 917        if (map->dso->annotate_warned)
 918                return -1;
 919
 920        browser.offsets = zalloc(size * sizeof(struct disasm_line *));
 921        if (browser.offsets == NULL) {
 922                ui__error("Not enough memory!");
 923                return -1;
 924        }
 925
 926        if (perf_evsel__is_group_event(evsel)) {
 927                nr_pcnt = evsel->nr_members;
 928                sizeof_bdl += sizeof(double) * (nr_pcnt - 1);
 929        }
 930
 931        if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
 932                ui__error("%s", ui_helpline__last_msg);
 933                goto out_free_offsets;
 934        }
 935
 936        ui_helpline__push("Press <- or ESC to exit");
 937
 938        notes = symbol__annotation(sym);
 939        browser.start = map__rip_2objdump(map, sym->start);
 940
 941        list_for_each_entry(pos, &notes->src->source, node) {
 942                struct browser_disasm_line *bpos;
 943                size_t line_len = strlen(pos->line);
 944
 945                if (browser.b.width < line_len)
 946                        browser.b.width = line_len;
 947                bpos = disasm_line__browser(pos);
 948                bpos->idx = browser.nr_entries++;
 949                if (pos->offset != -1) {
 950                        bpos->idx_asm = browser.nr_asm_entries++;
 951                        /*
 952                         * FIXME: short term bandaid to cope with assembly
 953                         * routines that comes with labels in the same column
 954                         * as the address in objdump, sigh.
 955                         *
 956                         * E.g. copy_user_generic_unrolled
 957                         */
 958                        if (pos->offset < (s64)size)
 959                                browser.offsets[pos->offset] = pos;
 960                } else
 961                        bpos->idx_asm = -1;
 962        }
 963
 964        annotate_browser__mark_jump_targets(&browser, size);
 965
 966        browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
 967        browser.max_addr_width = hex_width(sym->end);
 968        browser.jumps_width = width_jumps(browser.max_jump_sources);
 969        browser.nr_events = nr_pcnt;
 970        browser.b.nr_entries = browser.nr_entries;
 971        browser.b.entries = &notes->src->source,
 972        browser.b.width += 18; /* Percentage */
 973
 974        if (annotate_browser__opts.hide_src_code)
 975                annotate_browser__init_asm_mode(&browser);
 976
 977        annotate_browser__update_addr_width(&browser);
 978
 979        ret = annotate_browser__run(&browser, evsel, hbt);
 980        list_for_each_entry_safe(pos, n, &notes->src->source, node) {
 981                list_del(&pos->node);
 982                disasm_line__free(pos);
 983        }
 984
 985out_free_offsets:
 986        free(browser.offsets);
 987        return ret;
 988}
 989
 990#define ANNOTATE_CFG(n) \
 991        { .name = #n, .value = &annotate_browser__opts.n, }
 992
 993/*
 994 * Keep the entries sorted, they are bsearch'ed
 995 */
 996static struct annotate_config {
 997        const char *name;
 998        bool *value;
 999} annotate__configs[] = {
1000        ANNOTATE_CFG(hide_src_code),
1001        ANNOTATE_CFG(jump_arrows),
1002        ANNOTATE_CFG(show_linenr),
1003        ANNOTATE_CFG(show_nr_jumps),
1004        ANNOTATE_CFG(use_offset),
1005};
1006
1007#undef ANNOTATE_CFG
1008
1009static int annotate_config__cmp(const void *name, const void *cfgp)
1010{
1011        const struct annotate_config *cfg = cfgp;
1012
1013        return strcmp(name, cfg->name);
1014}
1015
1016static int annotate__config(const char *var, const char *value,
1017                            void *data __maybe_unused)
1018{
1019        struct annotate_config *cfg;
1020        const char *name;
1021
1022        if (prefixcmp(var, "annotate.") != 0)
1023                return 0;
1024
1025        name = var + 9;
1026        cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1027                      sizeof(struct annotate_config), annotate_config__cmp);
1028
1029        if (cfg == NULL)
1030                return -1;
1031
1032        *cfg->value = perf_config_bool(name, value);
1033        return 0;
1034}
1035
1036void annotate_browser__init(void)
1037{
1038        perf_config(annotate__config, NULL);
1039}
1040