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