aboutsummaryrefslogtreecommitdiff
path: root/tools/perf/builtin-trace.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r--tools/perf/builtin-trace.c265
1 files changed, 161 insertions, 104 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 329b7832b5d..f954c26de23 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -11,6 +11,8 @@
#include "util/intlist.h"
#include "util/thread_map.h"
#include "util/stat.h"
+#include "trace-event.h"
+#include "util/parse-events.h"
#include <libaudit.h>
#include <stdlib.h>
@@ -35,6 +37,10 @@
# define MADV_UNMERGEABLE 13
#endif
+#ifndef EFD_SEMAPHORE
+# define EFD_SEMAPHORE 1
+#endif
+
struct tp_field {
int offset;
union {
@@ -144,26 +150,39 @@ static int perf_evsel__init_tp_ptr_field(struct perf_evsel *evsel,
static void perf_evsel__delete_priv(struct perf_evsel *evsel)
{
- free(evsel->priv);
- evsel->priv = NULL;
+ zfree(&evsel->priv);
perf_evsel__delete(evsel);
}
-static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction,
- void *handler, int idx)
+static int perf_evsel__init_syscall_tp(struct perf_evsel *evsel, void *handler)
{
- struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction, idx);
-
- if (evsel) {
- evsel->priv = malloc(sizeof(struct syscall_tp));
-
- if (evsel->priv == NULL)
- goto out_delete;
-
+ evsel->priv = malloc(sizeof(struct syscall_tp));
+ if (evsel->priv != NULL) {
if (perf_evsel__init_sc_tp_uint_field(evsel, id))
goto out_delete;
evsel->handler = handler;
+ return 0;
+ }
+
+ return -ENOMEM;
+
+out_delete:
+ zfree(&evsel->priv);
+ return -ENOENT;
+}
+
+static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void *handler)
+{
+ struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction);
+
+ /* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */
+ if (evsel == NULL)
+ evsel = perf_evsel__newtp("syscalls", direction);
+
+ if (evsel) {
+ if (perf_evsel__init_syscall_tp(evsel, handler))
+ goto out_delete;
}
return evsel;
@@ -186,17 +205,16 @@ static int perf_evlist__add_syscall_newtp(struct perf_evlist *evlist,
void *sys_exit_handler)
{
int ret = -1;
- int idx = evlist->nr_entries;
struct perf_evsel *sys_enter, *sys_exit;
- sys_enter = perf_evsel__syscall_newtp("sys_enter", sys_enter_handler, idx++);
+ sys_enter = perf_evsel__syscall_newtp("sys_enter", sys_enter_handler);
if (sys_enter == NULL)
goto out;
if (perf_evsel__init_sc_tp_ptr_field(sys_enter, args))
goto out_delete_sys_enter;
- sys_exit = perf_evsel__syscall_newtp("sys_exit", sys_exit_handler, idx++);
+ sys_exit = perf_evsel__syscall_newtp("sys_exit", sys_exit_handler);
if (sys_exit == NULL)
goto out_delete_sys_enter;
@@ -265,6 +283,11 @@ static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size,
#define SCA_STRARRAY syscall_arg__scnprintf_strarray
+#if defined(__i386__) || defined(__x86_64__)
+/*
+ * FIXME: Make this available to all arches as soon as the ioctl beautifier
+ * gets rewritten to support all arches.
+ */
static size_t syscall_arg__scnprintf_strhexarray(char *bf, size_t size,
struct syscall_arg *arg)
{
@@ -272,6 +295,7 @@ static size_t syscall_arg__scnprintf_strhexarray(char *bf, size_t size,
}
#define SCA_STRHEXARRAY syscall_arg__scnprintf_strhexarray
+#endif /* defined(__i386__) || defined(__x86_64__) */
static size_t syscall_arg__scnprintf_fd(char *bf, size_t size,
struct syscall_arg *arg);
@@ -801,7 +825,6 @@ static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscal
P_SIGNUM(PIPE);
P_SIGNUM(ALRM);
P_SIGNUM(TERM);
- P_SIGNUM(STKFLT);
P_SIGNUM(CHLD);
P_SIGNUM(CONT);
P_SIGNUM(STOP);
@@ -817,6 +840,15 @@ static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscal
P_SIGNUM(IO);
P_SIGNUM(PWR);
P_SIGNUM(SYS);
+#ifdef SIGEMT
+ P_SIGNUM(EMT);
+#endif
+#ifdef SIGSTKFLT
+ P_SIGNUM(STKFLT);
+#endif
+#ifdef SIGSWI
+ P_SIGNUM(SWI);
+#endif
default: break;
}
@@ -825,6 +857,10 @@ static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscal
#define SCA_SIGNUM syscall_arg__scnprintf_signum
+#if defined(__i386__) || defined(__x86_64__)
+/*
+ * FIXME: Make this available to all arches.
+ */
#define TCGETS 0x5401
static const char *tioctls[] = {
@@ -846,6 +882,7 @@ static const char *tioctls[] = {
};
static DEFINE_STRARRAY_OFFSET(tioctls, 0x5401);
+#endif /* defined(__i386__) || defined(__x86_64__) */
#define STRARRAY(arg, name, array) \
.arg_scnprintf = { [arg] = SCA_STRARRAY, }, \
@@ -927,9 +964,16 @@ static struct syscall_fmt {
{ .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), },
{ .name = "ioctl", .errmsg = true,
.arg_scnprintf = { [0] = SCA_FD, /* fd */
+#if defined(__i386__) || defined(__x86_64__)
+/*
+ * FIXME: Make this available to all arches.
+ */
[1] = SCA_STRHEXARRAY, /* cmd */
[2] = SCA_HEX, /* arg */ },
.arg_parm = { [1] = &strarray__tioctls, /* cmd */ }, },
+#else
+ [2] = SCA_HEX, /* arg */ }, },
+#endif
{ .name = "kill", .errmsg = true,
.arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, },
{ .name = "linkat", .errmsg = true,
@@ -953,7 +997,8 @@ static struct syscall_fmt {
{ .name = "mmap", .hexret = true,
.arg_scnprintf = { [0] = SCA_HEX, /* addr */
[2] = SCA_MMAP_PROT, /* prot */
- [3] = SCA_MMAP_FLAGS, /* flags */ }, },
+ [3] = SCA_MMAP_FLAGS, /* flags */
+ [4] = SCA_FD, /* fd */ }, },
{ .name = "mprotect", .errmsg = true,
.arg_scnprintf = { [0] = SCA_HEX, /* start */
[2] = SCA_MMAP_PROT, /* prot */ }, },
@@ -1142,28 +1187,30 @@ struct trace {
int max;
struct syscall *table;
} syscalls;
- struct perf_record_opts opts;
+ struct record_opts opts;
struct machine *host;
u64 base_time;
- bool full_time;
FILE *output;
unsigned long nr_events;
struct strlist *ev_qualifier;
- bool not_ev_qualifier;
- bool live;
const char *last_vfs_getname;
struct intlist *tid_list;
struct intlist *pid_list;
+ double duration_filter;
+ double runtime_ms;
+ struct {
+ u64 vfs_getname,
+ proc_getname;
+ } stats;
+ bool not_ev_qualifier;
+ bool live;
+ bool full_time;
bool sched;
bool multiple_threads;
bool summary;
+ bool summary_only;
bool show_comm;
bool show_tool_stats;
- double duration_filter;
- double runtime_ms;
- struct {
- u64 vfs_getname, proc_getname;
- } stats;
};
static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname)
@@ -1260,10 +1307,8 @@ static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size,
size_t printed = syscall_arg__scnprintf_fd(bf, size, arg);
struct thread_trace *ttrace = arg->thread->priv;
- if (ttrace && fd >= 0 && fd <= ttrace->paths.max) {
- free(ttrace->paths.table[fd]);
- ttrace->paths.table[fd] = NULL;
- }
+ if (ttrace && fd >= 0 && fd <= ttrace->paths.max)
+ zfree(&ttrace->paths.table[fd]);
return printed;
}
@@ -1342,15 +1387,8 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
if (trace->host == NULL)
return -ENOMEM;
- if (perf_target__has_task(&trace->opts.target)) {
- err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads,
- trace__tool_process,
- trace->host);
- } else {
- err = perf_event__synthesize_threads(&trace->tool, trace__tool_process,
- trace->host);
- }
-
+ err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target,
+ evlist->threads, trace__tool_process, false);
if (err)
symbol__exit();
@@ -1425,11 +1463,11 @@ static int trace__read_syscall_info(struct trace *trace, int id)
sc->fmt = syscall_fmt__find(sc->name);
snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
- sc->tp_format = event_format__new("syscalls", tp_name);
+ sc->tp_format = trace_event__tp_format("syscalls", tp_name);
if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) {
snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias);
- sc->tp_format = event_format__new("syscalls", tp_name);
+ sc->tp_format = trace_event__tp_format("syscalls", tp_name);
}
if (sc->tp_format == NULL)
@@ -1607,7 +1645,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
args, trace, thread);
if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) {
- if (!trace->duration_filter) {
+ if (!trace->duration_filter && !trace->summary_only) {
trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output);
fprintf(trace->output, "%-70s\n", ttrace->entry_str);
}
@@ -1660,6 +1698,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
} else if (trace->duration_filter)
goto out;
+ if (trace->summary_only)
+ goto out;
+
trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output);
if (ttrace->entry_pending) {
@@ -1756,22 +1797,14 @@ static int trace__process_sample(struct perf_tool *tool,
if (!trace->full_time && trace->base_time == 0)
trace->base_time = sample->time;
- if (handler)
+ if (handler) {
+ ++trace->nr_events;
handler(trace, evsel, sample);
+ }
return err;
}
-static bool
-perf_session__has_tp(struct perf_session *session, const char *name)
-{
- struct perf_evsel *evsel;
-
- evsel = perf_evlist__find_tracepoint_by_name(session->evlist, name);
-
- return evsel != NULL;
-}
-
static int parse_target_str(struct trace *trace)
{
if (trace->opts.target.pid) {
@@ -1802,10 +1835,11 @@ static int trace__record(int argc, const char **argv)
"-R",
"-m", "1024",
"-c", "1",
- "-e", "raw_syscalls:sys_enter,raw_syscalls:sys_exit",
+ "-e",
};
- rec_argc = ARRAY_SIZE(record_args) + argc;
+ /* +1 is for the event string below */
+ rec_argc = ARRAY_SIZE(record_args) + 1 + argc;
rec_argv = calloc(rec_argc + 1, sizeof(char *));
if (rec_argv == NULL)
@@ -1814,6 +1848,17 @@ static int trace__record(int argc, const char **argv)
for (i = 0; i < ARRAY_SIZE(record_args); i++)
rec_argv[i] = record_args[i];
+ /* event string may be different for older kernels - e.g., RHEL6 */
+ if (is_valid_tracepoint("raw_syscalls:sys_enter"))
+ rec_argv[i] = "raw_syscalls:sys_enter,raw_syscalls:sys_exit";
+ else if (is_valid_tracepoint("syscalls:sys_enter"))
+ rec_argv[i] = "syscalls:sys_enter,syscalls:sys_exit";
+ else {
+ pr_err("Neither raw_syscalls nor syscalls events exist.\n");
+ return -1;
+ }
+ i++;
+
for (j = 0; j < (unsigned int)argc; j++, i++)
rec_argv[i] = argv[j];
@@ -1824,8 +1869,7 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp);
static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname",
- evlist->nr_entries);
+ struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname");
if (evsel == NULL)
return;
@@ -1872,7 +1916,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
err = trace__symbols_init(trace, evlist);
if (err < 0) {
fprintf(trace->output, "Problems initializing symbol libraries!\n");
- goto out_delete_maps;
+ goto out_delete_evlist;
}
perf_evlist__config(evlist, &trace->opts);
@@ -1882,10 +1926,10 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
if (forks) {
err = perf_evlist__prepare_workload(evlist, &trace->opts.target,
- argv, false, false);
+ argv, false, NULL);
if (err < 0) {
fprintf(trace->output, "Couldn't run the workload!\n");
- goto out_delete_maps;
+ goto out_delete_evlist;
}
}
@@ -1893,10 +1937,10 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
if (err < 0)
goto out_error_open;
- err = perf_evlist__mmap(evlist, UINT_MAX, false);
+ err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false);
if (err < 0) {
fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno));
- goto out_close_evlist;
+ goto out_delete_evlist;
}
perf_evlist__enable(evlist);
@@ -1980,11 +2024,6 @@ out_disable:
}
}
- perf_evlist__munmap(evlist);
-out_close_evlist:
- perf_evlist__close(evlist);
-out_delete_maps:
- perf_evlist__delete_maps(evlist);
out_delete_evlist:
perf_evlist__delete(evlist);
out:
@@ -2009,8 +2048,6 @@ out_error:
static int trace__replay(struct trace *trace)
{
const struct perf_evsel_str_handler handlers[] = {
- { "raw_syscalls:sys_enter", trace__sys_enter, },
- { "raw_syscalls:sys_exit", trace__sys_exit, },
{ "probe:vfs_getname", trace__vfs_getname, },
};
struct perf_data_file file = {
@@ -2018,6 +2055,7 @@ static int trace__replay(struct trace *trace)
.mode = PERF_DATA_MODE_READ,
};
struct perf_session *session;
+ struct perf_evsel *evsel;
int err = -1;
trace->tool.sample = trace__process_sample;
@@ -2049,13 +2087,36 @@ static int trace__replay(struct trace *trace)
if (err)
goto out;
- if (!perf_session__has_tp(session, "raw_syscalls:sys_enter")) {
- pr_err("Data file does not have raw_syscalls:sys_enter events\n");
+ evsel = perf_evlist__find_tracepoint_by_name(session->evlist,
+ "raw_syscalls:sys_enter");
+ /* older kernels have syscalls tp versus raw_syscalls */
+ if (evsel == NULL)
+ evsel = perf_evlist__find_tracepoint_by_name(session->evlist,
+ "syscalls:sys_enter");
+ if (evsel == NULL) {
+ pr_err("Data file does not have raw_syscalls:sys_enter event\n");
goto out;
}
- if (!perf_session__has_tp(session, "raw_syscalls:sys_exit")) {
- pr_err("Data file does not have raw_syscalls:sys_exit events\n");
+ if (perf_evsel__init_syscall_tp(evsel, trace__sys_enter) < 0 ||
+ perf_evsel__init_sc_tp_ptr_field(evsel, args)) {
+ pr_err("Error during initialize raw_syscalls:sys_enter event\n");
+ goto out;
+ }
+
+ evsel = perf_evlist__find_tracepoint_by_name(session->evlist,
+ "raw_syscalls:sys_exit");
+ if (evsel == NULL)
+ evsel = perf_evlist__find_tracepoint_by_name(session->evlist,
+ "syscalls:sys_exit");
+ if (evsel == NULL) {
+ pr_err("Data file does not have raw_syscalls:sys_exit event\n");
+ goto out;
+ }
+
+ if (perf_evsel__init_syscall_tp(evsel, trace__sys_exit) < 0 ||
+ perf_evsel__init_sc_tp_uint_field(evsel, ret)) {
+ pr_err("Error during initialize raw_syscalls:sys_exit event\n");
goto out;
}
@@ -2082,12 +2143,7 @@ static size_t trace__fprintf_threads_header(FILE *fp)
{
size_t printed;
- printed = fprintf(fp, "\n _____________________________________________________________________________\n");
- printed += fprintf(fp, " __) Summary of events (__\n\n");
- printed += fprintf(fp, " [ task - pid ] [ events ] [ ratio ] [ runtime ]\n");
- printed += fprintf(fp, " syscall count min max avg stddev\n");
- printed += fprintf(fp, " msec msec msec %%\n");
- printed += fprintf(fp, " _____________________________________________________________________________\n\n");
+ printed = fprintf(fp, "\n Summary of events:\n\n");
return printed;
}
@@ -2105,6 +2161,10 @@ static size_t thread__dump_stats(struct thread_trace *ttrace,
printed += fprintf(fp, "\n");
+ printed += fprintf(fp, " syscall calls min avg max stddev\n");
+ printed += fprintf(fp, " (msec) (msec) (msec) (%%)\n");
+ printed += fprintf(fp, " --------------- -------- --------- --------- --------- ------\n");
+
/* each int_node is a syscall */
while (inode) {
stats = inode->priv;
@@ -2119,10 +2179,10 @@ static size_t thread__dump_stats(struct thread_trace *ttrace,
avg /= NSEC_PER_MSEC;
sc = &trace->syscalls.table[inode->i];
- printed += fprintf(fp, "%24s %14s : ", "", sc->name);
- printed += fprintf(fp, "%5" PRIu64 " %8.3f %8.3f",
- n, min, max);
- printed += fprintf(fp, " %8.3f %6.2f\n", avg, pct);
+ printed += fprintf(fp, " %-15s", sc->name);
+ printed += fprintf(fp, " %8" PRIu64 " %9.3f %9.3f",
+ n, min, avg);
+ printed += fprintf(fp, " %9.3f %9.2f%%\n", max, pct);
}
inode = intlist__next(inode);
@@ -2147,7 +2207,6 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
size_t printed = data->printed;
struct trace *trace = data->trace;
struct thread_trace *ttrace = thread->priv;
- const char *color;
double ratio;
if (ttrace == NULL)
@@ -2155,18 +2214,10 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
ratio = (double)ttrace->nr_events / trace->nr_events * 100.0;
- color = PERF_COLOR_NORMAL;
- if (ratio > 50.0)
- color = PERF_COLOR_RED;
- else if (ratio > 25.0)
- color = PERF_COLOR_GREEN;
- else if (ratio > 5.0)
- color = PERF_COLOR_YELLOW;
-
- printed += color_fprintf(fp, color, "%20s", thread__comm_str(thread));
- printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events);
- printed += color_fprintf(fp, color, "%5.1f%%", ratio);
- printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms);
+ printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread->tid);
+ printed += fprintf(fp, "%lu events, ", ttrace->nr_events);
+ printed += fprintf(fp, "%.1f%%", ratio);
+ printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms);
printed += thread__dump_stats(ttrace, trace, fp);
data->printed += printed;
@@ -2237,7 +2288,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
},
.user_freq = UINT_MAX,
.user_interval = ULLONG_MAX,
- .no_delay = true,
+ .no_buffering = true,
.mmap_pages = 1024,
},
.output = stdout,
@@ -2275,8 +2326,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_INCR('v', "verbose", &verbose, "be more verbose"),
OPT_BOOLEAN('T', "time", &trace.full_time,
"Show full timestamp, not time relative to first start"),
- OPT_BOOLEAN(0, "summary", &trace.summary,
- "Show syscall summary with statistics"),
+ OPT_BOOLEAN('s', "summary", &trace.summary_only,
+ "Show only syscall summary with statistics"),
+ OPT_BOOLEAN('S', "with-summary", &trace.summary,
+ "Show all syscalls and summary with statistics"),
OPT_END()
};
int err;
@@ -2287,6 +2340,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
argc = parse_options(argc, argv, trace_options, trace_usage, 0);
+ /* summary_only implies summary option, but don't overwrite summary if set */
+ if (trace.summary_only)
+ trace.summary = trace.summary_only;
+
if (output_name != NULL) {
err = trace__open_output(&trace, output_name);
if (err < 0) {
@@ -2310,21 +2367,21 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
}
}
- err = perf_target__validate(&trace.opts.target);
+ err = target__validate(&trace.opts.target);
if (err) {
- perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
+ target__strerror(&trace.opts.target, err, bf, sizeof(bf));
fprintf(trace.output, "%s", bf);
goto out_close;
}
- err = perf_target__parse_uid(&trace.opts.target);
+ err = target__parse_uid(&trace.opts.target);
if (err) {
- perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
+ target__strerror(&trace.opts.target, err, bf, sizeof(bf));
fprintf(trace.output, "%s", bf);
goto out_close;
}
- if (!argc && perf_target__none(&trace.opts.target))
+ if (!argc && target__none(&trace.opts.target))
trace.opts.target.system_wide = true;
if (input_name)