Loading...
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}
1// SPDX-License-Identifier: GPL-2.0
2#include "../../builtin.h"
3#include "../../perf.h"
4#include "../../util/util.h" // perf_exe()
5#include "../util.h"
6#include "../../util/hist.h"
7#include "../../util/debug.h"
8#include "../../util/symbol.h"
9#include "../browser.h"
10#include "../libslang.h"
11#include "config.h"
12#include <linux/string.h>
13#include <linux/zalloc.h>
14#include <stdlib.h>
15
16#define SCRIPT_NAMELEN 128
17#define SCRIPT_MAX_NO 64
18/*
19 * Usually the full path for a script is:
20 * /home/username/libexec/perf-core/scripts/python/xxx.py
21 * /home/username/libexec/perf-core/scripts/perl/xxx.pl
22 * So 256 should be long enough to contain the full path.
23 */
24#define SCRIPT_FULLPATH_LEN 256
25
26struct script_config {
27 const char **names;
28 char **paths;
29 int index;
30 const char *perf;
31 char extra_format[256];
32};
33
34void 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
49static 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
62static 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
80/*
81 * When success, will copy the full path of the selected script
82 * into the buffer pointed by script_name, and return 0.
83 * Return -1 on failure.
84 */
85static int list_scripts(char *script_name, bool *custom,
86 struct evsel *evsel)
87{
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;
101
102 /* Preset the script name to SCRIPT_NAMELEN */
103 buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
104 if (!buf)
105 return -1;
106
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);
122 paths[i] = names[i] + SCRIPT_NAMELEN;
123 }
124
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;
133 }
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;
148
149out:
150 free(buf);
151 for (i = 0; i < max_std; i++)
152 zfree(&paths[i]);
153 return ret;
154}
155
156void run_script(char *cmd)
157{
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();
170}
171
172int script_browse(const char *script_opt, struct evsel *evsel)
173{
174 char *cmd, script_name[SCRIPT_FULLPATH_LEN];
175 bool custom = false;
176
177 memset(script_name, 0, SCRIPT_FULLPATH_LEN);
178 if (list_scripts(script_name, &custom, evsel))
179 return -1;
180
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;
188
189 run_script(cmd);
190 free(cmd);
191
192 return 0;
193}