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