18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include "../../builtin.h" 38c2ecf20Sopenharmony_ci#include "../../perf.h" 48c2ecf20Sopenharmony_ci#include "../../util/util.h" // perf_exe() 58c2ecf20Sopenharmony_ci#include "../util.h" 68c2ecf20Sopenharmony_ci#include "../../util/hist.h" 78c2ecf20Sopenharmony_ci#include "../../util/debug.h" 88c2ecf20Sopenharmony_ci#include "../../util/symbol.h" 98c2ecf20Sopenharmony_ci#include "../browser.h" 108c2ecf20Sopenharmony_ci#include "../libslang.h" 118c2ecf20Sopenharmony_ci#include "config.h" 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 148c2ecf20Sopenharmony_ci#include <stdlib.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define SCRIPT_NAMELEN 128 178c2ecf20Sopenharmony_ci#define SCRIPT_MAX_NO 64 188c2ecf20Sopenharmony_ci/* 198c2ecf20Sopenharmony_ci * Usually the full path for a script is: 208c2ecf20Sopenharmony_ci * /home/username/libexec/perf-core/scripts/python/xxx.py 218c2ecf20Sopenharmony_ci * /home/username/libexec/perf-core/scripts/perl/xxx.pl 228c2ecf20Sopenharmony_ci * So 256 should be long enough to contain the full path. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci#define SCRIPT_FULLPATH_LEN 256 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct script_config { 278c2ecf20Sopenharmony_ci const char **names; 288c2ecf20Sopenharmony_ci char **paths; 298c2ecf20Sopenharmony_ci int index; 308c2ecf20Sopenharmony_ci const char *perf; 318c2ecf20Sopenharmony_ci char extra_format[256]; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_civoid attr_to_script(char *extra_format, struct perf_event_attr *attr) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci extra_format[0] = 0; 378c2ecf20Sopenharmony_ci if (attr->read_format & PERF_FORMAT_GROUP) 388c2ecf20Sopenharmony_ci strcat(extra_format, " -F +metric"); 398c2ecf20Sopenharmony_ci if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK) 408c2ecf20Sopenharmony_ci strcat(extra_format, " -F +brstackinsn --xed"); 418c2ecf20Sopenharmony_ci if (attr->sample_type & PERF_SAMPLE_REGS_INTR) 428c2ecf20Sopenharmony_ci strcat(extra_format, " -F +iregs"); 438c2ecf20Sopenharmony_ci if (attr->sample_type & PERF_SAMPLE_REGS_USER) 448c2ecf20Sopenharmony_ci strcat(extra_format, " -F +uregs"); 458c2ecf20Sopenharmony_ci if (attr->sample_type & PERF_SAMPLE_PHYS_ADDR) 468c2ecf20Sopenharmony_ci strcat(extra_format, " -F +phys_addr"); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int add_script_option(const char *name, const char *opt, 508c2ecf20Sopenharmony_ci struct script_config *c) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci c->names[c->index] = name; 538c2ecf20Sopenharmony_ci if (asprintf(&c->paths[c->index], 548c2ecf20Sopenharmony_ci "%s script %s -F +metric %s %s", 558c2ecf20Sopenharmony_ci c->perf, opt, symbol_conf.inline_name ? " --inline" : "", 568c2ecf20Sopenharmony_ci c->extra_format) < 0) 578c2ecf20Sopenharmony_ci return -1; 588c2ecf20Sopenharmony_ci c->index++; 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int scripts_config(const char *var, const char *value, void *data) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct script_config *c = data; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (!strstarts(var, "scripts.")) 678c2ecf20Sopenharmony_ci return -1; 688c2ecf20Sopenharmony_ci if (c->index >= SCRIPT_MAX_NO) 698c2ecf20Sopenharmony_ci return -1; 708c2ecf20Sopenharmony_ci c->names[c->index] = strdup(var + 7); 718c2ecf20Sopenharmony_ci if (!c->names[c->index]) 728c2ecf20Sopenharmony_ci return -1; 738c2ecf20Sopenharmony_ci if (asprintf(&c->paths[c->index], "%s %s", value, 748c2ecf20Sopenharmony_ci c->extra_format) < 0) 758c2ecf20Sopenharmony_ci return -1; 768c2ecf20Sopenharmony_ci c->index++; 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 818c2ecf20Sopenharmony_ci * When success, will copy the full path of the selected script 828c2ecf20Sopenharmony_ci * into the buffer pointed by script_name, and return 0. 838c2ecf20Sopenharmony_ci * Return -1 on failure. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic int list_scripts(char *script_name, bool *custom, 868c2ecf20Sopenharmony_ci struct evsel *evsel) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO]; 898c2ecf20Sopenharmony_ci int i, num, choice; 908c2ecf20Sopenharmony_ci int ret = 0; 918c2ecf20Sopenharmony_ci int max_std, custom_perf; 928c2ecf20Sopenharmony_ci char pbuf[256]; 938c2ecf20Sopenharmony_ci const char *perf = perf_exe(pbuf, sizeof pbuf); 948c2ecf20Sopenharmony_ci struct script_config scriptc = { 958c2ecf20Sopenharmony_ci .names = (const char **)names, 968c2ecf20Sopenharmony_ci .paths = paths, 978c2ecf20Sopenharmony_ci .perf = perf 988c2ecf20Sopenharmony_ci }; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci script_name[0] = 0; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Preset the script name to SCRIPT_NAMELEN */ 1038c2ecf20Sopenharmony_ci buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN)); 1048c2ecf20Sopenharmony_ci if (!buf) 1058c2ecf20Sopenharmony_ci return -1; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (evsel) 1088c2ecf20Sopenharmony_ci attr_to_script(scriptc.extra_format, &evsel->core.attr); 1098c2ecf20Sopenharmony_ci add_script_option("Show individual samples", "", &scriptc); 1108c2ecf20Sopenharmony_ci add_script_option("Show individual samples with assembler", "-F +insn --xed", 1118c2ecf20Sopenharmony_ci &scriptc); 1128c2ecf20Sopenharmony_ci add_script_option("Show individual samples with source", "-F +srcline,+srccode", 1138c2ecf20Sopenharmony_ci &scriptc); 1148c2ecf20Sopenharmony_ci perf_config(scripts_config, &scriptc); 1158c2ecf20Sopenharmony_ci custom_perf = scriptc.index; 1168c2ecf20Sopenharmony_ci add_script_option("Show samples with custom perf script arguments", "", &scriptc); 1178c2ecf20Sopenharmony_ci i = scriptc.index; 1188c2ecf20Sopenharmony_ci max_std = i; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci for (; i < SCRIPT_MAX_NO; i++) { 1218c2ecf20Sopenharmony_ci names[i] = buf + (i - max_std) * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN); 1228c2ecf20Sopenharmony_ci paths[i] = names[i] + SCRIPT_NAMELEN; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci num = find_scripts(names + max_std, paths + max_std, SCRIPT_MAX_NO - max_std, 1268c2ecf20Sopenharmony_ci SCRIPT_FULLPATH_LEN); 1278c2ecf20Sopenharmony_ci if (num < 0) 1288c2ecf20Sopenharmony_ci num = 0; 1298c2ecf20Sopenharmony_ci choice = ui__popup_menu(num + max_std, (char * const *)names, NULL); 1308c2ecf20Sopenharmony_ci if (choice < 0) { 1318c2ecf20Sopenharmony_ci ret = -1; 1328c2ecf20Sopenharmony_ci goto out; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci if (choice == custom_perf) { 1358c2ecf20Sopenharmony_ci char script_args[50]; 1368c2ecf20Sopenharmony_ci int key = ui_browser__input_window("perf script command", 1378c2ecf20Sopenharmony_ci "Enter perf script command line (without perf script prefix)", 1388c2ecf20Sopenharmony_ci script_args, "", 0); 1398c2ecf20Sopenharmony_ci if (key != K_ENTER) { 1408c2ecf20Sopenharmony_ci ret = -1; 1418c2ecf20Sopenharmony_ci goto out; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci sprintf(script_name, "%s script %s", perf, script_args); 1448c2ecf20Sopenharmony_ci } else if (choice < num + max_std) { 1458c2ecf20Sopenharmony_ci strcpy(script_name, paths[choice]); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci *custom = choice >= max_std; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ciout: 1508c2ecf20Sopenharmony_ci free(buf); 1518c2ecf20Sopenharmony_ci for (i = 0; i < max_std; i++) 1528c2ecf20Sopenharmony_ci zfree(&paths[i]); 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_civoid run_script(char *cmd) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci pr_debug("Running %s\n", cmd); 1598c2ecf20Sopenharmony_ci SLang_reset_tty(); 1608c2ecf20Sopenharmony_ci if (system(cmd) < 0) 1618c2ecf20Sopenharmony_ci pr_warning("Cannot run %s\n", cmd); 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * SLang doesn't seem to reset the whole terminal, so be more 1648c2ecf20Sopenharmony_ci * forceful to get back to the original state. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci printf("\033[c\033[H\033[J"); 1678c2ecf20Sopenharmony_ci fflush(stdout); 1688c2ecf20Sopenharmony_ci SLang_init_tty(0, 0, 0); 1698c2ecf20Sopenharmony_ci SLsmg_refresh(); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciint script_browse(const char *script_opt, struct evsel *evsel) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci char *cmd, script_name[SCRIPT_FULLPATH_LEN]; 1758c2ecf20Sopenharmony_ci bool custom = false; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci memset(script_name, 0, SCRIPT_FULLPATH_LEN); 1788c2ecf20Sopenharmony_ci if (list_scripts(script_name, &custom, evsel)) 1798c2ecf20Sopenharmony_ci return -1; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (asprintf(&cmd, "%s%s %s %s%s 2>&1 | less", 1828c2ecf20Sopenharmony_ci custom ? "perf script -s " : "", 1838c2ecf20Sopenharmony_ci script_name, 1848c2ecf20Sopenharmony_ci script_opt ? script_opt : "", 1858c2ecf20Sopenharmony_ci input_name ? "-i " : "", 1868c2ecf20Sopenharmony_ci input_name ? input_name : "") < 0) 1878c2ecf20Sopenharmony_ci return -1; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci run_script(cmd); 1908c2ecf20Sopenharmony_ci free(cmd); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 194