xref: /linux/tools/perf/builtin-ftrace.c (revision b1d84af6f58022a0d75f5745367624549cd0bfbf)
11c6bec5bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d01f4e8dSNamhyung Kim /*
3d01f4e8dSNamhyung Kim  * builtin-ftrace.c
4d01f4e8dSNamhyung Kim  *
5d01f4e8dSNamhyung Kim  * Copyright (c) 2013  LG Electronics,  Namhyung Kim <namhyung@kernel.org>
6d01f4e8dSNamhyung Kim  */
7d01f4e8dSNamhyung Kim 
8d01f4e8dSNamhyung Kim #include "builtin.h"
9d01f4e8dSNamhyung Kim 
10a43783aeSArnaldo Carvalho de Melo #include <errno.h>
11d01f4e8dSNamhyung Kim #include <unistd.h>
12d01f4e8dSNamhyung Kim #include <signal.h>
13f2a39fe8SArnaldo Carvalho de Melo #include <stdlib.h>
14a9af6be5SNamhyung Kim #include <fcntl.h>
154208735dSArnaldo Carvalho de Melo #include <poll.h>
16c766f3dfSIgor Lubashev #include <linux/capability.h>
178520a98dSArnaldo Carvalho de Melo #include <linux/string.h>
18d01f4e8dSNamhyung Kim 
19d01f4e8dSNamhyung Kim #include "debug.h"
208520a98dSArnaldo Carvalho de Melo #include <subcmd/pager.h>
21d01f4e8dSNamhyung Kim #include <subcmd/parse-options.h>
2220a9ed28SArnaldo Carvalho de Melo #include <api/fs/tracing_path.h>
23d01f4e8dSNamhyung Kim #include "evlist.h"
24d01f4e8dSNamhyung Kim #include "target.h"
25dc231032SNamhyung Kim #include "cpumap.h"
26d01f4e8dSNamhyung Kim #include "thread_map.h"
27c766f3dfSIgor Lubashev #include "util/cap.h"
28b05d1093STaeung Song #include "util/config.h"
29846e1939SChangbin Du #include "util/units.h"
30*b1d84af6SChangbin Du #include "util/parse-sublevel-options.h"
31d01f4e8dSNamhyung Kim 
32d01f4e8dSNamhyung Kim #define DEFAULT_TRACER  "function_graph"
33d01f4e8dSNamhyung Kim 
34d01f4e8dSNamhyung Kim struct perf_ftrace {
3563503dbaSJiri Olsa 	struct evlist		*evlist;
36d01f4e8dSNamhyung Kim 	struct target		target;
37d01f4e8dSNamhyung Kim 	const char		*tracer;
38d6d81bfeSChangbin Du 	bool			list_avail_functions;
3978b83e8bSNamhyung Kim 	struct list_head	filters;
4078b83e8bSNamhyung Kim 	struct list_head	notrace;
4178b83e8bSNamhyung Kim 	struct list_head	graph_funcs;
4278b83e8bSNamhyung Kim 	struct list_head	nograph_funcs;
431096c35aSNamhyung Kim 	int			graph_depth;
44846e1939SChangbin Du 	unsigned long		percpu_buffer_size;
455b347472SChangbin Du 	bool			inherit;
46*b1d84af6SChangbin Du 	int			func_stack_trace;
4778b83e8bSNamhyung Kim };
4878b83e8bSNamhyung Kim 
4978b83e8bSNamhyung Kim struct filter_entry {
5078b83e8bSNamhyung Kim 	struct list_head	list;
5178b83e8bSNamhyung Kim 	char			name[];
52d01f4e8dSNamhyung Kim };
53d01f4e8dSNamhyung Kim 
5451a09d8fSChangbin Du static volatile int workload_exec_errno;
55d01f4e8dSNamhyung Kim static bool done;
56d01f4e8dSNamhyung Kim 
57d01f4e8dSNamhyung Kim static void sig_handler(int sig __maybe_unused)
58d01f4e8dSNamhyung Kim {
59d01f4e8dSNamhyung Kim 	done = true;
60d01f4e8dSNamhyung Kim }
61d01f4e8dSNamhyung Kim 
62d01f4e8dSNamhyung Kim /*
63d01f4e8dSNamhyung Kim  * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since
64d01f4e8dSNamhyung Kim  * we asked by setting its exec_error to the function below,
65d01f4e8dSNamhyung Kim  * ftrace__workload_exec_failed_signal.
66d01f4e8dSNamhyung Kim  *
67d01f4e8dSNamhyung Kim  * XXX We need to handle this more appropriately, emitting an error, etc.
68d01f4e8dSNamhyung Kim  */
69d01f4e8dSNamhyung Kim static void ftrace__workload_exec_failed_signal(int signo __maybe_unused,
70d01f4e8dSNamhyung Kim 						siginfo_t *info __maybe_unused,
71d01f4e8dSNamhyung Kim 						void *ucontext __maybe_unused)
72d01f4e8dSNamhyung Kim {
7351a09d8fSChangbin Du 	workload_exec_errno = info->si_value.sival_int;
74d01f4e8dSNamhyung Kim 	done = true;
75d01f4e8dSNamhyung Kim }
76d01f4e8dSNamhyung Kim 
77a9af6be5SNamhyung Kim static int __write_tracing_file(const char *name, const char *val, bool append)
78d01f4e8dSNamhyung Kim {
79d01f4e8dSNamhyung Kim 	char *file;
80d01f4e8dSNamhyung Kim 	int fd, ret = -1;
81d01f4e8dSNamhyung Kim 	ssize_t size = strlen(val);
82a9af6be5SNamhyung Kim 	int flags = O_WRONLY;
83e7bd9ba2SNamhyung Kim 	char errbuf[512];
8463cd02d8SChangbin Du 	char *val_copy;
85d01f4e8dSNamhyung Kim 
86d01f4e8dSNamhyung Kim 	file = get_tracing_file(name);
87d01f4e8dSNamhyung Kim 	if (!file) {
88d01f4e8dSNamhyung Kim 		pr_debug("cannot get tracing file: %s\n", name);
89d01f4e8dSNamhyung Kim 		return -1;
90d01f4e8dSNamhyung Kim 	}
91d01f4e8dSNamhyung Kim 
92a9af6be5SNamhyung Kim 	if (append)
93a9af6be5SNamhyung Kim 		flags |= O_APPEND;
94a9af6be5SNamhyung Kim 	else
95a9af6be5SNamhyung Kim 		flags |= O_TRUNC;
96a9af6be5SNamhyung Kim 
97a9af6be5SNamhyung Kim 	fd = open(file, flags);
98d01f4e8dSNamhyung Kim 	if (fd < 0) {
99e7bd9ba2SNamhyung Kim 		pr_debug("cannot open tracing file: %s: %s\n",
100e7bd9ba2SNamhyung Kim 			 name, str_error_r(errno, errbuf, sizeof(errbuf)));
101d01f4e8dSNamhyung Kim 		goto out;
102d01f4e8dSNamhyung Kim 	}
103d01f4e8dSNamhyung Kim 
10463cd02d8SChangbin Du 	/*
10563cd02d8SChangbin Du 	 * Copy the original value and append a '\n'. Without this,
10663cd02d8SChangbin Du 	 * the kernel can hide possible errors.
10763cd02d8SChangbin Du 	 */
10863cd02d8SChangbin Du 	val_copy = strdup(val);
10963cd02d8SChangbin Du 	if (!val_copy)
11063cd02d8SChangbin Du 		goto out_close;
11163cd02d8SChangbin Du 	val_copy[size] = '\n';
11263cd02d8SChangbin Du 
11363cd02d8SChangbin Du 	if (write(fd, val_copy, size + 1) == size + 1)
114d01f4e8dSNamhyung Kim 		ret = 0;
115d01f4e8dSNamhyung Kim 	else
116e7bd9ba2SNamhyung Kim 		pr_debug("write '%s' to tracing/%s failed: %s\n",
117e7bd9ba2SNamhyung Kim 			 val, name, str_error_r(errno, errbuf, sizeof(errbuf)));
118d01f4e8dSNamhyung Kim 
11963cd02d8SChangbin Du 	free(val_copy);
12063cd02d8SChangbin Du out_close:
121d01f4e8dSNamhyung Kim 	close(fd);
122d01f4e8dSNamhyung Kim out:
123d01f4e8dSNamhyung Kim 	put_tracing_file(file);
124d01f4e8dSNamhyung Kim 	return ret;
125d01f4e8dSNamhyung Kim }
126d01f4e8dSNamhyung Kim 
127a9af6be5SNamhyung Kim static int write_tracing_file(const char *name, const char *val)
128a9af6be5SNamhyung Kim {
129a9af6be5SNamhyung Kim 	return __write_tracing_file(name, val, false);
130a9af6be5SNamhyung Kim }
131a9af6be5SNamhyung Kim 
132a9af6be5SNamhyung Kim static int append_tracing_file(const char *name, const char *val)
133a9af6be5SNamhyung Kim {
134a9af6be5SNamhyung Kim 	return __write_tracing_file(name, val, true);
135a9af6be5SNamhyung Kim }
136a9af6be5SNamhyung Kim 
137d6d81bfeSChangbin Du static int read_tracing_file_to_stdout(const char *name)
138d6d81bfeSChangbin Du {
139d6d81bfeSChangbin Du 	char buf[4096];
140d6d81bfeSChangbin Du 	char *file;
141d6d81bfeSChangbin Du 	int fd;
142d6d81bfeSChangbin Du 	int ret = -1;
143d6d81bfeSChangbin Du 
144d6d81bfeSChangbin Du 	file = get_tracing_file(name);
145d6d81bfeSChangbin Du 	if (!file) {
146d6d81bfeSChangbin Du 		pr_debug("cannot get tracing file: %s\n", name);
147d6d81bfeSChangbin Du 		return -1;
148d6d81bfeSChangbin Du 	}
149d6d81bfeSChangbin Du 
150d6d81bfeSChangbin Du 	fd = open(file, O_RDONLY);
151d6d81bfeSChangbin Du 	if (fd < 0) {
152d6d81bfeSChangbin Du 		pr_debug("cannot open tracing file: %s: %s\n",
153d6d81bfeSChangbin Du 			 name, str_error_r(errno, buf, sizeof(buf)));
154d6d81bfeSChangbin Du 		goto out;
155d6d81bfeSChangbin Du 	}
156d6d81bfeSChangbin Du 
157d6d81bfeSChangbin Du 	/* read contents to stdout */
158d6d81bfeSChangbin Du 	while (true) {
159d6d81bfeSChangbin Du 		int n = read(fd, buf, sizeof(buf));
160d6d81bfeSChangbin Du 		if (n == 0)
161d6d81bfeSChangbin Du 			break;
162d6d81bfeSChangbin Du 		else if (n < 0)
163d6d81bfeSChangbin Du 			goto out_close;
164d6d81bfeSChangbin Du 
165d6d81bfeSChangbin Du 		if (fwrite(buf, n, 1, stdout) != 1)
166d6d81bfeSChangbin Du 			goto out_close;
167d6d81bfeSChangbin Du 	}
168d6d81bfeSChangbin Du 	ret = 0;
169d6d81bfeSChangbin Du 
170d6d81bfeSChangbin Du out_close:
171d6d81bfeSChangbin Du 	close(fd);
172d6d81bfeSChangbin Du out:
173d6d81bfeSChangbin Du 	put_tracing_file(file);
174d6d81bfeSChangbin Du 	return ret;
175d6d81bfeSChangbin Du }
176d6d81bfeSChangbin Du 
17768faab0fSChangbin Du static int write_tracing_file_int(const char *name, int value)
17868faab0fSChangbin Du {
17968faab0fSChangbin Du 	char buf[16];
18068faab0fSChangbin Du 
18168faab0fSChangbin Du 	snprintf(buf, sizeof(buf), "%d", value);
18268faab0fSChangbin Du 	if (write_tracing_file(name, buf) < 0)
18368faab0fSChangbin Du 		return -1;
18468faab0fSChangbin Du 
18568faab0fSChangbin Du 	return 0;
18668faab0fSChangbin Du }
18768faab0fSChangbin Du 
1885b347472SChangbin Du static int write_tracing_option_file(const char *name, const char *val)
1895b347472SChangbin Du {
1905b347472SChangbin Du 	char *file;
1915b347472SChangbin Du 	int ret;
1925b347472SChangbin Du 
1935b347472SChangbin Du 	if (asprintf(&file, "options/%s", name) < 0)
1945b347472SChangbin Du 		return -1;
1955b347472SChangbin Du 
1965b347472SChangbin Du 	ret = __write_tracing_file(file, val, false);
1975b347472SChangbin Du 	free(file);
1985b347472SChangbin Du 	return ret;
1995b347472SChangbin Du }
2005b347472SChangbin Du 
201dc231032SNamhyung Kim static int reset_tracing_cpu(void);
20278b83e8bSNamhyung Kim static void reset_tracing_filters(void);
203dc231032SNamhyung Kim 
2045b347472SChangbin Du static void reset_tracing_options(struct perf_ftrace *ftrace __maybe_unused)
2055b347472SChangbin Du {
2065b347472SChangbin Du 	write_tracing_option_file("function-fork", "0");
207*b1d84af6SChangbin Du 	write_tracing_option_file("func_stack_trace", "0");
2085b347472SChangbin Du }
2095b347472SChangbin Du 
210d01f4e8dSNamhyung Kim static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused)
211d01f4e8dSNamhyung Kim {
212d01f4e8dSNamhyung Kim 	if (write_tracing_file("tracing_on", "0") < 0)
213d01f4e8dSNamhyung Kim 		return -1;
214d01f4e8dSNamhyung Kim 
215d01f4e8dSNamhyung Kim 	if (write_tracing_file("current_tracer", "nop") < 0)
216d01f4e8dSNamhyung Kim 		return -1;
217d01f4e8dSNamhyung Kim 
218d01f4e8dSNamhyung Kim 	if (write_tracing_file("set_ftrace_pid", " ") < 0)
219d01f4e8dSNamhyung Kim 		return -1;
220d01f4e8dSNamhyung Kim 
221dc231032SNamhyung Kim 	if (reset_tracing_cpu() < 0)
222dc231032SNamhyung Kim 		return -1;
223dc231032SNamhyung Kim 
2241096c35aSNamhyung Kim 	if (write_tracing_file("max_graph_depth", "0") < 0)
2251096c35aSNamhyung Kim 		return -1;
2261096c35aSNamhyung Kim 
22778b83e8bSNamhyung Kim 	reset_tracing_filters();
2285b347472SChangbin Du 	reset_tracing_options(ftrace);
229d01f4e8dSNamhyung Kim 	return 0;
230d01f4e8dSNamhyung Kim }
231d01f4e8dSNamhyung Kim 
232a9af6be5SNamhyung Kim static int set_tracing_pid(struct perf_ftrace *ftrace)
233a9af6be5SNamhyung Kim {
234a9af6be5SNamhyung Kim 	int i;
235a9af6be5SNamhyung Kim 	char buf[16];
236a9af6be5SNamhyung Kim 
237a9af6be5SNamhyung Kim 	if (target__has_cpu(&ftrace->target))
238a9af6be5SNamhyung Kim 		return 0;
239a9af6be5SNamhyung Kim 
240a2f354e3SJiri Olsa 	for (i = 0; i < perf_thread_map__nr(ftrace->evlist->core.threads); i++) {
241a9af6be5SNamhyung Kim 		scnprintf(buf, sizeof(buf), "%d",
24203617c22SJiri Olsa 			  ftrace->evlist->core.threads->map[i]);
243a9af6be5SNamhyung Kim 		if (append_tracing_file("set_ftrace_pid", buf) < 0)
244a9af6be5SNamhyung Kim 			return -1;
245a9af6be5SNamhyung Kim 	}
246a9af6be5SNamhyung Kim 	return 0;
247a9af6be5SNamhyung Kim }
248a9af6be5SNamhyung Kim 
249f854839bSJiri Olsa static int set_tracing_cpumask(struct perf_cpu_map *cpumap)
250dc231032SNamhyung Kim {
251dc231032SNamhyung Kim 	char *cpumask;
252dc231032SNamhyung Kim 	size_t mask_size;
253dc231032SNamhyung Kim 	int ret;
254dc231032SNamhyung Kim 	int last_cpu;
255dc231032SNamhyung Kim 
256dc231032SNamhyung Kim 	last_cpu = cpu_map__cpu(cpumap, cpumap->nr - 1);
257cf30ae72SHe Zhe 	mask_size = last_cpu / 4 + 2; /* one more byte for EOS */
258dc231032SNamhyung Kim 	mask_size += last_cpu / 32; /* ',' is needed for every 32th cpus */
259dc231032SNamhyung Kim 
260dc231032SNamhyung Kim 	cpumask = malloc(mask_size);
261dc231032SNamhyung Kim 	if (cpumask == NULL) {
262dc231032SNamhyung Kim 		pr_debug("failed to allocate cpu mask\n");
263dc231032SNamhyung Kim 		return -1;
264dc231032SNamhyung Kim 	}
265dc231032SNamhyung Kim 
266dc231032SNamhyung Kim 	cpu_map__snprint_mask(cpumap, cpumask, mask_size);
267dc231032SNamhyung Kim 
268dc231032SNamhyung Kim 	ret = write_tracing_file("tracing_cpumask", cpumask);
269dc231032SNamhyung Kim 
270dc231032SNamhyung Kim 	free(cpumask);
271dc231032SNamhyung Kim 	return ret;
272dc231032SNamhyung Kim }
273dc231032SNamhyung Kim 
274dc231032SNamhyung Kim static int set_tracing_cpu(struct perf_ftrace *ftrace)
275dc231032SNamhyung Kim {
276f72f901dSJiri Olsa 	struct perf_cpu_map *cpumap = ftrace->evlist->core.cpus;
277dc231032SNamhyung Kim 
278dc231032SNamhyung Kim 	if (!target__has_cpu(&ftrace->target))
279dc231032SNamhyung Kim 		return 0;
280dc231032SNamhyung Kim 
281dc231032SNamhyung Kim 	return set_tracing_cpumask(cpumap);
282dc231032SNamhyung Kim }
283dc231032SNamhyung Kim 
284*b1d84af6SChangbin Du static int set_tracing_func_stack_trace(struct perf_ftrace *ftrace)
285*b1d84af6SChangbin Du {
286*b1d84af6SChangbin Du 	if (!ftrace->func_stack_trace)
287*b1d84af6SChangbin Du 		return 0;
288*b1d84af6SChangbin Du 
289*b1d84af6SChangbin Du 	if (write_tracing_option_file("func_stack_trace", "1") < 0)
290*b1d84af6SChangbin Du 		return -1;
291*b1d84af6SChangbin Du 
292*b1d84af6SChangbin Du 	return 0;
293*b1d84af6SChangbin Du }
294*b1d84af6SChangbin Du 
295dc231032SNamhyung Kim static int reset_tracing_cpu(void)
296dc231032SNamhyung Kim {
2979c3516d1SJiri Olsa 	struct perf_cpu_map *cpumap = perf_cpu_map__new(NULL);
298dc231032SNamhyung Kim 	int ret;
299dc231032SNamhyung Kim 
300dc231032SNamhyung Kim 	ret = set_tracing_cpumask(cpumap);
30138f01d8dSJiri Olsa 	perf_cpu_map__put(cpumap);
302dc231032SNamhyung Kim 	return ret;
303dc231032SNamhyung Kim }
304dc231032SNamhyung Kim 
30578b83e8bSNamhyung Kim static int __set_tracing_filter(const char *filter_file, struct list_head *funcs)
30678b83e8bSNamhyung Kim {
30778b83e8bSNamhyung Kim 	struct filter_entry *pos;
30878b83e8bSNamhyung Kim 
30978b83e8bSNamhyung Kim 	list_for_each_entry(pos, funcs, list) {
31078b83e8bSNamhyung Kim 		if (append_tracing_file(filter_file, pos->name) < 0)
31178b83e8bSNamhyung Kim 			return -1;
31278b83e8bSNamhyung Kim 	}
31378b83e8bSNamhyung Kim 
31478b83e8bSNamhyung Kim 	return 0;
31578b83e8bSNamhyung Kim }
31678b83e8bSNamhyung Kim 
31778b83e8bSNamhyung Kim static int set_tracing_filters(struct perf_ftrace *ftrace)
31878b83e8bSNamhyung Kim {
31978b83e8bSNamhyung Kim 	int ret;
32078b83e8bSNamhyung Kim 
32178b83e8bSNamhyung Kim 	ret = __set_tracing_filter("set_ftrace_filter", &ftrace->filters);
32278b83e8bSNamhyung Kim 	if (ret < 0)
32378b83e8bSNamhyung Kim 		return ret;
32478b83e8bSNamhyung Kim 
32578b83e8bSNamhyung Kim 	ret = __set_tracing_filter("set_ftrace_notrace", &ftrace->notrace);
32678b83e8bSNamhyung Kim 	if (ret < 0)
32778b83e8bSNamhyung Kim 		return ret;
32878b83e8bSNamhyung Kim 
32978b83e8bSNamhyung Kim 	ret = __set_tracing_filter("set_graph_function", &ftrace->graph_funcs);
33078b83e8bSNamhyung Kim 	if (ret < 0)
33178b83e8bSNamhyung Kim 		return ret;
33278b83e8bSNamhyung Kim 
33378b83e8bSNamhyung Kim 	/* old kernels do not have this filter */
33478b83e8bSNamhyung Kim 	__set_tracing_filter("set_graph_notrace", &ftrace->nograph_funcs);
33578b83e8bSNamhyung Kim 
33678b83e8bSNamhyung Kim 	return ret;
33778b83e8bSNamhyung Kim }
33878b83e8bSNamhyung Kim 
33978b83e8bSNamhyung Kim static void reset_tracing_filters(void)
34078b83e8bSNamhyung Kim {
34178b83e8bSNamhyung Kim 	write_tracing_file("set_ftrace_filter", " ");
34278b83e8bSNamhyung Kim 	write_tracing_file("set_ftrace_notrace", " ");
34378b83e8bSNamhyung Kim 	write_tracing_file("set_graph_function", " ");
34478b83e8bSNamhyung Kim 	write_tracing_file("set_graph_notrace", " ");
34578b83e8bSNamhyung Kim }
34678b83e8bSNamhyung Kim 
3471096c35aSNamhyung Kim static int set_tracing_depth(struct perf_ftrace *ftrace)
3481096c35aSNamhyung Kim {
3491096c35aSNamhyung Kim 	if (ftrace->graph_depth == 0)
3501096c35aSNamhyung Kim 		return 0;
3511096c35aSNamhyung Kim 
3521096c35aSNamhyung Kim 	if (ftrace->graph_depth < 0) {
3531096c35aSNamhyung Kim 		pr_err("invalid graph depth: %d\n", ftrace->graph_depth);
3541096c35aSNamhyung Kim 		return -1;
3551096c35aSNamhyung Kim 	}
3561096c35aSNamhyung Kim 
35768faab0fSChangbin Du 	if (write_tracing_file_int("max_graph_depth", ftrace->graph_depth) < 0)
3581096c35aSNamhyung Kim 		return -1;
3591096c35aSNamhyung Kim 
3601096c35aSNamhyung Kim 	return 0;
3611096c35aSNamhyung Kim }
3621096c35aSNamhyung Kim 
363846e1939SChangbin Du static int set_tracing_percpu_buffer_size(struct perf_ftrace *ftrace)
364846e1939SChangbin Du {
365846e1939SChangbin Du 	int ret;
366846e1939SChangbin Du 
367846e1939SChangbin Du 	if (ftrace->percpu_buffer_size == 0)
368846e1939SChangbin Du 		return 0;
369846e1939SChangbin Du 
370846e1939SChangbin Du 	ret = write_tracing_file_int("buffer_size_kb",
371846e1939SChangbin Du 				     ftrace->percpu_buffer_size / 1024);
372846e1939SChangbin Du 	if (ret < 0)
373846e1939SChangbin Du 		return ret;
374846e1939SChangbin Du 
375846e1939SChangbin Du 	return 0;
376846e1939SChangbin Du }
377846e1939SChangbin Du 
3785b347472SChangbin Du static int set_tracing_trace_inherit(struct perf_ftrace *ftrace)
3795b347472SChangbin Du {
3805b347472SChangbin Du 	if (!ftrace->inherit)
3815b347472SChangbin Du 		return 0;
3825b347472SChangbin Du 
3835b347472SChangbin Du 	if (write_tracing_option_file("function-fork", "1") < 0)
3845b347472SChangbin Du 		return -1;
3855b347472SChangbin Du 
3865b347472SChangbin Du 	return 0;
3875b347472SChangbin Du }
3885b347472SChangbin Du 
389d01f4e8dSNamhyung Kim static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
390d01f4e8dSNamhyung Kim {
391d01f4e8dSNamhyung Kim 	char *trace_file;
392d01f4e8dSNamhyung Kim 	int trace_fd;
393d01f4e8dSNamhyung Kim 	char buf[4096];
394d01f4e8dSNamhyung Kim 	struct pollfd pollfd = {
395d01f4e8dSNamhyung Kim 		.events = POLLIN,
396d01f4e8dSNamhyung Kim 	};
397d01f4e8dSNamhyung Kim 
3986b3e0e2eSAlexey Budankov 	if (!(perf_cap__capable(CAP_PERFMON) ||
3996b3e0e2eSAlexey Budankov 	      perf_cap__capable(CAP_SYS_ADMIN))) {
40073e5de70SArnaldo Carvalho de Melo 		pr_err("ftrace only works for %s!\n",
40173e5de70SArnaldo Carvalho de Melo #ifdef HAVE_LIBCAP_SUPPORT
4026b3e0e2eSAlexey Budankov 		"users with the CAP_PERFMON or CAP_SYS_ADMIN capability"
40373e5de70SArnaldo Carvalho de Melo #else
40473e5de70SArnaldo Carvalho de Melo 		"root"
40573e5de70SArnaldo Carvalho de Melo #endif
40673e5de70SArnaldo Carvalho de Melo 		);
407d01f4e8dSNamhyung Kim 		return -1;
408d01f4e8dSNamhyung Kim 	}
409d01f4e8dSNamhyung Kim 
410d01f4e8dSNamhyung Kim 	signal(SIGINT, sig_handler);
411d01f4e8dSNamhyung Kim 	signal(SIGUSR1, sig_handler);
412d01f4e8dSNamhyung Kim 	signal(SIGCHLD, sig_handler);
41358335964SNamhyung Kim 	signal(SIGPIPE, sig_handler);
414d01f4e8dSNamhyung Kim 
415d6d81bfeSChangbin Du 	if (ftrace->list_avail_functions)
416d6d81bfeSChangbin Du 		return read_tracing_file_to_stdout("available_filter_functions");
417d6d81bfeSChangbin Du 
41863cd02d8SChangbin Du 	if (reset_tracing_files(ftrace) < 0) {
41963cd02d8SChangbin Du 		pr_err("failed to reset ftrace\n");
420a9af6be5SNamhyung Kim 		goto out;
42163cd02d8SChangbin Du 	}
422d01f4e8dSNamhyung Kim 
423d01f4e8dSNamhyung Kim 	/* reset ftrace buffer */
424d01f4e8dSNamhyung Kim 	if (write_tracing_file("trace", "0") < 0)
425d01f4e8dSNamhyung Kim 		goto out;
426d01f4e8dSNamhyung Kim 
427a9af6be5SNamhyung Kim 	if (argc && perf_evlist__prepare_workload(ftrace->evlist,
428a9af6be5SNamhyung Kim 				&ftrace->target, argv, false,
429a9af6be5SNamhyung Kim 				ftrace__workload_exec_failed_signal) < 0) {
430d01f4e8dSNamhyung Kim 		goto out;
431a9af6be5SNamhyung Kim 	}
432a9af6be5SNamhyung Kim 
433a9af6be5SNamhyung Kim 	if (set_tracing_pid(ftrace) < 0) {
434a9af6be5SNamhyung Kim 		pr_err("failed to set ftrace pid\n");
435a9af6be5SNamhyung Kim 		goto out_reset;
436a9af6be5SNamhyung Kim 	}
437d01f4e8dSNamhyung Kim 
438dc231032SNamhyung Kim 	if (set_tracing_cpu(ftrace) < 0) {
439dc231032SNamhyung Kim 		pr_err("failed to set tracing cpumask\n");
440dc231032SNamhyung Kim 		goto out_reset;
441dc231032SNamhyung Kim 	}
442dc231032SNamhyung Kim 
443*b1d84af6SChangbin Du 	if (set_tracing_func_stack_trace(ftrace) < 0) {
444*b1d84af6SChangbin Du 		pr_err("failed to set tracing option func_stack_trace\n");
445*b1d84af6SChangbin Du 		goto out_reset;
446*b1d84af6SChangbin Du 	}
447*b1d84af6SChangbin Du 
44878b83e8bSNamhyung Kim 	if (set_tracing_filters(ftrace) < 0) {
44978b83e8bSNamhyung Kim 		pr_err("failed to set tracing filters\n");
45078b83e8bSNamhyung Kim 		goto out_reset;
45178b83e8bSNamhyung Kim 	}
45278b83e8bSNamhyung Kim 
4531096c35aSNamhyung Kim 	if (set_tracing_depth(ftrace) < 0) {
4541096c35aSNamhyung Kim 		pr_err("failed to set graph depth\n");
4551096c35aSNamhyung Kim 		goto out_reset;
4561096c35aSNamhyung Kim 	}
4571096c35aSNamhyung Kim 
458846e1939SChangbin Du 	if (set_tracing_percpu_buffer_size(ftrace) < 0) {
459846e1939SChangbin Du 		pr_err("failed to set tracing per-cpu buffer size\n");
460846e1939SChangbin Du 		goto out_reset;
461846e1939SChangbin Du 	}
462846e1939SChangbin Du 
4635b347472SChangbin Du 	if (set_tracing_trace_inherit(ftrace) < 0) {
4645b347472SChangbin Du 		pr_err("failed to set tracing option function-fork\n");
4655b347472SChangbin Du 		goto out_reset;
4665b347472SChangbin Du 	}
4675b347472SChangbin Du 
468d01f4e8dSNamhyung Kim 	if (write_tracing_file("current_tracer", ftrace->tracer) < 0) {
469d01f4e8dSNamhyung Kim 		pr_err("failed to set current_tracer to %s\n", ftrace->tracer);
470a9af6be5SNamhyung Kim 		goto out_reset;
471d01f4e8dSNamhyung Kim 	}
472d01f4e8dSNamhyung Kim 
47329681bc5SNamhyung Kim 	setup_pager();
47429681bc5SNamhyung Kim 
475d01f4e8dSNamhyung Kim 	trace_file = get_tracing_file("trace_pipe");
476d01f4e8dSNamhyung Kim 	if (!trace_file) {
477d01f4e8dSNamhyung Kim 		pr_err("failed to open trace_pipe\n");
478a9af6be5SNamhyung Kim 		goto out_reset;
479d01f4e8dSNamhyung Kim 	}
480d01f4e8dSNamhyung Kim 
481d01f4e8dSNamhyung Kim 	trace_fd = open(trace_file, O_RDONLY);
482d01f4e8dSNamhyung Kim 
483d01f4e8dSNamhyung Kim 	put_tracing_file(trace_file);
484d01f4e8dSNamhyung Kim 
485d01f4e8dSNamhyung Kim 	if (trace_fd < 0) {
486d01f4e8dSNamhyung Kim 		pr_err("failed to open trace_pipe\n");
487a9af6be5SNamhyung Kim 		goto out_reset;
488d01f4e8dSNamhyung Kim 	}
489d01f4e8dSNamhyung Kim 
490d01f4e8dSNamhyung Kim 	fcntl(trace_fd, F_SETFL, O_NONBLOCK);
491d01f4e8dSNamhyung Kim 	pollfd.fd = trace_fd;
492d01f4e8dSNamhyung Kim 
49381523c1eSChangbin Du 	/* display column headers */
49481523c1eSChangbin Du 	read_tracing_file_to_stdout("trace");
49581523c1eSChangbin Du 
496d01f4e8dSNamhyung Kim 	if (write_tracing_file("tracing_on", "1") < 0) {
497d01f4e8dSNamhyung Kim 		pr_err("can't enable tracing\n");
498d01f4e8dSNamhyung Kim 		goto out_close_fd;
499d01f4e8dSNamhyung Kim 	}
500d01f4e8dSNamhyung Kim 
501d01f4e8dSNamhyung Kim 	perf_evlist__start_workload(ftrace->evlist);
502d01f4e8dSNamhyung Kim 
503d01f4e8dSNamhyung Kim 	while (!done) {
504d01f4e8dSNamhyung Kim 		if (poll(&pollfd, 1, -1) < 0)
505d01f4e8dSNamhyung Kim 			break;
506d01f4e8dSNamhyung Kim 
507d01f4e8dSNamhyung Kim 		if (pollfd.revents & POLLIN) {
508d01f4e8dSNamhyung Kim 			int n = read(trace_fd, buf, sizeof(buf));
509d01f4e8dSNamhyung Kim 			if (n < 0)
510d01f4e8dSNamhyung Kim 				break;
511d01f4e8dSNamhyung Kim 			if (fwrite(buf, n, 1, stdout) != 1)
512d01f4e8dSNamhyung Kim 				break;
513d01f4e8dSNamhyung Kim 		}
514d01f4e8dSNamhyung Kim 	}
515d01f4e8dSNamhyung Kim 
516d01f4e8dSNamhyung Kim 	write_tracing_file("tracing_on", "0");
517d01f4e8dSNamhyung Kim 
51851a09d8fSChangbin Du 	if (workload_exec_errno) {
51951a09d8fSChangbin Du 		const char *emsg = str_error_r(workload_exec_errno, buf, sizeof(buf));
52051a09d8fSChangbin Du 		/* flush stdout first so below error msg appears at the end. */
52151a09d8fSChangbin Du 		fflush(stdout);
52251a09d8fSChangbin Du 		pr_err("workload failed: %s\n", emsg);
52351a09d8fSChangbin Du 		goto out_close_fd;
52451a09d8fSChangbin Du 	}
52551a09d8fSChangbin Du 
526d01f4e8dSNamhyung Kim 	/* read remaining buffer contents */
527d01f4e8dSNamhyung Kim 	while (true) {
528d01f4e8dSNamhyung Kim 		int n = read(trace_fd, buf, sizeof(buf));
529d01f4e8dSNamhyung Kim 		if (n <= 0)
530d01f4e8dSNamhyung Kim 			break;
531d01f4e8dSNamhyung Kim 		if (fwrite(buf, n, 1, stdout) != 1)
532d01f4e8dSNamhyung Kim 			break;
533d01f4e8dSNamhyung Kim 	}
534d01f4e8dSNamhyung Kim 
535d01f4e8dSNamhyung Kim out_close_fd:
536d01f4e8dSNamhyung Kim 	close(trace_fd);
537a9af6be5SNamhyung Kim out_reset:
538d01f4e8dSNamhyung Kim 	reset_tracing_files(ftrace);
539a9af6be5SNamhyung Kim out:
54051a09d8fSChangbin Du 	return (done && !workload_exec_errno) ? 0 : -1;
541d01f4e8dSNamhyung Kim }
542d01f4e8dSNamhyung Kim 
543b05d1093STaeung Song static int perf_ftrace_config(const char *var, const char *value, void *cb)
544b05d1093STaeung Song {
545b05d1093STaeung Song 	struct perf_ftrace *ftrace = cb;
546b05d1093STaeung Song 
5478e99b6d4SArnaldo Carvalho de Melo 	if (!strstarts(var, "ftrace."))
548b05d1093STaeung Song 		return 0;
549b05d1093STaeung Song 
550b05d1093STaeung Song 	if (strcmp(var, "ftrace.tracer"))
551b05d1093STaeung Song 		return -1;
552b05d1093STaeung Song 
553b05d1093STaeung Song 	if (!strcmp(value, "function_graph") ||
554b05d1093STaeung Song 	    !strcmp(value, "function")) {
555b05d1093STaeung Song 		ftrace->tracer = value;
556b05d1093STaeung Song 		return 0;
557b05d1093STaeung Song 	}
558b05d1093STaeung Song 
559b05d1093STaeung Song 	pr_err("Please select \"function_graph\" (default) or \"function\"\n");
560b05d1093STaeung Song 	return -1;
561b05d1093STaeung Song }
562b05d1093STaeung Song 
56378b83e8bSNamhyung Kim static int parse_filter_func(const struct option *opt, const char *str,
56478b83e8bSNamhyung Kim 			     int unset __maybe_unused)
56578b83e8bSNamhyung Kim {
56678b83e8bSNamhyung Kim 	struct list_head *head = opt->value;
56778b83e8bSNamhyung Kim 	struct filter_entry *entry;
56878b83e8bSNamhyung Kim 
56978b83e8bSNamhyung Kim 	entry = malloc(sizeof(*entry) + strlen(str) + 1);
57078b83e8bSNamhyung Kim 	if (entry == NULL)
57178b83e8bSNamhyung Kim 		return -ENOMEM;
57278b83e8bSNamhyung Kim 
57378b83e8bSNamhyung Kim 	strcpy(entry->name, str);
57478b83e8bSNamhyung Kim 	list_add_tail(&entry->list, head);
57578b83e8bSNamhyung Kim 
57678b83e8bSNamhyung Kim 	return 0;
57778b83e8bSNamhyung Kim }
57878b83e8bSNamhyung Kim 
57978b83e8bSNamhyung Kim static void delete_filter_func(struct list_head *head)
58078b83e8bSNamhyung Kim {
58178b83e8bSNamhyung Kim 	struct filter_entry *pos, *tmp;
58278b83e8bSNamhyung Kim 
58378b83e8bSNamhyung Kim 	list_for_each_entry_safe(pos, tmp, head, list) {
584e56fbc9dSArnaldo Carvalho de Melo 		list_del_init(&pos->list);
58578b83e8bSNamhyung Kim 		free(pos);
58678b83e8bSNamhyung Kim 	}
58778b83e8bSNamhyung Kim }
58878b83e8bSNamhyung Kim 
589846e1939SChangbin Du static int parse_buffer_size(const struct option *opt,
590846e1939SChangbin Du 			     const char *str, int unset)
591846e1939SChangbin Du {
592846e1939SChangbin Du 	unsigned long *s = (unsigned long *)opt->value;
593846e1939SChangbin Du 	static struct parse_tag tags_size[] = {
594846e1939SChangbin Du 		{ .tag  = 'B', .mult = 1       },
595846e1939SChangbin Du 		{ .tag  = 'K', .mult = 1 << 10 },
596846e1939SChangbin Du 		{ .tag  = 'M', .mult = 1 << 20 },
597846e1939SChangbin Du 		{ .tag  = 'G', .mult = 1 << 30 },
598846e1939SChangbin Du 		{ .tag  = 0 },
599846e1939SChangbin Du 	};
600846e1939SChangbin Du 	unsigned long val;
601846e1939SChangbin Du 
602846e1939SChangbin Du 	if (unset) {
603846e1939SChangbin Du 		*s = 0;
604846e1939SChangbin Du 		return 0;
605846e1939SChangbin Du 	}
606846e1939SChangbin Du 
607846e1939SChangbin Du 	val = parse_tag_value(str, tags_size);
608846e1939SChangbin Du 	if (val != (unsigned long) -1) {
609846e1939SChangbin Du 		if (val < 1024) {
610846e1939SChangbin Du 			pr_err("buffer size too small, must larger than 1KB.");
611846e1939SChangbin Du 			return -1;
612846e1939SChangbin Du 		}
613846e1939SChangbin Du 		*s = val;
614846e1939SChangbin Du 		return 0;
615846e1939SChangbin Du 	}
616846e1939SChangbin Du 
617846e1939SChangbin Du 	return -1;
618846e1939SChangbin Du }
619846e1939SChangbin Du 
620*b1d84af6SChangbin Du static int parse_func_tracer_opts(const struct option *opt,
621*b1d84af6SChangbin Du 				  const char *str, int unset)
622*b1d84af6SChangbin Du {
623*b1d84af6SChangbin Du 	int ret;
624*b1d84af6SChangbin Du 	struct perf_ftrace *ftrace = (struct perf_ftrace *) opt->value;
625*b1d84af6SChangbin Du 	struct sublevel_option func_tracer_opts[] = {
626*b1d84af6SChangbin Du 		{ .name = "call-graph",	.value_ptr = &ftrace->func_stack_trace },
627*b1d84af6SChangbin Du 		{ .name = NULL, }
628*b1d84af6SChangbin Du 	};
629*b1d84af6SChangbin Du 
630*b1d84af6SChangbin Du 	if (unset)
631*b1d84af6SChangbin Du 		return 0;
632*b1d84af6SChangbin Du 
633*b1d84af6SChangbin Du 	ret = perf_parse_sublevel_options(str, func_tracer_opts);
634*b1d84af6SChangbin Du 	if (ret)
635*b1d84af6SChangbin Du 		return ret;
636*b1d84af6SChangbin Du 
637*b1d84af6SChangbin Du 	return 0;
638*b1d84af6SChangbin Du }
639*b1d84af6SChangbin Du 
640eb6d31aeSChangbin Du static void select_tracer(struct perf_ftrace *ftrace)
641eb6d31aeSChangbin Du {
642eb6d31aeSChangbin Du 	bool graph = !list_empty(&ftrace->graph_funcs) ||
643eb6d31aeSChangbin Du 		     !list_empty(&ftrace->nograph_funcs);
644eb6d31aeSChangbin Du 	bool func = !list_empty(&ftrace->filters) ||
645eb6d31aeSChangbin Du 		    !list_empty(&ftrace->notrace);
646eb6d31aeSChangbin Du 
647eb6d31aeSChangbin Du 	/* The function_graph has priority over function tracer. */
648eb6d31aeSChangbin Du 	if (graph)
649eb6d31aeSChangbin Du 		ftrace->tracer = "function_graph";
650eb6d31aeSChangbin Du 	else if (func)
651eb6d31aeSChangbin Du 		ftrace->tracer = "function";
652eb6d31aeSChangbin Du 	/* Otherwise, the default tracer is used. */
653eb6d31aeSChangbin Du 
654eb6d31aeSChangbin Du 	pr_debug("%s tracer is used\n", ftrace->tracer);
655eb6d31aeSChangbin Du }
656eb6d31aeSChangbin Du 
657b0ad8ea6SArnaldo Carvalho de Melo int cmd_ftrace(int argc, const char **argv)
658d01f4e8dSNamhyung Kim {
659d01f4e8dSNamhyung Kim 	int ret;
660d01f4e8dSNamhyung Kim 	struct perf_ftrace ftrace = {
661bf062bd2STaeung Song 		.tracer = DEFAULT_TRACER,
662d01f4e8dSNamhyung Kim 		.target = { .uid = UINT_MAX, },
663d01f4e8dSNamhyung Kim 	};
664d01f4e8dSNamhyung Kim 	const char * const ftrace_usage[] = {
665a9af6be5SNamhyung Kim 		"perf ftrace [<options>] [<command>]",
666d01f4e8dSNamhyung Kim 		"perf ftrace [<options>] -- <command> [<options>]",
667d01f4e8dSNamhyung Kim 		NULL
668d01f4e8dSNamhyung Kim 	};
669d01f4e8dSNamhyung Kim 	const struct option ftrace_options[] = {
670d01f4e8dSNamhyung Kim 	OPT_STRING('t', "tracer", &ftrace.tracer, "tracer",
671ec347870SArnaldo Carvalho de Melo 		   "tracer to use: function_graph(default) or function"),
672d6d81bfeSChangbin Du 	OPT_BOOLEAN('F', "funcs", &ftrace.list_avail_functions,
673d6d81bfeSChangbin Du 		    "Show available functions to filter"),
674a9af6be5SNamhyung Kim 	OPT_STRING('p', "pid", &ftrace.target.pid, "pid",
675a9af6be5SNamhyung Kim 		   "trace on existing process id"),
676d01f4e8dSNamhyung Kim 	OPT_INCR('v', "verbose", &verbose,
677d01f4e8dSNamhyung Kim 		 "be more verbose"),
678dc231032SNamhyung Kim 	OPT_BOOLEAN('a', "all-cpus", &ftrace.target.system_wide,
679dc231032SNamhyung Kim 		    "system-wide collection from all CPUs"),
680dc231032SNamhyung Kim 	OPT_STRING('C', "cpu", &ftrace.target.cpu_list, "cpu",
681dc231032SNamhyung Kim 		    "list of cpus to monitor"),
68278b83e8bSNamhyung Kim 	OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func",
683eb6d31aeSChangbin Du 		     "trace given functions using function tracer",
684eb6d31aeSChangbin Du 		     parse_filter_func),
68578b83e8bSNamhyung Kim 	OPT_CALLBACK('N', "notrace-funcs", &ftrace.notrace, "func",
68678b83e8bSNamhyung Kim 		     "do not trace given functions", parse_filter_func),
687*b1d84af6SChangbin Du 	OPT_CALLBACK(0, "func-opts", &ftrace, "options",
688*b1d84af6SChangbin Du 		     "function tracer options, available options: call-graph",
689*b1d84af6SChangbin Du 		     parse_func_tracer_opts),
69078b83e8bSNamhyung Kim 	OPT_CALLBACK('G', "graph-funcs", &ftrace.graph_funcs, "func",
691eb6d31aeSChangbin Du 		     "trace given functions using function_graph tracer",
692eb6d31aeSChangbin Du 		     parse_filter_func),
69378b83e8bSNamhyung Kim 	OPT_CALLBACK('g', "nograph-funcs", &ftrace.nograph_funcs, "func",
69478b83e8bSNamhyung Kim 		     "Set nograph filter on given functions", parse_filter_func),
6951096c35aSNamhyung Kim 	OPT_INTEGER('D', "graph-depth", &ftrace.graph_depth,
6961096c35aSNamhyung Kim 		    "Max depth for function graph tracer"),
697846e1939SChangbin Du 	OPT_CALLBACK('m', "buffer-size", &ftrace.percpu_buffer_size, "size",
698846e1939SChangbin Du 		     "size of per cpu buffer", parse_buffer_size),
6995b347472SChangbin Du 	OPT_BOOLEAN(0, "inherit", &ftrace.inherit,
7005b347472SChangbin Du 		    "trace children processes"),
701d01f4e8dSNamhyung Kim 	OPT_END()
702d01f4e8dSNamhyung Kim 	};
703d01f4e8dSNamhyung Kim 
70478b83e8bSNamhyung Kim 	INIT_LIST_HEAD(&ftrace.filters);
70578b83e8bSNamhyung Kim 	INIT_LIST_HEAD(&ftrace.notrace);
70678b83e8bSNamhyung Kim 	INIT_LIST_HEAD(&ftrace.graph_funcs);
70778b83e8bSNamhyung Kim 	INIT_LIST_HEAD(&ftrace.nograph_funcs);
70878b83e8bSNamhyung Kim 
709b05d1093STaeung Song 	ret = perf_config(perf_ftrace_config, &ftrace);
710b05d1093STaeung Song 	if (ret < 0)
711b05d1093STaeung Song 		return -1;
712b05d1093STaeung Song 
713d01f4e8dSNamhyung Kim 	argc = parse_options(argc, argv, ftrace_options, ftrace_usage,
714d01f4e8dSNamhyung Kim 			    PARSE_OPT_STOP_AT_NON_OPTION);
715a9af6be5SNamhyung Kim 	if (!argc && target__none(&ftrace.target))
716452b0d16SChangbin Du 		ftrace.target.system_wide = true;
717d01f4e8dSNamhyung Kim 
718eb6d31aeSChangbin Du 	select_tracer(&ftrace);
719eb6d31aeSChangbin Du 
720a9af6be5SNamhyung Kim 	ret = target__validate(&ftrace.target);
721a9af6be5SNamhyung Kim 	if (ret) {
722a9af6be5SNamhyung Kim 		char errbuf[512];
723a9af6be5SNamhyung Kim 
724a9af6be5SNamhyung Kim 		target__strerror(&ftrace.target, ret, errbuf, 512);
725a9af6be5SNamhyung Kim 		pr_err("%s\n", errbuf);
72678b83e8bSNamhyung Kim 		goto out_delete_filters;
727a9af6be5SNamhyung Kim 	}
728a9af6be5SNamhyung Kim 
7290f98b11cSJiri Olsa 	ftrace.evlist = evlist__new();
73078b83e8bSNamhyung Kim 	if (ftrace.evlist == NULL) {
73178b83e8bSNamhyung Kim 		ret = -ENOMEM;
73278b83e8bSNamhyung Kim 		goto out_delete_filters;
73378b83e8bSNamhyung Kim 	}
734d01f4e8dSNamhyung Kim 
735d01f4e8dSNamhyung Kim 	ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target);
736d01f4e8dSNamhyung Kim 	if (ret < 0)
737d01f4e8dSNamhyung Kim 		goto out_delete_evlist;
738d01f4e8dSNamhyung Kim 
739d01f4e8dSNamhyung Kim 	ret = __cmd_ftrace(&ftrace, argc, argv);
740d01f4e8dSNamhyung Kim 
741d01f4e8dSNamhyung Kim out_delete_evlist:
742c12995a5SJiri Olsa 	evlist__delete(ftrace.evlist);
743d01f4e8dSNamhyung Kim 
74478b83e8bSNamhyung Kim out_delete_filters:
74578b83e8bSNamhyung Kim 	delete_filter_func(&ftrace.filters);
74678b83e8bSNamhyung Kim 	delete_filter_func(&ftrace.notrace);
74778b83e8bSNamhyung Kim 	delete_filter_func(&ftrace.graph_funcs);
74878b83e8bSNamhyung Kim 	delete_filter_func(&ftrace.nograph_funcs);
74978b83e8bSNamhyung Kim 
750d01f4e8dSNamhyung Kim 	return ret;
751d01f4e8dSNamhyung Kim }
752