linux/tools/perf/util/newt.c
<<
>>
Prefs
   1#define _GNU_SOURCE
   2#include <stdio.h>
   3#undef _GNU_SOURCE
   4/*
   5 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
   6 * the build if it isn't defined. Use the equivalent one that glibc
   7 * has on features.h.
   8 */
   9#include <features.h>
  10#ifndef HAVE_LONG_LONG
  11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
  12#endif
  13#include <slang.h>
  14#include <stdlib.h>
  15#include <newt.h>
  16#include <sys/ttydefaults.h>
  17
  18#include "cache.h"
  19#include "hist.h"
  20#include "pstack.h"
  21#include "session.h"
  22#include "sort.h"
  23#include "symbol.h"
  24
  25#if SLANG_VERSION < 20104
  26#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
  27#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
  28#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
  29                                                         (char *)fg, (char *)bg)
  30#else
  31#define slsmg_printf SLsmg_printf
  32#define slsmg_write_nstring SLsmg_write_nstring
  33#define sltt_set_color SLtt_set_color
  34#endif
  35
  36struct ui_progress {
  37        newtComponent form, scale;
  38};
  39
  40struct ui_progress *ui_progress__new(const char *title, u64 total)
  41{
  42        struct ui_progress *self = malloc(sizeof(*self));
  43
  44        if (self != NULL) {
  45                int cols;
  46
  47                if (use_browser <= 0)   
  48                        return self;
  49                newtGetScreenSize(&cols, NULL);
  50                cols -= 4;
  51                newtCenteredWindow(cols, 1, title);
  52                self->form  = newtForm(NULL, NULL, 0);
  53                if (self->form == NULL)
  54                        goto out_free_self;
  55                self->scale = newtScale(0, 0, cols, total);
  56                if (self->scale == NULL)
  57                        goto out_free_form;
  58                newtFormAddComponent(self->form, self->scale);
  59                newtRefresh();
  60        }
  61
  62        return self;
  63
  64out_free_form:
  65        newtFormDestroy(self->form);
  66out_free_self:
  67        free(self);
  68        return NULL;
  69}
  70
  71void ui_progress__update(struct ui_progress *self, u64 curr)
  72{
  73        /*
  74         * FIXME: We should have a per UI backend way of showing progress,
  75         * stdio will just show a percentage as NN%, etc.
  76         */
  77        if (use_browser <= 0)
  78                return;
  79        newtScaleSet(self->scale, curr);
  80        newtRefresh();
  81}
  82
  83void ui_progress__delete(struct ui_progress *self)
  84{
  85        if (use_browser > 0) {
  86                newtFormDestroy(self->form);
  87                newtPopWindow();
  88        }
  89        free(self);
  90}
  91
  92static void ui_helpline__pop(void)
  93{
  94        newtPopHelpLine();
  95}
  96
  97static void ui_helpline__push(const char *msg)
  98{
  99        newtPushHelpLine(msg);
 100}
 101
 102static void ui_helpline__vpush(const char *fmt, va_list ap)
 103{
 104        char *s;
 105
 106        if (vasprintf(&s, fmt, ap) < 0)
 107                vfprintf(stderr, fmt, ap);
 108        else {
 109                ui_helpline__push(s);
 110                free(s);
 111        }
 112}
 113
 114static void ui_helpline__fpush(const char *fmt, ...)
 115{
 116        va_list ap;
 117
 118        va_start(ap, fmt);
 119        ui_helpline__vpush(fmt, ap);
 120        va_end(ap);
 121}
 122
 123static void ui_helpline__puts(const char *msg)
 124{
 125        ui_helpline__pop();
 126        ui_helpline__push(msg);
 127}
 128
 129static char browser__last_msg[1024];
 130
 131int browser__show_help(const char *format, va_list ap)
 132{
 133        int ret;
 134        static int backlog;
 135
 136        ret = vsnprintf(browser__last_msg + backlog,
 137                        sizeof(browser__last_msg) - backlog, format, ap);
 138        backlog += ret;
 139
 140        if (browser__last_msg[backlog - 1] == '\n') {
 141                ui_helpline__puts(browser__last_msg);
 142                newtRefresh();
 143                backlog = 0;
 144        }
 145
 146        return ret;
 147}
 148
 149static void newt_form__set_exit_keys(newtComponent self)
 150{
 151        newtFormAddHotKey(self, NEWT_KEY_LEFT);
 152        newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
 153        newtFormAddHotKey(self, 'Q');
 154        newtFormAddHotKey(self, 'q');
 155        newtFormAddHotKey(self, CTRL('c'));
 156}
 157
 158static newtComponent newt_form__new(void)
 159{
 160        newtComponent self = newtForm(NULL, NULL, 0);
 161        if (self)
 162                newt_form__set_exit_keys(self);
 163        return self;
 164}
 165
 166static int popup_menu(int argc, char * const argv[])
 167{
 168        struct newtExitStruct es;
 169        int i, rc = -1, max_len = 5;
 170        newtComponent listbox, form = newt_form__new();
 171
 172        if (form == NULL)
 173                return -1;
 174
 175        listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
 176        if (listbox == NULL)
 177                goto out_destroy_form;
 178
 179        newtFormAddComponent(form, listbox);
 180
 181        for (i = 0; i < argc; ++i) {
 182                int len = strlen(argv[i]);
 183                if (len > max_len)
 184                        max_len = len;
 185                if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
 186                        goto out_destroy_form;
 187        }
 188
 189        newtCenteredWindow(max_len, argc, NULL);
 190        newtFormRun(form, &es);
 191        rc = newtListboxGetCurrent(listbox) - NULL;
 192        if (es.reason == NEWT_EXIT_HOTKEY)
 193                rc = -1;
 194        newtPopWindow();
 195out_destroy_form:
 196        newtFormDestroy(form);
 197        return rc;
 198}
 199
 200static int ui__help_window(const char *text)
 201{
 202        struct newtExitStruct es;
 203        newtComponent tb, form = newt_form__new();
 204        int rc = -1;
 205        int max_len = 0, nr_lines = 0;
 206        const char *t;
 207
 208        if (form == NULL)
 209                return -1;
 210
 211        t = text;
 212        while (1) {
 213                const char *sep = strchr(t, '\n');
 214                int len;
 215
 216                if (sep == NULL)
 217                        sep = strchr(t, '\0');
 218                len = sep - t;
 219                if (max_len < len)
 220                        max_len = len;
 221                ++nr_lines;
 222                if (*sep == '\0')
 223                        break;
 224                t = sep + 1;
 225        }
 226
 227        tb = newtTextbox(0, 0, max_len, nr_lines, 0);
 228        if (tb == NULL)
 229                goto out_destroy_form;
 230
 231        newtTextboxSetText(tb, text);
 232        newtFormAddComponent(form, tb);
 233        newtCenteredWindow(max_len, nr_lines, NULL);
 234        newtFormRun(form, &es);
 235        newtPopWindow();
 236        rc = 0;
 237out_destroy_form:
 238        newtFormDestroy(form);
 239        return rc;
 240}
 241
 242static bool dialog_yesno(const char *msg)
 243{
 244        /* newtWinChoice should really be accepting const char pointers... */
 245        char yes[] = "Yes", no[] = "No";
 246        return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
 247}
 248
 249static void ui__error_window(const char *fmt, ...)
 250{
 251        va_list ap;
 252
 253        va_start(ap, fmt);
 254        newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
 255        va_end(ap);
 256}
 257
 258#define HE_COLORSET_TOP         50
 259#define HE_COLORSET_MEDIUM      51
 260#define HE_COLORSET_NORMAL      52
 261#define HE_COLORSET_SELECTED    53
 262#define HE_COLORSET_CODE        54
 263
 264static int ui_browser__percent_color(double percent, bool current)
 265{
 266        if (current)
 267                return HE_COLORSET_SELECTED;
 268        if (percent >= MIN_RED)
 269                return HE_COLORSET_TOP;
 270        if (percent >= MIN_GREEN)
 271                return HE_COLORSET_MEDIUM;
 272        return HE_COLORSET_NORMAL;
 273}
 274
 275struct ui_browser {
 276        newtComponent   form, sb;
 277        u64             index, first_visible_entry_idx;
 278        void            *first_visible_entry, *entries;
 279        u16             top, left, width, height;
 280        void            *priv;
 281        u32             nr_entries;
 282};
 283
 284static void ui_browser__refresh_dimensions(struct ui_browser *self)
 285{
 286        int cols, rows;
 287        newtGetScreenSize(&cols, &rows);
 288
 289        if (self->width > cols - 4)
 290                self->width = cols - 4;
 291        self->height = rows - 5;
 292        if (self->height > self->nr_entries)
 293                self->height = self->nr_entries;
 294        self->top  = (rows - self->height) / 2;
 295        self->left = (cols - self->width) / 2;
 296}
 297
 298static void ui_browser__reset_index(struct ui_browser *self)
 299{
 300        self->index = self->first_visible_entry_idx = 0;
 301        self->first_visible_entry = NULL;
 302}
 303
 304static int objdump_line__show(struct objdump_line *self, struct list_head *head,
 305                              int width, struct hist_entry *he, int len,
 306                              bool current_entry)
 307{
 308        if (self->offset != -1) {
 309                struct symbol *sym = he->ms.sym;
 310                unsigned int hits = 0;
 311                double percent = 0.0;
 312                int color;
 313                struct sym_priv *priv = symbol__priv(sym);
 314                struct sym_ext *sym_ext = priv->ext;
 315                struct sym_hist *h = priv->hist;
 316                s64 offset = self->offset;
 317                struct objdump_line *next = objdump__get_next_ip_line(head, self);
 318
 319                while (offset < (s64)len &&
 320                       (next == NULL || offset < next->offset)) {
 321                        if (sym_ext) {
 322                                percent += sym_ext[offset].percent;
 323                        } else
 324                                hits += h->ip[offset];
 325
 326                        ++offset;
 327                }
 328
 329                if (sym_ext == NULL && h->sum)
 330                        percent = 100.0 * hits / h->sum;
 331
 332                color = ui_browser__percent_color(percent, current_entry);
 333                SLsmg_set_color(color);
 334                slsmg_printf(" %7.2f ", percent);
 335                if (!current_entry)
 336                        SLsmg_set_color(HE_COLORSET_CODE);
 337        } else {
 338                int color = ui_browser__percent_color(0, current_entry);
 339                SLsmg_set_color(color);
 340                slsmg_write_nstring(" ", 9);
 341        }
 342
 343        SLsmg_write_char(':');
 344        slsmg_write_nstring(" ", 8);
 345        if (!*self->line)
 346                slsmg_write_nstring(" ", width - 18);
 347        else
 348                slsmg_write_nstring(self->line, width - 18);
 349
 350        return 0;
 351}
 352
 353static int ui_browser__refresh_entries(struct ui_browser *self)
 354{
 355        struct objdump_line *pos;
 356        struct list_head *head = self->entries;
 357        struct hist_entry *he = self->priv;
 358        int row = 0;
 359        int len = he->ms.sym->end - he->ms.sym->start;
 360
 361        if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
 362                self->first_visible_entry = head->next;
 363
 364        pos = list_entry(self->first_visible_entry, struct objdump_line, node);
 365
 366        list_for_each_entry_from(pos, head, node) {
 367                bool current_entry = (self->first_visible_entry_idx + row) == self->index;
 368                SLsmg_gotorc(self->top + row, self->left);
 369                objdump_line__show(pos, head, self->width,
 370                                   he, len, current_entry);
 371                if (++row == self->height)
 372                        break;
 373        }
 374
 375        SLsmg_set_color(HE_COLORSET_NORMAL);
 376        SLsmg_fill_region(self->top + row, self->left,
 377                          self->height - row, self->width, ' ');
 378
 379        return 0;
 380}
 381
 382static int ui_browser__run(struct ui_browser *self, const char *title,
 383                           struct newtExitStruct *es)
 384{
 385        if (self->form) {
 386                newtFormDestroy(self->form);
 387                newtPopWindow();
 388        }
 389
 390        ui_browser__refresh_dimensions(self);
 391        newtCenteredWindow(self->width + 2, self->height, title);
 392        self->form = newt_form__new();
 393        if (self->form == NULL)
 394                return -1;
 395
 396        self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
 397                                         HE_COLORSET_NORMAL,
 398                                         HE_COLORSET_SELECTED);
 399        if (self->sb == NULL)
 400                return -1;
 401
 402        newtFormAddHotKey(self->form, NEWT_KEY_UP);
 403        newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
 404        newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
 405        newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
 406        newtFormAddHotKey(self->form, ' ');
 407        newtFormAddHotKey(self->form, NEWT_KEY_HOME);
 408        newtFormAddHotKey(self->form, NEWT_KEY_END);
 409        newtFormAddHotKey(self->form, NEWT_KEY_TAB);
 410        newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
 411
 412        if (ui_browser__refresh_entries(self) < 0)
 413                return -1;
 414        newtFormAddComponent(self->form, self->sb);
 415
 416        while (1) {
 417                unsigned int offset;
 418
 419                newtFormRun(self->form, es);
 420
 421                if (es->reason != NEWT_EXIT_HOTKEY)
 422                        break;
 423                if (is_exit_key(es->u.key))
 424                        return es->u.key;
 425                switch (es->u.key) {
 426                case NEWT_KEY_DOWN:
 427                        if (self->index == self->nr_entries - 1)
 428                                break;
 429                        ++self->index;
 430                        if (self->index == self->first_visible_entry_idx + self->height) {
 431                                struct list_head *pos = self->first_visible_entry;
 432                                ++self->first_visible_entry_idx;
 433                                self->first_visible_entry = pos->next;
 434                        }
 435                        break;
 436                case NEWT_KEY_UP:
 437                        if (self->index == 0)
 438                                break;
 439                        --self->index;
 440                        if (self->index < self->first_visible_entry_idx) {
 441                                struct list_head *pos = self->first_visible_entry;
 442                                --self->first_visible_entry_idx;
 443                                self->first_visible_entry = pos->prev;
 444                        }
 445                        break;
 446                case NEWT_KEY_PGDN:
 447                case ' ':
 448                        if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
 449                                break;
 450
 451                        offset = self->height;
 452                        if (self->index + offset > self->nr_entries - 1)
 453                                offset = self->nr_entries - 1 - self->index;
 454                        self->index += offset;
 455                        self->first_visible_entry_idx += offset;
 456
 457                        while (offset--) {
 458                                struct list_head *pos = self->first_visible_entry;
 459                                self->first_visible_entry = pos->next;
 460                        }
 461
 462                        break;
 463                case NEWT_KEY_PGUP:
 464                        if (self->first_visible_entry_idx == 0)
 465                                break;
 466
 467                        if (self->first_visible_entry_idx < self->height)
 468                                offset = self->first_visible_entry_idx;
 469                        else
 470                                offset = self->height;
 471
 472                        self->index -= offset;
 473                        self->first_visible_entry_idx -= offset;
 474
 475                        while (offset--) {
 476                                struct list_head *pos = self->first_visible_entry;
 477                                self->first_visible_entry = pos->prev;
 478                        }
 479                        break;
 480                case NEWT_KEY_HOME:
 481                        ui_browser__reset_index(self);
 482                        break;
 483                case NEWT_KEY_END: {
 484                        struct list_head *head = self->entries;
 485                        offset = self->height - 1;
 486
 487                        if (offset > self->nr_entries)
 488                                offset = self->nr_entries;
 489
 490                        self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
 491                        self->first_visible_entry = head->prev;
 492                        while (offset-- != 0) {
 493                                struct list_head *pos = self->first_visible_entry;
 494                                self->first_visible_entry = pos->prev;
 495                        }
 496                }
 497                        break;
 498                case NEWT_KEY_RIGHT:
 499                case NEWT_KEY_LEFT:
 500                case NEWT_KEY_TAB:
 501                        return es->u.key;
 502                default:
 503                        continue;
 504                }
 505                if (ui_browser__refresh_entries(self) < 0)
 506                        return -1;
 507        }
 508        return 0;
 509}
 510
 511/*
 512 * When debugging newt problems it was useful to be able to "unroll"
 513 * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
 514 * a source file with the sequence of calls to these methods, to then
 515 * tweak the arrays to get the intended results, so I'm keeping this code
 516 * here, may be useful again in the future.
 517 */
 518#undef NEWT_DEBUG
 519
 520static void newt_checkbox_tree__add(newtComponent tree, const char *str,
 521                                    void *priv, int *indexes)
 522{
 523#ifdef NEWT_DEBUG
 524        /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
 525        int i = 0, len = 40 - strlen(str);
 526
 527        fprintf(stderr,
 528                "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
 529                len, len, " ", str, priv);
 530        while (indexes[i] != NEWT_ARG_LAST) {
 531                if (indexes[i] != NEWT_ARG_APPEND)
 532                        fprintf(stderr, " %d,", indexes[i]);
 533                else
 534                        fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
 535                ++i;
 536        }
 537        fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
 538        fflush(stderr);
 539#endif
 540        newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
 541}
 542
 543static char *callchain_list__sym_name(struct callchain_list *self,
 544                                      char *bf, size_t bfsize)
 545{
 546        if (self->ms.sym)
 547                return self->ms.sym->name;
 548
 549        snprintf(bf, bfsize, "%#Lx", self->ip);
 550        return bf;
 551}
 552
 553static void __callchain__append_graph_browser(struct callchain_node *self,
 554                                              newtComponent tree, u64 total,
 555                                              int *indexes, int depth)
 556{
 557        struct rb_node *node;
 558        u64 new_total, remaining;
 559        int idx = 0;
 560
 561        if (callchain_param.mode == CHAIN_GRAPH_REL)
 562                new_total = self->children_hit;
 563        else
 564                new_total = total;
 565
 566        remaining = new_total;
 567        node = rb_first(&self->rb_root);
 568        while (node) {
 569                struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
 570                struct rb_node *next = rb_next(node);
 571                u64 cumul = cumul_hits(child);
 572                struct callchain_list *chain;
 573                int first = true, printed = 0;
 574                int chain_idx = -1;
 575                remaining -= cumul;
 576
 577                indexes[depth] = NEWT_ARG_APPEND;
 578                indexes[depth + 1] = NEWT_ARG_LAST;
 579
 580                list_for_each_entry(chain, &child->val, list) {
 581                        char ipstr[BITS_PER_LONG / 4 + 1],
 582                             *alloc_str = NULL;
 583                        const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
 584
 585                        if (first) {
 586                                double percent = cumul * 100.0 / new_total;
 587
 588                                first = false;
 589                                if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
 590                                        str = "Not enough memory!";
 591                                else
 592                                        str = alloc_str;
 593                        } else {
 594                                indexes[depth] = idx;
 595                                indexes[depth + 1] = NEWT_ARG_APPEND;
 596                                indexes[depth + 2] = NEWT_ARG_LAST;
 597                                ++chain_idx;
 598                        }
 599                        newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
 600                        free(alloc_str);
 601                        ++printed;
 602                }
 603
 604                indexes[depth] = idx;
 605                if (chain_idx != -1)
 606                        indexes[depth + 1] = chain_idx;
 607                if (printed != 0)
 608                        ++idx;
 609                __callchain__append_graph_browser(child, tree, new_total, indexes,
 610                                                  depth + (chain_idx != -1 ? 2 : 1));
 611                node = next;
 612        }
 613}
 614
 615static void callchain__append_graph_browser(struct callchain_node *self,
 616                                            newtComponent tree, u64 total,
 617                                            int *indexes, int parent_idx)
 618{
 619        struct callchain_list *chain;
 620        int i = 0;
 621
 622        indexes[1] = NEWT_ARG_APPEND;
 623        indexes[2] = NEWT_ARG_LAST;
 624
 625        list_for_each_entry(chain, &self->val, list) {
 626                char ipstr[BITS_PER_LONG / 4 + 1], *str;
 627
 628                if (chain->ip >= PERF_CONTEXT_MAX)
 629                        continue;
 630
 631                if (!i++ && sort__first_dimension == SORT_SYM)
 632                        continue;
 633
 634                str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
 635                newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
 636        }
 637
 638        indexes[1] = parent_idx;
 639        indexes[2] = NEWT_ARG_APPEND;
 640        indexes[3] = NEWT_ARG_LAST;
 641        __callchain__append_graph_browser(self, tree, total, indexes, 2);
 642}
 643
 644static void hist_entry__append_callchain_browser(struct hist_entry *self,
 645                                                 newtComponent tree, u64 total, int parent_idx)
 646{
 647        struct rb_node *rb_node;
 648        int indexes[1024] = { [0] = parent_idx, };
 649        int idx = 0;
 650        struct callchain_node *chain;
 651
 652        rb_node = rb_first(&self->sorted_chain);
 653        while (rb_node) {
 654                chain = rb_entry(rb_node, struct callchain_node, rb_node);
 655                switch (callchain_param.mode) {
 656                case CHAIN_FLAT:
 657                        break;
 658                case CHAIN_GRAPH_ABS: /* falldown */
 659                case CHAIN_GRAPH_REL:
 660                        callchain__append_graph_browser(chain, tree, total, indexes, idx++);
 661                        break;
 662                case CHAIN_NONE:
 663                default:
 664                        break;
 665                }
 666                rb_node = rb_next(rb_node);
 667        }
 668}
 669
 670static size_t hist_entry__append_browser(struct hist_entry *self,
 671                                         newtComponent tree, u64 total)
 672{
 673        char s[256];
 674        size_t ret;
 675
 676        if (symbol_conf.exclude_other && !self->parent)
 677                return 0;
 678
 679        ret = hist_entry__snprintf(self, s, sizeof(s), NULL,
 680                                   false, 0, false, total);
 681        if (symbol_conf.use_callchain) {
 682                int indexes[2];
 683
 684                indexes[0] = NEWT_ARG_APPEND;
 685                indexes[1] = NEWT_ARG_LAST;
 686                newt_checkbox_tree__add(tree, s, &self->ms, indexes);
 687        } else
 688                newtListboxAppendEntry(tree, s, &self->ms);
 689
 690        return ret;
 691}
 692
 693int hist_entry__tui_annotate(struct hist_entry *self)
 694{
 695        struct ui_browser browser;
 696        struct newtExitStruct es;
 697        struct objdump_line *pos, *n;
 698        LIST_HEAD(head);
 699        int ret;
 700
 701        if (self->ms.sym == NULL)
 702                return -1;
 703
 704        if (self->ms.map->dso->annotate_warned)
 705                return -1;
 706
 707        if (hist_entry__annotate(self, &head) < 0) {
 708                ui__error_window(browser__last_msg);
 709                return -1;
 710        }
 711
 712        ui_helpline__push("Press <- or ESC to exit");
 713
 714        memset(&browser, 0, sizeof(browser));
 715        browser.entries = &head;
 716        browser.priv = self;
 717        list_for_each_entry(pos, &head, node) {
 718                size_t line_len = strlen(pos->line);
 719                if (browser.width < line_len)
 720                        browser.width = line_len;
 721                ++browser.nr_entries;
 722        }
 723
 724        browser.width += 18; /* Percentage */
 725        ret = ui_browser__run(&browser, self->ms.sym->name, &es);
 726        newtFormDestroy(browser.form);
 727        newtPopWindow();
 728        list_for_each_entry_safe(pos, n, &head, node) {
 729                list_del(&pos->node);
 730                objdump_line__free(pos);
 731        }
 732        ui_helpline__pop();
 733        return ret;
 734}
 735
 736static const void *newt__symbol_tree_get_current(newtComponent self)
 737{
 738        if (symbol_conf.use_callchain)
 739                return newtCheckboxTreeGetCurrent(self);
 740        return newtListboxGetCurrent(self);
 741}
 742
 743static void hist_browser__selection(newtComponent self, void *data)
 744{
 745        const struct map_symbol **symbol_ptr = data;
 746        *symbol_ptr = newt__symbol_tree_get_current(self);
 747}
 748
 749struct hist_browser {
 750        newtComponent           form, tree;
 751        const struct map_symbol *selection;
 752};
 753
 754static struct hist_browser *hist_browser__new(void)
 755{
 756        struct hist_browser *self = malloc(sizeof(*self));
 757
 758        if (self != NULL)
 759                self->form = NULL;
 760
 761        return self;
 762}
 763
 764static void hist_browser__delete(struct hist_browser *self)
 765{
 766        newtFormDestroy(self->form);
 767        newtPopWindow();
 768        free(self);
 769}
 770
 771static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
 772                                  const char *title)
 773{
 774        int max_len = 0, idx, cols, rows;
 775        struct ui_progress *progress;
 776        struct rb_node *nd;
 777        u64 curr_hist = 0;
 778        char seq[] = ".", unit;
 779        char str[256];
 780        unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
 781
 782        if (self->form) {
 783                newtFormDestroy(self->form);
 784                newtPopWindow();
 785        }
 786
 787        nr_events = convert_unit(nr_events, &unit);
 788        snprintf(str, sizeof(str), "Events: %lu%c                            ",
 789                 nr_events, unit);
 790        newtDrawRootText(0, 0, str);
 791
 792        newtGetScreenSize(NULL, &rows);
 793
 794        if (symbol_conf.use_callchain)
 795                self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
 796                                                   NEWT_FLAG_SCROLL);
 797        else
 798                self->tree = newtListbox(0, 0, rows - 5,
 799                                        (NEWT_FLAG_SCROLL |
 800                                         NEWT_FLAG_RETURNEXIT));
 801
 802        newtComponentAddCallback(self->tree, hist_browser__selection,
 803                                 &self->selection);
 804
 805        progress = ui_progress__new("Adding entries to the browser...",
 806                                    hists->nr_entries);
 807        if (progress == NULL)
 808                return -1;
 809
 810        idx = 0;
 811        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 812                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 813                int len;
 814
 815                if (h->filtered)
 816                        continue;
 817
 818                len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
 819                if (len > max_len)
 820                        max_len = len;
 821                if (symbol_conf.use_callchain)
 822                        hist_entry__append_callchain_browser(h, self->tree,
 823                                                             hists->stats.total_period, idx++);
 824                ++curr_hist;
 825                if (curr_hist % 5)
 826                        ui_progress__update(progress, curr_hist);
 827        }
 828
 829        ui_progress__delete(progress);
 830
 831        newtGetScreenSize(&cols, &rows);
 832
 833        if (max_len > cols)
 834                max_len = cols - 3;
 835
 836        if (!symbol_conf.use_callchain)
 837                newtListboxSetWidth(self->tree, max_len);
 838
 839        newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
 840                           rows - 5, title);
 841        self->form = newt_form__new();
 842        if (self->form == NULL)
 843                return -1;
 844
 845        newtFormAddHotKey(self->form, 'A');
 846        newtFormAddHotKey(self->form, 'a');
 847        newtFormAddHotKey(self->form, 'D');
 848        newtFormAddHotKey(self->form, 'd');
 849        newtFormAddHotKey(self->form, 'T');
 850        newtFormAddHotKey(self->form, 't');
 851        newtFormAddHotKey(self->form, '?');
 852        newtFormAddHotKey(self->form, 'H');
 853        newtFormAddHotKey(self->form, 'h');
 854        newtFormAddHotKey(self->form, NEWT_KEY_F1);
 855        newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
 856        newtFormAddHotKey(self->form, NEWT_KEY_TAB);
 857        newtFormAddHotKey(self->form, NEWT_KEY_UNTAB);
 858        newtFormAddComponents(self->form, self->tree, NULL);
 859        self->selection = newt__symbol_tree_get_current(self->tree);
 860
 861        return 0;
 862}
 863
 864static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
 865{
 866        int *indexes;
 867
 868        if (!symbol_conf.use_callchain)
 869                goto out;
 870
 871        indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
 872        if (indexes) {
 873                bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
 874                free(indexes);
 875                if (is_hist_entry)
 876                        goto out;
 877        }
 878        return NULL;
 879out:
 880        return container_of(self->selection, struct hist_entry, ms);
 881}
 882
 883static struct thread *hist_browser__selected_thread(struct hist_browser *self)
 884{
 885        struct hist_entry *he = hist_browser__selected_entry(self);
 886        return he ? he->thread : NULL;
 887}
 888
 889static int hist_browser__title(char *bf, size_t size, const char *ev_name,
 890                               const struct dso *dso, const struct thread *thread)
 891{
 892        int printed = 0;
 893
 894        if (thread)
 895                printed += snprintf(bf + printed, size - printed,
 896                                    "Thread: %s(%d)",
 897                                    (thread->comm_set ?  thread->comm : ""),
 898                                    thread->pid);
 899        if (dso)
 900                printed += snprintf(bf + printed, size - printed,
 901                                    "%sDSO: %s", thread ? " " : "",
 902                                    dso->short_name);
 903        return printed ?: snprintf(bf, size, "Event: %s", ev_name);
 904}
 905
 906int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
 907{
 908        struct hist_browser *browser = hist_browser__new();
 909        struct pstack *fstack;
 910        const struct thread *thread_filter = NULL;
 911        const struct dso *dso_filter = NULL;
 912        struct newtExitStruct es;
 913        char msg[160];
 914        int key = -1;
 915
 916        if (browser == NULL)
 917                return -1;
 918
 919        fstack = pstack__new(2);
 920        if (fstack == NULL)
 921                goto out;
 922
 923        ui_helpline__push(helpline);
 924
 925        hist_browser__title(msg, sizeof(msg), ev_name,
 926                            dso_filter, thread_filter);
 927        if (hist_browser__populate(browser, self, msg) < 0)
 928                goto out_free_stack;
 929
 930        while (1) {
 931                const struct thread *thread;
 932                const struct dso *dso;
 933                char *options[16];
 934                int nr_options = 0, choice = 0, i,
 935                    annotate = -2, zoom_dso = -2, zoom_thread = -2;
 936
 937                newtFormRun(browser->form, &es);
 938
 939                thread = hist_browser__selected_thread(browser);
 940                dso = browser->selection->map ? browser->selection->map->dso : NULL;
 941
 942                if (es.reason == NEWT_EXIT_HOTKEY) {
 943                        key = es.u.key;
 944
 945                        switch (key) {
 946                        case NEWT_KEY_F1:
 947                                goto do_help;
 948                        case NEWT_KEY_TAB:
 949                        case NEWT_KEY_UNTAB:
 950                                /*
 951                                 * Exit the browser, let hists__browser_tree
 952                                 * go to the next or previous
 953                                 */
 954                                goto out_free_stack;
 955                        default:;
 956                        }
 957
 958                        key = toupper(key);
 959                        switch (key) {
 960                        case 'A':
 961                                if (browser->selection->map == NULL &&
 962                                    browser->selection->map->dso->annotate_warned)
 963                                        continue;
 964                                goto do_annotate;
 965                        case 'D':
 966                                goto zoom_dso;
 967                        case 'T':
 968                                goto zoom_thread;
 969                        case 'H':
 970                        case '?':
 971do_help:
 972                                ui__help_window("->        Zoom into DSO/Threads & Annotate current symbol\n"
 973                                                "<-        Zoom out\n"
 974                                                "a         Annotate current symbol\n"
 975                                                "h/?/F1    Show this window\n"
 976                                                "d         Zoom into current DSO\n"
 977                                                "t         Zoom into current Thread\n"
 978                                                "q/CTRL+C  Exit browser");
 979                                continue;
 980                        default:;
 981                        }
 982                        if (is_exit_key(key)) {
 983                                if (key == NEWT_KEY_ESCAPE) {
 984                                        if (dialog_yesno("Do you really want to exit?"))
 985                                                break;
 986                                        else
 987                                                continue;
 988                                } else
 989                                        break;
 990                        }
 991
 992                        if (es.u.key == NEWT_KEY_LEFT) {
 993                                const void *top;
 994
 995                                if (pstack__empty(fstack))
 996                                        continue;
 997                                top = pstack__pop(fstack);
 998                                if (top == &dso_filter)
 999                                        goto zoom_out_dso;
1000                                if (top == &thread_filter)
1001                                        goto zoom_out_thread;
1002                                continue;
1003                        }
1004                }
1005
1006                if (browser->selection->sym != NULL &&
1007                    !browser->selection->map->dso->annotate_warned &&
1008                    asprintf(&options[nr_options], "Annotate %s",
1009                             browser->selection->sym->name) > 0)
1010                        annotate = nr_options++;
1011
1012                if (thread != NULL &&
1013                    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1014                             (thread_filter ? "out of" : "into"),
1015                             (thread->comm_set ? thread->comm : ""),
1016                             thread->pid) > 0)
1017                        zoom_thread = nr_options++;
1018
1019                if (dso != NULL &&
1020                    asprintf(&options[nr_options], "Zoom %s %s DSO",
1021                             (dso_filter ? "out of" : "into"),
1022                             (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1023                        zoom_dso = nr_options++;
1024
1025                options[nr_options++] = (char *)"Exit";
1026
1027                choice = popup_menu(nr_options, options);
1028
1029                for (i = 0; i < nr_options - 1; ++i)
1030                        free(options[i]);
1031
1032                if (choice == nr_options - 1)
1033                        break;
1034
1035                if (choice == -1)
1036                        continue;
1037
1038                if (choice == annotate) {
1039                        struct hist_entry *he;
1040do_annotate:
1041                        if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
1042                                browser->selection->map->dso->annotate_warned = 1;
1043                                ui_helpline__puts("No vmlinux file found, can't "
1044                                                 "annotate with just a "
1045                                                 "kallsyms file");
1046                                continue;
1047                        }
1048
1049                        he = hist_browser__selected_entry(browser);
1050                        if (he == NULL)
1051                                continue;
1052
1053                        hist_entry__tui_annotate(he);
1054                } else if (choice == zoom_dso) {
1055zoom_dso:
1056                        if (dso_filter) {
1057                                pstack__remove(fstack, &dso_filter);
1058zoom_out_dso:
1059                                ui_helpline__pop();
1060                                dso_filter = NULL;
1061                        } else {
1062                                if (dso == NULL)
1063                                        continue;
1064                                ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1065                                                   dso->kernel ? "the Kernel" : dso->short_name);
1066                                dso_filter = dso;
1067                                pstack__push(fstack, &dso_filter);
1068                        }
1069                        hists__filter_by_dso(self, dso_filter);
1070                        hist_browser__title(msg, sizeof(msg), ev_name,
1071                                            dso_filter, thread_filter);
1072                        if (hist_browser__populate(browser, self, msg) < 0)
1073                                goto out;
1074                } else if (choice == zoom_thread) {
1075zoom_thread:
1076                        if (thread_filter) {
1077                                pstack__remove(fstack, &thread_filter);
1078zoom_out_thread:
1079                                ui_helpline__pop();
1080                                thread_filter = NULL;
1081                        } else {
1082                                ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1083                                                   thread->comm_set ? thread->comm : "",
1084                                                   thread->pid);
1085                                thread_filter = thread;
1086                                pstack__push(fstack, &thread_filter);
1087                        }
1088                        hists__filter_by_thread(self, thread_filter);
1089                        hist_browser__title(msg, sizeof(msg), ev_name,
1090                                            dso_filter, thread_filter);
1091                        if (hist_browser__populate(browser, self, msg) < 0)
1092                                goto out;
1093                }
1094        }
1095out_free_stack:
1096        pstack__delete(fstack);
1097out:
1098        hist_browser__delete(browser);
1099        return key;
1100}
1101
1102int hists__tui_browse_tree(struct rb_root *self, const char *help)
1103{
1104        struct rb_node *first = rb_first(self), *nd = first, *next;
1105        int key = 0;
1106
1107        while (nd) {
1108                struct hists *hists = rb_entry(nd, struct hists, rb_node);
1109                const char *ev_name = __event_name(hists->type, hists->config);
1110
1111                key = hists__browse(hists, help, ev_name);
1112
1113                if (is_exit_key(key))
1114                        break;
1115
1116                switch (key) {
1117                case NEWT_KEY_TAB:
1118                        next = rb_next(nd);
1119                        if (next)
1120                                nd = next;
1121                        break;
1122                case NEWT_KEY_UNTAB:
1123                        if (nd == first)
1124                                continue;
1125                        nd = rb_prev(nd);
1126                default:
1127                        break;
1128                }
1129        }
1130
1131        return key;
1132}
1133
1134static struct newtPercentTreeColors {
1135        const char *topColorFg, *topColorBg;
1136        const char *mediumColorFg, *mediumColorBg;
1137        const char *normalColorFg, *normalColorBg;
1138        const char *selColorFg, *selColorBg;
1139        const char *codeColorFg, *codeColorBg;
1140} defaultPercentTreeColors = {
1141        "red",       "lightgray",
1142        "green",     "lightgray",
1143        "black",     "lightgray",
1144        "lightgray", "magenta",
1145        "blue",      "lightgray",
1146};
1147
1148void setup_browser(void)
1149{
1150        struct newtPercentTreeColors *c = &defaultPercentTreeColors;
1151
1152        if (!isatty(1) || !use_browser || dump_trace) {
1153                use_browser = 0;
1154                setup_pager();
1155                return;
1156        }
1157
1158        use_browser = 1;
1159        newtInit();
1160        newtCls();
1161        ui_helpline__puts(" ");
1162        sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
1163        sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
1164        sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
1165        sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
1166        sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
1167}
1168
1169void exit_browser(bool wait_for_ok)
1170{
1171        if (use_browser > 0) {
1172                if (wait_for_ok) {
1173                        char title[] = "Fatal Error", ok[] = "Ok";
1174                        newtWinMessage(title, ok, browser__last_msg);
1175                }
1176                newtFinished();
1177        }
1178}
1179
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.