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