Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

721
tools/perf/ui/browser.c Normal file
View File

@@ -0,0 +1,721 @@
#include "../util.h"
#include "../cache.h"
#include "../../perf.h"
#include "libslang.h"
#include "ui.h"
#include "util.h"
#include <linux/compiler.h>
#include <linux/list.h>
#include <linux/rbtree.h>
#include <stdlib.h>
#include <sys/ttydefaults.h>
#include "browser.h"
#include "helpline.h"
#include "keysyms.h"
#include "../color.h"
static int ui_browser__percent_color(struct ui_browser *browser,
double percent, bool current)
{
if (current && (!browser->use_navkeypressed || browser->navkeypressed))
return HE_COLORSET_SELECTED;
if (percent >= MIN_RED)
return HE_COLORSET_TOP;
if (percent >= MIN_GREEN)
return HE_COLORSET_MEDIUM;
return HE_COLORSET_NORMAL;
}
int ui_browser__set_color(struct ui_browser *browser, int color)
{
int ret = browser->current_color;
browser->current_color = color;
SLsmg_set_color(color);
return ret;
}
void ui_browser__set_percent_color(struct ui_browser *browser,
double percent, bool current)
{
int color = ui_browser__percent_color(browser, percent, current);
ui_browser__set_color(browser, color);
}
void ui_browser__gotorc(struct ui_browser *browser, int y, int x)
{
SLsmg_gotorc(browser->y + y, browser->x + x);
}
static struct list_head *
ui_browser__list_head_filter_entries(struct ui_browser *browser,
struct list_head *pos)
{
do {
if (!browser->filter || !browser->filter(browser, pos))
return pos;
pos = pos->next;
} while (pos != browser->entries);
return NULL;
}
static struct list_head *
ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
struct list_head *pos)
{
do {
if (!browser->filter || !browser->filter(browser, pos))
return pos;
pos = pos->prev;
} while (pos != browser->entries);
return NULL;
}
void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence)
{
struct list_head *head = browser->entries;
struct list_head *pos;
if (browser->nr_entries == 0)
return;
switch (whence) {
case SEEK_SET:
pos = ui_browser__list_head_filter_entries(browser, head->next);
break;
case SEEK_CUR:
pos = browser->top;
break;
case SEEK_END:
pos = ui_browser__list_head_filter_prev_entries(browser, head->prev);
break;
default:
return;
}
assert(pos != NULL);
if (offset > 0) {
while (offset-- != 0)
pos = ui_browser__list_head_filter_entries(browser, pos->next);
} else {
while (offset++ != 0)
pos = ui_browser__list_head_filter_prev_entries(browser, pos->prev);
}
browser->top = pos;
}
void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence)
{
struct rb_root *root = browser->entries;
struct rb_node *nd;
switch (whence) {
case SEEK_SET:
nd = rb_first(root);
break;
case SEEK_CUR:
nd = browser->top;
break;
case SEEK_END:
nd = rb_last(root);
break;
default:
return;
}
if (offset > 0) {
while (offset-- != 0)
nd = rb_next(nd);
} else {
while (offset++ != 0)
nd = rb_prev(nd);
}
browser->top = nd;
}
unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser)
{
struct rb_node *nd;
int row = 0;
if (browser->top == NULL)
browser->top = rb_first(browser->entries);
nd = browser->top;
while (nd != NULL) {
ui_browser__gotorc(browser, row, 0);
browser->write(browser, nd, row);
if (++row == browser->height)
break;
nd = rb_next(nd);
}
return row;
}
bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row)
{
return browser->top_idx + row == browser->index;
}
void ui_browser__refresh_dimensions(struct ui_browser *browser)
{
browser->width = SLtt_Screen_Cols - 1;
browser->height = SLtt_Screen_Rows - 2;
browser->y = 1;
browser->x = 0;
}
void ui_browser__handle_resize(struct ui_browser *browser)
{
ui__refresh_dimensions(false);
ui_browser__show(browser, browser->title, ui_helpline__current);
ui_browser__refresh(browser);
}
int ui_browser__warning(struct ui_browser *browser, int timeout,
const char *format, ...)
{
va_list args;
char *text;
int key = 0, err;
va_start(args, format);
err = vasprintf(&text, format, args);
va_end(args);
if (err < 0) {
va_start(args, format);
ui_helpline__vpush(format, args);
va_end(args);
} else {
while ((key == ui__question_window("Warning!", text,
"Press any key...",
timeout)) == K_RESIZE)
ui_browser__handle_resize(browser);
free(text);
}
return key;
}
int ui_browser__help_window(struct ui_browser *browser, const char *text)
{
int key;
while ((key = ui__help_window(text)) == K_RESIZE)
ui_browser__handle_resize(browser);
return key;
}
bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
{
int key;
while ((key = ui__dialog_yesno(text)) == K_RESIZE)
ui_browser__handle_resize(browser);
return key == K_ENTER || toupper(key) == 'Y';
}
void ui_browser__reset_index(struct ui_browser *browser)
{
browser->index = browser->top_idx = 0;
browser->seek(browser, 0, SEEK_SET);
}
void __ui_browser__show_title(struct ui_browser *browser, const char *title)
{
SLsmg_gotorc(0, 0);
ui_browser__set_color(browser, HE_COLORSET_ROOT);
slsmg_write_nstring(title, browser->width + 1);
}
void ui_browser__show_title(struct ui_browser *browser, const char *title)
{
pthread_mutex_lock(&ui__lock);
__ui_browser__show_title(browser, title);
pthread_mutex_unlock(&ui__lock);
}
int ui_browser__show(struct ui_browser *browser, const char *title,
const char *helpline, ...)
{
int err;
va_list ap;
ui_browser__refresh_dimensions(browser);
pthread_mutex_lock(&ui__lock);
__ui_browser__show_title(browser, title);
browser->title = title;
free(browser->helpline);
browser->helpline = NULL;
va_start(ap, helpline);
err = vasprintf(&browser->helpline, helpline, ap);
va_end(ap);
if (err > 0)
ui_helpline__push(browser->helpline);
pthread_mutex_unlock(&ui__lock);
return err ? 0 : -1;
}
void ui_browser__hide(struct ui_browser *browser __maybe_unused)
{
pthread_mutex_lock(&ui__lock);
ui_helpline__pop();
free(browser->helpline);
browser->helpline = NULL;
pthread_mutex_unlock(&ui__lock);
}
static void ui_browser__scrollbar_set(struct ui_browser *browser)
{
int height = browser->height, h = 0, pct = 0,
col = browser->width,
row = browser->y - 1;
if (browser->nr_entries > 1) {
pct = ((browser->index * (browser->height - 1)) /
(browser->nr_entries - 1));
}
SLsmg_set_char_set(1);
while (h < height) {
ui_browser__gotorc(browser, row++, col);
SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
++h;
}
SLsmg_set_char_set(0);
}
static int __ui_browser__refresh(struct ui_browser *browser)
{
int row;
int width = browser->width;
row = browser->refresh(browser);
ui_browser__set_color(browser, HE_COLORSET_NORMAL);
if (!browser->use_navkeypressed || browser->navkeypressed)
ui_browser__scrollbar_set(browser);
else
width += 1;
SLsmg_fill_region(browser->y + row, browser->x,
browser->height - row, width, ' ');
return 0;
}
int ui_browser__refresh(struct ui_browser *browser)
{
pthread_mutex_lock(&ui__lock);
__ui_browser__refresh(browser);
pthread_mutex_unlock(&ui__lock);
return 0;
}
/*
* Here we're updating nr_entries _after_ we started browsing, i.e. we have to
* forget about any reference to any entry in the underlying data structure,
* that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
* after an output_resort and hist decay.
*/
void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
{
off_t offset = nr_entries - browser->nr_entries;
browser->nr_entries = nr_entries;
if (offset < 0) {
if (browser->top_idx < (u64)-offset)
offset = -browser->top_idx;
browser->index += offset;
browser->top_idx += offset;
}
browser->top = NULL;
browser->seek(browser, browser->top_idx, SEEK_SET);
}
int ui_browser__run(struct ui_browser *browser, int delay_secs)
{
int err, key;
while (1) {
off_t offset;
pthread_mutex_lock(&ui__lock);
err = __ui_browser__refresh(browser);
SLsmg_refresh();
pthread_mutex_unlock(&ui__lock);
if (err < 0)
break;
key = ui__getch(delay_secs);
if (key == K_RESIZE) {
ui__refresh_dimensions(false);
ui_browser__refresh_dimensions(browser);
__ui_browser__show_title(browser, browser->title);
ui_helpline__puts(browser->helpline);
continue;
}
if (browser->use_navkeypressed && !browser->navkeypressed) {
if (key == K_DOWN || key == K_UP ||
key == K_PGDN || key == K_PGUP ||
key == K_HOME || key == K_END ||
key == ' ') {
browser->navkeypressed = true;
continue;
} else
return key;
}
switch (key) {
case K_DOWN:
if (browser->index == browser->nr_entries - 1)
break;
++browser->index;
if (browser->index == browser->top_idx + browser->height) {
++browser->top_idx;
browser->seek(browser, +1, SEEK_CUR);
}
break;
case K_UP:
if (browser->index == 0)
break;
--browser->index;
if (browser->index < browser->top_idx) {
--browser->top_idx;
browser->seek(browser, -1, SEEK_CUR);
}
break;
case K_PGDN:
case ' ':
if (browser->top_idx + browser->height > browser->nr_entries - 1)
break;
offset = browser->height;
if (browser->index + offset > browser->nr_entries - 1)
offset = browser->nr_entries - 1 - browser->index;
browser->index += offset;
browser->top_idx += offset;
browser->seek(browser, +offset, SEEK_CUR);
break;
case K_PGUP:
if (browser->top_idx == 0)
break;
if (browser->top_idx < browser->height)
offset = browser->top_idx;
else
offset = browser->height;
browser->index -= offset;
browser->top_idx -= offset;
browser->seek(browser, -offset, SEEK_CUR);
break;
case K_HOME:
ui_browser__reset_index(browser);
break;
case K_END:
offset = browser->height - 1;
if (offset >= browser->nr_entries)
offset = browser->nr_entries - 1;
browser->index = browser->nr_entries - 1;
browser->top_idx = browser->index - offset;
browser->seek(browser, -offset, SEEK_END);
break;
default:
return key;
}
}
return -1;
}
unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
{
struct list_head *pos;
struct list_head *head = browser->entries;
int row = 0;
if (browser->top == NULL || browser->top == browser->entries)
browser->top = ui_browser__list_head_filter_entries(browser, head->next);
pos = browser->top;
list_for_each_from(pos, head) {
if (!browser->filter || !browser->filter(browser, pos)) {
ui_browser__gotorc(browser, row, 0);
browser->write(browser, pos, row);
if (++row == browser->height)
break;
}
}
return row;
}
static struct ui_browser_colorset {
const char *name, *fg, *bg;
int colorset;
} ui_browser__colorsets[] = {
{
.colorset = HE_COLORSET_TOP,
.name = "top",
.fg = "red",
.bg = "default",
},
{
.colorset = HE_COLORSET_MEDIUM,
.name = "medium",
.fg = "green",
.bg = "default",
},
{
.colorset = HE_COLORSET_NORMAL,
.name = "normal",
.fg = "default",
.bg = "default",
},
{
.colorset = HE_COLORSET_SELECTED,
.name = "selected",
.fg = "black",
.bg = "lightgray",
},
{
.colorset = HE_COLORSET_CODE,
.name = "code",
.fg = "blue",
.bg = "default",
},
{
.colorset = HE_COLORSET_ADDR,
.name = "addr",
.fg = "magenta",
.bg = "default",
},
{
.colorset = HE_COLORSET_ROOT,
.name = "root",
.fg = "white",
.bg = "blue",
},
{
.name = NULL,
}
};
static int ui_browser__color_config(const char *var, const char *value,
void *data __maybe_unused)
{
char *fg = NULL, *bg;
int i;
/* same dir for all commands */
if (prefixcmp(var, "colors.") != 0)
return 0;
for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
const char *name = var + 7;
if (strcmp(ui_browser__colorsets[i].name, name) != 0)
continue;
fg = strdup(value);
if (fg == NULL)
break;
bg = strchr(fg, ',');
if (bg == NULL)
break;
*bg = '\0';
while (isspace(*++bg));
ui_browser__colorsets[i].bg = bg;
ui_browser__colorsets[i].fg = fg;
return 0;
}
free(fg);
return -1;
}
void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
{
switch (whence) {
case SEEK_SET:
browser->top = browser->entries;
break;
case SEEK_CUR:
browser->top = browser->top + browser->top_idx + offset;
break;
case SEEK_END:
browser->top = browser->top + browser->nr_entries + offset;
break;
default:
return;
}
}
unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
{
unsigned int row = 0, idx = browser->top_idx;
char **pos;
if (browser->top == NULL)
browser->top = browser->entries;
pos = (char **)browser->top;
while (idx < browser->nr_entries) {
if (!browser->filter || !browser->filter(browser, *pos)) {
ui_browser__gotorc(browser, row, 0);
browser->write(browser, pos, row);
if (++row == browser->height)
break;
}
++idx;
++pos;
}
return row;
}
void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
u16 start, u16 end)
{
SLsmg_set_char_set(1);
ui_browser__gotorc(browser, start, column);
SLsmg_draw_vline(end - start + 1);
SLsmg_set_char_set(0);
}
void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
int graph)
{
SLsmg_set_char_set(1);
SLsmg_write_char(graph);
SLsmg_set_char_set(0);
}
static void __ui_browser__line_arrow_up(struct ui_browser *browser,
unsigned int column,
u64 start, u64 end)
{
unsigned int row, end_row;
SLsmg_set_char_set(1);
if (start < browser->top_idx + browser->height) {
row = start - browser->top_idx;
ui_browser__gotorc(browser, row, column);
SLsmg_write_char(SLSMG_LLCORN_CHAR);
ui_browser__gotorc(browser, row, column + 1);
SLsmg_draw_hline(2);
if (row-- == 0)
goto out;
} else
row = browser->height - 1;
if (end > browser->top_idx)
end_row = end - browser->top_idx;
else
end_row = 0;
ui_browser__gotorc(browser, end_row, column);
SLsmg_draw_vline(row - end_row + 1);
ui_browser__gotorc(browser, end_row, column);
if (end >= browser->top_idx) {
SLsmg_write_char(SLSMG_ULCORN_CHAR);
ui_browser__gotorc(browser, end_row, column + 1);
SLsmg_write_char(SLSMG_HLINE_CHAR);
ui_browser__gotorc(browser, end_row, column + 2);
SLsmg_write_char(SLSMG_RARROW_CHAR);
}
out:
SLsmg_set_char_set(0);
}
static void __ui_browser__line_arrow_down(struct ui_browser *browser,
unsigned int column,
u64 start, u64 end)
{
unsigned int row, end_row;
SLsmg_set_char_set(1);
if (start >= browser->top_idx) {
row = start - browser->top_idx;
ui_browser__gotorc(browser, row, column);
SLsmg_write_char(SLSMG_ULCORN_CHAR);
ui_browser__gotorc(browser, row, column + 1);
SLsmg_draw_hline(2);
if (row++ == 0)
goto out;
} else
row = 0;
if (end >= browser->top_idx + browser->height)
end_row = browser->height - 1;
else
end_row = end - browser->top_idx;;
ui_browser__gotorc(browser, row, column);
SLsmg_draw_vline(end_row - row + 1);
ui_browser__gotorc(browser, end_row, column);
if (end < browser->top_idx + browser->height) {
SLsmg_write_char(SLSMG_LLCORN_CHAR);
ui_browser__gotorc(browser, end_row, column + 1);
SLsmg_write_char(SLSMG_HLINE_CHAR);
ui_browser__gotorc(browser, end_row, column + 2);
SLsmg_write_char(SLSMG_RARROW_CHAR);
}
out:
SLsmg_set_char_set(0);
}
void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
u64 start, u64 end)
{
if (start > end)
__ui_browser__line_arrow_up(browser, column, start, end);
else
__ui_browser__line_arrow_down(browser, column, start, end);
}
void ui_browser__init(void)
{
int i = 0;
perf_config(ui_browser__color_config, NULL);
while (ui_browser__colorsets[i].name) {
struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
sltt_set_color(c->colorset, c->name, c->fg, c->bg);
}
annotate_browser__init();
}

