xref: /openbmc/linux/tools/perf/ui/browsers/scripts.c (revision e3d786a3)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <elf.h>
3 #include <inttypes.h>
4 #include <sys/ttydefaults.h>
5 #include <string.h>
6 #include "../../util/sort.h"
7 #include "../../util/util.h"
8 #include "../../util/hist.h"
9 #include "../../util/debug.h"
10 #include "../../util/symbol.h"
11 #include "../browser.h"
12 #include "../helpline.h"
13 #include "../libslang.h"
14 
15 /* 2048 lines should be enough for a script output */
16 #define MAX_LINES		2048
17 
18 /* 160 bytes for one output line */
19 #define AVERAGE_LINE_LEN	160
20 
21 struct script_line {
22 	struct list_head node;
23 	char line[AVERAGE_LINE_LEN];
24 };
25 
26 struct perf_script_browser {
27 	struct ui_browser b;
28 	struct list_head entries;
29 	const char *script_name;
30 	int nr_lines;
31 };
32 
33 #define SCRIPT_NAMELEN	128
34 #define SCRIPT_MAX_NO	64
35 /*
36  * Usually the full path for a script is:
37  *	/home/username/libexec/perf-core/scripts/python/xxx.py
38  *	/home/username/libexec/perf-core/scripts/perl/xxx.pl
39  * So 256 should be long enough to contain the full path.
40  */
41 #define SCRIPT_FULLPATH_LEN	256
42 
43 /*
44  * When success, will copy the full path of the selected script
45  * into  the buffer pointed by script_name, and return 0.
46  * Return -1 on failure.
47  */
48 static int list_scripts(char *script_name)
49 {
50 	char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO];
51 	int i, num, choice, ret = -1;
52 
53 	/* Preset the script name to SCRIPT_NAMELEN */
54 	buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
55 	if (!buf)
56 		return ret;
57 
58 	for (i = 0; i < SCRIPT_MAX_NO; i++) {
59 		names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
60 		paths[i] = names[i] + SCRIPT_NAMELEN;
61 	}
62 
63 	num = find_scripts(names, paths);
64 	if (num > 0) {
65 		choice = ui__popup_menu(num, names);
66 		if (choice < num && choice >= 0) {
67 			strcpy(script_name, paths[choice]);
68 			ret = 0;
69 		}
70 	}
71 
72 	free(buf);
73 	return ret;
74 }
75 
76 static void script_browser__write(struct ui_browser *browser,
77 				   void *entry, int row)
78 {
79 	struct script_line *sline = list_entry(entry, struct script_line, node);
80 	bool current_entry = ui_browser__is_current_entry(browser, row);
81 
82 	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
83 						       HE_COLORSET_NORMAL);
84 
85 	ui_browser__write_nstring(browser, sline->line, browser->width);
86 }
87 
88 static int script_browser__run(struct perf_script_browser *browser)
89 {
90 	int key;
91 
92 	if (ui_browser__show(&browser->b, browser->script_name,
93 			     "Press ESC to exit") < 0)
94 		return -1;
95 
96 	while (1) {
97 		key = ui_browser__run(&browser->b, 0);
98 
99 		/* We can add some special key handling here if needed */
100 		break;
101 	}
102 
103 	ui_browser__hide(&browser->b);
104 	return key;
105 }
106 
107 
108 int script_browse(const char *script_opt)
109 {
110 	char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN];
111 	char *line = NULL;
112 	size_t len = 0;
113 	ssize_t retlen;
114 	int ret = -1, nr_entries = 0;
115 	FILE *fp;
116 	void *buf;
117 	struct script_line *sline;
118 
119 	struct perf_script_browser script = {
120 		.b = {
121 			.refresh    = ui_browser__list_head_refresh,
122 			.seek	    = ui_browser__list_head_seek,
123 			.write	    = script_browser__write,
124 		},
125 		.script_name = script_name,
126 	};
127 
128 	INIT_LIST_HEAD(&script.entries);
129 
130 	/* Save each line of the output in one struct script_line object. */
131 	buf = zalloc((sizeof(*sline)) * MAX_LINES);
132 	if (!buf)
133 		return -1;
134 	sline = buf;
135 
136 	memset(script_name, 0, SCRIPT_FULLPATH_LEN);
137 	if (list_scripts(script_name))
138 		goto exit;
139 
140 	sprintf(cmd, "perf script -s %s ", script_name);
141 
142 	if (script_opt)
143 		strcat(cmd, script_opt);
144 
145 	if (input_name) {
146 		strcat(cmd, " -i ");
147 		strcat(cmd, input_name);
148 	}
149 
150 	strcat(cmd, " 2>&1");
151 
152 	fp = popen(cmd, "r");
153 	if (!fp)
154 		goto exit;
155 
156 	while ((retlen = getline(&line, &len, fp)) != -1) {
157 		strncpy(sline->line, line, AVERAGE_LINE_LEN);
158 
159 		/* If one output line is very large, just cut it short */
160 		if (retlen >= AVERAGE_LINE_LEN) {
161 			sline->line[AVERAGE_LINE_LEN - 1] = '\0';
162 			sline->line[AVERAGE_LINE_LEN - 2] = '\n';
163 		}
164 		list_add_tail(&sline->node, &script.entries);
165 
166 		if (script.b.width < retlen)
167 			script.b.width = retlen;
168 
169 		if (nr_entries++ >= MAX_LINES - 1)
170 			break;
171 		sline++;
172 	}
173 
174 	if (script.b.width > AVERAGE_LINE_LEN)
175 		script.b.width = AVERAGE_LINE_LEN;
176 
177 	free(line);
178 	pclose(fp);
179 
180 	script.nr_lines = nr_entries;
181 	script.b.nr_entries = nr_entries;
182 	script.b.entries = &script.entries;
183 
184 	ret = script_browser__run(&script);
185 exit:
186 	free(buf);
187 	return ret;
188 }
189