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