1 // SPDX-License-Identifier: GPL-2.0 2 #include "../../builtin.h" 3 #include "../../perf.h" 4 #include "../../util/util.h" 5 #include "../../util/hist.h" 6 #include "../../util/debug.h" 7 #include "../../util/symbol.h" 8 #include "../browser.h" 9 #include "../libslang.h" 10 #include "config.h" 11 #include <linux/string.h> 12 #include <linux/zalloc.h> 13 #include <stdlib.h> 14 15 #define SCRIPT_NAMELEN 128 16 #define SCRIPT_MAX_NO 64 17 /* 18 * Usually the full path for a script is: 19 * /home/username/libexec/perf-core/scripts/python/xxx.py 20 * /home/username/libexec/perf-core/scripts/perl/xxx.pl 21 * So 256 should be long enough to contain the full path. 22 */ 23 #define SCRIPT_FULLPATH_LEN 256 24 25 struct script_config { 26 const char **names; 27 char **paths; 28 int index; 29 const char *perf; 30 char extra_format[256]; 31 }; 32 33 void attr_to_script(char *extra_format, struct perf_event_attr *attr) 34 { 35 extra_format[0] = 0; 36 if (attr->read_format & PERF_FORMAT_GROUP) 37 strcat(extra_format, " -F +metric"); 38 if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK) 39 strcat(extra_format, " -F +brstackinsn --xed"); 40 if (attr->sample_type & PERF_SAMPLE_REGS_INTR) 41 strcat(extra_format, " -F +iregs"); 42 if (attr->sample_type & PERF_SAMPLE_REGS_USER) 43 strcat(extra_format, " -F +uregs"); 44 if (attr->sample_type & PERF_SAMPLE_PHYS_ADDR) 45 strcat(extra_format, " -F +phys_addr"); 46 } 47 48 static int add_script_option(const char *name, const char *opt, 49 struct script_config *c) 50 { 51 c->names[c->index] = name; 52 if (asprintf(&c->paths[c->index], 53 "%s script %s -F +metric %s %s", 54 c->perf, opt, symbol_conf.inline_name ? " --inline" : "", 55 c->extra_format) < 0) 56 return -1; 57 c->index++; 58 return 0; 59 } 60 61 static int scripts_config(const char *var, const char *value, void *data) 62 { 63 struct script_config *c = data; 64 65 if (!strstarts(var, "scripts.")) 66 return -1; 67 if (c->index >= SCRIPT_MAX_NO) 68 return -1; 69 c->names[c->index] = strdup(var + 7); 70 if (!c->names[c->index]) 71 return -1; 72 if (asprintf(&c->paths[c->index], "%s %s", value, 73 c->extra_format) < 0) 74 return -1; 75 c->index++; 76 return 0; 77 } 78 79 /* 80 * When success, will copy the full path of the selected script 81 * into the buffer pointed by script_name, and return 0. 82 * Return -1 on failure. 83 */ 84 static int list_scripts(char *script_name, bool *custom, 85 struct evsel *evsel) 86 { 87 char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO]; 88 int i, num, choice; 89 int ret = 0; 90 int max_std, custom_perf; 91 char pbuf[256]; 92 const char *perf = perf_exe(pbuf, sizeof pbuf); 93 struct script_config scriptc = { 94 .names = (const char **)names, 95 .paths = paths, 96 .perf = perf 97 }; 98 99 script_name[0] = 0; 100 101 /* Preset the script name to SCRIPT_NAMELEN */ 102 buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN)); 103 if (!buf) 104 return -1; 105 106 if (evsel) 107 attr_to_script(scriptc.extra_format, &evsel->core.attr); 108 add_script_option("Show individual samples", "", &scriptc); 109 add_script_option("Show individual samples with assembler", "-F +insn --xed", 110 &scriptc); 111 add_script_option("Show individual samples with source", "-F +srcline,+srccode", 112 &scriptc); 113 perf_config(scripts_config, &scriptc); 114 custom_perf = scriptc.index; 115 add_script_option("Show samples with custom perf script arguments", "", &scriptc); 116 i = scriptc.index; 117 max_std = i; 118 119 for (; i < SCRIPT_MAX_NO; i++) { 120 names[i] = buf + (i - max_std) * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN); 121 paths[i] = names[i] + SCRIPT_NAMELEN; 122 } 123 124 num = find_scripts(names + max_std, paths + max_std, SCRIPT_MAX_NO - max_std, 125 SCRIPT_FULLPATH_LEN); 126 if (num < 0) 127 num = 0; 128 choice = ui__popup_menu(num + max_std, (char * const *)names); 129 if (choice < 0) { 130 ret = -1; 131 goto out; 132 } 133 if (choice == custom_perf) { 134 char script_args[50]; 135 int key = ui_browser__input_window("perf script command", 136 "Enter perf script command line (without perf script prefix)", 137 script_args, "", 0); 138 if (key != K_ENTER) { 139 ret = -1; 140 goto out; 141 } 142 sprintf(script_name, "%s script %s", perf, script_args); 143 } else if (choice < num + max_std) { 144 strcpy(script_name, paths[choice]); 145 } 146 *custom = choice >= max_std; 147 148 out: 149 free(buf); 150 for (i = 0; i < max_std; i++) 151 zfree(&paths[i]); 152 return ret; 153 } 154 155 void run_script(char *cmd) 156 { 157 pr_debug("Running %s\n", cmd); 158 SLang_reset_tty(); 159 if (system(cmd) < 0) 160 pr_warning("Cannot run %s\n", cmd); 161 /* 162 * SLang doesn't seem to reset the whole terminal, so be more 163 * forceful to get back to the original state. 164 */ 165 printf("\033[c\033[H\033[J"); 166 fflush(stdout); 167 SLang_init_tty(0, 0, 0); 168 SLsmg_refresh(); 169 } 170 171 int script_browse(const char *script_opt, struct evsel *evsel) 172 { 173 char *cmd, script_name[SCRIPT_FULLPATH_LEN]; 174 bool custom = false; 175 176 memset(script_name, 0, SCRIPT_FULLPATH_LEN); 177 if (list_scripts(script_name, &custom, evsel)) 178 return -1; 179 180 if (asprintf(&cmd, "%s%s %s %s%s 2>&1 | less", 181 custom ? "perf script -s " : "", 182 script_name, 183 script_opt ? script_opt : "", 184 input_name ? "-i " : "", 185 input_name ? input_name : "") < 0) 186 return -1; 187 188 run_script(cmd); 189 free(cmd); 190 191 return 0; 192 } 193