perf/core improvements and fixes:

User visible:
 
 - Take tracefs into account when reporting errors about accessing
   tracepoint information in tools like 'perf trace' (Arnaldo Carvalho de Melo)
 
 - Let user have timestamps with per-thread recording in 'perf record' (Adrian Hunter)
 
 Infrastructure:
 
 - Introduce series of functions to build event filters so that we
   can set them in just one ioctl call, useful to set up common_pid,
   raw_syscalls:sys_{enter,exit}'s "id" filters to use with
   'perf trace' (Arnaldo Carvalho de Melo)
 
 - Delete an unnecessary check before calling strfilter__delete() (Markus Elfring)
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJVmp9sAAoJENZQFvNTUqpASCEQALTZ9nhHcZVWr4Uivgsjp62G
 axlWXO0U4wXNjpzwkL/OJsPvCLnOnxcaTshSaOKmHC95NpOMzktwu+rjd4zojTW3
 /4RQaXd2DZ2NmdOdVW6SDqOl91OYslr4TFRXlfwDPGtq/udTbt4ZHnpGGPS4TWP2
 Iwlyv0iOczZw8pBb2BQ4QSE42lNmZ9ZyLIVnuNF11pt8Gnx8hmeh1665m7dPrukc
 jhjZjPqVA/U4nyo6B3eG+nheACL+744nE32GKGiMEFws1CQcjXHx6i/Zgcq7dSRk
 2wfr2Ki0dnaEVjJiuQKa+D52EDyYQLARVLnfNNFzUp+UincBSx2OELVYf+Vbh2FT
 uVWYQ8HaHfPLAftyPsDr8jIkQn29uO0wtwLv1Aefxj9EGc5GcYI6GleoDVfn+4VW
 EV0fkJcuqyViG1+ixBTj/aaepp553VPAN2JYyuSuN/ilSi5WCGic8hwGUTxX2Dlm
 NUV9i0BufdXlg3KYTV334mtS+2Du4R3FcPR3qI2cjipILrELlhQXITdkC6Nxze+o
 t0M6MOZDYB+O4HMwUIpYB58OoOeoqnJZ4WPp4q1xI0CiMxxhzBCuHJifUhgcWuXE
 6mYpDf5ZrjCjDWUF64f9e1BTkZ6iv1hVnitST5wzdVbiHVc2F94voUorznOz5kMb
 PmdiXRuofZg++8faqJbH
 =A0RX
 -----END PGP SIGNATURE-----

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

User visible changes:

  - Take tracefs into account when reporting errors about accessing
    tracepoint information in tools like 'perf trace' (Arnaldo Carvalho de Melo)

  - Let user have timestamps with per-thread recording in 'perf record' (Adrian Hunter)

Infrastructure changes:

  - Introduce series of functions to build event filters so that we
    can set them in just one ioctl call, useful to set up common_pid,
    raw_syscalls:sys_{enter,exit}'s "id" filters to use with
    'perf trace' (Arnaldo Carvalho de Melo)

  - Delete an unnecessary check before calling strfilter__delete() (Markus Elfring)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2015-07-06 17:46:15 +02:00
commit 60cd37eb10
11 changed files with 224 additions and 81 deletions

View File

@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include "debugfs.h"
#include "tracefs.h"
#ifndef DEBUGFS_DEFAULT_PATH
#define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug"
@ -94,11 +95,21 @@ int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename
"Hint:\tIs the debugfs filesystem mounted?\n"
"Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
break;
case EACCES:
case EACCES: {
const char *mountpoint = debugfs_mountpoint;
if (!access(debugfs_mountpoint, R_OK) && strncmp(filename, "tracing/", 8) == 0) {
const char *tracefs_mntpoint = tracefs_find_mountpoint();
if (tracefs_mntpoint)
mountpoint = tracefs_mntpoint;
}
snprintf(buf, size,
"Error:\tNo permissions to read %s/%s\n"
"Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
debugfs_mountpoint, filename, debugfs_mountpoint);
debugfs_mountpoint, filename, mountpoint);
}
break;
default:
snprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf)));

View File

