/* * builtin-ftrace.c * * Copyright (c) 2013 LG Electronics, Namhyung Kim * * Released under the GPL v2. */ #include "builtin.h" #include "perf.h" #include #include #include "debug.h" #include #include "evlist.h" #include "target.h" #include "thread_map.h" #define DEFAULT_TRACER "function_graph" struct perf_ftrace { struct perf_evlist *evlist; struct target target; const char *tracer; }; static bool done; static void sig_handler(int sig __maybe_unused) { done = true; } /* * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since * we asked by setting its exec_error to the function below, * ftrace__workload_exec_failed_signal. * * XXX We need to handle this more appropriately, emitting an error, etc. */ static void ftrace__workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *info __maybe_unused, void *ucontext __maybe_unused) { /* workload_exec_errno = info->si_value.sival_int; */ done = true; } static int write_tracing_file(const char *name, const char *val) { char *file; int fd, ret = -1; ssize_t size = strlen(val); file = get_tracing_file(name); if (!file) { pr_debug("cannot get tracing file: %s\n", name); return -1; } fd = open(file, O_WRONLY); if (fd < 0) { pr_debug("cannot open tracing file: %s\n", name); goto out; } if (write(fd, val, size) == size) ret = 0; else pr_debug("write '%s' to tracing/%s failed\n", val, name); close(fd); out: put_tracing_file(file); return ret; } static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused) { if (write_tracing_file("tracing_on", "0") < 0) return -1; if (write_tracing_file("current_tracer", "nop") < 0) return -1; if (write_tracing_file("set_ftrace_pid", " ") < 0) return -1; return 0; } static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) { char *trace_file; int trace_fd; char *trace_pid; char buf[4096]; struct pollfd pollfd = { .events = POLLIN, }; if (geteuid() != 0) { pr_err("ftrace only works for root!\n"); return -1; } if (argc < 1) return -1; signal(SIGINT, sig_handler); signal(SIGUSR1, sig_handler); signal(SIGCHLD, sig_handler); reset_tracing_files(ftrace); /* reset ftrace buffer */ if (write_tracing_file("trace", "0") < 0) goto out; if (perf_evlist__prepare_workload(ftrace->evlist, &ftrace->target, argv, false, ftrace__workload_exec_failed_signal) < 0) goto out; if (write_tracing_file("current_tracer", ftrace->tracer) < 0) { pr_err("failed to set current_tracer to %s\n", ftrace->tracer); goto out; } if (asprintf(&trace_pid, "%d", thread_map__pid(ftrace->evlist->threads, 0)) < 0) { pr_err("failed to allocate pid string\n"); goto out; } if (write_tracing_file("set_ftrace_pid", trace_pid) < 0) { pr_err("failed to set pid: %s\n", trace_pid); goto out_free_pid; } trace_file = get_tracing_file("trace_pipe"); if (!trace_file) { pr_err("failed to open trace_pipe\n"); goto out_free_pid; } trace_fd = open(trace_file, O_RDONLY); put_tracing_file(trace_file); if (trace_fd < 0) { pr_err("failed to open trace_pipe\n"); goto out_free_pid; } fcntl(trace_fd, F_SETFL, O_NONBLOCK); pollfd.fd = trace_fd; if (write_tracing_file("tracing_on", "1") < 0) { pr_err("can't enable tracing\n"); goto out_close_fd; } perf_evlist__start_workload(ftrace->evlist); while (!done) { if (poll(&pollfd, 1, -1) < 0) break; if (pollfd.revents & POLLIN) { int n = read(trace_fd, buf, sizeof(buf)); if (n < 0) break; if (fwrite(buf, n, 1, stdout) != 1) break; } } write_tracing_file("tracing_on", "0"); /* read remaining buffer contents */ while (true) { int n = read(trace_fd, buf, sizeof(buf)); if (n <= 0) break; if (fwrite(buf, n, 1, stdout) != 1) break; } out_close_fd: close(trace_fd); out_free_pid: free(trace_pid); out: reset_tracing_files(ftrace); return done ? 0 : -1; } int cmd_ftrace(int argc, const char **argv, const char *prefix __maybe_unused) { int ret; struct perf_ftrace ftrace = { .tracer = "function_graph", .target = { .uid = UINT_MAX, }, }; const char * const ftrace_usage[] = { "perf ftrace [] ", "perf ftrace [] -- []", NULL }; const struct option ftrace_options[] = { OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", "tracer to use: function_graph(default) or function"), OPT_INCR('v', "verbose", &verbose, "be more verbose"), OPT_END() }; argc = parse_options(argc, argv, ftrace_options, ftrace_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (!argc) usage_with_options(ftrace_usage, ftrace_options); ftrace.evlist = perf_evlist__new(); if (ftrace.evlist == NULL) return -ENOMEM; ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target); if (ret < 0) goto out_delete_evlist; if (ftrace.tracer == NULL) ftrace.tracer = DEFAULT_TRACER; ret = __cmd_ftrace(&ftrace, argc, argv); out_delete_evlist: perf_evlist__delete(ftrace.evlist); return ret; }