linux/tools/perf/ui/browsers/annotate.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include "../browser.h"
   3#include "../helpline.h"
   4#include "../ui.h"
   5#include "../util.h"
   6#include "../../util/annotate.h"
   7#include "../../util/hist.h"
   8#include "../../util/sort.h"
   9#include "../../util/map.h"
  10#include "../../util/symbol.h"
  11#include "../../util/evsel.h"
  12#include "../../util/evlist.h"
  13#include <inttypes.h>
  14#include <pthread.h>
  15#include <linux/kernel.h>
  16#include <linux/string.h>
  17#include <linux/zalloc.h>
  18#include <sys/ttydefaults.h>
  19#include <asm/bug.h>
  20
  21struct disasm_line_samples {
  22        double                percent;
  23        struct sym_hist_entry he;
  24};
  25
  26struct arch;
  27
  28struct annotate_browser {
  29        struct ui_browser           b;
  30        struct rb_root              entries;
  31        struct rb_node             *curr_hot;
  32        struct annotation_line     *selection;
  33        struct arch                *arch;
  34        struct annotation_options  *opts;
  35        bool                        searching_backwards;
  36        char                        search_bf[128];
  37};
  38
  39static inline struct annotation *browser__annotation(struct ui_browser *browser)
  40{
  41        struct map_symbol *ms = browser->priv;
  42        return symbol__annotation(ms->sym);
  43}
  44
  45static bool disasm_line__filter(struct ui_browser *browser, void *entry)
  46{
  47        struct annotation *notes = browser__annotation(browser);
  48        struct annotation_line *al = list_entry(entry, struct annotation_line, node);
  49        return annotation_line__filter(al, notes);
  50}
  51
  52static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
  53{
  54        struct annotation *notes = browser__annotation(browser);
  55
  56        if (current && (!browser->use_navkeypressed || browser->navkeypressed))
  57                return HE_COLORSET_SELECTED;
  58        if (nr == notes->max_jump_sources)
  59                return HE_COLORSET_TOP;
  60        if (nr > 1)
  61                return HE_COLORSET_MEDIUM;
  62        return HE_COLORSET_NORMAL;
  63}
  64
  65static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
  66{
  67         int color = ui_browser__jumps_percent_color(browser, nr, current);
  68         return ui_browser__set_color(browser, color);
  69}
  70
  71static int annotate_browser__set_color(void *browser, int color)
  72{
  73        return ui_browser__set_color(browser, color);
  74}
  75
  76static void annotate_browser__write_graph(void *browser, int graph)
  77{
  78        ui_browser__write_graph(browser, graph);
  79}
  80
  81static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
  82{
  83        ui_browser__set_percent_color(browser, percent, current);
  84}
  85
  86static void annotate_browser__printf(void *browser, const char *fmt, ...)
  87{
  88        va_list args;
  89
  90        va_start(args, fmt);
  91        ui_browser__vprintf(browser, fmt, args);
  92        va_end(args);
  93}
  94
  95static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
  96{
  97        struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
  98        struct annotation *notes = browser__annotation(browser);
  99        struct annotation_line *al = list_entry(entry, struct annotation_line, node);
 100        const bool is_current_entry = ui_browser__is_current_entry(browser, row);
 101        struct annotation_write_ops ops = {
 102                .first_line              = row == 0,
 103                .current_entry           = is_current_entry,
 104                .change_color            = (!notes->options->hide_src_code &&
 105                                            (!is_current_entry ||
 106                                             (browser->use_navkeypressed &&
 107                                              !browser->navkeypressed))),
 108                .width                   = browser->width,
 109                .obj                     = browser,
 110                .set_color               = annotate_browser__set_color,
 111                .set_percent_color       = annotate_browser__set_percent_color,
 112                .set_jumps_percent_color = ui_browser__set_jumps_percent_color,
 113                .printf                  = annotate_browser__printf,
 114                .write_graph             = annotate_browser__write_graph,
 115        };
 116
 117        /* The scroll bar isn't being used */
 118        if (!browser->navkeypressed)
 119                ops.width += 1;
 120
 121        annotation_line__write(al, notes, &ops, ab->opts);
 122
 123        if (ops.current_entry)
 124                ab->selection = al;
 125}
 126
 127static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
 128{
 129        struct disasm_line *pos = list_prev_entry(cursor, al.node);
 130        const char *name;
 131
 132        if (!pos)
 133                return false;
 134
 135        if (ins__is_lock(&pos->ins))
 136                name = pos->ops.locked.ins.name;
 137        else
 138                name = pos->ins.name;
 139
 140        if (!name || !cursor->ins.name)
 141                return false;
 142
 143        return ins__is_fused(ab->arch, name, cursor->ins.name);
 144}
 145
 146static void annotate_browser__draw_current_jump(struct ui_browser *browser)
 147{
 148        struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
 149        struct disasm_line *cursor = disasm_line(ab->selection);
 150        struct annotation_line *target;
 151        unsigned int from, to;
 152        struct map_symbol *ms = ab->b.priv;
 153        struct symbol *sym = ms->sym;
 154        struct annotation *notes = symbol__annotation(sym);
 155        u8 pcnt_width = annotation__pcnt_width(notes);
 156        int width;
 157
 158        /* PLT symbols contain external offsets */
 159        if (strstr(sym->name, "@plt"))
 160                return;
 161
 162        if (!disasm_line__is_valid_local_jump(cursor, sym))
 163                return;
 164
 165        /*
 166         * This first was seen with a gcc function, _cpp_lex_token, that
 167         * has the usual jumps:
 168         *
 169         *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
 170         *
 171         * I.e. jumps to a label inside that function (_cpp_lex_token), and
 172         * those works, but also this kind:
 173         *
 174         *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
 175         *
 176         *  I.e. jumps to another function, outside _cpp_lex_token, which
 177         *  are not being correctly handled generating as a side effect references
 178         *  to ab->offset[] entries that are set to NULL, so to make this code
 179         *  more robust, check that here.
 180         *
 181         *  A proper fix for will be put in place, looking at the function
 182         *  name right after the '<' token and probably treating this like a
 183         *  'call' instruction.
 184         */
 185        target = notes->offsets[cursor->ops.target.offset];
 186        if (target == NULL) {
 187                ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
 188                                    cursor->ops.target.offset);
 189                return;
 190        }
 191
 192        if (notes->options->hide_src_code) {
 193                from = cursor->al.idx_asm;
 194                to = target->idx_asm;
 195        } else {
 196                from = (u64)cursor->al.idx;
 197                to = (u64)target->idx;
 198        }
 199
 200        width = annotation__cycles_width(notes);
 201
 202        ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
 203        __ui_browser__line_arrow(browser,
 204                                 pcnt_width + 2 + notes->widths.addr + width,
 205                                 from, to);
 206
 207        if (is_fused(ab, cursor)) {
 208                ui_browser__mark_fused(browser,
 209                                       pcnt_width + 3 + notes->widths.addr + width,
 210                                       from - 1,
 211                                       to > from ? true : false);
 212        }
 213}
 214
 215static unsigned int annotate_browser__refresh(struct ui_browser *browser)
 216{
 217        struct annotation *notes = browser__annotation(browser);
 218        int ret = ui_browser__list_head_refresh(browser);
 219        int pcnt_width = annotation__pcnt_width(notes);
 220
 221        if (notes->options->jump_arrows)
 222                annotate_browser__draw_current_jump(browser);
 223
 224        ui_browser__set_color(browser, HE_COLORSET_NORMAL);
 225        __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
 226        return ret;
 227}
 228
 229static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
 230                                                  int percent_type)
 231{
 232        int i;
 233
 234        for (i = 0; i < a->data_nr; i++) {
 235                if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
 236                        continue;
 237                return a->data[i].percent[percent_type] -
 238                           b->data[i].percent[percent_type];
 239        }
 240        return 0;
 241}
 242
 243static void disasm_rb_tree__insert(struct annotate_browser *browser,
 244                                struct annotation_line *al)
 245{
 246        struct rb_root *root = &browser->entries;
 247        struct rb_node **p = &root->rb_node;
 248        struct rb_node *parent = NULL;
 249        struct annotation_line *l;
 250
 251        while (*p != NULL) {
 252                parent = *p;
 253                l = rb_entry(parent, struct annotation_line, rb_node);
 254
 255                if (disasm__cmp(al, l, browser->opts->percent_type) < 0)
 256                        p = &(*p)->rb_left;
 257                else
 258                        p = &(*p)->rb_right;
 259        }
 260        rb_link_node(&al->rb_node, parent, p);
 261        rb_insert_color(&al->rb_node, root);
 262}
 263
 264static void annotate_browser__set_top(struct annotate_browser *browser,
 265                                      struct annotation_line *pos, u32 idx)
 266{
 267        struct annotation *notes = browser__annotation(&browser->b);
 268        unsigned back;
 269
 270        ui_browser__refresh_dimensions(&browser->b);
 271        back = browser->b.height / 2;
 272        browser->b.top_idx = browser->b.index = idx;
 273
 274        while (browser->b.top_idx != 0 && back != 0) {
 275                pos = list_entry(pos->node.prev, struct annotation_line, node);
 276
 277                if (annotation_line__filter(pos, notes))
 278                        continue;
 279
 280                --browser->b.top_idx;
 281                --back;
 282        }
 283
 284        browser->b.top = pos;
 285        browser->b.navkeypressed = true;
 286}
 287
 288static void annotate_browser__set_rb_top(struct annotate_browser *browser,
 289                                         struct rb_node *nd)
 290{
 291        struct annotation *notes = browser__annotation(&browser->b);
 292        struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
 293        u32 idx = pos->idx;
 294
 295        if (notes->options->hide_src_code)
 296                idx = pos->idx_asm;
 297        annotate_browser__set_top(browser, pos, idx);
 298        browser->curr_hot = nd;
 299}
 300
 301static void annotate_browser__calc_percent(struct annotate_browser *browser,
 302                                           struct perf_evsel *evsel)
 303{
 304        struct map_symbol *ms = browser->b.priv;
 305        struct symbol *sym = ms->sym;
 306        struct annotation *notes = symbol__annotation(sym);
 307        struct disasm_line *pos;
 308
 309        browser->entries = RB_ROOT;
 310
 311        pthread_mutex_lock(&notes->lock);
 312
 313        symbol__calc_percent(sym, evsel);
 314
 315        list_for_each_entry(pos, &notes->src->source, al.node) {
 316                double max_percent = 0.0;
 317                int i;
 318
 319                if (pos->al.offset == -1) {
 320                        RB_CLEAR_NODE(&pos->al.rb_node);
 321                        continue;
 322                }
 323
 324                for (i = 0; i < pos->al.data_nr; i++) {
 325                        double percent;
 326
 327                        percent = annotation_data__percent(&pos->al.data[i],
 328                                                           browser->opts->percent_type);
 329
 330                        if (max_percent < percent)
 331                                max_percent = percent;
 332                }
 333
 334                if (max_percent < 0.01 && pos->al.ipc == 0) {
 335                        RB_CLEAR_NODE(&pos->al.rb_node);
 336                        continue;
 337                }
 338                disasm_rb_tree__insert(browser, &pos->al);
 339        }
 340        pthread_mutex_unlock(&notes->lock);
 341
 342        browser->curr_hot = rb_last(&browser->entries);
 343}
 344
 345static bool annotate_browser__toggle_source(struct annotate_browser *browser)
 346{
 347        struct annotation *notes = browser__annotation(&browser->b);
 348        struct annotation_line *al;
 349        off_t offset = browser->b.index - browser->b.top_idx;
 350
 351        browser->b.seek(&browser->b, offset, SEEK_CUR);
 352        al = list_entry(browser->b.top, struct annotation_line, node);
 353
 354        if (notes->options->hide_src_code) {
 355                if (al->idx_asm < offset)
 356                        offset = al->idx;
 357
 358                browser->b.nr_entries = notes->nr_entries;
 359                notes->options->hide_src_code = false;
 360                browser->b.seek(&browser->b, -offset, SEEK_CUR);
 361                browser->b.top_idx = al->idx - offset;
 362                browser->b.index = al->idx;
 363        } else {
 364                if (al->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 (al->idx_asm < offset)
 371                        offset = al->idx_asm;
 372
 373                browser->b.nr_entries = notes->nr_asm_entries;
 374                notes->options->hide_src_code = true;
 375                browser->b.seek(&browser->b, -offset, SEEK_CUR);
 376                browser->b.top_idx = al->idx_asm - offset;
 377                browser->b.index = al->idx_asm;
 378        }
 379
 380        return true;
 381}
 382
 383static void ui_browser__init_asm_mode(struct ui_browser *browser)
 384{
 385        struct annotation *notes = browser__annotation(browser);
 386        ui_browser__reset_index(browser);
 387        browser->nr_entries = notes->nr_asm_entries;
 388}
 389
 390#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
 391
 392static int sym_title(struct symbol *sym, struct map *map, char *title,
 393                     size_t sz, int percent_type)
 394{
 395        return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name, map->dso->long_name,
 396                        percent_type_str(percent_type));
 397}
 398
 399/*
 400 * This can be called from external jumps, i.e. jumps from one functon
 401 * to another, like from the kernel's entry_SYSCALL_64 function to the
 402 * swapgs_restore_regs_and_return_to_usermode() function.
 403 *
 404 * So all we check here is that dl->ops.target.sym is set, if it is, just
 405 * go to that function and when exiting from its disassembly, come back
 406 * to the calling function.
 407 */
 408static bool annotate_browser__callq(struct annotate_browser *browser,
 409                                    struct perf_evsel *evsel,
 410                                    struct hist_browser_timer *hbt)
 411{
 412        struct map_symbol *ms = browser->b.priv;
 413        struct disasm_line *dl = disasm_line(browser->selection);
 414        struct annotation *notes;
 415        char title[SYM_TITLE_MAX_SIZE];
 416
 417        if (!dl->ops.target.sym) {
 418                ui_helpline__puts("The called function was not found.");
 419                return true;
 420        }
 421
 422        notes = symbol__annotation(dl->ops.target.sym);
 423        pthread_mutex_lock(&notes->lock);
 424
 425        if (!symbol__hists(dl->ops.target.sym, evsel->evlist->nr_entries)) {
 426                pthread_mutex_unlock(&notes->lock);
 427                ui__warning("Not enough memory for annotating '%s' symbol!\n",
 428                            dl->ops.target.sym->name);
 429                return true;
 430        }
 431
 432        pthread_mutex_unlock(&notes->lock);
 433        symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt, browser->opts);
 434        sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
 435        ui_browser__show_title(&browser->b, title);
 436        return true;
 437}
 438
 439static
 440struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
 441                                          s64 offset, s64 *idx)
 442{
 443        struct annotation *notes = browser__annotation(&browser->b);
 444        struct disasm_line *pos;
 445
 446        *idx = 0;
 447        list_for_each_entry(pos, &notes->src->source, al.node) {
 448                if (pos->al.offset == offset)
 449                        return pos;
 450                if (!annotation_line__filter(&pos->al, notes))
 451                        ++*idx;
 452        }
 453
 454        return NULL;
 455}
 456
 457static bool annotate_browser__jump(struct annotate_browser *browser,
 458                                   struct perf_evsel *evsel,
 459                                   struct hist_browser_timer *hbt)
 460{
 461        struct disasm_line *dl = disasm_line(browser->selection);
 462        u64 offset;
 463        s64 idx;
 464
 465        if (!ins__is_jump(&dl->ins))
 466                return false;
 467
 468        if (dl->ops.target.outside) {
 469                annotate_browser__callq(browser, evsel, hbt);
 470                return true;
 471        }
 472
 473        offset = dl->ops.target.offset;
 474        dl = annotate_browser__find_offset(browser, offset, &idx);
 475        if (dl == NULL) {
 476                ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
 477                return true;
 478        }
 479
 480        annotate_browser__set_top(browser, &dl->al, idx);
 481
 482        return true;
 483}
 484
 485static
 486struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
 487                                          char *s, s64 *idx)
 488{
 489        struct annotation *notes = browser__annotation(&browser->b);
 490        struct annotation_line *al = browser->selection;
 491
 492        *idx = browser->b.index;
 493        list_for_each_entry_continue(al, &notes->src->source, node) {
 494                if (annotation_line__filter(al, notes))
 495                        continue;
 496
 497                ++*idx;
 498
 499                if (al->line && strstr(al->line, s) != NULL)
 500                        return al;
 501        }
 502
 503        return NULL;
 504}
 505
 506static bool __annotate_browser__search(struct annotate_browser *browser)
 507{
 508        struct annotation_line *al;
 509        s64 idx;
 510
 511        al = annotate_browser__find_string(browser, browser->search_bf, &idx);
 512        if (al == NULL) {
 513                ui_helpline__puts("String not found!");
 514                return false;
 515        }
 516
 517        annotate_browser__set_top(browser, al, idx);
 518        browser->searching_backwards = false;
 519        return true;
 520}
 521
 522static
 523struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
 524                                                  char *s, s64 *idx)
 525{
 526        struct annotation *notes = browser__annotation(&browser->b);
 527        struct annotation_line *al = browser->selection;
 528
 529        *idx = browser->b.index;
 530        list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
 531                if (annotation_line__filter(al, notes))
 532                        continue;
 533
 534                --*idx;
 535
 536                if (al->line && strstr(al->line, s) != NULL)
 537                        return al;
 538        }
 539
 540        return NULL;
 541}
 542
 543static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
 544{
 545        struct annotation_line *al;
 546        s64 idx;
 547
 548        al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
 549        if (al == NULL) {
 550                ui_helpline__puts("String not found!");
 551                return false;
 552        }
 553
 554        annotate_browser__set_top(browser, al, idx);
 555        browser->searching_backwards = true;
 556        return true;
 557}
 558
 559static bool annotate_browser__search_window(struct annotate_browser *browser,
 560                                            int delay_secs)
 561{
 562        if (ui_browser__input_window("Search", "String: ", browser->search_bf,
 563                                     "ENTER: OK, ESC: Cancel",
 564                                     delay_secs * 2) != K_ENTER ||
 565            !*browser->search_bf)
 566                return false;
 567
 568        return true;
 569}
 570
 571static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
 572{
 573        if (annotate_browser__search_window(browser, delay_secs))
 574                return __annotate_browser__search(browser);
 575
 576        return false;
 577}
 578
 579static bool annotate_browser__continue_search(struct annotate_browser *browser,
 580                                              int delay_secs)
 581{
 582        if (!*browser->search_bf)
 583                return annotate_browser__search(browser, delay_secs);
 584
 585        return __annotate_browser__search(browser);
 586}
 587
 588static bool annotate_browser__search_reverse(struct annotate_browser *browser,
 589                                           int delay_secs)
 590{
 591        if (annotate_browser__search_window(browser, delay_secs))
 592                return __annotate_browser__search_reverse(browser);
 593
 594        return false;
 595}
 596
 597static
 598bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
 599                                               int delay_secs)
 600{
 601        if (!*browser->search_bf)
 602                return annotate_browser__search_reverse(browser, delay_secs);
 603
 604        return __annotate_browser__search_reverse(browser);
 605}
 606
 607static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
 608{
 609        struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
 610        struct map_symbol *ms = browser->priv;
 611        struct symbol *sym = ms->sym;
 612        char symbol_dso[SYM_TITLE_MAX_SIZE];
 613
 614        if (ui_browser__show(browser, title, help) < 0)
 615                return -1;
 616
 617        sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
 618
 619        ui_browser__gotorc_title(browser, 0, 0);
 620        ui_browser__set_color(browser, HE_COLORSET_ROOT);
 621        ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
 622        return 0;
 623}
 624
 625static void
 626switch_percent_type(struct annotation_options *opts, bool base)
 627{
 628        switch (opts->percent_type) {
 629        case PERCENT_HITS_LOCAL:
 630                if (base)
 631                        opts->percent_type = PERCENT_PERIOD_LOCAL;
 632                else
 633                        opts->percent_type = PERCENT_HITS_GLOBAL;
 634                break;
 635        case PERCENT_HITS_GLOBAL:
 636                if (base)
 637                        opts->percent_type = PERCENT_PERIOD_GLOBAL;
 638                else
 639                        opts->percent_type = PERCENT_HITS_LOCAL;
 640                break;
 641        case PERCENT_PERIOD_LOCAL:
 642                if (base)
 643                        opts->percent_type = PERCENT_HITS_LOCAL;
 644                else
 645                        opts->percent_type = PERCENT_PERIOD_GLOBAL;
 646                break;
 647        case PERCENT_PERIOD_GLOBAL:
 648                if (base)
 649                        opts->percent_type = PERCENT_HITS_GLOBAL;
 650                else
 651                        opts->percent_type = PERCENT_PERIOD_LOCAL;
 652                break;
 653        default:
 654                WARN_ON(1);
 655        }
 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 hists *hists = evsel__hists(evsel);
 664        struct map_symbol *ms = browser->b.priv;
 665        struct symbol *sym = ms->sym;
 666        struct annotation *notes = symbol__annotation(ms->sym);
 667        const char *help = "Press 'h' for help on key bindings";
 668        int delay_secs = hbt ? hbt->refresh : 0;
 669        char title[256];
 670        int key;
 671
 672        hists__scnprintf_title(hists, title, sizeof(title));
 673        if (annotate_browser__show(&browser->b, title, help) < 0)
 674                return -1;
 675
 676        annotate_browser__calc_percent(browser, evsel);
 677
 678        if (browser->curr_hot) {
 679                annotate_browser__set_rb_top(browser, browser->curr_hot);
 680                browser->b.navkeypressed = false;
 681        }
 682
 683        nd = browser->curr_hot;
 684
 685        while (1) {
 686                key = ui_browser__run(&browser->b, delay_secs);
 687
 688                if (delay_secs != 0) {
 689                        annotate_browser__calc_percent(browser, evsel);
 690                        /*
 691                         * Current line focus got out of the list of most active
 692                         * lines, NULL it so that if TAB|UNTAB is pressed, we
 693                         * move to curr_hot (current hottest line).
 694                         */
 695                        if (nd != NULL && RB_EMPTY_NODE(nd))
 696                                nd = NULL;
 697                }
 698
 699                switch (key) {
 700                case K_TIMER:
 701                        if (hbt)
 702                                hbt->timer(hbt->arg);
 703
 704                        if (delay_secs != 0) {
 705                                symbol__annotate_decay_histogram(sym, evsel->idx);
 706                                hists__scnprintf_title(hists, title, sizeof(title));
 707                                annotate_browser__show(&browser->b, title, help);
 708                        }
 709                        continue;
 710                case K_TAB:
 711                        if (nd != NULL) {
 712                                nd = rb_prev(nd);
 713                                if (nd == NULL)
 714                                        nd = rb_last(&browser->entries);
 715                        } else
 716                                nd = browser->curr_hot;
 717                        break;
 718                case K_UNTAB:
 719                        if (nd != NULL) {
 720                                nd = rb_next(nd);
 721                                if (nd == NULL)
 722                                        nd = rb_first(&browser->entries);
 723                        } else
 724                                nd = browser->curr_hot;
 725                        break;
 726                case K_F1:
 727                case 'h':
 728                        ui_browser__help_window(&browser->b,
 729                "UP/DOWN/PGUP\n"
 730                "PGDN/SPACE    Navigate\n"
 731                "q/ESC/CTRL+C  Exit\n\n"
 732                "ENTER         Go to target\n"
 733                "ESC           Exit\n"
 734                "H             Go to hottest instruction\n"
 735                "TAB/shift+TAB Cycle thru hottest instructions\n"
 736                "j             Toggle showing jump to target arrows\n"
 737                "J             Toggle showing number of jump sources on targets\n"
 738                "n             Search next string\n"
 739                "o             Toggle disassembler output/simplified view\n"
 740                "O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
 741                "s             Toggle source code view\n"
 742                "t             Circulate percent, total period, samples view\n"
 743                "c             Show min/max cycle\n"
 744                "/             Search string\n"
 745                "k             Toggle line numbers\n"
 746                "P             Print to [symbol_name].annotation file.\n"
 747                "r             Run available scripts\n"
 748                "p             Toggle percent type [local/global]\n"
 749                "b             Toggle percent base [period/hits]\n"
 750                "?             Search string backwards\n");
 751                        continue;
 752                case 'r':
 753                        {
 754                                script_browse(NULL, NULL);
 755                                continue;
 756                        }
 757                case 'k':
 758                        notes->options->show_linenr = !notes->options->show_linenr;
 759                        break;
 760                case 'H':
 761                        nd = browser->curr_hot;
 762                        break;
 763                case 's':
 764                        if (annotate_browser__toggle_source(browser))
 765                                ui_helpline__puts(help);
 766                        continue;
 767                case 'o':
 768                        notes->options->use_offset = !notes->options->use_offset;
 769                        annotation__update_column_widths(notes);
 770                        continue;
 771                case 'O':
 772                        if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
 773                                notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
 774                        continue;
 775                case 'j':
 776                        notes->options->jump_arrows = !notes->options->jump_arrows;
 777                        continue;
 778                case 'J':
 779                        notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
 780                        annotation__update_column_widths(notes);
 781                        continue;
 782                case '/':
 783                        if (annotate_browser__search(browser, delay_secs)) {
 784show_help:
 785                                ui_helpline__puts(help);
 786                        }
 787                        continue;
 788                case 'n':
 789                        if (browser->searching_backwards ?
 790                            annotate_browser__continue_search_reverse(browser, delay_secs) :
 791                            annotate_browser__continue_search(browser, delay_secs))
 792                                goto show_help;
 793                        continue;
 794                case '?':
 795                        if (annotate_browser__search_reverse(browser, delay_secs))
 796                                goto show_help;
 797                        continue;
 798                case 'D': {
 799                        static int seq;
 800                        ui_helpline__pop();
 801                        ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
 802                                           seq++, browser->b.nr_entries,
 803                                           browser->b.height,
 804                                           browser->b.index,
 805                                           browser->b.top_idx,
 806                                           notes->nr_asm_entries);
 807                }
 808                        continue;
 809                case K_ENTER:
 810                case K_RIGHT:
 811                {
 812                        struct disasm_line *dl = disasm_line(browser->selection);
 813
 814                        if (browser->selection == NULL)
 815                                ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
 816                        else if (browser->selection->offset == -1)
 817                                ui_helpline__puts("Actions are only available for assembly lines.");
 818                        else if (!dl->ins.ops)
 819                                goto show_sup_ins;
 820                        else if (ins__is_ret(&dl->ins))
 821                                goto out;
 822                        else if (!(annotate_browser__jump(browser, evsel, hbt) ||
 823                                     annotate_browser__callq(browser, evsel, hbt))) {
 824show_sup_ins:
 825                                ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
 826                        }
 827                        continue;
 828                }
 829                case 'P':
 830                        map_symbol__annotation_dump(ms, evsel, browser->opts);
 831                        continue;
 832                case 't':
 833                        if (notes->options->show_total_period) {
 834                                notes->options->show_total_period = false;
 835                                notes->options->show_nr_samples = true;
 836                        } else if (notes->options->show_nr_samples)
 837                                notes->options->show_nr_samples = false;
 838                        else
 839                                notes->options->show_total_period = true;
 840                        annotation__update_column_widths(notes);
 841                        continue;
 842                case 'c':
 843                        if (notes->options->show_minmax_cycle)
 844                                notes->options->show_minmax_cycle = false;
 845                        else
 846                                notes->options->show_minmax_cycle = true;
 847                        annotation__update_column_widths(notes);
 848                        continue;
 849                case 'p':
 850                case 'b':
 851                        switch_percent_type(browser->opts, key == 'b');
 852                        hists__scnprintf_title(hists, title, sizeof(title));
 853                        annotate_browser__show(&browser->b, title, help);
 854                        continue;
 855                case K_LEFT:
 856                case K_ESC:
 857                case 'q':
 858                case CTRL('c'):
 859                        goto out;
 860                default:
 861                        continue;
 862                }
 863
 864                if (nd != NULL)
 865                        annotate_browser__set_rb_top(browser, nd);
 866        }
 867out:
 868        ui_browser__hide(&browser->b);
 869        return key;
 870}
 871
 872int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
 873                             struct hist_browser_timer *hbt,
 874                             struct annotation_options *opts)
 875{
 876        return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
 877}
 878
 879int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
 880                             struct hist_browser_timer *hbt,
 881                             struct annotation_options *opts)
 882{
 883        /* reset abort key so that it can get Ctrl-C as a key */
 884        SLang_reset_tty();
 885        SLang_init_tty(0, 0, 0);
 886
 887        return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
 888}
 889
 890int symbol__tui_annotate(struct symbol *sym, struct map *map,
 891                         struct perf_evsel *evsel,
 892                         struct hist_browser_timer *hbt,
 893                         struct annotation_options *opts)
 894{
 895        struct annotation *notes = symbol__annotation(sym);
 896        struct map_symbol ms = {
 897                .map = map,
 898                .sym = sym,
 899        };
 900        struct annotate_browser browser = {
 901                .b = {
 902                        .refresh = annotate_browser__refresh,
 903                        .seek    = ui_browser__list_head_seek,
 904                        .write   = annotate_browser__write,
 905                        .filter  = disasm_line__filter,
 906                        .extra_title_lines = 1, /* for hists__scnprintf_title() */
 907                        .priv    = &ms,
 908                        .use_navkeypressed = true,
 909                },
 910                .opts = opts,
 911        };
 912        int ret = -1, err;
 913
 914        if (sym == NULL)
 915                return -1;
 916
 917        if (map->dso->annotate_warned)
 918                return -1;
 919
 920        err = symbol__annotate2(sym, map, evsel, opts, &browser.arch);
 921        if (err) {
 922                char msg[BUFSIZ];
 923                symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
 924                ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
 925                goto out_free_offsets;
 926        }
 927
 928        ui_helpline__push("Press ESC to exit");
 929
 930        browser.b.width = notes->max_line_len;
 931        browser.b.nr_entries = notes->nr_entries;
 932        browser.b.entries = &notes->src->source,
 933        browser.b.width += 18; /* Percentage */
 934
 935        if (notes->options->hide_src_code)
 936                ui_browser__init_asm_mode(&browser.b);
 937
 938        ret = annotate_browser__run(&browser, evsel, hbt);
 939
 940        annotated_source__purge(notes->src);
 941
 942out_free_offsets:
 943        zfree(&notes->offsets);
 944        return ret;
 945}
 946