@ -297,8 +297,7 @@ static void cleanup_params(void)
clear_perf_probe_event(params.events + i);
line_range__clear(&params.line_range);
free(params.target);
if (params.filter)
strfilter__delete(params.filter);
strfilter__delete(params.filter);
memset(&params, 0, sizeof(params));
}

View File

@ -1030,7 +1030,9 @@ struct option __record_options[] = {
OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat,
"per thread counts"),
OPT_BOOLEAN('d', "data", &record.opts.sample_address, "Record the sample addresses"),
OPT_BOOLEAN('T', "timestamp", &record.opts.sample_time, "Record the sample timestamps"),
OPT_BOOLEAN_SET('T', "timestamp", &record.opts.sample_time,
&record.opts.sample_time_set,
"Record the sample timestamps"),
OPT_BOOLEAN('P', "period", &record.opts.period, "Record the sample period"),
OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples,
"don't sample"),

View File

@ -247,42 +247,6 @@ out_delete:
({ struct syscall_tp *fields = evsel->priv; \
fields->name.pointer(&fields->name, sample); })
static int perf_evlist__add_syscall_newtp(struct perf_evlist *evlist,
void *sys_enter_handler,
void *sys_exit_handler)
{
int ret = -1;
struct perf_evsel *sys_enter, *sys_exit;
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);
if (sys_exit == NULL)
goto out_delete_sys_enter;
if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret))
goto out_delete_sys_exit;
perf_evlist__add(evlist, sys_enter);
perf_evlist__add(evlist, sys_exit);
ret = 0;
out:
return ret;
out_delete_sys_exit:
perf_evsel__delete_priv(sys_exit);
out_delete_sys_enter:
perf_evsel__delete_priv(sys_enter);
goto out;
}
struct syscall_arg {
unsigned long val;
struct thread *thread;
@ -1223,7 +1187,6 @@ struct syscall {
int nr_args;
struct format_field *args;
const char *name;
bool filtered;
bool is_exit;
struct syscall_fmt *fmt;
size_t (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg);
@ -1307,6 +1270,10 @@ struct trace {
struct {
int max;
struct syscall *table;
struct {
struct perf_evsel *sys_enter,
*sys_exit;
} events;
} syscalls;
struct record_opts opts;
struct perf_evlist *evlist;
@ -1316,6 +1283,10 @@ struct trace {
FILE *output;
unsigned long nr_events;
struct strlist *ev_qualifier;
struct {
size_t nr;
int *entries;
} ev_qualifier_ids;
const char *last_vfs_getname;
struct intlist *tid_list;
struct intlist *pid_list;
@ -1578,19 +1549,6 @@ static int trace__read_syscall_info(struct trace *trace, int id)
sc = trace->syscalls.table + id;
sc->name = name;
if (trace->ev_qualifier) {
bool in = strlist__find(trace->ev_qualifier, name) != NULL;
if (!(in ^ trace->not_ev_qualifier)) {
sc->filtered = true;
/*
* No need to do read tracepoint information since this will be
* filtered out.
*/
return 0;
}
}
sc->fmt = syscall_fmt__find(sc->name);
snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
@ -1619,13 +1577,27 @@ static int trace__read_syscall_info(struct trace *trace, int id)
static int trace__validate_ev_qualifier(struct trace *trace)
{
int err = 0;
int err = 0, i;
struct str_node *pos;
trace->ev_qualifier_ids.nr = strlist__nr_entries(trace->ev_qualifier);
trace->ev_qualifier_ids.entries = malloc(trace->ev_qualifier_ids.nr *
sizeof(trace->ev_qualifier_ids.entries[0]));
if (trace->ev_qualifier_ids.entries == NULL) {
fputs("Error:\tNot enough memory for allocating events qualifier ids\n",
trace->output);
err = -EINVAL;
goto out;
}
i = 0;
strlist__for_each(pos, trace->ev_qualifier) {
const char *sc = pos->s;
int id = audit_name_to_syscall(sc, trace->audit.machine);
if (audit_name_to_syscall(sc, trace->audit.machine) < 0) {
if (id < 0) {
if (err == 0) {
fputs("Error:\tInvalid syscall ", trace->output);
err = -EINVAL;
@ -1635,13 +1607,17 @@ static int trace__validate_ev_qualifier(struct trace *trace)
fputs(sc, trace->output);
}
trace->ev_qualifier_ids.entries[i++] = id;
}
if (err < 0) {
fputs("\nHint:\ttry 'perf list syscalls:sys_enter_*'"
"\nHint:\tand: 'man syscalls'\n", trace->output);
zfree(&trace->ev_qualifier_ids.entries);
trace->ev_qualifier_ids.nr = 0;
}
out:
return err;
}
@ -1833,9 +1809,6 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
if (sc == NULL)
return -1;
if (sc->filtered)
return 0;
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
ttrace = thread__trace(thread, trace->output);
if (ttrace == NULL)
@ -1891,9 +1864,6 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
if (sc == NULL)
return -1;
if (sc->filtered)
return 0;
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
ttrace = thread__trace(thread, trace->output);
if (ttrace == NULL)
@ -2283,9 +2253,68 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st
}
}
static int trace__add_syscall_newtp(struct trace *trace)
{
int ret = -1;
struct perf_evlist *evlist = trace->evlist;
struct perf_evsel *sys_enter, *sys_exit;
sys_enter = perf_evsel__syscall_newtp("sys_enter", trace__sys_enter);
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", trace__sys_exit);
if (sys_exit == NULL)
goto out_delete_sys_enter;
if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret))
goto out_delete_sys_exit;
perf_evlist__add(evlist, sys_enter);
perf_evlist__add(evlist, sys_exit);
trace->syscalls.events.sys_enter = sys_enter;
trace->syscalls.events.sys_exit = sys_exit;
ret = 0;
out:
return ret;
out_delete_sys_exit:
perf_evsel__delete_priv(sys_exit);
out_delete_sys_enter:
perf_evsel__delete_priv(sys_enter);
goto out;
}
static int trace__set_ev_qualifier_filter(struct trace *trace)
{
int err = -1;
char *filter = asprintf_expr_inout_ints("id", !trace->not_ev_qualifier,
trace->ev_qualifier_ids.nr,
trace->ev_qualifier_ids.entries);
if (filter == NULL)
goto out_enomem;
if (!perf_evsel__append_filter(trace->syscalls.events.sys_enter, "&&", filter))
err = perf_evsel__append_filter(trace->syscalls.events.sys_exit, "&&", filter);
free(filter);
out:
return err;
out_enomem:
errno = ENOMEM;
goto out;
}
static int trace__run(struct trace *trace, int argc, const char **argv)
{
struct perf_evlist *evlist = trace->evlist;
struct perf_evsel *evsel;
int err = -1, i;
unsigned long before;
const bool forks = argc > 0;
@ -2293,9 +2322,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
trace->live = true;
if (trace->trace_syscalls &&
perf_evlist__add_syscall_newtp(evlist, trace__sys_enter,
trace__sys_exit))
if (trace->trace_syscalls && trace__add_syscall_newtp(trace))
goto out_error_raw_syscalls;
if (trace->trace_syscalls)
@ -2356,11 +2383,21 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
else if (thread_map__pid(evlist->threads, 0) == -1)
err = perf_evlist__set_filter_pid(evlist, getpid());
if (err < 0) {
printf("err=%d,%s\n", -err, strerror(-err));
exit(1);
if (err < 0)
goto out_error_mem;
if (trace->ev_qualifier_ids.nr > 0) {
err = trace__set_ev_qualifier_filter(trace);
if (err < 0)
goto out_errno;
}
pr_debug("%s\n", trace->syscalls.events.sys_exit->filter);
err = perf_evlist__apply_filters(evlist, &evsel);
if (err < 0)
goto out_error_apply_filters;
err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false);
if (err < 0)
goto out_error_mmap;
@ -2462,10 +2499,21 @@ out_error_open:
out_error:
fprintf(trace->output, "%s\n", errbuf);
goto out_delete_evlist;
out_error_apply_filters:
fprintf(trace->output,
"Failed to set filter \"%s\" on event %s with %d (%s)\n",
evsel->filter, perf_evsel__name(evsel), errno,
strerror_r(errno, errbuf, sizeof(errbuf)));
goto out_delete_evlist;
}
out_error_mem:
fprintf(trace->output, "Not enough memory to run!\n");
goto out_delete_evlist;
out_errno:
fprintf(trace->output, "errno=%d,%s\n", errno, strerror(errno));
goto out_delete_evlist;
}
static int trace__replay(struct trace *trace)

View File

@ -51,6 +51,7 @@ struct record_opts {
bool sample_address;
bool sample_weight;
bool sample_time;
bool sample_time_set;
bool period;
bool sample_intr_regs;
bool running_time;

View File

@ -1161,7 +1161,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **e
if (evsel->filter == NULL)
continue;
err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter);
err = perf_evsel__apply_filter(evsel, ncpus, nthreads, evsel->filter);
if (err) {
*err_evsel = evsel;
break;
@ -1175,11 +1175,9 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)
{
struct perf_evsel *evsel;
int err = 0;
const int ncpus = cpu_map__nr(evlist->cpus),
nthreads = thread_map__nr(evlist->threads);
evlist__for_each(evlist, evsel) {
err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter);
err = perf_evsel__set_filter(evsel, filter);
if (err)
break;
}

View File

@ -707,7 +707,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
*/
if (opts->sample_time &&
(!perf_missing_features.sample_id_all &&
(!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu)))
(!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu ||
opts->sample_time_set)))
perf_evsel__set_sample_bit(evsel, TIME);
if (opts->raw_samples && !evsel->no_aux_samples) {
@ -815,14 +816,44 @@ static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthrea
return 0;
}
int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
const char *filter)
int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
const char *filter)
{
return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
PERF_EVENT_IOC_SET_FILTER,
(void *)filter);
}
int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter)
{
char *new_filter = strdup(filter);
if (new_filter != NULL) {
free(evsel->filter);
evsel->filter = new_filter;
return 0;
}
return -1;
}
int perf_evsel__append_filter(struct perf_evsel *evsel,
const char *op, const char *filter)
{
char *new_filter;
if (evsel->filter == NULL)
return perf_evsel__set_filter(evsel, filter);
if (asprintf(&new_filter,"(%s) %s (%s)", evsel->filter, op, filter) > 0) {
free(evsel->filter);
evsel->filter = new_filter;
return 0;
}
return -1;
}
int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads)
{
return perf_evsel__run_ioctl(evsel, ncpus, nthreads,

View File

@ -182,8 +182,11 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel,
void perf_evsel__set_sample_id(struct perf_evsel *evsel,
bool use_sample_identifier);
int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
const char *filter);
int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter);
int perf_evsel__append_filter(struct perf_evsel *evsel,
const char *op, const char *filter);
int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
const char *filter);
int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,

