aboutsummaryrefslogtreecommitdiff
path: root/gdb/common
diff options
context:
space:
mode:
authorMarkus Metzger <markus.t.metzger@intel.com>2013-06-03 15:39:35 +0200
committerMarkus Metzger <markus.t.metzger@intel.com>2014-01-16 13:11:42 +0100
commit969c39fbcd6a5675c1f4b97cd23d680e4b5b6487 (patch)
tree54d7a2c546ecf86fbe37536db86d0916734203d8 /gdb/common
parent0b722aec57e2e54083c1d56657762945ad4604fc (diff)
downloadgdb-969c39fbcd6a5675c1f4b97cd23d680e4b5b6487.zip
gdb-969c39fbcd6a5675c1f4b97cd23d680e4b5b6487.tar.gz
gdb-969c39fbcd6a5675c1f4b97cd23d680e4b5b6487.tar.bz2
btrace, gdbserver: read branch trace incrementally
Read branch trace data incrementally and extend the current trace rather than discarding it and reading the entire trace buffer each time. If the branch trace buffer overflowed, we can't extend the current trace so we discard it and start anew by reading the entire branch trace buffer. 2014-01-16 Markus Metzger <markus.t.metzger@intel.com> * common/linux-btrace.c (perf_event_read_bts, linux_read_btrace): Support delta reads. (linux_disable_btrace): Change return type. * common/linux-btrace.h (linux_read_btrace): Change parameters and return type to allow error reporting. Update users. (linux_disable_btrace): Change return type. Update users. * common/btrace-common.h (btrace_read_type) <BTRACE_READ_DELTA>: New. (btrace_error): New. (btrace_block) <begin>: Comment on BEGIN == 0. * btrace.c (btrace_compute_ftrace): Start from the end of the current trace. (btrace_stitch_trace, btrace_clear_history): New. (btrace_fetch): Read delta trace, return if replaying. (btrace_clear): Move clear history code to btrace_clear_history. (parse_xml_btrace): Throw an error if parsing failed. * target.h (struct target_ops) <to_read_btrace>: Change parameters and return type to allow error reporting. (target_read_btrace): Change parameters and return type to allow error reporting. * target.c (target_read_btrace): Update. * remote.c (remote_read_btrace): Support delta reads. Pass errors on. * NEWS: Announce it. gdbserver/ * target.h (target_ops) <read_btrace>: Change parameters and return type to allow error reporting. * server.c (handle_qxfer_btrace): Support delta reads. Pass trace reading errors on. * linux-low.c (linux_low_read_btrace): Pass trace reading errors on. (linux_low_disable_btrace): New.
Diffstat (limited to 'gdb/common')
-rw-r--r--gdb/common/btrace-common.h27
-rw-r--r--gdb/common/linux-btrace.c97
-rw-r--r--gdb/common/linux-btrace.h15
3 files changed, 101 insertions, 38 deletions
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
index 1d389af..25617bb 100644
--- a/gdb/common/btrace-common.h
+++ b/gdb/common/btrace-common.h
@@ -42,7 +42,9 @@
asynchronous, e.g. interrupts. */
struct btrace_block
{
- /* The address of the first byte of the first instruction in the block. */
+ /* The address of the first byte of the first instruction in the block.
+ The address may be zero if we do not know the beginning of this block,
+ such as for the first block in a delta trace. */
CORE_ADDR begin;
/* The address of the first byte of the last instruction in the block. */
@@ -67,7 +69,28 @@ enum btrace_read_type
BTRACE_READ_ALL,
/* Send all available trace, if it changed. */
- BTRACE_READ_NEW
+ BTRACE_READ_NEW,
+
+ /* Send the trace since the last request. This will fail if the trace
+ buffer overflowed. */
+ BTRACE_READ_DELTA
+};
+
+/* Enumeration of btrace errors. */
+
+enum btrace_error
+{
+ /* No error. Everything is OK. */
+ BTRACE_ERR_NONE,
+
+ /* An unknown error. */
+ BTRACE_ERR_UNKNOWN,
+
+ /* Branch tracing is not supported on this system. */
+ BTRACE_ERR_NOT_SUPPORTED,
+
+ /* The branch trace buffer overflowed; no delta read possible. */
+ BTRACE_ERR_OVERFLOW
};
#endif /* BTRACE_COMMON_H */
diff --git a/gdb/common/linux-btrace.c b/gdb/common/linux-btrace.c
index e469900..218e0ce 100644
--- a/gdb/common/linux-btrace.c
+++ b/gdb/common/linux-btrace.c
@@ -172,11 +172,11 @@ perf_event_sample_ok (const struct perf_event_sample *sample)
static VEC (btrace_block_s) *
perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
- const uint8_t *end, const uint8_t *start)
+ const uint8_t *end, const uint8_t *start, size_t size)
{
VEC (btrace_block_s) *btrace = NULL;
struct perf_event_sample sample;
- size_t read = 0, size = (end - begin);
+ size_t read = 0;
struct btrace_block block = { 0, 0 };
struct regcache *regcache;
@@ -252,6 +252,13 @@ perf_event_read_bts (struct btrace_target_info* tinfo, const uint8_t *begin,
block.end = psample->bts.from;
}
+ /* Push the last block (i.e. the first one of inferior execution), as well.
+ We don't know where it ends, but we know where it starts. If we're
+ reading delta trace, we can fill in the start address later on.
+ Otherwise we will prune it. */
+ block.begin = 0;
+ VEC_safe_push (btrace_block_s, btrace, &block);
+
return btrace;
}
@@ -476,7 +483,7 @@ linux_enable_btrace (ptid_t ptid)
/* See linux-btrace.h. */
-int
+enum btrace_error
linux_disable_btrace (struct btrace_target_info *tinfo)
{
int errcode;
@@ -484,12 +491,12 @@ linux_disable_btrace (struct btrace_target_info *tinfo)
errno = 0;
errcode = munmap (tinfo->buffer, perf_event_mmap_size (tinfo));
if (errcode != 0)
- return errno;
+ return BTRACE_ERR_UNKNOWN;
close (tinfo->file);
xfree (tinfo);
- return 0;
+ return BTRACE_ERR_NONE;
}
/* Check whether the branch trace has changed. */
@@ -504,21 +511,24 @@ linux_btrace_has_changed (struct btrace_target_info *tinfo)
/* See linux-btrace.h. */
-VEC (btrace_block_s) *
-linux_read_btrace (struct btrace_target_info *tinfo,
+enum btrace_error
+linux_read_btrace (VEC (btrace_block_s) **btrace,
+ struct btrace_target_info *tinfo,
enum btrace_read_type type)
{
- VEC (btrace_block_s) *btrace = NULL;
volatile struct perf_event_mmap_page *header;
const uint8_t *begin, *end, *start;
- unsigned long data_head, retries = 5;
- size_t buffer_size;
+ unsigned long data_head, data_tail, retries = 5;
+ size_t buffer_size, size;
+ /* For delta reads, we return at least the partial last block containing
+ the current PC. */
if (type == BTRACE_READ_NEW && !linux_btrace_has_changed (tinfo))
- return NULL;
+ return BTRACE_ERR_NONE;
header = perf_event_header (tinfo);
buffer_size = perf_event_buffer_size (tinfo);
+ data_tail = tinfo->data_head;
/* We may need to retry reading the trace. See below. */
while (retries--)
@@ -526,23 +536,45 @@ linux_read_btrace (struct btrace_target_info *tinfo,
data_head = header->data_head;
/* Delete any leftover trace from the previous iteration. */
- VEC_free (btrace_block_s, btrace);
+ VEC_free (btrace_block_s, *btrace);
- /* If there's new trace, let's read it. */
- if (data_head != tinfo->data_head)
+ if (type == BTRACE_READ_DELTA)
{
- /* Data_head keeps growing; the buffer itself is circular. */
- begin = perf_event_buffer_begin (tinfo);
- start = begin + data_head % buffer_size;
-
- if (data_head <= buffer_size)
- end = start;
- else
- end = perf_event_buffer_end (tinfo);
+ /* Determine the number of bytes to read and check for buffer
+ overflows. */
+
+ /* Check for data head overflows. We might be able to recover from
+ those but they are very unlikely and it's not really worth the
+ effort, I think. */
+ if (data_head < data_tail)
+ return BTRACE_ERR_OVERFLOW;
+
+ /* If the buffer is smaller than the trace delta, we overflowed. */
+ size = data_head - data_tail;
+ if (buffer_size < size)
+ return BTRACE_ERR_OVERFLOW;
+ }
+ else
+ {
+ /* Read the entire buffer. */
+ size = buffer_size;
- btrace = perf_event_read_bts (tinfo, begin, end, start);
+ /* Adjust the size if the buffer has not overflowed, yet. */
+ if (data_head < size)
+ size = data_head;
}
+ /* Data_head keeps growing; the buffer itself is circular. */
+ begin = perf_event_buffer_begin (tinfo);
+ start = begin + data_head % buffer_size;
+
+ if (data_head <= buffer_size)
+ end = start;
+ else
+ end = perf_event_buffer_end (tinfo);
+
+ *btrace = perf_event_read_bts (tinfo, begin, end, start, size);
+
/* The stopping thread notifies its ptracer before it is scheduled out.
On multi-core systems, the debugger might therefore run while the
kernel might be writing the last branch trace records.
@@ -554,7 +586,13 @@ linux_read_btrace (struct btrace_target_info *tinfo,
tinfo->data_head = data_head;
- return btrace;
+ /* Prune the incomplete last block (i.e. the first one of inferior execution)
+ if we're not doing a delta read. There is no way of filling in its zeroed
+ BEGIN element. */
+ if (!VEC_empty (btrace_block_s, *btrace) && type != BTRACE_READ_DELTA)
+ VEC_pop (btrace_block_s, *btrace);
+
+ return BTRACE_ERR_NONE;
}
#else /* !HAVE_LINUX_PERF_EVENT_H */
@@ -577,19 +615,20 @@ linux_enable_btrace (ptid_t ptid)
/* See linux-btrace.h. */
-int
+enum btrace_error
linux_disable_btrace (struct btrace_target_info *tinfo)
{
- return ENOSYS;
+ return BTRACE_ERR_NOT_SUPPORTED;
}
/* See linux-btrace.h. */
-VEC (btrace_block_s) *
-linux_read_btrace (struct btrace_target_info *tinfo,
+enum btrace_error
+linux_read_btrace (VEC (btrace_block_s) **btrace,
+ struct btrace_target_info *tinfo,
enum btrace_read_type type)
{
- return NULL;
+ return BTRACE_ERR_NOT_SUPPORTED;
}
#endif /* !HAVE_LINUX_PERF_EVENT_H */
diff --git a/gdb/common/linux-btrace.h b/gdb/common/linux-btrace.h
index 32a0403..a97b697 100644
--- a/gdb/common/linux-btrace.h
+++ b/gdb/common/linux-btrace.h
@@ -61,17 +61,18 @@ struct btrace_target_info
int ptr_bits;
};
-/* Check whether branch tracing is supported. */
+/* See to_supports_btrace in target.h. */
extern int linux_supports_btrace (void);
-/* Enable branch tracing for @ptid. */
+/* See to_enable_btrace in target.h. */
extern struct btrace_target_info *linux_enable_btrace (ptid_t ptid);
-/* Disable branch tracing and deallocate @tinfo. */
-extern int linux_disable_btrace (struct btrace_target_info *tinfo);
+/* See to_disable_btrace in target.h. */
+extern enum btrace_error linux_disable_btrace (struct btrace_target_info *ti);
-/* Read branch trace data. */
-extern VEC (btrace_block_s) *linux_read_btrace (struct btrace_target_info *,
- enum btrace_read_type);
+/* See to_read_btrace in target.h. */
+extern enum btrace_error linux_read_btrace (VEC (btrace_block_s) **btrace,
+ struct btrace_target_info *btinfo,
+ enum btrace_read_type type);
#endif /* LINUX_BTRACE_H */