xref: /linux/tools/perf/builtin-ftrace.c (revision 2ae05fe0a9dfe204a6c83bbe6bd1312b3bb3d301)
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>
60094024aSChangbin Du  * Copyright (c) 2020  Changbin Du <changbin.du@gmail.com>, significant enhancement.
7d01f4e8dSNamhyung Kim  */
8d01f4e8dSNamhyung Kim 
9d01f4e8dSNamhyung Kim #include "builtin.h"
10d01f4e8dSNamhyung Kim 
11a43783aeSArnaldo Carvalho de Melo #include <errno.h>
12d01f4e8dSNamhyung Kim #include <unistd.h>
13d01f4e8dSNamhyung Kim #include <signal.h>
14f2a39fe8SArnaldo Carvalho de Melo #include <stdlib.h>
15a9af6be5SNamhyung Kim #include <fcntl.h>
164208735dSArnaldo Carvalho de Melo #include <poll.h>
17c766f3dfSIgor Lubashev #include <linux/capability.h>
188520a98dSArnaldo Carvalho de Melo #include <linux/string.h>
19d01f4e8dSNamhyung Kim 
20d01f4e8dSNamhyung Kim #include "debug.h"
218520a98dSArnaldo Carvalho de Melo #include <subcmd/pager.h>
22d01f4e8dSNamhyung Kim #include <subcmd/parse-options.h>
2320a9ed28SArnaldo Carvalho de Melo #include <api/fs/tracing_path.h>
24d01f4e8dSNamhyung Kim #include "evlist.h"
25d01f4e8dSNamhyung Kim #include "target.h"
26dc231032SNamhyung Kim #include "cpumap.h"
27d01f4e8dSNamhyung Kim #include "thread_map.h"
28*2ae05fe0SChangbin Du #include "strfilter.h"
29c766f3dfSIgor Lubashev #include "util/cap.h"
30b05d1093STaeung Song #include "util/config.h"
31846e1939SChangbin Du #include "util/units.h"
32b1d84af6SChangbin Du #include "util/parse-sublevel-options.h"
33d01f4e8dSNamhyung Kim 
34d01f4e8dSNamhyung Kim #define DEFAULT_TRACER  "function_graph"
35d01f4e8dSNamhyung Kim 
36d01f4e8dSNamhyung Kim struct perf_ftrace {
3763503dbaSJiri Olsa 	struct evlist		*evlist;
38d01f4e8dSNamhyung Kim 	struct target		target;
39d01f4e8dSNamhyung Kim 	const char		*tracer;
4078b83e8bSNamhyung Kim 	struct list_head	filters;
4178b83e8bSNamhyung Kim 	struct list_head	notrace;
4278b83e8bSNamhyung Kim 	struct list_head	graph_funcs;
4378b83e8bSNamhyung Kim 	struct list_head	nograph_funcs;
441096c35aSNamhyung Kim 	int			graph_depth;
45846e1939SChangbin Du 	unsigned long		percpu_buffer_size;
465b347472SChangbin Du 	bool			inherit;
47b1d84af6SChangbin Du 	int			func_stack_trace;
48c81fc34eSChangbin Du 	int			func_irq_info;
4938988f2eSChangbin Du 	int			graph_nosleep_time;
50d1bcf17cSChangbin Du 	int			graph_noirqs;
5159486fb0SChangbin Du 	int			graph_verbose;
5200c85d5fSChangbin Du 	int			graph_thresh;
536555c2f6SChangbin Du 	unsigned int		initial_delay;
5478b83e8bSNamhyung Kim };
5578b83e8bSNamhyung Kim 
5678b83e8bSNamhyung Kim struct filter_entry {
5778b83e8bSNamhyung Kim 	struct list_head	list;
5878b83e8bSNamhyung Kim 	char			name[];
59d01f4e8dSNamhyung Kim };
60d01f4e8dSNamhyung Kim 
6151a09d8fSChangbin Du static volatile int workload_exec_errno;
62d01f4e8dSNamhyung Kim static bool done;
63d01f4e8dSNamhyung Kim 
64d01f4e8dSNamhyung Kim static void sig_handler(int sig __maybe_unused)
65d01f4e8dSNamhyung Kim {
66d01f4e8dSNamhyung Kim 	done = true;
67d01f4e8dSNamhyung Kim }
68d01f4e8dSNamhyung Kim 
69d01f4e8dSNamhyung Kim /*
70d01f4e8dSNamhyung Kim  * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since
71d01f4e8dSNamhyung Kim  * we asked by setting its exec_error to the function below,
72d01f4e8dSNamhyung Kim  * ftrace__workload_exec_failed_signal.
73d01f4e8dSNamhyung Kim  *
74d01f4e8dSNamhyung Kim  * XXX We need to handle this more appropriately, emitting an error, etc.
75d01f4e8dSNamhyung Kim  */
76d01f4e8dSNamhyung Kim static void ftrace__workload_exec_failed_signal(int signo __maybe_unused,
77d01f4e8dSNamhyung Kim 						siginfo_t *info __maybe_unused,
78d01f4e8dSNamhyung Kim 						void *ucontext __maybe_unused)
79d01f4e8dSNamhyung Kim {
8051a09d8fSChangbin Du 	workload_exec_errno = info->si_value.sival_int;
81d01f4e8dSNamhyung Kim 	done = true;
82d01f4e8dSNamhyung Kim }
83d01f4e8dSNamhyung Kim 
84a9af6be5SNamhyung Kim static int __write_tracing_file(const char *name, const char *val, bool append)
85d01f4e8dSNamhyung Kim {
86d01f4e8dSNamhyung Kim 	char *file;
87d01f4e8dSNamhyung Kim 	int fd, ret = -1;
88d01f4e8dSNamhyung Kim 	ssize_t size = strlen(val);
89a9af6be5SNamhyung Kim 	int flags = O_WRONLY;
90e7bd9ba2SNamhyung Kim 	char errbuf[512];
9163cd02d8SChangbin Du 	char *val_copy;
92d01f4e8dSNamhyung Kim 
93d01f4e8dSNamhyung Kim 	file = get_tracing_file(name);
94d01f4e8dSNamhyung Kim 	if (!file) {
95d01f4e8dSNamhyung Kim 		pr_debug("cannot get tracing file: %s\n", name);
96d01f4e8dSNamhyung Kim 		return -1;
97d01f4e8dSNamhyung Kim 	}
98d01f4e8dSNamhyung Kim 
99a9af6be5SNamhyung Kim 	if (append)
100a9af6be5SNamhyung Kim 		flags |= O_APPEND;
101a9af6be5SNamhyung Kim 	else
102a9af6be5SNamhyung Kim 		flags |= O_TRUNC;
103a9af6be5SNamhyung Kim 
104a9af6be5SNamhyung Kim 	fd = open(file, flags);
105d01f4e8dSNamhyung Kim 	if (fd < 0) {
106e7bd9ba2SNamhyung Kim 		pr_debug("cannot open tracing file: %s: %s\n",
107e7bd9ba2SNamhyung Kim 			 name, str_error_r(errno, errbuf, sizeof(errbuf)));
108d01f4e8dSNamhyung Kim 		goto out;
109d01f4e8dSNamhyung Kim 	}
110d01f4e8dSNamhyung Kim 
11163cd02d8SChangbin Du 	/*
11263cd02d8SChangbin Du 	 * Copy the original value and append a '\n'. Without this,
11363cd02d8SChangbin Du 	 * the kernel can hide possible errors.
11463cd02d8SChangbin Du 	 */
11563cd02d8SChangbin Du 	val_copy = strdup(val);
11663cd02d8SChangbin Du 	if (!val_copy)
11763cd02d8SChangbin Du 		goto out_close;
11863cd02d8SChangbin Du 	val_copy[size] = '\n';
11963cd02d8SChangbin Du 
12063cd02d8SChangbin Du 	if (write(fd, val_copy, size + 1) == size + 1)
121d01f4e8dSNamhyung Kim 		ret = 0;
122d01f4e8dSNamhyung Kim 	else
123e7bd9ba2SNamhyung Kim 		pr_debug("write '%s' to tracing/%s failed: %s\n",
124e7bd9ba2SNamhyung Kim 			 val, name, str_error_r(errno, errbuf, sizeof(errbuf)));
125d01f4e8dSNamhyung Kim 
12663cd02d8SChangbin Du 	free(val_copy);
12763cd02d8SChangbin Du out_close:
128d01f4e8dSNamhyung Kim 	close(fd);
129d01f4e8dSNamhyung Kim out:
130d01f4e8dSNamhyung Kim 	put_tracing_file(file);
131d01f4e8dSNamhyung Kim 	return ret;
132d01f4e8dSNamhyung Kim }
133d01f4e8dSNamhyung Kim 
134a9af6be5SNamhyung Kim static int write_tracing_file(const char *name, const char *val)
135a9af6be5SNamhyung Kim {
136a9af6be5SNamhyung Kim 	return __write_tracing_file(name, val, false);
137a9af6be5SNamhyung Kim }
138a9af6be5SNamhyung Kim 
139a9af6be5SNamhyung Kim static int append_tracing_file(const char *name, const char *val)
140a9af6be5SNamhyung Kim {
141a9af6be5SNamhyung Kim 	return __write_tracing_file(name, val, true);
142a9af6be5SNamhyung Kim }
143a9af6be5SNamhyung Kim 
144d6d81bfeSChangbin Du static int read_tracing_file_to_stdout(const char *name)
145d6d81bfeSChangbin Du {
146d6d81bfeSChangbin Du 	char buf[4096];
147d6d81bfeSChangbin Du 	char *file;
148d6d81bfeSChangbin Du 	int fd;
149d6d81bfeSChangbin Du 	int ret = -1;
150d6d81bfeSChangbin Du 
151d6d81bfeSChangbin Du 	file = get_tracing_file(name);
152d6d81bfeSChangbin Du 	if (!file) {
153d6d81bfeSChangbin Du 		pr_debug("cannot get tracing file: %s\n", name);
154d6d81bfeSChangbin Du 		return -1;
155d6d81bfeSChangbin Du 	}
156d6d81bfeSChangbin Du 
157d6d81bfeSChangbin Du 	fd = open(file, O_RDONLY);
158d6d81bfeSChangbin Du 	if (fd < 0) {
159d6d81bfeSChangbin Du 		pr_debug("cannot open tracing file: %s: %s\n",
160d6d81bfeSChangbin Du 			 name, str_error_r(errno, buf, sizeof(buf)));
161d6d81bfeSChangbin Du 		goto out;
162d6d81bfeSChangbin Du 	}
163d6d81bfeSChangbin Du 
164d6d81bfeSChangbin Du 	/* read contents to stdout */
165d6d81bfeSChangbin Du 	while (true) {
166d6d81bfeSChangbin Du 		int n = read(fd, buf, sizeof(buf));
167d6d81bfeSChangbin Du 		if (n == 0)
168d6d81bfeSChangbin Du 			break;
169d6d81bfeSChangbin Du 		else if (n < 0)
170d6d81bfeSChangbin Du 			goto out_close;
171d6d81bfeSChangbin Du 
172d6d81bfeSChangbin Du 		if (fwrite(buf, n, 1, stdout) != 1)
173d6d81bfeSChangbin Du 			goto out_close;
174d6d81bfeSChangbin Du 	}
175d6d81bfeSChangbin Du 	ret = 0;
176d6d81bfeSChangbin Du 
177d6d81bfeSChangbin Du out_close:
178d6d81bfeSChangbin Du 	close(fd);
179d6d81bfeSChangbin Du out:
180d6d81bfeSChangbin Du 	put_tracing_file(file);
181d6d81bfeSChangbin Du 	return ret;
182d6d81bfeSChangbin Du }
183d6d81bfeSChangbin Du 
184*2ae05fe0SChangbin Du static int read_tracing_file_by_line(const char *name,
185*2ae05fe0SChangbin Du 				     void (*cb)(char *str, void *arg),
186*2ae05fe0SChangbin Du 				     void *cb_arg)
187*2ae05fe0SChangbin Du {
188*2ae05fe0SChangbin Du 	char *line = NULL;
189*2ae05fe0SChangbin Du 	size_t len = 0;
190*2ae05fe0SChangbin Du 	char *file;
191*2ae05fe0SChangbin Du 	FILE *fp;
192*2ae05fe0SChangbin Du 
193*2ae05fe0SChangbin Du 	file = get_tracing_file(name);
194*2ae05fe0SChangbin Du 	if (!file) {
195*2ae05fe0SChangbin Du 		pr_debug("cannot get tracing file: %s\n", name);
196*2ae05fe0SChangbin Du 		return -1;
197*2ae05fe0SChangbin Du 	}
198*2ae05fe0SChangbin Du 
199*2ae05fe0SChangbin Du 	fp = fopen(file, "r");
200*2ae05fe0SChangbin Du 	if (fp == NULL) {
201*2ae05fe0SChangbin Du 		pr_debug("cannot open tracing file: %s\n", name);
202*2ae05fe0SChangbin Du 		put_tracing_file(file);
203*2ae05fe0SChangbin Du 		return -1;
204*2ae05fe0SChangbin Du 	}
205*2ae05fe0SChangbin Du 
206*2ae05fe0SChangbin Du 	while (getline(&line, &len, fp) != -1) {
207*2ae05fe0SChangbin Du 		cb(line, cb_arg);
208*2ae05fe0SChangbin Du 	}
209*2ae05fe0SChangbin Du 
210*2ae05fe0SChangbin Du 	if (line)
211*2ae05fe0SChangbin Du 		free(line);
212*2ae05fe0SChangbin Du 
213*2ae05fe0SChangbin Du 	fclose(fp);
214*2ae05fe0SChangbin Du 	put_tracing_file(file);
215*2ae05fe0SChangbin Du 	return 0;
216*2ae05fe0SChangbin Du }
217*2ae05fe0SChangbin Du 
21868faab0fSChangbin Du static int write_tracing_file_int(const char *name, int value)
21968faab0fSChangbin Du {
22068faab0fSChangbin Du 	char buf[16];
22168faab0fSChangbin Du 
22268faab0fSChangbin Du 	snprintf(buf, sizeof(buf), "%d", value);
22368faab0fSChangbin Du 	if (write_tracing_file(name, buf) < 0)
22468faab0fSChangbin Du 		return -1;
22568faab0fSChangbin Du 
22668faab0fSChangbin Du 	return 0;
22768faab0fSChangbin Du }
22868faab0fSChangbin Du 
2295b347472SChangbin Du static int write_tracing_option_file(const char *name, const char *val)
2305b347472SChangbin Du {
2315b347472SChangbin Du 	char *file;
2325b347472SChangbin Du 	int ret;
2335b347472SChangbin Du 
2345b347472SChangbin Du 	if (asprintf(&file, "options/%s", name) < 0)
2355b347472SChangbin Du 		return -1;
2365b347472SChangbin Du 
2375b347472SChangbin Du 	ret = __write_tracing_file(file, val, false);
2385b347472SChangbin Du 	free(file);
2395b347472SChangbin Du 	return ret;
2405b347472SChangbin Du }
2415b347472SChangbin Du 
242dc231032SNamhyung Kim static int reset_tracing_cpu(void);
24378b83e8bSNamhyung Kim static void reset_tracing_filters(void);
244dc231032SNamhyung Kim 
2455b347472SChangbin Du static void reset_tracing_options(struct perf_ftrace *ftrace __maybe_unused)
2465b347472SChangbin Du {
2475b347472SChangbin Du 	write_tracing_option_file("function-fork", "0");
248b1d84af6SChangbin Du 	write_tracing_option_file("func_stack_trace", "0");
24938988f2eSChangbin Du 	write_tracing_option_file("sleep-time", "1");
250d1bcf17cSChangbin Du 	write_tracing_option_file("funcgraph-irqs", "1");
25159486fb0SChangbin Du 	write_tracing_option_file("funcgraph-proc", "0");
25259486fb0SChangbin Du 	write_tracing_option_file("funcgraph-abstime", "0");
25359486fb0SChangbin Du 	write_tracing_option_file("latency-format", "0");
254c81fc34eSChangbin Du 	write_tracing_option_file("irq-info", "0");
2555b347472SChangbin Du }
2565b347472SChangbin Du 
257d01f4e8dSNamhyung Kim static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused)
258d01f4e8dSNamhyung Kim {
259d01f4e8dSNamhyung Kim 	if (write_tracing_file("tracing_on", "0") < 0)
260d01f4e8dSNamhyung Kim 		return -1;
261d01f4e8dSNamhyung Kim 
262d01f4e8dSNamhyung Kim 	if (write_tracing_file("current_tracer", "nop") < 0)
263d01f4e8dSNamhyung Kim 		return -1;
264d01f4e8dSNamhyung Kim 
265d01f4e8dSNamhyung Kim 	if (write_tracing_file("set_ftrace_pid", " ") < 0)
266d01f4e8dSNamhyung Kim 		return -1;
267d01f4e8dSNamhyung Kim 
268dc231032SNamhyung Kim 	if (reset_tracing_cpu() < 0)
269dc231032SNamhyung Kim 		return -1;
270dc231032SNamhyung Kim 
2711096c35aSNamhyung Kim 	if (write_tracing_file("max_graph_depth", "0") < 0)
2721096c35aSNamhyung Kim 		return -1;
2731096c35aSNamhyung Kim 
27400c85d5fSChangbin Du 	if (write_tracing_file("tracing_thresh", "0") < 0)
27500c85d5fSChangbin Du 		return -1;
27600c85d5fSChangbin Du 
27778b83e8bSNamhyung Kim 	reset_tracing_filters();
2785b347472SChangbin Du 	reset_tracing_options(ftrace);
279d01f4e8dSNamhyung Kim 	return 0;
280d01f4e8dSNamhyung Kim }
281d01f4e8dSNamhyung Kim 
282a9af6be5SNamhyung Kim static int set_tracing_pid(struct perf_ftrace *ftrace)
283a9af6be5SNamhyung Kim {
284a9af6be5SNamhyung Kim 	int i;
285a9af6be5SNamhyung Kim 	char buf[16];
286a9af6be5SNamhyung Kim 
287a9af6be5SNamhyung Kim 	if (target__has_cpu(&ftrace->target))
288a9af6be5SNamhyung Kim 		return 0;
289a9af6be5SNamhyung Kim 
290a2f354e3SJiri Olsa 	for (i = 0; i < perf_thread_map__nr(ftrace->evlist->core.threads); i++) {
291a9af6be5SNamhyung Kim 		scnprintf(buf, sizeof(buf), "%d",
29203617c22SJiri Olsa 			  ftrace->evlist->core.threads->map[i]);
293a9af6be5SNamhyung Kim 		if (append_tracing_file("set_ftrace_pid", buf) < 0)
294a9af6be5SNamhyung Kim 			return -1;
295a9af6be5SNamhyung Kim 	}
296a9af6be5SNamhyung Kim 	return 0;
297a9af6be5SNamhyung Kim }
298a9af6be5SNamhyung Kim 
299f854839bSJiri Olsa static int set_tracing_cpumask(struct perf_cpu_map *cpumap)
300dc231032SNamhyung Kim {
301dc231032SNamhyung Kim 	char *cpumask;
302dc231032SNamhyung Kim 	size_t mask_size;
303dc231032SNamhyung Kim 	int ret;
304dc231032SNamhyung Kim 	int last_cpu;
305dc231032SNamhyung Kim 
306dc231032SNamhyung Kim 	last_cpu = cpu_map__cpu(cpumap, cpumap->nr - 1);
307cf30ae72SHe Zhe 	mask_size = last_cpu / 4 + 2; /* one more byte for EOS */
308dc231032SNamhyung Kim 	mask_size += last_cpu / 32; /* ',' is needed for every 32th cpus */
309dc231032SNamhyung Kim 
310dc231032SNamhyung Kim 	cpumask = malloc(mask_size);
311dc231032SNamhyung Kim 	if (cpumask == NULL) {
312dc231032SNamhyung Kim 		pr_debug("failed to allocate cpu mask\n");
313dc231032SNamhyung Kim 		return -1;
314dc231032SNamhyung Kim 	}
315dc231032SNamhyung Kim 
316dc231032SNamhyung Kim 	cpu_map__snprint_mask(cpumap, cpumask, mask_size);
317dc231032SNamhyung Kim 
318dc231032SNamhyung Kim 	ret = write_tracing_file("tracing_cpumask", cpumask);
319dc231032SNamhyung Kim 
320dc231032SNamhyung Kim 	free(cpumask);
321dc231032SNamhyung Kim 	return ret;
322dc231032SNamhyung Kim }
323dc231032SNamhyung Kim 
324dc231032SNamhyung Kim static int set_tracing_cpu(struct perf_ftrace *ftrace)
325dc231032SNamhyung Kim {
326f72f901dSJiri Olsa 	struct perf_cpu_map *cpumap = ftrace->evlist->core.cpus;
327dc231032SNamhyung Kim 
328dc231032SNamhyung Kim 	if (!target__has_cpu(&ftrace->target))
329dc231032SNamhyung Kim 		return 0;
330dc231032SNamhyung Kim 
331dc231032SNamhyung Kim 	return set_tracing_cpumask(cpumap);
332dc231032SNamhyung Kim }
333dc231032SNamhyung Kim 
334b1d84af6SChangbin Du static int set_tracing_func_stack_trace(struct perf_ftrace *ftrace)
335b1d84af6SChangbin Du {
336b1d84af6SChangbin Du 	if (!ftrace->func_stack_trace)
337b1d84af6SChangbin Du 		return 0;
338b1d84af6SChangbin Du 
339b1d84af6SChangbin Du 	if (write_tracing_option_file("func_stack_trace", "1") < 0)
340b1d84af6SChangbin Du 		return -1;
341b1d84af6SChangbin Du 
342b1d84af6SChangbin Du 	return 0;
343b1d84af6SChangbin Du }
344b1d84af6SChangbin Du 
345c81fc34eSChangbin Du static int set_tracing_func_irqinfo(struct perf_ftrace *ftrace)
346c81fc34eSChangbin Du {
347c81fc34eSChangbin Du 	if (!ftrace->func_irq_info)
348c81fc34eSChangbin Du 		return 0;
349c81fc34eSChangbin Du 
350c81fc34eSChangbin Du 	if (write_tracing_option_file("irq-info", "1") < 0)
351c81fc34eSChangbin Du 		return -1;
352c81fc34eSChangbin Du 
353c81fc34eSChangbin Du 	return 0;
354c81fc34eSChangbin Du }
355c81fc34eSChangbin Du 
356dc231032SNamhyung Kim static int reset_tracing_cpu(void)
357dc231032SNamhyung Kim {
3589c3516d1SJiri Olsa 	struct perf_cpu_map *cpumap = perf_cpu_map__new(NULL);
359dc231032SNamhyung Kim 	int ret;
360dc231032SNamhyung Kim 
361dc231032SNamhyung Kim 	ret = set_tracing_cpumask(cpumap);
36238f01d8dSJiri Olsa 	perf_cpu_map__put(cpumap);
363dc231032SNamhyung Kim 	return ret;
364dc231032SNamhyung Kim }
365dc231032SNamhyung Kim 
36678b83e8bSNamhyung Kim static int __set_tracing_filter(const char *filter_file, struct list_head *funcs)
36778b83e8bSNamhyung Kim {
36878b83e8bSNamhyung Kim 	struct filter_entry *pos;
36978b83e8bSNamhyung Kim 
37078b83e8bSNamhyung Kim 	list_for_each_entry(pos, funcs, list) {
37178b83e8bSNamhyung Kim 		if (append_tracing_file(filter_file, pos->name) < 0)
37278b83e8bSNamhyung Kim 			return -1;
37378b83e8bSNamhyung Kim 	}
37478b83e8bSNamhyung Kim 
37578b83e8bSNamhyung Kim 	return 0;
37678b83e8bSNamhyung Kim }
37778b83e8bSNamhyung Kim 
37878b83e8bSNamhyung Kim static int set_tracing_filters(struct perf_ftrace *ftrace)
37978b83e8bSNamhyung Kim {
38078b83e8bSNamhyung Kim 	int ret;
38178b83e8bSNamhyung Kim 
38278b83e8bSNamhyung Kim 	ret = __set_tracing_filter("set_ftrace_filter", &ftrace->filters);
38378b83e8bSNamhyung Kim 	if (ret < 0)
38478b83e8bSNamhyung Kim 		return ret;
38578b83e8bSNamhyung Kim 
38678b83e8bSNamhyung Kim 	ret = __set_tracing_filter("set_ftrace_notrace", &ftrace->notrace);
38778b83e8bSNamhyung Kim 	if (ret < 0)
38878b83e8bSNamhyung Kim 		return ret;
38978b83e8bSNamhyung Kim 
39078b83e8bSNamhyung Kim 	ret = __set_tracing_filter("set_graph_function", &ftrace->graph_funcs);
39178b83e8bSNamhyung Kim 	if (ret < 0)
39278b83e8bSNamhyung Kim 		return ret;
39378b83e8bSNamhyung Kim 
39478b83e8bSNamhyung Kim 	/* old kernels do not have this filter */
39578b83e8bSNamhyung Kim 	__set_tracing_filter("set_graph_notrace", &ftrace->nograph_funcs);
39678b83e8bSNamhyung Kim 
39778b83e8bSNamhyung Kim 	return ret;
39878b83e8bSNamhyung Kim }
39978b83e8bSNamhyung Kim 
40078b83e8bSNamhyung Kim static void reset_tracing_filters(void)
40178b83e8bSNamhyung Kim {
40278b83e8bSNamhyung Kim 	write_tracing_file("set_ftrace_filter", " ");
40378b83e8bSNamhyung Kim 	write_tracing_file("set_ftrace_notrace", " ");
40478b83e8bSNamhyung Kim 	write_tracing_file("set_graph_function", " ");
40578b83e8bSNamhyung Kim 	write_tracing_file("set_graph_notrace", " ");
40678b83e8bSNamhyung Kim }
40778b83e8bSNamhyung Kim 
4081096c35aSNamhyung Kim static int set_tracing_depth(struct perf_ftrace *ftrace)
4091096c35aSNamhyung Kim {
4101096c35aSNamhyung Kim 	if (ftrace->graph_depth == 0)
4111096c35aSNamhyung Kim 		return 0;
4121096c35aSNamhyung Kim 
4131096c35aSNamhyung Kim 	if (ftrace->graph_depth < 0) {
4141096c35aSNamhyung Kim 		pr_err("invalid graph depth: %d\n", ftrace->graph_depth);
4151096c35aSNamhyung Kim 		return -1;
4161096c35aSNamhyung Kim 	}
4171096c35aSNamhyung Kim 
41868faab0fSChangbin Du 	if (write_tracing_file_int("max_graph_depth", ftrace->graph_depth) < 0)
4191096c35aSNamhyung Kim 		return -1;
4201096c35aSNamhyung Kim 
4211096c35aSNamhyung Kim 	return 0;
4221096c35aSNamhyung Kim }
4231096c35aSNamhyung Kim 
424846e1939SChangbin Du static int set_tracing_percpu_buffer_size(struct perf_ftrace *ftrace)
425846e1939SChangbin Du {
426846e1939SChangbin Du 	int ret;
427846e1939SChangbin Du 
428846e1939SChangbin Du 	if (ftrace->percpu_buffer_size == 0)
429846e1939SChangbin Du 		return 0;
430846e1939SChangbin Du 
431846e1939SChangbin Du 	ret = write_tracing_file_int("buffer_size_kb",
432846e1939SChangbin Du 				     ftrace->percpu_buffer_size / 1024);
433846e1939SChangbin Du 	if (ret < 0)
434846e1939SChangbin Du 		return ret;
435846e1939SChangbin Du 
436846e1939SChangbin Du 	return 0;
437846e1939SChangbin Du }
438846e1939SChangbin Du 
4395b347472SChangbin Du static int set_tracing_trace_inherit(struct perf_ftrace *ftrace)
4405b347472SChangbin Du {
4415b347472SChangbin Du 	if (!ftrace->inherit)
4425b347472SChangbin Du 		return 0;
4435b347472SChangbin Du 
4445b347472SChangbin Du 	if (write_tracing_option_file("function-fork", "1") < 0)
4455b347472SChangbin Du 		return -1;
4465b347472SChangbin Du 
4475b347472SChangbin Du 	return 0;
4485b347472SChangbin Du }
4495b347472SChangbin Du 
45038988f2eSChangbin Du static int set_tracing_sleep_time(struct perf_ftrace *ftrace)
45138988f2eSChangbin Du {
45238988f2eSChangbin Du 	if (!ftrace->graph_nosleep_time)
45338988f2eSChangbin Du 		return 0;
45438988f2eSChangbin Du 
45538988f2eSChangbin Du 	if (write_tracing_option_file("sleep-time", "0") < 0)
45638988f2eSChangbin Du 		return -1;
45738988f2eSChangbin Du 
45838988f2eSChangbin Du 	return 0;
45938988f2eSChangbin Du }
46038988f2eSChangbin Du 
461d1bcf17cSChangbin Du static int set_tracing_funcgraph_irqs(struct perf_ftrace *ftrace)
462d1bcf17cSChangbin Du {
463d1bcf17cSChangbin Du 	if (!ftrace->graph_noirqs)
464d1bcf17cSChangbin Du 		return 0;
465d1bcf17cSChangbin Du 
466d1bcf17cSChangbin Du 	if (write_tracing_option_file("funcgraph-irqs", "0") < 0)
467d1bcf17cSChangbin Du 		return -1;
468d1bcf17cSChangbin Du 
469d1bcf17cSChangbin Du 	return 0;
470d1bcf17cSChangbin Du }
471d1bcf17cSChangbin Du 
47259486fb0SChangbin Du static int set_tracing_funcgraph_verbose(struct perf_ftrace *ftrace)
47359486fb0SChangbin Du {
47459486fb0SChangbin Du 	if (!ftrace->graph_verbose)
47559486fb0SChangbin Du 		return 0;
47659486fb0SChangbin Du 
47759486fb0SChangbin Du 	if (write_tracing_option_file("funcgraph-proc", "1") < 0)
47859486fb0SChangbin Du 		return -1;
47959486fb0SChangbin Du 
48059486fb0SChangbin Du 	if (write_tracing_option_file("funcgraph-abstime", "1") < 0)
48159486fb0SChangbin Du 		return -1;
48259486fb0SChangbin Du 
48359486fb0SChangbin Du 	if (write_tracing_option_file("latency-format", "1") < 0)
48459486fb0SChangbin Du 		return -1;
48559486fb0SChangbin Du 
48659486fb0SChangbin Du 	return 0;
48759486fb0SChangbin Du }
48859486fb0SChangbin Du 
48900c85d5fSChangbin Du static int set_tracing_thresh(struct perf_ftrace *ftrace)
49000c85d5fSChangbin Du {
49100c85d5fSChangbin Du 	int ret;
49200c85d5fSChangbin Du 
49300c85d5fSChangbin Du 	if (ftrace->graph_thresh == 0)
49400c85d5fSChangbin Du 		return 0;
49500c85d5fSChangbin Du 
49600c85d5fSChangbin Du 	ret = write_tracing_file_int("tracing_thresh", ftrace->graph_thresh);
49700c85d5fSChangbin Du 	if (ret < 0)
49800c85d5fSChangbin Du 		return ret;
49900c85d5fSChangbin Du 
50000c85d5fSChangbin Du 	return 0;
50100c85d5fSChangbin Du }
50200c85d5fSChangbin Du 
5033c4dc21bSChangbin Du static int set_tracing_options(struct perf_ftrace *ftrace)
5043c4dc21bSChangbin Du {
5053c4dc21bSChangbin Du 	if (set_tracing_pid(ftrace) < 0) {
5063c4dc21bSChangbin Du 		pr_err("failed to set ftrace pid\n");
5073c4dc21bSChangbin Du 		return -1;
5083c4dc21bSChangbin Du 	}
5093c4dc21bSChangbin Du 
5103c4dc21bSChangbin Du 	if (set_tracing_cpu(ftrace) < 0) {
5113c4dc21bSChangbin Du 		pr_err("failed to set tracing cpumask\n");
5123c4dc21bSChangbin Du 		return -1;
5133c4dc21bSChangbin Du 	}
5143c4dc21bSChangbin Du 
5153c4dc21bSChangbin Du 	if (set_tracing_func_stack_trace(ftrace) < 0) {
5163c4dc21bSChangbin Du 		pr_err("failed to set tracing option func_stack_trace\n");
5173c4dc21bSChangbin Du 		return -1;
5183c4dc21bSChangbin Du 	}
5193c4dc21bSChangbin Du 
5203c4dc21bSChangbin Du 	if (set_tracing_func_irqinfo(ftrace) < 0) {
5213c4dc21bSChangbin Du 		pr_err("failed to set tracing option irq-info\n");
5223c4dc21bSChangbin Du 		return -1;
5233c4dc21bSChangbin Du 	}
5243c4dc21bSChangbin Du 
5253c4dc21bSChangbin Du 	if (set_tracing_filters(ftrace) < 0) {
5263c4dc21bSChangbin Du 		pr_err("failed to set tracing filters\n");
5273c4dc21bSChangbin Du 		return -1;
5283c4dc21bSChangbin Du 	}
5293c4dc21bSChangbin Du 
5303c4dc21bSChangbin Du 	if (set_tracing_depth(ftrace) < 0) {
5313c4dc21bSChangbin Du 		pr_err("failed to set graph depth\n");
5323c4dc21bSChangbin Du 		return -1;
5333c4dc21bSChangbin Du 	}
5343c4dc21bSChangbin Du 
5353c4dc21bSChangbin Du 	if (set_tracing_percpu_buffer_size(ftrace) < 0) {
5363c4dc21bSChangbin Du 		pr_err("failed to set tracing per-cpu buffer size\n");
5373c4dc21bSChangbin Du 		return -1;
5383c4dc21bSChangbin Du 	}
5393c4dc21bSChangbin Du 
5403c4dc21bSChangbin Du 	if (set_tracing_trace_inherit(ftrace) < 0) {
5413c4dc21bSChangbin Du 		pr_err("failed to set tracing option function-fork\n");
5423c4dc21bSChangbin Du 		return -1;
5433c4dc21bSChangbin Du 	}
5443c4dc21bSChangbin Du 
5453c4dc21bSChangbin Du 	if (set_tracing_sleep_time(ftrace) < 0) {
5463c4dc21bSChangbin Du 		pr_err("failed to set tracing option sleep-time\n");
5473c4dc21bSChangbin Du 		return -1;
5483c4dc21bSChangbin Du 	}
5493c4dc21bSChangbin Du 
5503c4dc21bSChangbin Du 	if (set_tracing_funcgraph_irqs(ftrace) < 0) {
5513c4dc21bSChangbin Du 		pr_err("failed to set tracing option funcgraph-irqs\n");
5523c4dc21bSChangbin Du 		return -1;
5533c4dc21bSChangbin Du 	}
5543c4dc21bSChangbin Du 
5553c4dc21bSChangbin Du 	if (set_tracing_funcgraph_verbose(ftrace) < 0) {
5563c4dc21bSChangbin Du 		pr_err("failed to set tracing option funcgraph-proc/funcgraph-abstime\n");
5573c4dc21bSChangbin Du 		return -1;
5583c4dc21bSChangbin Du 	}
5593c4dc21bSChangbin Du 
5603c4dc21bSChangbin Du 	if (set_tracing_thresh(ftrace) < 0) {
5613c4dc21bSChangbin Du 		pr_err("failed to set tracing thresh\n");
5623c4dc21bSChangbin Du 		return -1;
5633c4dc21bSChangbin Du 	}
5643c4dc21bSChangbin Du 
5653c4dc21bSChangbin Du 	return 0;
5663c4dc21bSChangbin Du }
5673c4dc21bSChangbin Du 
568d01f4e8dSNamhyung Kim static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
569d01f4e8dSNamhyung Kim {
570d01f4e8dSNamhyung Kim 	char *trace_file;
571d01f4e8dSNamhyung Kim 	int trace_fd;
572d01f4e8dSNamhyung Kim 	char buf[4096];
573d01f4e8dSNamhyung Kim 	struct pollfd pollfd = {
574d01f4e8dSNamhyung Kim 		.events = POLLIN,
575d01f4e8dSNamhyung Kim 	};
576d01f4e8dSNamhyung Kim 
5776b3e0e2eSAlexey Budankov 	if (!(perf_cap__capable(CAP_PERFMON) ||
5786b3e0e2eSAlexey Budankov 	      perf_cap__capable(CAP_SYS_ADMIN))) {
57973e5de70SArnaldo Carvalho de Melo 		pr_err("ftrace only works for %s!\n",
58073e5de70SArnaldo Carvalho de Melo #ifdef HAVE_LIBCAP_SUPPORT
5816b3e0e2eSAlexey Budankov 		"users with the CAP_PERFMON or CAP_SYS_ADMIN capability"
58273e5de70SArnaldo Carvalho de Melo #else
58373e5de70SArnaldo Carvalho de Melo 		"root"
58473e5de70SArnaldo Carvalho de Melo #endif
58573e5de70SArnaldo Carvalho de Melo 		);
586d01f4e8dSNamhyung Kim 		return -1;
587d01f4e8dSNamhyung Kim 	}
588d01f4e8dSNamhyung Kim 
589d01f4e8dSNamhyung Kim 	signal(SIGINT, sig_handler);
590d01f4e8dSNamhyung Kim 	signal(SIGUSR1, sig_handler);
591d01f4e8dSNamhyung Kim 	signal(SIGCHLD, sig_handler);
59258335964SNamhyung Kim 	signal(SIGPIPE, sig_handler);
593d01f4e8dSNamhyung Kim 
59463cd02d8SChangbin Du 	if (reset_tracing_files(ftrace) < 0) {
59563cd02d8SChangbin Du 		pr_err("failed to reset ftrace\n");
596a9af6be5SNamhyung Kim 		goto out;
59763cd02d8SChangbin Du 	}
598d01f4e8dSNamhyung Kim 
599d01f4e8dSNamhyung Kim 	/* reset ftrace buffer */
600d01f4e8dSNamhyung Kim 	if (write_tracing_file("trace", "0") < 0)
601d01f4e8dSNamhyung Kim 		goto out;
602d01f4e8dSNamhyung Kim 
603a9af6be5SNamhyung Kim 	if (argc && perf_evlist__prepare_workload(ftrace->evlist,
604a9af6be5SNamhyung Kim 				&ftrace->target, argv, false,
605a9af6be5SNamhyung Kim 				ftrace__workload_exec_failed_signal) < 0) {
606d01f4e8dSNamhyung Kim 		goto out;
607a9af6be5SNamhyung Kim 	}
608a9af6be5SNamhyung Kim 
6093c4dc21bSChangbin Du 	if (set_tracing_options(ftrace) < 0)
610a9af6be5SNamhyung Kim 		goto out_reset;
61100c85d5fSChangbin Du 
612d01f4e8dSNamhyung Kim 	if (write_tracing_file("current_tracer", ftrace->tracer) < 0) {
613d01f4e8dSNamhyung Kim 		pr_err("failed to set current_tracer to %s\n", ftrace->tracer);
614a9af6be5SNamhyung Kim 		goto out_reset;
615d01f4e8dSNamhyung Kim 	}
616d01f4e8dSNamhyung Kim 
61729681bc5SNamhyung Kim 	setup_pager();
61829681bc5SNamhyung Kim 
619d01f4e8dSNamhyung Kim 	trace_file = get_tracing_file("trace_pipe");
620d01f4e8dSNamhyung Kim 	if (!trace_file) {
621d01f4e8dSNamhyung Kim 		pr_err("failed to open trace_pipe\n");
622a9af6be5SNamhyung Kim 		goto out_reset;
623d01f4e8dSNamhyung Kim 	}
624d01f4e8dSNamhyung Kim 
625d01f4e8dSNamhyung Kim 	trace_fd = open(trace_file, O_RDONLY);
626d01f4e8dSNamhyung Kim 
627d01f4e8dSNamhyung Kim 	put_tracing_file(trace_file);
628d01f4e8dSNamhyung Kim 
629d01f4e8dSNamhyung Kim 	if (trace_fd < 0) {
630d01f4e8dSNamhyung Kim 		pr_err("failed to open trace_pipe\n");
631a9af6be5SNamhyung Kim 		goto out_reset;
632d01f4e8dSNamhyung Kim 	}
633d01f4e8dSNamhyung Kim 
634d01f4e8dSNamhyung Kim 	fcntl(trace_fd, F_SETFL, O_NONBLOCK);
635d01f4e8dSNamhyung Kim 	pollfd.fd = trace_fd;
636d01f4e8dSNamhyung Kim 
63781523c1eSChangbin Du 	/* display column headers */
63881523c1eSChangbin Du 	read_tracing_file_to_stdout("trace");
63981523c1eSChangbin Du 
6406555c2f6SChangbin Du 	if (!ftrace->initial_delay) {
641d01f4e8dSNamhyung Kim 		if (write_tracing_file("tracing_on", "1") < 0) {
642d01f4e8dSNamhyung Kim 			pr_err("can't enable tracing\n");
643d01f4e8dSNamhyung Kim 			goto out_close_fd;
644d01f4e8dSNamhyung Kim 		}
6456555c2f6SChangbin Du 	}
646d01f4e8dSNamhyung Kim 
647d01f4e8dSNamhyung Kim 	perf_evlist__start_workload(ftrace->evlist);
648d01f4e8dSNamhyung Kim 
6496555c2f6SChangbin Du 	if (ftrace->initial_delay) {
6506555c2f6SChangbin Du 		usleep(ftrace->initial_delay * 1000);
6516555c2f6SChangbin Du 		if (write_tracing_file("tracing_on", "1") < 0) {
6526555c2f6SChangbin Du 			pr_err("can't enable tracing\n");
6536555c2f6SChangbin Du 			goto out_close_fd;
6546555c2f6SChangbin Du 		}
6556555c2f6SChangbin Du 	}
6566555c2f6SChangbin Du 
657d01f4e8dSNamhyung Kim 	while (!done) {
658d01f4e8dSNamhyung Kim 		if (poll(&pollfd, 1, -1) < 0)
659d01f4e8dSNamhyung Kim 			break;
660d01f4e8dSNamhyung Kim 
661d01f4e8dSNamhyung Kim 		if (pollfd.revents & POLLIN) {
662d01f4e8dSNamhyung Kim 			int n = read(trace_fd, buf, sizeof(buf));
663d01f4e8dSNamhyung Kim 			if (n < 0)
664d01f4e8dSNamhyung Kim 				break;
665d01f4e8dSNamhyung Kim 			if (fwrite(buf, n, 1, stdout) != 1)
666d01f4e8dSNamhyung Kim 				break;
667d01f4e8dSNamhyung Kim 		}
668d01f4e8dSNamhyung Kim 	}
669d01f4e8dSNamhyung Kim 
670d01f4e8dSNamhyung Kim 	write_tracing_file("tracing_on", "0");
671d01f4e8dSNamhyung Kim 
67251a09d8fSChangbin Du 	if (workload_exec_errno) {
67351a09d8fSChangbin Du 		const char *emsg = str_error_r(workload_exec_errno, buf, sizeof(buf));
67451a09d8fSChangbin Du 		/* flush stdout first so below error msg appears at the end. */
67551a09d8fSChangbin Du 		fflush(stdout);
67651a09d8fSChangbin Du 		pr_err("workload failed: %s\n", emsg);
67751a09d8fSChangbin Du 		goto out_close_fd;
67851a09d8fSChangbin Du 	}
67951a09d8fSChangbin Du 
680d01f4e8dSNamhyung Kim 	/* read remaining buffer contents */
681d01f4e8dSNamhyung Kim 	while (true) {
682d01f4e8dSNamhyung Kim 		int n = read(trace_fd, buf, sizeof(buf));
683d01f4e8dSNamhyung Kim 		if (n <= 0)
684d01f4e8dSNamhyung Kim 			break;
685d01f4e8dSNamhyung Kim 		if (fwrite(buf, n, 1, stdout) != 1)
686d01f4e8dSNamhyung Kim 			break;
687d01f4e8dSNamhyung Kim 	}
688d01f4e8dSNamhyung Kim 
689d01f4e8dSNamhyung Kim out_close_fd:
690d01f4e8dSNamhyung Kim 	close(trace_fd);
691a9af6be5SNamhyung Kim out_reset:
692d01f4e8dSNamhyung Kim 	reset_tracing_files(ftrace);
693a9af6be5SNamhyung Kim out:
69451a09d8fSChangbin Du 	return (done && !workload_exec_errno) ? 0 : -1;
695d01f4e8dSNamhyung Kim }
696d01f4e8dSNamhyung Kim 
697b05d1093STaeung Song static int perf_ftrace_config(const char *var, const char *value, void *cb)
698b05d1093STaeung Song {
699b05d1093STaeung Song 	struct perf_ftrace *ftrace = cb;
700b05d1093STaeung Song 
7018e99b6d4SArnaldo Carvalho de Melo 	if (!strstarts(var, "ftrace."))
702b05d1093STaeung Song 		return 0;
703b05d1093STaeung Song 
704b05d1093STaeung Song 	if (strcmp(var, "ftrace.tracer"))
705b05d1093STaeung Song 		return -1;
706b05d1093STaeung Song 
707b05d1093STaeung Song 	if (!strcmp(value, "function_graph") ||
708b05d1093STaeung Song 	    !strcmp(value, "function")) {
709b05d1093STaeung Song 		ftrace->tracer = value;
710b05d1093STaeung Song 		return 0;
711b05d1093STaeung Song 	}
712b05d1093STaeung Song 
713b05d1093STaeung Song 	pr_err("Please select \"function_graph\" (default) or \"function\"\n");
714b05d1093STaeung Song 	return -1;
715b05d1093STaeung Song }
716b05d1093STaeung Song 
717*2ae05fe0SChangbin Du static void list_function_cb(char *str, void *arg)
718*2ae05fe0SChangbin Du {
719*2ae05fe0SChangbin Du 	struct strfilter *filter = (struct strfilter *)arg;
720*2ae05fe0SChangbin Du 
721*2ae05fe0SChangbin Du 	if (strfilter__compare(filter, str))
722*2ae05fe0SChangbin Du 		printf("%s", str);
723*2ae05fe0SChangbin Du }
724*2ae05fe0SChangbin Du 
725*2ae05fe0SChangbin Du static int opt_list_avail_functions(const struct option *opt __maybe_unused,
726*2ae05fe0SChangbin Du 				    const char *str, int unset)
727*2ae05fe0SChangbin Du {
728*2ae05fe0SChangbin Du 	struct strfilter *filter;
729*2ae05fe0SChangbin Du 	const char *err = NULL;
730*2ae05fe0SChangbin Du 	int ret;
731*2ae05fe0SChangbin Du 
732*2ae05fe0SChangbin Du 	if (unset || !str)
733*2ae05fe0SChangbin Du 		return -1;
734*2ae05fe0SChangbin Du 
735*2ae05fe0SChangbin Du 	filter = strfilter__new(str, &err);
736*2ae05fe0SChangbin Du 	if (!filter)
737*2ae05fe0SChangbin Du 		return err ? -EINVAL : -ENOMEM;
738*2ae05fe0SChangbin Du 
739*2ae05fe0SChangbin Du 	ret = strfilter__or(filter, str, &err);
740*2ae05fe0SChangbin Du 	if (ret == -EINVAL) {
741*2ae05fe0SChangbin Du 		pr_err("Filter parse error at %td.\n", err - str + 1);
742*2ae05fe0SChangbin Du 		pr_err("Source: \"%s\"\n", str);
743*2ae05fe0SChangbin Du 		pr_err("         %*c\n", (int)(err - str + 1), '^');
744*2ae05fe0SChangbin Du 		strfilter__delete(filter);
745*2ae05fe0SChangbin Du 		return ret;
746*2ae05fe0SChangbin Du 	}
747*2ae05fe0SChangbin Du 
748*2ae05fe0SChangbin Du 	ret = read_tracing_file_by_line("available_filter_functions",
749*2ae05fe0SChangbin Du 					list_function_cb, filter);
750*2ae05fe0SChangbin Du 	strfilter__delete(filter);
751*2ae05fe0SChangbin Du 	if (ret < 0)
752*2ae05fe0SChangbin Du 		return ret;
753*2ae05fe0SChangbin Du 
754*2ae05fe0SChangbin Du 	exit(0);
755*2ae05fe0SChangbin Du }
756*2ae05fe0SChangbin Du 
75778b83e8bSNamhyung Kim static int parse_filter_func(const struct option *opt, const char *str,
75878b83e8bSNamhyung Kim 			     int unset __maybe_unused)
75978b83e8bSNamhyung Kim {
76078b83e8bSNamhyung Kim 	struct list_head *head = opt->value;
76178b83e8bSNamhyung Kim 	struct filter_entry *entry;
76278b83e8bSNamhyung Kim 
76378b83e8bSNamhyung Kim 	entry = malloc(sizeof(*entry) + strlen(str) + 1);
76478b83e8bSNamhyung Kim 	if (entry == NULL)
76578b83e8bSNamhyung Kim 		return -ENOMEM;
76678b83e8bSNamhyung Kim 
76778b83e8bSNamhyung Kim 	strcpy(entry->name, str);
76878b83e8bSNamhyung Kim 	list_add_tail(&entry->list, head);
76978b83e8bSNamhyung Kim 
77078b83e8bSNamhyung Kim 	return 0;
77178b83e8bSNamhyung Kim }
77278b83e8bSNamhyung Kim 
77378b83e8bSNamhyung Kim static void delete_filter_func(struct list_head *head)
77478b83e8bSNamhyung Kim {
77578b83e8bSNamhyung Kim 	struct filter_entry *pos, *tmp;
77678b83e8bSNamhyung Kim 
77778b83e8bSNamhyung Kim 	list_for_each_entry_safe(pos, tmp, head, list) {
778e56fbc9dSArnaldo Carvalho de Melo 		list_del_init(&pos->list);
77978b83e8bSNamhyung Kim 		free(pos);
78078b83e8bSNamhyung Kim 	}
78178b83e8bSNamhyung Kim }
78278b83e8bSNamhyung Kim 
783846e1939SChangbin Du static int parse_buffer_size(const struct option *opt,
784846e1939SChangbin Du 			     const char *str, int unset)
785846e1939SChangbin Du {
786846e1939SChangbin Du 	unsigned long *s = (unsigned long *)opt->value;
787846e1939SChangbin Du 	static struct parse_tag tags_size[] = {
788846e1939SChangbin Du 		{ .tag  = 'B', .mult = 1       },
789846e1939SChangbin Du 		{ .tag  = 'K', .mult = 1 << 10 },
790846e1939SChangbin Du 		{ .tag  = 'M', .mult = 1 << 20 },
791846e1939SChangbin Du 		{ .tag  = 'G', .mult = 1 << 30 },
792846e1939SChangbin Du 		{ .tag  = 0 },
793846e1939SChangbin Du 	};
794846e1939SChangbin Du 	unsigned long val;
795846e1939SChangbin Du 
796846e1939SChangbin Du 	if (unset) {
797846e1939SChangbin Du 		*s = 0;
798846e1939SChangbin Du 		return 0;
799846e1939SChangbin Du 	}
800846e1939SChangbin Du 
801846e1939SChangbin Du 	val = parse_tag_value(str, tags_size);
802846e1939SChangbin Du 	if (val != (unsigned long) -1) {
803846e1939SChangbin Du 		if (val < 1024) {
804846e1939SChangbin Du 			pr_err("buffer size too small, must larger than 1KB.");
805846e1939SChangbin Du 			return -1;
806846e1939SChangbin Du 		}
807846e1939SChangbin Du 		*s = val;
808846e1939SChangbin Du 		return 0;
809846e1939SChangbin Du 	}
810846e1939SChangbin Du 
811846e1939SChangbin Du 	return -1;
812846e1939SChangbin Du }
813846e1939SChangbin Du 
814b1d84af6SChangbin Du static int parse_func_tracer_opts(const struct option *opt,
815b1d84af6SChangbin Du 				  const char *str, int unset)
816b1d84af6SChangbin Du {
817b1d84af6SChangbin Du 	int ret;
818b1d84af6SChangbin Du 	struct perf_ftrace *ftrace = (struct perf_ftrace *) opt->value;
819b1d84af6SChangbin Du 	struct sublevel_option func_tracer_opts[] = {
820b1d84af6SChangbin Du 		{ .name = "call-graph",	.value_ptr = &ftrace->func_stack_trace },
821c81fc34eSChangbin Du 		{ .name = "irq-info",	.value_ptr = &ftrace->func_irq_info },
822b1d84af6SChangbin Du 		{ .name = NULL, }
823b1d84af6SChangbin Du 	};
824b1d84af6SChangbin Du 
825b1d84af6SChangbin Du 	if (unset)
826b1d84af6SChangbin Du 		return 0;
827b1d84af6SChangbin Du 
828b1d84af6SChangbin Du 	ret = perf_parse_sublevel_options(str, func_tracer_opts);
829b1d84af6SChangbin Du 	if (ret)
830b1d84af6SChangbin Du 		return ret;
831b1d84af6SChangbin Du 
832b1d84af6SChangbin Du 	return 0;
833b1d84af6SChangbin Du }
834b1d84af6SChangbin Du 
83538988f2eSChangbin Du static int parse_graph_tracer_opts(const struct option *opt,
83638988f2eSChangbin Du 				  const char *str, int unset)
83738988f2eSChangbin Du {
83838988f2eSChangbin Du 	int ret;
83938988f2eSChangbin Du 	struct perf_ftrace *ftrace = (struct perf_ftrace *) opt->value;
84038988f2eSChangbin Du 	struct sublevel_option graph_tracer_opts[] = {
84138988f2eSChangbin Du 		{ .name = "nosleep-time",	.value_ptr = &ftrace->graph_nosleep_time },
842d1bcf17cSChangbin Du 		{ .name = "noirqs",		.value_ptr = &ftrace->graph_noirqs },
84359486fb0SChangbin Du 		{ .name = "verbose",		.value_ptr = &ftrace->graph_verbose },
84400c85d5fSChangbin Du 		{ .name = "thresh",		.value_ptr = &ftrace->graph_thresh },
845a8f87a5cSChangbin Du 		{ .name = "depth",		.value_ptr = &ftrace->graph_depth },
84638988f2eSChangbin Du 		{ .name = NULL, }
84738988f2eSChangbin Du 	};
84838988f2eSChangbin Du 
84938988f2eSChangbin Du 	if (unset)
85038988f2eSChangbin Du 		return 0;
85138988f2eSChangbin Du 
85238988f2eSChangbin Du 	ret = perf_parse_sublevel_options(str, graph_tracer_opts);
85338988f2eSChangbin Du 	if (ret)
85438988f2eSChangbin Du 		return ret;
85538988f2eSChangbin Du 
85638988f2eSChangbin Du 	return 0;
85738988f2eSChangbin Du }
85838988f2eSChangbin Du 
859eb6d31aeSChangbin Du static void select_tracer(struct perf_ftrace *ftrace)
860eb6d31aeSChangbin Du {
861eb6d31aeSChangbin Du 	bool graph = !list_empty(&ftrace->graph_funcs) ||
862eb6d31aeSChangbin Du 		     !list_empty(&ftrace->nograph_funcs);
863eb6d31aeSChangbin Du 	bool func = !list_empty(&ftrace->filters) ||
864eb6d31aeSChangbin Du 		    !list_empty(&ftrace->notrace);
865eb6d31aeSChangbin Du 
866eb6d31aeSChangbin Du 	/* The function_graph has priority over function tracer. */
867eb6d31aeSChangbin Du 	if (graph)
868eb6d31aeSChangbin Du 		ftrace->tracer = "function_graph";
869eb6d31aeSChangbin Du 	else if (func)
870eb6d31aeSChangbin Du 		ftrace->tracer = "function";
871eb6d31aeSChangbin Du 	/* Otherwise, the default tracer is used. */
872eb6d31aeSChangbin Du 
873eb6d31aeSChangbin Du 	pr_debug("%s tracer is used\n", ftrace->tracer);
874eb6d31aeSChangbin Du }
875eb6d31aeSChangbin Du 
876b0ad8ea6SArnaldo Carvalho de Melo int cmd_ftrace(int argc, const char **argv)
877d01f4e8dSNamhyung Kim {
878d01f4e8dSNamhyung Kim 	int ret;
879d01f4e8dSNamhyung Kim 	struct perf_ftrace ftrace = {
880bf062bd2STaeung Song 		.tracer = DEFAULT_TRACER,
881d01f4e8dSNamhyung Kim 		.target = { .uid = UINT_MAX, },
882d01f4e8dSNamhyung Kim 	};
883d01f4e8dSNamhyung Kim 	const char * const ftrace_usage[] = {
884a9af6be5SNamhyung Kim 		"perf ftrace [<options>] [<command>]",
885d01f4e8dSNamhyung Kim 		"perf ftrace [<options>] -- <command> [<options>]",
886d01f4e8dSNamhyung Kim 		NULL
887d01f4e8dSNamhyung Kim 	};
888d01f4e8dSNamhyung Kim 	const struct option ftrace_options[] = {
889d01f4e8dSNamhyung Kim 	OPT_STRING('t', "tracer", &ftrace.tracer, "tracer",
890492e4edbSArnaldo Carvalho de Melo 		   "Tracer to use: function_graph(default) or function"),
891*2ae05fe0SChangbin Du 	OPT_CALLBACK_DEFAULT('F', "funcs", NULL, "[FILTER]",
892*2ae05fe0SChangbin Du 			     "Show available functions to filter",
893*2ae05fe0SChangbin Du 			     opt_list_avail_functions, "*"),
894a9af6be5SNamhyung Kim 	OPT_STRING('p', "pid", &ftrace.target.pid, "pid",
895492e4edbSArnaldo Carvalho de Melo 		   "Trace on existing process id"),
89642145d71SChangbin Du 	/* TODO: Add short option -t after -t/--tracer can be removed. */
89742145d71SChangbin Du 	OPT_STRING(0, "tid", &ftrace.target.tid, "tid",
898492e4edbSArnaldo Carvalho de Melo 		   "Trace on existing thread id (exclusive to --pid)"),
899d01f4e8dSNamhyung Kim 	OPT_INCR('v', "verbose", &verbose,
900492e4edbSArnaldo Carvalho de Melo 		 "Be more verbose"),
901dc231032SNamhyung Kim 	OPT_BOOLEAN('a', "all-cpus", &ftrace.target.system_wide,
902492e4edbSArnaldo Carvalho de Melo 		    "System-wide collection from all CPUs"),
903dc231032SNamhyung Kim 	OPT_STRING('C', "cpu", &ftrace.target.cpu_list, "cpu",
904492e4edbSArnaldo Carvalho de Melo 		    "List of cpus to monitor"),
90578b83e8bSNamhyung Kim 	OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func",
906492e4edbSArnaldo Carvalho de Melo 		     "Trace given functions using function tracer",
907eb6d31aeSChangbin Du 		     parse_filter_func),
90878b83e8bSNamhyung Kim 	OPT_CALLBACK('N', "notrace-funcs", &ftrace.notrace, "func",
909492e4edbSArnaldo Carvalho de Melo 		     "Do not trace given functions", parse_filter_func),
910b1d84af6SChangbin Du 	OPT_CALLBACK(0, "func-opts", &ftrace, "options",
911492e4edbSArnaldo Carvalho de Melo 		     "Function tracer options, available options: call-graph,irq-info",
912b1d84af6SChangbin Du 		     parse_func_tracer_opts),
91378b83e8bSNamhyung Kim 	OPT_CALLBACK('G', "graph-funcs", &ftrace.graph_funcs, "func",
914492e4edbSArnaldo Carvalho de Melo 		     "Trace given functions using function_graph tracer",
915eb6d31aeSChangbin Du 		     parse_filter_func),
91678b83e8bSNamhyung Kim 	OPT_CALLBACK('g', "nograph-funcs", &ftrace.nograph_funcs, "func",
91778b83e8bSNamhyung Kim 		     "Set nograph filter on given functions", parse_filter_func),
91838988f2eSChangbin Du 	OPT_CALLBACK(0, "graph-opts", &ftrace, "options",
919492e4edbSArnaldo Carvalho de Melo 		     "Graph tracer options, available options: nosleep-time,noirqs,verbose,thresh=<n>,depth=<n>",
92038988f2eSChangbin Du 		     parse_graph_tracer_opts),
921846e1939SChangbin Du 	OPT_CALLBACK('m', "buffer-size", &ftrace.percpu_buffer_size, "size",
922492e4edbSArnaldo Carvalho de Melo 		     "Size of per cpu buffer, needs to use a B, K, M or G suffix.", parse_buffer_size),
9235b347472SChangbin Du 	OPT_BOOLEAN(0, "inherit", &ftrace.inherit,
924492e4edbSArnaldo Carvalho de Melo 		    "Trace children processes"),
9256555c2f6SChangbin Du 	OPT_UINTEGER('D', "delay", &ftrace.initial_delay,
926492e4edbSArnaldo Carvalho de Melo 		     "Number of milliseconds to wait before starting tracing after program start"),
927d01f4e8dSNamhyung Kim 	OPT_END()
928d01f4e8dSNamhyung Kim 	};
929d01f4e8dSNamhyung Kim 
93078b83e8bSNamhyung Kim 	INIT_LIST_HEAD(&ftrace.filters);
93178b83e8bSNamhyung Kim 	INIT_LIST_HEAD(&ftrace.notrace);
93278b83e8bSNamhyung Kim 	INIT_LIST_HEAD(&ftrace.graph_funcs);
93378b83e8bSNamhyung Kim 	INIT_LIST_HEAD(&ftrace.nograph_funcs);
93478b83e8bSNamhyung Kim 
935b05d1093STaeung Song 	ret = perf_config(perf_ftrace_config, &ftrace);
936b05d1093STaeung Song 	if (ret < 0)
937b05d1093STaeung Song 		return -1;
938b05d1093STaeung Song 
939d01f4e8dSNamhyung Kim 	argc = parse_options(argc, argv, ftrace_options, ftrace_usage,
940d01f4e8dSNamhyung Kim 			    PARSE_OPT_STOP_AT_NON_OPTION);
941a9af6be5SNamhyung Kim 	if (!argc && target__none(&ftrace.target))
942452b0d16SChangbin Du 		ftrace.target.system_wide = true;
943d01f4e8dSNamhyung Kim 
944eb6d31aeSChangbin Du 	select_tracer(&ftrace);
945eb6d31aeSChangbin Du 
946a9af6be5SNamhyung Kim 	ret = target__validate(&ftrace.target);
947a9af6be5SNamhyung Kim 	if (ret) {
948a9af6be5SNamhyung Kim 		char errbuf[512];
949a9af6be5SNamhyung Kim 
950a9af6be5SNamhyung Kim 		target__strerror(&ftrace.target, ret, errbuf, 512);
951a9af6be5SNamhyung Kim 		pr_err("%s\n", errbuf);
95278b83e8bSNamhyung Kim 		goto out_delete_filters;
953a9af6be5SNamhyung Kim 	}
954a9af6be5SNamhyung Kim 
9550f98b11cSJiri Olsa 	ftrace.evlist = evlist__new();
95678b83e8bSNamhyung Kim 	if (ftrace.evlist == NULL) {
95778b83e8bSNamhyung Kim 		ret = -ENOMEM;
95878b83e8bSNamhyung Kim 		goto out_delete_filters;
95978b83e8bSNamhyung Kim 	}
960d01f4e8dSNamhyung Kim 
961d01f4e8dSNamhyung Kim 	ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target);
962d01f4e8dSNamhyung Kim 	if (ret < 0)
963d01f4e8dSNamhyung Kim 		goto out_delete_evlist;
964d01f4e8dSNamhyung Kim 
965d01f4e8dSNamhyung Kim 	ret = __cmd_ftrace(&ftrace, argc, argv);
966d01f4e8dSNamhyung Kim 
967d01f4e8dSNamhyung Kim out_delete_evlist:
968c12995a5SJiri Olsa 	evlist__delete(ftrace.evlist);
969d01f4e8dSNamhyung Kim 
97078b83e8bSNamhyung Kim out_delete_filters:
97178b83e8bSNamhyung Kim 	delete_filter_func(&ftrace.filters);
97278b83e8bSNamhyung Kim 	delete_filter_func(&ftrace.notrace);
97378b83e8bSNamhyung Kim 	delete_filter_func(&ftrace.graph_funcs);
97478b83e8bSNamhyung Kim 	delete_filter_func(&ftrace.nograph_funcs);
97578b83e8bSNamhyung Kim 
976d01f4e8dSNamhyung Kim 	return ret;
977d01f4e8dSNamhyung Kim }
978