74
tools/perf/ui/browser.h Normal file
View File

@@ -0,0 +1,74 @@
#ifndef _PERF_UI_BROWSER_H_
#define _PERF_UI_BROWSER_H_ 1
#include <stdbool.h>
#include <sys/types.h>
#include "../types.h"
#define HE_COLORSET_TOP 50
#define HE_COLORSET_MEDIUM 51
#define HE_COLORSET_NORMAL 52
#define HE_COLORSET_SELECTED 53
#define HE_COLORSET_CODE 54
#define HE_COLORSET_ADDR 55
#define HE_COLORSET_ROOT 56
struct ui_browser {
u64 index, top_idx;
void *top, *entries;
u16 y, x, width, height;
int current_color;
void *priv;
const char *title;
char *helpline;
unsigned int (*refresh)(struct ui_browser *self);
void (*write)(struct ui_browser *self, void *entry, int row);
void (*seek)(struct ui_browser *self, off_t offset, int whence);
bool (*filter)(struct ui_browser *self, void *entry);
u32 nr_entries;
bool navkeypressed;
bool use_navkeypressed;
};
int ui_browser__set_color(struct ui_browser *browser, int color);
void ui_browser__set_percent_color(struct ui_browser *self,
double percent, bool current);
bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
void ui_browser__refresh_dimensions(struct ui_browser *self);
void ui_browser__reset_index(struct ui_browser *self);
void ui_browser__gotorc(struct ui_browser *self, int y, int x);
void ui_browser__write_graph(struct ui_browser *browser, int graph);
void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
u64 start, u64 end);
void __ui_browser__show_title(struct ui_browser *browser, const char *title);
void ui_browser__show_title(struct ui_browser *browser, const char *title);
int ui_browser__show(struct ui_browser *self, const char *title,
const char *helpline, ...);
void ui_browser__hide(struct ui_browser *self);
int ui_browser__refresh(struct ui_browser *self);
int ui_browser__run(struct ui_browser *browser, int delay_secs);
void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries);
void ui_browser__handle_resize(struct ui_browser *browser);
void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
u16 start, u16 end);
int ui_browser__warning(struct ui_browser *browser, int timeout,
const char *format, ...);
int ui_browser__help_window(struct ui_browser *browser, const char *text);
bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text);
int ui_browser__input_window(const char *title, const char *text, char *input,
const char *exit_msg, int delay_sec);
void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence);
unsigned int ui_browser__argv_refresh(struct ui_browser *browser);
void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
void ui_browser__init(void);
void annotate_browser__init(void);
#endif /* _PERF_UI_BROWSER_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,130 @@
#include "../libslang.h"
#include <elf.h>
#include <inttypes.h>
#include <sys/ttydefaults.h>
#include <string.h>
#include <linux/bitops.h>
#include "../../util/util.h"
#include "../../util/debug.h"
#include "../../util/symbol.h"
#include "../browser.h"
#include "../helpline.h"
#include "../keysyms.h"
#include "map.h"
struct map_browser {
struct ui_browser b;
struct map *map;
u8 addrlen;
};
static void map_browser__write(struct ui_browser *self, void *nd, int row)
{
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
struct map_browser *mb = container_of(self, struct map_browser, b);
bool current_entry = ui_browser__is_current_entry(self, row);
int width;
ui_browser__set_percent_color(self, 0, current_entry);
slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ",
mb->addrlen, sym->start, mb->addrlen, sym->end,
sym->binding == STB_GLOBAL ? 'g' :
sym->binding == STB_LOCAL ? 'l' : 'w');
width = self->width - ((mb->addrlen * 2) + 4);
if (width > 0)
slsmg_write_nstring(sym->name, width);
}
/* FIXME uber-kludgy, see comment on cmd_report... */
static u32 *symbol__browser_index(struct symbol *self)
{
return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
}
static int map_browser__search(struct map_browser *self)
{
char target[512];
struct symbol *sym;
int err = ui_browser__input_window("Search by name/addr",
"Prefix with 0x to search by address",
target, "ENTER: OK, ESC: Cancel", 0);
if (err != K_ENTER)
return -1;
if (target[0] == '0' && tolower(target[1]) == 'x') {
u64 addr = strtoull(target, NULL, 16);
sym = map__find_symbol(self->map, addr, NULL);
} else
sym = map__find_symbol_by_name(self->map, target, NULL);
if (sym != NULL) {
u32 *idx = symbol__browser_index(sym);
self->b.top = &sym->rb_node;
self->b.index = self->b.top_idx = *idx;
} else
ui_helpline__fpush("%s not found!", target);
return 0;
}
static int map_browser__run(struct map_browser *self)
{
int key;
if (ui_browser__show(&self->b, self->map->dso->long_name,
"Press <- or ESC to exit, %s / to search",
verbose ? "" : "restart with -v to use") < 0)
return -1;
while (1) {
key = ui_browser__run(&self->b, 0);
switch (key) {
case '/':
if (verbose)
map_browser__search(self);
default:
break;
case K_LEFT:
case K_ESC:
case 'q':
case CTRL('c'):
goto out;
}
}
out:
ui_browser__hide(&self->b);
return key;
}
int map__browse(struct map *self)
{
struct map_browser mb = {
.b = {
.entries = &self->dso->symbols[self->type],
.refresh = ui_browser__rb_tree_refresh,
.seek = ui_browser__rb_tree_seek,
.write = map_browser__write,
},
.map = self,
};
struct rb_node *nd;
char tmp[BITS_PER_LONG / 4];
u64 maxaddr = 0;
for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
if (maxaddr < pos->end)
maxaddr = pos->end;
if (verbose) {
u32 *idx = symbol__browser_index(pos);
*idx = mb.b.nr_entries;
}
++mb.b.nr_entries;
}
mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr);
return map_browser__run(&mb);
}

View File

@@ -0,0 +1,6 @@
#ifndef _PERF_UI_MAP_BROWSER_H_
#define _PERF_UI_MAP_BROWSER_H_ 1
struct map;
int map__browse(struct map *self);
#endif /* _PERF_UI_MAP_BROWSER_H_ */

View File