View File

@ -1177,8 +1177,7 @@ int parse_filter(const struct option *opt, const char *str,
return -1;
}
last->filter = strdup(str);
if (last->filter == NULL) {
if (perf_evsel__set_filter(last, str) < 0) {
fprintf(stderr, "not enough memory to hold filter string\n");
return -1;
}

View File

@ -357,3 +357,42 @@ void *memdup(const void *src, size_t len)
return p;
}
char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
{
/*
* FIXME: replace this with an expression using log10() when we
* find a suitable implementation, maybe the one in the dvb drivers...
*
* "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators
*/
size_t size = nints * 28 + 1; /* \0 */
size_t i, printed = 0;
char *expr = malloc(size);
if (expr) {
const char *or_and = "||", *eq_neq = "==";
char *e = expr;
if (!in) {
or_and = "&&";
eq_neq = "!=";
}
for (i = 0; i < nints; ++i) {
if (printed == size)
goto out_err_overflow;
if (i > 0)
printed += snprintf(e + printed, size - printed, " %s ", or_and);
printed += scnprintf(e + printed, size - printed,
"%s %s %d", var, eq_neq, ints[i]);
}
}
return expr;
out_err_overflow:
free(expr);
return NULL;
}

View File

@ -339,4 +339,16 @@ int gzip_decompress_to_file(const char *input, int output_fd);
int lzma_decompress_to_file(const char *input, int output_fd);
#endif
char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints);
static inline char *asprintf_expr_in_ints(const char *var, size_t nints, int *ints)
{
return asprintf_expr_inout_ints(var, true, nints, ints);
}
static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int *ints)
{
return asprintf_expr_inout_ints(var, false, nints, ints);
}
#endif /* GIT_COMPAT_UTIL_H */