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