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" 29d01f4e8dSNamhyung Kim 30d01f4e8dSNamhyung Kim #define DEFAULT_TRACER "function_graph" 31d01f4e8dSNamhyung Kim 32d01f4e8dSNamhyung Kim struct perf_ftrace { 3363503dbaSJiri Olsa struct evlist *evlist; 34d01f4e8dSNamhyung Kim struct target target; 35d01f4e8dSNamhyung Kim const char *tracer; 3678b83e8bSNamhyung Kim struct list_head filters; 3778b83e8bSNamhyung Kim struct list_head notrace; 3878b83e8bSNamhyung Kim struct list_head graph_funcs; 3978b83e8bSNamhyung Kim struct list_head nograph_funcs; 401096c35aSNamhyung Kim int graph_depth; 4178b83e8bSNamhyung Kim }; 4278b83e8bSNamhyung Kim 4378b83e8bSNamhyung Kim struct filter_entry { 4478b83e8bSNamhyung Kim struct list_head list; 4578b83e8bSNamhyung Kim char name[]; 46d01f4e8dSNamhyung Kim }; 47d01f4e8dSNamhyung Kim 48d01f4e8dSNamhyung Kim static bool done; 49d01f4e8dSNamhyung Kim 50d01f4e8dSNamhyung Kim static void sig_handler(int sig __maybe_unused) 51d01f4e8dSNamhyung Kim { 52d01f4e8dSNamhyung Kim done = true; 53d01f4e8dSNamhyung Kim } 54d01f4e8dSNamhyung Kim 55d01f4e8dSNamhyung Kim /* 56d01f4e8dSNamhyung Kim * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since 57d01f4e8dSNamhyung Kim * we asked by setting its exec_error to the function below, 58d01f4e8dSNamhyung Kim * ftrace__workload_exec_failed_signal. 59d01f4e8dSNamhyung Kim * 60d01f4e8dSNamhyung Kim * XXX We need to handle this more appropriately, emitting an error, etc. 61d01f4e8dSNamhyung Kim */ 62d01f4e8dSNamhyung Kim static void ftrace__workload_exec_failed_signal(int signo __maybe_unused, 63d01f4e8dSNamhyung Kim siginfo_t *info __maybe_unused, 64d01f4e8dSNamhyung Kim void *ucontext __maybe_unused) 65d01f4e8dSNamhyung Kim { 66d01f4e8dSNamhyung Kim /* workload_exec_errno = info->si_value.sival_int; */ 67d01f4e8dSNamhyung Kim done = true; 68d01f4e8dSNamhyung Kim } 69d01f4e8dSNamhyung Kim 70a9af6be5SNamhyung Kim static int __write_tracing_file(const char *name, const char *val, bool append) 71d01f4e8dSNamhyung Kim { 72d01f4e8dSNamhyung Kim char *file; 73d01f4e8dSNamhyung Kim int fd, ret = -1; 74d01f4e8dSNamhyung Kim ssize_t size = strlen(val); 75a9af6be5SNamhyung Kim int flags = O_WRONLY; 76e7bd9ba2SNamhyung Kim char errbuf[512]; 7763cd02d8SChangbin Du char *val_copy; 78d01f4e8dSNamhyung Kim 79d01f4e8dSNamhyung Kim file = get_tracing_file(name); 80d01f4e8dSNamhyung Kim if (!file) { 81d01f4e8dSNamhyung Kim pr_debug("cannot get tracing file: %s\n", name); 82d01f4e8dSNamhyung Kim return -1; 83d01f4e8dSNamhyung Kim } 84d01f4e8dSNamhyung Kim 85a9af6be5SNamhyung Kim if (append) 86a9af6be5SNamhyung Kim flags |= O_APPEND; 87a9af6be5SNamhyung Kim else 88a9af6be5SNamhyung Kim flags |= O_TRUNC; 89a9af6be5SNamhyung Kim 90a9af6be5SNamhyung Kim fd = open(file, flags); 91d01f4e8dSNamhyung Kim if (fd < 0) { 92e7bd9ba2SNamhyung Kim pr_debug("cannot open tracing file: %s: %s\n", 93e7bd9ba2SNamhyung Kim name, str_error_r(errno, errbuf, sizeof(errbuf))); 94d01f4e8dSNamhyung Kim goto out; 95d01f4e8dSNamhyung Kim } 96d01f4e8dSNamhyung Kim 9763cd02d8SChangbin Du /* 9863cd02d8SChangbin Du * Copy the original value and append a '\n'. Without this, 9963cd02d8SChangbin Du * the kernel can hide possible errors. 10063cd02d8SChangbin Du */ 10163cd02d8SChangbin Du val_copy = strdup(val); 10263cd02d8SChangbin Du if (!val_copy) 10363cd02d8SChangbin Du goto out_close; 10463cd02d8SChangbin Du val_copy[size] = '\n'; 10563cd02d8SChangbin Du 10663cd02d8SChangbin Du if (write(fd, val_copy, size + 1) == size + 1) 107d01f4e8dSNamhyung Kim ret = 0; 108d01f4e8dSNamhyung Kim else 109e7bd9ba2SNamhyung Kim pr_debug("write '%s' to tracing/%s failed: %s\n", 110e7bd9ba2SNamhyung Kim val, name, str_error_r(errno, errbuf, sizeof(errbuf))); 111d01f4e8dSNamhyung Kim 11263cd02d8SChangbin Du free(val_copy); 11363cd02d8SChangbin Du out_close: 114d01f4e8dSNamhyung Kim close(fd); 115d01f4e8dSNamhyung Kim out: 116d01f4e8dSNamhyung Kim put_tracing_file(file); 117d01f4e8dSNamhyung Kim return ret; 118d01f4e8dSNamhyung Kim } 119d01f4e8dSNamhyung Kim 120a9af6be5SNamhyung Kim static int write_tracing_file(const char *name, const char *val) 121a9af6be5SNamhyung Kim { 122a9af6be5SNamhyung Kim return __write_tracing_file(name, val, false); 123a9af6be5SNamhyung Kim } 124a9af6be5SNamhyung Kim 125a9af6be5SNamhyung Kim static int append_tracing_file(const char *name, const char *val) 126a9af6be5SNamhyung Kim { 127a9af6be5SNamhyung Kim return __write_tracing_file(name, val, true); 128a9af6be5SNamhyung Kim } 129a9af6be5SNamhyung Kim 130dc231032SNamhyung Kim static int reset_tracing_cpu(void); 13178b83e8bSNamhyung Kim static void reset_tracing_filters(void); 132dc231032SNamhyung Kim 133d01f4e8dSNamhyung Kim static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused) 134d01f4e8dSNamhyung Kim { 135d01f4e8dSNamhyung Kim if (write_tracing_file("tracing_on", "0") < 0) 136d01f4e8dSNamhyung Kim return -1; 137d01f4e8dSNamhyung Kim 138d01f4e8dSNamhyung Kim if (write_tracing_file("current_tracer", "nop") < 0) 139d01f4e8dSNamhyung Kim return -1; 140d01f4e8dSNamhyung Kim 141d01f4e8dSNamhyung Kim if (write_tracing_file("set_ftrace_pid", " ") < 0) 142d01f4e8dSNamhyung Kim return -1; 143d01f4e8dSNamhyung Kim 144dc231032SNamhyung Kim if (reset_tracing_cpu() < 0) 145dc231032SNamhyung Kim return -1; 146dc231032SNamhyung Kim 1471096c35aSNamhyung Kim if (write_tracing_file("max_graph_depth", "0") < 0) 1481096c35aSNamhyung Kim return -1; 1491096c35aSNamhyung Kim 15078b83e8bSNamhyung Kim reset_tracing_filters(); 151d01f4e8dSNamhyung Kim return 0; 152d01f4e8dSNamhyung Kim } 153d01f4e8dSNamhyung Kim 154a9af6be5SNamhyung Kim static int set_tracing_pid(struct perf_ftrace *ftrace) 155a9af6be5SNamhyung Kim { 156a9af6be5SNamhyung Kim int i; 157a9af6be5SNamhyung Kim char buf[16]; 158a9af6be5SNamhyung Kim 159a9af6be5SNamhyung Kim if (target__has_cpu(&ftrace->target)) 160a9af6be5SNamhyung Kim return 0; 161a9af6be5SNamhyung Kim 162a2f354e3SJiri Olsa for (i = 0; i < perf_thread_map__nr(ftrace->evlist->core.threads); i++) { 163a9af6be5SNamhyung Kim scnprintf(buf, sizeof(buf), "%d", 16403617c22SJiri Olsa ftrace->evlist->core.threads->map[i]); 165a9af6be5SNamhyung Kim if (append_tracing_file("set_ftrace_pid", buf) < 0) 166a9af6be5SNamhyung Kim return -1; 167a9af6be5SNamhyung Kim } 168a9af6be5SNamhyung Kim return 0; 169a9af6be5SNamhyung Kim } 170a9af6be5SNamhyung Kim 171f854839bSJiri Olsa static int set_tracing_cpumask(struct perf_cpu_map *cpumap) 172dc231032SNamhyung Kim { 173dc231032SNamhyung Kim char *cpumask; 174dc231032SNamhyung Kim size_t mask_size; 175dc231032SNamhyung Kim int ret; 176dc231032SNamhyung Kim int last_cpu; 177dc231032SNamhyung Kim 178dc231032SNamhyung Kim last_cpu = cpu_map__cpu(cpumap, cpumap->nr - 1); 179cf30ae72SHe Zhe mask_size = last_cpu / 4 + 2; /* one more byte for EOS */ 180dc231032SNamhyung Kim mask_size += last_cpu / 32; /* ',' is needed for every 32th cpus */ 181dc231032SNamhyung Kim 182dc231032SNamhyung Kim cpumask = malloc(mask_size); 183dc231032SNamhyung Kim if (cpumask == NULL) { 184dc231032SNamhyung Kim pr_debug("failed to allocate cpu mask\n"); 185dc231032SNamhyung Kim return -1; 186dc231032SNamhyung Kim } 187dc231032SNamhyung Kim 188dc231032SNamhyung Kim cpu_map__snprint_mask(cpumap, cpumask, mask_size); 189dc231032SNamhyung Kim 190dc231032SNamhyung Kim ret = write_tracing_file("tracing_cpumask", cpumask); 191dc231032SNamhyung Kim 192dc231032SNamhyung Kim free(cpumask); 193dc231032SNamhyung Kim return ret; 194dc231032SNamhyung Kim } 195dc231032SNamhyung Kim 196dc231032SNamhyung Kim static int set_tracing_cpu(struct perf_ftrace *ftrace) 197dc231032SNamhyung Kim { 198f72f901dSJiri Olsa struct perf_cpu_map *cpumap = ftrace->evlist->core.cpus; 199dc231032SNamhyung Kim 200dc231032SNamhyung Kim if (!target__has_cpu(&ftrace->target)) 201dc231032SNamhyung Kim return 0; 202dc231032SNamhyung Kim 203dc231032SNamhyung Kim return set_tracing_cpumask(cpumap); 204dc231032SNamhyung Kim } 205dc231032SNamhyung Kim 206dc231032SNamhyung Kim static int reset_tracing_cpu(void) 207dc231032SNamhyung Kim { 2089c3516d1SJiri Olsa struct perf_cpu_map *cpumap = perf_cpu_map__new(NULL); 209dc231032SNamhyung Kim int ret; 210dc231032SNamhyung Kim 211dc231032SNamhyung Kim ret = set_tracing_cpumask(cpumap); 21238f01d8dSJiri Olsa perf_cpu_map__put(cpumap); 213dc231032SNamhyung Kim return ret; 214dc231032SNamhyung Kim } 215dc231032SNamhyung Kim 21678b83e8bSNamhyung Kim static int __set_tracing_filter(const char *filter_file, struct list_head *funcs) 21778b83e8bSNamhyung Kim { 21878b83e8bSNamhyung Kim struct filter_entry *pos; 21978b83e8bSNamhyung Kim 22078b83e8bSNamhyung Kim list_for_each_entry(pos, funcs, list) { 22178b83e8bSNamhyung Kim if (append_tracing_file(filter_file, pos->name) < 0) 22278b83e8bSNamhyung Kim return -1; 22378b83e8bSNamhyung Kim } 22478b83e8bSNamhyung Kim 22578b83e8bSNamhyung Kim return 0; 22678b83e8bSNamhyung Kim } 22778b83e8bSNamhyung Kim 22878b83e8bSNamhyung Kim static int set_tracing_filters(struct perf_ftrace *ftrace) 22978b83e8bSNamhyung Kim { 23078b83e8bSNamhyung Kim int ret; 23178b83e8bSNamhyung Kim 23278b83e8bSNamhyung Kim ret = __set_tracing_filter("set_ftrace_filter", &ftrace->filters); 23378b83e8bSNamhyung Kim if (ret < 0) 23478b83e8bSNamhyung Kim return ret; 23578b83e8bSNamhyung Kim 23678b83e8bSNamhyung Kim ret = __set_tracing_filter("set_ftrace_notrace", &ftrace->notrace); 23778b83e8bSNamhyung Kim if (ret < 0) 23878b83e8bSNamhyung Kim return ret; 23978b83e8bSNamhyung Kim 24078b83e8bSNamhyung Kim ret = __set_tracing_filter("set_graph_function", &ftrace->graph_funcs); 24178b83e8bSNamhyung Kim if (ret < 0) 24278b83e8bSNamhyung Kim return ret; 24378b83e8bSNamhyung Kim 24478b83e8bSNamhyung Kim /* old kernels do not have this filter */ 24578b83e8bSNamhyung Kim __set_tracing_filter("set_graph_notrace", &ftrace->nograph_funcs); 24678b83e8bSNamhyung Kim 24778b83e8bSNamhyung Kim return ret; 24878b83e8bSNamhyung Kim } 24978b83e8bSNamhyung Kim 25078b83e8bSNamhyung Kim static void reset_tracing_filters(void) 25178b83e8bSNamhyung Kim { 25278b83e8bSNamhyung Kim write_tracing_file("set_ftrace_filter", " "); 25378b83e8bSNamhyung Kim write_tracing_file("set_ftrace_notrace", " "); 25478b83e8bSNamhyung Kim write_tracing_file("set_graph_function", " "); 25578b83e8bSNamhyung Kim write_tracing_file("set_graph_notrace", " "); 25678b83e8bSNamhyung Kim } 25778b83e8bSNamhyung Kim 2581096c35aSNamhyung Kim static int set_tracing_depth(struct perf_ftrace *ftrace) 2591096c35aSNamhyung Kim { 2601096c35aSNamhyung Kim char buf[16]; 2611096c35aSNamhyung Kim 2621096c35aSNamhyung Kim if (ftrace->graph_depth == 0) 2631096c35aSNamhyung Kim return 0; 2641096c35aSNamhyung Kim 2651096c35aSNamhyung Kim if (ftrace->graph_depth < 0) { 2661096c35aSNamhyung Kim pr_err("invalid graph depth: %d\n", ftrace->graph_depth); 2671096c35aSNamhyung Kim return -1; 2681096c35aSNamhyung Kim } 2691096c35aSNamhyung Kim 2701096c35aSNamhyung Kim snprintf(buf, sizeof(buf), "%d", ftrace->graph_depth); 2711096c35aSNamhyung Kim 2721096c35aSNamhyung Kim if (write_tracing_file("max_graph_depth", buf) < 0) 2731096c35aSNamhyung Kim return -1; 2741096c35aSNamhyung Kim 2751096c35aSNamhyung Kim return 0; 2761096c35aSNamhyung Kim } 2771096c35aSNamhyung Kim 278d01f4e8dSNamhyung Kim static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) 279d01f4e8dSNamhyung Kim { 280d01f4e8dSNamhyung Kim char *trace_file; 281d01f4e8dSNamhyung Kim int trace_fd; 282d01f4e8dSNamhyung Kim char buf[4096]; 283d01f4e8dSNamhyung Kim struct pollfd pollfd = { 284d01f4e8dSNamhyung Kim .events = POLLIN, 285d01f4e8dSNamhyung Kim }; 286d01f4e8dSNamhyung Kim 2876b3e0e2eSAlexey Budankov if (!(perf_cap__capable(CAP_PERFMON) || 2886b3e0e2eSAlexey Budankov perf_cap__capable(CAP_SYS_ADMIN))) { 28973e5de70SArnaldo Carvalho de Melo pr_err("ftrace only works for %s!\n", 29073e5de70SArnaldo Carvalho de Melo #ifdef HAVE_LIBCAP_SUPPORT 2916b3e0e2eSAlexey Budankov "users with the CAP_PERFMON or CAP_SYS_ADMIN capability" 29273e5de70SArnaldo Carvalho de Melo #else 29373e5de70SArnaldo Carvalho de Melo "root" 29473e5de70SArnaldo Carvalho de Melo #endif 29573e5de70SArnaldo Carvalho de Melo ); 296d01f4e8dSNamhyung Kim return -1; 297d01f4e8dSNamhyung Kim } 298d01f4e8dSNamhyung Kim 299d01f4e8dSNamhyung Kim signal(SIGINT, sig_handler); 300d01f4e8dSNamhyung Kim signal(SIGUSR1, sig_handler); 301d01f4e8dSNamhyung Kim signal(SIGCHLD, sig_handler); 30258335964SNamhyung Kim signal(SIGPIPE, sig_handler); 303d01f4e8dSNamhyung Kim 30463cd02d8SChangbin Du if (reset_tracing_files(ftrace) < 0) { 30563cd02d8SChangbin Du pr_err("failed to reset ftrace\n"); 306a9af6be5SNamhyung Kim goto out; 30763cd02d8SChangbin Du } 308d01f4e8dSNamhyung Kim 309d01f4e8dSNamhyung Kim /* reset ftrace buffer */ 310d01f4e8dSNamhyung Kim if (write_tracing_file("trace", "0") < 0) 311d01f4e8dSNamhyung Kim goto out; 312d01f4e8dSNamhyung Kim 313a9af6be5SNamhyung Kim if (argc && perf_evlist__prepare_workload(ftrace->evlist, 314a9af6be5SNamhyung Kim &ftrace->target, argv, false, 315a9af6be5SNamhyung Kim ftrace__workload_exec_failed_signal) < 0) { 316d01f4e8dSNamhyung Kim goto out; 317a9af6be5SNamhyung Kim } 318a9af6be5SNamhyung Kim 319a9af6be5SNamhyung Kim if (set_tracing_pid(ftrace) < 0) { 320a9af6be5SNamhyung Kim pr_err("failed to set ftrace pid\n"); 321a9af6be5SNamhyung Kim goto out_reset; 322a9af6be5SNamhyung Kim } 323d01f4e8dSNamhyung Kim 324dc231032SNamhyung Kim if (set_tracing_cpu(ftrace) < 0) { 325dc231032SNamhyung Kim pr_err("failed to set tracing cpumask\n"); 326dc231032SNamhyung Kim goto out_reset; 327dc231032SNamhyung Kim } 328dc231032SNamhyung Kim 32978b83e8bSNamhyung Kim if (set_tracing_filters(ftrace) < 0) { 33078b83e8bSNamhyung Kim pr_err("failed to set tracing filters\n"); 33178b83e8bSNamhyung Kim goto out_reset; 33278b83e8bSNamhyung Kim } 33378b83e8bSNamhyung Kim 3341096c35aSNamhyung Kim if (set_tracing_depth(ftrace) < 0) { 3351096c35aSNamhyung Kim pr_err("failed to set graph depth\n"); 3361096c35aSNamhyung Kim goto out_reset; 3371096c35aSNamhyung Kim } 3381096c35aSNamhyung Kim 339d01f4e8dSNamhyung Kim if (write_tracing_file("current_tracer", ftrace->tracer) < 0) { 340d01f4e8dSNamhyung Kim pr_err("failed to set current_tracer to %s\n", ftrace->tracer); 341a9af6be5SNamhyung Kim goto out_reset; 342d01f4e8dSNamhyung Kim } 343d01f4e8dSNamhyung Kim 34429681bc5SNamhyung Kim setup_pager(); 34529681bc5SNamhyung Kim 346d01f4e8dSNamhyung Kim trace_file = get_tracing_file("trace_pipe"); 347d01f4e8dSNamhyung Kim if (!trace_file) { 348d01f4e8dSNamhyung Kim pr_err("failed to open trace_pipe\n"); 349a9af6be5SNamhyung Kim goto out_reset; 350d01f4e8dSNamhyung Kim } 351d01f4e8dSNamhyung Kim 352d01f4e8dSNamhyung Kim trace_fd = open(trace_file, O_RDONLY); 353d01f4e8dSNamhyung Kim 354d01f4e8dSNamhyung Kim put_tracing_file(trace_file); 355d01f4e8dSNamhyung Kim 356d01f4e8dSNamhyung Kim if (trace_fd < 0) { 357d01f4e8dSNamhyung Kim pr_err("failed to open trace_pipe\n"); 358a9af6be5SNamhyung Kim goto out_reset; 359d01f4e8dSNamhyung Kim } 360d01f4e8dSNamhyung Kim 361d01f4e8dSNamhyung Kim fcntl(trace_fd, F_SETFL, O_NONBLOCK); 362d01f4e8dSNamhyung Kim pollfd.fd = trace_fd; 363d01f4e8dSNamhyung Kim 364d01f4e8dSNamhyung Kim if (write_tracing_file("tracing_on", "1") < 0) { 365d01f4e8dSNamhyung Kim pr_err("can't enable tracing\n"); 366d01f4e8dSNamhyung Kim goto out_close_fd; 367d01f4e8dSNamhyung Kim } 368d01f4e8dSNamhyung Kim 369d01f4e8dSNamhyung Kim perf_evlist__start_workload(ftrace->evlist); 370d01f4e8dSNamhyung Kim 371d01f4e8dSNamhyung Kim while (!done) { 372d01f4e8dSNamhyung Kim if (poll(&pollfd, 1, -1) < 0) 373d01f4e8dSNamhyung Kim break; 374d01f4e8dSNamhyung Kim 375d01f4e8dSNamhyung Kim if (pollfd.revents & POLLIN) { 376d01f4e8dSNamhyung Kim int n = read(trace_fd, buf, sizeof(buf)); 377d01f4e8dSNamhyung Kim if (n < 0) 378d01f4e8dSNamhyung Kim break; 379d01f4e8dSNamhyung Kim if (fwrite(buf, n, 1, stdout) != 1) 380d01f4e8dSNamhyung Kim break; 381d01f4e8dSNamhyung Kim } 382d01f4e8dSNamhyung Kim } 383d01f4e8dSNamhyung Kim 384d01f4e8dSNamhyung Kim write_tracing_file("tracing_on", "0"); 385d01f4e8dSNamhyung Kim 386d01f4e8dSNamhyung Kim /* read remaining buffer contents */ 387d01f4e8dSNamhyung Kim while (true) { 388d01f4e8dSNamhyung Kim int n = read(trace_fd, buf, sizeof(buf)); 389d01f4e8dSNamhyung Kim if (n <= 0) 390d01f4e8dSNamhyung Kim break; 391d01f4e8dSNamhyung Kim if (fwrite(buf, n, 1, stdout) != 1) 392d01f4e8dSNamhyung Kim break; 393d01f4e8dSNamhyung Kim } 394d01f4e8dSNamhyung Kim 395d01f4e8dSNamhyung Kim out_close_fd: 396d01f4e8dSNamhyung Kim close(trace_fd); 397a9af6be5SNamhyung Kim out_reset: 398d01f4e8dSNamhyung Kim reset_tracing_files(ftrace); 399a9af6be5SNamhyung Kim out: 400d01f4e8dSNamhyung Kim return done ? 0 : -1; 401d01f4e8dSNamhyung Kim } 402d01f4e8dSNamhyung Kim 403b05d1093STaeung Song static int perf_ftrace_config(const char *var, const char *value, void *cb) 404b05d1093STaeung Song { 405b05d1093STaeung Song struct perf_ftrace *ftrace = cb; 406b05d1093STaeung Song 4078e99b6d4SArnaldo Carvalho de Melo if (!strstarts(var, "ftrace.")) 408b05d1093STaeung Song return 0; 409b05d1093STaeung Song 410b05d1093STaeung Song if (strcmp(var, "ftrace.tracer")) 411b05d1093STaeung Song return -1; 412b05d1093STaeung Song 413b05d1093STaeung Song if (!strcmp(value, "function_graph") || 414b05d1093STaeung Song !strcmp(value, "function")) { 415b05d1093STaeung Song ftrace->tracer = value; 416b05d1093STaeung Song return 0; 417b05d1093STaeung Song } 418b05d1093STaeung Song 419b05d1093STaeung Song pr_err("Please select \"function_graph\" (default) or \"function\"\n"); 420b05d1093STaeung Song return -1; 421b05d1093STaeung Song } 422b05d1093STaeung Song 42378b83e8bSNamhyung Kim static int parse_filter_func(const struct option *opt, const char *str, 42478b83e8bSNamhyung Kim int unset __maybe_unused) 42578b83e8bSNamhyung Kim { 42678b83e8bSNamhyung Kim struct list_head *head = opt->value; 42778b83e8bSNamhyung Kim struct filter_entry *entry; 42878b83e8bSNamhyung Kim 42978b83e8bSNamhyung Kim entry = malloc(sizeof(*entry) + strlen(str) + 1); 43078b83e8bSNamhyung Kim if (entry == NULL) 43178b83e8bSNamhyung Kim return -ENOMEM; 43278b83e8bSNamhyung Kim 43378b83e8bSNamhyung Kim strcpy(entry->name, str); 43478b83e8bSNamhyung Kim list_add_tail(&entry->list, head); 43578b83e8bSNamhyung Kim 43678b83e8bSNamhyung Kim return 0; 43778b83e8bSNamhyung Kim } 43878b83e8bSNamhyung Kim 43978b83e8bSNamhyung Kim static void delete_filter_func(struct list_head *head) 44078b83e8bSNamhyung Kim { 44178b83e8bSNamhyung Kim struct filter_entry *pos, *tmp; 44278b83e8bSNamhyung Kim 44378b83e8bSNamhyung Kim list_for_each_entry_safe(pos, tmp, head, list) { 444e56fbc9dSArnaldo Carvalho de Melo list_del_init(&pos->list); 44578b83e8bSNamhyung Kim free(pos); 44678b83e8bSNamhyung Kim } 44778b83e8bSNamhyung Kim } 44878b83e8bSNamhyung Kim 449b0ad8ea6SArnaldo Carvalho de Melo int cmd_ftrace(int argc, const char **argv) 450d01f4e8dSNamhyung Kim { 451d01f4e8dSNamhyung Kim int ret; 452d01f4e8dSNamhyung Kim struct perf_ftrace ftrace = { 453bf062bd2STaeung Song .tracer = DEFAULT_TRACER, 454d01f4e8dSNamhyung Kim .target = { .uid = UINT_MAX, }, 455d01f4e8dSNamhyung Kim }; 456d01f4e8dSNamhyung Kim const char * const ftrace_usage[] = { 457a9af6be5SNamhyung Kim "perf ftrace [<options>] [<command>]", 458d01f4e8dSNamhyung Kim "perf ftrace [<options>] -- <command> [<options>]", 459d01f4e8dSNamhyung Kim NULL 460d01f4e8dSNamhyung Kim }; 461d01f4e8dSNamhyung Kim const struct option ftrace_options[] = { 462d01f4e8dSNamhyung Kim OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", 463ec347870SArnaldo Carvalho de Melo "tracer to use: function_graph(default) or function"), 464a9af6be5SNamhyung Kim OPT_STRING('p', "pid", &ftrace.target.pid, "pid", 465a9af6be5SNamhyung Kim "trace on existing process id"), 466d01f4e8dSNamhyung Kim OPT_INCR('v', "verbose", &verbose, 467d01f4e8dSNamhyung Kim "be more verbose"), 468dc231032SNamhyung Kim OPT_BOOLEAN('a', "all-cpus", &ftrace.target.system_wide, 469dc231032SNamhyung Kim "system-wide collection from all CPUs"), 470dc231032SNamhyung Kim OPT_STRING('C', "cpu", &ftrace.target.cpu_list, "cpu", 471dc231032SNamhyung Kim "list of cpus to monitor"), 47278b83e8bSNamhyung Kim OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func", 47378b83e8bSNamhyung Kim "trace given functions only", parse_filter_func), 47478b83e8bSNamhyung Kim OPT_CALLBACK('N', "notrace-funcs", &ftrace.notrace, "func", 47578b83e8bSNamhyung Kim "do not trace given functions", parse_filter_func), 47678b83e8bSNamhyung Kim OPT_CALLBACK('G', "graph-funcs", &ftrace.graph_funcs, "func", 47778b83e8bSNamhyung Kim "Set graph filter on given functions", parse_filter_func), 47878b83e8bSNamhyung Kim OPT_CALLBACK('g', "nograph-funcs", &ftrace.nograph_funcs, "func", 47978b83e8bSNamhyung Kim "Set nograph filter on given functions", parse_filter_func), 4801096c35aSNamhyung Kim OPT_INTEGER('D', "graph-depth", &ftrace.graph_depth, 4811096c35aSNamhyung Kim "Max depth for function graph tracer"), 482d01f4e8dSNamhyung Kim OPT_END() 483d01f4e8dSNamhyung Kim }; 484d01f4e8dSNamhyung Kim 48578b83e8bSNamhyung Kim INIT_LIST_HEAD(&ftrace.filters); 48678b83e8bSNamhyung Kim INIT_LIST_HEAD(&ftrace.notrace); 48778b83e8bSNamhyung Kim INIT_LIST_HEAD(&ftrace.graph_funcs); 48878b83e8bSNamhyung Kim INIT_LIST_HEAD(&ftrace.nograph_funcs); 48978b83e8bSNamhyung Kim 490b05d1093STaeung Song ret = perf_config(perf_ftrace_config, &ftrace); 491b05d1093STaeung Song if (ret < 0) 492b05d1093STaeung Song return -1; 493b05d1093STaeung Song 494d01f4e8dSNamhyung Kim argc = parse_options(argc, argv, ftrace_options, ftrace_usage, 495d01f4e8dSNamhyung Kim PARSE_OPT_STOP_AT_NON_OPTION); 496a9af6be5SNamhyung Kim if (!argc && target__none(&ftrace.target)) 497*452b0d16SChangbin Du ftrace.target.system_wide = true; 498d01f4e8dSNamhyung Kim 499a9af6be5SNamhyung Kim ret = target__validate(&ftrace.target); 500a9af6be5SNamhyung Kim if (ret) { 501a9af6be5SNamhyung Kim char errbuf[512]; 502a9af6be5SNamhyung Kim 503a9af6be5SNamhyung Kim target__strerror(&ftrace.target, ret, errbuf, 512); 504a9af6be5SNamhyung Kim pr_err("%s\n", errbuf); 50578b83e8bSNamhyung Kim goto out_delete_filters; 506a9af6be5SNamhyung Kim } 507a9af6be5SNamhyung Kim 5080f98b11cSJiri Olsa ftrace.evlist = evlist__new(); 50978b83e8bSNamhyung Kim if (ftrace.evlist == NULL) { 51078b83e8bSNamhyung Kim ret = -ENOMEM; 51178b83e8bSNamhyung Kim goto out_delete_filters; 51278b83e8bSNamhyung Kim } 513d01f4e8dSNamhyung Kim 514d01f4e8dSNamhyung Kim ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target); 515d01f4e8dSNamhyung Kim if (ret < 0) 516d01f4e8dSNamhyung Kim goto out_delete_evlist; 517d01f4e8dSNamhyung Kim 518d01f4e8dSNamhyung Kim ret = __cmd_ftrace(&ftrace, argc, argv); 519d01f4e8dSNamhyung Kim 520d01f4e8dSNamhyung Kim out_delete_evlist: 521c12995a5SJiri Olsa evlist__delete(ftrace.evlist); 522d01f4e8dSNamhyung Kim 52378b83e8bSNamhyung Kim out_delete_filters: 52478b83e8bSNamhyung Kim delete_filter_func(&ftrace.filters); 52578b83e8bSNamhyung Kim delete_filter_func(&ftrace.notrace); 52678b83e8bSNamhyung Kim delete_filter_func(&ftrace.graph_funcs); 52778b83e8bSNamhyung Kim delete_filter_func(&ftrace.nograph_funcs); 52878b83e8bSNamhyung Kim 529d01f4e8dSNamhyung Kim return ret; 530d01f4e8dSNamhyung Kim } 531