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