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