blob: d6d09b4a348c226fd152c7e02b51cd85a5a89ad3 [file]
// SPDX-License-Identifier: GPL-2.0
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "../../util/util.h" // perf_exe()
#include "../util.h"
#include "../../util/evlist.h"
#include "../../util/hist.h"
#include "../../util/debug.h"
#include "../../util/session.h"
#include "../../util/symbol.h"
#include "../browser.h"
#include "../keysyms.h"
#include "../libslang.h"
#include "config.h"
#include <linux/err.h>
#include <linux/string.h>
#include <linux/zalloc.h>
#include <subcmd/exec-cmd.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;
}
/*
* Return -1 if none is found, otherwise the actual scripts number.
*
* Currently the only user of this function is the script browser, which
* will list all statically runnable scripts, select one, execute it and
* show the output in a perf browser.
*/
static int find_scripts(char **scripts_array __maybe_unused,
char **scripts_path_array __maybe_unused, int num __maybe_unused,
int pathlen __maybe_unused)
{
int i = 0;
#ifdef HAVE_PYTHON_MODULE_SUPPORT
{
int namelen;
struct dirent *script_dirent;
char *temp;
const char *exec_path = get_argv_exec_path();
char py_scripts_path[PATH_MAX];
int py_scripts_dir_fd;
DIR *py_scripts_dir;
int len;
snprintf(py_scripts_path, sizeof(py_scripts_path), "%s/python", exec_path);
py_scripts_dir_fd = open(py_scripts_path, O_DIRECTORY);
if (py_scripts_dir_fd != -1) {
py_scripts_dir = fdopendir(py_scripts_dir_fd);
if (py_scripts_dir) {
while ((script_dirent = readdir(py_scripts_dir)) != NULL) {
if (script_dirent->d_type == DT_DIR)
continue;
if (script_dirent->d_type == DT_UNKNOWN &&
is_directory_at(py_scripts_dir_fd,
script_dirent->d_name))
continue;
/* Skip those real time scripts: xxxtop.p[yl] */
if (strstr(script_dirent->d_name, "top."))
continue;
if (i >= num)
break;
len = strlen(script_dirent->d_name);
if (len <= 3 ||
strcmp(script_dirent->d_name + len - 3, ".py"))
continue;
scnprintf(scripts_path_array[i], pathlen, "%s/python/%s",
exec_path,
script_dirent->d_name);
temp = strrchr(script_dirent->d_name, '.');
namelen = temp ? (int)(temp - script_dirent->d_name)
: (int)strlen(script_dirent->d_name);
if (namelen >= SCRIPT_NAMELEN)
namelen = SCRIPT_NAMELEN - 1;
snprintf(scripts_array[i], namelen + 1, "%s",
script_dirent->d_name);
i++;
}
closedir(py_scripts_dir);
} else {
close(py_scripts_dir_fd);
}
}
}
#endif
return i;
}
/*
* 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 +disasm",
&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);
SLtty_set_suspend_state(true);
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 " : "",
script_name,
script_opt ? script_opt : "",
input_name ? "-i " : "",
input_name ? input_name : "") < 0)
return -1;
run_script(cmd);
free(cmd);
return 0;
}