]>
Commit | Line | Data |
---|---|---|
d01f4e8d NK |
1 | /* |
2 | * builtin-ftrace.c | |
3 | * | |
4 | * Copyright (c) 2013 LG Electronics, Namhyung Kim <namhyung@kernel.org> | |
5 | * | |
6 | * Released under the GPL v2. | |
7 | */ | |
8 | ||
9 | #include "builtin.h" | |
10 | #include "perf.h" | |
11 | ||
12 | #include <unistd.h> | |
13 | #include <signal.h> | |
14 | ||
15 | #include "debug.h" | |
16 | #include <subcmd/parse-options.h> | |
17 | #include "evlist.h" | |
18 | #include "target.h" | |
19 | #include "thread_map.h" | |
b05d1093 | 20 | #include "util/config.h" |
d01f4e8d NK |
21 | |
22 | ||
23 | #define DEFAULT_TRACER "function_graph" | |
24 | ||
25 | struct perf_ftrace { | |
26 | struct perf_evlist *evlist; | |
27 | struct target target; | |
28 | const char *tracer; | |
29 | }; | |
30 | ||
31 | static bool done; | |
32 | ||
33 | static void sig_handler(int sig __maybe_unused) | |
34 | { | |
35 | done = true; | |
36 | } | |
37 | ||
38 | /* | |
39 | * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since | |
40 | * we asked by setting its exec_error to the function below, | |
41 | * ftrace__workload_exec_failed_signal. | |
42 | * | |
43 | * XXX We need to handle this more appropriately, emitting an error, etc. | |
44 | */ | |
45 | static void ftrace__workload_exec_failed_signal(int signo __maybe_unused, | |
46 | siginfo_t *info __maybe_unused, | |
47 | void *ucontext __maybe_unused) | |
48 | { | |
49 | /* workload_exec_errno = info->si_value.sival_int; */ | |
50 | done = true; | |
51 | } | |
52 | ||
53 | static int write_tracing_file(const char *name, const char *val) | |
54 | { | |
55 | char *file; | |
56 | int fd, ret = -1; | |
57 | ssize_t size = strlen(val); | |
58 | ||
59 | file = get_tracing_file(name); | |
60 | if (!file) { | |
61 | pr_debug("cannot get tracing file: %s\n", name); | |
62 | return -1; | |
63 | } | |
64 | ||
65 | fd = open(file, O_WRONLY); | |
66 | if (fd < 0) { | |
67 | pr_debug("cannot open tracing file: %s\n", name); | |
68 | goto out; | |
69 | } | |
70 | ||
71 | if (write(fd, val, size) == size) | |
72 | ret = 0; | |
73 | else | |
74 | pr_debug("write '%s' to tracing/%s failed\n", val, name); | |
75 | ||
76 | close(fd); | |
77 | out: | |
78 | put_tracing_file(file); | |
79 | return ret; | |
80 | } | |
81 | ||
82 | static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused) | |
83 | { | |
84 | if (write_tracing_file("tracing_on", "0") < 0) | |
85 | return -1; | |
86 | ||
87 | if (write_tracing_file("current_tracer", "nop") < 0) | |
88 | return -1; | |
89 | ||
90 | if (write_tracing_file("set_ftrace_pid", " ") < 0) | |
91 | return -1; | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) | |
97 | { | |
98 | char *trace_file; | |
99 | int trace_fd; | |
100 | char *trace_pid; | |
101 | char buf[4096]; | |
102 | struct pollfd pollfd = { | |
103 | .events = POLLIN, | |
104 | }; | |
105 | ||
106 | if (geteuid() != 0) { | |
107 | pr_err("ftrace only works for root!\n"); | |
108 | return -1; | |
109 | } | |
110 | ||
111 | if (argc < 1) | |
112 | return -1; | |
113 | ||
114 | signal(SIGINT, sig_handler); | |
115 | signal(SIGUSR1, sig_handler); | |
116 | signal(SIGCHLD, sig_handler); | |
117 | ||
118 | reset_tracing_files(ftrace); | |
119 | ||
120 | /* reset ftrace buffer */ | |
121 | if (write_tracing_file("trace", "0") < 0) | |
122 | goto out; | |
123 | ||
124 | if (perf_evlist__prepare_workload(ftrace->evlist, &ftrace->target, | |
125 | argv, false, ftrace__workload_exec_failed_signal) < 0) | |
126 | goto out; | |
127 | ||
128 | if (write_tracing_file("current_tracer", ftrace->tracer) < 0) { | |
129 | pr_err("failed to set current_tracer to %s\n", ftrace->tracer); | |
130 | goto out; | |
131 | } | |
132 | ||
133 | if (asprintf(&trace_pid, "%d", thread_map__pid(ftrace->evlist->threads, 0)) < 0) { | |
134 | pr_err("failed to allocate pid string\n"); | |
135 | goto out; | |
136 | } | |
137 | ||
138 | if (write_tracing_file("set_ftrace_pid", trace_pid) < 0) { | |
139 | pr_err("failed to set pid: %s\n", trace_pid); | |
140 | goto out_free_pid; | |
141 | } | |
142 | ||
143 | trace_file = get_tracing_file("trace_pipe"); | |
144 | if (!trace_file) { | |
145 | pr_err("failed to open trace_pipe\n"); | |
146 | goto out_free_pid; | |
147 | } | |
148 | ||
149 | trace_fd = open(trace_file, O_RDONLY); | |
150 | ||
151 | put_tracing_file(trace_file); | |
152 | ||
153 | if (trace_fd < 0) { | |
154 | pr_err("failed to open trace_pipe\n"); | |
155 | goto out_free_pid; | |
156 | } | |
157 | ||
158 | fcntl(trace_fd, F_SETFL, O_NONBLOCK); | |
159 | pollfd.fd = trace_fd; | |
160 | ||
161 | if (write_tracing_file("tracing_on", "1") < 0) { | |
162 | pr_err("can't enable tracing\n"); | |
163 | goto out_close_fd; | |
164 | } | |
165 | ||
166 | perf_evlist__start_workload(ftrace->evlist); | |
167 | ||
168 | while (!done) { | |
169 | if (poll(&pollfd, 1, -1) < 0) | |
170 | break; | |
171 | ||
172 | if (pollfd.revents & POLLIN) { | |
173 | int n = read(trace_fd, buf, sizeof(buf)); | |
174 | if (n < 0) | |
175 | break; | |
176 | if (fwrite(buf, n, 1, stdout) != 1) | |
177 | break; | |
178 | } | |
179 | } | |
180 | ||
181 | write_tracing_file("tracing_on", "0"); | |
182 | ||
183 | /* read remaining buffer contents */ | |
184 | while (true) { | |
185 | int n = read(trace_fd, buf, sizeof(buf)); | |
186 | if (n <= 0) | |
187 | break; | |
188 | if (fwrite(buf, n, 1, stdout) != 1) | |
189 | break; | |
190 | } | |
191 | ||
192 | out_close_fd: | |
193 | close(trace_fd); | |
194 | out_free_pid: | |
195 | free(trace_pid); | |
196 | out: | |
197 | reset_tracing_files(ftrace); | |
198 | ||
199 | return done ? 0 : -1; | |
200 | } | |
201 | ||
b05d1093 TS |
202 | static int perf_ftrace_config(const char *var, const char *value, void *cb) |
203 | { | |
204 | struct perf_ftrace *ftrace = cb; | |
205 | ||
206 | if (prefixcmp(var, "ftrace.")) | |
207 | return 0; | |
208 | ||
209 | if (strcmp(var, "ftrace.tracer")) | |
210 | return -1; | |
211 | ||
212 | if (!strcmp(value, "function_graph") || | |
213 | !strcmp(value, "function")) { | |
214 | ftrace->tracer = value; | |
215 | return 0; | |
216 | } | |
217 | ||
218 | pr_err("Please select \"function_graph\" (default) or \"function\"\n"); | |
219 | return -1; | |
220 | } | |
221 | ||
d01f4e8d NK |
222 | int cmd_ftrace(int argc, const char **argv, const char *prefix __maybe_unused) |
223 | { | |
224 | int ret; | |
225 | struct perf_ftrace ftrace = { | |
bf062bd2 | 226 | .tracer = DEFAULT_TRACER, |
d01f4e8d NK |
227 | .target = { .uid = UINT_MAX, }, |
228 | }; | |
229 | const char * const ftrace_usage[] = { | |
230 | "perf ftrace [<options>] <command>", | |
231 | "perf ftrace [<options>] -- <command> [<options>]", | |
232 | NULL | |
233 | }; | |
234 | const struct option ftrace_options[] = { | |
235 | OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", | |
ec347870 | 236 | "tracer to use: function_graph(default) or function"), |
d01f4e8d NK |
237 | OPT_INCR('v', "verbose", &verbose, |
238 | "be more verbose"), | |
239 | OPT_END() | |
240 | }; | |
241 | ||
b05d1093 TS |
242 | ret = perf_config(perf_ftrace_config, &ftrace); |
243 | if (ret < 0) | |
244 | return -1; | |
245 | ||
d01f4e8d NK |
246 | argc = parse_options(argc, argv, ftrace_options, ftrace_usage, |
247 | PARSE_OPT_STOP_AT_NON_OPTION); | |
248 | if (!argc) | |
249 | usage_with_options(ftrace_usage, ftrace_options); | |
250 | ||
251 | ftrace.evlist = perf_evlist__new(); | |
252 | if (ftrace.evlist == NULL) | |
253 | return -ENOMEM; | |
254 | ||
255 | ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target); | |
256 | if (ret < 0) | |
257 | goto out_delete_evlist; | |
258 | ||
d01f4e8d NK |
259 | ret = __cmd_ftrace(&ftrace, argc, argv); |
260 | ||
261 | out_delete_evlist: | |
262 | perf_evlist__delete(ftrace.evlist); | |
263 | ||
264 | return ret; | |
265 | } |