Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368// 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;}