@@ -0,0 +1,188 @@
#include <elf.h>
#include <inttypes.h>
#include <sys/ttydefaults.h>
#include <string.h>
#include "../../util/sort.h"
#include "../../util/util.h"
#include "../../util/hist.h"
#include "../../util/debug.h"
#include "../../util/symbol.h"
#include "../browser.h"
#include "../helpline.h"
#include "../libslang.h"
/* 2048 lines should be enough for a script output */
#define MAX_LINES 2048
/* 160 bytes for one output line */
#define AVERAGE_LINE_LEN 160
struct script_line {
struct list_head node;
char line[AVERAGE_LINE_LEN];
};
struct perf_script_browser {
struct ui_browser b;
struct list_head entries;
const char *script_name;
int nr_lines;
};
#define SCRIPT_NAMELEN 128
#define SCRIPT_MAX_NO 64
/*
* Usually the full path for a script is:
* /home/username/libexec/perf-core/scripts/python/xxx.py
* /home/username/libexec/perf-core/scripts/perl/xxx.pl
* So 256 should be long enough to contain the full path.
*/
#define SCRIPT_FULLPATH_LEN 256
/*
* When success, will copy the full path of the selected script
* into the buffer pointed by script_name, and return 0.
* Return -1 on failure.
*/
static int list_scripts(char *script_name)
{
char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO];
int i, num, choice, ret = -1;
/* Preset the script name to SCRIPT_NAMELEN */
buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
if (!buf)
return ret;
for (i = 0; i < SCRIPT_MAX_NO; i++) {
names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
paths[i] = names[i] + SCRIPT_NAMELEN;
}
num = find_scripts(names, paths);
if (num > 0) {
choice = ui__popup_menu(num, names);
if (choice < num && choice >= 0) {
strcpy(script_name, paths[choice]);
ret = 0;
}
}
free(buf);
return ret;
}
static void script_browser__write(struct ui_browser *browser,
void *entry, int row)
{
struct script_line *sline = list_entry(entry, struct script_line, node);
bool current_entry = ui_browser__is_current_entry(browser, row);
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
HE_COLORSET_NORMAL);
slsmg_write_nstring(sline->line, browser->width);
}
static int script_browser__run(struct perf_script_browser *self)
{
int key;
if (ui_browser__show(&self->b, self->script_name,
"Press <- or ESC to exit") < 0)
return -1;
while (1) {
key = ui_browser__run(&self->b, 0);
/* We can add some special key handling here if needed */
break;
}
ui_browser__hide(&self->b);
return key;
}
int script_browse(const char *script_opt)
{
char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN];
char *line = NULL;
size_t len = 0;
ssize_t retlen;
int ret = -1, nr_entries = 0;
FILE *fp;
void *buf;
struct script_line *sline;
struct perf_script_browser script = {
.b = {
.refresh = ui_browser__list_head_refresh,
.seek = ui_browser__list_head_seek,
.write = script_browser__write,
},
.script_name = script_name,
};
INIT_LIST_HEAD(&script.entries);
/* Save each line of the output in one struct script_line object. */
buf = zalloc((sizeof(*sline)) * MAX_LINES);
if (!buf)
return -1;
sline = buf;
memset(script_name, 0, SCRIPT_FULLPATH_LEN);
if (list_scripts(script_name))
goto exit;
sprintf(cmd, "perf script -s %s ", script_name);
if (script_opt)
strcat(cmd, script_opt);
if (input_name) {
strcat(cmd, " -i ");
strcat(cmd, input_name);
}
strcat(cmd, " 2>&1");
fp = popen(cmd, "r");
if (!fp)
goto exit;
while ((retlen = getline(&line, &len, fp)) != -1) {
strncpy(sline->line, line, AVERAGE_LINE_LEN);
/* If one output line is very large, just cut it short */
if (retlen >= AVERAGE_LINE_LEN) {
sline->line[AVERAGE_LINE_LEN - 1] = '\0';
sline->line[AVERAGE_LINE_LEN - 2] = '\n';
}
list_add_tail(&sline->node, &script.entries);
if (script.b.width < retlen)
script.b.width = retlen;
if (nr_entries++ >= MAX_LINES - 1)
break;
sline++;
}
if (script.b.width > AVERAGE_LINE_LEN)
script.b.width = AVERAGE_LINE_LEN;
if (line)
free(line);
pclose(fp);
script.nr_lines = nr_entries;
script.b.nr_entries = nr_entries;
script.b.entries = &script.entries;
ret = script_browser__run(&script);
exit:
free(buf);
return ret;
}

View File

@@ -0,0 +1,245 @@
#include "gtk.h"
#include "util/debug.h"
#include "util/annotate.h"
#include "util/evsel.h"
#include "ui/helpline.h"
enum {
ANN_COL__PERCENT,
ANN_COL__OFFSET,
ANN_COL__LINE,
MAX_ANN_COLS
};
static const char *const col_names[] = {
"Overhead",
"Offset",
"Line"
};
static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym,
struct disasm_line *dl, int evidx)
{
struct sym_hist *symhist;
double percent = 0.0;
const char *markup;
int ret = 0;
strcpy(buf, "");
if (dl->offset == (s64) -1)
return 0;
symhist = annotation__histogram(symbol__annotation(sym), evidx);
if (!symbol_conf.event_group && !symhist->addr[dl->offset])
return 0;
percent = 100.0 * symhist->addr[dl->offset] / symhist->sum;
markup = perf_gtk__get_percent_color(percent);
if (markup)
ret += scnprintf(buf, size, "%s", markup);
ret += scnprintf(buf + ret, size - ret, "%6.2f%%", percent);
if (markup)
ret += scnprintf(buf + ret, size - ret, "</span>");
return ret;
}
static int perf_gtk__get_offset(char *buf, size_t size, struct symbol *sym,
struct map *map, struct disasm_line *dl)
{
u64 start = map__rip_2objdump(map, sym->start);
strcpy(buf, "");
if (dl->offset == (s64) -1)
return 0;
return scnprintf(buf, size, "%"PRIx64, start + dl->offset);
}
static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl)
{
int ret = 0;
char *line = g_markup_escape_text(dl->line, -1);
const char *markup = "<span fgcolor='gray'>";
strcpy(buf, "");
if (!line)
return 0;
if (dl->offset != (s64) -1)
markup = NULL;
if (markup)
ret += scnprintf(buf, size, "%s", markup);
ret += scnprintf(buf + ret, size - ret, "%s", line);
if (markup)
ret += scnprintf(buf + ret, size - ret, "</span>");
g_free(line);
return ret;
}
static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
struct map *map, struct perf_evsel *evsel,
struct hist_browser_timer *hbt __maybe_unused)
{
struct disasm_line *pos, *n;
struct annotation *notes;
GType col_types[MAX_ANN_COLS];
GtkCellRenderer *renderer;
GtkListStore *store;
GtkWidget *view;
int i;
char s[512];
notes = symbol__annotation(sym);
for (i = 0; i < MAX_ANN_COLS; i++) {
col_types[i] = G_TYPE_STRING;
}
store = gtk_list_store_newv(MAX_ANN_COLS, col_types);
view = gtk_tree_view_new();
renderer = gtk_cell_renderer_text_new();
for (i = 0; i < MAX_ANN_COLS; i++) {
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
-1, col_names[i], renderer, "markup",
i, NULL);
}
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
g_object_unref(GTK_TREE_MODEL(store));
list_for_each_entry(pos, &notes->src->source, node) {
GtkTreeIter iter;
int ret = 0;
gtk_list_store_append(store, &iter);
if (perf_evsel__is_group_event(evsel)) {
for (i = 0; i < evsel->nr_members; i++) {
ret += perf_gtk__get_percent(s + ret,
sizeof(s) - ret,
sym, pos,
evsel->idx + i);
ret += scnprintf(s + ret, sizeof(s) - ret, " ");
}
} else {
ret = perf_gtk__get_percent(s, sizeof(s), sym, pos,
evsel->idx);
}
if (ret)
gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1);
if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos))
gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1);
if (perf_gtk__get_line(s, sizeof(s), pos))
gtk_list_store_set(store, &iter, ANN_COL__LINE, s, -1);
}
gtk_container_add(GTK_CONTAINER(window), view);
list_for_each_entry_safe(pos, n, &notes->src->source, node) {
list_del(&pos->node);
disasm_line__free(pos);
}
return 0;
}
int symbol__gtk_annotate(struct symbol *sym, struct map *map,
struct perf_evsel *evsel,
struct hist_browser_timer *hbt)
{
GtkWidget *window;
GtkWidget *notebook;
GtkWidget *scrolled_window;
GtkWidget *tab_label;
if (map->dso->annotate_warned)
return -1;
if (symbol__annotate(sym, map, 0) < 0) {
ui__error("%s", ui_helpline__current);
return -1;
}
if (perf_gtk__is_active_context(pgctx)) {
window = pgctx->main_window;
notebook = pgctx->notebook;
} else {
GtkWidget *vbox;
GtkWidget *infobar;
GtkWidget *statbar;
signal(SIGSEGV, perf_gtk__signal);
signal(SIGFPE, perf_gtk__signal);
signal(SIGINT, perf_gtk__signal);
signal(SIGQUIT, perf_gtk__signal);
signal(SIGTERM, perf_gtk__signal);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "perf annotate");
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
pgctx = perf_gtk__activate_context(window);
if (!pgctx)
return -1;
vbox = gtk_vbox_new(FALSE, 0);
notebook = gtk_notebook_new();
pgctx->notebook = notebook;
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
infobar = perf_gtk__setup_info_bar();
if (infobar) {
gtk_box_pack_start(GTK_BOX(vbox), infobar,
FALSE, FALSE, 0);
}
statbar = perf_gtk__setup_statusbar();
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
}
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
tab_label = gtk_label_new(sym->name);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window,
tab_label);
perf_gtk__annotate_symbol(scrolled_window, sym, map, evsel, hbt);
return 0;
}
void perf_gtk__show_annotations(void)
{
GtkWidget *window;
if (!perf_gtk__is_active_context(pgctx))
return;
window = pgctx->main_window;
gtk_widget_show_all(window);
perf_gtk__resize_window(window);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_main();
perf_gtk__deactivate_context(&pgctx);
}

View File

@@ -0,0 +1,87 @@
#include "../evlist.h"
#include "../cache.h"
#include "../evsel.h"
#include "../sort.h"
#include "../hist.h"
#include "../helpline.h"
#include "gtk.h"
#include <signal.h>
void perf_gtk__signal(int sig)
{
perf_gtk__exit(false);
psignal(sig, "perf");
}
void perf_gtk__resize_window(GtkWidget *window)
{
GdkRectangle rect;
GdkScreen *screen;
int monitor;
int height;
int width;
screen = gtk_widget_get_screen(window);
monitor = gdk_screen_get_monitor_at_window(screen, window->window);
gdk_screen_get_monitor_geometry(screen, monitor, &rect);
width = rect.width * 3 / 4;
height = rect.height * 3 / 4;
gtk_window_resize(GTK_WINDOW(window), width, height);
}
const char *perf_gtk__get_percent_color(double percent)
{
if (percent >= MIN_RED)
return "<span fgcolor='red'>";
if (percent >= MIN_GREEN)
return "<span fgcolor='dark green'>";
return NULL;
}
#ifdef HAVE_GTK_INFO_BAR
GtkWidget *perf_gtk__setup_info_bar(void)
{
GtkWidget *info_bar;
GtkWidget *label;
GtkWidget *content_area;
info_bar = gtk_info_bar_new();
gtk_widget_set_no_show_all(info_bar, TRUE);
label = gtk_label_new("");
gtk_widget_show(label);
content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(info_bar));
gtk_container_add(GTK_CONTAINER(content_area), label);
gtk_info_bar_add_button(GTK_INFO_BAR(info_bar), GTK_STOCK_OK,
GTK_RESPONSE_OK);
g_signal_connect(info_bar, "response",
G_CALLBACK(gtk_widget_hide), NULL);
pgctx->info_bar = info_bar;
pgctx->message_label = label;
return info_bar;
}
#endif
GtkWidget *perf_gtk__setup_statusbar(void)
{
GtkWidget *stbar;
unsigned ctxid;
stbar = gtk_statusbar_new();
ctxid = gtk_statusbar_get_context_id(GTK_STATUSBAR(stbar),
"perf report");
pgctx->statbar = stbar;
pgctx->statbar_ctx_id = ctxid;
return stbar;
}

