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