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