From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 1331 invoked by alias); 20 Nov 2014 10:47:50 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 1238 invoked by uid 89); 20 Nov 2014 10:47:49 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.8 required=5.0 tests=AWL,BAYES_00,T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mga11.intel.com Received: from mga11.intel.com (HELO mga11.intel.com) (192.55.52.93) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 20 Nov 2014 10:47:42 +0000 Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga102.fm.intel.com with ESMTP; 20 Nov 2014 02:47:37 -0800 X-ExtLoop1: 1 Received: from irvmail001.ir.intel.com ([163.33.26.43]) by fmsmga002.fm.intel.com with ESMTP; 20 Nov 2014 02:47:30 -0800 Received: from ulvlx001.iul.intel.com (ulvlx001.iul.intel.com [172.28.207.17]) by irvmail001.ir.intel.com (8.14.3/8.13.6/MailSET/Hub) with ESMTP id sAKAlTka008507; Thu, 20 Nov 2014 10:47:29 GMT Received: from ulvlx001.iul.intel.com (localhost [127.0.0.1]) by ulvlx001.iul.intel.com with ESMTP id sAKAlTMs010294; Thu, 20 Nov 2014 11:47:29 +0100 Received: (from mmetzger@localhost) by ulvlx001.iul.intel.com with œ id sAKAlTqJ010290; Thu, 20 Nov 2014 11:47:29 +0100 From: Markus Metzger To: palves@redhat.com Cc: gdb-patches@sourceware.org Subject: [PATCH v2 11/13] record-btrace: indicate gaps Date: Thu, 20 Nov 2014 10:47:00 -0000 Message-Id: <1416480444-9943-12-git-send-email-markus.t.metzger@intel.com> In-Reply-To: <1416480444-9943-1-git-send-email-markus.t.metzger@intel.com> References: <1416480444-9943-1-git-send-email-markus.t.metzger@intel.com> X-IsSubscribed: yes X-SW-Source: 2014-11/txt/msg00471.txt.bz2 Indicate gaps in the trace due to decode errors. Internally, a gap is represented as a btrace function segment without instructions and with a non-zero format-specific error code. Show the gap when traversing the instruction or function call history. Also indicate gaps in "info record". It looks like this: (gdb) info record Active record target: record-btrace Recording format: Intel(R) Branch Trace Store. Buffer size: 64KB. Recorded 32 instructions in 5 functions (1 gaps) for thread 1 (process 7182). (gdb) record function-call-history /cli 1 fib inst 1,9 at src/fib.c:9,14 2 fib inst 10,20 at src/fib.c:6,14 3 [decode error (1): instruction overflow] 4 fib inst 21,28 at src/fib.c:11,14 5 fib inst 29,33 at src/fib.c:6,9 (gdb) record instruction-history 20,22 20 0x000000000040062f : sub $0x1,%rax [decode error (1): instruction overflow] 21 0x0000000000400613 : add $0x1,%rax 22 0x0000000000400617 : mov %rax,0x200a3a(%rip) (gdb) Gaps are ignored during reverse execution and replay. 2014-11-20 Markus Metzger * btrace.c (ftrace_find_call): Skip gaps. (ftrace_new_gap): New. (ftrace_update_function): Create new function after gap. (btrace_compute_ftrace_bts): Create gap on error. (btrace_clear): Reset the number of gaps. (btrace_insn_get): Return NULL if the iterator points to a gap. (btrace_insn_number): Return zero if the iterator points to a gap. (btrace_insn_end): Assert that the last function is not empty. (btrace_insn_next, btrace_insn_prev, btrace_insn_cmp): Handle gaps. (btrace_find_insn_by_number): Assert that the found iterator does not point to a gap. (btrace_call_next, btrace_call_prev): Assert that the last function is not a gap. * btrace.h (btrace_bts_error): New. (btrace_function): Update comment. (btrace_function) : Update comment. (btrace_function) : New. (btrace_thread_info) : New. (btrace_thread_info) : Update comment. (btrace_insn_get): Update comment. * record-btrace.c (btrace_ui_out_decode_error): New. (record_btrace_info): Print number of gaps. (btrace_insn_history, btrace_call_history): Call btrace_ui_out_decode_error for gaps. (record_btrace_step_thread): Skip gaps. testsuite/ * gdb.btrace/buffer-size.exp: Update "info record" output. * gdb.btrace/delta.exp: Update "info record" output. * gdb.btrace/enable.exp: Update "info record" output. * gdb.btrace/finish.exp: Update "info record" output. * gdb.btrace/instruction_history.exp: Update "info record" output. * gdb.btrace/next.exp: Update "info record" output. * gdb.btrace/nexti.exp: Update "info record" output. * gdb.btrace/step.exp: Update "info record" output. * gdb.btrace/stepi.exp: Update "info record" output. * gdb.btrace/nohist.exp: Update "info record" output. --- gdb/btrace.c | 135 +++++++++++++++++-- gdb/btrace.h | 40 +++++- gdb/record-btrace.c | 160 ++++++++++++++++++----- gdb/testsuite/gdb.btrace/buffer-size.exp | 4 +- gdb/testsuite/gdb.btrace/delta.exp | 8 +- gdb/testsuite/gdb.btrace/enable.exp | 2 +- gdb/testsuite/gdb.btrace/finish.exp | 2 +- gdb/testsuite/gdb.btrace/instruction_history.exp | 2 +- gdb/testsuite/gdb.btrace/next.exp | 4 +- gdb/testsuite/gdb.btrace/nexti.exp | 4 +- gdb/testsuite/gdb.btrace/nohist.exp | 2 +- gdb/testsuite/gdb.btrace/step.exp | 4 +- gdb/testsuite/gdb.btrace/stepi.exp | 4 +- 13 files changed, 306 insertions(+), 65 deletions(-) diff --git a/gdb/btrace.c b/gdb/btrace.c index efd0572..91df7a0 100644 --- a/gdb/btrace.c +++ b/gdb/btrace.c @@ -336,8 +336,9 @@ ftrace_find_call (struct btrace_function *bfun) { struct btrace_insn *last; - /* We do not allow empty function segments. */ - gdb_assert (!VEC_empty (btrace_insn_s, bfun->insn)); + /* Skip gaps. */ + if (bfun->errcode != 0) + continue; last = VEC_last (btrace_insn_s, bfun->insn); @@ -446,6 +447,32 @@ ftrace_new_switch (struct btrace_function *prev, return bfun; } +/* Add a new function segment for a gap in the trace due to a decode error. + PREV is the chronologically preceding function segment. + ERRCODE is the format-specific error code. */ + +static struct btrace_function * +ftrace_new_gap (struct btrace_function *prev, int errcode) +{ + struct btrace_function *bfun; + + /* We hijack prev if it was empty. */ + if (prev != NULL && prev->errcode == 0 + && VEC_empty (btrace_insn_s, prev->insn)) + bfun = prev; + else + bfun = ftrace_new_function (prev, NULL, NULL); + + /* This is a gap in the trace. The call stack will likely be wrong at this + point. We keep the function level, though. */ + bfun->level = prev != NULL ? prev->level : 0; + bfun->errcode = errcode; + + ftrace_debug (bfun, "new gap"); + + return bfun; +} + /* Update BFUN with respect to the instruction at PC. This may create new function segments. Return the chronologically latest function segment, never NULL. */ @@ -468,8 +495,8 @@ ftrace_update_function (struct btrace_function *bfun, CORE_ADDR pc) if (fun == NULL && mfun == NULL) DEBUG_FTRACE ("no symbol at %s", core_addr_to_string_nz (pc)); - /* If we didn't have a function before, we create one. */ - if (bfun == NULL) + /* If we didn't have a function or if we had a gap before, we create one. */ + if (bfun == NULL || bfun->errcode != 0) return ftrace_new_function (bfun, mfun, fun); /* Check the last instruction, if we have one. @@ -597,13 +624,14 @@ btrace_compute_ftrace_bts (struct thread_info *tp, struct btrace_thread_info *btinfo; struct btrace_function *begin, *end; struct gdbarch *gdbarch; - unsigned int blk; + unsigned int blk, ngaps; int level; gdbarch = target_gdbarch (); btinfo = &tp->btrace; begin = btinfo->begin; end = btinfo->end; + ngaps = btinfo->ngaps; level = begin != NULL ? -btinfo->level : INT_MAX; blk = VEC_length (btrace_block_s, btrace->blocks); @@ -628,6 +656,11 @@ btrace_compute_ftrace_bts (struct thread_info *tp, { warning (_("Recorded trace may be corrupted around %s."), core_addr_to_string_nz (pc)); + + /* Indicate the gap in the trace. */ + end = ftrace_new_gap (end, BDE_BTS_OVERFLOW); + ngaps += 1; + break; } @@ -660,6 +693,11 @@ btrace_compute_ftrace_bts (struct thread_info *tp, { warning (_("Recorded trace may be incomplete around %s."), core_addr_to_string_nz (pc)); + + /* Indicate the gap in the trace. */ + end = ftrace_new_gap (end, BDE_BTS_INSN_SIZE); + ngaps += 1; + break; } @@ -678,6 +716,7 @@ btrace_compute_ftrace_bts (struct thread_info *tp, btinfo->begin = begin; btinfo->end = end; + btinfo->ngaps = ngaps; /* LEVEL is the minimal function level of all btrace function segments. Define the global level offset to -LEVEL so all function levels are @@ -1009,6 +1048,7 @@ btrace_clear (struct thread_info *tp) btinfo->begin = NULL; btinfo->end = NULL; + btinfo->ngaps = 0; btrace_clear_history (btinfo); } @@ -1206,6 +1246,10 @@ btrace_insn_get (const struct btrace_insn_iterator *it) index = it->index; bfun = it->function; + /* Check if the iterator points to a gap in the trace. */ + if (bfun->errcode != 0) + return NULL; + /* The index is within the bounds of this function's instruction vector. */ end = VEC_length (btrace_insn_s, bfun->insn); gdb_assert (0 < end); @@ -1222,6 +1266,11 @@ btrace_insn_number (const struct btrace_insn_iterator *it) const struct btrace_function *bfun; bfun = it->function; + + /* Return zero if the iterator points to a gap in the trace. */ + if (bfun->errcode != 0) + return 0; + return bfun->insn_offset + it->index; } @@ -1257,6 +1306,7 @@ btrace_insn_end (struct btrace_insn_iterator *it, /* The last instruction in the last function is the current instruction. We point to it - it is one past the end of the execution trace. */ length = VEC_length (btrace_insn_s, bfun->insn); + gdb_assert (length > 0); it->function = bfun; it->index = length - 1; @@ -1280,6 +1330,23 @@ btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride) end = VEC_length (btrace_insn_s, bfun->insn); + /* An empty function segment represents a gap in the trace. We count + it as one instruction. */ + if (end == 0) + { + stride -= 1; + steps += 1; + + bfun = bfun->flow.next; + index = 0; + + /* There won't be a gap at the end since we will always add + an entry for the current PC. */ + gdb_assert (bfun != NULL); + + continue; + } + gdb_assert (0 < end); gdb_assert (index < end); @@ -1354,12 +1421,20 @@ btrace_insn_prev (struct btrace_insn_iterator *it, unsigned int stride) bfun = prev; index = VEC_length (btrace_insn_s, bfun->insn); - /* There is at least one instruction in this function segment. */ - gdb_assert (index > 0); + /* An empty function segment represents a gap in the trace. We count + it as one instruction. */ + if (index == 0) + { + stride -= 1; + steps += 1; + + continue; + } } /* Advance the iterator as far as possible within this segment. */ adv = min (index, stride); + stride -= adv; index -= adv; steps += adv; @@ -1386,6 +1461,37 @@ btrace_insn_cmp (const struct btrace_insn_iterator *lhs, lnum = btrace_insn_number (lhs); rnum = btrace_insn_number (rhs); + /* A gap has an instruction number of zero. Things are getting more + complicated if gaps are involved. + + We take the instruction number offset from the iterator's function. + This is the number of the first instruction after the gap. + + This is OK as long as both lhs and rhs point to gaps. If only one of + them does, we need to adjust the number based on the other's regular + instruction number. Otherwise, a gap might compare equal to an + instruction. */ + + if (lnum == 0 && rnum == 0) + { + lnum = lhs->function->insn_offset; + rnum = rhs->function->insn_offset; + } + else if (lnum == 0) + { + lnum = lhs->function->insn_offset; + + if (lnum == rnum) + lnum -= 1; + } + else if (rnum == 0) + { + rnum = rhs->function->insn_offset; + + if (rnum == lnum) + rnum -= 1; + } + return (int) (lnum - rnum); } @@ -1397,7 +1503,7 @@ btrace_find_insn_by_number (struct btrace_insn_iterator *it, unsigned int number) { const struct btrace_function *bfun; - unsigned int end; + unsigned int end, length; for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev) if (bfun->insn_offset <= number) @@ -1406,7 +1512,12 @@ btrace_find_insn_by_number (struct btrace_insn_iterator *it, if (bfun == NULL) return 0; - end = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn); + /* Since we're searching backwards from the end, we will never find gaps; + we will already stop at the function segment succeeding a gap. */ + length = VEC_length (btrace_insn_s, bfun->insn); + gdb_assert (length > 0); + + end = bfun->insn_offset + length; if (end <= number) return 0; @@ -1508,6 +1619,9 @@ btrace_call_next (struct btrace_call_iterator *it, unsigned int stride) insns = VEC_length (btrace_insn_s, bfun->insn); if (insns == 1) steps -= 1; + + /* We won't have gaps at the end. */ + gdb_assert (insns > 0); } if (stride == steps) @@ -1548,6 +1662,9 @@ btrace_call_prev (struct btrace_call_iterator *it, unsigned int stride) if (insns == 1) bfun = bfun->flow.prev; + /* We won't have gaps at the end. */ + gdb_assert (insns > 0); + if (bfun == NULL) return 0; diff --git a/gdb/btrace.h b/gdb/btrace.h index 2082bc7..71be12e 100644 --- a/gdb/btrace.h +++ b/gdb/btrace.h @@ -86,12 +86,25 @@ enum btrace_function_flag BFUN_UP_LINKS_TO_TAILCALL = (1 << 1) }; +/* Decode errors for the BTS recording format. */ +enum btrace_bts_error +{ + /* The instruction trace overflowed the end of the trace block. */ + BDE_BTS_OVERFLOW = 1, + + /* The instruction size could not be determined. */ + BDE_BTS_INSN_SIZE +}; + /* A branch trace function segment. This represents a function segment in a branch trace, i.e. a consecutive number of instructions belonging to the same function. - We do not allow function segments without any instructions. */ + In case of decode errors, we add an empty function segment to indicate + the gap in the trace. + + We do not allow function segments without instructions otherwise. */ struct btrace_function { /* The full and minimal symbol for the function. Both may be NULL. */ @@ -110,14 +123,23 @@ struct btrace_function struct btrace_function *up; /* The instructions in this function segment. - The instruction vector will never be empty. */ + The instruction vector will be empty if the function segment + represents a decode error. */ VEC (btrace_insn_s) *insn; + /* The error code of a decode error that led to a gap. + Must be zero unless INSN is empty; non-zero otherwise. */ + int errcode; + /* The instruction number offset for the first instruction in this - function segment. */ + function segment. + If INSN is empty this is the insn_offset of the succeding function + segment in control-flow order. */ unsigned int insn_offset; - /* The function number in control-flow order. */ + /* The function number in control-flow order. + If INSN is empty indicating a gap in the trace due to a decode error, + we still count the gap as a function. */ unsigned int number; /* The function level in a back trace across the entire branch trace. @@ -223,6 +245,9 @@ struct btrace_thread_info becomes zero. */ int level; + /* The number of gaps in the trace. */ + unsigned int ngaps; + /* A bit-vector of btrace_thread_flag. */ enum btrace_thread_flag flags; @@ -232,7 +257,9 @@ struct btrace_thread_info /* The function call history iterator. */ struct btrace_call_history *call_history; - /* The current replay position. NULL if not replaying. */ + /* The current replay position. NULL if not replaying. + Gaps are skipped during replay, so REPLAY always points to a valid + instruction. */ struct btrace_insn_iterator *replay; }; @@ -270,7 +297,8 @@ extern void parse_xml_btrace (struct btrace_data *data, const char *xml); extern void parse_xml_btrace_conf (struct btrace_config *conf, const char *xml); /* Dereference a branch trace instruction iterator. Return a pointer to the - instruction the iterator points to. */ + instruction the iterator points to. + May return NULL if the iterator points to a gap in the trace. */ extern const struct btrace_insn * btrace_insn_get (const struct btrace_insn_iterator *); diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c index d9c1456..3075b8e 100644 --- a/gdb/record-btrace.c +++ b/gdb/record-btrace.c @@ -350,7 +350,7 @@ record_btrace_info (struct target_ops *self) struct btrace_thread_info *btinfo; const struct btrace_config *conf; struct thread_info *tp; - unsigned int insns, calls; + unsigned int insns, calls, gaps; DEBUG ("info"); @@ -368,6 +368,7 @@ record_btrace_info (struct target_ops *self) insns = 0; calls = 0; + gaps = 0; if (!btrace_is_empty (tp)) { @@ -379,19 +380,67 @@ record_btrace_info (struct target_ops *self) calls = btrace_call_number (&call); btrace_insn_end (&insn, btinfo); - btrace_insn_prev (&insn, 1); insns = btrace_insn_number (&insn); + /* The last instruction does not really belong to the trace. */ + insns -= 1; + + gaps = btinfo->ngaps; } - printf_unfiltered (_("Recorded %u instructions in %u functions for thread " - "%d (%s).\n"), insns, calls, tp->num, - target_pid_to_str (tp->ptid)); + printf_unfiltered (_("Recorded %u instructions in %u functions (%u gaps) " + "for thread %d (%s).\n"), insns, calls, gaps, + tp->num, target_pid_to_str (tp->ptid)); if (btrace_is_replaying (tp)) printf_unfiltered (_("Replay in progress. At instruction %u.\n"), btrace_insn_number (btinfo->replay)); } +/* Print a decode error. */ + +static void +btrace_ui_out_decode_error (struct ui_out *uiout, int errcode, + enum btrace_format format) +{ + const char *errstr; + int is_error; + + errstr = _("unknown"); + is_error = 1; + + switch (format) + { + default: + break; + + case BTRACE_FORMAT_BTS: + switch (errcode) + { + default: + break; + + case BDE_BTS_OVERFLOW: + errstr = _("instruction overflow"); + break; + + case BDE_BTS_INSN_SIZE: + errstr = _("unknown instruction"); + break; + } + break; + } + + ui_out_text (uiout, _("[")); + if (is_error) + { + ui_out_text (uiout, _("decode error (")); + ui_out_field_int (uiout, "errcode", errcode); + ui_out_text (uiout, _("): ")); + } + ui_out_text (uiout, errstr); + ui_out_text (uiout, _("]\n")); +} + /* Print an unsigned int. */ static void @@ -404,6 +453,7 @@ ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val) static void btrace_insn_history (struct ui_out *uiout, + const struct btrace_thread_info *btinfo, const struct btrace_insn_iterator *begin, const struct btrace_insn_iterator *end, int flags) { @@ -421,13 +471,30 @@ btrace_insn_history (struct ui_out *uiout, insn = btrace_insn_get (&it); - /* Print the instruction index. */ - ui_out_field_uint (uiout, "index", btrace_insn_number (&it)); - ui_out_text (uiout, "\t"); + /* A NULL instruction indicates a gap in the trace. */ + if (insn == NULL) + { + const struct btrace_config *conf; + + conf = btrace_conf (btinfo); - /* Disassembly with '/m' flag may not produce the expected result. - See PR gdb/11833. */ - gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc, insn->pc + 1); + /* We have trace so we must have a configuration. */ + gdb_assert (conf != NULL); + + btrace_ui_out_decode_error (uiout, it.function->errcode, + conf->format); + } + else + { + /* Print the instruction index. */ + ui_out_field_uint (uiout, "index", btrace_insn_number (&it)); + ui_out_text (uiout, "\t"); + + /* Disassembly with '/m' flag may not produce the expected result. + See PR gdb/11833. */ + gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc, + insn->pc + 1); + } } } @@ -504,7 +571,7 @@ record_btrace_insn_history (struct target_ops *self, int size, int flags) } if (covered > 0) - btrace_insn_history (uiout, &begin, &end, flags); + btrace_insn_history (uiout, btinfo, &begin, &end, flags); else { if (size < 0) @@ -564,7 +631,7 @@ record_btrace_insn_history_range (struct target_ops *self, btrace_insn_next (&end, 1); } - btrace_insn_history (uiout, &begin, &end, flags); + btrace_insn_history (uiout, btinfo, &begin, &end, flags); btrace_set_insn_history (btinfo, &begin, &end); do_cleanups (uiout_cleanup); @@ -705,6 +772,21 @@ btrace_call_history (struct ui_out *uiout, ui_out_field_uint (uiout, "index", bfun->number); ui_out_text (uiout, "\t"); + /* Indicate gaps in the trace. */ + if (bfun->errcode != 0) + { + const struct btrace_config *conf; + + conf = btrace_conf (btinfo); + + /* We have trace so we must have a configuration. */ + gdb_assert (conf != NULL); + + btrace_ui_out_decode_error (uiout, bfun->errcode, conf->format); + + continue; + } + if ((flags & RECORD_PRINT_INDENT_CALLS) != 0) { int level = bfun->level + btinfo->level, i; @@ -1713,9 +1795,13 @@ record_btrace_step_thread (struct thread_info *tp) if (replay == NULL) return btrace_step_no_history (); - /* We are always able to step at least once. */ - steps = btrace_insn_next (replay, 1); - gdb_assert (steps == 1); + /* We are always able to step at least once - to the last instruction. + Skip gaps during replay. */ + do + { + steps = btrace_insn_next (replay, 1); + gdb_assert (steps == 1); + } while (btrace_insn_get (replay) == NULL); /* Determine the end of the instruction trace. */ btrace_insn_end (&end, btinfo); @@ -1731,10 +1817,14 @@ record_btrace_step_thread (struct thread_info *tp) if (replay == NULL) replay = record_btrace_start_replaying (tp); - /* If we can't step any further, we reached the end of the history. */ - steps = btrace_insn_prev (replay, 1); - if (steps == 0) - return btrace_step_no_history (); + /* If we can't step any further, we reached the end of the history. + Skip gaps during replay. */ + do + { + steps = btrace_insn_prev (replay, 1); + if (steps == 0) + return btrace_step_no_history (); + } while (btrace_insn_get (replay) == NULL); return btrace_step_stopped (); @@ -1753,9 +1843,15 @@ record_btrace_step_thread (struct thread_info *tp) { const struct btrace_insn *insn; - /* We are always able to step at least once. */ - steps = btrace_insn_next (replay, 1); - gdb_assert (steps == 1); + /* We are always able to step at least once - to the last instruction. + Skip gaps during replay. */ + do + { + steps = btrace_insn_next (replay, 1); + gdb_assert (steps == 1); + + insn = btrace_insn_get (replay); + } while (insn == NULL); /* We stop replaying if we reached the end of the trace. */ if (btrace_insn_cmp (replay, &end) == 0) @@ -1764,9 +1860,6 @@ record_btrace_step_thread (struct thread_info *tp) return btrace_step_no_history (); } - insn = btrace_insn_get (replay); - gdb_assert (insn); - DEBUG ("stepping %d (%s) ... %s", tp->num, target_pid_to_str (tp->ptid), core_addr_to_string_nz (insn->pc)); @@ -1787,13 +1880,16 @@ record_btrace_step_thread (struct thread_info *tp) { const struct btrace_insn *insn; - /* If we can't step any further, we're done. */ - steps = btrace_insn_prev (replay, 1); - if (steps == 0) - return btrace_step_no_history (); + /* If we can't step any further, we reached the end of the history. + Skip gaps during replay. */ + do + { + steps = btrace_insn_prev (replay, 1); + if (steps == 0) + return btrace_step_no_history (); - insn = btrace_insn_get (replay); - gdb_assert (insn); + insn = btrace_insn_get (replay); + } while (insn == NULL); DEBUG ("reverse-stepping %d (%s) ... %s", tp->num, target_pid_to_str (tp->ptid), diff --git a/gdb/testsuite/gdb.btrace/buffer-size.exp b/gdb/testsuite/gdb.btrace/buffer-size.exp index 9d36361..51d861c 100644 --- a/gdb/testsuite/gdb.btrace/buffer-size.exp +++ b/gdb/testsuite/gdb.btrace/buffer-size.exp @@ -39,7 +39,7 @@ gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: Intel\\\(R\\\) Branch Trace Store\." \ "Buffer size: 4kB\." \ - "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \ + "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \ ] "\r\n"] "info record with small bts buffer" gdb_test "record stop" ".*" "stop recording with small bts buffer" @@ -52,6 +52,6 @@ gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: Intel\\\(R\\\) Branch Trace Store\." \ "Buffer size: .*\." \ - "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \ + "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \ ] "\r\n"] "info record with unlimited bts buffer" gdb_test "record stop" ".*" "stop recording with unlimited bts buffer" diff --git a/gdb/testsuite/gdb.btrace/delta.exp b/gdb/testsuite/gdb.btrace/delta.exp index 71ccf07..d594102 100644 --- a/gdb/testsuite/gdb.btrace/delta.exp +++ b/gdb/testsuite/gdb.btrace/delta.exp @@ -40,7 +40,7 @@ with_test_prefix "no trace" { gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 0 instructions in 0 functions for .*" \ + "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for .*" \ ] "\r\n"] gdb_test "record instruction-history" "No trace\." gdb_test "record function-call-history" "No trace\." @@ -53,7 +53,7 @@ proc check_trace {} { gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 1 instructions in 1 functions for .*" \ + "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \ ] "\r\n"] gdb_test "record instruction-history /f 1" \ "1\t 0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tmov *\\\$0x0,%eax\r" @@ -74,7 +74,7 @@ gdb_test "reverse-stepi" gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 1 instructions in 1 functions for .*" \ + "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \ "Replay in progress\. At instruction 1\." \ ] "\r\n"] "reverse-stepi" @@ -83,5 +83,5 @@ gdb_test "stepi" gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 1 instructions in 1 functions for .*" \ + "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \ ] "\r\n"] "and back" diff --git a/gdb/testsuite/gdb.btrace/enable.exp b/gdb/testsuite/gdb.btrace/enable.exp index 2926c0f..37203a3 100644 --- a/gdb/testsuite/gdb.btrace/enable.exp +++ b/gdb/testsuite/gdb.btrace/enable.exp @@ -58,7 +58,7 @@ gdb_test "record full" "The process is already being recorded\\. Use \"record s # no trace recorded yet gdb_test "info record" "Active record target: record-btrace\r .*\r -Recorded 0 instructions in 0 functions for thread 1.*\\." "info record without trace" +Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for thread 1.*\\." "info record without trace" # stop btrace record gdb_test "record stop" "Process record is stopped and all execution logs are deleted\\." "record stop" diff --git a/gdb/testsuite/gdb.btrace/finish.exp b/gdb/testsuite/gdb.btrace/finish.exp index 593055b..c55c9de 100644 --- a/gdb/testsuite/gdb.btrace/finish.exp +++ b/gdb/testsuite/gdb.btrace/finish.exp @@ -38,7 +38,7 @@ proc check_replay_at { insn } { gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 40 instructions in 16 functions for .*" \ + "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \ "Replay in progress\. At instruction $insn\." \ ] "\r\n"] } diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp index 5a77357..e61a297 100644 --- a/gdb/testsuite/gdb.btrace/instruction_history.exp +++ b/gdb/testsuite/gdb.btrace/instruction_history.exp @@ -50,7 +50,7 @@ gdb_continue_to_breakpoint "cont to $bp_location" ".*$srcfile2:$bp_location.*" set traced {} set testname "determine number of recorded instructions" gdb_test_multiple "info record" $testname { - -re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions for thread 1 .*\\.\r\n$gdb_prompt $" { + -re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions \\\(0 gaps\\\) for thread 1 .*\\.\r\n$gdb_prompt $" { set traced $expect_out(1,string) pass $testname } diff --git a/gdb/testsuite/gdb.btrace/next.exp b/gdb/testsuite/gdb.btrace/next.exp index 1bc8125..7bf7cc9 100644 --- a/gdb/testsuite/gdb.btrace/next.exp +++ b/gdb/testsuite/gdb.btrace/next.exp @@ -38,7 +38,7 @@ proc check_replay_at { insn } { gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 40 instructions in 16 functions for .*" \ + "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \ "Replay in progress\. At instruction $insn\." \ ] "\r\n"] } @@ -57,7 +57,7 @@ gdb_test "next" ".*main\.3.*" gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \ + "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \ ] "\r\n"] "next back" # let's go somewhere where we can step some more diff --git a/gdb/testsuite/gdb.btrace/nexti.exp b/gdb/testsuite/gdb.btrace/nexti.exp index a263607..2cdaf73 100644 --- a/gdb/testsuite/gdb.btrace/nexti.exp +++ b/gdb/testsuite/gdb.btrace/nexti.exp @@ -38,7 +38,7 @@ proc check_replay_at { insn } { gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 40 instructions in 16 functions for .*" \ + "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \ "Replay in progress\. At instruction $insn\." \ ] "\r\n"] } @@ -57,7 +57,7 @@ gdb_test "nexti" ".*main\.3.*" "next, 1.5" gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \ + "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \ ] "\r\n"] "nexti back" # let's go somewhere where we can step some more diff --git a/gdb/testsuite/gdb.btrace/nohist.exp b/gdb/testsuite/gdb.btrace/nohist.exp index 4c1d875..6925193 100644 --- a/gdb/testsuite/gdb.btrace/nohist.exp +++ b/gdb/testsuite/gdb.btrace/nohist.exp @@ -34,7 +34,7 @@ proc check_not_replaying {} { gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \ + "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \ ] "\r\n"] } diff --git a/gdb/testsuite/gdb.btrace/step.exp b/gdb/testsuite/gdb.btrace/step.exp index 483166b..86ffa0a 100644 --- a/gdb/testsuite/gdb.btrace/step.exp +++ b/gdb/testsuite/gdb.btrace/step.exp @@ -38,7 +38,7 @@ proc check_replay_at { insn } { gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 40 instructions in 16 functions for .*" \ + "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \ "Replay in progress\. At instruction $insn\." \ ] "\r\n"] } @@ -87,5 +87,5 @@ gdb_test "step" ".*main\.3.*" gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \ + "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \ ] "\r\n"] "step to live" diff --git a/gdb/testsuite/gdb.btrace/stepi.exp b/gdb/testsuite/gdb.btrace/stepi.exp index cce41e6..5c9625c 100644 --- a/gdb/testsuite/gdb.btrace/stepi.exp +++ b/gdb/testsuite/gdb.btrace/stepi.exp @@ -36,7 +36,7 @@ proc check_replay_at { insn } { gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 40 instructions in 16 functions for .*" \ + "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \ "Replay in progress\. At instruction $insn\." \ ] "\r\n"] } @@ -61,7 +61,7 @@ gdb_test "stepi" ".*main\.3.*" gdb_test "info record" [join [list \ "Active record target: record-btrace" \ "Recording format: .*" \ - "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \ + "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \ ] "\r\n"] "stepi to live" # let's step from a goto position somewhere in the middle -- 1.8.3.1