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
 431#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
 432
 433static int sym_title(struct symbol *sym, struct map *map, char *title,
 434                     size_t sz)
 435{
 436        return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
 437}
 438
 439static bool annotate_browser__callq(struct annotate_browser *browser,
 440                                    struct perf_evsel *evsel,
 441                                    struct hist_browser_timer *hbt)
 442{
 443        struct map_symbol *ms = browser->b.priv;
 444        struct disasm_line *dl = browser->selection;
 445        struct annotation *notes;
 446        struct addr_map_symbol target = {
 447                .map = ms->map,
 448                .addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
 449        };
 450        char title[SYM_TITLE_MAX_SIZE];
 451
 452        if (!ins__is_call(dl->ins))
 453                return false;
 454
 455        if (map_groups__find_ams(&target, NULL) ||
 456            map__rip_2objdump(target.map, target.map->map_ip(target.map,
 457                                                             target.addr)) !=
 458            dl->ops.target.addr) {
 459                ui_helpline__puts("The called function was not found.");
 460                return true;
 461        }
 462
 463        notes = symbol__annotation(target.sym);
 464        pthread_mutex_lock(&notes->lock);
 465
 466        if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
 467                pthread_mutex_unlock(&notes->lock);
 468                ui__warning("Not enough memory for annotating '%s' symbol!\n",
 469                            target.sym->name);
 470                return true;
 471        }
 472
 473        pthread_mutex_unlock(&notes->lock);
 474        symbol__tui_annotate(target.sym, target.map, evsel, hbt);
 475        sym_title(ms->sym, ms->map, title, sizeof(title));
 476        ui_browser__show_title(&browser->b, title);
 477        return true;
 478}
 479
 480static
 481struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
 482                                          s64 offset, s64 *idx)
 483{
 484        struct map_symbol *ms = browser->b.priv;
 485        struct symbol *sym = ms->sym;
 486        struct annotation *notes = symbol__annotation(sym);
 487        struct disasm_line *pos;
 488
 489        *idx = 0;
 490        list_for_each_entry(pos, &notes->src->source, node) {
 491                if (pos->offset == offset)
 492                        return pos;
 493                if (!disasm_line__filter(&browser->b, &pos->node))
 494                        ++*idx;
 495        }
 496
 497        return NULL;
 498}
 499
 500static bool annotate_browser__jump(struct annotate_browser *browser)
 501{
 502        struct disasm_line *dl = browser->selection;
 503        s64 idx;
 504
 505        if (!ins__is_jump(dl->ins))
 506                return false;
 507
 508        dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
 509        if (dl == NULL) {
 510                ui_helpline__puts("Invalid jump offset");
 511                return true;
 512        }
 513
 514        annotate_browser__set_top(browser, dl, idx);
 515        
 516        return true;
 517}
 518
 519static
 520struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
 521                                          char *s, s64 *idx)
 522{
 523        struct map_symbol *ms = browser->b.priv;
 524        struct symbol *sym = ms->sym;
 525        struct annotation *notes = symbol__annotation(sym);
 526        struct disasm_line *pos = browser->selection;
 527
 528        *idx = browser->b.index;
 529        list_for_each_entry_continue(pos, &notes->src->source, node) {
 530                if (disasm_line__filter(&browser->b, &pos->node))
 531                        continue;
 532
 533                ++*idx;
 534
 535                if (pos->line && strstr(pos->line, s) != NULL)
 536                        return pos;
 537        }
 538
 539        return NULL;
 540}
 541
 542static bool __annotate_browser__search(struct annotate_browser *browser)
 543{
 544        struct disasm_line *dl;
 545        s64 idx;
 546
 547        dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
 548        if (dl == NULL) {
 549                ui_helpline__puts("String not found!");
 550                return false;
 551        }
 552
 553        annotate_browser__set_top(browser, dl, idx);
 554        browser->searching_backwards = false;
 555        return true;
 556}
 557
 558static
 559struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
 560                                                  char *s, s64 *idx)
 561{
 562        struct map_symbol *ms = browser->b.priv;
 563        struct symbol *sym = ms->sym;
 564        struct annotation *notes = symbol__annotation(sym);
 565        struct disasm_line *pos = browser->selection;
 566
 567        *idx = browser->b.index;
 568        list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
 569                if (disasm_line__filter(&browser->b, &pos->node))
 570                        continue;
 571
 572                --*idx;
 573
 574                if (pos->line && strstr(pos->line, s) != NULL)
 575                        return pos;
 576        }
 577
 578        return NULL;
 579}
 580
 581static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
 582{
 583        struct disasm_line *dl;
 584        s64 idx;
 585
 586        dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
 587        if (dl == NULL) {
 588                ui_helpline__puts("String not found!");
 589                return false;
 590        }
 591
 592        annotate_browser__set_top(browser, dl, idx);
 593        browser->searching_backwards = true;
 594        return true;
 595}
 596
 597static bool annotate_browser__search_window(struct annotate_browser *browser,
 598                                            int delay_secs)
 599{
 600        if (ui_browser__input_window("Search", "String: ", browser->search_bf,
 601                                     "ENTER: OK, ESC: Cancel",
 602                                     delay_secs * 2) != K_ENTER ||
 603            !*browser->search_bf)
 604                return false;
 605
 606        return true;
 607}
 608
 609static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
 610{
 611        if (annotate_browser__search_window(browser, delay_secs))
 612                return __annotate_browser__search(browser);
 613
 614        return false;
 615}
 616
 617static bool annotate_browser__continue_search(struct annotate_browser *browser,
 618                                              int delay_secs)
 619{
 620        if (!*browser->search_bf)
 621                return annotate_browser__search(browser, delay_secs);
 622
 623        return __annotate_browser__search(browser);
 624}
 625
 626static bool annotate_browser__search_reverse(struct annotate_browser *browser,
 627                                           int delay_secs)
 628{
 629        if (annotate_browser__search_window(browser, delay_secs))
 630                return __annotate_browser__search_reverse(browser);
 631
 632        return false;
 633}
 634
 635static
 636bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
 637                                               int delay_secs)
 638{
 639        if (!*browser->search_bf)
 640                return annotate_browser__search_reverse(browser, delay_secs);
 641
 642        return __annotate_browser__search_reverse(browser);
 643}
 644
 645static void annotate_browser__update_addr_width(struct annotate_browser *browser)
 646{
 647        if (annotate_browser__opts.use_offset)
 648                browser->target_width = browser->min_addr_width;
 649        else
 650                browser->target_width = browser->max_addr_width;
 651
 652        browser->addr_width = browser->target_width;
 653
 654        if (annotate_browser__opts.show_nr_jumps)
 655                browser->addr_width += browser->jumps_width + 1;
 656}
 657
 658static int annotate_browser__run(struct annotate_browser *browser,
 659                                 struct perf_evsel *evsel,
 660                                 struct hist_browser_timer *hbt)
 661{
 662        struct rb_node *nd = NULL;
 663        struct map_symbol *ms = browser->b.priv;
 664        struct symbol *sym = ms->sym;
 665        const char *help = "Press 'h' for help on key bindings";
 666        int delay_secs = hbt ? hbt->refresh : 0;
 667        int key;
 668        char title[SYM_TITLE_MAX_SIZE];
 669
 670        sym_title(sym, ms->map, title, sizeof(title));
 671        if (ui_browser__show(&browser->b, title, help) < 0)
 672                return -1;
 673
 674        annotate_browser__calc_percent(browser, evsel);
 675
 676        if (browser->curr_hot) {
 677                annotate_browser__set_rb_top(browser, browser->curr_hot);
 678                browser->b.navkeypressed = false;
 679        }
 680
 681        nd = browser->curr_hot;
 682
 683        while (1) {
 684                key = ui_browser__run(&browser->b, delay_secs);
 685
 686                if (delay_secs != 0) {
 687                        annotate_browser__calc_percent(browser, evsel);
 688                        /*
 689                         * Current line focus got out of the list of most active
 690                         * lines, NULL it so that if TAB|UNTAB is pressed, we
 691                         * move to curr_hot (current hottest line).
 692                         */
 693                        if (nd != NULL && RB_EMPTY_NODE(nd))
 694                                nd = NULL;
 695                }
 696
 697                switch (key) {
 698                case K_TIMER:
 699                        if (hbt)
 700                                hbt->timer(hbt->arg);
 701
 702                        if (delay_secs != 0)
 703                                symbol__annotate_decay_histogram(sym, evsel->idx);
 704                        continue;
 705                case K_TAB:
 706                        if (nd != NULL) {
 707                                nd = rb_prev(nd);
 708                                if (nd == NULL)
 709                                        nd = rb_last(&browser->entries);
 710                        } else
 711                                nd = browser->curr_hot;
 712                        break;
 713                case K_UNTAB:
 714                        if (nd != NULL)
 715                                nd = rb_next(nd);
 716                                if (nd == NULL)
 717                                        nd = rb_first(&browser->entries);
 718                        else
 719                                nd = browser->curr_hot;
 720                        break;
 721                case K_F1:
 722                case 'h':
 723                        ui_browser__help_window(&browser->b,
 724                "UP/DOWN/PGUP\n"
 725                "PGDN/SPACE    Navigate\n"
 726                "q/ESC/CTRL+C  Exit\n\n"
 727                "->            Go to target\n"
 728                "<-            Exit\n"
 729                "H             Cycle thru hottest instructions\n"
 730                "j             Toggle showing jump to target arrows\n"
 731                "J             Toggle showing number of jump sources on targets\n"
 732                "n             Search next string\n"
 733                "o             Toggle disassembler output/simplified view\n"
 734                "s             Toggle source code view\n"
 735                "/             Search string\n"
 736                "r             Run available scripts\n"
 737                "?             Search string backwards\n");
 738                        continue;
 739                case 'r':
 740                        {
 741                                script_browse(NULL);
 742                                continue;
 743                        }
 744                case 'H':
 745                        nd = browser->curr_hot;
 746                        break;
 747                case 's':
 748                        if (annotate_browser__toggle_source(browser))
 749                                ui_helpline__puts(help);
 750                        continue;
 751                case 'o':
 752                        annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
 753                        annotate_browser__update_addr_width(browser);
 754                        continue;
 755                case 'j':
 756                        annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
 757                        continue;
 758                case 'J':
 759                        annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
 760                        annotate_browser__update_addr_width(browser);
 761                        continue;
 762                case '/':
 763                        if (annotate_browser__search(browser, delay_secs)) {
 764show_help:
 765                                ui_helpline__puts(help);
 766                        }
 767                        continue;
 768                case 'n':
 769                        if (browser->searching_backwards ?
 770                            annotate_browser__continue_search_reverse(browser, delay_secs) :
 771                            annotate_browser__continue_search(browser, delay_secs))
 772                                goto show_help;
 773                        continue;
 774                case '?':
 775                        if (annotate_browser__search_reverse(browser, delay_secs))
 776                                goto show_help;
 777                        continue;
 778                case 'D': {
 779                        static int seq;
 780                        ui_helpline__pop();
 781                        ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
 782                                           seq++, browser->b.nr_entries,
 783                                           browser->b.height,
 784                                           browser->b.index,
 785                                           browser->b.top_idx,
 786                                           browser->nr_asm_entries);
 787                }
 788                        continue;
 789                case K_ENTER:
 790                case K_RIGHT:
 791                        if (browser->selection == NULL)
 792                                ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
 793                        else if (browser->selection->offset == -1)
 794                                ui_helpline__puts("Actions are only available for assembly lines.");
 795                        else if (!browser->selection->ins) {
 796                                if (strcmp(browser->selection->name, "retq"))
 797                                        goto show_sup_ins;
 798                                goto out;
 799                        } else if (!(annotate_browser__jump(browser) ||
 800                                     annotate_browser__callq(browser, evsel, hbt))) {
 801show_sup_ins:
 802                                ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
 803                        }
 804                        continue;
 805                case K_LEFT:
 806                case K_ESC:
 807                case 'q':
 808                case CTRL('c'):
 809                        goto out;
 810                default:
 811                        continue;
 812                }
 813
 814                if (nd != NULL)
 815                        annotate_browser__set_rb_top(browser, nd);
 816        }
 817out:
 818        ui_browser__hide(&browser->b);
 819        return key;
 820}
 821
 822int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
 823                             struct hist_browser_timer *hbt)
 824{
 825        return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);
 826}
 827
 828static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
 829                                                size_t size)
 830{
 831        u64 offset;
 832        struct map_symbol *ms = browser->b.priv;
 833        struct symbol *sym = ms->sym;
 834
 835        /* PLT symbols contain external offsets */
 836        if (strstr(sym->name, "@plt"))
 837                return;
 838
 839        for (offset = 0; offset < size; ++offset) {
 840                struct disasm_line *dl = browser->offsets[offset], *dlt;
 841                struct browser_disasm_line *bdlt;
 842
 843                if (!disasm_line__is_valid_jump(dl, sym))
 844                        continue;
 845
 846                dlt = browser->offsets[dl->ops.target.offset];
 847                /*
 848                 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 849                 * have to adjust to the previous offset?
 850                 */
 851                if (dlt == NULL)
 852                        continue;
 853
 854                bdlt = disasm_line__browser(dlt);
 855                if (++bdlt->jump_sources > browser->max_jump_sources)
 856                        browser->max_jump_sources = bdlt->jump_sources;
 857
 858                ++browser->nr_jumps;
 859        }
 860                
 861}
 862
 863static inline int width_jumps(int n)
 864{
 865        if (n >= 100)
 866                return 5;
 867        if (n / 10)
 868                return 2;
 869        return 1;
 870}
 871
 872int symbol__tui_annotate(struct symbol *sym, struct map *map,
 873                         struct perf_evsel *evsel,
 874                         struct hist_browser_timer *hbt)
 875{
 876        struct disasm_line *pos, *n;
 877        struct annotation *notes;
 878        size_t size;
 879        struct map_symbol ms = {
 880                .map = map,
 881                .sym = sym,
 882        };
 883        struct annotate_browser browser = {
 884                .b = {
 885                        .refresh = annotate_browser__refresh,
 886                        .seek    = ui_browser__list_head_seek,
 887                        .write   = annotate_browser__write,
 888                        .filter  = disasm_line__filter,
 889                        .priv    = &ms,
 890                        .use_navkeypressed = true,
 891                },
 892        };
 893        int ret = -1;
 894        int nr_pcnt = 1;
 895        size_t sizeof_bdl = sizeof(struct browser_disasm_line);
 896
 897        if (sym == NULL)
 898                return -1;
 899
 900        size = symbol__size(sym);
 901
 902        if (map->dso->annotate_warned)
 903                return -1;
 904
 905        browser.offsets = zalloc(size * sizeof(struct disasm_line *));
 906        if (browser.offsets == NULL) {
 907                ui__error("Not enough memory!");
 908                return -1;
 909        }
 910
 911        if (perf_evsel__is_group_event(evsel)) {
 912                nr_pcnt = evsel->nr_members;
 913                sizeof_bdl += sizeof(double) * (nr_pcnt - 1);
 914        }
 915
 916        if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
 917                ui__error("%s", ui_helpline__last_msg);
 918                goto out_free_offsets;
 919        }
 920
 921        ui_helpline__push("Press <- or ESC to exit");
 922
 923        notes = symbol__annotation(sym);
 924        browser.start = map__rip_2objdump(map, sym->start);
 925
 926        list_for_each_entry(pos, &notes->src->source, node) {
 927                struct browser_disasm_line *bpos;
 928                size_t line_len = strlen(pos->line);
 929
 930                if (browser.b.width < line_len)
 931                        browser.b.width = line_len;
 932                bpos = disasm_line__browser(pos);
 933                bpos->idx = browser.nr_entries++;
 934                if (pos->offset != -1) {
 935                        bpos->idx_asm = browser.nr_asm_entries++;
 936                        /*
 937                         * FIXME: short term bandaid to cope with assembly
 938                         * routines that comes with labels in the same column
 939                         * as the address in objdump, sigh.
 940                         *
 941                         * E.g. copy_user_generic_unrolled
 942                         */
 943                        if (pos->offset < (s64)size)
 944                                browser.offsets[pos->offset] = pos;
 945                } else
 946                        bpos->idx_asm = -1;
 947        }
 948
 949        annotate_browser__mark_jump_targets(&browser, size);
 950
 951        browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
 952        browser.max_addr_width = hex_width(sym->end);
 953        browser.jumps_width = width_jumps(browser.max_jump_sources);
 954        browser.nr_events = nr_pcnt;
 955        browser.b.nr_entries = browser.nr_entries;
 956        browser.b.entries = &notes->src->source,
 957        browser.b.width += 18; /* Percentage */
 958
 959        if (annotate_browser__opts.hide_src_code)
 960                annotate_browser__init_asm_mode(&browser);
 961
 962        annotate_browser__update_addr_width(&browser);
 963
 964        ret = annotate_browser__run(&browser, evsel, hbt);
 965        list_for_each_entry_safe(pos, n, &notes->src->source, node) {
 966                list_del(&pos->node);
 967                disasm_line__free(pos);
 968        }
 969
 970out_free_offsets:
 971        free(browser.offsets);
 972        return ret;
 973}
 974
 975#define ANNOTATE_CFG(n) \
 976        { .name = #n, .value = &annotate_browser__opts.n, }
 977
 978/*
 979 * Keep the entries sorted, they are bsearch'ed
 980 */
 981static struct annotate_config {
 982        const char *name;
 983        bool *value;
 984} annotate__configs[] = {
 985        ANNOTATE_CFG(hide_src_code),
 986        ANNOTATE_CFG(jump_arrows),
 987        ANNOTATE_CFG(show_nr_jumps),
 988        ANNOTATE_CFG(use_offset),
 989};
 990
 991#undef ANNOTATE_CFG
 992
 993static int annotate_config__cmp(const void *name, const void *cfgp)
 994{
 995        const struct annotate_config *cfg = cfgp;
 996
 997        return strcmp(name, cfg->name);
 998}
 999
1000static int annotate__config(const char *var, const char *value,
1001                            void *data __maybe_unused)
1002{
1003        struct annotate_config *cfg;
1004        const char *name;
1005
1006        if (prefixcmp(var, "annotate.") != 0)
1007                return 0;
1008
1009        name = var + 9;
1010        cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1011                      sizeof(struct annotate_config), annotate_config__cmp);
1012
1013        if (cfg == NULL)
1014                return -1;
1015
1016        *cfg->value = perf_config_bool(name, value);
1017        return 0;
1018}
1019
1020void annotate_browser__init(void)
1021{
1022        perf_config(annotate__config, NULL);
1023}
1024