Commit | Line | Data |
---|---|---|
66517826 | 1 | #include <elf.h> |
66517826 FT |
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 | ||
20 | struct script_line { | |
21 | struct list_head node; | |
22 | char line[AVERAGE_LINE_LEN]; | |
23 | }; | |
24 | ||
25 | struct 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 | */ | |
47 | static 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 | ||
75 | static 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 | ||
316c7136 | 87 | static int script_browser__run(struct perf_script_browser *browser) |
66517826 FT |
88 | { |
89 | int key; | |
90 | ||
316c7136 | 91 | if (ui_browser__show(&browser->b, browser->script_name, |
66517826 FT |
92 | "Press <- or ESC to exit") < 0) |
93 | return -1; | |
94 | ||
95 | while (1) { | |
316c7136 | 96 | key = ui_browser__run(&browser->b, 0); |
66517826 FT |
97 | |
98 | /* We can add some special key handling here if needed */ | |
99 | break; | |
100 | } | |
101 | ||
316c7136 | 102 | ui_browser__hide(&browser->b); |
66517826 FT |
103 | return key; |
104 | } | |
105 | ||
106 | ||
107 | int 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 | ||
f5385650 | 176 | free(line); |
66517826 FT |
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); | |
184 | exit: | |
185 | free(buf); | |
186 | return ret; | |
187 | } |