51
tools/perf/ui/gtk/gtk.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef _PERF_GTK_H_
#define _PERF_GTK_H_ 1
#include <stdbool.h>
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include <gtk/gtk.h>
#pragma GCC diagnostic error "-Wstrict-prototypes"
struct perf_gtk_context {
GtkWidget *main_window;
GtkWidget *notebook;
#ifdef HAVE_GTK_INFO_BAR
GtkWidget *info_bar;
GtkWidget *message_label;
#endif
GtkWidget *statbar;
guint statbar_ctx_id;
};
extern struct perf_gtk_context *pgctx;
static inline bool perf_gtk__is_active_context(struct perf_gtk_context *ctx)
{
return ctx && ctx->main_window;
}
struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window);
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx);
void perf_gtk__init_helpline(void);
void perf_gtk__init_progress(void);
void perf_gtk__init_hpp(void);
void perf_gtk__signal(int sig);
void perf_gtk__resize_window(GtkWidget *window);
const char *perf_gtk__get_percent_color(double percent);
GtkWidget *perf_gtk__setup_statusbar(void);
#ifdef HAVE_GTK_INFO_BAR
GtkWidget *perf_gtk__setup_info_bar(void);
#else
static inline GtkWidget *perf_gtk__setup_info_bar(void)
{
return NULL;
}
#endif
#endif /* _PERF_GTK_H_ */

View File

@@ -0,0 +1,57 @@
#include <stdio.h>
#include <string.h>
#include "gtk.h"
#include "../ui.h"
#include "../helpline.h"
#include "../../util/debug.h"
static void gtk_helpline_pop(void)
{
if (!perf_gtk__is_active_context(pgctx))
return;
gtk_statusbar_pop(GTK_STATUSBAR(pgctx->statbar),
pgctx->statbar_ctx_id);
}
static void gtk_helpline_push(const char *msg)
{
if (!perf_gtk__is_active_context(pgctx))
return;
gtk_statusbar_push(GTK_STATUSBAR(pgctx->statbar),
pgctx->statbar_ctx_id, msg);
}
static int gtk_helpline_show(const char *fmt, va_list ap)
{
int ret;
char *ptr;
static int backlog;
ret = vscnprintf(ui_helpline__current + backlog,
sizeof(ui_helpline__current) - backlog, fmt, ap);
backlog += ret;
/* only first line can be displayed */
ptr = strchr(ui_helpline__current, '\n');
if (ptr && (ptr - ui_helpline__current) <= backlog) {
*ptr = '\0';
ui_helpline__puts(ui_helpline__current);
backlog = 0;
}
return ret;
}
static struct ui_helpline gtk_helpline_fns = {
.pop = gtk_helpline_pop,
.push = gtk_helpline_push,
.show = gtk_helpline_show,
};
void perf_gtk__init_helpline(void)
{
helpline_fns = &gtk_helpline_fns;
}

309
tools/perf/ui/gtk/hists.c Normal file
View File

@@ -0,0 +1,309 @@
#include "../evlist.h"
#include "../cache.h"
#include "../evsel.h"
#include "../sort.h"
#include "../hist.h"
#include "../helpline.h"
#include "gtk.h"
#define MAX_COLUMNS 32
static int __percent_color_snprintf(char *buf, size_t size, double percent)
{
int ret = 0;
const char *markup;
markup = perf_gtk__get_percent_color(percent);
if (markup)
ret += scnprintf(buf, size, markup);
ret += scnprintf(buf + ret, size - ret, " %6.2f%%", percent);
if (markup)
ret += scnprintf(buf + ret, size - ret, "</span>");
return ret;
}
static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
u64 (*get_field)(struct hist_entry *))
{
int ret;
double percent = 0.0;
struct hists *hists = he->hists;
struct perf_evsel *evsel = hists_to_evsel(hists);
if (hists->stats.total_period)
percent = 100.0 * get_field(he) / hists->stats.total_period;
ret = __percent_color_snprintf(hpp->buf, hpp->size, percent);
if (perf_evsel__is_group_event(evsel)) {
int prev_idx, idx_delta;
struct hist_entry *pair;
int nr_members = evsel->nr_members;
prev_idx = perf_evsel__group_idx(evsel);
list_for_each_entry(pair, &he->pairs.head, pairs.node) {
u64 period = get_field(pair);
u64 total = pair->hists->stats.total_period;
evsel = hists_to_evsel(pair->hists);
idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
while (idx_delta--) {
/*
* zero-fill group members in the middle which
* have no sample
*/
ret += __percent_color_snprintf(hpp->buf + ret,
hpp->size - ret,
0.0);
}
percent = 100.0 * period / total;
ret += __percent_color_snprintf(hpp->buf + ret,
hpp->size - ret,
percent);
prev_idx = perf_evsel__group_idx(evsel);
}
idx_delta = nr_members - prev_idx - 1;
while (idx_delta--) {
/*
* zero-fill group members at last which have no sample
*/
ret += __percent_color_snprintf(hpp->buf + ret,
hpp->size - ret,
0.0);
}
}
return ret;
}
#define __HPP_COLOR_PERCENT_FN(_type, _field) \
static u64 he_get_##_field(struct hist_entry *he) \
{ \
return he->stat._field; \
} \
\
static int perf_gtk__hpp_color_##_type(struct perf_hpp *hpp, \
struct hist_entry *he) \
{ \
return __hpp__color_fmt(hpp, he, he_get_##_field); \
}
__HPP_COLOR_PERCENT_FN(overhead, period)
__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
#undef __HPP_COLOR_PERCENT_FN
void perf_gtk__init_hpp(void)
{
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
perf_hpp__init();
perf_hpp__format[PERF_HPP__OVERHEAD].color =
perf_gtk__hpp_color_overhead;
perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
perf_gtk__hpp_color_overhead_sys;
perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
perf_gtk__hpp_color_overhead_us;
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
perf_gtk__hpp_color_overhead_guest_sys;
perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
perf_gtk__hpp_color_overhead_guest_us;
}
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
{
struct perf_hpp_fmt *fmt;
GType col_types[MAX_COLUMNS];
GtkCellRenderer *renderer;
struct sort_entry *se;
GtkListStore *store;
struct rb_node *nd;
GtkWidget *view;
int col_idx;
int nr_cols;
char s[512];
struct perf_hpp hpp = {
.buf = s,
.size = sizeof(s),
.ptr = hists_to_evsel(hists),
};
nr_cols = 0;
perf_hpp__for_each_format(fmt)
col_types[nr_cols++] = G_TYPE_STRING;
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
col_types[nr_cols++] = G_TYPE_STRING;
}
store = gtk_list_store_newv(nr_cols, col_types);
view = gtk_tree_view_new();
renderer = gtk_cell_renderer_text_new();
col_idx = 0;
perf_hpp__for_each_format(fmt) {
fmt->header(&hpp);
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
-1, ltrim(s),
renderer, "markup",
col_idx++, NULL);
}
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
-1, se->se_header,
renderer, "text",
col_idx++, NULL);
}
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
g_object_unref(GTK_TREE_MODEL(store));
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
GtkTreeIter iter;
if (h->filtered)
continue;
gtk_list_store_append(store, &iter);
col_idx = 0;
perf_hpp__for_each_format(fmt) {
if (fmt->color)
fmt->color(&hpp, h);
else
fmt->entry(&hpp, h);
gtk_list_store_set(store, &iter, col_idx++, s, -1);
}
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
se->se_snprintf(h, s, ARRAY_SIZE(s),
hists__col_len(hists, se->se_width_idx));
gtk_list_store_set(store, &iter, col_idx++, s, -1);
}
}
gtk_container_add(GTK_CONTAINER(window), view);
}
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
const char *help,
struct hist_browser_timer *hbt __maybe_unused)
{
struct perf_evsel *pos;
GtkWidget *vbox;
GtkWidget *notebook;
GtkWidget *info_bar;
GtkWidget *statbar;
GtkWidget *window;
signal(SIGSEGV, perf_gtk__signal);
signal(SIGFPE, perf_gtk__signal);
signal(SIGINT, perf_gtk__signal);
signal(SIGQUIT, perf_gtk__signal);
signal(SIGTERM, perf_gtk__signal);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "perf report");
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
pgctx = perf_gtk__activate_context(window);
if (!pgctx)
return -1;
vbox = gtk_vbox_new(FALSE, 0);
notebook = gtk_notebook_new();
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
info_bar = perf_gtk__setup_info_bar();
if (info_bar)
gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
statbar = perf_gtk__setup_statusbar();
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
list_for_each_entry(pos, &evlist->entries, node) {
struct hists *hists = &pos->hists;
const char *evname = perf_evsel__name(pos);
GtkWidget *scrolled_window;
GtkWidget *tab_label;
char buf[512];
size_t size = sizeof(buf);
if (symbol_conf.event_group) {
if (!perf_evsel__is_group_leader(pos))
continue;
if (pos->nr_members > 1) {
perf_evsel__group_desc(pos, buf, size);
evname = buf;
}
}
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
perf_gtk__show_hists(scrolled_window, hists);
tab_label = gtk_label_new(evname);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
}
gtk_widget_show_all(window);
perf_gtk__resize_window(window);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
ui_helpline__push(help);
gtk_main();
perf_gtk__deactivate_context(&pgctx);
return 0;
}

View File

@@ -0,0 +1,59 @@
#include <inttypes.h>
#include "gtk.h"
#include "../progress.h"
#include "util.h"
static GtkWidget *dialog;
static GtkWidget *progress;
static void gtk_progress_update(u64 curr, u64 total, const char *title)
{
double fraction = total ? 1.0 * curr / total : 0.0;
char buf[1024];
if (dialog == NULL) {
GtkWidget *vbox = gtk_vbox_new(TRUE, 5);
GtkWidget *label = gtk_label_new(title);
dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
progress = gtk_progress_bar_new();
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 3);
gtk_box_pack_start(GTK_BOX(vbox), progress, TRUE, TRUE, 3);
gtk_container_add(GTK_CONTAINER(dialog), vbox);
gtk_window_set_title(GTK_WINDOW(dialog), "perf");
gtk_window_resize(GTK_WINDOW(dialog), 300, 80);
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
gtk_widget_show_all(dialog);
}
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction);
snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, curr, total);
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf);
/* we didn't call gtk_main yet, so do it manually */
while (gtk_events_pending())
gtk_main_iteration();
}
static void gtk_progress_finish(void)
{
/* this will also destroy all of its children */
gtk_widget_destroy(dialog);
dialog = NULL;
}
static struct ui_progress gtk_progress_fns = {
.update = gtk_progress_update,
.finish = gtk_progress_finish,
};
void perf_gtk__init_progress(void)
{
progress_fns = &gtk_progress_fns;
}

