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 hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
 833                             struct hist_browser_timer *hbt)
 834{
 835        return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);
 836}
 837
 838static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
 839                                                size_t size)
 840{
 841        u64 offset;
 842        struct map_symbol *ms = browser->b.priv;
 843        struct symbol *sym = ms->sym;
 844
 845        /* PLT symbols contain external offsets */
 846        if (strstr(sym->name, "@plt"))
 847                return;
 848
 849        for (offset = 0; offset < size; ++offset) {
 850                struct disasm_line *dl = browser->offsets[offset], *dlt;
 851                struct browser_disasm_line *bdlt;
 852
 853                if (!disasm_line__is_valid_jump(dl, sym))
 854                        continue;
 855
 856                dlt = browser->offsets[dl->ops.target.offset];
 857                /*
 858                 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 859                 * have to adjust to the previous offset?
 860                 */
 861                if (dlt == NULL)
 862                        continue;
 863
 864                bdlt = disasm_line__browser(dlt);
 865                if (++bdlt->jump_sources > browser->max_jump_sources)
 866                        browser->max_jump_sources = bdlt->jump_sources;
 867
 868                ++browser->nr_jumps;
 869        }
 870                
 871}
 872
 873static inline int width_jumps(int n)
 874{
 875        if (n >= 100)
 876                return 5;
 877        if (n / 10)
 878                return 2;
 879        return 1;
 880}
 881
 882int symbol__tui_annotate(struct symbol *sym, struct map *map,
 883                         struct perf_evsel *evsel,
 884                         struct hist_browser_timer *hbt)
 885{
 886        struct disasm_line *pos, *n;
 887        struct annotation *notes;
 888        size_t size;
 889        struct map_symbol ms = {
 890                .map = map,
 891                .sym = sym,
 892        };
 893        struct annotate_browser browser = {
 894                .b = {
 895                        .refresh = annotate_browser__refresh,
 896                        .seek    = ui_browser__list_head_seek,
 897                        .write   = annotate_browser__write,
 898                        .filter  = disasm_line__filter,
 899                        .priv    = &ms,
 900                        .use_navkeypressed = true,
 901                },
 902        };
 903        int ret = -1;
 904        int nr_pcnt = 1;
 905        size_t sizeof_bdl = sizeof(struct browser_disasm_line);
 906
 907        if (sym == NULL)
 908                return -1;
 909
 910        size = symbol__size(sym);
 911
 912        if (map->dso->annotate_warned)
 913                return -1;
 914
 915        browser.offsets = zalloc(size * sizeof(struct disasm_line *));
 916        if (browser.offsets == NULL) {
 917                ui__error("Not enough memory!");
 918                return -1;
 919        }
 920
 921        if (perf_evsel__is_group_event(evsel)) {
 922                nr_pcnt = evsel->nr_members;
 923                sizeof_bdl += sizeof(double) * (nr_pcnt - 1);
 924        }
 925
 926        if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
 927                ui__error("%s", ui_helpline__last_msg);
 928                goto out_free_offsets;
 929        }
 930
 931        ui_helpline__push("Press <- or ESC to exit");
 932
 933        notes = symbol__annotation(sym);
 934        browser.start = map__rip_2objdump(map, sym->start);
 935
 936        list_for_each_entry(pos, &notes->src->source, node) {
 937                struct browser_disasm_line *bpos;
 938                size_t line_len = strlen(pos->line);
 939
 940                if (browser.b.width < line_len)
 941                        browser.b.width = line_len;
 942                bpos = disasm_line__browser(pos);
 943                bpos->idx = browser.nr_entries++;
 944                if (pos->offset != -1) {
 945                        bpos->idx_asm = browser.nr_asm_entries++;
 946                        /*
 947                         * FIXME: short term bandaid to cope with assembly
 948                         * routines that comes with labels in the same column
 949                         * as the address in objdump, sigh.
 950                         *
 951                         * E.g. copy_user_generic_unrolled
 952                         */
 953                        if (pos->offset < (s64)size)
 954                                browser.offsets[pos->offset] = pos;
 955                } else
 956                        bpos->idx_asm = -1;
 957        }
 958
 959        annotate_browser__mark_jump_targets(&browser, size);
 960
 961        browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
 962        browser.max_addr_width = hex_width(sym->end);
 963        browser.jumps_width = width_jumps(browser.max_jump_sources);
 964        browser.nr_events = nr_pcnt;
 965        browser.b.nr_entries = browser.nr_entries;
 966        browser.b.entries = &notes->src->source,
 967        browser.b.width += 18; /* Percentage */
 968
 969        if (annotate_browser__opts.hide_src_code)
 970                annotate_browser__init_asm_mode(&browser);
 971
 972        annotate_browser__update_addr_width(&browser);
 973
 974        ret = annotate_browser__run(&browser, evsel, hbt);
 975        list_for_each_entry_safe(pos, n, &notes->src->source, node) {
 976                list_del(&pos->node);
 977                disasm_line__free(pos);
 978        }
 979
 980out_free_offsets:
 981        free(browser.offsets);
 982        return ret;
 983}
 984
 985#define ANNOTATE_CFG(n) \
 986        { .name = #n, .value = &annotate_browser__opts.n, }
 987
 988/*
 989 * Keep the entries sorted, they are bsearch'ed
 990 */
 991static struct annotate_config {
 992        const char *name;
 993        bool *value;
 994} annotate__configs[] = {
 995        ANNOTATE_CFG(hide_src_code),
 996        ANNOTATE_CFG(jump_arrows),
 997        ANNOTATE_CFG(show_linenr),
 998        ANNOTATE_CFG(show_nr_jumps),
 999        ANNOTATE_CFG(use_offset),
1000};
1001
1002#undef ANNOTATE_CFG
1003
1004static int annotate_config__cmp(const void *name, const void *cfgp)
1005{
1006        const struct annotate_config *cfg = cfgp;
1007
1008        return strcmp(name, cfg->name);
1009}
1010
1011static int annotate__config(const char *var, const char *value,
1012                            void *data __maybe_unused)
1013{
1014        struct annotate_config *cfg;
1015        const char *name;
1016
1017        if (prefixcmp(var, "annotate.") != 0)
1018                return 0;
1019
1020        name = var + 9;
1021        cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1022                      sizeof(struct annotate_config), annotate_config__cmp);
1023
1024        if (cfg == NULL)
1025                return -1;
1026
1027        *cfg->value = perf_config_bool(name, value);
1028        return 0;
1029}
1030
1031void annotate_browser__init(void)
1032{
1033        perf_config(annotate__config, NULL);
1034}
1035