mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 07:18:51 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
722
tools/perf/ui/browser.c
Normal file
722
tools/perf/ui/browser.c
Normal file
|
|
@ -0,0 +1,722 @@
|
|||
#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->rows)
|
||||
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 = browser->rows = 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;
|
||||
|
||||
if (browser->refresh_dimensions == NULL)
|
||||
browser->refresh_dimensions = ui_browser__refresh_dimensions;
|
||||
|
||||
browser->refresh_dimensions(browser);
|
||||
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
__ui_browser__show_title(browser, title);
|
||||
|
||||
browser->title = title;
|
||||
zfree(&browser->helpline);
|
||||
|
||||
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)
|
||||
{
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
ui_helpline__pop();
|
||||
zfree(&browser->helpline);
|
||||
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 = 0;
|
||||
|
||||
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);
|
||||
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->rows) {
|
||||
++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->rows > browser->nr_entries - 1)
|
||||
break;
|
||||
|
||||
offset = browser->rows;
|
||||
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->rows)
|
||||
offset = browser->top_idx;
|
||||
else
|
||||
offset = browser->rows;
|
||||
|
||||
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->rows - 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->rows)
|
||||
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 - 1 + 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->rows)
|
||||
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->rows) {
|
||||
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->rows - 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->rows)
|
||||
end_row = browser->rows - 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->rows) {
|
||||
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();
|
||||
}
|
||||
75
tools/perf/ui/browser.h
Normal file
75
tools/perf/ui/browser.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef _PERF_UI_BROWSER_H_
|
||||
#define _PERF_UI_BROWSER_H_ 1
|
||||
|
||||
#include <linux/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, rows;
|
||||
int current_color;
|
||||
void *priv;
|
||||
const char *title;
|
||||
char *helpline;
|
||||
void (*refresh_dimensions)(struct ui_browser *browser);
|
||||
unsigned int (*refresh)(struct ui_browser *browser);
|
||||
void (*write)(struct ui_browser *browser, void *entry, int row);
|
||||
void (*seek)(struct ui_browser *browser, off_t offset, int whence);
|
||||
bool (*filter)(struct ui_browser *browser, 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 *browser,
|
||||
double percent, bool current);
|
||||
bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row);
|
||||
void ui_browser__refresh_dimensions(struct ui_browser *browser);
|
||||
void ui_browser__reset_index(struct ui_browser *browser);
|
||||
|
||||
void ui_browser__gotorc(struct ui_browser *browser, 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 *browser, const char *title,
|
||||
const char *helpline, ...);
|
||||
void ui_browser__hide(struct ui_browser *browser);
|
||||
int ui_browser__refresh(struct ui_browser *browser);
|
||||
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);
|
||||
struct perf_session_env;
|
||||
int tui__header_window(struct perf_session_env *env);
|
||||
|
||||
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 *browser, off_t offset, int whence);
|
||||
unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser);
|
||||
|
||||
void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence);
|
||||
unsigned int ui_browser__list_head_refresh(struct ui_browser *browser);
|
||||
|
||||
void ui_browser__init(void);
|
||||
void annotate_browser__init(void);
|
||||
#endif /* _PERF_UI_BROWSER_H_ */
|
||||
1023
tools/perf/ui/browsers/annotate.c
Normal file
1023
tools/perf/ui/browsers/annotate.c
Normal file
File diff suppressed because it is too large
Load diff
128
tools/perf/ui/browsers/header.c
Normal file
128
tools/perf/ui/browsers/header.c
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#include "util/cache.h"
|
||||
#include "util/debug.h"
|
||||
#include "ui/browser.h"
|
||||
#include "ui/keysyms.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/util.h"
|
||||
#include "ui/libslang.h"
|
||||
#include "util/header.h"
|
||||
#include "util/session.h"
|
||||
|
||||
static void ui_browser__argv_write(struct ui_browser *browser,
|
||||
void *entry, int row)
|
||||
{
|
||||
char **arg = entry;
|
||||
char *str = *arg;
|
||||
char empty[] = " ";
|
||||
bool current_entry = ui_browser__is_current_entry(browser, row);
|
||||
unsigned long offset = (unsigned long)browser->priv;
|
||||
|
||||
if (offset >= strlen(str))
|
||||
str = empty;
|
||||
else
|
||||
str = str + offset;
|
||||
|
||||
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
|
||||
HE_COLORSET_NORMAL);
|
||||
|
||||
slsmg_write_nstring(str, browser->width);
|
||||
}
|
||||
|
||||
static int list_menu__run(struct ui_browser *menu)
|
||||
{
|
||||
int key;
|
||||
unsigned long offset;
|
||||
const char help[] =
|
||||
"h/?/F1 Show this window\n"
|
||||
"UP/DOWN/PGUP\n"
|
||||
"PGDN/SPACE\n"
|
||||
"LEFT/RIGHT Navigate\n"
|
||||
"q/ESC/CTRL+C Exit browser";
|
||||
|
||||
if (ui_browser__show(menu, "Header information", "Press 'q' to exit") < 0)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
key = ui_browser__run(menu, 0);
|
||||
|
||||
switch (key) {
|
||||
case K_RIGHT:
|
||||
offset = (unsigned long)menu->priv;
|
||||
offset += 10;
|
||||
menu->priv = (void *)offset;
|
||||
continue;
|
||||
case K_LEFT:
|
||||
offset = (unsigned long)menu->priv;
|
||||
if (offset >= 10)
|
||||
offset -= 10;
|
||||
menu->priv = (void *)offset;
|
||||
continue;
|
||||
case K_F1:
|
||||
case 'h':
|
||||
case '?':
|
||||
ui_browser__help_window(menu, help);
|
||||
continue;
|
||||
case K_ESC:
|
||||
case 'q':
|
||||
case CTRL('c'):
|
||||
key = -1;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ui_browser__hide(menu);
|
||||
return key;
|
||||
}
|
||||
|
||||
static int ui__list_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 list_menu__run(&menu);
|
||||
}
|
||||
|
||||
int tui__header_window(struct perf_session_env *env)
|
||||
{
|
||||
int i, argc = 0;
|
||||
char **argv;
|
||||
struct perf_session *session;
|
||||
char *ptr, *pos;
|
||||
size_t size;
|
||||
FILE *fp = open_memstream(&ptr, &size);
|
||||
|
||||
session = container_of(env, struct perf_session, header.env);
|
||||
perf_header__fprintf_info(session, fp, true);
|
||||
fclose(fp);
|
||||
|
||||
for (pos = ptr, argc = 0; (pos = strchr(pos, '\n')) != NULL; pos++)
|
||||
argc++;
|
||||
|
||||
argv = calloc(argc + 1, sizeof(*argv));
|
||||
if (argv == NULL)
|
||||
goto out;
|
||||
|
||||
argv[0] = pos = ptr;
|
||||
for (i = 1; (pos = strchr(pos, '\n')) != NULL; i++) {
|
||||
*pos++ = '\0';
|
||||
argv[i] = pos;
|
||||
}
|
||||
|
||||
BUG_ON(i != argc + 1);
|
||||
|
||||
ui__list_menu(argc, argv);
|
||||
|
||||
out:
|
||||
free(argv);
|
||||
free(ptr);
|
||||
return 0;
|
||||
}
|
||||
2028
tools/perf/ui/browsers/hists.c
Normal file
2028
tools/perf/ui/browsers/hists.c
Normal file
File diff suppressed because it is too large
Load diff
130
tools/perf/ui/browsers/map.c
Normal file
130
tools/perf/ui/browsers/map.c
Normal 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 *browser, void *nd, int row)
|
||||
{
|
||||
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
|
||||
struct map_browser *mb = container_of(browser, struct map_browser, b);
|
||||
bool current_entry = ui_browser__is_current_entry(browser, row);
|
||||
int width;
|
||||
|
||||
ui_browser__set_percent_color(browser, 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 = browser->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 *browser)
|
||||
{
|
||||
return ((void *)browser) - sizeof(struct rb_node) - sizeof(u32);
|
||||
}
|
||||
|
||||
static int map_browser__search(struct map_browser *browser)
|
||||
{
|
||||
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(browser->map, addr, NULL);
|
||||
} else
|
||||
sym = map__find_symbol_by_name(browser->map, target, NULL);
|
||||
|
||||
if (sym != NULL) {
|
||||
u32 *idx = symbol__browser_index(sym);
|
||||
|
||||
browser->b.top = &sym->rb_node;
|
||||
browser->b.index = browser->b.top_idx = *idx;
|
||||
} else
|
||||
ui_helpline__fpush("%s not found!", target);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int map_browser__run(struct map_browser *browser)
|
||||
{
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(&browser->b, browser->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(&browser->b, 0);
|
||||
|
||||
switch (key) {
|
||||
case '/':
|
||||
if (verbose)
|
||||
map_browser__search(browser);
|
||||
default:
|
||||
break;
|
||||
case K_LEFT:
|
||||
case K_ESC:
|
||||
case 'q':
|
||||
case CTRL('c'):
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
ui_browser__hide(&browser->b);
|
||||
return key;
|
||||
}
|
||||
|
||||
int map__browse(struct map *map)
|
||||
{
|
||||
struct map_browser mb = {
|
||||
.b = {
|
||||
.entries = &map->dso->symbols[map->type],
|
||||
.refresh = ui_browser__rb_tree_refresh,
|
||||
.seek = ui_browser__rb_tree_seek,
|
||||
.write = map_browser__write,
|
||||
},
|
||||
.map = map,
|
||||
};
|
||||
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);
|
||||
}
|
||||
6
tools/perf/ui/browsers/map.h
Normal file
6
tools/perf/ui/browsers/map.h
Normal 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 *map);
|
||||
#endif /* _PERF_UI_MAP_BROWSER_H_ */
|
||||
187
tools/perf/ui/browsers/scripts.c
Normal file
187
tools/perf/ui/browsers/scripts.c
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
#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 *browser)
|
||||
{
|
||||
int key;
|
||||
|
||||
if (ui_browser__show(&browser->b, browser->script_name,
|
||||
"Press <- or ESC to exit") < 0)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
key = ui_browser__run(&browser->b, 0);
|
||||
|
||||
/* We can add some special key handling here if needed */
|
||||
break;
|
||||
}
|
||||
|
||||
ui_browser__hide(&browser->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;
|
||||
|
||||
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;
|
||||
}
|
||||
252
tools/perf/ui/gtk/annotate.c
Normal file
252
tools/perf/ui/gtk/annotate.c
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
#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, ¬es->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, ¬es->src->source, node) {
|
||||
list_del(&pos->node);
|
||||
disasm_line__free(pos);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
int hist_entry__gtk_annotate(struct hist_entry *he,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt)
|
||||
{
|
||||
return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
87
tools/perf/ui/gtk/browser.c
Normal file
87
tools/perf/ui/gtk/browser.c
Normal 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_SUPPORT
|
||||
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;
|
||||
}
|
||||
67
tools/perf/ui/gtk/gtk.h
Normal file
67
tools/perf/ui/gtk/gtk.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#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_SUPPORT
|
||||
GtkWidget *info_bar;
|
||||
GtkWidget *message_label;
|
||||
#endif
|
||||
GtkWidget *statbar;
|
||||
guint statbar_ctx_id;
|
||||
};
|
||||
|
||||
int perf_gtk__init(void);
|
||||
void perf_gtk__exit(bool wait_for_ok);
|
||||
|
||||
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 gtk_ui_progress__init(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_SUPPORT
|
||||
GtkWidget *perf_gtk__setup_info_bar(void);
|
||||
#else
|
||||
static inline GtkWidget *perf_gtk__setup_info_bar(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct perf_evsel;
|
||||
struct perf_evlist;
|
||||
struct hist_entry;
|
||||
struct hist_browser_timer;
|
||||
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
struct hist_browser_timer *hbt,
|
||||
float min_pcnt);
|
||||
int hist_entry__gtk_annotate(struct hist_entry *he,
|
||||
struct perf_evsel *evsel,
|
||||
struct hist_browser_timer *hbt);
|
||||
void perf_gtk__show_annotations(void);
|
||||
|
||||
#endif /* _PERF_GTK_H_ */
|
||||
57
tools/perf/ui/gtk/helpline.c
Normal file
57
tools/perf/ui/gtk/helpline.c
Normal 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 = >k_helpline_fns;
|
||||
}
|
||||
365
tools/perf/ui/gtk/hists.c
Normal file
365
tools/perf/ui/gtk/hists.c
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
#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(struct perf_hpp *hpp, const char *fmt, ...)
|
||||
{
|
||||
int ret = 0;
|
||||
int len;
|
||||
va_list args;
|
||||
double percent;
|
||||
const char *markup;
|
||||
char *buf = hpp->buf;
|
||||
size_t size = hpp->size;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = va_arg(args, int);
|
||||
percent = va_arg(args, double);
|
||||
va_end(args);
|
||||
|
||||
markup = perf_gtk__get_percent_color(percent);
|
||||
if (markup)
|
||||
ret += scnprintf(buf, size, markup);
|
||||
|
||||
ret += scnprintf(buf + ret, size - ret, fmt, len, percent);
|
||||
|
||||
if (markup)
|
||||
ret += scnprintf(buf + ret, size - ret, "</span>");
|
||||
|
||||
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_fmt *fmt, \
|
||||
struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
|
||||
__percent_color_snprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
|
||||
static u64 he_get_acc_##_field(struct hist_entry *he) \
|
||||
{ \
|
||||
return he->stat_acc->_field; \
|
||||
} \
|
||||
\
|
||||
static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
|
||||
__percent_color_snprintf, true); \
|
||||
}
|
||||
|
||||
__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)
|
||||
__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
|
||||
|
||||
#undef __HPP_COLOR_PERCENT_FN
|
||||
|
||||
|
||||
void perf_gtk__init_hpp(void)
|
||||
{
|
||||
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;
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
|
||||
perf_gtk__hpp_color_overhead_acc;
|
||||
}
|
||||
|
||||
static void callchain_list__sym_name(struct callchain_list *cl,
|
||||
char *bf, size_t bfsize)
|
||||
{
|
||||
if (cl->ms.sym)
|
||||
scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
|
||||
else
|
||||
scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
|
||||
}
|
||||
|
||||
static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
|
||||
GtkTreeIter *parent, int col, u64 total)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
bool has_single_node = (rb_first(root) == rb_last(root));
|
||||
|
||||
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
|
||||
struct callchain_node *node;
|
||||
struct callchain_list *chain;
|
||||
GtkTreeIter iter, new_parent;
|
||||
bool need_new_parent;
|
||||
double percent;
|
||||
u64 hits, child_total;
|
||||
|
||||
node = rb_entry(nd, struct callchain_node, rb_node);
|
||||
|
||||
hits = callchain_cumul_hits(node);
|
||||
percent = 100.0 * hits / total;
|
||||
|
||||
new_parent = *parent;
|
||||
need_new_parent = !has_single_node && (node->val_nr > 1);
|
||||
|
||||
list_for_each_entry(chain, &node->val, list) {
|
||||
char buf[128];
|
||||
|
||||
gtk_tree_store_append(store, &iter, &new_parent);
|
||||
|
||||
scnprintf(buf, sizeof(buf), "%5.2f%%", percent);
|
||||
gtk_tree_store_set(store, &iter, 0, buf, -1);
|
||||
|
||||
callchain_list__sym_name(chain, buf, sizeof(buf));
|
||||
gtk_tree_store_set(store, &iter, col, buf, -1);
|
||||
|
||||
if (need_new_parent) {
|
||||
/*
|
||||
* Only show the top-most symbol in a callchain
|
||||
* if it's not the only callchain.
|
||||
*/
|
||||
new_parent = iter;
|
||||
need_new_parent = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
child_total = node->children_hit;
|
||||
else
|
||||
child_total = total;
|
||||
|
||||
/* Now 'iter' contains info of the last callchain_list */
|
||||
perf_gtk__add_callchain(&node->rb_root, store, &iter, col,
|
||||
child_total);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_row_activated(GtkTreeView *view, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col __maybe_unused,
|
||||
gpointer user_data __maybe_unused)
|
||||
{
|
||||
bool expanded = gtk_tree_view_row_expanded(view, path);
|
||||
|
||||
if (expanded)
|
||||
gtk_tree_view_collapse_row(view, path);
|
||||
else
|
||||
gtk_tree_view_expand_row(view, path, FALSE);
|
||||
}
|
||||
|
||||
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
||||
float min_pcnt)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
GType col_types[MAX_COLUMNS];
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeStore *store;
|
||||
struct rb_node *nd;
|
||||
GtkWidget *view;
|
||||
int col_idx;
|
||||
int sym_col = -1;
|
||||
int nr_cols;
|
||||
char s[512];
|
||||
|
||||
struct perf_hpp hpp = {
|
||||
.buf = s,
|
||||
.size = sizeof(s),
|
||||
};
|
||||
|
||||
nr_cols = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt)
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
|
||||
store = gtk_tree_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) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* XXX no way to determine where symcol column is..
|
||||
* Just use last column for now.
|
||||
*/
|
||||
if (perf_hpp__is_sort_entry(fmt))
|
||||
sym_col = col_idx;
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, fmt->name,
|
||||
renderer, "markup",
|
||||
col_idx++, NULL);
|
||||
}
|
||||
|
||||
for (col_idx = 0; col_idx < nr_cols; col_idx++) {
|
||||
GtkTreeViewColumn *column;
|
||||
|
||||
column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx);
|
||||
gtk_tree_view_column_set_resizable(column, TRUE);
|
||||
|
||||
if (col_idx == sym_col) {
|
||||
gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view),
|
||||
column);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
u64 total = hists__total_period(h->hists);
|
||||
float percent;
|
||||
|
||||
if (h->filtered)
|
||||
continue;
|
||||
|
||||
percent = hist_entry__get_percent_limit(h);
|
||||
if (percent < min_pcnt)
|
||||
continue;
|
||||
|
||||
gtk_tree_store_append(store, &iter, NULL);
|
||||
|
||||
col_idx = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
if (fmt->color)
|
||||
fmt->color(fmt, &hpp, h);
|
||||
else
|
||||
fmt->entry(fmt, &hpp, h);
|
||||
|
||||
gtk_tree_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain && sort__has_sym) {
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
total = symbol_conf.cumulate_callchain ?
|
||||
h->stat_acc->period : h->stat.period;
|
||||
|
||||
perf_gtk__add_callchain(&h->sorted_chain, store, &iter,
|
||||
sym_col, total);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
|
||||
|
||||
g_signal_connect(view, "row-activated",
|
||||
G_CALLBACK(on_row_activated), NULL);
|
||||
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,
|
||||
float min_pcnt)
|
||||
{
|
||||
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);
|
||||
|
||||
evlist__for_each(evlist, pos) {
|
||||
struct hists *hists = evsel__hists(pos);
|
||||
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, min_pcnt);
|
||||
|
||||
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;
|
||||
}
|
||||
59
tools/perf/ui/gtk/progress.c
Normal file
59
tools/perf/ui/gtk/progress.c
Normal 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_ui_progress__update(struct ui_progress *p)
|
||||
{
|
||||
double fraction = p->total ? 1.0 * p->curr / p->total : 0.0;
|
||||
char buf[1024];
|
||||
|
||||
if (dialog == NULL) {
|
||||
GtkWidget *vbox = gtk_vbox_new(TRUE, 5);
|
||||
GtkWidget *label = gtk_label_new(p->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, p->curr, p->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_ui_progress__finish(void)
|
||||
{
|
||||
/* this will also destroy all of its children */
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
dialog = NULL;
|
||||
}
|
||||
|
||||
static struct ui_progress_ops gtk_ui_progress__ops = {
|
||||
.update = gtk_ui_progress__update,
|
||||
.finish = gtk_ui_progress__finish,
|
||||
};
|
||||
|
||||
void gtk_ui_progress__init(void)
|
||||
{
|
||||
ui_progress__ops = >k_ui_progress__ops;
|
||||
}
|
||||
23
tools/perf/ui/gtk/setup.c
Normal file
23
tools/perf/ui/gtk/setup.c
Normal 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();
|
||||
gtk_ui_progress__init();
|
||||
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();
|
||||
}
|
||||
112
tools/perf/ui/gtk/util.c
Normal file
112
tools/perf/ui/gtk/util.c
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#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;
|
||||
|
||||
zfree(ctx);
|
||||
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_SUPPORT
|
||||
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_SUPPORT
|
||||
.warning = perf_gtk__warning_info_bar,
|
||||
#else
|
||||
.warning = perf_gtk__warning_statusbar,
|
||||
#endif
|
||||
};
|
||||
73
tools/perf/ui/helpline.c
Normal file
73
tools/perf/ui/helpline.c
Normal 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
29
tools/perf/ui/helpline.h
Normal 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_ */
|
||||
686
tools/perf/ui/hist.c
Normal file
686
tools/perf/ui/hist.c
Normal file
|
|
@ -0,0 +1,686 @@
|
|||
#include <math.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#include "../util/hist.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/sort.h"
|
||||
#include "../util/evsel.h"
|
||||
|
||||
/* hist period print (hpp) functions */
|
||||
|
||||
#define hpp__call_print_fn(hpp, fn, fmt, ...) \
|
||||
({ \
|
||||
int __ret = fn(hpp, fmt, ##__VA_ARGS__); \
|
||||
advance_hpp(hpp, __ret); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
hpp_field_fn get_field, const char *fmt, int len,
|
||||
hpp_snprint_fn print_fn, bool fmt_percent)
|
||||
{
|
||||
int ret;
|
||||
struct hists *hists = he->hists;
|
||||
struct perf_evsel *evsel = hists_to_evsel(hists);
|
||||
char *buf = hpp->buf;
|
||||
size_t size = hpp->size;
|
||||
|
||||
if (fmt_percent) {
|
||||
double percent = 0.0;
|
||||
u64 total = hists__total_period(hists);
|
||||
|
||||
if (total)
|
||||
percent = 100.0 * get_field(he) / total;
|
||||
|
||||
ret = hpp__call_print_fn(hpp, print_fn, fmt, len, percent);
|
||||
} else
|
||||
ret = hpp__call_print_fn(hpp, print_fn, fmt, len, 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 = hists__total_period(pair->hists);
|
||||
|
||||
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
|
||||
*/
|
||||
if (fmt_percent) {
|
||||
ret += hpp__call_print_fn(hpp, print_fn,
|
||||
fmt, len, 0.0);
|
||||
} else {
|
||||
ret += hpp__call_print_fn(hpp, print_fn,
|
||||
fmt, len, 0ULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (fmt_percent) {
|
||||
ret += hpp__call_print_fn(hpp, print_fn, fmt, len,
|
||||
100.0 * period / total);
|
||||
} else {
|
||||
ret += hpp__call_print_fn(hpp, print_fn, fmt,
|
||||
len, 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
|
||||
*/
|
||||
if (fmt_percent) {
|
||||
ret += hpp__call_print_fn(hpp, print_fn,
|
||||
fmt, len, 0.0);
|
||||
} else {
|
||||
ret += hpp__call_print_fn(hpp, print_fn,
|
||||
fmt, len, 0ULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore original buf and size as it's where caller expects
|
||||
* the result will be saved.
|
||||
*/
|
||||
hpp->buf = buf;
|
||||
hpp->size = size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct hist_entry *he, hpp_field_fn get_field,
|
||||
const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
|
||||
{
|
||||
int len = fmt->user_len ?: fmt->len;
|
||||
|
||||
if (symbol_conf.field_sep) {
|
||||
return __hpp__fmt(hpp, he, get_field, fmtstr, 1,
|
||||
print_fn, fmt_percent);
|
||||
}
|
||||
|
||||
if (fmt_percent)
|
||||
len -= 2; /* 2 for a space and a % sign */
|
||||
else
|
||||
len -= 1;
|
||||
|
||||
return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmt_percent);
|
||||
}
|
||||
|
||||
int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct hist_entry *he, hpp_field_fn get_field,
|
||||
const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
|
||||
{
|
||||
if (!symbol_conf.cumulate_callchain) {
|
||||
int len = fmt->user_len ?: fmt->len;
|
||||
return snprintf(hpp->buf, hpp->size, " %*s", len - 1, "N/A");
|
||||
}
|
||||
|
||||
return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmt_percent);
|
||||
}
|
||||
|
||||
static int field_cmp(u64 field_a, u64 field_b)
|
||||
{
|
||||
if (field_a > field_b)
|
||||
return 1;
|
||||
if (field_a < field_b)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
|
||||
hpp_field_fn get_field)
|
||||
{
|
||||
s64 ret;
|
||||
int i, nr_members;
|
||||
struct perf_evsel *evsel;
|
||||
struct hist_entry *pair;
|
||||
u64 *fields_a, *fields_b;
|
||||
|
||||
ret = field_cmp(get_field(a), get_field(b));
|
||||
if (ret || !symbol_conf.event_group)
|
||||
return ret;
|
||||
|
||||
evsel = hists_to_evsel(a->hists);
|
||||
if (!perf_evsel__is_group_event(evsel))
|
||||
return ret;
|
||||
|
||||
nr_members = evsel->nr_members;
|
||||
fields_a = calloc(sizeof(*fields_a), nr_members);
|
||||
fields_b = calloc(sizeof(*fields_b), nr_members);
|
||||
|
||||
if (!fields_a || !fields_b)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(pair, &a->pairs.head, pairs.node) {
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
fields_a[perf_evsel__group_idx(evsel)] = get_field(pair);
|
||||
}
|
||||
|
||||
list_for_each_entry(pair, &b->pairs.head, pairs.node) {
|
||||
evsel = hists_to_evsel(pair->hists);
|
||||
fields_b[perf_evsel__group_idx(evsel)] = get_field(pair);
|
||||
}
|
||||
|
||||
for (i = 1; i < nr_members; i++) {
|
||||
ret = field_cmp(fields_a[i], fields_b[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
free(fields_a);
|
||||
free(fields_b);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
|
||||
hpp_field_fn get_field)
|
||||
{
|
||||
s64 ret = 0;
|
||||
|
||||
if (symbol_conf.cumulate_callchain) {
|
||||
/*
|
||||
* Put caller above callee when they have equal period.
|
||||
*/
|
||||
ret = field_cmp(get_field(a), get_field(b));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = b->callchain->max_depth - a->callchain->max_depth;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hpp__width_fn(struct perf_hpp_fmt *fmt,
|
||||
struct perf_hpp *hpp __maybe_unused,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
int len = fmt->user_len ?: fmt->len;
|
||||
|
||||
if (symbol_conf.event_group)
|
||||
len = max(len, evsel->nr_members * fmt->len);
|
||||
|
||||
if (len < (int)strlen(fmt->name))
|
||||
len = strlen(fmt->name);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
int len = hpp__width_fn(fmt, hpp, evsel);
|
||||
return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name);
|
||||
}
|
||||
|
||||
static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
ssize_t ssize = hpp->size;
|
||||
double percent;
|
||||
int ret, len;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = va_arg(args, int);
|
||||
percent = va_arg(args, double);
|
||||
ret = percent_color_len_snprintf(hpp->buf, hpp->size, fmt, len, percent);
|
||||
va_end(args);
|
||||
|
||||
return (ret >= ssize) ? (ssize - 1) : ret;
|
||||
}
|
||||
|
||||
static int hpp_entry_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
ssize_t ssize = hpp->size;
|
||||
int ret;
|
||||
|
||||
va_start(args, fmt);
|
||||
ret = vsnprintf(hpp->buf, hpp->size, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return (ret >= ssize) ? (ssize - 1) : ret;
|
||||
}
|
||||
|
||||
#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_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
|
||||
hpp_color_scnprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
|
||||
hpp_entry_scnprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_SORT_FN(_type, _field) \
|
||||
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
|
||||
{ \
|
||||
return __hpp__sort(a, b, he_get_##_field); \
|
||||
}
|
||||
|
||||
#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
|
||||
static u64 he_get_acc_##_field(struct hist_entry *he) \
|
||||
{ \
|
||||
return he->stat_acc->_field; \
|
||||
} \
|
||||
\
|
||||
static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
|
||||
hpp_color_scnprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
|
||||
hpp_entry_scnprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_SORT_ACC_FN(_type, _field) \
|
||||
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
|
||||
{ \
|
||||
return __hpp__sort_acc(a, b, he_get_acc_##_field); \
|
||||
}
|
||||
|
||||
#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_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return hpp__fmt(fmt, hpp, he, he_get_raw_##_field, " %*"PRIu64, \
|
||||
hpp_entry_scnprintf, false); \
|
||||
}
|
||||
|
||||
#define __HPP_SORT_RAW_FN(_type, _field) \
|
||||
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
|
||||
{ \
|
||||
return __hpp__sort(a, b, he_get_raw_##_field); \
|
||||
}
|
||||
|
||||
|
||||
#define HPP_PERCENT_FNS(_type, _field) \
|
||||
__HPP_COLOR_PERCENT_FN(_type, _field) \
|
||||
__HPP_ENTRY_PERCENT_FN(_type, _field) \
|
||||
__HPP_SORT_FN(_type, _field)
|
||||
|
||||
#define HPP_PERCENT_ACC_FNS(_type, _field) \
|
||||
__HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
|
||||
__HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \
|
||||
__HPP_SORT_ACC_FN(_type, _field)
|
||||
|
||||
#define HPP_RAW_FNS(_type, _field) \
|
||||
__HPP_ENTRY_RAW_FN(_type, _field) \
|
||||
__HPP_SORT_RAW_FN(_type, _field)
|
||||
|
||||
HPP_PERCENT_FNS(overhead, period)
|
||||
HPP_PERCENT_FNS(overhead_sys, period_sys)
|
||||
HPP_PERCENT_FNS(overhead_us, period_us)
|
||||
HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys)
|
||||
HPP_PERCENT_FNS(overhead_guest_us, period_guest_us)
|
||||
HPP_PERCENT_ACC_FNS(overhead_acc, period)
|
||||
|
||||
HPP_RAW_FNS(samples, nr_events)
|
||||
HPP_RAW_FNS(period, period)
|
||||
|
||||
static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
|
||||
struct hist_entry *b __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HPP__COLOR_PRINT_FNS(_name, _fn) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.header = hpp__header_fn, \
|
||||
.width = hpp__width_fn, \
|
||||
.color = hpp__color_ ## _fn, \
|
||||
.entry = hpp__entry_ ## _fn, \
|
||||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _fn, \
|
||||
}
|
||||
|
||||
#define HPP__COLOR_ACC_PRINT_FNS(_name, _fn) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.header = hpp__header_fn, \
|
||||
.width = hpp__width_fn, \
|
||||
.color = hpp__color_ ## _fn, \
|
||||
.entry = hpp__entry_ ## _fn, \
|
||||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _fn, \
|
||||
}
|
||||
|
||||
#define HPP__PRINT_FNS(_name, _fn) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.header = hpp__header_fn, \
|
||||
.width = hpp__width_fn, \
|
||||
.entry = hpp__entry_ ## _fn, \
|
||||
.cmp = hpp__nop_cmp, \
|
||||
.collapse = hpp__nop_cmp, \
|
||||
.sort = hpp__sort_ ## _fn, \
|
||||
}
|
||||
|
||||
struct perf_hpp_fmt perf_hpp__format[] = {
|
||||
HPP__COLOR_PRINT_FNS("Overhead", overhead),
|
||||
HPP__COLOR_PRINT_FNS("sys", overhead_sys),
|
||||
HPP__COLOR_PRINT_FNS("usr", overhead_us),
|
||||
HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys),
|
||||
HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us),
|
||||
HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc),
|
||||
HPP__PRINT_FNS("Samples", samples),
|
||||
HPP__PRINT_FNS("Period", period)
|
||||
};
|
||||
|
||||
LIST_HEAD(perf_hpp__list);
|
||||
LIST_HEAD(perf_hpp__sort_list);
|
||||
|
||||
|
||||
#undef HPP__COLOR_PRINT_FNS
|
||||
#undef HPP__COLOR_ACC_PRINT_FNS
|
||||
#undef HPP__PRINT_FNS
|
||||
|
||||
#undef HPP_PERCENT_FNS
|
||||
#undef HPP_PERCENT_ACC_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_COLOR_ACC_PERCENT_FN
|
||||
#undef __HPP_ENTRY_ACC_PERCENT_FN
|
||||
#undef __HPP_ENTRY_RAW_FN
|
||||
#undef __HPP_SORT_FN
|
||||
#undef __HPP_SORT_ACC_FN
|
||||
#undef __HPP_SORT_RAW_FN
|
||||
|
||||
|
||||
void perf_hpp__init(void)
|
||||
{
|
||||
struct list_head *list;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
|
||||
struct perf_hpp_fmt *fmt = &perf_hpp__format[i];
|
||||
|
||||
INIT_LIST_HEAD(&fmt->list);
|
||||
|
||||
/* sort_list may be linked by setup_sorting() */
|
||||
if (fmt->sort_list.next == NULL)
|
||||
INIT_LIST_HEAD(&fmt->sort_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* If user specified field order, no need to setup default fields.
|
||||
*/
|
||||
if (is_strict_order(field_order))
|
||||
return;
|
||||
|
||||
if (symbol_conf.cumulate_callchain) {
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC);
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self";
|
||||
}
|
||||
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
|
||||
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);
|
||||
|
||||
/* prepend overhead field for backward compatiblity. */
|
||||
list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list;
|
||||
if (list_empty(list))
|
||||
list_add(list, &perf_hpp__sort_list);
|
||||
|
||||
if (symbol_conf.cumulate_callchain) {
|
||||
list = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC].sort_list;
|
||||
if (list_empty(list))
|
||||
list_add(list, &perf_hpp__sort_list);
|
||||
}
|
||||
}
|
||||
|
||||
void perf_hpp__column_register(struct perf_hpp_fmt *format)
|
||||
{
|
||||
list_add_tail(&format->list, &perf_hpp__list);
|
||||
}
|
||||
|
||||
void perf_hpp__column_unregister(struct perf_hpp_fmt *format)
|
||||
{
|
||||
list_del(&format->list);
|
||||
}
|
||||
|
||||
void perf_hpp__register_sort_field(struct perf_hpp_fmt *format)
|
||||
{
|
||||
list_add_tail(&format->sort_list, &perf_hpp__sort_list);
|
||||
}
|
||||
|
||||
void perf_hpp__column_enable(unsigned col)
|
||||
{
|
||||
BUG_ON(col >= PERF_HPP__MAX_INDEX);
|
||||
perf_hpp__column_register(&perf_hpp__format[col]);
|
||||
}
|
||||
|
||||
void perf_hpp__column_disable(unsigned col)
|
||||
{
|
||||
BUG_ON(col >= PERF_HPP__MAX_INDEX);
|
||||
perf_hpp__column_unregister(&perf_hpp__format[col]);
|
||||
}
|
||||
|
||||
void perf_hpp__cancel_cumulate(void)
|
||||
{
|
||||
if (is_strict_order(field_order))
|
||||
return;
|
||||
|
||||
perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC);
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].name = "Overhead";
|
||||
}
|
||||
|
||||
void perf_hpp__setup_output_field(void)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
/* append sort keys to output field */
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
if (!list_empty(&fmt->list))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* sort entry fields are dynamically created,
|
||||
* so they can share a same sort key even though
|
||||
* the list is empty.
|
||||
*/
|
||||
if (perf_hpp__is_sort_entry(fmt)) {
|
||||
struct perf_hpp_fmt *pos;
|
||||
|
||||
perf_hpp__for_each_format(pos) {
|
||||
if (perf_hpp__same_sort_entry(pos, fmt))
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
perf_hpp__column_register(fmt);
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
void perf_hpp__append_sort_keys(void)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
/* append output fields to sort keys */
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (!list_empty(&fmt->sort_list))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* sort entry fields are dynamically created,
|
||||
* so they can share a same sort key even though
|
||||
* the list is empty.
|
||||
*/
|
||||
if (perf_hpp__is_sort_entry(fmt)) {
|
||||
struct perf_hpp_fmt *pos;
|
||||
|
||||
perf_hpp__for_each_sort_list(pos) {
|
||||
if (perf_hpp__same_sort_entry(pos, fmt))
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
perf_hpp__register_sort_field(fmt);
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
void perf_hpp__reset_output_field(void)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt, *tmp;
|
||||
|
||||
/* reset output fields */
|
||||
perf_hpp__for_each_format_safe(fmt, tmp) {
|
||||
list_del_init(&fmt->list);
|
||||
list_del_init(&fmt->sort_list);
|
||||
}
|
||||
|
||||
/* reset sort keys */
|
||||
perf_hpp__for_each_sort_list_safe(fmt, tmp) {
|
||||
list_del_init(&fmt->list);
|
||||
list_del_init(&fmt->sort_list);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* See hists__fprintf to match the column widths
|
||||
*/
|
||||
unsigned int hists__sort_list_width(struct hists *hists)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
int ret = 0;
|
||||
bool first = true;
|
||||
struct perf_hpp dummy_hpp;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ret += 2;
|
||||
|
||||
ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
|
||||
}
|
||||
|
||||
if (verbose && sort__has_sym) /* Addr + origin */
|
||||
ret += 3 + BITS_PER_LONG / 4;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (perf_hpp__is_sort_entry(fmt))
|
||||
return perf_hpp__reset_sort_width(fmt, hists);
|
||||
|
||||
for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
|
||||
if (fmt == &perf_hpp__format[idx])
|
||||
break;
|
||||
}
|
||||
|
||||
if (idx == PERF_HPP__MAX_INDEX)
|
||||
return;
|
||||
|
||||
switch (idx) {
|
||||
case PERF_HPP__OVERHEAD:
|
||||
case PERF_HPP__OVERHEAD_SYS:
|
||||
case PERF_HPP__OVERHEAD_US:
|
||||
case PERF_HPP__OVERHEAD_ACC:
|
||||
fmt->len = 8;
|
||||
break;
|
||||
|
||||
case PERF_HPP__OVERHEAD_GUEST_SYS:
|
||||
case PERF_HPP__OVERHEAD_GUEST_US:
|
||||
fmt->len = 9;
|
||||
break;
|
||||
|
||||
case PERF_HPP__SAMPLES:
|
||||
case PERF_HPP__PERIOD:
|
||||
fmt->len = 12;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void perf_hpp__set_user_width(const char *width_list_str)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
const char *ptr = width_list_str;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
char *p;
|
||||
|
||||
int len = strtol(ptr, &p, 10);
|
||||
fmt->user_len = len;
|
||||
|
||||
if (*p == ',')
|
||||
ptr = p + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
28
tools/perf/ui/keysyms.h
Normal file
28
tools/perf/ui/keysyms.h
Normal 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
29
tools/perf/ui/libslang.h
Normal 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_ */
|
||||
38
tools/perf/ui/progress.c
Normal file
38
tools/perf/ui/progress.c
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#include "../cache.h"
|
||||
#include "progress.h"
|
||||
|
||||
static void null_progress__update(struct ui_progress *p __maybe_unused)
|
||||
{
|
||||
}
|
||||
|
||||
static struct ui_progress_ops null_progress__ops =
|
||||
{
|
||||
.update = null_progress__update,
|
||||
};
|
||||
|
||||
struct ui_progress_ops *ui_progress__ops = &null_progress__ops;
|
||||
|
||||
void ui_progress__update(struct ui_progress *p, u64 adv)
|
||||
{
|
||||
p->curr += adv;
|
||||
|
||||
if (p->curr >= p->next) {
|
||||
p->next += p->step;
|
||||
ui_progress__ops->update(p);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_progress__init(struct ui_progress *p, u64 total, const char *title)
|
||||
{
|
||||
p->curr = 0;
|
||||
p->next = p->step = total / 16;
|
||||
p->total = total;
|
||||
p->title = title;
|
||||
|
||||
}
|
||||
|
||||
void ui_progress__finish(void)
|
||||
{
|
||||
if (ui_progress__ops->finish)
|
||||
ui_progress__ops->finish();
|
||||
}
|
||||
23
tools/perf/ui/progress.h
Normal file
23
tools/perf/ui/progress.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef _PERF_UI_PROGRESS_H_
|
||||
#define _PERF_UI_PROGRESS_H_ 1
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
void ui_progress__finish(void);
|
||||
|
||||
struct ui_progress {
|
||||
const char *title;
|
||||
u64 curr, next, step, total;
|
||||
};
|
||||
|
||||
void ui_progress__init(struct ui_progress *p, u64 total, const char *title);
|
||||
void ui_progress__update(struct ui_progress *p, u64 adv);
|
||||
|
||||
struct ui_progress_ops {
|
||||
void (*update)(struct ui_progress *p);
|
||||
void (*finish)(void);
|
||||
};
|
||||
|
||||
extern struct ui_progress_ops *ui_progress__ops;
|
||||
|
||||
#endif
|
||||
107
tools/perf/ui/setup.c
Normal file
107
tools/perf/ui/setup.c
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#include <pthread.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "../util/cache.h"
|
||||
#include "../util/debug.h"
|
||||
#include "../util/hist.h"
|
||||
|
||||
pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
void *perf_gtk_handle;
|
||||
|
||||
#ifdef HAVE_GTK2_SUPPORT
|
||||
static int setup_gtk_browser(void)
|
||||
{
|
||||
int (*perf_ui_init)(void);
|
||||
|
||||
if (perf_gtk_handle)
|
||||
return 0;
|
||||
|
||||
perf_gtk_handle = dlopen(PERF_GTK_DSO, RTLD_LAZY);
|
||||
if (perf_gtk_handle == NULL) {
|
||||
char buf[PATH_MAX];
|
||||
scnprintf(buf, sizeof(buf), "%s/%s", LIBDIR, PERF_GTK_DSO);
|
||||
perf_gtk_handle = dlopen(buf, RTLD_LAZY);
|
||||
}
|
||||
if (perf_gtk_handle == NULL)
|
||||
return -1;
|
||||
|
||||
perf_ui_init = dlsym(perf_gtk_handle, "perf_gtk__init");
|
||||
if (perf_ui_init == NULL)
|
||||
goto out_close;
|
||||
|
||||
if (perf_ui_init() == 0)
|
||||
return 0;
|
||||
|
||||
out_close:
|
||||
dlclose(perf_gtk_handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void exit_gtk_browser(bool wait_for_ok)
|
||||
{
|
||||
void (*perf_ui_exit)(bool);
|
||||
|
||||
if (perf_gtk_handle == NULL)
|
||||
return;
|
||||
|
||||
perf_ui_exit = dlsym(perf_gtk_handle, "perf_gtk__exit");
|
||||
if (perf_ui_exit == NULL)
|
||||
goto out_close;
|
||||
|
||||
perf_ui_exit(wait_for_ok);
|
||||
|
||||
out_close:
|
||||
dlclose(perf_gtk_handle);
|
||||
|
||||
perf_gtk_handle = NULL;
|
||||
}
|
||||
#else
|
||||
static inline int setup_gtk_browser(void) { return -1; }
|
||||
static inline void exit_gtk_browser(bool wait_for_ok __maybe_unused) {}
|
||||
#endif
|
||||
|
||||
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 (setup_gtk_browser() == 0)
|
||||
break;
|
||||
printf("GTK browser requested but could not find %s\n",
|
||||
PERF_GTK_DSO);
|
||||
sleep(1);
|
||||
/* fall through */
|
||||
case 1:
|
||||
use_browser = 1;
|
||||
if (ui__init() == 0)
|
||||
break;
|
||||
/* fall through */
|
||||
default:
|
||||
use_browser = 0;
|
||||
if (fallback_to_pager)
|
||||
setup_pager();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void exit_browser(bool wait_for_ok)
|
||||
{
|
||||
switch (use_browser) {
|
||||
case 2:
|
||||
exit_gtk_browser(wait_for_ok);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
ui__exit(wait_for_ok);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
516
tools/perf/ui/stdio/hist.c
Normal file
516
tools/perf/ui/stdio/hist.c
Normal file
|
|
@ -0,0 +1,516 @@
|
|||
#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++ && field_order == NULL &&
|
||||
sort_order && !prefixcmp(sort_order, "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 *node,
|
||||
u64 total_samples)
|
||||
{
|
||||
struct callchain_list *chain;
|
||||
size_t ret = 0;
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
ret += __callchain__fprintf_flat(fp, node->parent, total_samples);
|
||||
|
||||
|
||||
list_for_each_entry(chain, &node->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 *tree,
|
||||
u64 total_samples)
|
||||
{
|
||||
size_t ret = 0;
|
||||
u32 entries_printed = 0;
|
||||
struct callchain_node *chain;
|
||||
struct rb_node *rb_node = rb_first(tree);
|
||||
|
||||
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,
|
||||
symbol_conf.cumulate_callchain ?
|
||||
he->stat_acc->period : 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 (field_order == NULL && (sort_order == NULL ||
|
||||
!prefixcmp(sort_order, "comm"))) {
|
||||
struct perf_hpp_fmt *fmt;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (!perf_hpp__is_sort_entry(fmt))
|
||||
continue;
|
||||
|
||||
/* must be 'comm' sort entry */
|
||||
left_margin = fmt->width(fmt, NULL, hists_to_evsel(hists));
|
||||
left_margin -= thread__comm_len(he->thread);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
|
||||
}
|
||||
|
||||
static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
|
||||
{
|
||||
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 (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* 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 (perf_hpp__use_color() && fmt->color)
|
||||
ret = fmt->color(fmt, hpp, he);
|
||||
else
|
||||
ret = fmt->entry(fmt, hpp, he);
|
||||
|
||||
advance_hpp(hpp, ret);
|
||||
}
|
||||
|
||||
return hpp->buf - start;
|
||||
}
|
||||
|
||||
static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
||||
struct hists *hists,
|
||||
char *bf, size_t bfsz, FILE *fp)
|
||||
{
|
||||
int ret;
|
||||
struct perf_hpp hpp = {
|
||||
.buf = bf,
|
||||
.size = size,
|
||||
};
|
||||
|
||||
if (size == 0 || size > bfsz)
|
||||
size = hpp.size = bfsz;
|
||||
|
||||
hist_entry__snprintf(he, &hpp);
|
||||
|
||||
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, float min_pcnt, FILE *fp)
|
||||
{
|
||||
struct perf_hpp_fmt *fmt;
|
||||
struct rb_node *nd;
|
||||
size_t ret = 0;
|
||||
unsigned int width;
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
int nr_rows = 0;
|
||||
char bf[96];
|
||||
struct perf_hpp dummy_hpp = {
|
||||
.buf = bf,
|
||||
.size = sizeof(bf),
|
||||
};
|
||||
bool first = true;
|
||||
size_t linesz;
|
||||
char *line = NULL;
|
||||
|
||||
init_rem_hits();
|
||||
|
||||
perf_hpp__for_each_format(fmt)
|
||||
perf_hpp__reset_width(fmt, hists);
|
||||
|
||||
if (symbol_conf.col_width_list_str)
|
||||
perf_hpp__set_user_width(symbol_conf.col_width_list_str);
|
||||
|
||||
if (!show_header)
|
||||
goto print_entries;
|
||||
|
||||
fprintf(fp, "# ");
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
if (!first)
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
else
|
||||
first = false;
|
||||
|
||||
fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
|
||||
fprintf(fp, "%s", bf);
|
||||
}
|
||||
|
||||
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 (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
if (!first)
|
||||
fprintf(fp, "%s", sep ?: " ");
|
||||
else
|
||||
first = false;
|
||||
|
||||
width = fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
|
||||
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:
|
||||
linesz = hists__sort_list_width(hists) + 3 + 1;
|
||||
linesz += perf_hpp__color_overhead();
|
||||
line = malloc(linesz);
|
||||
if (line == NULL) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
|
||||
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
||||
float percent;
|
||||
|
||||
if (h->filtered)
|
||||
continue;
|
||||
|
||||
percent = hist_entry__get_percent_limit(h);
|
||||
if (percent < min_pcnt)
|
||||
continue;
|
||||
|
||||
ret += hist_entry__fprintf(h, max_cols, hists, line, linesz, fp);
|
||||
|
||||
if (max_rows && ++nr_rows >= max_rows)
|
||||
break;
|
||||
|
||||
if (h->ms.map == NULL && verbose > 1) {
|
||||
__map_groups__fprintf_maps(h->thread->mg,
|
||||
MAP__FUNCTION, fp);
|
||||
fprintf(fp, "%.10s end\n", graph_dotted_line);
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
out:
|
||||
zfree(&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;
|
||||
}
|
||||
58
tools/perf/ui/tui/helpline.c
Normal file
58
tools/perf/ui/tui/helpline.c
Normal 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(" ");
|
||||
}
|
||||
44
tools/perf/ui/tui/progress.c
Normal file
44
tools/perf/ui/tui/progress.c
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#include "../cache.h"
|
||||
#include "../progress.h"
|
||||
#include "../libslang.h"
|
||||
#include "../ui.h"
|
||||
#include "tui.h"
|
||||
#include "../browser.h"
|
||||
|
||||
static void tui_progress__update(struct ui_progress *p)
|
||||
{
|
||||
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 (p->total == 0)
|
||||
return;
|
||||
|
||||
ui__refresh_dimensions(false);
|
||||
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 *)p->title);
|
||||
SLsmg_fill_region(y, 1, 1, SLtt_Screen_Cols - 2, ' ');
|
||||
SLsmg_set_color(HE_COLORSET_SELECTED);
|
||||
bar = ((SLtt_Screen_Cols - 2) * p->curr) / p->total;
|
||||
SLsmg_fill_region(y, 1, 1, bar, ' ');
|
||||
SLsmg_refresh();
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
}
|
||||
|
||||
static struct ui_progress_ops tui_progress__ops =
|
||||
{
|
||||
.update = tui_progress__update,
|
||||
};
|
||||
|
||||
void tui_progress__init(void)
|
||||
{
|
||||
ui_progress__ops = &tui_progress__ops;
|
||||
}
|
||||
151
tools/perf/ui/tui/setup.c
Normal file
151
tools/perf/ui/tui/setup.c
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
#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"
|
||||
#include "tui.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();
|
||||
tui_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);
|
||||
}
|
||||
6
tools/perf/ui/tui/tui.h
Normal file
6
tools/perf/ui/tui/tui.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef _PERF_TUI_H_
|
||||
#define _PERF_TUI_H_ 1
|
||||
|
||||
void tui_progress__init(void);
|
||||
|
||||
#endif /* _PERF_TUI_H_ */
|
||||
256
tools/perf/ui/tui/util.c
Normal file
256
tools/perf/ui/tui/util.c
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
|
||||
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();
|
||||
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
|
||||
x += 2;
|
||||
len = 0;
|
||||
key = ui__getch(delay_secs);
|
||||
while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
|
||||
if (key == K_BKSPC) {
|
||||
if (len == 0) {
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
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();
|
||||
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
|
||||
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();
|
||||
|
||||
pthread_mutex_unlock(&ui__lock);
|
||||
|
||||
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;
|
||||
|
||||
key = ui__question_window(title, s, "Press any key...", 0);
|
||||
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,
|
||||
};
|
||||
29
tools/perf/ui/ui.h
Normal file
29
tools/perf/ui/ui.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#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 void *perf_gtk_handle;
|
||||
|
||||
extern int use_browser;
|
||||
|
||||
void setup_browser(bool fallback_to_pager);
|
||||
void exit_browser(bool wait_for_ok);
|
||||
|
||||
#ifdef HAVE_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
|
||||
|
||||
void ui__refresh_dimensions(bool force);
|
||||
|
||||
#endif /* _PERF_UI_H_ */
|
||||
84
tools/perf/ui/util.c
Normal file
84
tools/perf/ui/util.c
Normal 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
21
tools/perf/ui/util.h
Normal 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_ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue