From db58b3735f45345c06cb9a14d0f83f5b26c1ebf3 Mon Sep 17 00:00:00 2001 From: Markus Metzger Date: Fri, 14 Oct 2016 09:08:01 +0200 Subject: btrace: read entire aux buffer The data_head of a perf event data buffer grows indefinitely. Users are expected to compute data_head % data_size to find the location inside the perf event data buffer. The aux_head of a perf event aux buffer wraps around and always stays within the perf event aux buffer. Well, at least that's the behaviour for BTS and PT - where BTS uses the data buffer and PT the aux buffer. GDB does not read beyond data_head or aux_head. This is OK for BTS but wrong for PT. It causes only a portion of the trace to be considered by GDB. In the extreme case, the buffer may appear (almost) empty. Thanks to Tim Wiederhake for reporting the anomaly. Change it to read the entire aux buffer for PT. The buffer is initially zero so any extra zeroes we read before aux_head wraps around the first time will be ignored when searching for the first PSB packet in order to synchronize onto the trace stream. gdb/ * nat/linux-btrace.c (perf_event_read): Allow data_head < size. * nat/linux-btrace.c (perf_event_read_all): Do not adjust size. Change-Id: If4f8049a2080a5f16f336309450b32a3eb1e3ec9 --- gdb/ChangeLog | 5 +++++ gdb/nat/linux-btrace.c | 19 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index b4398f6b6fd..600925fc18b 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,8 @@ +2016-11-14 Markus Metzger + + * nat/linux-btrace.c (perf_event_read): Allow data_head < size. + * nat/linux-btrace.c (perf_event_read_all): Do not adjust size. + 2016-11-12 Tom Tromey * rust-exp.y (super_name): Use std::vector. diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c index 5ac5db24e1b..e4144c63f3b 100644 --- a/gdb/nat/linux-btrace.c +++ b/gdb/nat/linux-btrace.c @@ -119,10 +119,24 @@ perf_event_read (const struct perf_event_buffer *pev, __u64 data_head, if (size == 0) return NULL; + /* We should never ask for more data than the buffer can hold. */ + buffer_size = pev->size; + gdb_assert (size <= buffer_size); + + /* If we ask for more data than we seem to have, we wrap around and read + data from the end of the buffer. This is already handled by the % + BUFFER_SIZE operation, below. Here, we just need to make sure that we + don't underflow. + + Note that this is perfectly OK for perf event buffers where data_head + doesn'grow indefinitely and instead wraps around to remain within the + buffer's boundaries. */ + if (data_head < size) + data_head += buffer_size; + gdb_assert (size <= data_head); data_tail = data_head - size; - buffer_size = pev->size; begin = pev->mem; start = begin + data_tail % buffer_size; stop = begin + data_head % buffer_size; @@ -153,10 +167,7 @@ perf_event_read_all (struct perf_event_buffer *pev, gdb_byte **data, __u64 data_head; data_head = *pev->data_head; - size = pev->size; - if (data_head < size) - size = (size_t) data_head; *data = perf_event_read (pev, data_head, size); *psize = size; -- cgit v1.2.1