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.ops && dl->ins.ops->scnprintf) {
 217                        if (ins__is_jump(&dl->ins)) {
 218                                bool fwd = dl->ops.target.offset > 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.ops || !ins__is_jump(&dl->ins)
 247            || !disasm_line__has_offset(dl)
 248            || dl->ops.target.offset < 0
 249            || dl->ops.target.offset >= (s64)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_JUMP_ARROWS);
 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) ||
 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        u64 offset;
 548        s64 idx;
 549
 550        if (!ins__is_jump(&dl->ins))
 551                return false;
 552
 553        offset = dl->ops.target.offset;
 554        dl = annotate_browser__find_offset(browser, offset, &idx);
 555        if (dl == NULL) {
 556                ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
 557                return true;
 558        }
 559
 560        annotate_browser__set_top(browser, dl, idx);
 561
 562        return true;
 563}
 564
 565static
 566struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
 567                                          char *s, s64 *idx)
 568{
 569        struct map_symbol *ms = browser->b.priv;
 570        struct symbol *sym = ms->sym;
 571        struct annotation *notes = symbol__annotation(sym);
 572        struct disasm_line *pos = browser->selection;
 573
 574        *idx = browser->b.index;
 575        list_for_each_entry_continue(pos, &notes->src->source, node) {
 576                if (disasm_line__filter(&browser->b, &pos->node))
 577                        continue;
 578
 579                ++*idx;
 580
 581                if (pos->line && strstr(pos->line, s) != NULL)
 582                        return pos;
 583        }
 584
 585        return NULL;
 586}
 587
 588static bool __annotate_browser__search(struct annotate_browser *browser)
 589{
 590        struct disasm_line *dl;
 591        s64 idx;
 592
 593        dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
 594        if (dl == NULL) {
 595                ui_helpline__puts("String not found!");
 596                return false;
 597        }
 598
 599        annotate_browser__set_top(browser, dl, idx);
 600        browser->searching_backwards = false;
 601        return true;
 602}
 603
 604static
 605struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
 606                                                  char *s, s64 *idx)
 607{
 608        struct map_symbol *ms = browser->b.priv;
 609        struct symbol *sym = ms->sym;
 610        struct annotation *notes = symbol__annotation(sym);
 611        struct disasm_line *pos = browser->selection;
 612
 613        *idx = browser->b.index;
 614        list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
 615                if (disasm_line__filter(&browser->b, &pos->node))
 616                        continue;
 617
 618                --*idx;
 619
 620                if (pos->line && strstr(pos->line, s) != NULL)
 621                        return pos;
 622        }
 623
 624        return NULL;
 625}
 626
 627static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
 628{
 629        struct disasm_line *dl;
 630        s64 idx;
 631
 632        dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
 633        if (dl == NULL) {
 634                ui_helpline__puts("String not found!");
 635                return false;
 636        }
 637
 638        annotate_browser__set_top(browser, dl, idx);
 639        browser->searching_backwards = true;
 640        return true;
 641}
 642
 643static bool annotate_browser__search_window(struct annotate_browser *browser,
 644                                            int delay_secs)
 645{
 646        if (ui_browser__input_window("Search", "String: ", browser->search_bf,
 647                                     "ENTER: OK, ESC: Cancel",
 648                                     delay_secs * 2) != K_ENTER ||
 649            !*browser->search_bf)
 650                return false;
 651
 652        return true;
 653}
 654
 655static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
 656{
 657        if (annotate_browser__search_window(browser, delay_secs))
 658                return __annotate_browser__search(browser);
 659
 660        return false;
 661}
 662
 663static bool annotate_browser__continue_search(struct annotate_browser *browser,
 664                                              int delay_secs)
 665{
 666        if (!*browser->search_bf)
 667                return annotate_browser__search(browser, delay_secs);
 668
 669        return __annotate_browser__search(browser);
 670}
 671
 672static bool annotate_browser__search_reverse(struct annotate_browser *browser,
 673                                           int delay_secs)
 674{
 675        if (annotate_browser__search_window(browser, delay_secs))
 676                return __annotate_browser__search_reverse(browser);
 677
 678        return false;
 679}
 680
 681static
 682bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
 683                                               int delay_secs)
 684{
 685        if (!*browser->search_bf)
 686                return annotate_browser__search_reverse(browser, delay_secs);
 687
 688        return __annotate_browser__search_reverse(browser);
 689}
 690
 691static void annotate_browser__update_addr_width(struct annotate_browser *browser)
 692{
 693        if (annotate_browser__opts.use_offset)
 694                browser->target_width = browser->min_addr_width;
 695        else
 696                browser->target_width = browser->max_addr_width;
 697
 698        browser->addr_width = browser->target_width;
 699
 700        if (annotate_browser__opts.show_nr_jumps)
 701                browser->addr_width += browser->jumps_width + 1;
 702}
 703
 704static int annotate_browser__run(struct annotate_browser *browser,
 705                                 struct perf_evsel *evsel,
 706                                 struct hist_browser_timer *hbt)
 707{
 708        struct rb_node *nd = NULL;
 709        struct map_symbol *ms = browser->b.priv;
 710        struct symbol *sym = ms->sym;
 711        const char *help = "Press 'h' for help on key bindings";
 712        int delay_secs = hbt ? hbt->refresh : 0;
 713        int key;
 714        char title[SYM_TITLE_MAX_SIZE];
 715
 716        sym_title(sym, ms->map, title, sizeof(title));
 717        if (ui_browser__show(&browser->b, title, help) < 0)
 718                return -1;
 719
 720        annotate_browser__calc_percent(browser, evsel);
 721
 722        if (browser->curr_hot) {
 723                annotate_browser__set_rb_top(browser, browser->curr_hot);
 724                browser->b.navkeypressed = false;
 725        }
 726
 727        nd = browser->curr_hot;
 728
 729        while (1) {
 730                key = ui_browser__run(&browser->b, delay_secs);
 731
 732                if (delay_secs != 0) {
 733                        annotate_browser__calc_percent(browser, evsel);
 734                        /*
 735                         * Current line focus got out of the list of most active
 736                         * lines, NULL it so that if TAB|UNTAB is pressed, we
 737                         * move to curr_hot (current hottest line).
 738                         */
 739                        if (nd != NULL && RB_EMPTY_NODE(nd))
 740                                nd = NULL;
 741                }
 742
 743                switch (key) {
 744                case K_TIMER:
 745                        if (hbt)
 746                                hbt->timer(hbt->arg);
 747
 748                        if (delay_secs != 0)
 749                                symbol__annotate_decay_histogram(sym, evsel->idx);
 750                        continue;
 751                case K_TAB:
 752                        if (nd != NULL) {
 753                                nd = rb_prev(nd);
 754                                if (nd == NULL)
 755                                        nd = rb_last(&browser->entries);
 756                        } else
 757                                nd = browser->curr_hot;
 758                        break;
 759                case K_UNTAB:
 760                        if (nd != NULL) {
 761                                nd = rb_next(nd);
 762                                if (nd == NULL)
 763                                        nd = rb_first(&browser->entries);
 764                        } else
 765                                nd = browser->curr_hot;
 766                        break;
 767                case K_F1:
 768                case 'h':
 769                        ui_browser__help_window(&browser->b,
 770                "UP/DOWN/PGUP\n"
 771                "PGDN/SPACE    Navigate\n"
 772                "q/ESC/CTRL+C  Exit\n\n"
 773                "ENTER         Go to target\n"
 774                "ESC           Exit\n"
 775                "H             Cycle thru hottest instructions\n"
 776                "j             Toggle showing jump to target arrows\n"
 777                "J             Toggle showing number of jump sources on targets\n"
 778                "n             Search next string\n"
 779                "o             Toggle disassembler output/simplified view\n"
 780                "s             Toggle source code view\n"
 781                "t             Toggle total period view\n"
 782                "/             Search string\n"
 783                "k             Toggle line numbers\n"
 784                "r             Run available scripts\n"
 785                "?             Search string backwards\n");
 786                        continue;
 787                case 'r':
 788                        {
 789                                script_browse(NULL);
 790                                continue;
 791                        }
 792                case 'k':
 793                        annotate_browser__opts.show_linenr =
 794                                !annotate_browser__opts.show_linenr;
 795                        break;
 796                case 'H':
 797                        nd = browser->curr_hot;
 798                        break;
 799                case 's':
 800                        if (annotate_browser__toggle_source(browser))
 801                                ui_helpline__puts(help);
 802                        continue;
 803                case 'o':
 804                        annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
 805                        annotate_browser__update_addr_width(browser);
 806                        continue;
 807                case 'j':
 808                        annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
 809                        continue;
 810                case 'J':
 811                        annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
 812                        annotate_browser__update_addr_width(browser);
 813                        continue;
 814                case '/':
 815                        if (annotate_browser__search(browser, delay_secs)) {
 816show_help:
 817                                ui_helpline__puts(help);
 818                        }
 819                        continue;
 820                case 'n':
 821                        if (browser->searching_backwards ?
 822                            annotate_browser__continue_search_reverse(browser, delay_secs) :
 823                            annotate_browser__continue_search(browser, delay_secs))
 824                                goto show_help;
 825                        continue;
 826                case '?':
 827                        if (annotate_browser__search_reverse(browser, delay_secs))
 828                                goto show_help;
 829                        continue;
 830                case 'D': {
 831                        static int seq;
 832                        ui_helpline__pop();
 833                        ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
 834                                           seq++, browser->b.nr_entries,
 835                                           browser->b.height,
 836                                           browser->b.index,
 837                                           browser->b.top_idx,
 838                                           browser->nr_asm_entries);
 839                }
 840                        continue;
 841                case K_ENTER:
 842                case K_RIGHT:
 843                        if (browser->selection == NULL)
 844                                ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
 845                        else if (browser->selection->offset == -1)
 846                                ui_helpline__puts("Actions are only available for assembly lines.");
 847                        else if (!browser->selection->ins.ops)
 848                                goto show_sup_ins;
 849                        else if (ins__is_ret(&browser->selection->ins))
 850                                goto out;
 851                        else if (!(annotate_browser__jump(browser) ||
 852                                     annotate_browser__callq(browser, evsel, hbt))) {
 853show_sup_ins:
 854                                ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
 855                        }
 856                        continue;
 857                case 't':
 858                        annotate_browser__opts.show_total_period =
 859                          !annotate_browser__opts.show_total_period;
 860                        annotate_browser__update_addr_width(browser);
 861                        continue;
 862                case K_LEFT:
 863                case K_ESC:
 864                case 'q':
 865                case CTRL('c'):
 866                        goto out;
 867                default:
 868                        continue;
 869                }
 870
 871                if (nd != NULL)
 872                        annotate_browser__set_rb_top(browser, nd);
 873        }
 874out:
 875        ui_browser__hide(&browser->b);
 876        return key;
 877}
 878
 879int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
 880                             struct hist_browser_timer *hbt)
 881{
 882        /* Set default value for show_total_period.  */
 883        annotate_browser__opts.show_total_period =
 884          symbol_conf.show_total_period;
 885
 886        return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
 887}
 888
 889int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
 890                             struct hist_browser_timer *hbt)
 891{
 892        /* reset abort key so that it can get Ctrl-C as a key */
 893        SLang_reset_tty();
 894        SLang_init_tty(0, 0, 0);
 895
 896        return map_symbol__tui_annotate(&he->ms, evsel, hbt);
 897}
 898
 899
 900static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
 901{
 902        unsigned n_insn = 0;
 903        u64 offset;
 904
 905        for (offset = start; offset <= end; offset++) {
 906                if (browser->offsets[offset])
 907                        n_insn++;
 908        }
 909        return n_insn;
 910}
 911
 912static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
 913                           struct cyc_hist *ch)
 914{
 915        unsigned n_insn;
 916        u64 offset;
 917
 918        n_insn = count_insn(browser, start, end);
 919        if (n_insn && ch->num && ch->cycles) {
 920                float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
 921
 922                /* Hide data when there are too many overlaps. */
 923                if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
 924                        return;
 925
 926                for (offset = start; offset <= end; offset++) {
 927                        struct disasm_line *dl = browser->offsets[offset];
 928
 929                        if (dl)
 930                                dl->ipc = ipc;
 931                }
 932        }
 933}
 934
 935/*
 936 * This should probably be in util/annotate.c to share with the tty
 937 * annotate, but right now we need the per byte offsets arrays,
 938 * which are only here.
 939 */
 940static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
 941                           struct symbol *sym)
 942{
 943        u64 offset;
 944        struct annotation *notes = symbol__annotation(sym);
 945
 946        if (!notes->src || !notes->src->cycles_hist)
 947                return;
 948
 949        pthread_mutex_lock(&notes->lock);
 950        for (offset = 0; offset < size; ++offset) {
 951                struct cyc_hist *ch;
 952
 953                ch = &notes->src->cycles_hist[offset];
 954                if (ch && ch->cycles) {
 955                        struct disasm_line *dl;
 956
 957                        if (ch->have_start)
 958                                count_and_fill(browser, ch->start, offset, ch);
 959                        dl = browser->offsets[offset];
 960                        if (dl && ch->num_aggr)
 961                                dl->cycles = ch->cycles_aggr / ch->num_aggr;
 962                        browser->have_cycles = true;
 963                }
 964        }
 965        pthread_mutex_unlock(&notes->lock);
 966}
 967
 968static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
 969                                                size_t size)
 970{
 971        u64 offset;
 972        struct map_symbol *ms = browser->b.priv;
 973        struct symbol *sym = ms->sym;
 974
 975        /* PLT symbols contain external offsets */
 976        if (strstr(sym->name, "@plt"))
 977                return;
 978
 979        for (offset = 0; offset < size; ++offset) {
 980                struct disasm_line *dl = browser->offsets[offset], *dlt;
 981                struct browser_disasm_line *bdlt;
 982
 983                if (!disasm_line__is_valid_jump(dl, sym))
 984                        continue;
 985
 986                dlt = browser->offsets[dl->ops.target.offset];
 987                /*
 988                 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 989                 * have to adjust to the previous offset?
 990                 */
 991                if (dlt == NULL)
 992                        continue;
 993
 994                bdlt = disasm_line__browser(dlt);
 995                if (++bdlt->jump_sources > browser->max_jump_sources)
 996                        browser->max_jump_sources = bdlt->jump_sources;
 997
 998                ++browser->nr_jumps;
 999        }
1000}
1001
1002static inline int width_jumps(int n)
1003{
1004        if (n >= 100)
1005                return 5;
1006        if (n / 10)
1007                return 2;
1008        return 1;
1009}
1010
1011int symbol__tui_annotate(struct symbol *sym, struct map *map,
1012                         struct perf_evsel *evsel,
1013                         struct hist_browser_timer *hbt)
1014{
1015        struct disasm_line *pos, *n;
1016        struct annotation *notes;
1017        size_t size;
1018        struct map_symbol ms = {
1019                .map = map,
1020                .sym = sym,
1021        };
1022        struct annotate_browser browser = {
1023                .b = {
1024                        .refresh = annotate_browser__refresh,
1025                        .seek    = ui_browser__list_head_seek,
1026                        .write   = annotate_browser__write,
1027                        .filter  = disasm_line__filter,
1028                        .priv    = &ms,
1029                        .use_navkeypressed = true,
1030                },
1031        };
1032        int ret = -1, err;
1033        int nr_pcnt = 1;
1034        size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1035
1036        if (sym == NULL)
1037                return -1;
1038
1039        size = symbol__size(sym);
1040
1041        if (map->dso->annotate_warned)
1042                return -1;
1043
1044        browser.offsets = zalloc(size * sizeof(struct disasm_line *));
1045        if (browser.offsets == NULL) {
1046                ui__error("Not enough memory!");
1047                return -1;
1048        }
1049
1050        if (perf_evsel__is_group_event(evsel)) {
1051                nr_pcnt = evsel->nr_members;
1052                sizeof_bdl += sizeof(struct disasm_line_samples) *
1053                  (nr_pcnt - 1);
1054        }
1055
1056        err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel), sizeof_bdl);
1057        if (err) {
1058                char msg[BUFSIZ];
1059                symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
1060                ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1061                goto out_free_offsets;
1062        }
1063
1064        ui_helpline__push("Press ESC to exit");
1065
1066        notes = symbol__annotation(sym);
1067        browser.start = map__rip_2objdump(map, sym->start);
1068
1069        list_for_each_entry(pos, &notes->src->source, node) {
1070                struct browser_disasm_line *bpos;
1071                size_t line_len = strlen(pos->line);
1072
1073                if (browser.b.width < line_len)
1074                        browser.b.width = line_len;
1075                bpos = disasm_line__browser(pos);
1076                bpos->idx = browser.nr_entries++;
1077                if (pos->offset != -1) {
1078                        bpos->idx_asm = browser.nr_asm_entries++;
1079                        /*
1080                         * FIXME: short term bandaid to cope with assembly
1081                         * routines that comes with labels in the same column
1082                         * as the address in objdump, sigh.
1083                         *
1084                         * E.g. copy_user_generic_unrolled
1085                         */
1086                        if (pos->offset < (s64)size)
1087                                browser.offsets[pos->offset] = pos;
1088                } else
1089                        bpos->idx_asm = -1;
1090        }
1091
1092        annotate_browser__mark_jump_targets(&browser, size);
1093        annotate__compute_ipc(&browser, size, sym);
1094
1095        browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1096        browser.max_addr_width = hex_width(sym->end);
1097        browser.jumps_width = width_jumps(browser.max_jump_sources);
1098        browser.nr_events = nr_pcnt;
1099        browser.b.nr_entries = browser.nr_entries;
1100        browser.b.entries = &notes->src->source,
1101        browser.b.width += 18; /* Percentage */
1102
1103        if (annotate_browser__opts.hide_src_code)
1104                annotate_browser__init_asm_mode(&browser);
1105
1106        annotate_browser__update_addr_width(&browser);
1107
1108        ret = annotate_browser__run(&browser, evsel, hbt);
1109        list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1110                list_del(&pos->node);
1111                disasm_line__free(pos);
1112        }
1113
1114out_free_offsets:
1115        free(browser.offsets);
1116        return ret;
1117}
1118
1119#define ANNOTATE_CFG(n) \
1120        { .name = #n, .value = &annotate_browser__opts.n, }
1121
1122/*
1123 * Keep the entries sorted, they are bsearch'ed
1124 */
1125static struct annotate_config {
1126        const char *name;
1127        bool *value;
1128} annotate__configs[] = {
1129        ANNOTATE_CFG(hide_src_code),
1130        ANNOTATE_CFG(jump_arrows),
1131        ANNOTATE_CFG(show_linenr),
1132        ANNOTATE_CFG(show_nr_jumps),
1133        ANNOTATE_CFG(show_total_period),
1134        ANNOTATE_CFG(use_offset),
1135};
1136
1137#undef ANNOTATE_CFG
1138
1139static int annotate_config__cmp(const void *name, const void *cfgp)
1140{
1141        const struct annotate_config *cfg = cfgp;
1142
1143        return strcmp(name, cfg->name);
1144}
1145
1146static int annotate__config(const char *var, const char *value,
1147                            void *data __maybe_unused)
1148{
1149        struct annotate_config *cfg;
1150        const char *name;
1151
1152        if (prefixcmp(var, "annotate.") != 0)
1153                return 0;
1154
1155        name = var + 9;
1156        cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1157                      sizeof(struct annotate_config), annotate_config__cmp);
1158
1159        if (cfg == NULL)
1160                ui__warning("%s variable unknown, ignoring...", var);
1161        else
1162                *cfg->value = perf_config_bool(name, value);
1163        return 0;
1164}
1165
1166void annotate_browser__init(void)
1167{
1168        perf_config(annotate__config, NULL);
1169}
1170