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