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;
 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        pthread_mutex_unlock(&notes->lock);
 434        symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt, browser->opts);
 435        sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
 436        ui_browser__show_title(&browser->b, title);
 437        return true;
 438}
 439
 440static
 441struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
 442                                          s64 offset, s64 *idx)
 443{
 444        struct annotation *notes = browser__annotation(&browser->b);
 445        struct disasm_line *pos;
 446
 447        *idx = 0;
 448        list_for_each_entry(pos, &notes->src->source, al.node) {
 449                if (pos->al.offset == offset)
 450                        return pos;
 451                if (!annotation_line__filter(&pos->al, notes))
 452                        ++*idx;
 453        }
 454
 455        return NULL;
 456}
 457
 458static bool annotate_browser__jump(struct annotate_browser *browser,
 459                                   struct evsel *evsel,
 460                                   struct hist_browser_timer *hbt)
 461{
 462        struct disasm_line *dl = disasm_line(browser->selection);
 463        u64 offset;
 464        s64 idx;
 465
 466        if (!ins__is_jump(&dl->ins))
 467                return false;
 468
 469        if (dl->ops.target.outside) {
 470                annotate_browser__callq(browser, evsel, hbt);
 471                return true;
 472        }
 473
 474        offset = dl->ops.target.offset;
 475        dl = annotate_browser__find_offset(browser, offset, &idx);
 476        if (dl == NULL) {
 477                ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
 478                return true;
 479        }
 480
 481        annotate_browser__set_top(browser, &dl->al, idx);
 482
 483        return true;
 484}
 485
 486static
 487struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
 488                                          char *s, s64 *idx)
 489{
 490        struct annotation *notes = browser__annotation(&browser->b);
 491        struct annotation_line *al = browser->selection;
 492
 493        *idx = browser->b.index;
 494        list_for_each_entry_continue(al, &notes->src->source, node) {
 495                if (annotation_line__filter(al, notes))
 496                        continue;
 497
 498                ++*idx;
 499
 500                if (al->line && strstr(al->line, s) != NULL)
 501                        return al;
 502        }
 503
 504        return NULL;
 505}
 506
 507static bool __annotate_browser__search(struct annotate_browser *browser)
 508{
 509        struct annotation_line *al;
 510        s64 idx;
 511
 512        al = annotate_browser__find_string(browser, browser->search_bf, &idx);
 513        if (al == NULL) {
 514                ui_helpline__puts("String not found!");
 515                return false;
 516        }
 517
 518        annotate_browser__set_top(browser, al, idx);
 519        browser->searching_backwards = false;
 520        return true;
 521}
 522
 523static
 524struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
 525                                                  char *s, s64 *idx)
 526{
 527        struct annotation *notes = browser__annotation(&browser->b);
 528        struct annotation_line *al = browser->selection;
 529
 530        *idx = browser->b.index;
 531        list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
 532                if (annotation_line__filter(al, notes))
 533                        continue;
 534
 535                --*idx;
 536
 537                if (al->line && strstr(al->line, s) != NULL)
 538                        return al;
 539        }
 540
 541        return NULL;
 542}
 543
 544static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
 545{
 546        struct annotation_line *al;
 547        s64 idx;
 548
 549        al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
 550        if (al == NULL) {
 551                ui_helpline__puts("String not found!");
 552                return false;
 553        }
 554
 555        annotate_browser__set_top(browser, al, idx);
 556        browser->searching_backwards = true;
 557        return true;
 558}
 559
 560static bool annotate_browser__search_window(struct annotate_browser *browser,
 561                                            int delay_secs)
 562{
 563        if (ui_browser__input_window("Search", "String: ", browser->search_bf,
 564                                     "ENTER: OK, ESC: Cancel",
 565                                     delay_secs * 2) != K_ENTER ||
 566            !*browser->search_bf)
 567                return false;
 568
 569        return true;
 570}
 571
 572static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
 573{
 574        if (annotate_browser__search_window(browser, delay_secs))
 575                return __annotate_browser__search(browser);
 576
 577        return false;
 578}
 579
 580static bool annotate_browser__continue_search(struct annotate_browser *browser,
 581                                              int delay_secs)
 582{
 583        if (!*browser->search_bf)
 584                return annotate_browser__search(browser, delay_secs);
 585
 586        return __annotate_browser__search(browser);
 587}
 588
 589static bool annotate_browser__search_reverse(struct annotate_browser *browser,
 590                                           int delay_secs)
 591{
 592        if (annotate_browser__search_window(browser, delay_secs))
 593                return __annotate_browser__search_reverse(browser);
 594
 595        return false;
 596}
 597
 598static
 599bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
 600                                               int delay_secs)
 601{
 602        if (!*browser->search_bf)
 603                return annotate_browser__search_reverse(browser, delay_secs);
 604
 605        return __annotate_browser__search_reverse(browser);
 606}
 607
 608static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
 609{
 610        struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
 611        struct map_symbol *ms = browser->priv;
 612        struct symbol *sym = ms->sym;
 613        char symbol_dso[SYM_TITLE_MAX_SIZE];
 614
 615        if (ui_browser__show(browser, title, help) < 0)
 616                return -1;
 617
 618        sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
 619
 620        ui_browser__gotorc_title(browser, 0, 0);
 621        ui_browser__set_color(browser, HE_COLORSET_ROOT);
 622        ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
 623        return 0;
 624}
 625
 626static void
 627switch_percent_type(struct annotation_options *opts, bool base)
 628{
 629        switch (opts->percent_type) {
 630        case PERCENT_HITS_LOCAL:
 631                if (base)
 632                        opts->percent_type = PERCENT_PERIOD_LOCAL;
 633                else
 634                        opts->percent_type = PERCENT_HITS_GLOBAL;
 635                break;
 636        case PERCENT_HITS_GLOBAL:
 637                if (base)
 638                        opts->percent_type = PERCENT_PERIOD_GLOBAL;
 639                else
 640                        opts->percent_type = PERCENT_HITS_LOCAL;
 641                break;
 642        case PERCENT_PERIOD_LOCAL:
 643                if (base)
 644                        opts->percent_type = PERCENT_HITS_LOCAL;
 645                else
 646                        opts->percent_type = PERCENT_PERIOD_GLOBAL;
 647                break;
 648        case PERCENT_PERIOD_GLOBAL:
 649                if (base)
 650                        opts->percent_type = PERCENT_HITS_GLOBAL;
 651                else
 652                        opts->percent_type = PERCENT_PERIOD_LOCAL;
 653                break;
 654        default:
 655                WARN_ON(1);
 656        }
 657}
 658
 659static int annotate_browser__run(struct annotate_browser *browser,
 660                                 struct evsel *evsel,
 661                                 struct hist_browser_timer *hbt)
 662{
 663        struct rb_node *nd = NULL;
 664        struct hists *hists = evsel__hists(evsel);
 665        struct map_symbol *ms = browser->b.priv;
 666        struct symbol *sym = ms->sym;
 667        struct annotation *notes = symbol__annotation(ms->sym);
 668        const char *help = "Press 'h' for help on key bindings";
 669        int delay_secs = hbt ? hbt->refresh : 0;
 670        char title[256];
 671        int key;
 672
 673        hists__scnprintf_title(hists, title, sizeof(title));
 674        if (annotate_browser__show(&browser->b, title, help) < 0)
 675                return -1;
 676
 677        annotate_browser__calc_percent(browser, evsel);
 678
 679        if (browser->curr_hot) {
 680                annotate_browser__set_rb_top(browser, browser->curr_hot);
 681                browser->b.navkeypressed = false;
 682        }
 683
 684        nd = browser->curr_hot;
 685
 686        while (1) {
 687                key = ui_browser__run(&browser->b, delay_secs);
 688
 689                if (delay_secs != 0) {
 690                        annotate_browser__calc_percent(browser, evsel);
 691                        /*
 692                         * Current line focus got out of the list of most active
 693                         * lines, NULL it so that if TAB|UNTAB is pressed, we
 694                         * move to curr_hot (current hottest line).
 695                         */
 696                        if (nd != NULL && RB_EMPTY_NODE(nd))
 697                                nd = NULL;
 698                }
 699
 700                switch (key) {
 701                case K_TIMER:
 702                        if (hbt)
 703                                hbt->timer(hbt->arg);
 704
 705                        if (delay_secs != 0) {
 706                                symbol__annotate_decay_histogram(sym, evsel->idx);
 707                                hists__scnprintf_title(hists, title, sizeof(title));
 708                                annotate_browser__show(&browser->b, title, help);
 709                        }
 710                        continue;
 711                case K_TAB:
 712                        if (nd != NULL) {
 713                                nd = rb_prev(nd);
 714                                if (nd == NULL)
 715                                        nd = rb_last(&browser->entries);
 716                        } else
 717                                nd = browser->curr_hot;
 718                        break;
 719                case K_UNTAB:
 720                        if (nd != NULL) {
 721                                nd = rb_next(nd);
 722                                if (nd == NULL)
 723                                        nd = rb_first(&browser->entries);
 724                        } else
 725                                nd = browser->curr_hot;
 726                        break;
 727                case K_F1:
 728                case 'h':
 729                        ui_browser__help_window(&browser->b,
 730                "UP/DOWN/PGUP\n"
 731                "PGDN/SPACE    Navigate\n"
 732                "q/ESC/CTRL+C  Exit\n\n"
 733                "ENTER         Go to target\n"
 734                "ESC           Exit\n"
 735                "H             Go to hottest instruction\n"
 736                "TAB/shift+TAB Cycle thru hottest instructions\n"
 737                "j             Toggle showing jump to target arrows\n"
 738                "J             Toggle showing number of jump sources on targets\n"
 739                "n             Search next string\n"
 740                "o             Toggle disassembler output/simplified view\n"
 741                "O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
 742                "s             Toggle source code view\n"
 743                "t             Circulate percent, total period, samples view\n"
 744                "c             Show min/max cycle\n"
 745                "/             Search string\n"
 746                "k             Toggle line numbers\n"
 747                "P             Print to [symbol_name].annotation file.\n"
 748                "r             Run available scripts\n"
 749                "p             Toggle percent type [local/global]\n"
 750                "b             Toggle percent base [period/hits]\n"
 751                "?             Search string backwards\n");
 752                        continue;
 753                case 'r':
 754                        {
 755                                script_browse(NULL, NULL);
 756                                continue;
 757                        }
 758                case 'k':
 759                        notes->options->show_linenr = !notes->options->show_linenr;
 760                        break;
 761                case 'H':
 762                        nd = browser->curr_hot;
 763                        break;
 764                case 's':
 765                        if (annotate_browser__toggle_source(browser))
 766                                ui_helpline__puts(help);
 767                        continue;
 768                case 'o':
 769                        notes->options->use_offset = !notes->options->use_offset;
 770                        annotation__update_column_widths(notes);
 771                        continue;
 772                case 'O':
 773                        if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
 774                                notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
 775                        continue;
 776                case 'j':
 777                        notes->options->jump_arrows = !notes->options->jump_arrows;
 778                        continue;
 779                case 'J':
 780                        notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
 781                        annotation__update_column_widths(notes);
 782                        continue;
 783                case '/':
 784                        if (annotate_browser__search(browser, delay_secs)) {
 785show_help:
 786                                ui_helpline__puts(help);
 787                        }
 788                        continue;
 789                case 'n':
 790                        if (browser->searching_backwards ?
 791                            annotate_browser__continue_search_reverse(browser, delay_secs) :
 792                            annotate_browser__continue_search(browser, delay_secs))
 793                                goto show_help;
 794                        continue;
 795                case '?':
 796                        if (annotate_browser__search_reverse(browser, delay_secs))
 797                                goto show_help;
 798                        continue;
 799                case 'D': {
 800                        static int seq;
 801                        ui_helpline__pop();
 802                        ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
 803                                           seq++, browser->b.nr_entries,
 804                                           browser->b.height,
 805                                           browser->b.index,
 806                                           browser->b.top_idx,
 807                                           notes->nr_asm_entries);
 808                }
 809                        continue;
 810                case K_ENTER:
 811                case K_RIGHT:
 812                {
 813                        struct disasm_line *dl = disasm_line(browser->selection);
 814
 815                        if (browser->selection == NULL)
 816                                ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
 817                        else if (browser->selection->offset == -1)
 818                                ui_helpline__puts("Actions are only available for assembly lines.");
 819                        else if (!dl->ins.ops)
 820                                goto show_sup_ins;
 821                        else if (ins__is_ret(&dl->ins))
 822                                goto out;
 823                        else if (!(annotate_browser__jump(browser, evsel, hbt) ||
 824                                     annotate_browser__callq(browser, evsel, hbt))) {
 825show_sup_ins:
 826                                ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
 827                        }
 828                        continue;
 829                }
 830                case 'P':
 831                        map_symbol__annotation_dump(ms, evsel, browser->opts);
 832                        continue;
 833                case 't':
 834                        if (notes->options->show_total_period) {
 835                                notes->options->show_total_period = false;
 836                                notes->options->show_nr_samples = true;
 837                        } else if (notes->options->show_nr_samples)
 838                                notes->options->show_nr_samples = false;
 839                        else
 840                                notes->options->show_total_period = true;
 841                        annotation__update_column_widths(notes);
 842                        continue;
 843                case 'c':
 844                        if (notes->options->show_minmax_cycle)
 845                                notes->options->show_minmax_cycle = false;
 846                        else
 847                                notes->options->show_minmax_cycle = true;
 848                        annotation__update_column_widths(notes);
 849                        continue;
 850                case 'p':
 851                case 'b':
 852                        switch_percent_type(browser->opts, key == 'b');
 853                        hists__scnprintf_title(hists, title, sizeof(title));
 854                        annotate_browser__show(&browser->b, title, help);
 855                        continue;
 856                case K_LEFT:
 857                case K_ESC:
 858                case 'q':
 859                case CTRL('c'):
 860                        goto out;
 861                default:
 862                        continue;
 863                }
 864
 865                if (nd != NULL)
 866                        annotate_browser__set_rb_top(browser, nd);
 867        }
 868out:
 869        ui_browser__hide(&browser->b);
 870        return key;
 871}
 872
 873int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
 874                             struct hist_browser_timer *hbt,
 875                             struct annotation_options *opts)
 876{
 877        return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
 878}
 879
 880int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
 881                             struct hist_browser_timer *hbt,
 882                             struct annotation_options *opts)
 883{
 884        /* reset abort key so that it can get Ctrl-C as a key */
 885        SLang_reset_tty();
 886        SLang_init_tty(0, 0, 0);
 887
 888        return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
 889}
 890
 891int symbol__tui_annotate(struct symbol *sym, struct map *map,
 892                         struct evsel *evsel,
 893                         struct hist_browser_timer *hbt,
 894                         struct annotation_options *opts)
 895{
 896        struct annotation *notes = symbol__annotation(sym);
 897        struct map_symbol ms = {
 898                .map = map,
 899                .sym = sym,
 900        };
 901        struct annotate_browser browser = {
 902                .b = {
 903                        .refresh = annotate_browser__refresh,
 904                        .seek    = ui_browser__list_head_seek,
 905                        .write   = annotate_browser__write,
 906                        .filter  = disasm_line__filter,
 907                        .extra_title_lines = 1, /* for hists__scnprintf_title() */
 908                        .priv    = &ms,
 909                        .use_navkeypressed = true,
 910                },
 911                .opts = opts,
 912        };
 913        int ret = -1, err;
 914
 915        if (sym == NULL)
 916                return -1;
 917
 918        if (map->dso->annotate_warned)
 919                return -1;
 920
 921        err = symbol__annotate2(sym, map, evsel, opts, &browser.arch);
 922        if (err) {
 923                char msg[BUFSIZ];
 924                symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
 925                ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
 926                goto out_free_offsets;
 927        }
 928
 929        ui_helpline__push("Press ESC to exit");
 930
 931        browser.b.width = notes->max_line_len;
 932        browser.b.nr_entries = notes->nr_entries;
 933        browser.b.entries = &notes->src->source,
 934        browser.b.width += 18; /* Percentage */
 935
 936        if (notes->options->hide_src_code)
 937                ui_browser__init_asm_mode(&browser.b);
 938
 939        ret = annotate_browser__run(&browser, evsel, hbt);
 940
 941        annotated_source__purge(notes->src);
 942
 943out_free_offsets:
 944        zfree(&notes->offsets);
 945        return ret;
 946}
 947