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