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