linux/tools/perf/ui/browser.c
<<
>>
Prefs
   1#include "../util.h"
   2#include "../config.h"
   3#include "../../perf.h"
   4#include "libslang.h"
   5#include "ui.h"
   6#include "util.h"
   7#include <linux/compiler.h>
   8#include <linux/list.h>
   9#include <linux/rbtree.h>
  10#include <stdlib.h>
  11#include <sys/ttydefaults.h>
  12#include "browser.h"
  13#include "helpline.h"
  14#include "keysyms.h"
  15#include "../color.h"
  16
  17static int ui_browser__percent_color(struct ui_browser *browser,
  18                                     double percent, bool current)
  19{
  20        if (current && (!browser->use_navkeypressed || browser->navkeypressed))
  21                return HE_COLORSET_SELECTED;
  22        if (percent >= MIN_RED)
  23                return HE_COLORSET_TOP;
  24        if (percent >= MIN_GREEN)
  25                return HE_COLORSET_MEDIUM;
  26        return HE_COLORSET_NORMAL;
  27}
  28
  29int ui_browser__set_color(struct ui_browser *browser, int color)
  30{
  31        int ret = browser->current_color;
  32        browser->current_color = color;
  33        SLsmg_set_color(color);
  34        return ret;
  35}
  36
  37void ui_browser__set_percent_color(struct ui_browser *browser,
  38                                   double percent, bool current)
  39{
  40         int color = ui_browser__percent_color(browser, percent, current);
  41         ui_browser__set_color(browser, color);
  42}
  43
  44void ui_browser__gotorc(struct ui_browser *browser, int y, int x)
  45{
  46        SLsmg_gotorc(browser->y + y, browser->x + x);
  47}
  48
  49void ui_browser__write_nstring(struct ui_browser *browser __maybe_unused, const char *msg,
  50                               unsigned int width)
  51{
  52        slsmg_write_nstring(msg, width);
  53}
  54
  55void ui_browser__printf(struct ui_browser *browser __maybe_unused, const char *fmt, ...)
  56{
  57        va_list args;
  58
  59        va_start(args, fmt);
  60        slsmg_vprintf(fmt, args);
  61        va_end(args);
  62}
  63
  64static struct list_head *
  65ui_browser__list_head_filter_entries(struct ui_browser *browser,
  66                                     struct list_head *pos)
  67{
  68        do {
  69                if (!browser->filter || !browser->filter(browser, pos))
  70                        return pos;
  71                pos = pos->next;
  72        } while (pos != browser->entries);
  73
  74        return NULL;
  75}
  76
  77static struct list_head *
  78ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
  79                                          struct list_head *pos)
  80{
  81        do {
  82                if (!browser->filter || !browser->filter(browser, pos))
  83                        return pos;
  84                pos = pos->prev;
  85        } while (pos != browser->entries);
  86
  87        return NULL;
  88}
  89
  90void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence)
  91{
  92        struct list_head *head = browser->entries;
  93        struct list_head *pos;
  94
  95        if (browser->nr_entries == 0)
  96                return;
  97
  98        switch (whence) {
  99        case SEEK_SET:
 100                pos = ui_browser__list_head_filter_entries(browser, head->next);
 101                break;
 102        case SEEK_CUR:
 103                pos = browser->top;
 104                break;
 105        case SEEK_END:
 106                pos = ui_browser__list_head_filter_prev_entries(browser, head->prev);
 107                break;
 108        default:
 109                return;
 110        }
 111
 112        assert(pos != NULL);
 113
 114        if (offset > 0) {
 115                while (offset-- != 0)
 116                        pos = ui_browser__list_head_filter_entries(browser, pos->next);
 117        } else {
 118                while (offset++ != 0)
 119                        pos = ui_browser__list_head_filter_prev_entries(browser, pos->prev);
 120        }
 121
 122        browser->top = pos;
 123}
 124
 125void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence)
 126{
 127        struct rb_root *root = browser->entries;
 128        struct rb_node *nd;
 129
 130        switch (whence) {
 131        case SEEK_SET:
 132                nd = rb_first(root);
 133                break;
 134        case SEEK_CUR:
 135                nd = browser->top;
 136                break;
 137        case SEEK_END:
 138                nd = rb_last(root);
 139                break;
 140        default:
 141                return;
 142        }
 143
 144        if (offset > 0) {
 145                while (offset-- != 0)
 146                        nd = rb_next(nd);
 147        } else {
 148                while (offset++ != 0)
 149                        nd = rb_prev(nd);
 150        }
 151
 152        browser->top = nd;
 153}
 154
 155unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser)
 156{
 157        struct rb_node *nd;
 158        int row = 0;
 159
 160        if (browser->top == NULL)
 161                browser->top = rb_first(browser->entries);
 162
 163        nd = browser->top;
 164
 165        while (nd != NULL) {
 166                ui_browser__gotorc(browser, row, 0);
 167                browser->write(browser, nd, row);
 168                if (++row == browser->rows)
 169                        break;
 170                nd = rb_next(nd);
 171        }
 172
 173        return row;
 174}
 175
 176bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row)
 177{
 178        return browser->top_idx + row == browser->index;
 179}
 180
 181void ui_browser__refresh_dimensions(struct ui_browser *browser)
 182{
 183        browser->width = SLtt_Screen_Cols - 1;
 184        browser->height = browser->rows = SLtt_Screen_Rows - 2;
 185        browser->y = 1;
 186        browser->x = 0;
 187}
 188
 189void ui_browser__handle_resize(struct ui_browser *browser)
 190{
 191        ui__refresh_dimensions(false);
 192        ui_browser__show(browser, browser->title, ui_helpline__current);
 193        ui_browser__refresh(browser);
 194}
 195
 196int ui_browser__warning(struct ui_browser *browser, int timeout,
 197                        const char *format, ...)
 198{
 199        va_list args;
 200        char *text;
 201        int key = 0, err;
 202
 203        va_start(args, format);
 204        err = vasprintf(&text, format, args);
 205        va_end(args);
 206
 207        if (err < 0) {
 208                va_start(args, format);
 209                ui_helpline__vpush(format, args);
 210                va_end(args);
 211        } else {
 212                while ((key = ui__question_window("Warning!", text,
 213                                                   "Press any key...",
 214                                                   timeout)) == K_RESIZE)
 215                        ui_browser__handle_resize(browser);
 216                free(text);
 217        }
 218
 219        return key;
 220}
 221
 222int ui_browser__help_window(struct ui_browser *browser, const char *text)
 223{
 224        int key;
 225
 226        while ((key = ui__help_window(text)) == K_RESIZE)
 227                ui_browser__handle_resize(browser);
 228
 229        return key;
 230}
 231
 232bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
 233{
 234        int key;
 235
 236        while ((key = ui__dialog_yesno(text)) == K_RESIZE)
 237                ui_browser__handle_resize(browser);
 238
 239        return key == K_ENTER || toupper(key) == 'Y';
 240}
 241
 242void ui_browser__reset_index(struct ui_browser *browser)
 243{
 244        browser->index = browser->top_idx = 0;
 245        browser->seek(browser, 0, SEEK_SET);
 246}
 247
 248void __ui_browser__show_title(struct ui_browser *browser, const char *title)
 249{
 250        SLsmg_gotorc(0, 0);
 251        ui_browser__set_color(browser, HE_COLORSET_ROOT);
 252        ui_browser__write_nstring(browser, title, browser->width + 1);
 253}
 254
 255void ui_browser__show_title(struct ui_browser *browser, const char *title)
 256{
 257        pthread_mutex_lock(&ui__lock);
 258        __ui_browser__show_title(browser, title);
 259        pthread_mutex_unlock(&ui__lock);
 260}
 261
 262int ui_browser__show(struct ui_browser *browser, const char *title,
 263                     const char *helpline, ...)
 264{
 265        int err;
 266        va_list ap;
 267
 268        if (browser->refresh_dimensions == NULL)
 269                browser->refresh_dimensions = ui_browser__refresh_dimensions;
 270
 271        browser->refresh_dimensions(browser);
 272
 273        pthread_mutex_lock(&ui__lock);
 274        __ui_browser__show_title(browser, title);
 275
 276        browser->title = title;
 277        zfree(&browser->helpline);
 278
 279        va_start(ap, helpline);
 280        err = vasprintf(&browser->helpline, helpline, ap);
 281        va_end(ap);
 282        if (err > 0)
 283                ui_helpline__push(browser->helpline);
 284        pthread_mutex_unlock(&ui__lock);
 285        return err ? 0 : -1;
 286}
 287
 288void ui_browser__hide(struct ui_browser *browser)
 289{
 290        pthread_mutex_lock(&ui__lock);
 291        ui_helpline__pop();
 292        zfree(&browser->helpline);
 293        pthread_mutex_unlock(&ui__lock);
 294}
 295
 296static void ui_browser__scrollbar_set(struct ui_browser *browser)
 297{
 298        int height = browser->height, h = 0, pct = 0,
 299            col = browser->width,
 300            row = 0;
 301
 302        if (browser->nr_entries > 1) {
 303                pct = ((browser->index * (browser->height - 1)) /
 304                       (browser->nr_entries - 1));
 305        }
 306
 307        SLsmg_set_char_set(1);
 308
 309        while (h < height) {
 310                ui_browser__gotorc(browser, row++, col);
 311                SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
 312                ++h;
 313        }
 314
 315        SLsmg_set_char_set(0);
 316}
 317
 318static int __ui_browser__refresh(struct ui_browser *browser)
 319{
 320        int row;
 321        int width = browser->width;
 322
 323        row = browser->refresh(browser);
 324        ui_browser__set_color(browser, HE_COLORSET_NORMAL);
 325
 326        if (!browser->use_navkeypressed || browser->navkeypressed)
 327                ui_browser__scrollbar_set(browser);
 328        else
 329                width += 1;
 330
 331        SLsmg_fill_region(browser->y + row, browser->x,
 332                          browser->height - row, width, ' ');
 333
 334        return 0;
 335}
 336
 337int ui_browser__refresh(struct ui_browser *browser)
 338{
 339        pthread_mutex_lock(&ui__lock);
 340        __ui_browser__refresh(browser);
 341        pthread_mutex_unlock(&ui__lock);
 342
 343        return 0;
 344}
 345
 346/*
 347 * Here we're updating nr_entries _after_ we started browsing, i.e.  we have to
 348 * forget about any reference to any entry in the underlying data structure,
 349 * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
 350 * after an output_resort and hist decay.
 351 */
 352void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
 353{
 354        off_t offset = nr_entries - browser->nr_entries;
 355
 356        browser->nr_entries = nr_entries;
 357
 358        if (offset < 0) {
 359                if (browser->top_idx < (u64)-offset)
 360                        offset = -browser->top_idx;
 361
 362                browser->index += offset;
 363                browser->top_idx += offset;
 364        }
 365
 366        browser->top = NULL;
 367        browser->seek(browser, browser->top_idx, SEEK_SET);
 368}
 369
 370int ui_browser__run(struct ui_browser *browser, int delay_secs)
 371{
 372        int err, key;
 373
 374        while (1) {
 375                off_t offset;
 376
 377                pthread_mutex_lock(&ui__lock);
 378                err = __ui_browser__refresh(browser);
 379                SLsmg_refresh();
 380                pthread_mutex_unlock(&ui__lock);
 381                if (err < 0)
 382                        break;
 383
 384                key = ui__getch(delay_secs);
 385
 386                if (key == K_RESIZE) {
 387                        ui__refresh_dimensions(false);
 388                        browser->refresh_dimensions(browser);
 389                        __ui_browser__show_title(browser, browser->title);
 390                        ui_helpline__puts(browser->helpline);
 391                        continue;
 392                }
 393
 394                if (browser->use_navkeypressed && !browser->navkeypressed) {
 395                        if (key == K_DOWN || key == K_UP ||
 396                            (browser->columns && (key == K_LEFT || key == K_RIGHT)) ||
 397                            key == K_PGDN || key == K_PGUP ||
 398                            key == K_HOME || key == K_END ||
 399                            key == ' ') {
 400                                browser->navkeypressed = true;
 401                                continue;
 402                        } else
 403                                return key;
 404                }
 405
 406                switch (key) {
 407                case K_DOWN:
 408                        if (browser->index == browser->nr_entries - 1)
 409                                break;
 410                        ++browser->index;
 411                        if (browser->index == browser->top_idx + browser->rows) {
 412                                ++browser->top_idx;
 413                                browser->seek(browser, +1, SEEK_CUR);
 414                        }
 415                        break;
 416                case K_UP:
 417                        if (browser->index == 0)
 418                                break;
 419                        --browser->index;
 420                        if (browser->index < browser->top_idx) {
 421                                --browser->top_idx;
 422                                browser->seek(browser, -1, SEEK_CUR);
 423                        }
 424                        break;
 425                case K_RIGHT:
 426                        if (!browser->columns)
 427                                goto out;
 428                        if (browser->horiz_scroll < browser->columns - 1)
 429                                ++browser->horiz_scroll;
 430                        break;
 431                case K_LEFT:
 432                        if (!browser->columns)
 433                                goto out;
 434                        if (browser->horiz_scroll != 0)
 435                                --browser->horiz_scroll;
 436                        break;
 437                case K_PGDN:
 438                case ' ':
 439                        if (browser->top_idx + browser->rows > browser->nr_entries - 1)
 440                                break;
 441
 442                        offset = browser->rows;
 443                        if (browser->index + offset > browser->nr_entries - 1)
 444                                offset = browser->nr_entries - 1 - browser->index;
 445                        browser->index += offset;
 446                        browser->top_idx += offset;
 447                        browser->seek(browser, +offset, SEEK_CUR);
 448                        break;
 449                case K_PGUP:
 450                        if (browser->top_idx == 0)
 451                                break;
 452
 453                        if (browser->top_idx < browser->rows)
 454                                offset = browser->top_idx;
 455                        else
 456                                offset = browser->rows;
 457
 458                        browser->index -= offset;
 459                        browser->top_idx -= offset;
 460                        browser->seek(browser, -offset, SEEK_CUR);
 461                        break;
 462                case K_HOME:
 463                        ui_browser__reset_index(browser);
 464                        break;
 465                case K_END:
 466                        offset = browser->rows - 1;
 467                        if (offset >= browser->nr_entries)
 468                                offset = browser->nr_entries - 1;
 469
 470                        browser->index = browser->nr_entries - 1;
 471                        browser->top_idx = browser->index - offset;
 472                        browser->seek(browser, -offset, SEEK_END);
 473                        break;
 474                default:
 475                out:
 476                        return key;
 477                }
 478        }
 479        return -1;
 480}
 481
 482unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
 483{
 484        struct list_head *pos;
 485        struct list_head *head = browser->entries;
 486        int row = 0;
 487
 488        if (browser->top == NULL || browser->top == browser->entries)
 489                browser->top = ui_browser__list_head_filter_entries(browser, head->next);
 490
 491        pos = browser->top;
 492
 493        list_for_each_from(pos, head) {
 494                if (!browser->filter || !browser->filter(browser, pos)) {
 495                        ui_browser__gotorc(browser, row, 0);
 496                        browser->write(browser, pos, row);
 497                        if (++row == browser->rows)
 498                                break;
 499                }
 500        }
 501
 502        return row;
 503}
 504
 505static struct ui_browser_colorset {
 506        const char *name, *fg, *bg;
 507        int colorset;
 508} ui_browser__colorsets[] = {
 509        {
 510                .colorset = HE_COLORSET_TOP,
 511                .name     = "top",
 512                .fg       = "red",
 513                .bg       = "default",
 514        },
 515        {
 516                .colorset = HE_COLORSET_MEDIUM,
 517                .name     = "medium",
 518                .fg       = "green",
 519                .bg       = "default",
 520        },
 521        {
 522                .colorset = HE_COLORSET_NORMAL,
 523                .name     = "normal",
 524                .fg       = "default",
 525                .bg       = "default",
 526        },
 527        {
 528                .colorset = HE_COLORSET_SELECTED,
 529                .name     = "selected",
 530                .fg       = "black",
 531                .bg       = "yellow",
 532        },
 533        {
 534                .colorset = HE_COLORSET_JUMP_ARROWS,
 535                .name     = "jump_arrows",
 536                .fg       = "blue",
 537                .bg       = "default",
 538        },
 539        {
 540                .colorset = HE_COLORSET_ADDR,
 541                .name     = "addr",
 542                .fg       = "magenta",
 543                .bg       = "default",
 544        },
 545        {
 546                .colorset = HE_COLORSET_ROOT,
 547                .name     = "root",
 548                .fg       = "white",
 549                .bg       = "blue",
 550        },
 551        {
 552                .name = NULL,
 553        }
 554};
 555
 556
 557static int ui_browser__color_config(const char *var, const char *value,
 558                                    void *data __maybe_unused)
 559{
 560        char *fg = NULL, *bg;
 561        int i;
 562
 563        /* same dir for all commands */
 564        if (prefixcmp(var, "colors.") != 0)
 565                return 0;
 566
 567        for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
 568                const char *name = var + 7;
 569
 570                if (strcmp(ui_browser__colorsets[i].name, name) != 0)
 571                        continue;
 572
 573                fg = strdup(value);
 574                if (fg == NULL)
 575                        break;
 576
 577                bg = strchr(fg, ',');
 578                if (bg == NULL)
 579                        break;
 580
 581                *bg = '\0';
 582                while (isspace(*++bg));
 583                ui_browser__colorsets[i].bg = bg;
 584                ui_browser__colorsets[i].fg = fg;
 585                return 0;
 586        }
 587
 588        free(fg);
 589        return -1;
 590}
 591
 592void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
 593{
 594        switch (whence) {
 595        case SEEK_SET:
 596                browser->top = browser->entries;
 597                break;
 598        case SEEK_CUR:
 599                browser->top = browser->top + browser->top_idx + offset;
 600                break;
 601        case SEEK_END:
 602                browser->top = browser->top + browser->nr_entries - 1 + offset;
 603                break;
 604        default:
 605                return;
 606        }
 607}
 608
 609unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
 610{
 611        unsigned int row = 0, idx = browser->top_idx;
 612        char **pos;
 613
 614        if (browser->top == NULL)
 615                browser->top = browser->entries;
 616
 617        pos = (char **)browser->top;
 618        while (idx < browser->nr_entries) {
 619                if (!browser->filter || !browser->filter(browser, *pos)) {
 620                        ui_browser__gotorc(browser, row, 0);
 621                        browser->write(browser, pos, row);
 622                        if (++row == browser->rows)
 623                                break;
 624                }
 625
 626                ++idx;
 627                ++pos;
 628        }
 629
 630        return row;
 631}
 632
 633void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
 634                         u16 start, u16 end)
 635{
 636        SLsmg_set_char_set(1);
 637        ui_browser__gotorc(browser, start, column);
 638        SLsmg_draw_vline(end - start + 1);
 639        SLsmg_set_char_set(0);
 640}
 641
 642void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
 643                             int graph)
 644{
 645        SLsmg_set_char_set(1);
 646        SLsmg_write_char(graph);
 647        SLsmg_set_char_set(0);
 648}
 649
 650static void __ui_browser__line_arrow_up(struct ui_browser *browser,
 651                                        unsigned int column,
 652                                        u64 start, u64 end)
 653{
 654        unsigned int row, end_row;
 655
 656        SLsmg_set_char_set(1);
 657
 658        if (start < browser->top_idx + browser->rows) {
 659                row = start - browser->top_idx;
 660                ui_browser__gotorc(browser, row, column);
 661                SLsmg_write_char(SLSMG_LLCORN_CHAR);
 662                ui_browser__gotorc(browser, row, column + 1);
 663                SLsmg_draw_hline(2);
 664
 665                if (row-- == 0)
 666                        goto out;
 667        } else
 668                row = browser->rows - 1;
 669
 670        if (end > browser->top_idx)
 671                end_row = end - browser->top_idx;
 672        else
 673                end_row = 0;
 674
 675        ui_browser__gotorc(browser, end_row, column);
 676        SLsmg_draw_vline(row - end_row + 1);
 677
 678        ui_browser__gotorc(browser, end_row, column);
 679        if (end >= browser->top_idx) {
 680                SLsmg_write_char(SLSMG_ULCORN_CHAR);
 681                ui_browser__gotorc(browser, end_row, column + 1);
 682                SLsmg_write_char(SLSMG_HLINE_CHAR);
 683                ui_browser__gotorc(browser, end_row, column + 2);
 684                SLsmg_write_char(SLSMG_RARROW_CHAR);
 685        }
 686out:
 687        SLsmg_set_char_set(0);
 688}
 689
 690static void __ui_browser__line_arrow_down(struct ui_browser *browser,
 691                                          unsigned int column,
 692                                          u64 start, u64 end)
 693{
 694        unsigned int row, end_row;
 695
 696        SLsmg_set_char_set(1);
 697
 698        if (start >= browser->top_idx) {
 699                row = start - browser->top_idx;
 700                ui_browser__gotorc(browser, row, column);
 701                SLsmg_write_char(SLSMG_ULCORN_CHAR);
 702                ui_browser__gotorc(browser, row, column + 1);
 703                SLsmg_draw_hline(2);
 704
 705                if (row++ == 0)
 706                        goto out;
 707        } else
 708                row = 0;
 709
 710        if (end >= browser->top_idx + browser->rows)
 711                end_row = browser->rows - 1;
 712        else
 713                end_row = end - browser->top_idx;
 714
 715        ui_browser__gotorc(browser, row, column);
 716        SLsmg_draw_vline(end_row - row + 1);
 717
 718        ui_browser__gotorc(browser, end_row, column);
 719        if (end < browser->top_idx + browser->rows) {
 720                SLsmg_write_char(SLSMG_LLCORN_CHAR);
 721                ui_browser__gotorc(browser, end_row, column + 1);
 722                SLsmg_write_char(SLSMG_HLINE_CHAR);
 723                ui_browser__gotorc(browser, end_row, column + 2);
 724                SLsmg_write_char(SLSMG_RARROW_CHAR);
 725        }
 726out:
 727        SLsmg_set_char_set(0);
 728}
 729
 730void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
 731                              u64 start, u64 end)
 732{
 733        if (start > end)
 734                __ui_browser__line_arrow_up(browser, column, start, end);
 735        else
 736                __ui_browser__line_arrow_down(browser, column, start, end);
 737}
 738
 739void ui_browser__init(void)
 740{
 741        int i = 0;
 742
 743        perf_config(ui_browser__color_config, NULL);
 744
 745        while (ui_browser__colorsets[i].name) {
 746                struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
 747                sltt_set_color(c->colorset, c->name, c->fg, c->bg);
 748        }
 749
 750        annotate_browser__init();
 751}
 752