In snapshot mode AUX records may straddle the boundary of multiple trace buffers. Currently we only queue AUX records whose range is contained wholly within a single buffer.
Fix it by queuing any partial AUX range that overlaps any buffer.
Signed-off-by: James Clark james.clark@linaro.org --- tools/perf/util/cs-etm.c | 54 +++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 24 deletions(-)
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 28f5ce63ba80..5bafee31cf4b 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -3197,7 +3197,8 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o union perf_event *auxtrace_event_union; struct perf_record_auxtrace *auxtrace_event; union perf_event auxtrace_fragment; - __u64 aux_offset, aux_size; + __u64 aux_start, aux_end, auxtrace_start, auxtrace_end; + __u64 frag_start, frag_end, frag_size; enum cs_etm_format format;
struct cs_etm_auxtrace *etm = container_of(session->auxtrace, @@ -3245,42 +3246,41 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o }
if (aux_event->flags & PERF_AUX_FLAG_OVERWRITE) { - /* - * Clamp size in snapshot mode. The buffer size is clamped in - * __auxtrace_mmap__read() for snapshots, so the aux record size doesn't reflect - * the buffer size. - */ - aux_size = min(aux_event->aux_size, auxtrace_event->size); - /* * In this mode, the head also points to the end of the buffer so aux_offset * needs to have the size subtracted so it points to the beginning as in normal mode */ - aux_offset = aux_event->aux_offset - aux_size; + aux_start = aux_event->aux_offset - aux_event->aux_size; + aux_end = aux_event->aux_offset; } else { - aux_size = aux_event->aux_size; - aux_offset = aux_event->aux_offset; + aux_start = aux_event->aux_offset; + aux_end = aux_event->aux_offset + aux_event->aux_size; }
- if (aux_offset >= auxtrace_event->offset && - aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) { + auxtrace_start = auxtrace_event->offset; + auxtrace_end = auxtrace_start + auxtrace_event->size; + frag_start = max(aux_start, auxtrace_start); + frag_end = min(aux_end, auxtrace_end); + + if (frag_start < frag_end) { struct cs_etm_queue *etmq = cs_etm__get_queue(etm, sample->cpu);
if (!etmq) return -EINVAL;
/* - * If this AUX event was inside this buffer somewhere, create a new auxtrace event - * based on the sizes of the aux event, and queue that fragment. + * If this AUX event overlaps this buffer, create a new auxtrace event + * for the overlapping range and queue that fragment. */ + frag_size = frag_end - frag_start; auxtrace_fragment.auxtrace = *auxtrace_event; - auxtrace_fragment.auxtrace.size = aux_size; - auxtrace_fragment.auxtrace.offset = aux_offset; + auxtrace_fragment.auxtrace.size = frag_size; + auxtrace_fragment.auxtrace.offset = frag_start; auxtrace_fragment.auxtrace.idx = etmq->queue_nr; - file_offset += aux_offset - auxtrace_event->offset + auxtrace_event->header.size; + file_offset += frag_start - auxtrace_start + auxtrace_event->header.size;
pr_debug3("CS ETM: Queue buffer size: %#"PRI_lx64" offset: %#"PRI_lx64 - " tid: %d cpu: %d\n", aux_size, aux_offset, sample->tid, sample->cpu); + " tid: %d cpu: %d\n", frag_size, frag_start, sample->tid, sample->cpu); err = auxtrace_queues__add_event(&etm->queues, session, &auxtrace_fragment, file_offset, NULL); if (err) @@ -3320,6 +3320,7 @@ static int cs_etm__queue_aux_records_cb(struct perf_session *session, union perf struct auxtrace_index *auxtrace_index; struct evsel *evsel; size_t i; + bool found = false;
/* Don't care about any other events, we're only queuing buffers for AUX events */ if (event->header.type != PERF_RECORD_AUX) @@ -3353,11 +3354,14 @@ static int cs_etm__queue_aux_records_cb(struct perf_session *session, union perf ret = cs_etm__queue_aux_fragment(session, ent->file_offset, ent->sz, &event->aux, &sample); /* - * Stop search on error or successful values. Continue search on - * 1 ('not found') + * Stop search on error values. Continue search on + * 1 ('not found') or 0 ('queued') */ - if (ret != 1) + if (ret < 0) goto out; + + if (!ret) + found = true; } }
@@ -3365,8 +3369,10 @@ static int cs_etm__queue_aux_records_cb(struct perf_session *session, union perf * Couldn't find the buffer corresponding to this aux record, something went wrong. Warn but * don't exit with an error because it will still be possible to decode other aux records. */ - pr_err("CS ETM: Couldn't find auxtrace buffer for aux_offset: %#"PRI_lx64 - " tid: %d cpu: %d\n", event->aux.aux_offset, sample.tid, sample.cpu); + if (!found) { + pr_err("CS ETM: Couldn't find auxtrace buffer for aux_offset: %#"PRI_lx64 + " tid: %d cpu: %d\n", event->aux.aux_offset, sample.tid, sample.cpu); + } ret = 0; out: perf_sample__exit(&sample);