1d01f4e8dSNamhyung Kim /* 2d01f4e8dSNamhyung Kim * builtin-ftrace.c 3d01f4e8dSNamhyung Kim * 4d01f4e8dSNamhyung Kim * Copyright (c) 2013 LG Electronics, Namhyung Kim <namhyung@kernel.org> 5d01f4e8dSNamhyung Kim * 6d01f4e8dSNamhyung Kim * Released under the GPL v2. 7d01f4e8dSNamhyung Kim */ 8d01f4e8dSNamhyung Kim 9d01f4e8dSNamhyung Kim #include "builtin.h" 10d01f4e8dSNamhyung Kim #include "perf.h" 11d01f4e8dSNamhyung Kim 12d01f4e8dSNamhyung Kim #include <unistd.h> 13d01f4e8dSNamhyung Kim #include <signal.h> 14d01f4e8dSNamhyung Kim 15d01f4e8dSNamhyung Kim #include "debug.h" 16d01f4e8dSNamhyung Kim #include <subcmd/parse-options.h> 17d01f4e8dSNamhyung Kim #include "evlist.h" 18d01f4e8dSNamhyung Kim #include "target.h" 19d01f4e8dSNamhyung Kim #include "thread_map.h" 20d01f4e8dSNamhyung Kim 21d01f4e8dSNamhyung Kim 22d01f4e8dSNamhyung Kim #define DEFAULT_TRACER "function_graph" 23d01f4e8dSNamhyung Kim 24d01f4e8dSNamhyung Kim struct perf_ftrace { 25d01f4e8dSNamhyung Kim struct perf_evlist *evlist; 26d01f4e8dSNamhyung Kim struct target target; 27d01f4e8dSNamhyung Kim const char *tracer; 28d01f4e8dSNamhyung Kim }; 29d01f4e8dSNamhyung Kim 30d01f4e8dSNamhyung Kim static bool done; 31d01f4e8dSNamhyung Kim 32d01f4e8dSNamhyung Kim static void sig_handler(int sig __maybe_unused) 33d01f4e8dSNamhyung Kim { 34d01f4e8dSNamhyung Kim done = true; 35d01f4e8dSNamhyung Kim } 36d01f4e8dSNamhyung Kim 37d01f4e8dSNamhyung Kim /* 38d01f4e8dSNamhyung Kim * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since 39d01f4e8dSNamhyung Kim * we asked by setting its exec_error to the function below, 40d01f4e8dSNamhyung Kim * ftrace__workload_exec_failed_signal. 41d01f4e8dSNamhyung Kim * 42d01f4e8dSNamhyung Kim * XXX We need to handle this more appropriately, emitting an error, etc. 43d01f4e8dSNamhyung Kim */ 44d01f4e8dSNamhyung Kim static void ftrace__workload_exec_failed_signal(int signo __maybe_unused, 45d01f4e8dSNamhyung Kim siginfo_t *info __maybe_unused, 46d01f4e8dSNamhyung Kim void *ucontext __maybe_unused) 47d01f4e8dSNamhyung Kim { 48d01f4e8dSNamhyung Kim /* workload_exec_errno = info->si_value.sival_int; */ 49d01f4e8dSNamhyung Kim done = true; 50d01f4e8dSNamhyung Kim } 51d01f4e8dSNamhyung Kim 52d01f4e8dSNamhyung Kim static int write_tracing_file(const char *name, const char *val) 53d01f4e8dSNamhyung Kim { 54d01f4e8dSNamhyung Kim char *file; 55d01f4e8dSNamhyung Kim int fd, ret = -1; 56d01f4e8dSNamhyung Kim ssize_t size = strlen(val); 57d01f4e8dSNamhyung Kim 58d01f4e8dSNamhyung Kim file = get_tracing_file(name); 59d01f4e8dSNamhyung Kim if (!file) { 60d01f4e8dSNamhyung Kim pr_debug("cannot get tracing file: %s\n", name); 61d01f4e8dSNamhyung Kim return -1; 62d01f4e8dSNamhyung Kim } 63d01f4e8dSNamhyung Kim 64d01f4e8dSNamhyung Kim fd = open(file, O_WRONLY); 65d01f4e8dSNamhyung Kim if (fd < 0) { 66d01f4e8dSNamhyung Kim pr_debug("cannot open tracing file: %s\n", name); 67d01f4e8dSNamhyung Kim goto out; 68d01f4e8dSNamhyung Kim } 69d01f4e8dSNamhyung Kim 70d01f4e8dSNamhyung Kim if (write(fd, val, size) == size) 71d01f4e8dSNamhyung Kim ret = 0; 72d01f4e8dSNamhyung Kim else 73d01f4e8dSNamhyung Kim pr_debug("write '%s' to tracing/%s failed\n", val, name); 74d01f4e8dSNamhyung Kim 75d01f4e8dSNamhyung Kim close(fd); 76d01f4e8dSNamhyung Kim out: 77d01f4e8dSNamhyung Kim put_tracing_file(file); 78d01f4e8dSNamhyung Kim return ret; 79d01f4e8dSNamhyung Kim } 80d01f4e8dSNamhyung Kim 81d01f4e8dSNamhyung Kim static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused) 82d01f4e8dSNamhyung Kim { 83d01f4e8dSNamhyung Kim if (write_tracing_file("tracing_on", "0") < 0) 84d01f4e8dSNamhyung Kim return -1; 85d01f4e8dSNamhyung Kim 86d01f4e8dSNamhyung Kim if (write_tracing_file("current_tracer", "nop") < 0) 87d01f4e8dSNamhyung Kim return -1; 88d01f4e8dSNamhyung Kim 89d01f4e8dSNamhyung Kim if (write_tracing_file("set_ftrace_pid", " ") < 0) 90d01f4e8dSNamhyung Kim return -1; 91d01f4e8dSNamhyung Kim 92d01f4e8dSNamhyung Kim return 0; 93d01f4e8dSNamhyung Kim } 94d01f4e8dSNamhyung Kim 95d01f4e8dSNamhyung Kim static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) 96d01f4e8dSNamhyung Kim { 97d01f4e8dSNamhyung Kim char *trace_file; 98d01f4e8dSNamhyung Kim int trace_fd; 99d01f4e8dSNamhyung Kim char *trace_pid; 100d01f4e8dSNamhyung Kim char buf[4096]; 101d01f4e8dSNamhyung Kim struct pollfd pollfd = { 102d01f4e8dSNamhyung Kim .events = POLLIN, 103d01f4e8dSNamhyung Kim }; 104d01f4e8dSNamhyung Kim 105d01f4e8dSNamhyung Kim if (geteuid() != 0) { 106d01f4e8dSNamhyung Kim pr_err("ftrace only works for root!\n"); 107d01f4e8dSNamhyung Kim return -1; 108d01f4e8dSNamhyung Kim } 109d01f4e8dSNamhyung Kim 110d01f4e8dSNamhyung Kim if (argc < 1) 111d01f4e8dSNamhyung Kim return -1; 112d01f4e8dSNamhyung Kim 113d01f4e8dSNamhyung Kim signal(SIGINT, sig_handler); 114d01f4e8dSNamhyung Kim signal(SIGUSR1, sig_handler); 115d01f4e8dSNamhyung Kim signal(SIGCHLD, sig_handler); 116d01f4e8dSNamhyung Kim 117d01f4e8dSNamhyung Kim reset_tracing_files(ftrace); 118d01f4e8dSNamhyung Kim 119d01f4e8dSNamhyung Kim /* reset ftrace buffer */ 120d01f4e8dSNamhyung Kim if (write_tracing_file("trace", "0") < 0) 121d01f4e8dSNamhyung Kim goto out; 122d01f4e8dSNamhyung Kim 123d01f4e8dSNamhyung Kim if (perf_evlist__prepare_workload(ftrace->evlist, &ftrace->target, 124d01f4e8dSNamhyung Kim argv, false, ftrace__workload_exec_failed_signal) < 0) 125d01f4e8dSNamhyung Kim goto out; 126d01f4e8dSNamhyung Kim 127d01f4e8dSNamhyung Kim if (write_tracing_file("current_tracer", ftrace->tracer) < 0) { 128d01f4e8dSNamhyung Kim pr_err("failed to set current_tracer to %s\n", ftrace->tracer); 129d01f4e8dSNamhyung Kim goto out; 130d01f4e8dSNamhyung Kim } 131d01f4e8dSNamhyung Kim 132d01f4e8dSNamhyung Kim if (asprintf(&trace_pid, "%d", thread_map__pid(ftrace->evlist->threads, 0)) < 0) { 133d01f4e8dSNamhyung Kim pr_err("failed to allocate pid string\n"); 134d01f4e8dSNamhyung Kim goto out; 135d01f4e8dSNamhyung Kim } 136d01f4e8dSNamhyung Kim 137d01f4e8dSNamhyung Kim if (write_tracing_file("set_ftrace_pid", trace_pid) < 0) { 138d01f4e8dSNamhyung Kim pr_err("failed to set pid: %s\n", trace_pid); 139d01f4e8dSNamhyung Kim goto out_free_pid; 140d01f4e8dSNamhyung Kim } 141d01f4e8dSNamhyung Kim 142d01f4e8dSNamhyung Kim trace_file = get_tracing_file("trace_pipe"); 143d01f4e8dSNamhyung Kim if (!trace_file) { 144d01f4e8dSNamhyung Kim pr_err("failed to open trace_pipe\n"); 145d01f4e8dSNamhyung Kim goto out_free_pid; 146d01f4e8dSNamhyung Kim } 147d01f4e8dSNamhyung Kim 148d01f4e8dSNamhyung Kim trace_fd = open(trace_file, O_RDONLY); 149d01f4e8dSNamhyung Kim 150d01f4e8dSNamhyung Kim put_tracing_file(trace_file); 151d01f4e8dSNamhyung Kim 152d01f4e8dSNamhyung Kim if (trace_fd < 0) { 153d01f4e8dSNamhyung Kim pr_err("failed to open trace_pipe\n"); 154d01f4e8dSNamhyung Kim goto out_free_pid; 155d01f4e8dSNamhyung Kim } 156d01f4e8dSNamhyung Kim 157d01f4e8dSNamhyung Kim fcntl(trace_fd, F_SETFL, O_NONBLOCK); 158d01f4e8dSNamhyung Kim pollfd.fd = trace_fd; 159d01f4e8dSNamhyung Kim 160d01f4e8dSNamhyung Kim if (write_tracing_file("tracing_on", "1") < 0) { 161d01f4e8dSNamhyung Kim pr_err("can't enable tracing\n"); 162d01f4e8dSNamhyung Kim goto out_close_fd; 163d01f4e8dSNamhyung Kim } 164d01f4e8dSNamhyung Kim 165d01f4e8dSNamhyung Kim perf_evlist__start_workload(ftrace->evlist); 166d01f4e8dSNamhyung Kim 167d01f4e8dSNamhyung Kim while (!done) { 168d01f4e8dSNamhyung Kim if (poll(&pollfd, 1, -1) < 0) 169d01f4e8dSNamhyung Kim break; 170d01f4e8dSNamhyung Kim 171d01f4e8dSNamhyung Kim if (pollfd.revents & POLLIN) { 172d01f4e8dSNamhyung Kim int n = read(trace_fd, buf, sizeof(buf)); 173d01f4e8dSNamhyung Kim if (n < 0) 174d01f4e8dSNamhyung Kim break; 175d01f4e8dSNamhyung Kim if (fwrite(buf, n, 1, stdout) != 1) 176d01f4e8dSNamhyung Kim break; 177d01f4e8dSNamhyung Kim } 178d01f4e8dSNamhyung Kim } 179d01f4e8dSNamhyung Kim 180d01f4e8dSNamhyung Kim write_tracing_file("tracing_on", "0"); 181d01f4e8dSNamhyung Kim 182d01f4e8dSNamhyung Kim /* read remaining buffer contents */ 183d01f4e8dSNamhyung Kim while (true) { 184d01f4e8dSNamhyung Kim int n = read(trace_fd, buf, sizeof(buf)); 185d01f4e8dSNamhyung Kim if (n <= 0) 186d01f4e8dSNamhyung Kim break; 187d01f4e8dSNamhyung Kim if (fwrite(buf, n, 1, stdout) != 1) 188d01f4e8dSNamhyung Kim break; 189d01f4e8dSNamhyung Kim } 190d01f4e8dSNamhyung Kim 191d01f4e8dSNamhyung Kim out_close_fd: 192d01f4e8dSNamhyung Kim close(trace_fd); 193d01f4e8dSNamhyung Kim out_free_pid: 194d01f4e8dSNamhyung Kim free(trace_pid); 195d01f4e8dSNamhyung Kim out: 196d01f4e8dSNamhyung Kim reset_tracing_files(ftrace); 197d01f4e8dSNamhyung Kim 198d01f4e8dSNamhyung Kim return done ? 0 : -1; 199d01f4e8dSNamhyung Kim } 200d01f4e8dSNamhyung Kim 201d01f4e8dSNamhyung Kim int cmd_ftrace(int argc, const char **argv, const char *prefix __maybe_unused) 202d01f4e8dSNamhyung Kim { 203d01f4e8dSNamhyung Kim int ret; 204d01f4e8dSNamhyung Kim struct perf_ftrace ftrace = { 205*ec347870SArnaldo Carvalho de Melo .tracer = "function_graph", 206d01f4e8dSNamhyung Kim .target = { .uid = UINT_MAX, }, 207d01f4e8dSNamhyung Kim }; 208d01f4e8dSNamhyung Kim const char * const ftrace_usage[] = { 209d01f4e8dSNamhyung Kim "perf ftrace [<options>] <command>", 210d01f4e8dSNamhyung Kim "perf ftrace [<options>] -- <command> [<options>]", 211d01f4e8dSNamhyung Kim NULL 212d01f4e8dSNamhyung Kim }; 213d01f4e8dSNamhyung Kim const struct option ftrace_options[] = { 214d01f4e8dSNamhyung Kim OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", 215*ec347870SArnaldo Carvalho de Melo "tracer to use: function_graph(default) or function"), 216d01f4e8dSNamhyung Kim OPT_INCR('v', "verbose", &verbose, 217d01f4e8dSNamhyung Kim "be more verbose"), 218d01f4e8dSNamhyung Kim OPT_END() 219d01f4e8dSNamhyung Kim }; 220d01f4e8dSNamhyung Kim 221d01f4e8dSNamhyung Kim argc = parse_options(argc, argv, ftrace_options, ftrace_usage, 222d01f4e8dSNamhyung Kim PARSE_OPT_STOP_AT_NON_OPTION); 223d01f4e8dSNamhyung Kim if (!argc) 224d01f4e8dSNamhyung Kim usage_with_options(ftrace_usage, ftrace_options); 225d01f4e8dSNamhyung Kim 226d01f4e8dSNamhyung Kim ftrace.evlist = perf_evlist__new(); 227d01f4e8dSNamhyung Kim if (ftrace.evlist == NULL) 228d01f4e8dSNamhyung Kim return -ENOMEM; 229d01f4e8dSNamhyung Kim 230d01f4e8dSNamhyung Kim ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target); 231d01f4e8dSNamhyung Kim if (ret < 0) 232d01f4e8dSNamhyung Kim goto out_delete_evlist; 233d01f4e8dSNamhyung Kim 234d01f4e8dSNamhyung Kim if (ftrace.tracer == NULL) 235d01f4e8dSNamhyung Kim ftrace.tracer = DEFAULT_TRACER; 236d01f4e8dSNamhyung Kim 237d01f4e8dSNamhyung Kim ret = __cmd_ftrace(&ftrace, argc, argv); 238d01f4e8dSNamhyung Kim 239d01f4e8dSNamhyung Kim out_delete_evlist: 240d01f4e8dSNamhyung Kim perf_evlist__delete(ftrace.evlist); 241d01f4e8dSNamhyung Kim 242d01f4e8dSNamhyung Kim return ret; 243d01f4e8dSNamhyung Kim } 244