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