188 lines
4.2 KiB
C
188 lines
4.2 KiB
C
|
#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;
|
||
|
}
|