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 #include "perf.h" 10d01f4e8dSNamhyung Kim 11a43783aeSArnaldo Carvalho de Melo #include <errno.h> 12d01f4e8dSNamhyung Kim #include <unistd.h> 13d01f4e8dSNamhyung Kim #include <signal.h> 14a9af6be5SNamhyung Kim #include <fcntl.h> 154208735dSArnaldo Carvalho de Melo #include <poll.h> 16d01f4e8dSNamhyung Kim 17d01f4e8dSNamhyung Kim #include "debug.h" 18d01f4e8dSNamhyung Kim #include <subcmd/parse-options.h> 1920a9ed28SArnaldo Carvalho de Melo #include <api/fs/tracing_path.h> 20d01f4e8dSNamhyung Kim #include "evlist.h" 21d01f4e8dSNamhyung Kim #include "target.h" 22dc231032SNamhyung Kim #include "cpumap.h" 23d01f4e8dSNamhyung Kim #include "thread_map.h" 24b05d1093STaeung Song #include "util/config.h" 25d01f4e8dSNamhyung Kim 26d01f4e8dSNamhyung Kim 27d01f4e8dSNamhyung Kim #define DEFAULT_TRACER "function_graph" 28d01f4e8dSNamhyung Kim 29d01f4e8dSNamhyung Kim struct perf_ftrace { 3063503dbaSJiri Olsa struct evlist *evlist; 31d01f4e8dSNamhyung Kim struct target target; 32d01f4e8dSNamhyung Kim const char *tracer; 3378b83e8bSNamhyung Kim struct list_head filters; 3478b83e8bSNamhyung Kim struct list_head notrace; 3578b83e8bSNamhyung Kim struct list_head graph_funcs; 3678b83e8bSNamhyung Kim struct list_head nograph_funcs; 371096c35aSNamhyung Kim int graph_depth; 3878b83e8bSNamhyung Kim }; 3978b83e8bSNamhyung Kim 4078b83e8bSNamhyung Kim struct filter_entry { 4178b83e8bSNamhyung Kim struct list_head list; 4278b83e8bSNamhyung Kim char name[]; 43d01f4e8dSNamhyung Kim }; 44d01f4e8dSNamhyung Kim 45d01f4e8dSNamhyung Kim static bool done; 46d01f4e8dSNamhyung Kim 47d01f4e8dSNamhyung Kim static void sig_handler(int sig __maybe_unused) 48d01f4e8dSNamhyung Kim { 49d01f4e8dSNamhyung Kim done = true; 50d01f4e8dSNamhyung Kim } 51d01f4e8dSNamhyung Kim 52d01f4e8dSNamhyung Kim /* 53d01f4e8dSNamhyung Kim * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since 54d01f4e8dSNamhyung Kim * we asked by setting its exec_error to the function below, 55d01f4e8dSNamhyung Kim * ftrace__workload_exec_failed_signal. 56d01f4e8dSNamhyung Kim * 57d01f4e8dSNamhyung Kim * XXX We need to handle this more appropriately, emitting an error, etc. 58d01f4e8dSNamhyung Kim */ 59d01f4e8dSNamhyung Kim static void ftrace__workload_exec_failed_signal(int signo __maybe_unused, 60d01f4e8dSNamhyung Kim siginfo_t *info __maybe_unused, 61d01f4e8dSNamhyung Kim void *ucontext __maybe_unused) 62d01f4e8dSNamhyung Kim { 63d01f4e8dSNamhyung Kim /* workload_exec_errno = info->si_value.sival_int; */ 64d01f4e8dSNamhyung Kim done = true; 65d01f4e8dSNamhyung Kim } 66d01f4e8dSNamhyung Kim 67a9af6be5SNamhyung Kim static int __write_tracing_file(const char *name, const char *val, bool append) 68d01f4e8dSNamhyung Kim { 69d01f4e8dSNamhyung Kim char *file; 70d01f4e8dSNamhyung Kim int fd, ret = -1; 71d01f4e8dSNamhyung Kim ssize_t size = strlen(val); 72a9af6be5SNamhyung Kim int flags = O_WRONLY; 73e7bd9ba2SNamhyung Kim char errbuf[512]; 7463cd02d8SChangbin Du char *val_copy; 75d01f4e8dSNamhyung Kim 76d01f4e8dSNamhyung Kim file = get_tracing_file(name); 77d01f4e8dSNamhyung Kim if (!file) { 78d01f4e8dSNamhyung Kim pr_debug("cannot get tracing file: %s\n", name); 79d01f4e8dSNamhyung Kim return -1; 80d01f4e8dSNamhyung Kim } 81d01f4e8dSNamhyung Kim 82a9af6be5SNamhyung Kim if (append) 83a9af6be5SNamhyung Kim flags |= O_APPEND; 84a9af6be5SNamhyung Kim else 85a9af6be5SNamhyung Kim flags |= O_TRUNC; 86a9af6be5SNamhyung Kim 87a9af6be5SNamhyung Kim fd = open(file, flags); 88d01f4e8dSNamhyung Kim if (fd < 0) { 89e7bd9ba2SNamhyung Kim pr_debug("cannot open tracing file: %s: %s\n", 90e7bd9ba2SNamhyung Kim name, str_error_r(errno, errbuf, sizeof(errbuf))); 91d01f4e8dSNamhyung Kim goto out; 92d01f4e8dSNamhyung Kim } 93d01f4e8dSNamhyung Kim 9463cd02d8SChangbin Du /* 9563cd02d8SChangbin Du * Copy the original value and append a '\n'. Without this, 9663cd02d8SChangbin Du * the kernel can hide possible errors. 9763cd02d8SChangbin Du */ 9863cd02d8SChangbin Du val_copy = strdup(val); 9963cd02d8SChangbin Du if (!val_copy) 10063cd02d8SChangbin Du goto out_close; 10163cd02d8SChangbin Du val_copy[size] = '\n'; 10263cd02d8SChangbin Du 10363cd02d8SChangbin Du if (write(fd, val_copy, size + 1) == size + 1) 104d01f4e8dSNamhyung Kim ret = 0; 105d01f4e8dSNamhyung Kim else 106e7bd9ba2SNamhyung Kim pr_debug("write '%s' to tracing/%s failed: %s\n", 107e7bd9ba2SNamhyung Kim val, name, str_error_r(errno, errbuf, sizeof(errbuf))); 108d01f4e8dSNamhyung Kim 10963cd02d8SChangbin Du free(val_copy); 11063cd02d8SChangbin Du out_close: 111d01f4e8dSNamhyung Kim close(fd); 112d01f4e8dSNamhyung Kim out: 113d01f4e8dSNamhyung Kim put_tracing_file(file); 114d01f4e8dSNamhyung Kim return ret; 115d01f4e8dSNamhyung Kim } 116d01f4e8dSNamhyung Kim 117a9af6be5SNamhyung Kim static int write_tracing_file(const char *name, const char *val) 118a9af6be5SNamhyung Kim { 119a9af6be5SNamhyung Kim return __write_tracing_file(name, val, false); 120a9af6be5SNamhyung Kim } 121a9af6be5SNamhyung Kim 122a9af6be5SNamhyung Kim static int append_tracing_file(const char *name, const char *val) 123a9af6be5SNamhyung Kim { 124a9af6be5SNamhyung Kim return __write_tracing_file(name, val, true); 125a9af6be5SNamhyung Kim } 126a9af6be5SNamhyung Kim 127dc231032SNamhyung Kim static int reset_tracing_cpu(void); 12878b83e8bSNamhyung Kim static void reset_tracing_filters(void); 129dc231032SNamhyung Kim 130d01f4e8dSNamhyung Kim static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused) 131d01f4e8dSNamhyung Kim { 132d01f4e8dSNamhyung Kim if (write_tracing_file("tracing_on", "0") < 0) 133d01f4e8dSNamhyung Kim return -1; 134d01f4e8dSNamhyung Kim 135d01f4e8dSNamhyung Kim if (write_tracing_file("current_tracer", "nop") < 0) 136d01f4e8dSNamhyung Kim return -1; 137d01f4e8dSNamhyung Kim 138d01f4e8dSNamhyung Kim if (write_tracing_file("set_ftrace_pid", " ") < 0) 139d01f4e8dSNamhyung Kim return -1; 140d01f4e8dSNamhyung Kim 141dc231032SNamhyung Kim if (reset_tracing_cpu() < 0) 142dc231032SNamhyung Kim return -1; 143dc231032SNamhyung Kim 1441096c35aSNamhyung Kim if (write_tracing_file("max_graph_depth", "0") < 0) 1451096c35aSNamhyung Kim return -1; 1461096c35aSNamhyung Kim 14778b83e8bSNamhyung Kim reset_tracing_filters(); 148d01f4e8dSNamhyung Kim return 0; 149d01f4e8dSNamhyung Kim } 150d01f4e8dSNamhyung Kim 151a9af6be5SNamhyung Kim static int set_tracing_pid(struct perf_ftrace *ftrace) 152a9af6be5SNamhyung Kim { 153a9af6be5SNamhyung Kim int i; 154a9af6be5SNamhyung Kim char buf[16]; 155a9af6be5SNamhyung Kim 156a9af6be5SNamhyung Kim if (target__has_cpu(&ftrace->target)) 157a9af6be5SNamhyung Kim return 0; 158a9af6be5SNamhyung Kim 159a9af6be5SNamhyung Kim for (i = 0; i < thread_map__nr(ftrace->evlist->threads); i++) { 160a9af6be5SNamhyung Kim scnprintf(buf, sizeof(buf), "%d", 161a9af6be5SNamhyung Kim ftrace->evlist->threads->map[i]); 162a9af6be5SNamhyung Kim if (append_tracing_file("set_ftrace_pid", buf) < 0) 163a9af6be5SNamhyung Kim return -1; 164a9af6be5SNamhyung Kim } 165a9af6be5SNamhyung Kim return 0; 166a9af6be5SNamhyung Kim } 167a9af6be5SNamhyung Kim 168f854839bSJiri Olsa static int set_tracing_cpumask(struct perf_cpu_map *cpumap) 169dc231032SNamhyung Kim { 170dc231032SNamhyung Kim char *cpumask; 171dc231032SNamhyung Kim size_t mask_size; 172dc231032SNamhyung Kim int ret; 173dc231032SNamhyung Kim int last_cpu; 174dc231032SNamhyung Kim 175dc231032SNamhyung Kim last_cpu = cpu_map__cpu(cpumap, cpumap->nr - 1); 176dc231032SNamhyung Kim mask_size = (last_cpu + 3) / 4 + 1; 177dc231032SNamhyung Kim mask_size += last_cpu / 32; /* ',' is needed for every 32th cpus */ 178dc231032SNamhyung Kim 179dc231032SNamhyung Kim cpumask = malloc(mask_size); 180dc231032SNamhyung Kim if (cpumask == NULL) { 181dc231032SNamhyung Kim pr_debug("failed to allocate cpu mask\n"); 182dc231032SNamhyung Kim return -1; 183dc231032SNamhyung Kim } 184dc231032SNamhyung Kim 185dc231032SNamhyung Kim cpu_map__snprint_mask(cpumap, cpumask, mask_size); 186dc231032SNamhyung Kim 187dc231032SNamhyung Kim ret = write_tracing_file("tracing_cpumask", cpumask); 188dc231032SNamhyung Kim 189dc231032SNamhyung Kim free(cpumask); 190dc231032SNamhyung Kim return ret; 191dc231032SNamhyung Kim } 192dc231032SNamhyung Kim 193dc231032SNamhyung Kim static int set_tracing_cpu(struct perf_ftrace *ftrace) 194dc231032SNamhyung Kim { 195f854839bSJiri Olsa struct perf_cpu_map *cpumap = ftrace->evlist->cpus; 196dc231032SNamhyung Kim 197dc231032SNamhyung Kim if (!target__has_cpu(&ftrace->target)) 198dc231032SNamhyung Kim return 0; 199dc231032SNamhyung Kim 200dc231032SNamhyung Kim return set_tracing_cpumask(cpumap); 201dc231032SNamhyung Kim } 202dc231032SNamhyung Kim 203dc231032SNamhyung Kim static int reset_tracing_cpu(void) 204dc231032SNamhyung Kim { 205*9c3516d1SJiri Olsa struct perf_cpu_map *cpumap = perf_cpu_map__new(NULL); 206dc231032SNamhyung Kim int ret; 207dc231032SNamhyung Kim 208dc231032SNamhyung Kim ret = set_tracing_cpumask(cpumap); 20938f01d8dSJiri Olsa perf_cpu_map__put(cpumap); 210dc231032SNamhyung Kim return ret; 211dc231032SNamhyung Kim } 212dc231032SNamhyung Kim 21378b83e8bSNamhyung Kim static int __set_tracing_filter(const char *filter_file, struct list_head *funcs) 21478b83e8bSNamhyung Kim { 21578b83e8bSNamhyung Kim struct filter_entry *pos; 21678b83e8bSNamhyung Kim 21778b83e8bSNamhyung Kim list_for_each_entry(pos, funcs, list) { 21878b83e8bSNamhyung Kim if (append_tracing_file(filter_file, pos->name) < 0) 21978b83e8bSNamhyung Kim return -1; 22078b83e8bSNamhyung Kim } 22178b83e8bSNamhyung Kim 22278b83e8bSNamhyung Kim return 0; 22378b83e8bSNamhyung Kim } 22478b83e8bSNamhyung Kim 22578b83e8bSNamhyung Kim static int set_tracing_filters(struct perf_ftrace *ftrace) 22678b83e8bSNamhyung Kim { 22778b83e8bSNamhyung Kim int ret; 22878b83e8bSNamhyung Kim 22978b83e8bSNamhyung Kim ret = __set_tracing_filter("set_ftrace_filter", &ftrace->filters); 23078b83e8bSNamhyung Kim if (ret < 0) 23178b83e8bSNamhyung Kim return ret; 23278b83e8bSNamhyung Kim 23378b83e8bSNamhyung Kim ret = __set_tracing_filter("set_ftrace_notrace", &ftrace->notrace); 23478b83e8bSNamhyung Kim if (ret < 0) 23578b83e8bSNamhyung Kim return ret; 23678b83e8bSNamhyung Kim 23778b83e8bSNamhyung Kim ret = __set_tracing_filter("set_graph_function", &ftrace->graph_funcs); 23878b83e8bSNamhyung Kim if (ret < 0) 23978b83e8bSNamhyung Kim return ret; 24078b83e8bSNamhyung Kim 24178b83e8bSNamhyung Kim /* old kernels do not have this filter */ 24278b83e8bSNamhyung Kim __set_tracing_filter("set_graph_notrace", &ftrace->nograph_funcs); 24378b83e8bSNamhyung Kim 24478b83e8bSNamhyung Kim return ret; 24578b83e8bSNamhyung Kim } 24678b83e8bSNamhyung Kim 24778b83e8bSNamhyung Kim static void reset_tracing_filters(void) 24878b83e8bSNamhyung Kim { 24978b83e8bSNamhyung Kim write_tracing_file("set_ftrace_filter", " "); 25078b83e8bSNamhyung Kim write_tracing_file("set_ftrace_notrace", " "); 25178b83e8bSNamhyung Kim write_tracing_file("set_graph_function", " "); 25278b83e8bSNamhyung Kim write_tracing_file("set_graph_notrace", " "); 25378b83e8bSNamhyung Kim } 25478b83e8bSNamhyung Kim 2551096c35aSNamhyung Kim static int set_tracing_depth(struct perf_ftrace *ftrace) 2561096c35aSNamhyung Kim { 2571096c35aSNamhyung Kim char buf[16]; 2581096c35aSNamhyung Kim 2591096c35aSNamhyung Kim if (ftrace->graph_depth == 0) 2601096c35aSNamhyung Kim return 0; 2611096c35aSNamhyung Kim 2621096c35aSNamhyung Kim if (ftrace->graph_depth < 0) { 2631096c35aSNamhyung Kim pr_err("invalid graph depth: %d\n", ftrace->graph_depth); 2641096c35aSNamhyung Kim return -1; 2651096c35aSNamhyung Kim } 2661096c35aSNamhyung Kim 2671096c35aSNamhyung Kim snprintf(buf, sizeof(buf), "%d", ftrace->graph_depth); 2681096c35aSNamhyung Kim 2691096c35aSNamhyung Kim if (write_tracing_file("max_graph_depth", buf) < 0) 2701096c35aSNamhyung Kim return -1; 2711096c35aSNamhyung Kim 2721096c35aSNamhyung Kim return 0; 2731096c35aSNamhyung Kim } 2741096c35aSNamhyung Kim 275d01f4e8dSNamhyung Kim static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) 276d01f4e8dSNamhyung Kim { 277d01f4e8dSNamhyung Kim char *trace_file; 278d01f4e8dSNamhyung Kim int trace_fd; 279d01f4e8dSNamhyung Kim char buf[4096]; 280d01f4e8dSNamhyung Kim struct pollfd pollfd = { 281d01f4e8dSNamhyung Kim .events = POLLIN, 282d01f4e8dSNamhyung Kim }; 283d01f4e8dSNamhyung Kim 284d01f4e8dSNamhyung Kim if (geteuid() != 0) { 285d01f4e8dSNamhyung Kim pr_err("ftrace only works for root!\n"); 286d01f4e8dSNamhyung Kim return -1; 287d01f4e8dSNamhyung Kim } 288d01f4e8dSNamhyung Kim 289d01f4e8dSNamhyung Kim signal(SIGINT, sig_handler); 290d01f4e8dSNamhyung Kim signal(SIGUSR1, sig_handler); 291d01f4e8dSNamhyung Kim signal(SIGCHLD, sig_handler); 29258335964SNamhyung Kim signal(SIGPIPE, sig_handler); 293d01f4e8dSNamhyung Kim 29463cd02d8SChangbin Du if (reset_tracing_files(ftrace) < 0) { 29563cd02d8SChangbin Du pr_err("failed to reset ftrace\n"); 296a9af6be5SNamhyung Kim goto out; 29763cd02d8SChangbin Du } 298d01f4e8dSNamhyung Kim 299d01f4e8dSNamhyung Kim /* reset ftrace buffer */ 300d01f4e8dSNamhyung Kim if (write_tracing_file("trace", "0") < 0) 301d01f4e8dSNamhyung Kim goto out; 302d01f4e8dSNamhyung Kim 303a9af6be5SNamhyung Kim if (argc && perf_evlist__prepare_workload(ftrace->evlist, 304a9af6be5SNamhyung Kim &ftrace->target, argv, false, 305a9af6be5SNamhyung Kim ftrace__workload_exec_failed_signal) < 0) { 306d01f4e8dSNamhyung Kim goto out; 307a9af6be5SNamhyung Kim } 308a9af6be5SNamhyung Kim 309a9af6be5SNamhyung Kim if (set_tracing_pid(ftrace) < 0) { 310a9af6be5SNamhyung Kim pr_err("failed to set ftrace pid\n"); 311a9af6be5SNamhyung Kim goto out_reset; 312a9af6be5SNamhyung Kim } 313d01f4e8dSNamhyung Kim 314dc231032SNamhyung Kim if (set_tracing_cpu(ftrace) < 0) { 315dc231032SNamhyung Kim pr_err("failed to set tracing cpumask\n"); 316dc231032SNamhyung Kim goto out_reset; 317dc231032SNamhyung Kim } 318dc231032SNamhyung Kim 31978b83e8bSNamhyung Kim if (set_tracing_filters(ftrace) < 0) { 32078b83e8bSNamhyung Kim pr_err("failed to set tracing filters\n"); 32178b83e8bSNamhyung Kim goto out_reset; 32278b83e8bSNamhyung Kim } 32378b83e8bSNamhyung Kim 3241096c35aSNamhyung Kim if (set_tracing_depth(ftrace) < 0) { 3251096c35aSNamhyung Kim pr_err("failed to set graph depth\n"); 3261096c35aSNamhyung Kim goto out_reset; 3271096c35aSNamhyung Kim } 3281096c35aSNamhyung Kim 329d01f4e8dSNamhyung Kim if (write_tracing_file("current_tracer", ftrace->tracer) < 0) { 330d01f4e8dSNamhyung Kim pr_err("failed to set current_tracer to %s\n", ftrace->tracer); 331a9af6be5SNamhyung Kim goto out_reset; 332d01f4e8dSNamhyung Kim } 333d01f4e8dSNamhyung Kim 33429681bc5SNamhyung Kim setup_pager(); 33529681bc5SNamhyung Kim 336d01f4e8dSNamhyung Kim trace_file = get_tracing_file("trace_pipe"); 337d01f4e8dSNamhyung Kim if (!trace_file) { 338d01f4e8dSNamhyung Kim pr_err("failed to open trace_pipe\n"); 339a9af6be5SNamhyung Kim goto out_reset; 340d01f4e8dSNamhyung Kim } 341d01f4e8dSNamhyung Kim 342d01f4e8dSNamhyung Kim trace_fd = open(trace_file, O_RDONLY); 343d01f4e8dSNamhyung Kim 344d01f4e8dSNamhyung Kim put_tracing_file(trace_file); 345d01f4e8dSNamhyung Kim 346d01f4e8dSNamhyung Kim if (trace_fd < 0) { 347d01f4e8dSNamhyung Kim pr_err("failed to open trace_pipe\n"); 348a9af6be5SNamhyung Kim goto out_reset; 349d01f4e8dSNamhyung Kim } 350d01f4e8dSNamhyung Kim 351d01f4e8dSNamhyung Kim fcntl(trace_fd, F_SETFL, O_NONBLOCK); 352d01f4e8dSNamhyung Kim pollfd.fd = trace_fd; 353d01f4e8dSNamhyung Kim 354d01f4e8dSNamhyung Kim if (write_tracing_file("tracing_on", "1") < 0) { 355d01f4e8dSNamhyung Kim pr_err("can't enable tracing\n"); 356d01f4e8dSNamhyung Kim goto out_close_fd; 357d01f4e8dSNamhyung Kim } 358d01f4e8dSNamhyung Kim 359d01f4e8dSNamhyung Kim perf_evlist__start_workload(ftrace->evlist); 360d01f4e8dSNamhyung Kim 361d01f4e8dSNamhyung Kim while (!done) { 362d01f4e8dSNamhyung Kim if (poll(&pollfd, 1, -1) < 0) 363d01f4e8dSNamhyung Kim break; 364d01f4e8dSNamhyung Kim 365d01f4e8dSNamhyung Kim if (pollfd.revents & POLLIN) { 366d01f4e8dSNamhyung Kim int n = read(trace_fd, buf, sizeof(buf)); 367d01f4e8dSNamhyung Kim if (n < 0) 368d01f4e8dSNamhyung Kim break; 369d01f4e8dSNamhyung Kim if (fwrite(buf, n, 1, stdout) != 1) 370d01f4e8dSNamhyung Kim break; 371d01f4e8dSNamhyung Kim } 372d01f4e8dSNamhyung Kim } 373d01f4e8dSNamhyung Kim 374d01f4e8dSNamhyung Kim write_tracing_file("tracing_on", "0"); 375d01f4e8dSNamhyung Kim 376d01f4e8dSNamhyung Kim /* read remaining buffer contents */ 377d01f4e8dSNamhyung Kim while (true) { 378d01f4e8dSNamhyung Kim int n = read(trace_fd, buf, sizeof(buf)); 379d01f4e8dSNamhyung Kim if (n <= 0) 380d01f4e8dSNamhyung Kim break; 381d01f4e8dSNamhyung Kim if (fwrite(buf, n, 1, stdout) != 1) 382d01f4e8dSNamhyung Kim break; 383d01f4e8dSNamhyung Kim } 384d01f4e8dSNamhyung Kim 385d01f4e8dSNamhyung Kim out_close_fd: 386d01f4e8dSNamhyung Kim close(trace_fd); 387a9af6be5SNamhyung Kim out_reset: 388d01f4e8dSNamhyung Kim reset_tracing_files(ftrace); 389a9af6be5SNamhyung Kim out: 390d01f4e8dSNamhyung Kim return done ? 0 : -1; 391d01f4e8dSNamhyung Kim } 392d01f4e8dSNamhyung Kim 393b05d1093STaeung Song static int perf_ftrace_config(const char *var, const char *value, void *cb) 394b05d1093STaeung Song { 395b05d1093STaeung Song struct perf_ftrace *ftrace = cb; 396b05d1093STaeung Song 3978e99b6d4SArnaldo Carvalho de Melo if (!strstarts(var, "ftrace.")) 398b05d1093STaeung Song return 0; 399b05d1093STaeung Song 400b05d1093STaeung Song if (strcmp(var, "ftrace.tracer")) 401b05d1093STaeung Song return -1; 402b05d1093STaeung Song 403b05d1093STaeung Song if (!strcmp(value, "function_graph") || 404b05d1093STaeung Song !strcmp(value, "function")) { 405b05d1093STaeung Song ftrace->tracer = value; 406b05d1093STaeung Song return 0; 407b05d1093STaeung Song } 408b05d1093STaeung Song 409b05d1093STaeung Song pr_err("Please select \"function_graph\" (default) or \"function\"\n"); 410b05d1093STaeung Song return -1; 411b05d1093STaeung Song } 412b05d1093STaeung Song 41378b83e8bSNamhyung Kim static int parse_filter_func(const struct option *opt, const char *str, 41478b83e8bSNamhyung Kim int unset __maybe_unused) 41578b83e8bSNamhyung Kim { 41678b83e8bSNamhyung Kim struct list_head *head = opt->value; 41778b83e8bSNamhyung Kim struct filter_entry *entry; 41878b83e8bSNamhyung Kim 41978b83e8bSNamhyung Kim entry = malloc(sizeof(*entry) + strlen(str) + 1); 42078b83e8bSNamhyung Kim if (entry == NULL) 42178b83e8bSNamhyung Kim return -ENOMEM; 42278b83e8bSNamhyung Kim 42378b83e8bSNamhyung Kim strcpy(entry->name, str); 42478b83e8bSNamhyung Kim list_add_tail(&entry->list, head); 42578b83e8bSNamhyung Kim 42678b83e8bSNamhyung Kim return 0; 42778b83e8bSNamhyung Kim } 42878b83e8bSNamhyung Kim 42978b83e8bSNamhyung Kim static void delete_filter_func(struct list_head *head) 43078b83e8bSNamhyung Kim { 43178b83e8bSNamhyung Kim struct filter_entry *pos, *tmp; 43278b83e8bSNamhyung Kim 43378b83e8bSNamhyung Kim list_for_each_entry_safe(pos, tmp, head, list) { 434e56fbc9dSArnaldo Carvalho de Melo list_del_init(&pos->list); 43578b83e8bSNamhyung Kim free(pos); 43678b83e8bSNamhyung Kim } 43778b83e8bSNamhyung Kim } 43878b83e8bSNamhyung Kim 439b0ad8ea6SArnaldo Carvalho de Melo int cmd_ftrace(int argc, const char **argv) 440d01f4e8dSNamhyung Kim { 441d01f4e8dSNamhyung Kim int ret; 442d01f4e8dSNamhyung Kim struct perf_ftrace ftrace = { 443bf062bd2STaeung Song .tracer = DEFAULT_TRACER, 444d01f4e8dSNamhyung Kim .target = { .uid = UINT_MAX, }, 445d01f4e8dSNamhyung Kim }; 446d01f4e8dSNamhyung Kim const char * const ftrace_usage[] = { 447a9af6be5SNamhyung Kim "perf ftrace [<options>] [<command>]", 448d01f4e8dSNamhyung Kim "perf ftrace [<options>] -- <command> [<options>]", 449d01f4e8dSNamhyung Kim NULL 450d01f4e8dSNamhyung Kim }; 451d01f4e8dSNamhyung Kim const struct option ftrace_options[] = { 452d01f4e8dSNamhyung Kim OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", 453ec347870SArnaldo Carvalho de Melo "tracer to use: function_graph(default) or function"), 454a9af6be5SNamhyung Kim OPT_STRING('p', "pid", &ftrace.target.pid, "pid", 455a9af6be5SNamhyung Kim "trace on existing process id"), 456d01f4e8dSNamhyung Kim OPT_INCR('v', "verbose", &verbose, 457d01f4e8dSNamhyung Kim "be more verbose"), 458dc231032SNamhyung Kim OPT_BOOLEAN('a', "all-cpus", &ftrace.target.system_wide, 459dc231032SNamhyung Kim "system-wide collection from all CPUs"), 460dc231032SNamhyung Kim OPT_STRING('C', "cpu", &ftrace.target.cpu_list, "cpu", 461dc231032SNamhyung Kim "list of cpus to monitor"), 46278b83e8bSNamhyung Kim OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func", 46378b83e8bSNamhyung Kim "trace given functions only", parse_filter_func), 46478b83e8bSNamhyung Kim OPT_CALLBACK('N', "notrace-funcs", &ftrace.notrace, "func", 46578b83e8bSNamhyung Kim "do not trace given functions", parse_filter_func), 46678b83e8bSNamhyung Kim OPT_CALLBACK('G', "graph-funcs", &ftrace.graph_funcs, "func", 46778b83e8bSNamhyung Kim "Set graph filter on given functions", parse_filter_func), 46878b83e8bSNamhyung Kim OPT_CALLBACK('g', "nograph-funcs", &ftrace.nograph_funcs, "func", 46978b83e8bSNamhyung Kim "Set nograph filter on given functions", parse_filter_func), 4701096c35aSNamhyung Kim OPT_INTEGER('D', "graph-depth", &ftrace.graph_depth, 4711096c35aSNamhyung Kim "Max depth for function graph tracer"), 472d01f4e8dSNamhyung Kim OPT_END() 473d01f4e8dSNamhyung Kim }; 474d01f4e8dSNamhyung Kim 47578b83e8bSNamhyung Kim INIT_LIST_HEAD(&ftrace.filters); 47678b83e8bSNamhyung Kim INIT_LIST_HEAD(&ftrace.notrace); 47778b83e8bSNamhyung Kim INIT_LIST_HEAD(&ftrace.graph_funcs); 47878b83e8bSNamhyung Kim INIT_LIST_HEAD(&ftrace.nograph_funcs); 47978b83e8bSNamhyung Kim 480b05d1093STaeung Song ret = perf_config(perf_ftrace_config, &ftrace); 481b05d1093STaeung Song if (ret < 0) 482b05d1093STaeung Song return -1; 483b05d1093STaeung Song 484d01f4e8dSNamhyung Kim argc = parse_options(argc, argv, ftrace_options, ftrace_usage, 485d01f4e8dSNamhyung Kim PARSE_OPT_STOP_AT_NON_OPTION); 486a9af6be5SNamhyung Kim if (!argc && target__none(&ftrace.target)) 487d01f4e8dSNamhyung Kim usage_with_options(ftrace_usage, ftrace_options); 488d01f4e8dSNamhyung Kim 489a9af6be5SNamhyung Kim ret = target__validate(&ftrace.target); 490a9af6be5SNamhyung Kim if (ret) { 491a9af6be5SNamhyung Kim char errbuf[512]; 492a9af6be5SNamhyung Kim 493a9af6be5SNamhyung Kim target__strerror(&ftrace.target, ret, errbuf, 512); 494a9af6be5SNamhyung Kim pr_err("%s\n", errbuf); 49578b83e8bSNamhyung Kim goto out_delete_filters; 496a9af6be5SNamhyung Kim } 497a9af6be5SNamhyung Kim 4980f98b11cSJiri Olsa ftrace.evlist = evlist__new(); 49978b83e8bSNamhyung Kim if (ftrace.evlist == NULL) { 50078b83e8bSNamhyung Kim ret = -ENOMEM; 50178b83e8bSNamhyung Kim goto out_delete_filters; 50278b83e8bSNamhyung Kim } 503d01f4e8dSNamhyung Kim 504d01f4e8dSNamhyung Kim ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target); 505d01f4e8dSNamhyung Kim if (ret < 0) 506d01f4e8dSNamhyung Kim goto out_delete_evlist; 507d01f4e8dSNamhyung Kim 508d01f4e8dSNamhyung Kim ret = __cmd_ftrace(&ftrace, argc, argv); 509d01f4e8dSNamhyung Kim 510d01f4e8dSNamhyung Kim out_delete_evlist: 511c12995a5SJiri Olsa evlist__delete(ftrace.evlist); 512d01f4e8dSNamhyung Kim 51378b83e8bSNamhyung Kim out_delete_filters: 51478b83e8bSNamhyung Kim delete_filter_func(&ftrace.filters); 51578b83e8bSNamhyung Kim delete_filter_func(&ftrace.notrace); 51678b83e8bSNamhyung Kim delete_filter_func(&ftrace.graph_funcs); 51778b83e8bSNamhyung Kim delete_filter_func(&ftrace.nograph_funcs); 51878b83e8bSNamhyung Kim 519d01f4e8dSNamhyung Kim return ret; 520d01f4e8dSNamhyung Kim } 521