23
tools/perf/ui/gtk/setup.c Normal file
View File

@@ -0,0 +1,23 @@
#include "gtk.h"
#include "../../util/cache.h"
#include "../../util/debug.h"
extern struct perf_error_ops perf_gtk_eops;
int perf_gtk__init(void)
{
perf_error__register(&perf_gtk_eops);
perf_gtk__init_helpline();
perf_gtk__init_progress();
perf_gtk__init_hpp();
return gtk_init_check(NULL, NULL) ? 0 : -1;
}
void perf_gtk__exit(bool wait_for_ok __maybe_unused)
{
if (!perf_gtk__is_active_context(pgctx))
return;
perf_error__unregister(&perf_gtk_eops);
gtk_main_quit();
}

113
tools/perf/ui/gtk/util.c Normal file
View File

@@ -0,0 +1,113 @@
#include "../util.h"
#include "../../util/debug.h"
#include "gtk.h"
#include <string.h>
struct perf_gtk_context *pgctx;
struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window)
{
struct perf_gtk_context *ctx;
ctx = malloc(sizeof(*pgctx));
if (ctx)
ctx->main_window = window;
return ctx;
}
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx)
{
if (!perf_gtk__is_active_context(*ctx))
return -1;
free(*ctx);
*ctx = NULL;
return 0;
}
static int perf_gtk__error(const char *format, va_list args)
{
char *msg;
GtkWidget *dialog;
if (!perf_gtk__is_active_context(pgctx) ||
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Error:\n");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
return -1;
}
dialog = gtk_message_dialog_new_with_markup(GTK_WINDOW(pgctx->main_window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"<b>Error</b>\n\n%s", msg);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
free(msg);
return 0;
}
#ifdef HAVE_GTK_INFO_BAR
static int perf_gtk__warning_info_bar(const char *format, va_list args)
{
char *msg;
if (!perf_gtk__is_active_context(pgctx) ||
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
return -1;
}
gtk_label_set_text(GTK_LABEL(pgctx->message_label), msg);
gtk_info_bar_set_message_type(GTK_INFO_BAR(pgctx->info_bar),
GTK_MESSAGE_WARNING);
gtk_widget_show(pgctx->info_bar);
free(msg);
return 0;
}
#else
static int perf_gtk__warning_statusbar(const char *format, va_list args)
{
char *msg, *p;
if (!perf_gtk__is_active_context(pgctx) ||
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
return -1;
}
gtk_statusbar_pop(GTK_STATUSBAR(pgctx->statbar),
pgctx->statbar_ctx_id);
/* Only first line can be displayed */
p = strchr(msg, '\n');
if (p)
*p = '\0';
gtk_statusbar_push(GTK_STATUSBAR(pgctx->statbar),
pgctx->statbar_ctx_id, msg);
free(msg);
return 0;
}
#endif
struct perf_error_ops perf_gtk_eops = {
.error = perf_gtk__error,
#ifdef HAVE_GTK_INFO_BAR
.warning = perf_gtk__warning_info_bar,
#else
.warning = perf_gtk__warning_statusbar,
#endif
};

73
tools/perf/ui/helpline.c Normal file
View File

@@ -0,0 +1,73 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../debug.h"
#include "helpline.h"
#include "ui.h"
char ui_helpline__current[512];
static void nop_helpline__pop(void)
{
}
static void nop_helpline__push(const char *msg __maybe_unused)
{
}
static int nop_helpline__show(const char *fmt __maybe_unused,
va_list ap __maybe_unused)
{
return 0;
}
static struct ui_helpline default_helpline_fns = {
.pop = nop_helpline__pop,
.push = nop_helpline__push,
.show = nop_helpline__show,
};
struct ui_helpline *helpline_fns = &default_helpline_fns;
void ui_helpline__pop(void)
{
helpline_fns->pop();
}
void ui_helpline__push(const char *msg)
{
helpline_fns->push(msg);
}
void ui_helpline__vpush(const char *fmt, va_list ap)
{
char *s;
if (vasprintf(&s, fmt, ap) < 0)
vfprintf(stderr, fmt, ap);
else {
ui_helpline__push(s);
free(s);
}
}
void ui_helpline__fpush(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
ui_helpline__vpush(fmt, ap);
va_end(ap);
}
void ui_helpline__puts(const char *msg)
{
ui_helpline__pop();
ui_helpline__push(msg);
}
int ui_helpline__vshow(const char *fmt, va_list ap)
{
return helpline_fns->show(fmt, ap);
}

29
tools/perf/ui/helpline.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef _PERF_UI_HELPLINE_H_
#define _PERF_UI_HELPLINE_H_ 1
#include <stdio.h>
#include <stdarg.h>
#include "../util/cache.h"
struct ui_helpline {
void (*pop)(void);
void (*push)(const char *msg);
int (*show)(const char *fmt, va_list ap);
};
extern struct ui_helpline *helpline_fns;
void ui_helpline__init(void);
void ui_helpline__pop(void);
void ui_helpline__push(const char *msg);
void ui_helpline__vpush(const char *fmt, va_list ap);
void ui_helpline__fpush(const char *fmt, ...);
void ui_helpline__puts(const char *msg);
int ui_helpline__vshow(const char *fmt, va_list ap);
extern char ui_helpline__current[512];
extern char ui_helpline__last_msg[];
#endif /* _PERF_UI_HELPLINE_H_ */

513
tools/perf/ui/hist.c Normal file
View File

@@ -0,0 +1,513 @@
#include <math.h>
#include "../util/hist.h"
#include "../util/util.h"
#include "../util/sort.h"
#include "../util/evsel.h"
/* hist period print (hpp) functions */
typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...);
static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
u64 (*get_field)(struct hist_entry *),
const char *fmt, hpp_snprint_fn print_fn,
bool fmt_percent)
{
int ret;
struct hists *hists = he->hists;
struct perf_evsel *evsel = hists_to_evsel(hists);
if (fmt_percent) {
double percent = 0.0;
if (hists->stats.total_period)
percent = 100.0 * get_field(he) /
hists->stats.total_period;
ret = print_fn(hpp->buf, hpp->size, fmt, percent);
} else
ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he));
if (perf_evsel__is_group_event(evsel)) {
int prev_idx, idx_delta;
struct hist_entry *pair;
int nr_members = evsel->nr_members;
prev_idx = perf_evsel__group_idx(evsel);
list_for_each_entry(pair, &he->pairs.head, pairs.node) {
u64 period = get_field(pair);
u64 total = pair->hists->stats.total_period;
if (!total)
continue;
evsel = hists_to_evsel(pair->hists);
idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
while (idx_delta--) {
/*
* zero-fill group members in the middle which
* have no sample
*/
ret += print_fn(hpp->buf + ret, hpp->size - ret,
fmt, 0);
}
if (fmt_percent)
ret += print_fn(hpp->buf + ret, hpp->size - ret,
fmt, 100.0 * period / total);
else
ret += print_fn(hpp->buf + ret, hpp->size - ret,
fmt, period);
prev_idx = perf_evsel__group_idx(evsel);
}
idx_delta = nr_members - prev_idx - 1;
while (idx_delta--) {
/*
* zero-fill group members at last which have no sample
*/
ret += print_fn(hpp->buf + ret, hpp->size - ret,
fmt, 0);
}
}
return ret;
}
#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
static int hpp__header_##_type(struct perf_hpp *hpp) \
{ \
int len = _min_width; \
\
if (symbol_conf.event_group) { \
struct perf_evsel *evsel = hpp->ptr; \
\
len = max(len, evsel->nr_members * _unit_width); \
} \
return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \
}
#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \
static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused) \
{ \
int len = _min_width; \
\
if (symbol_conf.event_group) { \
struct perf_evsel *evsel = hpp->ptr; \
\
len = max(len, evsel->nr_members * _unit_width); \
} \
return len; \
}
#define __HPP_COLOR_PERCENT_FN(_type, _field) \
static u64 he_get_##_field(struct hist_entry *he) \
{ \
return he->stat._field; \
} \
\
static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
{ \
return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
(hpp_snprint_fn)percent_color_snprintf, true); \
}
#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
{ \
const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
return __hpp__fmt(hpp, he, he_get_##_field, fmt, \
scnprintf, true); \
}
#define __HPP_ENTRY_RAW_FN(_type, _field) \
static u64 he_get_raw_##_field(struct hist_entry *he) \
{ \
return he->stat._field; \
} \
\
static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
{ \
const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \
}
#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
__HPP_COLOR_PERCENT_FN(_type, _field) \
__HPP_ENTRY_PERCENT_FN(_type, _field)
#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
__HPP_ENTRY_RAW_FN(_type, _field)
HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)
HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)
HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)
HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
HPP_RAW_FNS(period, "Period", period, 12, 12)
static int hpp__header_baseline(struct perf_hpp *hpp)
{
return scnprintf(hpp->buf, hpp->size, "Baseline");
}
static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused)
{
return 8;
}
static double baseline_percent(struct hist_entry *he)
{
struct hist_entry *pair = hist_entry__next_pair(he);
struct hists *pair_hists = pair ? pair->hists : NULL;
double percent = 0.0;
if (pair) {
u64 total_period = pair_hists->stats.total_period;
u64 base_period = pair->stat.period;
percent = 100.0 * base_period / total_period;
}
return percent;
}
static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he)
{
double percent = baseline_percent(he);
if (hist_entry__has_pairs(he) || symbol_conf.field_sep)
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
else
return scnprintf(hpp->buf, hpp->size, " ");
}
static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he)
{
double percent = baseline_percent(he);
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%";
if (hist_entry__has_pairs(he) || symbol_conf.field_sep)
return scnprintf(hpp->buf, hpp->size, fmt, percent);
else
return scnprintf(hpp->buf, hpp->size, " ");
}
static int hpp__header_period_baseline(struct perf_hpp *hpp)
{
const char *fmt = symbol_conf.field_sep ? "%s" : "%12s";
return scnprintf(hpp->buf, hpp->size, fmt, "Period Base");
}
static int hpp__width_period_baseline(struct perf_hpp *hpp __maybe_unused)
{
return 12;
}
static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *he)
{
struct hist_entry *pair = hist_entry__next_pair(he);
u64 period = pair ? pair->stat.period : 0;
const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64;
return scnprintf(hpp->buf, hpp->size, fmt, period);
}
static int hpp__header_delta(struct perf_hpp *hpp)
{
const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
return scnprintf(hpp->buf, hpp->size, fmt, "Delta");
}
static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused)
{
return 7;
}
static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he)
{
struct hist_entry *pair = hist_entry__next_pair(he);
const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s";
char buf[32] = " ";
double diff = 0.0;
if (pair) {
if (he->diff.computed)
diff = he->diff.period_ratio_delta;
else
diff = perf_diff__compute_delta(he, pair);
} else
diff = perf_diff__period_percent(he, he->stat.period);
if (fabs(diff) >= 0.01)
scnprintf(buf, sizeof(buf), "%+4.2F%%", diff);
return scnprintf(hpp->buf, hpp->size, fmt, buf);
}
static int hpp__header_ratio(struct perf_hpp *hpp)
{
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
return scnprintf(hpp->buf, hpp->size, fmt, "Ratio");
}
static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused)
{
return 14;
}
static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he)
{
struct hist_entry *pair = hist_entry__next_pair(he);
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
char buf[32] = " ";
double ratio = 0.0;
if (pair) {
if (he->diff.computed)
ratio = he->diff.period_ratio;
else
ratio = perf_diff__compute_ratio(he, pair);
}
if (ratio > 0.0)
scnprintf(buf, sizeof(buf), "%+14.6F", ratio);
return scnprintf(hpp->buf, hpp->size, fmt, buf);
}
static int hpp__header_wdiff(struct perf_hpp *hpp)
{
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
return scnprintf(hpp->buf, hpp->size, fmt, "Weighted diff");
}
static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused)
{
return 14;
}
static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he)
{
struct hist_entry *pair = hist_entry__next_pair(he);
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
char buf[32] = " ";
s64 wdiff = 0;
if (pair) {
if (he->diff.computed)
wdiff = he->diff.wdiff;
else
wdiff = perf_diff__compute_wdiff(he, pair);
}
if (wdiff != 0)
scnprintf(buf, sizeof(buf), "%14ld", wdiff);
return scnprintf(hpp->buf, hpp->size, fmt, buf);
}
static int hpp__header_formula(struct perf_hpp *hpp)
{
const char *fmt = symbol_conf.field_sep ? "%s" : "%70s";
return scnprintf(hpp->buf, hpp->size, fmt, "Formula");
}
static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused)
{
return 70;
}
static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he)
{
struct hist_entry *pair = hist_entry__next_pair(he);
const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s";
char buf[96] = " ";
if (pair)
perf_diff__formula(he, pair, buf, sizeof(buf));
return scnprintf(hpp->buf, hpp->size, fmt, buf);
}
#define HPP__COLOR_PRINT_FNS(_name) \
{ \
.header = hpp__header_ ## _name, \
.width = hpp__width_ ## _name, \
.color = hpp__color_ ## _name, \
.entry = hpp__entry_ ## _name \
}
#define HPP__PRINT_FNS(_name) \
{ \
.header = hpp__header_ ## _name, \
.width = hpp__width_ ## _name, \
.entry = hpp__entry_ ## _name \
}
struct perf_hpp_fmt perf_hpp__format[] = {
HPP__COLOR_PRINT_FNS(baseline),
HPP__COLOR_PRINT_FNS(overhead),
HPP__COLOR_PRINT_FNS(overhead_sys),
HPP__COLOR_PRINT_FNS(overhead_us),
HPP__COLOR_PRINT_FNS(overhead_guest_sys),
HPP__COLOR_PRINT_FNS(overhead_guest_us),
HPP__PRINT_FNS(samples),
HPP__PRINT_FNS(period),
HPP__PRINT_FNS(period_baseline),
HPP__PRINT_FNS(delta),
HPP__PRINT_FNS(ratio),
HPP__PRINT_FNS(wdiff),
HPP__PRINT_FNS(formula)
};
LIST_HEAD(perf_hpp__list);
#undef HPP__COLOR_PRINT_FNS
#undef HPP__PRINT_FNS
#undef HPP_PERCENT_FNS
#undef HPP_RAW_FNS
#undef __HPP_HEADER_FN
#undef __HPP_WIDTH_FN
#undef __HPP_COLOR_PERCENT_FN
#undef __HPP_ENTRY_PERCENT_FN
#undef __HPP_ENTRY_RAW_FN
void perf_hpp__init(void)
{
if (symbol_conf.show_cpu_utilization) {
perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS);
perf_hpp__column_enable(PERF_HPP__OVERHEAD_US);
if (perf_guest) {
perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS);
perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US);
}
}
if (symbol_conf.show_nr_samples)
perf_hpp__column_enable(PERF_HPP__SAMPLES);
if (symbol_conf.show_total_period)
perf_hpp__column_enable(PERF_HPP__PERIOD);
}
void perf_hpp__column_register(struct perf_hpp_fmt *format)
{
list_add_tail(&format->list, &perf_hpp__list);
}
void perf_hpp__column_enable(unsigned col)
{
BUG_ON(col >= PERF_HPP__MAX_INDEX);
perf_hpp__column_register(&perf_hpp__format[col]);
}
static inline void advance_hpp(struct perf_hpp *hpp, int inc)
{
hpp->buf += inc;
hpp->size -= inc;
}
int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
bool color)
{
const char *sep = symbol_conf.field_sep;
struct perf_hpp_fmt *fmt;
char *start = hpp->buf;
int ret;
bool first = true;
if (symbol_conf.exclude_other && !he->parent)
return 0;
perf_hpp__for_each_format(fmt) {
/*
* If there's no field_sep, we still need
* to display initial ' '.
*/
if (!sep || !first) {
ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " ");
advance_hpp(hpp, ret);
} else
first = false;
if (color && fmt->color)
ret = fmt->color(hpp, he);
else
ret = fmt->entry(hpp, he);
advance_hpp(hpp, ret);
}
return hpp->buf - start;
}
int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,
struct hists *hists)
{
const char *sep = symbol_conf.field_sep;
struct sort_entry *se;
int ret = 0;
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
ret += scnprintf(s + ret, size - ret, "%s", sep ?: " ");
ret += se->se_snprintf(he, s + ret, size - ret,
hists__col_len(hists, se->se_width_idx));
}
return ret;
}
/*
* See hists__fprintf to match the column widths
*/
unsigned int hists__sort_list_width(struct hists *hists)
{
struct perf_hpp_fmt *fmt;
struct sort_entry *se;
int i = 0, ret = 0;
struct perf_hpp dummy_hpp = {
.ptr = hists_to_evsel(hists),
};
perf_hpp__for_each_format(fmt) {
if (i)
ret += 2;
ret += fmt->width(&dummy_hpp);
}
list_for_each_entry(se, &hist_entry__sort_list, list)
if (!se->elide)
ret += 2 + hists__col_len(hists, se->se_width_idx);
if (verbose) /* Addr + origin */
ret += 3 + BITS_PER_LONG / 4;
return ret;
}

