]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - tools/perf/builtin-ftrace.c
perf ftrace: Add support for --pid option
[mirror_ubuntu-artful-kernel.git] / tools / perf / builtin-ftrace.c
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 #include <fcntl.h>
15
16 #include "debug.h"
17 #include <subcmd/parse-options.h>
18 #include "evlist.h"
19 #include "target.h"
20 #include "thread_map.h"
21 #include "util/config.h"
22
23
24 #define DEFAULT_TRACER "function_graph"
25
26 struct perf_ftrace {
27 struct perf_evlist *evlist;
28 struct target target;
29 const char *tracer;
30 };
31
32 static bool done;
33
34 static void sig_handler(int sig __maybe_unused)
35 {
36 done = true;
37 }
38
39 /*
40 * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since
41 * we asked by setting its exec_error to the function below,
42 * ftrace__workload_exec_failed_signal.
43 *
44 * XXX We need to handle this more appropriately, emitting an error, etc.
45 */
46 static void ftrace__workload_exec_failed_signal(int signo __maybe_unused,
47 siginfo_t *info __maybe_unused,
48 void *ucontext __maybe_unused)
49 {
50 /* workload_exec_errno = info->si_value.sival_int; */
51 done = true;
52 }
53
54 static int __write_tracing_file(const char *name, const char *val, bool append)
55 {
56 char *file;
57 int fd, ret = -1;
58 ssize_t size = strlen(val);
59 int flags = O_WRONLY;
60
61 file = get_tracing_file(name);
62 if (!file) {
63 pr_debug("cannot get tracing file: %s\n", name);
64 return -1;
65 }
66
67 if (append)
68 flags |= O_APPEND;
69 else
70 flags |= O_TRUNC;
71
72 fd = open(file, flags);
73 if (fd < 0) {
74 pr_debug("cannot open tracing file: %s\n", name);
75 goto out;
76 }
77
78 if (write(fd, val, size) == size)
79 ret = 0;
80 else
81 pr_debug("write '%s' to tracing/%s failed\n", val, name);
82
83 close(fd);
84 out:
85 put_tracing_file(file);
86 return ret;
87 }
88
89 static int write_tracing_file(const char *name, const char *val)
90 {
91 return __write_tracing_file(name, val, false);
92 }
93
94 static int append_tracing_file(const char *name, const char *val)
95 {
96 return __write_tracing_file(name, val, true);
97 }
98
99 static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused)
100 {
101 if (write_tracing_file("tracing_on", "0") < 0)
102 return -1;
103
104 if (write_tracing_file("current_tracer", "nop") < 0)
105 return -1;
106
107 if (write_tracing_file("set_ftrace_pid", " ") < 0)
108 return -1;
109
110 return 0;
111 }
112
113 static int set_tracing_pid(struct perf_ftrace *ftrace)
114 {
115 int i;
116 char buf[16];
117
118 if (target__has_cpu(&ftrace->target))
119 return 0;
120
121 for (i = 0; i < thread_map__nr(ftrace->evlist->threads); i++) {
122 scnprintf(buf, sizeof(buf), "%d",
123 ftrace->evlist->threads->map[i]);
124 if (append_tracing_file("set_ftrace_pid", buf) < 0)
125 return -1;
126 }
127 return 0;
128 }
129
130 static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
131 {
132 char *trace_file;
133 int trace_fd;
134 char buf[4096];
135 struct pollfd pollfd = {
136 .events = POLLIN,
137 };
138
139 if (geteuid() != 0) {
140 pr_err("ftrace only works for root!\n");
141 return -1;
142 }
143
144 signal(SIGINT, sig_handler);
145 signal(SIGUSR1, sig_handler);
146 signal(SIGCHLD, sig_handler);
147
148 if (reset_tracing_files(ftrace) < 0)
149 goto out;
150
151 /* reset ftrace buffer */
152 if (write_tracing_file("trace", "0") < 0)
153 goto out;
154
155 if (argc && perf_evlist__prepare_workload(ftrace->evlist,
156 &ftrace->target, argv, false,
157 ftrace__workload_exec_failed_signal) < 0) {
158 goto out;
159 }
160
161 if (set_tracing_pid(ftrace) < 0) {
162 pr_err("failed to set ftrace pid\n");
163 goto out_reset;
164 }
165
166 if (write_tracing_file("current_tracer", ftrace->tracer) < 0) {
167 pr_err("failed to set current_tracer to %s\n", ftrace->tracer);
168 goto out_reset;
169 }
170
171 trace_file = get_tracing_file("trace_pipe");
172 if (!trace_file) {
173 pr_err("failed to open trace_pipe\n");
174 goto out_reset;
175 }
176
177 trace_fd = open(trace_file, O_RDONLY);
178
179 put_tracing_file(trace_file);
180
181 if (trace_fd < 0) {
182 pr_err("failed to open trace_pipe\n");
183 goto out_reset;
184 }
185
186 fcntl(trace_fd, F_SETFL, O_NONBLOCK);
187 pollfd.fd = trace_fd;
188
189 if (write_tracing_file("tracing_on", "1") < 0) {
190 pr_err("can't enable tracing\n");
191 goto out_close_fd;
192 }
193
194 perf_evlist__start_workload(ftrace->evlist);
195
196 while (!done) {
197 if (poll(&pollfd, 1, -1) < 0)
198 break;
199
200 if (pollfd.revents & POLLIN) {
201 int n = read(trace_fd, buf, sizeof(buf));
202 if (n < 0)
203 break;
204 if (fwrite(buf, n, 1, stdout) != 1)
205 break;
206 }
207 }
208
209 write_tracing_file("tracing_on", "0");
210
211 /* read remaining buffer contents */
212 while (true) {
213 int n = read(trace_fd, buf, sizeof(buf));
214 if (n <= 0)
215 break;
216 if (fwrite(buf, n, 1, stdout) != 1)
217 break;
218 }
219
220 out_close_fd:
221 close(trace_fd);
222 out_reset:
223 reset_tracing_files(ftrace);
224 out:
225 return done ? 0 : -1;
226 }
227
228 static int perf_ftrace_config(const char *var, const char *value, void *cb)
229 {
230 struct perf_ftrace *ftrace = cb;
231
232 if (prefixcmp(var, "ftrace."))
233 return 0;
234
235 if (strcmp(var, "ftrace.tracer"))
236 return -1;
237
238 if (!strcmp(value, "function_graph") ||
239 !strcmp(value, "function")) {
240 ftrace->tracer = value;
241 return 0;
242 }
243
244 pr_err("Please select \"function_graph\" (default) or \"function\"\n");
245 return -1;
246 }
247
248 int cmd_ftrace(int argc, const char **argv, const char *prefix __maybe_unused)
249 {
250 int ret;
251 struct perf_ftrace ftrace = {
252 .tracer = DEFAULT_TRACER,
253 .target = { .uid = UINT_MAX, },
254 };
255 const char * const ftrace_usage[] = {
256 "perf ftrace [<options>] [<command>]",
257 "perf ftrace [<options>] -- <command> [<options>]",
258 NULL
259 };
260 const struct option ftrace_options[] = {
261 OPT_STRING('t', "tracer", &ftrace.tracer, "tracer",
262 "tracer to use: function_graph(default) or function"),
263 OPT_STRING('p', "pid", &ftrace.target.pid, "pid",
264 "trace on existing process id"),
265 OPT_INCR('v', "verbose", &verbose,
266 "be more verbose"),
267 OPT_END()
268 };
269
270 ret = perf_config(perf_ftrace_config, &ftrace);
271 if (ret < 0)
272 return -1;
273
274 argc = parse_options(argc, argv, ftrace_options, ftrace_usage,
275 PARSE_OPT_STOP_AT_NON_OPTION);
276 if (!argc && target__none(&ftrace.target))
277 usage_with_options(ftrace_usage, ftrace_options);
278
279 ret = target__validate(&ftrace.target);
280 if (ret) {
281 char errbuf[512];
282
283 target__strerror(&ftrace.target, ret, errbuf, 512);
284 pr_err("%s\n", errbuf);
285 return -EINVAL;
286 }
287
288 ftrace.evlist = perf_evlist__new();
289 if (ftrace.evlist == NULL)
290 return -ENOMEM;
291
292 ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target);
293 if (ret < 0)
294 goto out_delete_evlist;
295
296 ret = __cmd_ftrace(&ftrace, argc, argv);
297
298 out_delete_evlist:
299 perf_evlist__delete(ftrace.evlist);
300
301 return ret;
302 }