aboutsummaryrefslogtreecommitdiff
path: root/gdb/btrace.c
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/btrace.c
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/btrace.c')
-rw-r--r--gdb/btrace.c156
1 files changed, 139 insertions, 17 deletions
diff --git a/gdb/btrace.c b/gdb/btrace.c
index ba87e16..28970c3 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -599,9 +599,9 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo,
DEBUG ("compute ftrace");
gdbarch = target_gdbarch ();
- begin = NULL;
- end = NULL;
- level = INT_MAX;
+ begin = btinfo->begin;
+ end = btinfo->end;
+ level = begin != NULL ? -btinfo->level : INT_MAX;
blk = VEC_length (btrace_block_s, btrace);
while (blk != 0)
@@ -728,27 +728,158 @@ btrace_teardown (struct thread_info *tp)
btrace_clear (tp);
}
+/* Adjust the block trace in order to stitch old and new trace together.
+ BTRACE is the new delta trace between the last and the current stop.
+ BTINFO is the old branch trace until the last stop.
+ May modify BTRACE as well as the existing trace in BTINFO.
+ Return 0 on success, -1 otherwise. */
+
+static int
+btrace_stitch_trace (VEC (btrace_block_s) **btrace,
+ const struct btrace_thread_info *btinfo)
+{
+ struct btrace_function *last_bfun;
+ struct btrace_insn *last_insn;
+ btrace_block_s *first_new_block;
+
+ /* If we don't have trace, there's nothing to do. */
+ if (VEC_empty (btrace_block_s, *btrace))
+ return 0;
+
+ last_bfun = btinfo->end;
+ gdb_assert (last_bfun != NULL);
+
+ /* Beware that block trace starts with the most recent block, so the
+ chronologically first block in the new trace is the last block in
+ the new trace's block vector. */
+ first_new_block = VEC_last (btrace_block_s, *btrace);
+ last_insn = VEC_last (btrace_insn_s, last_bfun->insn);
+
+ /* If the current PC at the end of the block is the same as in our current
+ trace, there are two explanations:
+ 1. we executed the instruction and some branch brought us back.
+ 2. we have not made any progress.
+ In the first case, the delta trace vector should contain at least two
+ entries.
+ In the second case, the delta trace vector should contain exactly one
+ entry for the partial block containing the current PC. Remove it. */
+ if (first_new_block->end == last_insn->pc
+ && VEC_length (btrace_block_s, *btrace) == 1)
+ {
+ VEC_pop (btrace_block_s, *btrace);
+ return 0;
+ }
+
+ DEBUG ("stitching %s to %s", ftrace_print_insn_addr (last_insn),
+ core_addr_to_string_nz (first_new_block->end));
+
+ /* Do a simple sanity check to make sure we don't accidentally end up
+ with a bad block. This should not occur in practice. */
+ if (first_new_block->end < last_insn->pc)
+ {
+ warning (_("Error while trying to read delta trace. Falling back to "
+ "a full read."));
+ return -1;
+ }
+
+ /* We adjust the last block to start at the end of our current trace. */
+ gdb_assert (first_new_block->begin == 0);
+ first_new_block->begin = last_insn->pc;
+
+ /* We simply pop the last insn so we can insert it again as part of
+ the normal branch trace computation.
+ Since instruction iterators are based on indices in the instructions
+ vector, we don't leave any pointers dangling. */
+ DEBUG ("pruning insn at %s for stitching",
+ ftrace_print_insn_addr (last_insn));
+
+ VEC_pop (btrace_insn_s, last_bfun->insn);
+
+ /* The instructions vector may become empty temporarily if this has
+ been the only instruction in this function segment.
+ This violates the invariant but will be remedied shortly by
+ btrace_compute_ftrace when we add the new trace. */
+ return 0;
+}
+
+/* Clear the branch trace histories in BTINFO. */
+
+static void
+btrace_clear_history (struct btrace_thread_info *btinfo)
+{
+ xfree (btinfo->insn_history);
+ xfree (btinfo->call_history);
+ xfree (btinfo->replay);
+
+ btinfo->insn_history = NULL;
+ btinfo->call_history = NULL;
+ btinfo->replay = NULL;
+}
+
/* See btrace.h. */
void
btrace_fetch (struct thread_info *tp)
{
struct btrace_thread_info *btinfo;
+ struct btrace_target_info *tinfo;
VEC (btrace_block_s) *btrace;
struct cleanup *cleanup;
+ int errcode;
DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+ btrace = NULL;
btinfo = &tp->btrace;
- if (btinfo->target == NULL)
+ tinfo = btinfo->target;
+ if (tinfo == NULL)
+ return;
+
+ /* There's no way we could get new trace while replaying.
+ On the other hand, delta trace would return a partial record with the
+ current PC, which is the replay PC, not the last PC, as expected. */
+ if (btinfo->replay != NULL)
return;
- btrace = target_read_btrace (btinfo->target, BTRACE_READ_NEW);
cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
+ /* Let's first try to extend the trace we already have. */
+ if (btinfo->end != NULL)
+ {
+ errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_DELTA);
+ if (errcode == 0)
+ {
+ /* Success. Let's try to stitch the traces together. */
+ errcode = btrace_stitch_trace (&btrace, btinfo);
+ }
+ else
+ {
+ /* We failed to read delta trace. Let's try to read new trace. */
+ errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_NEW);
+
+ /* If we got any new trace, discard what we have. */
+ if (errcode == 0 && !VEC_empty (btrace_block_s, btrace))
+ btrace_clear (tp);
+ }
+
+ /* If we were not able to read the trace, we start over. */
+ if (errcode != 0)
+ {
+ btrace_clear (tp);
+ errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_ALL);
+ }
+ }
+ else
+ errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_ALL);
+
+ /* If we were not able to read the branch trace, signal an error. */
+ if (errcode != 0)
+ error (_("Failed to read branch trace."));
+
+ /* Compute the trace, provided we have any. */
if (!VEC_empty (btrace_block_s, btrace))
{
- btrace_clear (tp);
+ btrace_clear_history (btinfo);
btrace_compute_ftrace (btinfo, btrace);
}
@@ -783,13 +914,7 @@ btrace_clear (struct thread_info *tp)
btinfo->begin = NULL;
btinfo->end = NULL;
- xfree (btinfo->insn_history);
- xfree (btinfo->call_history);
- xfree (btinfo->replay);
-
- btinfo->insn_history = NULL;
- btinfo->call_history = NULL;
- btinfo->replay = NULL;
+ btrace_clear_history (btinfo);
}
/* See btrace.h. */
@@ -881,10 +1006,7 @@ parse_xml_btrace (const char *buffer)
errcode = gdb_xml_parse_quick (_("btrace"), "btrace.dtd", btrace_elements,
buffer, &btrace);
if (errcode != 0)
- {
- do_cleanups (cleanup);
- return NULL;
- }
+ error (_("Error parsing branch trace."));
/* Keep parse results. */
discard_cleanups (cleanup);