lkml.org 
[lkml]   [2014]   [Feb]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH v1 08/11] x86: perf: intel_pt: Add sampling functionality
Date
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


\
 
 \ /
  Last update: 2014-02-06 12:41    [W:0.201 / U:0.804 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site