28
tools/perf/ui/keysyms.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef _PERF_KEYSYMS_H_
#define _PERF_KEYSYMS_H_ 1
#include "libslang.h"
#define K_DOWN SL_KEY_DOWN
#define K_END SL_KEY_END
#define K_ENTER '\r'
#define K_ESC 033
#define K_F1 SL_KEY_F(1)
#define K_HOME SL_KEY_HOME
#define K_LEFT SL_KEY_LEFT
#define K_PGDN SL_KEY_NPAGE
#define K_PGUP SL_KEY_PPAGE
#define K_RIGHT SL_KEY_RIGHT
#define K_TAB '\t'
#define K_UNTAB SL_KEY_UNTAB
#define K_UP SL_KEY_UP
#define K_BKSPC 0x7f
#define K_DEL SL_KEY_DELETE
/* Not really keys */
#define K_TIMER -1
#define K_ERROR -2
#define K_RESIZE -3
#define K_SWITCH_INPUT_DATA -4
#endif /* _PERF_KEYSYMS_H_ */

29
tools/perf/ui/libslang.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef _PERF_UI_SLANG_H_
#define _PERF_UI_SLANG_H_ 1
/*
* slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
* the build if it isn't defined. Use the equivalent one that glibc
* has on features.h.
*/
#include <features.h>
#ifndef HAVE_LONG_LONG
#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
#endif
#include <slang.h>
#if SLANG_VERSION < 20104
#define slsmg_printf(msg, args...) \
SLsmg_printf((char *)(msg), ##args)
#define slsmg_write_nstring(msg, len) \
SLsmg_write_nstring((char *)(msg), len)
#define sltt_set_color(obj, name, fg, bg) \
SLtt_set_color(obj,(char *)(name), (char *)(fg), (char *)(bg))
#else
#define slsmg_printf SLsmg_printf
#define slsmg_write_nstring SLsmg_write_nstring
#define sltt_set_color SLtt_set_color
#endif
#define SL_KEY_UNTAB 0x1000
#endif /* _PERF_UI_SLANG_H_ */

26
tools/perf/ui/progress.c Normal file
View File

@@ -0,0 +1,26 @@
#include "../cache.h"
#include "progress.h"
static void nop_progress_update(u64 curr __maybe_unused,
u64 total __maybe_unused,
const char *title __maybe_unused)
{
}
static struct ui_progress default_progress_fns =
{
.update = nop_progress_update,
};
struct ui_progress *progress_fns = &default_progress_fns;
void ui_progress__update(u64 curr, u64 total, const char *title)
{
return progress_fns->update(curr, total, title);
}
void ui_progress__finish(void)
{
if (progress_fns->finish)
progress_fns->finish();
}

18
tools/perf/ui/progress.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef _PERF_UI_PROGRESS_H_
#define _PERF_UI_PROGRESS_H_ 1
#include <../types.h>
struct ui_progress {
void (*update)(u64, u64, const char *);
void (*finish)(void);
};
extern struct ui_progress *progress_fns;
void ui_progress__init(void);
void ui_progress__update(u64 curr, u64 total, const char *title);
void ui_progress__finish(void);
#endif

53
tools/perf/ui/setup.c Normal file
View File

@@ -0,0 +1,53 @@
#include <pthread.h>
#include "../util/cache.h"
#include "../util/debug.h"
#include "../util/hist.h"
pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
void setup_browser(bool fallback_to_pager)
{
if (use_browser < 2 && (!isatty(1) || dump_trace))
use_browser = 0;
/* default to TUI */
if (use_browser < 0)
use_browser = 1;
switch (use_browser) {
case 2:
if (perf_gtk__init() == 0)
break;
/* fall through */
case 1:
use_browser = 1;
if (ui__init() == 0)
break;
/* fall through */
default:
use_browser = 0;
if (fallback_to_pager)
setup_pager();
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
perf_hpp__init();
break;
}
}
void exit_browser(bool wait_for_ok)
{
switch (use_browser) {
case 2:
perf_gtk__exit(wait_for_ok);
break;
case 1:
ui__exit(wait_for_ok);
break;
default:
break;
}
}

484
tools/perf/ui/stdio/hist.c Normal file
View File

@@ -0,0 +1,484 @@
#include <stdio.h>
#include "../../util/util.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
#include "../../util/evsel.h"
static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
{
int i;
int ret = fprintf(fp, " ");
for (i = 0; i < left_margin; i++)
ret += fprintf(fp, " ");
return ret;
}
static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
int left_margin)
{
int i;
size_t ret = callchain__fprintf_left_margin(fp, left_margin);
for (i = 0; i < depth; i++)
if (depth_mask & (1 << i))
ret += fprintf(fp, "| ");
else
ret += fprintf(fp, " ");
ret += fprintf(fp, "\n");
return ret;
}
static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
int depth, int depth_mask, int period,
u64 total_samples, u64 hits,
int left_margin)
{
int i;
size_t ret = 0;
ret += callchain__fprintf_left_margin(fp, left_margin);
for (i = 0; i < depth; i++) {
if (depth_mask & (1 << i))
ret += fprintf(fp, "|");
else
ret += fprintf(fp, " ");
if (!period && i == depth - 1) {
double percent;
percent = hits * 100.0 / total_samples;
ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
} else
ret += fprintf(fp, "%s", " ");
}
if (chain->ms.sym)
ret += fprintf(fp, "%s\n", chain->ms.sym->name);
else
ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip);
return ret;
}
static struct symbol *rem_sq_bracket;
static struct callchain_list rem_hits;
static void init_rem_hits(void)
{
rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
if (!rem_sq_bracket) {
fprintf(stderr, "Not enough memory to display remaining hits\n");
return;
}
strcpy(rem_sq_bracket->name, "[...]");
rem_hits.ms.sym = rem_sq_bracket;
}
static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
u64 total_samples, int depth,
int depth_mask, int left_margin)
{
struct rb_node *node, *next;
struct callchain_node *child;
struct callchain_list *chain;
int new_depth_mask = depth_mask;
u64 remaining;
size_t ret = 0;
int i;
uint entries_printed = 0;
remaining = total_samples;
node = rb_first(root);
while (node) {
u64 new_total;
u64 cumul;
child = rb_entry(node, struct callchain_node, rb_node);
cumul = callchain_cumul_hits(child);
remaining -= cumul;
/*
* The depth mask manages the output of pipes that show
* the depth. We don't want to keep the pipes of the current
* level for the last child of this depth.
* Except if we have remaining filtered hits. They will
* supersede the last child
*/
next = rb_next(node);
if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
new_depth_mask &= ~(1 << (depth - 1));
/*
* But we keep the older depth mask for the line separator
* to keep the level link until we reach the last child
*/
ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
left_margin);
i = 0;
list_for_each_entry(chain, &child->val, list) {
ret += ipchain__fprintf_graph(fp, chain, depth,
new_depth_mask, i++,
total_samples,
cumul,
left_margin);
}
if (callchain_param.mode == CHAIN_GRAPH_REL)
new_total = child->children_hit;
else
new_total = total_samples;
ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
depth + 1,
new_depth_mask | (1 << depth),
left_margin);
node = next;
if (++entries_printed == callchain_param.print_limit)
break;
}
if (callchain_param.mode == CHAIN_GRAPH_REL &&
remaining && remaining != total_samples) {
if (!rem_sq_bracket)
return ret;
new_depth_mask &= ~(1 << (depth - 1));
ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
new_depth_mask, 0, total_samples,
remaining, left_margin);
}
return ret;
}
static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
u64 total_samples, int left_margin)
{
struct callchain_node *cnode;
struct callchain_list *chain;
u32 entries_printed = 0;
bool printed = false;
struct rb_node *node;
int i = 0;
int ret = 0;
/*
* If have one single callchain root, don't bother printing
* its percentage (100 % in fractal mode and the same percentage
* than the hist in graph mode). This also avoid one level of column.
*/
node = rb_first(root);
if (node && !rb_next(node)) {
cnode = rb_entry(node, struct callchain_node, rb_node);
list_for_each_entry(chain, &cnode->val, list) {
/*
* If we sort by symbol, the first entry is the same than
* the symbol. No need to print it otherwise it appears as
* displayed twice.
*/
if (!i++ && sort__first_dimension == SORT_SYM)
continue;
if (!printed) {
ret += callchain__fprintf_left_margin(fp, left_margin);
ret += fprintf(fp, "|\n");
ret += callchain__fprintf_left_margin(fp, left_margin);
ret += fprintf(fp, "---");
left_margin += 3;
printed = true;
} else
ret += callchain__fprintf_left_margin(fp, left_margin);
if (chain->ms.sym)
ret += fprintf(fp, " %s\n", chain->ms.sym->name);
else
ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
if (++entries_printed == callchain_param.print_limit)
break;
}
root = &cnode->rb_root;
}
ret += __callchain__fprintf_graph(fp, root, total_samples,
1, 1, left_margin);
ret += fprintf(fp, "\n");
return ret;
}
static size_t __callchain__fprintf_flat(FILE *fp,
struct callchain_node *self,
u64 total_samples)
{
struct callchain_list *chain;
size_t ret = 0;
if (!self)
return 0;
ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
list_for_each_entry(chain, &self->val, list) {
if (chain->ip >= PERF_CONTEXT_MAX)
continue;
if (chain->ms.sym)
ret += fprintf(fp, " %s\n", chain->ms.sym->name);
else
ret += fprintf(fp, " %p\n",
(void *)(long)chain->ip);
}
return ret;
}
static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
u64 total_samples)
{
size_t ret = 0;
u32 entries_printed = 0;
struct rb_node *rb_node;
struct callchain_node *chain;
rb_node = rb_first(self);
while (rb_node) {
double percent;
chain = rb_entry(rb_node, struct callchain_node, rb_node);
percent = chain->hit * 100.0 / total_samples;
ret = percent_color_fprintf(fp, " %6.2f%%\n", percent);
ret += __callchain__fprintf_flat(fp, chain, total_samples);
ret += fprintf(fp, "\n");
if (++entries_printed == callchain_param.print_limit)
break;
rb_node = rb_next(rb_node);
}
return ret;
}
static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
u64 total_samples, int left_margin,
FILE *fp)
{
switch (callchain_param.mode) {
case CHAIN_GRAPH_REL:
return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period,
left_margin);
break;
case CHAIN_GRAPH_ABS:
return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
left_margin);
break;
case CHAIN_FLAT:
return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
break;
case CHAIN_NONE:
break;
default:
pr_err("Bad callchain mode\n");
}
return 0;
}
static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
struct hists *hists,
FILE *fp)
{
int left_margin = 0;
u64 total_period = hists->stats.total_period;
if (sort__first_dimension == SORT_COMM) {
struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
typeof(*se), list);
left_margin = hists__col_len(hists, se->se_width_idx);
left_margin -= thread__comm_len(he->thread);
}
return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
}
static int hist_entry__fprintf(struct hist_entry *he, size_t size,
struct hists *hists, FILE *fp)
{
char bf[512];
int ret;
struct perf_hpp hpp = {
.buf = bf,
.size = size,
};
bool color = !symbol_conf.field_sep;
if (size == 0 || size > sizeof(bf))
size = hpp.size = sizeof(bf);
ret = hist_entry__period_snprintf(&hpp, he, color);
hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
ret = fprintf(fp, "%s\n", bf);
if (symbol_conf.use_callchain)
ret += hist_entry__callchain_fprintf(he, hists, fp);
return ret;
}
size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
int max_cols, FILE *fp)
{
struct perf_hpp_fmt *fmt;
struct sort_entry *se;
struct rb_node *nd;
size_t ret = 0;
unsigned int width;
const char *sep = symbol_conf.field_sep;
const char *col_width = symbol_conf.col_width_list_str;
int nr_rows = 0;
char bf[96];
struct perf_hpp dummy_hpp = {
.buf = bf,
.size = sizeof(bf),
.ptr = hists_to_evsel(hists),
};
bool first = true;
init_rem_hits();
if (!show_header)
goto print_entries;
fprintf(fp, "# ");
perf_hpp__for_each_format(fmt) {
if (!first)
fprintf(fp, "%s", sep ?: " ");
else
first = false;
fmt->header(&dummy_hpp);
fprintf(fp, "%s", bf);
}
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
if (sep) {
fprintf(fp, "%c%s", *sep, se->se_header);
continue;
}
width = strlen(se->se_header);
if (symbol_conf.col_width_list_str) {
if (col_width) {
hists__set_col_len(hists, se->se_width_idx,
atoi(col_width));
col_width = strchr(col_width, ',');
if (col_width)
++col_width;
}
}
if (!hists__new_col_len(hists, se->se_width_idx, width))
width = hists__col_len(hists, se->se_width_idx);
fprintf(fp, " %*s", width, se->se_header);
}
fprintf(fp, "\n");
if (max_rows && ++nr_rows >= max_rows)
goto out;
if (sep)
goto print_entries;
first = true;
fprintf(fp, "# ");
perf_hpp__for_each_format(fmt) {
unsigned int i;
if (!first)
fprintf(fp, "%s", sep ?: " ");
else
first = false;
width = fmt->width(&dummy_hpp);
for (i = 0; i < width; i++)
fprintf(fp, ".");
}
list_for_each_entry(se, &hist_entry__sort_list, list) {
unsigned int i;
if (se->elide)
continue;
fprintf(fp, " ");
width = hists__col_len(hists, se->se_width_idx);
if (width == 0)
width = strlen(se->se_header);
for (i = 0; i < width; i++)
fprintf(fp, ".");
}
fprintf(fp, "\n");
if (max_rows && ++nr_rows >= max_rows)
goto out;
fprintf(fp, "#\n");
if (max_rows && ++nr_rows >= max_rows)
goto out;
print_entries:
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
if (h->filtered)
continue;
ret += hist_entry__fprintf(h, max_cols, hists, fp);
if (max_rows && ++nr_rows >= max_rows)
goto out;
if (h->ms.map == NULL && verbose > 1) {
__map_groups__fprintf_maps(&h->thread->mg,
MAP__FUNCTION, verbose, fp);
fprintf(fp, "%.10s end\n", graph_dotted_line);
}
}
out:
free(rem_sq_bracket);
return ret;
}
size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)
{
int i;
size_t ret = 0;
for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
const char *name;
if (stats->nr_events[i] == 0)
continue;
name = perf_event__name(i);
if (!strcmp(name, "UNKNOWN"))
continue;
ret += fprintf(fp, "%16s events: %10d\n", name,
stats->nr_events[i]);
}
return ret;
}

