scripts.c 9.0 KB

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