// SPDX-License-Identifier: GPL-2.0
#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 "../libslang.h"
#include "config.h"
#include <linux/err.h>
#include <linux/string.h>
#include <linux/zalloc.h>
#include <subcmd/exec-cmd.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 ;
}
/*
* Some scripts specify the required events in their " xxx - record " file ,
* this function will check if the events in perf . data match those
* mentioned in the " xxx - record " .
*
* Fixme : All existing " xxx - record " are all in good formats " - e event " ,
* which is covered well now . And new parsing code should be added to
* cover the future complex formats like event groups etc .
*/
static int check_ev_match(int dir_fd, const char *scriptname, struct perf_session *session)
{
char line[BUFSIZ];
FILE *fp;
{
char filename[NAME_MAX + 5 ];
int fd;
scnprintf(filename, sizeof (filename), "bin/%s-record" , scriptname);
fd = openat(dir_fd, filename, O_RDONLY);
if (fd == -1 )
return -1 ;
fp = fdopen(fd, "r" );
if (!fp)
return -1 ;
}
while (fgets(line, sizeof (line), fp)) {
char *p = skip_spaces(line);
if (*p == '#' )
continue ;
while (strlen(p)) {
int match, len;
struct evsel *pos;
char evname[128 ];
p = strstr(p, "-e" );
if (!p)
break ;
p += 2 ;
p = skip_spaces(p);
len = strcspn(p, " \t" );
if (!len)
break ;
snprintf(evname, len + 1 , "%s" , p);
match = 0 ;
evlist__for_each_entry(session->evlist, pos) {
if (evsel__name_is(pos, evname)) {
match = 1 ;
break ;
}
}
if (!match) {
fclose(fp);
return -1 ;
}
}
}
fclose(fp);
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, char **scripts_path_array, int num,
int pathlen)
{
struct dirent *script_dirent, *lang_dirent;
int scripts_dir_fd, lang_dir_fd;
DIR *scripts_dir, *lang_dir;
struct perf_session *session;
struct perf_data data = {
.path = input_name,
.mode = PERF_DATA_MODE_READ,
};
char *temp;
int i = 0 ;
const char *exec_path = get_argv_exec_path();
session = perf_session__new(&data, NULL);
if (IS_ERR(session))
return PTR_ERR(session);
{
char scripts_path[PATH_MAX];
snprintf(scripts_path, sizeof (scripts_path), "%s/scripts" , exec_path);
scripts_dir_fd = open(scripts_path, O_DIRECTORY);
pr_err("Failed to open directory '%s'" , scripts_path);
if (scripts_dir_fd == -1 ) {
perf_session__delete(session);
return -1 ;
}
}
scripts_dir = fdopendir(scripts_dir_fd);
if (!scripts_dir) {
close(scripts_dir_fd);
perf_session__delete(session);
return -1 ;
}
while ((lang_dirent = readdir(scripts_dir)) != NULL) {
if (lang_dirent->d_type != DT_DIR &&
(lang_dirent->d_type == DT_UNKNOWN &&
!is_directory_at(scripts_dir_fd, lang_dirent->d_name)))
continue ;
if (!strcmp(lang_dirent->d_name, "." ) || !strcmp(lang_dirent->d_name, ".." ))
continue ;
#ifndef HAVE_LIBPERL_SUPPORT
if (strstr(lang_dirent->d_name, "perl" ))
continue ;
#endif
#ifndef HAVE_LIBPYTHON_SUPPORT
if (strstr(lang_dirent->d_name, "python" ))
continue ;
#endif
lang_dir_fd = openat(scripts_dir_fd, lang_dirent->d_name, O_DIRECTORY);
if (lang_dir_fd == -1 )
continue ;
lang_dir = fdopendir(lang_dir_fd);
if (!lang_dir) {
close(lang_dir_fd);
continue ;
}
while ((script_dirent = readdir(lang_dir)) != NULL) {
if (script_dirent->d_type == DT_DIR)
continue ;
if (script_dirent->d_type == DT_UNKNOWN &&
is_directory_at(lang_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 ;
scnprintf(scripts_path_array[i], pathlen, "%s/scripts/%s/%s" ,
exec_path,
lang_dirent->d_name,
script_dirent->d_name);
temp = strchr(script_dirent->d_name, '.' );
snprintf(scripts_array[i],
(temp - script_dirent->d_name) + 1 ,
"%s" , script_dirent->d_name);
if (check_ev_match(lang_dir_fd, scripts_array[i], session))
continue ;
i++;
}
closedir(lang_dir);
}
closedir(scripts_dir);
perf_session__delete(session);
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 -s " : "" ,
script_name,
script_opt ? script_opt : "" ,
input_name ? "-i " : "" ,
input_name ? input_name : "" ) < 0 )
return -1 ;
run_script(cmd);
free(cmd);
return 0 ;
}
Messung V0.5 in Prozent C=98 H=87 G=92
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-10-12)
¤
*© Formatika GbR, Deutschland