View File

@@ -0,0 +1,58 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "../../util/debug.h"
#include "../helpline.h"
#include "../ui.h"
#include "../libslang.h"
char ui_helpline__last_msg[1024];
static void tui_helpline__pop(void)
{
}
static void tui_helpline__push(const char *msg)
{
const size_t sz = sizeof(ui_helpline__current);
SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
SLsmg_set_color(0);
SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols);
SLsmg_refresh();
strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0';
}
static int tui_helpline__show(const char *format, va_list ap)
{
int ret;
static int backlog;
pthread_mutex_lock(&ui__lock);
ret = vscnprintf(ui_helpline__last_msg + backlog,
sizeof(ui_helpline__last_msg) - backlog, format, ap);
backlog += ret;
if (ui_helpline__last_msg[backlog - 1] == '\n') {
ui_helpline__puts(ui_helpline__last_msg);
SLsmg_refresh();
backlog = 0;
}
pthread_mutex_unlock(&ui__lock);
return ret;
}
struct ui_helpline tui_helpline_fns = {
.pop = tui_helpline__pop,
.push = tui_helpline__push,
.show = tui_helpline__show,
};
void ui_helpline__init(void)
{
helpline_fns = &tui_helpline_fns;
ui_helpline__puts(" ");
}

View File

@@ -0,0 +1,42 @@
#include "../cache.h"
#include "../progress.h"
#include "../libslang.h"
#include "../ui.h"
#include "../browser.h"
static void tui_progress__update(u64 curr, u64 total, const char *title)
{
int bar, y;
/*
* FIXME: We should have a per UI backend way of showing progress,
* stdio will just show a percentage as NN%, etc.
*/
if (use_browser <= 0)
return;
if (total == 0)
return;
ui__refresh_dimensions(true);
pthread_mutex_lock(&ui__lock);
y = SLtt_Screen_Rows / 2 - 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);
SLsmg_gotorc(y++, 1);
SLsmg_write_string((char *)title);
SLsmg_set_color(HE_COLORSET_SELECTED);
bar = ((SLtt_Screen_Cols - 2) * curr) / total;
SLsmg_fill_region(y, 1, 1, bar, ' ');
SLsmg_refresh();
pthread_mutex_unlock(&ui__lock);
}
static struct ui_progress tui_progress_fns =
{
.update = tui_progress__update,
};
void ui_progress__init(void)
{
progress_fns = &tui_progress_fns;
}

