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