194 lines
4.9 KiB
C
194 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include "../../builtin.h"
|
|
#include "../../perf.h"
|
|
#include "../../util/util.h" // perf_exe()
|
|
#include "../util.h"
|
|
#include "../../util/hist.h"
|
|
#include "../../util/debug.h"
|
|
#include "../../util/symbol.h"
|
|
#include "../browser.h"
|
|
#include "../libslang.h"
|
|
#include "config.h"
|
|
#include <linux/string.h>
|
|
#include <linux/zalloc.h>
|
|
#include <stdlib.h>
|
|
|
|
#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
|
|
|
|
struct script_config {
|
|
const char **names;
|
|
char **paths;
|
|
int index;
|
|
const char *perf;
|
|
char extra_format[256];
|
|
};
|
|
|
|
void attr_to_script(char *extra_format, struct perf_event_attr *attr)
|
|
{
|
|
extra_format[0] = 0;
|
|
if (attr->read_format & PERF_FORMAT_GROUP)
|
|
strcat(extra_format, " -F +metric");
|
|
if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK)
|
|
strcat(extra_format, " -F +brstackinsn --xed");
|
|
if (attr->sample_type & PERF_SAMPLE_REGS_INTR)
|
|
strcat(extra_format, " -F +iregs");
|
|
if (attr->sample_type & PERF_SAMPLE_REGS_USER)
|
|
strcat(extra_format, " -F +uregs");
|
|
if (attr->sample_type & PERF_SAMPLE_PHYS_ADDR)
|
|
strcat(extra_format, " -F +phys_addr");
|
|
}
|
|
|
|
static int add_script_option(const char *name, const char *opt,
|
|
struct script_config *c)
|
|
{
|
|
c->names[c->index] = name;
|
|
if (asprintf(&c->paths[c->index],
|
|
"%s script %s -F +metric %s %s",
|
|
c->perf, opt, symbol_conf.inline_name ? " --inline" : "",
|
|
c->extra_format) < 0)
|
|
return -1;
|
|
c->index++;
|
|
return 0;
|
|
}
|
|
|
|
static int scripts_config(const char *var, const char *value, void *data)
|
|
{
|
|
struct script_config *c = data;
|
|
|
|
if (!strstarts(var, "scripts."))
|
|
return -1;
|
|
if (c->index >= SCRIPT_MAX_NO)
|
|
return -1;
|
|
c->names[c->index] = strdup(var + 7);
|
|
if (!c->names[c->index])
|
|
return -1;
|
|
if (asprintf(&c->paths[c->index], "%s %s", value,
|
|
c->extra_format) < 0)
|
|
return -1;
|
|
c->index++;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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, bool *custom,
|
|
struct evsel *evsel)
|
|
{
|
|
char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO];
|
|
int i, num, choice;
|
|
int ret = 0;
|
|
int max_std, custom_perf;
|
|
char pbuf[256];
|
|
const char *perf = perf_exe(pbuf, sizeof pbuf);
|
|
struct script_config scriptc = {
|
|
.names = (const char **)names,
|
|
.paths = paths,
|
|
.perf = perf
|
|
};
|
|
|
|
script_name[0] = 0;
|
|
|
|
/* Preset the script name to SCRIPT_NAMELEN */
|
|
buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
|
|
if (!buf)
|
|
return -1;
|
|
|
|
if (evsel)
|
|
attr_to_script(scriptc.extra_format, &evsel->core.attr);
|
|
add_script_option("Show individual samples", "", &scriptc);
|
|
add_script_option("Show individual samples with assembler", "-F +insn --xed",
|
|
&scriptc);
|
|
add_script_option("Show individual samples with source", "-F +srcline,+srccode",
|
|
&scriptc);
|
|
perf_config(scripts_config, &scriptc);
|
|
custom_perf = scriptc.index;
|
|
add_script_option("Show samples with custom perf script arguments", "", &scriptc);
|
|
i = scriptc.index;
|
|
max_std = i;
|
|
|
|
for (; i < SCRIPT_MAX_NO; i++) {
|
|
names[i] = buf + (i - max_std) * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
|
|
paths[i] = names[i] + SCRIPT_NAMELEN;
|
|
}
|
|
|
|
num = find_scripts(names + max_std, paths + max_std, SCRIPT_MAX_NO - max_std,
|
|
SCRIPT_FULLPATH_LEN);
|
|
if (num < 0)
|
|
num = 0;
|
|
choice = ui__popup_menu(num + max_std, (char * const *)names, NULL);
|
|
if (choice < 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (choice == custom_perf) {
|
|
char script_args[50];
|
|
int key = ui_browser__input_window("perf script command",
|
|
"Enter perf script command line (without perf script prefix)",
|
|
script_args, "", 0);
|
|
if (key != K_ENTER) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
sprintf(script_name, "%s script %s", perf, script_args);
|
|
} else if (choice < num + max_std) {
|
|
strcpy(script_name, paths[choice]);
|
|
}
|
|
*custom = choice >= max_std;
|
|
|
|
out:
|
|
free(buf);
|
|
for (i = 0; i < max_std; i++)
|
|
zfree(&paths[i]);
|
|
return ret;
|
|
}
|
|
|
|
void run_script(char *cmd)
|
|
{
|
|
pr_debug("Running %s\n", cmd);
|
|
SLang_reset_tty();
|
|
if (system(cmd) < 0)
|
|
pr_warning("Cannot run %s\n", cmd);
|
|
/*
|
|
* SLang doesn't seem to reset the whole terminal, so be more
|
|
* forceful to get back to the original state.
|
|
*/
|
|
printf("\033[c\033[H\033[J");
|
|
fflush(stdout);
|
|
SLang_init_tty(0, 0, 0);
|
|
SLsmg_refresh();
|
|
}
|
|
|
|
int script_browse(const char *script_opt, struct evsel *evsel)
|
|
{
|
|
char *cmd, script_name[SCRIPT_FULLPATH_LEN];
|
|
bool custom = false;
|
|
|
|
memset(script_name, 0, SCRIPT_FULLPATH_LEN);
|
|
if (list_scripts(script_name, &custom, evsel))
|
|
return -1;
|
|
|
|
if (asprintf(&cmd, "%s%s %s %s%s 2>&1 | less",
|
|
custom ? "perf script -s " : "",
|
|
script_name,
|
|
script_opt ? script_opt : "",
|
|
input_name ? "-i " : "",
|
|
input_name ? input_name : "") < 0)
|
|
return -1;
|
|
|
|
run_script(cmd);
|
|
free(cmd);
|
|
|
|
return 0;
|
|
}
|