- Introduce 'perf ftrace' a perf front end to the kernel's ftrace function and function_graph tracer, defaulting to the "function_graph" tracer, more work will be done in reviving this effort, forward porting it from its initial patch submission (Namhyung Kim) - Add 'e' and 'c' hotkeys to expand/collapse call chains for a single hist entry in the 'perf report' and 'perf top' TUI (Jiri Olsa) Fixes: - Fix wrong register name for arm64, used in 'perf probe' (He Kuang) - Fix map offsets in relocation in libbpf (Joe Stringer) - Fix looking up dwarf unwind stack info (Matija Glavinic Pecotic) Infrastructure: - libbpf prog functions sync with what is exported via uapi (Joe Stringer) Trivial: - Remove unnecessary checks and assignments in 'perf probe's try_to_find_absolute_address() (Markus Elfring) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJYig7UAAoJENZQFvNTUqpAhJQP/iI0T7A8TNekPGLv7j20c302 89N9+9TAFtVqjgr1hIzqQgGOqbOdAW1tU3VTPW92nNDBn9JV5qwuF9YWEiDaAVv2 0bmV5hLnrNlymddm3pdg/PbD1TVlwk2NFxtrkPxuf/vx0ZhEGqsSrRUCR/xGXbtQ TcMg3rQquspV9JNv4HzFdQC9nsG1CGNotZKsE1avRw70pWAqCtF81B0m8teb6OWo 5qnN+AMJlYcC+OGffROemUksuehkMvi5L8v1e/6RO/lU1qt9Jrc/2sT9cqvjVFNR k4c76cUgWOCYzDEotENMpU4bc6e/24DE2ydFeovihdXw8Qs4ajEA9LXKM4yW+ZoE MZE3GS153a8n+CvTfkB9Ow1QJ8rgmR/L0BuhmGb6bYW/MtuTRTShhSduZwOrIyap 9KckHYti4p3oN3CKFYGO9PN3DRUdx+Xqg/miwrgjkPo09QFp+lzfFFOk0P2/Zqw2 yfvdWeHxkkrwoWQIyMHVKp/E9jQPuyYqwnKdp68LCN+DgNiFpPpSA8id5e47RQDE otqrK8U/82ktakfrBijSPBI6EEqFg7ltip2KT/xlDMfnP9HtxgFhzrk52dyi6pM/ jkBhJaTQhVZTyaFvUXuaLmBSdPpcaaGM4KJ+2iAayA2r0KLiDj6IdzD5ROCRFOvJ SFA472mIxNxUjpQEUTtc =tYKN -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo-4.11-20170126' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull the latest perf/core updates from Arnaldo Carvalho de Melo: New features: - Introduce 'perf ftrace' a perf front end to the kernel's ftrace function and function_graph tracer, defaulting to the "function_graph" tracer, more work will be done in reviving this effort, forward porting it from its initial patch submission (Namhyung Kim) - Add 'e' and 'c' hotkeys to expand/collapse call chains for a single hist entry in the 'perf report' and 'perf top' TUI (Jiri Olsa) Fixes: - Fix wrong register name for arm64, used in 'perf probe' (He Kuang) - Fix map offsets in relocation in libbpf (Joe Stringer) - Fix looking up dwarf unwind stack info (Matija Glavinic Pecotic) Infrastructure changes: - libbpf prog functions sync with what is exported via uapi (Joe Stringer) Trivial changes: - Remove unnecessary checks and assignments in 'perf probe's try_to_find_absolute_address() (Markus Elfring) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>tirimbino
commit
e2cf00c257
@ -0,0 +1,36 @@ |
|||||||
|
perf-ftrace(1) |
||||||
|
============= |
||||||
|
|
||||||
|
NAME |
||||||
|
---- |
||||||
|
perf-ftrace - simple wrapper for kernel's ftrace functionality |
||||||
|
|
||||||
|
|
||||||
|
SYNOPSIS |
||||||
|
-------- |
||||||
|
[verse] |
||||||
|
'perf ftrace' <command> |
||||||
|
|
||||||
|
DESCRIPTION |
||||||
|
----------- |
||||||
|
The 'perf ftrace' command is a simple wrapper of kernel's ftrace |
||||||
|
functionality. It only supports single thread tracing currently and |
||||||
|
just reads trace_pipe in text and then write it to stdout. |
||||||
|
|
||||||
|
The following options apply to perf ftrace. |
||||||
|
|
||||||
|
OPTIONS |
||||||
|
------- |
||||||
|
|
||||||
|
-t:: |
||||||
|
--tracer=:: |
||||||
|
Tracer to use: function_graph or function. |
||||||
|
|
||||||
|
-v:: |
||||||
|
--verbose=:: |
||||||
|
Verbosity level. |
||||||
|
|
||||||
|
|
||||||
|
SEE ALSO |
||||||
|
-------- |
||||||
|
linkperf:perf-record[1], linkperf:perf-trace[1] |
@ -0,0 +1,243 @@ |
|||||||
|
/*
|
||||||
|
* builtin-ftrace.c |
||||||
|
* |
||||||
|
* Copyright (c) 2013 LG Electronics, Namhyung Kim <namhyung@kernel.org> |
||||||
|
* |
||||||
|
* Released under the GPL v2. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "builtin.h" |
||||||
|
#include "perf.h" |
||||||
|
|
||||||
|
#include <unistd.h> |
||||||
|
#include <signal.h> |
||||||
|
|
||||||
|
#include "debug.h" |
||||||
|
#include <subcmd/parse-options.h> |
||||||
|
#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 [<options>] <command>", |
||||||
|
"perf ftrace [<options>] -- <command> [<options>]", |
||||||
|
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; |
||||||
|
} |
Loading…
Reference in new issue