hc
2023-12-08 01573e231f18eb2d99162747186f59511f56b64d
kernel/tools/perf/ui/browsers/scripts.c
....@@ -1,34 +1,17 @@
11 // 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"
2
+#include "../../builtin.h"
3
+#include "../../perf.h"
4
+#include "../../util/util.h" // perf_exe()
5
+#include "../util.h"
86 #include "../../util/hist.h"
97 #include "../../util/debug.h"
108 #include "../../util/symbol.h"
119 #include "../browser.h"
12
-#include "../helpline.h"
1310 #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
-};
11
+#include "config.h"
12
+#include <linux/string.h>
13
+#include <linux/zalloc.h>
14
+#include <stdlib.h>
3215
3316 #define SCRIPT_NAMELEN 128
3417 #define SCRIPT_MAX_NO 64
....@@ -40,149 +23,171 @@
4023 */
4124 #define SCRIPT_FULLPATH_LEN 256
4225
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
+
34
+void attr_to_script(char *extra_format, struct perf_event_attr *attr)
35
+{
36
+ extra_format[0] = 0;
37
+ if (attr->read_format & PERF_FORMAT_GROUP)
38
+ strcat(extra_format, " -F +metric");
39
+ if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK)
40
+ strcat(extra_format, " -F +brstackinsn --xed");
41
+ if (attr->sample_type & PERF_SAMPLE_REGS_INTR)
42
+ strcat(extra_format, " -F +iregs");
43
+ if (attr->sample_type & PERF_SAMPLE_REGS_USER)
44
+ strcat(extra_format, " -F +uregs");
45
+ if (attr->sample_type & PERF_SAMPLE_PHYS_ADDR)
46
+ strcat(extra_format, " -F +phys_addr");
47
+}
48
+
49
+static int add_script_option(const char *name, const char *opt,
50
+ struct script_config *c)
51
+{
52
+ c->names[c->index] = name;
53
+ if (asprintf(&c->paths[c->index],
54
+ "%s script %s -F +metric %s %s",
55
+ c->perf, opt, symbol_conf.inline_name ? " --inline" : "",
56
+ c->extra_format) < 0)
57
+ return -1;
58
+ c->index++;
59
+ return 0;
60
+}
61
+
62
+static int scripts_config(const char *var, const char *value, void *data)
63
+{
64
+ struct script_config *c = data;
65
+
66
+ if (!strstarts(var, "scripts."))
67
+ return -1;
68
+ if (c->index >= SCRIPT_MAX_NO)
69
+ return -1;
70
+ c->names[c->index] = strdup(var + 7);
71
+ if (!c->names[c->index])
72
+ return -1;
73
+ if (asprintf(&c->paths[c->index], "%s %s", value,
74
+ c->extra_format) < 0)
75
+ return -1;
76
+ c->index++;
77
+ return 0;
78
+}
79
+
4380 /*
4481 * When success, will copy the full path of the selected script
4582 * into the buffer pointed by script_name, and return 0.
4683 * Return -1 on failure.
4784 */
48
-static int list_scripts(char *script_name)
85
+static int list_scripts(char *script_name, bool *custom,
86
+ struct evsel *evsel)
4987 {
50
- char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO];
51
- int i, num, choice, ret = -1;
88
+ char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO];
89
+ int i, num, choice;
90
+ int ret = 0;
91
+ int max_std, custom_perf;
92
+ char pbuf[256];
93
+ const char *perf = perf_exe(pbuf, sizeof pbuf);
94
+ struct script_config scriptc = {
95
+ .names = (const char **)names,
96
+ .paths = paths,
97
+ .perf = perf
98
+ };
99
+
100
+ script_name[0] = 0;
52101
53102 /* Preset the script name to SCRIPT_NAMELEN */
54103 buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
55104 if (!buf)
56
- return ret;
105
+ return -1;
57106
58
- for (i = 0; i < SCRIPT_MAX_NO; i++) {
59
- names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
107
+ if (evsel)
108
+ attr_to_script(scriptc.extra_format, &evsel->core.attr);
109
+ add_script_option("Show individual samples", "", &scriptc);
110
+ add_script_option("Show individual samples with assembler", "-F +insn --xed",
111
+ &scriptc);
112
+ add_script_option("Show individual samples with source", "-F +srcline,+srccode",
113
+ &scriptc);
114
+ perf_config(scripts_config, &scriptc);
115
+ custom_perf = scriptc.index;
116
+ add_script_option("Show samples with custom perf script arguments", "", &scriptc);
117
+ i = scriptc.index;
118
+ max_std = i;
119
+
120
+ for (; i < SCRIPT_MAX_NO; i++) {
121
+ names[i] = buf + (i - max_std) * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
60122 paths[i] = names[i] + SCRIPT_NAMELEN;
61123 }
62124
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
- }
125
+ num = find_scripts(names + max_std, paths + max_std, SCRIPT_MAX_NO - max_std,
126
+ SCRIPT_FULLPATH_LEN);
127
+ if (num < 0)
128
+ num = 0;
129
+ choice = ui__popup_menu(num + max_std, (char * const *)names, NULL);
130
+ if (choice < 0) {
131
+ ret = -1;
132
+ goto out;
70133 }
134
+ if (choice == custom_perf) {
135
+ char script_args[50];
136
+ int key = ui_browser__input_window("perf script command",
137
+ "Enter perf script command line (without perf script prefix)",
138
+ script_args, "", 0);
139
+ if (key != K_ENTER) {
140
+ ret = -1;
141
+ goto out;
142
+ }
143
+ sprintf(script_name, "%s script %s", perf, script_args);
144
+ } else if (choice < num + max_std) {
145
+ strcpy(script_name, paths[choice]);
146
+ }
147
+ *custom = choice >= max_std;
71148
149
+out:
72150 free(buf);
151
+ for (i = 0; i < max_std; i++)
152
+ zfree(&paths[i]);
73153 return ret;
74154 }
75155
76
-static void script_browser__write(struct ui_browser *browser,
77
- void *entry, int row)
156
+void run_script(char *cmd)
78157 {
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);
158
+ pr_debug("Running %s\n", cmd);
159
+ SLang_reset_tty();
160
+ if (system(cmd) < 0)
161
+ pr_warning("Cannot run %s\n", cmd);
162
+ /*
163
+ * SLang doesn't seem to reset the whole terminal, so be more
164
+ * forceful to get back to the original state.
165
+ */
166
+ printf("\033[c\033[H\033[J");
167
+ fflush(stdout);
168
+ SLang_init_tty(0, 0, 0);
169
+ SLsmg_refresh();
86170 }
87171
88
-static int script_browser__run(struct perf_script_browser *browser)
172
+int script_browse(const char *script_opt, struct evsel *evsel)
89173 {
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;
174
+ char *cmd, script_name[SCRIPT_FULLPATH_LEN];
175
+ bool custom = false;
135176
136177 memset(script_name, 0, SCRIPT_FULLPATH_LEN);
137
- if (list_scripts(script_name))
138
- goto exit;
178
+ if (list_scripts(script_name, &custom, evsel))
179
+ return -1;
139180
140
- sprintf(cmd, "perf script -s %s ", script_name);
181
+ if (asprintf(&cmd, "%s%s %s %s%s 2>&1 | less",
182
+ custom ? "perf script -s " : "",
183
+ script_name,
184
+ script_opt ? script_opt : "",
185
+ input_name ? "-i " : "",
186
+ input_name ? input_name : "") < 0)
187
+ return -1;
141188
142
- if (script_opt)
143
- strcat(cmd, script_opt);
189
+ run_script(cmd);
190
+ free(cmd);
144191
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;
192
+ return 0;
188193 }