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/config.h"
  12#include <inttypes.h>
  13#include <pthread.h>
  14#include <linux/kernel.h>
  15#include <sys/ttydefaults.h>
  16
  17struct disasm_line_samples {
  18        double          percent;
  19        u64             nr;
  20};
  21
  22#define IPC_WIDTH 6
  23#define CYCLES_WIDTH 6
  24
  25struct browser_disasm_line {
  26        struct rb_node                  rb_node;
  27        u32                             idx;
  28        int                             idx_asm;
  29        int                             jump_sources;
  30        /*
  31         * actual length of this array is saved on the nr_events field
  32         * of the struct annotate_browser
  33         */
  34        struct disasm_line_samples      samples[1];
  35};
  36
  37static struct annotate_browser_opt {
  38        bool hide_src_code,
  39             use_offset,
  40             jump_arrows,
  41             show_linenr,
  42             show_nr_jumps,
  43             show_total_period;
  44} annotate_browser__opts = {
  45        .use_offset     = true,
  46        .jump_arrows    = true,
  47};
  48
  49struct arch;
  50
  51struct annotate_browser {
  52        struct ui_browser b;
  53        struct rb_root    entries;
  54        struct rb_node    *curr_hot;
  55        struct disasm_line  *selection;
  56        struct disasm_line  **offsets;
  57        struct arch         *arch;
  58        int                 nr_events;
  59        u64                 start;
  60        int                 nr_asm_entries;
  61        int                 nr_entries;
  62        int                 max_jump_sources;
  63        int                 nr_jumps;
  64        bool                searching_backwards;
  65        bool                have_cycles;
  66        u8                  addr_width;
  67        u8                  jumps_width;
  68        u8                  target_width;
  69        u8                  min_addr_width;
  70        u8                  max_addr_width;
  71        char                search_bf[128];
  72};
  73
  74static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
  75{
  76        return (struct browser_disasm_line *)(dl + 1);
  77}
  78
  79static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
  80                                void *entry)
  81{
  82        if (annotate_browser__opts.hide_src_code) {
  83                struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
  84                return dl->offset == -1;
  85        }
  86
  87        return false;
  88}
  89
  90static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
  91                                                 int nr, bool current)
  92{
  93        if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
  94                return HE_COLORSET_SELECTED;
  95        if (nr == browser->max_jump_sources)
  96                return HE_COLORSET_TOP;
  97        if (nr > 1)
  98                return HE_COLORSET_MEDIUM;
  99        return HE_COLORSET_NORMAL;
 100}
 101
 102static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
 103                                                     int nr, bool current)
 104{
 105         int color = annotate_browser__jumps_percent_color(browser, nr, current);
 106         return ui_browser__set_color(&browser->b, color);
 107}
 108
 109static int annotate_browser__pcnt_width(struct annotate_browser *ab)
 110{
 111        int w = 7 * ab->nr_events;
 112
 113        if (ab->have_cycles)
 114                w += IPC_WIDTH + CYCLES_WIDTH;
 115        return w;
 116}
 117
 118static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
 119{
 120        struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
 121        struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
 122        struct browser_disasm_line *bdl = disasm_line__browser(dl);
 123        bool current_entry = ui_browser__is_current_entry(browser, row);
 124        bool change_color = (!annotate_browser__opts.hide_src_code &&
 125                             (!current_entry || (browser->use_navkeypressed &&
 126                                                 !browser->navkeypressed)));
 127        int width = browser->width, printed;
 128        int i, pcnt_width = annotate_browser__pcnt_width(ab);
 129        double percent_max = 0.0;
 130        char bf[256];
 131        bool show_title = false;
 132
 133        for (i = 0; i < ab->nr_events; i++) {
 134                if (bdl->samples[i].percent > percent_max)
 135                        percent_max = bdl->samples[i].percent;
 136        }
 137
 138        if ((row == 0) && (dl->offset == -1 || percent_max == 0.0)) {
 139                if (ab->have_cycles) {
 140                        if (dl->ipc == 0.0 && dl->cycles == 0)
 141                                show_title = true;
 142                } else
 143                        show_title = true;
 144        }
 145
 146        if (dl->offset != -1 && percent_max != 0.0) {
 147                for (i = 0; i < ab->nr_events; i++) {
 148                        ui_browser__set_percent_color(browser,
 149                                                bdl->samples[i].percent,
 150                                                current_entry);
 151                        if (annotate_browser__opts.show_total_period) {
 152                                ui_browser__printf(browser, "%6" PRIu64 " ",
 153                                                   bdl->samples[i].nr);
 154                        } else {
 155                                ui_browser__printf(browser, "%6.2f ",
 156                                                   bdl->samples[i].percent);
 157                        }
 158                }
 159        } else {
 160                ui_browser__set_percent_color(browser, 0, current_entry);
 161
 162                if (!show_title)
 163                        ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
 164                else
 165                        ui_browser__printf(browser, "%*s", 7, "Percent");
 166        }
 167        if (ab->have_cycles) {
 168                if (dl->ipc)
 169                        ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
 170                else if (!show_title)
 171                        ui_browser__write_nstring(browser, " ", IPC_WIDTH);
 172                else
 173                        ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");
 174
 175                if (dl->cycles)
 176                        ui_browser__printf(browser, "%*" PRIu64 " ",
 177                                           CYCLES_WIDTH - 1, dl->cycles);
 178                else if (!show_title)
 179                        ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
 180                else
 181                        ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
 182        }
 183
 184        SLsmg_write_char(' ');
 185
 186        /* The scroll bar isn't being used */
 187        if (!browser->navkeypressed)
 188                width += 1;
 189
 190        if (!*dl->line)
 191                ui_browser__write_nstring(browser, " ", width - pcnt_width);
 192        else if (dl->offset == -1) {
 193                if (dl->line_nr && annotate_browser__opts.show_linenr)
 194                        printed = scnprintf(bf, sizeof(bf), "%-*d ",
 195                                        ab->addr_width + 1, dl->line_nr);
 196                else
 197                        printed = scnprintf(bf, sizeof(bf), "%*s  ",
 198                                    ab->addr_width, " ");
 199                ui_browser__write_nstring(browser, bf, printed);
 200                ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1);
 201        } else {
 202                u64 addr = dl->offset;
 203                int color = -1;
 204
 205                if (!annotate_browser__opts.use_offset)
 206                        addr += ab->start;
 207
 208                if (!annotate_browser__opts.use_offset) {
 209                        printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
 210                } else {
 211                        if (bdl->jump_sources) {
 212                                if (annotate_browser__opts.show_nr_jumps) {
 213                                        int prev;
 214                                        printed = scnprintf(bf, sizeof(bf), "%*d ",
 215                                                            ab->jumps_width,
 216                                                            bdl->jump_sources);
 217                                        prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
 218                                                                                         current_entry);
 219                                        ui_browser__write_nstring(browser, bf, printed);
 220                                        ui_browser__set_color(browser, prev);
 221                                }
 222
 223                                printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
 224                                                    ab->target_width, addr);
 225                        } else {
 226                                printed = scnprintf(bf, sizeof(bf), "%*s  ",
 227                                                    ab->addr_width, " ");
 228                        }
 229                }
 230
 231                if (change_color)
 232                        color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
 233                ui_browser__write_nstring(browser, bf, printed);
 234                if (change_color)
 235                        ui_browser__set_color(browser, color);
 236                if (dl->ins.ops && dl->ins.ops->scnprintf) {
 237                        if (ins__is_jump(&dl->ins)) {
 238                                bool fwd = dl->ops.target.offset > dl->offset;
 239
 240                                ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
 241                                                                    SLSMG_UARROW_CHAR);
 242                                SLsmg_write_char(' ');
 243                        } else if (ins__is_call(&dl->ins)) {
 244                                ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
 245                                SLsmg_write_char(' ');
 246                        } else if (ins__is_ret(&dl->ins)) {
 247                                ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
 248                                SLsmg_write_char(' ');
 249                        } else {
 250                                ui_browser__write_nstring(browser, " ", 2);
 251                        }
 252                } else {
 253                        ui_browser__write_nstring(browser, " ", 2);
 254                }
 255
 256                disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
 257                ui_browser__write_nstring(browser, bf, width - pcnt_width - 3 - printed);
 258        }
 259
 260        if (current_entry)
 261                ab->selection = dl;
 262}
 263
 264static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
 265{
 266        if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
 267            || !disasm_line__has_offset(dl)
 268            || dl->ops.target.offset < 0
 269            || dl->ops.target.offset >= (s64)symbol__size(sym))
 270                return false;
 271
 272        return true;
 273}
 274
 275static void annotate_browser__draw_current_jump(struct ui_browser *browser)
 276{
 277        struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
 278        struct disasm_line *cursor = ab->selection, *target;
 279        struct browser_disasm_line *btarget, *bcursor;
 280        unsigned int from, to;
 281        struct map_symbol *ms = ab->b.priv;
 282        struct symbol *sym = ms->sym;
 283        u8 pcnt_width = annotate_browser__pcnt_width(ab);
 284
 285        /* PLT symbols contain external offsets */
 286        if (strstr(sym->name, "@plt"))
 287                return;
 288
 289        if (!disasm_line__is_valid_jump(cursor, sym))
 290                return;
 291
 292        target = ab->offsets[cursor->ops.target.offset];
 293        if (!target)
 294                return;
 295
 296        bcursor = disasm_line__browser(cursor);
 297        btarget = disasm_line__browser(target);
 298
 299        if (annotate_browser__opts.hide_src_code) {
 300                from = bcursor->idx_asm;
 301                to = btarget->idx_asm;
 302        } else {
 303                from = (u64)bcursor->idx;
 304                to = (u64)btarget->idx;
 305        }
 306
 307        ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
 308        __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
 309                                 from, to);
 310}
 311
 312static unsigned int annotate_browser__refresh(struct ui_browser *browser)
 313{
 314        struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
 315        int ret = ui_browser__list_head_refresh(browser);
 316        int pcnt_width = annotate_browser__pcnt_width(ab);
 317
 318        if (annotate_browser__opts.jump_arrows)
 319                annotate_browser__draw_current_jump(browser);
 320
 321        ui_browser__set_color(browser, HE_COLORSET_NORMAL);
 322        __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
 323        return ret;
 324}
 325
 326static int disasm__cmp(struct browser_disasm_line *a,
 327                       struct browser_disasm_line *b, int nr_pcnt)
 328{
 329        int i;
 330
 331        for (i = 0; i < nr_pcnt; i++) {
 332                if (a->samples[i].percent == b->samples[i].percent)
 333                        continue;
 334                return a->samples[i].percent < b->samples[i].percent;
 335        }
 336        return 0;
 337}
 338
 339static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
 340                                   int nr_events)
 341{
 342        struct rb_node **p = &root->rb_node;
 343        struct rb_node *parent = NULL;
 344        struct browser_disasm_line *l;
 345
 346        while (*p != NULL) {
 347                parent = *p;
 348                l = rb_entry(parent, struct browser_disasm_line, rb_node);
 349
 350                if (disasm__cmp(bdl, l, nr_events))
 351                        p = &(*p)->rb_left;
 352                else
 353                        p = &(*p)->rb_right;
 354        }
 355        rb_link_node(&bdl->rb_node, parent, p);
 356        rb_insert_color(&bdl->rb_node, root);
 357}
 358
 359static void annotate_browser__set_top(struct annotate_browser *browser,
 360                                      struct disasm_line *pos, u32 idx)
 361{
 362        unsigned back;
 363
 364        ui_browser__refresh_dimensions(&browser->b);
 365        back = browser->b.height / 2;
 366        browser->b.top_idx = browser->b.index = idx;
 367
 368        while (browser->b.top_idx != 0 && back != 0) {
 369                pos = list_entry(pos->node.prev, struct disasm_line, node);
 370
 371                if (disasm_line__filter(&browser->b, &pos->node))
 372                        continue;
 373
 374                --browser->b.top_idx;
 375                --back;
 376        }
 377
 378        browser->b.top = pos;
 379        browser->b.navkeypressed = true;
 380}
 381
 382static void annotate_browser__set_rb_top(struct annotate_browser *browser,
 383                                         struct rb_node *nd)
 384{
 385        struct browser_disasm_line *bpos;
 386        struct disasm_line *pos;
 387        u32 idx;
 388
 389        bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
 390        pos = ((struct disasm_line *)bpos) - 1;
 391        idx = bpos->idx;
 392        if (annotate_browser__opts.hide_src_code)
 393                idx = bpos->idx_asm;
 394        annotate_browser__set_top(browser, pos, idx);
 395        browser->curr_hot = nd;
 396}
 397
 398static void annotate_browser__calc_percent(struct annotate_browser *browser,
 399                                           struct perf_evsel *evsel)
 400{
 401        struct map_symbol *ms = browser->b.priv;
 402        struct symbol *sym = ms->sym;
 403        struct annotation *notes = symbol__annotation(sym);
 404        struct disasm_line *pos, *next;
 405        s64 len = symbol__size(sym);
 406
 407        browser->entries = RB_ROOT;
 408
 409        pthread_mutex_lock(&notes->lock);
 410
 411        list_for_each_entry(pos, &notes->src->source, node) {
 412                struct browser_disasm_line *bpos = disasm_line__browser(pos);
 413                const char *path = NULL;
 414                double max_percent = 0.0;
 415                int i;
 416
 417                if (pos->offset == -1) {
 418                        RB_CLEAR_NODE(&bpos->rb_node);
 419                        continue;
 420                }
 421
 422                next = disasm__get_next_ip_line(&notes->src->source, pos);
 423
 424                for (i = 0; i < browser->nr_events; i++) {
 425                        u64 nr_samples;
 426
 427                        bpos->samples[i].percent = disasm__calc_percent(notes,
 428                                                evsel->idx + i,
 429                                                pos->offset,
 430                                                next ? next->offset : len,
 431                                                &path, &nr_samples);
 432                        bpos->samples[i].nr = nr_samples;
 433
 434                        if (max_percent < bpos->samples[i].percent)
 435                                max_percent = bpos->samples[i].percent;
 436                }
 437
 438                if (max_percent < 0.01 && pos->ipc == 0) {
 439                        RB_CLEAR_NODE(&bpos->rb_node);
 440                        continue;
 441                }
 442                disasm_rb_tree__insert(&browser->entries, bpos,
 443                                       browser->nr_events);
 444        }
 445        pthread_mutex_unlock(&notes->lock);
 446
 447        browser->curr_hot = rb_last(&browser->entries);
 448}
 449
 450static bool annotate_browser__toggle_source(struct annotate_browser *browser)
 451{
 452        struct disasm_line *dl;
 453        struct browser_disasm_line *bdl;
 454        off_t offset = browser->b.index - browser->b.top_idx;
 455
 456        browser->b.seek(&browser->b, offset, SEEK_CUR);
 457        dl = list_entry(browser->b.top, struct disasm_line, node);
 458        bdl = disasm_line__browser(dl);
 459
 460        if (annotate_browser__opts.hide_src_code) {
 461                if (bdl->idx_asm < offset)
 462                        offset = bdl->idx;
 463
 464                browser->b.nr_entries = browser->nr_entries;
 465                annotate_browser__opts.hide_src_code = false;
 466                browser->b.seek(&browser->b, -offset, SEEK_CUR);
 467                browser->b.top_idx = bdl->idx - offset;
 468                browser->b.index = bdl->idx;
 469        } else {
 470                if (bdl->idx_asm < 0) {
 471                        ui_helpline__puts("Only available for assembly lines.");
 472                        browser->b.seek(&browser->b, -offset, SEEK_CUR);
 473                        return false;
 474                }
 475
 476                if (bdl->idx_asm < offset)
 477                        offset = bdl->idx_asm;
 478
 479                browser->b.nr_entries = browser->nr_asm_entries;
 480                annotate_browser__opts.hide_src_code = true;
 481                browser->b.seek(&browser->b, -offset, SEEK_CUR);
 482                browser->b.top_idx = bdl->idx_asm - offset;
 483                browser->b.index = bdl->idx_asm;
 484        }
 485
 486        return true;
 487}
 488
 489static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
 490{
 491        ui_browser__reset_index(&browser->b);
 492        browser->b.nr_entries = browser->nr_asm_entries;
 493}
 494
 495#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
 496
 497static int sym_title(struct symbol *sym, struct map *map, char *title,
 498                     size_t sz)
 499{
 500        return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
 501}
 502
 503static bool annotate_browser__callq(struct annotate_browser *browser,
 504                                    struct perf_evsel *evsel,
 505                                    struct hist_browser_timer *hbt)
 506{
 507        struct map_symbol *ms = browser->b.priv;
 508        struct disasm_line *dl = browser->selection;
 509        struct annotation *notes;
 510        struct addr_map_symbol target = {
 511                .map = ms->map,
 512                .addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
 513        };
 514        char title[SYM_TITLE_MAX_SIZE];
 515
 516        if (!ins__is_call(&dl->ins))
 517                return false;
 518
 519        if (map_groups__find_ams(&target) ||
 520            map__rip_2objdump(target.map, target.map->map_ip(target.map,
 521                                                             target.addr)) !=
 522            dl->ops.target.addr) {
 523                ui_helpline__puts("The called function was not found.");
 524                return true;
 525        }
 526
 527        notes = symbol__annotation(target.sym);
 528        pthread_mutex_lock(&notes->lock);
 529
 530        if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
 531                pthread_mutex_unlock(&notes->lock);
 532                ui__warning("Not enough memory for annotating '%s' symbol!\n",
 533                            target.sym->name);
 534                return true;
 535        }
 536
 537        pthread_mutex_unlock(&notes->lock);
 538        symbol__tui_annotate(target.sym, target.map, evsel, hbt);
 539        sym_title(ms->sym, ms->map, title, sizeof(title));
 540        ui_browser__show_title(&browser->b, title);
 541        return true;
 542}
 543
 544static
 545struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
 546                                          s64 offset, s64 *idx)
 547{
 548        struct map_symbol *ms = browser->b.priv;
 549        struct symbol *sym = ms->sym;
 550        struct annotation *notes = symbol__annotation(sym);
 551        struct disasm_line *pos;
 552
 553        *idx = 0;
 554        list_for_each_entry(pos, &notes->src->source, node) {
 555                if (pos->offset == offset)
 556                        return pos;
 557                if (!disasm_line__filter(&browser->b, &pos->node))
 558                        ++*idx;
 559        }
 560
 561        return NULL;
 562}
 563
 564static bool annotate_browser__jump(struct annotate_browser *browser)
 565{
 566        struct disasm_line *dl = browser->selection;
 567        u64 offset;
 568        s64 idx;
 569
 570        if (!ins__is_jump(&dl->ins))
 571                return false;
 572
 573        offset = dl->ops.target.offset;
 574        dl = annotate_browser__find_offset(browser, offset, &idx);
 575        if (dl == NULL) {
 576                ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
 577                return true;
 578        }
 579
 580        annotate_browser__set_top(browser, dl, idx);
 581
 582        return true;
 583}
 584
 585static
 586struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
 587                                          char *s, s64 *idx)
 588{
 589        struct map_symbol *ms = browser->b.priv;
 590        struct symbol *sym = ms->sym;
 591        struct annotation *notes = symbol__annotation(sym);
 592        struct disasm_line *pos = browser->selection;
 593
 594        *idx = browser->b.index;
 595        list_for_each_entry_continue(pos, &notes->src->source, node) {
 596                if (disasm_line__filter(&browser->b, &pos->node))
 597                        continue;
 598
 599                ++*idx;
 600
 601                if (pos->line && strstr(pos->line, s) != NULL)
 602                        return pos;
 603        }
 604
 605        return NULL;
 606}
 607
 608static bool __annotate_browser__search(struct annotate_browser *browser)
 609{
 610        struct disasm_line *dl;
 611        s64 idx;
 612
 613        dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
 614        if (dl == NULL) {
 615                ui_helpline__puts("String not found!");
 616                return false;
 617        }
 618
 619        annotate_browser__set_top(browser, dl, idx);
 620        browser->searching_backwards = false;
 621        return true;
 622}
 623
 624static
 625struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
 626                                                  char *s, s64 *idx)
 627{
 628        struct map_symbol *ms = browser->b.priv;
 629        struct symbol *sym = ms->sym;
 630        struct annotation *notes = symbol__annotation(sym);
 631        struct disasm_line *pos = browser->selection;
 632
 633        *idx = browser->b.index;
 634        list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
 635                if (disasm_line__filter(&browser->b, &pos->node))
 636                        continue;
 637
 638                --*idx;
 639
 640                if (pos->line && strstr(pos->line, s) != NULL)
 641                        return pos;
 642        }
 643
 644        return NULL;
 645}
 646
 647static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
 648{
 649        struct disasm_line *dl;
 650        s64 idx;
 651
 652        dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
 653        if (dl == NULL) {
 654                ui_helpline__puts("String not found!");
 655                return false;
 656        }
 657
 658        annotate_browser__set_top(browser, dl, idx);
 659        browser->searching_backwards = true;
 660        return true;
 661}
 662
 663static bool annotate_browser__search_window(struct annotate_browser *browser,
 664                                            int delay_secs)
 665{
 666        if (ui_browser__input_window("Search", "String: ", browser->search_bf,
 667                                     "ENTER: OK, ESC: Cancel",
 668                                     delay_secs * 2) != K_ENTER ||
 669            !*browser->search_bf)
 670                return false;
 671
 672        return true;
 673}
 674
 675static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
 676{
 677        if (annotate_browser__search_window(browser, delay_secs))
 678                return __annotate_browser__search(browser);
 679
 680        return false;
 681}
 682
 683static bool annotate_browser__continue_search(struct annotate_browser *browser,
 684                                              int delay_secs)
 685{
 686        if (!*browser->search_bf)
 687                return annotate_browser__search(browser, delay_secs);
 688
 689        return __annotate_browser__search(browser);
 690}
 691
 692static bool annotate_browser__search_reverse(struct annotate_browser *browser,
 693                                           int delay_secs)
 694{
 695        if (annotate_browser__search_window(browser, delay_secs))
 696                return __annotate_browser__search_reverse(browser);
 697
 698        return false;
 699}
 700
 701static
 702bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
 703                                               int delay_secs)
 704{
 705        if (!*browser->search_bf)
 706                return annotate_browser__search_reverse(browser, delay_secs);
 707
 708        return __annotate_browser__search_reverse(browser);
 709}
 710
 711static void annotate_browser__update_addr_width(struct annotate_browser *browser)
 712{
 713        if (annotate_browser__opts.use_offset)
 714                browser->target_width = browser->min_addr_width;
 715        else
 716                browser->target_width = browser->max_addr_width;
 717
 718        browser->addr_width = browser->target_width;
 719
 720        if (annotate_browser__opts.show_nr_jumps)
 721                browser->addr_width += browser->jumps_width + 1;
 722}
 723
 724static int annotate_browser__run(struct annotate_browser *browser,
 725                                 struct perf_evsel *evsel,
 726                                 struct hist_browser_timer *hbt)
 727{
 728        struct rb_node *nd = NULL;
 729        struct map_symbol *ms = browser->b.priv;
 730        struct symbol *sym = ms->sym;
 731        const char *help = "Press 'h' for help on key bindings";
 732        int delay_secs = hbt ? hbt->refresh : 0;
 733        int key;
 734        char title[SYM_TITLE_MAX_SIZE];
 735
 736        sym_title(sym, ms->map, title, sizeof(title));
 737        if (ui_browser__show(&browser->b, title, help) < 0)
 738                return -1;
 739
 740        annotate_browser__calc_percent(browser, evsel);
 741
 742        if (browser->curr_hot) {
 743                annotate_browser__set_rb_top(browser, browser->curr_hot);
 744                browser->b.navkeypressed = false;
 745        }
 746
 747        nd = browser->curr_hot;
 748
 749        while (1) {
 750                key = ui_browser__run(&browser->b, delay_secs);
 751
 752                if (delay_secs != 0) {
 753                        annotate_browser__calc_percent(browser, evsel);
 754                        /*
 755                         * Current line focus got out of the list of most active
 756                         * lines, NULL it so that if TAB|UNTAB is pressed, we
 757                         * move to curr_hot (current hottest line).
 758                         */
 759                        if (nd != NULL && RB_EMPTY_NODE(nd))
 760                                nd = NULL;
 761                }
 762
 763                switch (key) {
 764                case K_TIMER:
 765                        if (hbt)
 766                                hbt->timer(hbt->arg);
 767
 768                        if (delay_secs != 0)
 769                                symbol__annotate_decay_histogram(sym, evsel->idx);
 770                        continue;
 771                case K_TAB:
 772                        if (nd != NULL) {
 773                                nd = rb_prev(nd);
 774                                if (nd == NULL)
 775                                        nd = rb_last(&browser->entries);
 776                        } else
 777                                nd = browser->curr_hot;
 778                        break;
 779                case K_UNTAB:
 780                        if (nd != NULL) {
 781                                nd = rb_next(nd);
 782                                if (nd == NULL)
 783                                        nd = rb_first(&browser->entries);
 784                        } else
 785                                nd = browser->curr_hot;
 786                        break;
 787                case K_F1:
 788                case 'h':
 789                        ui_browser__help_window(&browser->b,
 790                "UP/DOWN/PGUP\n"
 791                "PGDN/SPACE    Navigate\n"
 792                "q/ESC/CTRL+C  Exit\n\n"
 793                "ENTER         Go to target\n"
 794                "ESC           Exit\n"
 795                "H             Cycle thru hottest instructions\n"
 796                "j             Toggle showing jump to target arrows\n"
 797                "J             Toggle showing number of jump sources on targets\n"
 798                "n             Search next string\n"
 799                "o             Toggle disassembler output/simplified view\n"
 800                "s             Toggle source code view\n"
 801                "t             Toggle total period view\n"
 802                "/             Search string\n"
 803                "k             Toggle line numbers\n"
 804                "r             Run available scripts\n"
 805                "?             Search string backwards\n");
 806                        continue;
 807                case 'r':
 808                        {
 809                                script_browse(NULL);
 810                                continue;
 811                        }
 812                case 'k':
 813                        annotate_browser__opts.show_linenr =
 814                                !annotate_browser__opts.show_linenr;
 815                        break;
 816                case 'H':
 817                        nd = browser->curr_hot;
 818                        break;
 819                case 's':
 820                        if (annotate_browser__toggle_source(browser))
 821                                ui_helpline__puts(help);
 822                        continue;
 823                case 'o':
 824                        annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
 825                        annotate_browser__update_addr_width(browser);
 826                        continue;
 827                case 'j':
 828                        annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
 829                        continue;
 830                case 'J':
 831                        annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
 832                        annotate_browser__update_addr_width(browser);
 833                        continue;
 834                case '/':
 835                        if (annotate_browser__search(browser, delay_secs)) {
 836show_help:
 837                                ui_helpline__puts(help);
 838                        }
 839                        continue;
 840                case 'n':
 841                        if (browser->searching_backwards ?
 842                            annotate_browser__continue_search_reverse(browser, delay_secs) :
 843                            annotate_browser__continue_search(browser, delay_secs))
 844                                goto show_help;
 845                        continue;
 846                case '?':
 847                        if (annotate_browser__search_reverse(browser, delay_secs))
 848                                goto show_help;
 849                        continue;
 850                case 'D': {
 851                        static int seq;
 852                        ui_helpline__pop();
 853                        ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
 854                                           seq++, browser->b.nr_entries,
 855                                           browser->b.height,
 856                                           browser->b.index,
 857                                           browser->b.top_idx,
 858                                           browser->nr_asm_entries);
 859                }
 860                        continue;
 861                case K_ENTER:
 862                case K_RIGHT:
 863                        if (browser->selection == NULL)
 864                                ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
 865                        else if (browser->selection->offset == -1)
 866                                ui_helpline__puts("Actions are only available for assembly lines.");
 867                        else if (!browser->selection->ins.ops)
 868                                goto show_sup_ins;
 869                        else if (ins__is_ret(&browser->selection->ins))
 870                                goto out;
 871                        else if (!(annotate_browser__jump(browser) ||
 872                                     annotate_browser__callq(browser, evsel, hbt))) {
 873show_sup_ins:
 874                                ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
 875                        }
 876                        continue;
 877                case 't':
 878                        annotate_browser__opts.show_total_period =
 879                          !annotate_browser__opts.show_total_period;
 880                        annotate_browser__update_addr_width(browser);
 881                        continue;
 882                case K_LEFT:
 883                case K_ESC:
 884                case 'q':
 885                case CTRL('c'):
 886                        goto out;
 887                default:
 888                        continue;
 889                }
 890
 891                if (nd != NULL)
 892                        annotate_browser__set_rb_top(browser, nd);
 893        }
 894out:
 895        ui_browser__hide(&browser->b);
 896        return key;
 897}
 898
 899int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
 900                             struct hist_browser_timer *hbt)
 901{
 902        /* Set default value for show_total_period.  */
 903        annotate_browser__opts.show_total_period =
 904          symbol_conf.show_total_period;
 905
 906        return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
 907}
 908
 909int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
 910                             struct hist_browser_timer *hbt)
 911{
 912        /* reset abort key so that it can get Ctrl-C as a key */
 913        SLang_reset_tty();
 914        SLang_init_tty(0, 0, 0);
 915
 916        return map_symbol__tui_annotate(&he->ms, evsel, hbt);
 917}
 918
 919
 920static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
 921{
 922        unsigned n_insn = 0;
 923        u64 offset;
 924
 925        for (offset = start; offset <= end; offset++) {
 926                if (browser->offsets[offset])
 927                        n_insn++;
 928        }
 929        return n_insn;
 930}
 931
 932static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
 933                           struct cyc_hist *ch)
 934{
 935        unsigned n_insn;
 936        u64 offset;
 937
 938        n_insn = count_insn(browser, start, end);
 939        if (n_insn && ch->num && ch->cycles) {
 940                float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
 941
 942                /* Hide data when there are too many overlaps. */
 943                if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
 944                        return;
 945
 946                for (offset = start; offset <= end; offset++) {
 947                        struct disasm_line *dl = browser->offsets[offset];
 948
 949                        if (dl)
 950                                dl->ipc = ipc;
 951                }
 952        }
 953}
 954
 955/*
 956 * This should probably be in util/annotate.c to share with the tty
 957 * annotate, but right now we need the per byte offsets arrays,
 958 * which are only here.
 959 */
 960static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
 961                           struct symbol *sym)
 962{
 963        u64 offset;
 964        struct annotation *notes = symbol__annotation(sym);
 965
 966        if (!notes->src || !notes->src->cycles_hist)
 967                return;
 968
 969        pthread_mutex_lock(&notes->lock);
 970        for (offset = 0; offset < size; ++offset) {
 971                struct cyc_hist *ch;
 972
 973                ch = &notes->src->cycles_hist[offset];
 974                if (ch && ch->cycles) {
 975                        struct disasm_line *dl;
 976
 977                        if (ch->have_start)
 978                                count_and_fill(browser, ch->start, offset, ch);
 979                        dl = browser->offsets[offset];
 980                        if (dl && ch->num_aggr)
 981                                dl->cycles = ch->cycles_aggr / ch->num_aggr;
 982                        browser->have_cycles = true;
 983                }
 984        }
 985        pthread_mutex_unlock(&notes->lock);
 986}
 987
 988static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
 989                                                size_t size)
 990{
 991        u64 offset;
 992        struct map_symbol *ms = browser->b.priv;
 993        struct symbol *sym = ms->sym;
 994
 995        /* PLT symbols contain external offsets */
 996        if (strstr(sym->name, "@plt"))
 997                return;
 998
 999        for (offset = 0; offset < size; ++offset) {
1000                struct disasm_line *dl = browser->offsets[offset], *dlt;
1001                struct browser_disasm_line *bdlt;
1002
1003                if (!disasm_line__is_valid_jump(dl, sym))
1004                        continue;
1005
1006                dlt = browser->offsets[dl->ops.target.offset];
1007                /*
1008                 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
1009                 * have to adjust to the previous offset?
1010                 */
1011                if (dlt == NULL)
1012                        continue;
1013
1014                bdlt = disasm_line__browser(dlt);
1015                if (++bdlt->jump_sources > browser->max_jump_sources)
1016                        browser->max_jump_sources = bdlt->jump_sources;
1017
1018                ++browser->nr_jumps;
1019        }
1020}
1021
1022static inline int width_jumps(int n)
1023{
1024        if (n >= 100)
1025                return 5;
1026        if (n / 10)
1027                return 2;
1028        return 1;
1029}
1030
1031int symbol__tui_annotate(struct symbol *sym, struct map *map,
1032                         struct perf_evsel *evsel,
1033                         struct hist_browser_timer *hbt)
1034{
1035        struct disasm_line *pos, *n;
1036        struct annotation *notes;
1037        size_t size;
1038        struct map_symbol ms = {
1039                .map = map,
1040                .sym = sym,
1041        };
1042        struct annotate_browser browser = {
1043                .b = {
1044                        .refresh = annotate_browser__refresh,
1045                        .seek    = ui_browser__list_head_seek,
1046                        .write   = annotate_browser__write,
1047                        .filter  = disasm_line__filter,
1048                        .priv    = &ms,
1049                        .use_navkeypressed = true,
1050                },
1051        };
1052        int ret = -1, err;
1053        int nr_pcnt = 1;
1054        size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1055
1056        if (sym == NULL)
1057                return -1;
1058
1059        size = symbol__size(sym);
1060
1061        if (map->dso->annotate_warned)
1062                return -1;
1063
1064        browser.offsets = zalloc(size * sizeof(struct disasm_line *));
1065        if (browser.offsets == NULL) {
1066                ui__error("Not enough memory!");
1067                return -1;
1068        }
1069
1070        if (perf_evsel__is_group_event(evsel)) {
1071                nr_pcnt = evsel->nr_members;
1072                sizeof_bdl += sizeof(struct disasm_line_samples) *
1073                  (nr_pcnt - 1);
1074        }
1075
1076        err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
1077                                  sizeof_bdl, &browser.arch);
1078        if (err) {
1079                char msg[BUFSIZ];
1080                symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
1081                ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1082                goto out_free_offsets;
1083        }
1084
1085        ui_helpline__push("Press ESC to exit");
1086
1087        notes = symbol__annotation(sym);
1088        browser.start = map__rip_2objdump(map, sym->start);
1089
1090        list_for_each_entry(pos, &notes->src->source, node) {
1091                struct browser_disasm_line *bpos;
1092                size_t line_len = strlen(pos->line);
1093
1094                if (browser.b.width < line_len)
1095                        browser.b.width = line_len;
1096                bpos = disasm_line__browser(pos);
1097                bpos->idx = browser.nr_entries++;
1098                if (pos->offset != -1) {
1099                        bpos->idx_asm = browser.nr_asm_entries++;
1100                        /*
1101                         * FIXME: short term bandaid to cope with assembly
1102                         * routines that comes with labels in the same column
1103                         * as the address in objdump, sigh.
1104                         *
1105                         * E.g. copy_user_generic_unrolled
1106                         */
1107                        if (pos->offset < (s64)size)
1108                                browser.offsets[pos->offset] = pos;
1109                } else
1110                        bpos->idx_asm = -1;
1111        }
1112
1113        annotate_browser__mark_jump_targets(&browser, size);
1114        annotate__compute_ipc(&browser, size, sym);
1115
1116        browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1117        browser.max_addr_width = hex_width(sym->end);
1118        browser.jumps_width = width_jumps(browser.max_jump_sources);
1119        browser.nr_events = nr_pcnt;
1120        browser.b.nr_entries = browser.nr_entries;
1121        browser.b.entries = &notes->src->source,
1122        browser.b.width += 18; /* Percentage */
1123
1124        if (annotate_browser__opts.hide_src_code)
1125                annotate_browser__init_asm_mode(&browser);
1126
1127        annotate_browser__update_addr_width(&browser);
1128
1129        ret = annotate_browser__run(&browser, evsel, hbt);
1130        list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1131                list_del(&pos->node);
1132                disasm_line__free(pos);
1133        }
1134
1135out_free_offsets:
1136        free(browser.offsets);
1137        return ret;
1138}
1139
1140#define ANNOTATE_CFG(n) \
1141        { .name = #n, .value = &annotate_browser__opts.n, }
1142
1143/*
1144 * Keep the entries sorted, they are bsearch'ed
1145 */
1146static struct annotate_config {
1147        const char *name;
1148        bool *value;
1149} annotate__configs[] = {
1150        ANNOTATE_CFG(hide_src_code),
1151        ANNOTATE_CFG(jump_arrows),
1152        ANNOTATE_CFG(show_linenr),
1153        ANNOTATE_CFG(show_nr_jumps),
1154        ANNOTATE_CFG(show_total_period),
1155        ANNOTATE_CFG(use_offset),
1156};
1157
1158#undef ANNOTATE_CFG
1159
1160static int annotate_config__cmp(const void *name, const void *cfgp)
1161{
1162        const struct annotate_config *cfg = cfgp;
1163
1164        return strcmp(name, cfg->name);
1165}
1166
1167static int annotate__config(const char *var, const char *value,
1168                            void *data __maybe_unused)
1169{
1170        struct annotate_config *cfg;
1171        const char *name;
1172
1173        if (prefixcmp(var, "annotate.") != 0)
1174                return 0;
1175
1176        name = var + 9;
1177        cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1178                      sizeof(struct annotate_config), annotate_config__cmp);
1179
1180        if (cfg == NULL)
1181                ui__warning("%s variable unknown, ignoring...", var);
1182        else
1183                *cfg->value = perf_config_bool(name, value);
1184        return 0;
1185}
1186
1187void annotate_browser__init(void)
1188{
1189        perf_config(annotate__config, NULL);
1190}
1191