150
tools/perf/ui/tui/setup.c Normal file
View File

@@ -0,0 +1,150 @@
#include <signal.h>
#include <stdbool.h>
#include "../../util/cache.h"
#include "../../util/debug.h"
#include "../browser.h"
#include "../helpline.h"
#include "../ui.h"
#include "../util.h"
#include "../libslang.h"
#include "../keysyms.h"
static volatile int ui__need_resize;
extern struct perf_error_ops perf_tui_eops;
extern void hist_browser__init_hpp(void);
void ui__refresh_dimensions(bool force)
{
if (force || ui__need_resize) {
ui__need_resize = 0;
pthread_mutex_lock(&ui__lock);
SLtt_get_screen_size();
SLsmg_reinit_smg();
pthread_mutex_unlock(&ui__lock);
}
}
static void ui__sigwinch(int sig __maybe_unused)
{
ui__need_resize = 1;
}
static void ui__setup_sigwinch(void)
{
static bool done;
if (done)
return;
done = true;
pthread__unblock_sigwinch();
signal(SIGWINCH, ui__sigwinch);
}
int ui__getch(int delay_secs)
{
struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL;
fd_set read_set;
int err, key;
ui__setup_sigwinch();
FD_ZERO(&read_set);
FD_SET(0, &read_set);
if (delay_secs) {
timeout.tv_sec = delay_secs;
timeout.tv_usec = 0;
}
err = select(1, &read_set, NULL, NULL, ptimeout);
if (err == 0)
return K_TIMER;
if (err == -1) {
if (errno == EINTR)
return K_RESIZE;
return K_ERROR;
}
key = SLang_getkey();
if (key != K_ESC)
return key;
FD_ZERO(&read_set);
FD_SET(0, &read_set);
timeout.tv_sec = 0;
timeout.tv_usec = 20;
err = select(1, &read_set, NULL, NULL, &timeout);
if (err == 0)
return K_ESC;
SLang_ungetkey(key);
return SLkp_getkey();
}
static void ui__signal(int sig)
{
ui__exit(false);
psignal(sig, "perf");
exit(0);
}
int ui__init(void)
{
int err;
SLutf8_enable(-1);
SLtt_get_terminfo();
SLtt_get_screen_size();
err = SLsmg_init_smg();
if (err < 0)
goto out;
err = SLang_init_tty(0, 0, 0);
if (err < 0)
goto out;
err = SLkp_init();
if (err < 0) {
pr_err("TUI initialization failed.\n");
goto out;
}
SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB);
ui_helpline__init();
ui_browser__init();
ui_progress__init();
signal(SIGSEGV, ui__signal);
signal(SIGFPE, ui__signal);
signal(SIGINT, ui__signal);
signal(SIGQUIT, ui__signal);
signal(SIGTERM, ui__signal);
perf_error__register(&perf_tui_eops);
hist_browser__init_hpp();
out:
return err;
}
void ui__exit(bool wait_for_ok)
{
if (wait_for_ok)
ui__question_window("Fatal Error",
ui_helpline__last_msg,
"Press any key...", 0);
SLtt_set_cursor_visibility(1);
SLsmg_refresh();
SLsmg_reset_smg();
SLang_reset_tty();
perf_error__unregister(&perf_tui_eops);
}

243
tools/perf/ui/tui/util.c Normal file
View File

@@ -0,0 +1,243 @@
#include "../../util/util.h"
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <sys/ttydefaults.h>
#include "../../util/cache.h"
#include "../../util/debug.h"
#include "../browser.h"
#include "../keysyms.h"
#include "../helpline.h"
#include "../ui.h"
#include "../util.h"
#include "../libslang.h"
static void ui_browser__argv_write(struct ui_browser *browser,
void *entry, int row)
{
char **arg = entry;
bool current_entry = ui_browser__is_current_entry(browser, row);
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
HE_COLORSET_NORMAL);
slsmg_write_nstring(*arg, browser->width);
}
static int popup_menu__run(struct ui_browser *menu)
{
int key;
if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
return -1;
while (1) {
key = ui_browser__run(menu, 0);
switch (key) {
case K_RIGHT:
case K_ENTER:
key = menu->index;
break;
case K_LEFT:
case K_ESC:
case 'q':
case CTRL('c'):
key = -1;
break;
default:
continue;
}
break;
}
ui_browser__hide(menu);
return key;
}
int ui__popup_menu(int argc, char * const argv[])
{
struct ui_browser menu = {
.entries = (void *)argv,
.refresh = ui_browser__argv_refresh,
.seek = ui_browser__argv_seek,
.write = ui_browser__argv_write,
.nr_entries = argc,
};
return popup_menu__run(&menu);
}
int ui_browser__input_window(const char *title, const char *text, char *input,
const char *exit_msg, int delay_secs)
{
int x, y, len, key;
int max_len = 60, nr_lines = 0;
static char buf[50];
const char *t;
t = text;
while (1) {
const char *sep = strchr(t, '\n');
if (sep == NULL)
sep = strchr(t, '\0');
len = sep - t;
if (max_len < len)
max_len = len;
++nr_lines;
if (*sep == '\0')
break;
t = sep + 1;
}
max_len += 2;
nr_lines += 8;
y = SLtt_Screen_Rows / 2 - nr_lines / 2;
x = SLtt_Screen_Cols / 2 - max_len / 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 7;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
y += nr_lines;
len = 5;
while (len--) {
SLsmg_gotorc(y + len - 1, x);
SLsmg_write_nstring((char *)" ", max_len);
}
SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
SLsmg_gotorc(y + 3, x);
SLsmg_write_nstring((char *)exit_msg, max_len);
SLsmg_refresh();
x += 2;
len = 0;
key = ui__getch(delay_secs);
while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
if (key == K_BKSPC) {
if (len == 0)
goto next_key;
SLsmg_gotorc(y, x + --len);
SLsmg_write_char(' ');
} else {
buf[len] = key;
SLsmg_gotorc(y, x + len++);
SLsmg_write_char(key);
}
SLsmg_refresh();
/* XXX more graceful overflow handling needed */
if (len == sizeof(buf) - 1) {
ui_helpline__push("maximum size of symbol name reached!");
key = K_ENTER;
break;
}
next_key:
key = ui__getch(delay_secs);
}
buf[len] = '\0';
strncpy(input, buf, len+1);
return key;
}
int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs)
{
int x, y;
int max_len = 0, nr_lines = 0;
const char *t;
t = text;
while (1) {
const char *sep = strchr(t, '\n');
int len;
if (sep == NULL)
sep = strchr(t, '\0');
len = sep - t;
if (max_len < len)
max_len = len;
++nr_lines;
if (*sep == '\0')
break;
t = sep + 1;
}
max_len += 2;
nr_lines += 4;
y = SLtt_Screen_Rows / 2 - nr_lines / 2,
x = SLtt_Screen_Cols / 2 - max_len / 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 2;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
SLsmg_gotorc(y + nr_lines - 2, x);
SLsmg_write_nstring((char *)" ", max_len);
SLsmg_gotorc(y + nr_lines - 1, x);
SLsmg_write_nstring((char *)exit_msg, max_len);
SLsmg_refresh();
return ui__getch(delay_secs);
}
int ui__help_window(const char *text)
{
return ui__question_window("Help", text, "Press any key...", 0);
}
int ui__dialog_yesno(const char *msg)
{
return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
}
static int __ui__warning(const char *title, const char *format, va_list args)
{
char *s;
if (vasprintf(&s, format, args) > 0) {
int key;
pthread_mutex_lock(&ui__lock);
key = ui__question_window(title, s, "Press any key...", 0);
pthread_mutex_unlock(&ui__lock);
free(s);
return key;
}
fprintf(stderr, "%s\n", title);
vfprintf(stderr, format, args);
return K_ESC;
}
static int perf_tui__error(const char *format, va_list args)
{
return __ui__warning("Error:", format, args);
}
static int perf_tui__warning(const char *format, va_list args)
{
return __ui__warning("Warning:", format, args);
}
struct perf_error_ops perf_tui_eops = {
.error = perf_tui__error,
.warning = perf_tui__warning,
};

39
tools/perf/ui/ui.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef _PERF_UI_H_
#define _PERF_UI_H_ 1
#include <pthread.h>
#include <stdbool.h>
#include <linux/compiler.h>
extern pthread_mutex_t ui__lock;
extern int use_browser;
void setup_browser(bool fallback_to_pager);
void exit_browser(bool wait_for_ok);
#ifdef SLANG_SUPPORT
int ui__init(void);
void ui__exit(bool wait_for_ok);
#else
static inline int ui__init(void)
{
return -1;
}
static inline void ui__exit(bool wait_for_ok __maybe_unused) {}
#endif
#ifdef GTK2_SUPPORT
int perf_gtk__init(void);
void perf_gtk__exit(bool wait_for_ok);
#else
static inline int perf_gtk__init(void)
{
return -1;
}
static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {}
#endif
void ui__refresh_dimensions(bool force);
#endif /* _PERF_UI_H_ */

84
tools/perf/ui/util.c Normal file
View File

@@ -0,0 +1,84 @@
#include "util.h"
#include "../debug.h"
/*
* Default error logging functions
*/
static int perf_stdio__error(const char *format, va_list args)
{
fprintf(stderr, "Error:\n");
vfprintf(stderr, format, args);
return 0;
}
static int perf_stdio__warning(const char *format, va_list args)
{
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
return 0;
}
static struct perf_error_ops default_eops =
{
.error = perf_stdio__error,
.warning = perf_stdio__warning,
};
static struct perf_error_ops *perf_eops = &default_eops;
int ui__error(const char *format, ...)
{
int ret;
va_list args;
va_start(args, format);
ret = perf_eops->error(format, args);
va_end(args);
return ret;
}
int ui__warning(const char *format, ...)
{
int ret;
va_list args;
va_start(args, format);
ret = perf_eops->warning(format, args);
va_end(args);
return ret;
}
/**
* perf_error__register - Register error logging functions
* @eops: The pointer to error logging function struct
*
* Register UI-specific error logging functions. Before calling this,
* other logging functions should be unregistered, if any.
*/
int perf_error__register(struct perf_error_ops *eops)
{
if (perf_eops != &default_eops)
return -1;
perf_eops = eops;
return 0;
}
/**
* perf_error__unregister - Unregister error logging functions
* @eops: The pointer to error logging function struct
*
* Unregister already registered error logging functions.
*/
int perf_error__unregister(struct perf_error_ops *eops)
{
if (perf_eops != eops)
return -1;
perf_eops = &default_eops;
return 0;
}

21
tools/perf/ui/util.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef _PERF_UI_UTIL_H_
#define _PERF_UI_UTIL_H_ 1
#include <stdarg.h>
int ui__getch(int delay_secs);
int ui__popup_menu(int argc, char * const argv[]);
int ui__help_window(const char *text);
int ui__dialog_yesno(const char *msg);
int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs);
struct perf_error_ops {
int (*error)(const char *format, va_list args);
int (*warning)(const char *format, va_list args);
};
int perf_error__register(struct perf_error_ops *eops);
int perf_error__unregister(struct perf_error_ops *eops);
#endif /* _PERF_UI_UTIL_H_ */