1 // SPDX-License-Identifier: GPL-2.0 2 #include "../../util/util.h" // perf_exe() 3 #include "../util.h" 4 #include "../../util/evlist.h" 5 #include "../../util/hist.h" 6 #include "../../util/debug.h" 7 #include "../../util/session.h" 8 #include "../../util/symbol.h" 9 #include "../browser.h" 10 #include "../libslang.h" 11 #include "config.h" 12 #include <linux/err.h> 13 #include <linux/string.h> 14 #include <linux/zalloc.h> 15 #include <subcmd/exec-cmd.h> 16 #include <stdlib.h> 17 18 #define SCRIPT_NAMELEN 128 19 #define SCRIPT_MAX_NO 64 20 /* 21 * Usually the full path for a script is: 22 * /home/username/libexec/perf-core/scripts/python/xxx.py 23 * /home/username/libexec/perf-core/scripts/perl/xxx.pl 24 * So 256 should be long enough to contain the full path. 25 */ 26 #define SCRIPT_FULLPATH_LEN 256 27 28 struct script_config { 29 const char **names; 30 char **paths; 31 int index; 32 const char *perf; 33 char extra_format[256]; 34 }; 35 36 void attr_to_script(char *extra_format, struct perf_event_attr *attr) 37 { 38 extra_format[0] = 0; 39 if (attr->read_format & PERF_FORMAT_GROUP) 40 strcat(extra_format, " -F +metric"); 41 if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK) 42 strcat(extra_format, " -F +brstackinsn --xed"); 43 if (attr->sample_type & PERF_SAMPLE_REGS_INTR) 44 strcat(extra_format, " -F +iregs"); 45 if (attr->sample_type & PERF_SAMPLE_REGS_USER) 46 strcat(extra_format, " -F +uregs"); 47 if (attr->sample_type & PERF_SAMPLE_PHYS_ADDR) 48 strcat(extra_format, " -F +phys_addr"); 49 } 50 51 static int add_script_option(const char *name, const char *opt, 52 struct script_config *c) 53 { 54 c->names[c->index] = name; 55 if (asprintf(&c->paths[c->index], 56 "%s script %s -F +metric %s %s", 57 c->perf, opt, symbol_conf.inline_name ? " --inline" : "", 58 c->extra_format) < 0) 59 return -1; 60 c->index++; 61 return 0; 62 } 63 64 static int scripts_config(const char *var, const char *value, void *data) 65 { 66 struct script_config *c = data; 67 68 if (!strstarts(var, "scripts.")) 69 return -1; 70 if (c->index >= SCRIPT_MAX_NO) 71 return -1; 72 c->names[c->index] = strdup(var + 7); 73 if (!c->names[c->index]) 74 return -1; 75 if (asprintf(&c->paths[c->index], "%s %s", value, 76 c->extra_format) < 0) 77 return -1; 78 c->index++; 79 return 0; 80 } 81 82 /* 83 * Some scripts specify the required events in their "xxx-record" file, 84 * this function will check if the events in perf.data match those 85 * mentioned in the "xxx-record". 86 * 87 * Fixme: All existing "xxx-record" are all in good formats "-e event ", 88 * which is covered well now. And new parsing code should be added to 89 * cover the future complex formats like event groups etc. 90 */ 91 static int check_ev_match(int dir_fd, const char *scriptname, struct perf_session *session) 92 { 93 char line[BUFSIZ]; 94 FILE *fp; 95 96 { 97 char filename[FILENAME_MAX + 5]; 98 int fd; 99 100 scnprintf(filename, sizeof(filename), "bin/%s-record", scriptname); 101 fd = openat(dir_fd, filename, O_RDONLY); 102 if (fd == -1) 103 return -1; 104 fp = fdopen(fd, "r"); 105 if (!fp) 106 return -1; 107 } 108 109 while (fgets(line, sizeof(line), fp)) { 110 char *p = skip_spaces(line); 111 112 if (*p == '#') 113 continue; 114 115 while (strlen(p)) { 116 int match, len; 117 struct evsel *pos; 118 char evname[128]; 119 120 p = strstr(p, "-e"); 121 if (!p) 122 break; 123 124 p += 2; 125 p = skip_spaces(p); 126 len = strcspn(p, " \t"); 127 if (!len) 128 break; 129 130 snprintf(evname, len + 1, "%s", p); 131 132 match = 0; 133 evlist__for_each_entry(session->evlist, pos) { 134 if (evsel__name_is(pos, evname)) { 135 match = 1; 136 break; 137 } 138 } 139 140 if (!match) { 141 fclose(fp); 142 return -1; 143 } 144 } 145 } 146 147 fclose(fp); 148 return 0; 149 } 150 151 /* 152 * Return -1 if none is found, otherwise the actual scripts number. 153 * 154 * Currently the only user of this function is the script browser, which 155 * will list all statically runnable scripts, select one, execute it and 156 * show the output in a perf browser. 157 */ 158 static int find_scripts(char **scripts_array, char **scripts_path_array, int num, 159 int pathlen) 160 { 161 struct dirent *script_dirent, *lang_dirent; 162 int scripts_dir_fd, lang_dir_fd; 163 DIR *scripts_dir, *lang_dir; 164 struct perf_session *session; 165 struct perf_data data = { 166 .path = input_name, 167 .mode = PERF_DATA_MODE_READ, 168 }; 169 char *temp; 170 int i = 0; 171 const char *exec_path = get_argv_exec_path(); 172 173 session = perf_session__new(&data, NULL); 174 if (IS_ERR(session)) 175 return PTR_ERR(session); 176 177 { 178 char scripts_path[PATH_MAX]; 179 180 snprintf(scripts_path, sizeof(scripts_path), "%s/scripts", exec_path); 181 scripts_dir_fd = open(scripts_path, O_DIRECTORY); 182 pr_err("Failed to open directory '%s'", scripts_path); 183 if (scripts_dir_fd == -1) { 184 perf_session__delete(session); 185 return -1; 186 } 187 } 188 scripts_dir = fdopendir(scripts_dir_fd); 189 if (!scripts_dir) { 190 close(scripts_dir_fd); 191 perf_session__delete(session); 192 return -1; 193 } 194 195 while ((lang_dirent = readdir(scripts_dir)) != NULL) { 196 if (lang_dirent->d_type != DT_DIR && 197 (lang_dirent->d_type == DT_UNKNOWN && 198 !is_directory_at(scripts_dir_fd, lang_dirent->d_name))) 199 continue; 200 if (!strcmp(lang_dirent->d_name, ".") || !strcmp(lang_dirent->d_name, "..")) 201 continue; 202 203 #ifndef HAVE_LIBPERL_SUPPORT 204 if (strstr(lang_dirent->d_name, "perl")) 205 continue; 206 #endif 207 #ifndef HAVE_LIBPYTHON_SUPPORT 208 if (strstr(lang_dirent->d_name, "python")) 209 continue; 210 #endif 211 212 lang_dir_fd = openat(scripts_dir_fd, lang_dirent->d_name, O_DIRECTORY); 213 if (lang_dir_fd == -1) 214 continue; 215 lang_dir = fdopendir(lang_dir_fd); 216 if (!lang_dir) { 217 close(lang_dir_fd); 218 continue; 219 } 220 while ((script_dirent = readdir(lang_dir)) != NULL) { 221 if (script_dirent->d_type == DT_DIR) 222 continue; 223 if (script_dirent->d_type == DT_UNKNOWN && 224 is_directory_at(lang_dir_fd, script_dirent->d_name)) 225 continue; 226 /* Skip those real time scripts: xxxtop.p[yl] */ 227 if (strstr(script_dirent->d_name, "top.")) 228 continue; 229 if (i >= num) 230 break; 231 scnprintf(scripts_path_array[i], pathlen, "%s/scripts/%s/%s", 232 exec_path, 233 lang_dirent->d_name, 234 script_dirent->d_name); 235 temp = strchr(script_dirent->d_name, '.'); 236 snprintf(scripts_array[i], 237 (temp - script_dirent->d_name) + 1, 238 "%s", script_dirent->d_name); 239 240 if (check_ev_match(lang_dir_fd, scripts_array[i], session)) 241 continue; 242 243 i++; 244 } 245 closedir(lang_dir); 246 } 247 248 closedir(scripts_dir); 249 perf_session__delete(session); 250 return i; 251 } 252 253 /* 254 * When success, will copy the full path of the selected script 255 * into the buffer pointed by script_name, and return 0. 256 * Return -1 on failure. 257 */ 258 static int list_scripts(char *script_name, bool *custom, 259 struct evsel *evsel) 260 { 261 char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO]; 262 int i, num, choice; 263 int ret = 0; 264 int max_std, custom_perf; 265 char pbuf[256]; 266 const char *perf = perf_exe(pbuf, sizeof pbuf); 267 struct script_config scriptc = { 268 .names = (const char **)names, 269 .paths = paths, 270 .perf = perf 271 }; 272 273 script_name[0] = 0; 274 275 /* Preset the script name to SCRIPT_NAMELEN */ 276 buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN)); 277 if (!buf) 278 return -1; 279 280 if (evsel) 281 attr_to_script(scriptc.extra_format, &evsel->core.attr); 282 add_script_option("Show individual samples", "", &scriptc); 283 add_script_option("Show individual samples with assembler", "-F +disasm", 284 &scriptc); 285 add_script_option("Show individual samples with source", "-F +srcline,+srccode", 286 &scriptc); 287 perf_config(scripts_config, &scriptc); 288 custom_perf = scriptc.index; 289 add_script_option("Show samples with custom perf script arguments", "", &scriptc); 290 i = scriptc.index; 291 max_std = i; 292 293 for (; i < SCRIPT_MAX_NO; i++) { 294 names[i] = buf + (i - max_std) * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN); 295 paths[i] = names[i] + SCRIPT_NAMELEN; 296 } 297 298 num = find_scripts(names + max_std, paths + max_std, SCRIPT_MAX_NO - max_std, 299 SCRIPT_FULLPATH_LEN); 300 if (num < 0) 301 num = 0; 302 choice = ui__popup_menu(num + max_std, (char * const *)names, NULL); 303 if (choice < 0) { 304 ret = -1; 305 goto out; 306 } 307 if (choice == custom_perf) { 308 char script_args[50]; 309 int key = ui_browser__input_window("perf script command", 310 "Enter perf script command line (without perf script prefix)", 311 script_args, "", 0); 312 if (key != K_ENTER) { 313 ret = -1; 314 goto out; 315 } 316 sprintf(script_name, "%s script %s", perf, script_args); 317 } else if (choice < num + max_std) { 318 strcpy(script_name, paths[choice]); 319 } 320 *custom = choice >= max_std; 321 322 out: 323 free(buf); 324 for (i = 0; i < max_std; i++) 325 zfree(&paths[i]); 326 return ret; 327 } 328 329 void run_script(char *cmd) 330 { 331 pr_debug("Running %s\n", cmd); 332 SLang_reset_tty(); 333 if (system(cmd) < 0) 334 pr_warning("Cannot run %s\n", cmd); 335 /* 336 * SLang doesn't seem to reset the whole terminal, so be more 337 * forceful to get back to the original state. 338 */ 339 printf("\033[c\033[H\033[J"); 340 fflush(stdout); 341 SLang_init_tty(0, 0, 0); 342 SLtty_set_suspend_state(true); 343 SLsmg_refresh(); 344 } 345 346 int script_browse(const char *script_opt, struct evsel *evsel) 347 { 348 char *cmd, script_name[SCRIPT_FULLPATH_LEN]; 349 bool custom = false; 350 351 memset(script_name, 0, SCRIPT_FULLPATH_LEN); 352 if (list_scripts(script_name, &custom, evsel)) 353 return -1; 354 355 if (asprintf(&cmd, "%s%s %s %s%s 2>&1 | less", 356 custom ? "perf script -s " : "", 357 script_name, 358 script_opt ? script_opt : "", 359 input_name ? "-i " : "", 360 input_name ? input_name : "") < 0) 361 return -1; 362 363 run_script(cmd); 364 free(cmd); 365 366 return 0; 367 } 368