perf ftrace: Introduce new 'ftrace' tool
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. Committer notes: Testing it: # perf ftrace -f function_graph usleep 123456 <SNIP> 2) | SyS_nanosleep() { 2) | _copy_from_user() { <SNIP> 2) 0.900 us | } 2) 1.354 us | } 2) | hrtimer_nanosleep() { 2) 0.062 us | __hrtimer_init(); 2) | do_nanosleep() { 2) | hrtimer_start_range_ns() { <SNIP> 2) 5.025 us | } 2) | schedule() { 2) 0.125 us | rcu_note_context_switch(); 2) 0.057 us | _raw_spin_lock(); 2) | deactivate_task() { 2) 0.369 us | update_rq_clock.part.77(); 2) | dequeue_task_fair() { <SNIP> 2) + 22.453 us | } 2) + 23.736 us | } 2) | pick_next_task_fair() { <SNIP> 2) + 47.167 us | } 2) | pick_next_task_idle() { <SNIP> 2) 4.462 us | } ------------------------------------------ 2) usleep-20387 => <idle>-0 ------------------------------------------ 2) 0.806 us | switch_mm_irqs_off(); ------------------------------------------ 2) <idle>-0 => usleep-20387 ------------------------------------------ 2) 0.151 us | finish_task_switch(); 2) @ 123597.2 us | } 2) 0.037 us | _cond_resched(); 2) | hrtimer_try_to_cancel() { 2) 0.064 us | hrtimer_active(); 2) 0.353 us | } 2) @ 123605.3 us | } 2) @ 123606.2 us | } 2) @ 123608.3 us | } /* SyS_nanosleep */ 2) | __do_page_fault() { <SNIP> Signed-off-by: Namhyung Kim <namhyung@kernel.org> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Tested-by: Masami Hiramatsu <mhiramat@kernel.org> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Jeremy Eder <jeder@redhat.com> Cc: Jiri Olsa <jolsa@redhat.com>, Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Stephane Eranian <eranian@google.com> Cc: Steven Rostedt <rostedt@goodmis.org> Link: http://lkml.kernel.org/n/tip-r1hgmsj4dxny8arn3o9mw512@git.kernel.org [ Various foward port fixes, add man page ] Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Родитель
a7619aef6d
Коммит
d01f4e8db2
|
@ -3,6 +3,7 @@ perf-y += builtin-annotate.o
|
|||
perf-y += builtin-config.o
|
||||
perf-y += builtin-diff.o
|
||||
perf-y += builtin-evlist.o
|
||||
perf-y += builtin-ftrace.o
|
||||
perf-y += builtin-help.o
|
||||
perf-y += builtin-sched.o
|
||||
perf-y += builtin-buildid-list.o
|
||||
|
|
|
@ -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,242 @@
|
|||
/*
|
||||
* 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 = {
|
||||
.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 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;
|
||||
}
|
|
@ -41,6 +41,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix);
|
|||
int cmd_inject(int argc, const char **argv, const char *prefix);
|
||||
int cmd_mem(int argc, const char **argv, const char *prefix);
|
||||
int cmd_data(int argc, const char **argv, const char *prefix);
|
||||
int cmd_ftrace(int argc, const char **argv, const char *prefix);
|
||||
|
||||
int find_scripts(char **scripts_array, char **scripts_path_array);
|
||||
#endif
|
||||
|
|
|
@ -11,6 +11,7 @@ perf-data mainporcelain common
|
|||
perf-diff mainporcelain common
|
||||
perf-config mainporcelain common
|
||||
perf-evlist mainporcelain common
|
||||
perf-ftrace mainporcelain common
|
||||
perf-inject mainporcelain common
|
||||
perf-kallsyms mainporcelain common
|
||||
perf-kmem mainporcelain common
|
||||
|
|
|
@ -71,6 +71,7 @@ static struct cmd_struct commands[] = {
|
|||
{ "inject", cmd_inject, 0 },
|
||||
{ "mem", cmd_mem, 0 },
|
||||
{ "data", cmd_data, 0 },
|
||||
{ "ftrace", cmd_ftrace, 0 },
|
||||
};
|
||||
|
||||
struct pager_config {
|
||||
|
|
Загрузка…
Ссылка в новой задаче