[PATCH v1 08/11] x86: perf: intel_pt: Add sampling functionality

Alexander Shishkin
2014-02-06T10:51:37Z

Intel Processor Trace (PT) data can be used in perf event samples to annotate
other perf events. This patch implements itrace sampling related hooks that
configure and output sampling data to perf stream. Users will need to include
PERF_SAMPLE_ITRACE flag in the attr.sample_type mask and specify PT's PMU type
in attr.itrace_sample_type field.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
arch/x86/kernel/cpu/perf_event_intel_pt.c | 115 ++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)

diff --git a/arch/x86/kernel/cpu/perf_event_intel_pt.c b/arch/x86/kernel/cpu/perf_event_intel_pt.c
index b6b1a84..af1482d 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_pt.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_pt.c
@@ -946,17 +946,130 @@ static void pt_event_read(struct perf_event *event)
{
}

+typedef unsigned int (*pt_copyfn)(void *data, const void *src,
+ unsigned int len);
+
+/**
+ * pt_buffer_output - copy part of pt buffer to perf stream
+ * @buf: buffer to copy from
+ * @from: initial offset
+ * @to: final offset
+ * @copyfn: function that copies data out (like perf_output_copy())
+ * @data: data to be passed on to the copy function (like perf_output_handle)
+ */
+static int pt_buffer_output(struct pt_buffer *buf, unsigned long from,
+ unsigned long to, pt_copyfn copyfn, void *data)
+{
+ unsigned long tocopy;
+ unsigned int len = 0, remainder;
+ void *page;
+
+ do {
+ tocopy = PAGE_SIZE - offset_in_page(from);
+ if (to > from)
+ tocopy = min(tocopy, to - from);
+ if (!tocopy)
+ break;
+
+ page = pt_buffer_get_page(buf, from >> PAGE_SHIFT);
+ if (WARN_ONCE(!page, "no data page for %lx offset\n", from))
+ break;
+
+ page += offset_in_page(from);
+
+ remainder = copyfn(data, page, tocopy);
+ if (remainder)
+ return -EFAULT;
+
+ len += tocopy;
+ from += tocopy;
+ if (from == buf->size)
+ from = 0;
+ } while (to != from);
+ return len;
+}
+
static int pt_event_init(struct perf_event *event)
{
if (event->attr.type != pt_pmu.itrace.pmu.type)
return -ENOENT;

+ /* can't be both */
+ if (event->attr.sample_type & PERF_SAMPLE_ITRACE)
+ return -ENOENT;
+
if (!pt_event_valid(event))
return -EINVAL;

return 0;
}

+static unsigned long pt_trace_sampler_trace(struct perf_event *event,
+ struct perf_sample_data *data)
+{
+ struct pt_buffer *buf;
+
+ pt_event_stop(event, 0);
+
+ buf = itrace_event_get_priv(event);
+ if (!buf) {
+ data->trace.size = 0;
+ goto out;
+ }
+
+ pt_read_offset(buf);
+ pt_update_head(buf);
+
+ data->trace.to = local64_read(&buf->head);
+
+ if (data->trace.to < event->attr.itrace_sample_size)
+ data->trace.from = buf->size + data->trace.to -
+ event->attr.itrace_sample_size;
+ else
+ data->trace.from = data->trace.to -
+ event->attr.itrace_sample_size;
+ data->trace.size = ALIGN(event->attr.itrace_sample_size, sizeof(u64));
+
+ itrace_event_put(event);
+
+out:
+ if (!data->trace.size)
+ pt_event_start(event, 0);
+
+ return data->trace.size;
+}
+
+static void pt_trace_sampler_output(struct perf_event *event,
+ struct perf_output_handle *handle,
+ struct perf_sample_data *data)
+{
+ unsigned long padding;
+ struct pt_buffer *buf;
+ int ret;
+
+ buf = itrace_event_get_priv(event);
+ if (!buf)
+ return;
+
+ ret = pt_buffer_output(buf, data->trace.from, data->trace.to,
+ (pt_copyfn)perf_output_copy, handle);
+ itrace_event_put(event);
+ if (ret < 0) {
+ pr_warn("%s: failed to copy trace data\n", __func__);
+ goto out;
+ }
+
+ padding = data->trace.size - ret;
+ if (padding) {
+ u64 u = 0;
+
+ perf_output_copy(handle, &u, padding);
+ }
+
+out:
+ pt_event_start(event, 0);
+}
+
static __init int pt_init(void)
{
int ret, cpu;
@@ -982,6 +1095,8 @@ static __init int pt_init(void)
pt_pmu.itrace.pmu.read = pt_event_read;
pt_pmu.itrace.alloc_buffer = pt_buffer_itrace_alloc;
pt_pmu.itrace.free_buffer = pt_buffer_itrace_free;
+ pt_pmu.itrace.sample_trace = pt_trace_sampler_trace;
+ pt_pmu.itrace.sample_output = pt_trace_sampler_output;
pt_pmu.itrace.name = "intel_pt";
ret = itrace_pmu_register(&pt_pmu.itrace);

--
1.8.5.2