Commit 375637bc authored by Alexander Shishkin's avatar Alexander Shishkin Committed by Ingo Molnar

perf/core: Introduce address range filtering

Many instruction tracing PMUs out there support address range-based
filtering, which would, for example, generate trace data only for a
given range of instruction addresses, which is useful for tracing
individual functions, modules or libraries. Other PMUs may also
utilize this functionality to allow filtering to or filtering out
code at certain address ranges.

This patch introduces the interface for userspace to specify these
filters and for the PMU drivers to apply these filters to hardware
configuration.

The user interface is an ASCII string that is passed via an ioctl()
and specifies (in the form of an ASCII string) address ranges within
certain object files or within kernel. There is no special treatment
for kernel modules yet, but it might be a worthy pursuit.

The PMU driver interface basically adds two extra callbacks to the
PMU driver structure, one of which validates the filter configuration
proposed by the user against what the hardware is actually capable of
doing and the other one translates hardware-independent filter
configuration into something that can be programmed into the
hardware.
Signed-off-by: default avatarAlexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarMathieu Poirier <mathieu.poirier@linaro.org>
Cc: Arnaldo Carvalho de Melo <acme@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Cc: vince@deater.net
Link: http://lkml.kernel.org/r/1461771888-10409-6-git-send-email-alexander.shishkin@linux.intel.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent b73e4fef
...@@ -151,6 +151,15 @@ struct hw_perf_event { ...@@ -151,6 +151,15 @@ struct hw_perf_event {
*/ */
struct task_struct *target; struct task_struct *target;
/*
* PMU would store hardware filter configuration
* here.
*/
void *addr_filters;
/* Last sync'ed generation of filters */
unsigned long addr_filters_gen;
/* /*
* hw_perf_event::state flags; used to track the PERF_EF_* state. * hw_perf_event::state flags; used to track the PERF_EF_* state.
*/ */
...@@ -240,6 +249,9 @@ struct pmu { ...@@ -240,6 +249,9 @@ struct pmu {
int task_ctx_nr; int task_ctx_nr;
int hrtimer_interval_ms; int hrtimer_interval_ms;
/* number of address filters this PMU can do */
unsigned int nr_addr_filters;
/* /*
* Fully disable/enable this PMU, can be used to protect from the PMI * Fully disable/enable this PMU, can be used to protect from the PMI
* as well as for lazy/batch writing of the MSRs. * as well as for lazy/batch writing of the MSRs.
...@@ -392,12 +404,71 @@ struct pmu { ...@@ -392,12 +404,71 @@ struct pmu {
*/ */
void (*free_aux) (void *aux); /* optional */ void (*free_aux) (void *aux); /* optional */
/*
* Validate address range filters: make sure the HW supports the
* requested configuration and number of filters; return 0 if the
* supplied filters are valid, -errno otherwise.
*
* Runs in the context of the ioctl()ing process and is not serialized
* with the rest of the PMU callbacks.
*/
int (*addr_filters_validate) (struct list_head *filters);
/* optional */
/*
* Synchronize address range filter configuration:
* translate hw-agnostic filters into hardware configuration in
* event::hw::addr_filters.
*
* Runs as a part of filter sync sequence that is done in ->start()
* callback by calling perf_event_addr_filters_sync().
*
* May (and should) traverse event::addr_filters::list, for which its
* caller provides necessary serialization.
*/
void (*addr_filters_sync) (struct perf_event *event);
/* optional */
/* /*
* Filter events for PMU-specific reasons. * Filter events for PMU-specific reasons.
*/ */
int (*filter_match) (struct perf_event *event); /* optional */ int (*filter_match) (struct perf_event *event); /* optional */
}; };
/**
* struct perf_addr_filter - address range filter definition
* @entry: event's filter list linkage
* @inode: object file's inode for file-based filters
* @offset: filter range offset
* @size: filter range size
* @range: 1: range, 0: address
* @filter: 1: filter/start, 0: stop
*
* This is a hardware-agnostic filter configuration as specified by the user.
*/
struct perf_addr_filter {
struct list_head entry;
struct inode *inode;
unsigned long offset;
unsigned long size;
unsigned int range : 1,
filter : 1;
};
/**
* struct perf_addr_filters_head - container for address range filters
* @list: list of filters for this event
* @lock: spinlock that serializes accesses to the @list and event's
* (and its children's) filter generations.
*
* A child event will use parent's @list (and therefore @lock), so they are
* bundled together; see perf_event_addr_filters().
*/
struct perf_addr_filters_head {
struct list_head list;
raw_spinlock_t lock;
};
/** /**
* enum perf_event_active_state - the states of a event * enum perf_event_active_state - the states of a event
*/ */
...@@ -566,6 +637,12 @@ struct perf_event { ...@@ -566,6 +637,12 @@ struct perf_event {
atomic_t event_limit; atomic_t event_limit;
/* address range filters */
struct perf_addr_filters_head addr_filters;
/* vma address array for file-based filders */
unsigned long *addr_filters_offs;
unsigned long addr_filters_gen;
void (*destroy)(struct perf_event *); void (*destroy)(struct perf_event *);
struct rcu_head rcu_head; struct rcu_head rcu_head;
...@@ -1070,6 +1147,27 @@ static inline bool is_write_backward(struct perf_event *event) ...@@ -1070,6 +1147,27 @@ static inline bool is_write_backward(struct perf_event *event)
return !!event->attr.write_backward; return !!event->attr.write_backward;
} }
static inline bool has_addr_filter(struct perf_event *event)
{
return event->pmu->nr_addr_filters;
}
/*
* An inherited event uses parent's filters
*/
static inline struct perf_addr_filters_head *
perf_event_addr_filters(struct perf_event *event)
{
struct perf_addr_filters_head *ifh = &event->addr_filters;
if (event->parent)
ifh = &event->parent->addr_filters;
return ifh;
}
extern void perf_event_addr_filters_sync(struct perf_event *event);
extern int perf_output_begin(struct perf_output_handle *handle, extern int perf_output_begin(struct perf_output_handle *handle,
struct perf_event *event, unsigned int size); struct perf_event *event, unsigned int size);
extern int perf_output_begin_forward(struct perf_output_handle *handle, extern int perf_output_begin_forward(struct perf_output_handle *handle,
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment