xref: /linux/tools/perf/ui/browsers/scripts.c (revision 78beef629fd95be4ed853b2d37b832f766bd96ca)
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