[PATCH v10 28/28] record-btrace: add (reverse-)stepping support

Markus Metzger
2014-01-14T08:05:03Z

Provide to_resume and to_wait target methods for the btrace record target
to allow reverse stepping and replay support.

Replay is limited in the sense that only stepping and source correlation
are supported. We do not record data and thus can not show variables.

Non-stop mode is not working. Do not allow record-btrace in non-stop mode.

Reviewed-by: Eli Zaretskii

2014-01-14 Markus Metzger <markus.t.metzger@intel.com>

* btrace.h (btrace_thread_flag): New.
(struct btrace_thread_info) <flags>: New.
* record-btrace.c (record_btrace_resume_thread)
(record_btrace_find_thread_to_move, btrace_step_no_history)
(btrace_step_stopped, record_btrace_start_replaying)
(record_btrace_step_thread, record_btrace_decr_pc_after_break)
(record_btrace_find_resume_thread): New.
(record_btrace_resume, record_btrace_wait): Extend.
(record_btrace_can_execute_reverse): New.
(record_btrace_open): Fail in non-stop mode.
(record_btrace_set_replay): Split into this, ...
(record_btrace_stop_replaying): ... this, ...
(record_btrace_clear_histories): ... and this.
(init_record_btrace_ops): Init to_can_execute_reverse.
* NEWS: Announce it.

testsuite/
* gdb.btrace/delta.exp: Check reverse stepi.
* gdb.btrace/tailcall.exp: Update. Add stepping tests.
* gdb.btrace/finish.exp: New.
* gdb.btrace/next.exp: New.
* gdb.btrace/nexti.exp: New.
* gdb.btrace/record_goto.c: Add comments.
* gdb.btrace/step.exp: New.
* gdb.btrace/stepi.exp: New.
* gdb.btrace/multi-thread-step.c: New.
* gdb.btrace/multi-thread-step.exp: New.
* gdb.btrace/rn-dl-bind.c: New.
* gdb.btrace/rn-dl-bind.exp: New.
* gdb.btrace/data.c: New.
* gdb.btrace/data.exp: New.
* gdb.btrace/Makefile.in (EXECUTABLES): Add new.

doc/
* gdb.texinfo: Document limited reverse/replay support
for target record-btrace.

---
gdb/NEWS | 4 +
gdb/btrace.h | 22 ++
gdb/doc/gdb.texinfo | 6 +-
gdb/record-btrace.c | 430 +++++++++++++++++++++++--
gdb/testsuite/gdb.btrace/Makefile.in | 3 +-
gdb/testsuite/gdb.btrace/data.c | 36 +++
gdb/testsuite/gdb.btrace/data.exp | 45 +++
gdb/testsuite/gdb.btrace/delta.exp | 15 +
gdb/testsuite/gdb.btrace/finish.exp | 59 ++++
gdb/testsuite/gdb.btrace/multi-thread-step.c | 53 +++
gdb/testsuite/gdb.btrace/multi-thread-step.exp | 135 ++++++++
gdb/testsuite/gdb.btrace/next.exp | 76 +++++
gdb/testsuite/gdb.btrace/nexti.exp | 76 +++++
gdb/testsuite/gdb.btrace/record_goto.c | 36 +--
gdb/testsuite/gdb.btrace/rn-dl-bind.c | 37 +++
gdb/testsuite/gdb.btrace/rn-dl-bind.exp | 52 +++
gdb/testsuite/gdb.btrace/step.exp | 89 +++++
gdb/testsuite/gdb.btrace/stepi.exp | 93 ++++++
gdb/testsuite/gdb.btrace/tailcall.exp | 13 +
19 files changed, 1238 insertions(+), 42 deletions(-)
create mode 100644 gdb/testsuite/gdb.btrace/data.c
create mode 100644 gdb/testsuite/gdb.btrace/data.exp
create mode 100644 gdb/testsuite/gdb.btrace/finish.exp
create mode 100644 gdb/testsuite/gdb.btrace/multi-thread-step.c
create mode 100644 gdb/testsuite/gdb.btrace/multi-thread-step.exp
create mode 100644 gdb/testsuite/gdb.btrace/next.exp
create mode 100644 gdb/testsuite/gdb.btrace/nexti.exp
create mode 100644 gdb/testsuite/gdb.btrace/rn-dl-bind.c
create mode 100644 gdb/testsuite/gdb.btrace/rn-dl-bind.exp
create mode 100644 gdb/testsuite/gdb.btrace/step.exp
create mode 100644 gdb/testsuite/gdb.btrace/stepi.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 440f04b..bf205b2 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -117,6 +117,10 @@ info exceptions REGEXP
For locations inside the execution trace, the back trace is computed
based on the information stored in the execution trace.

+* The btrace record target supports limited reverse execution and replay.
+ The target does not record data and therefore does not allow reading
+ memory or registers.
+
* New options

set debug symfile off|on
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 193f916..f83a80f 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -153,6 +153,25 @@ struct btrace_call_history
struct btrace_call_iterator end;
};

+/* Branch trace thread flags. */
+enum btrace_thread_flag
+{
+ /* The thread is to be stepped forwards. */
+ BTHR_STEP = (1 << 0),
+
+ /* The thread is to be stepped backwards. */
+ BTHR_RSTEP = (1 << 1),
+
+ /* The thread is to be continued forwards. */
+ BTHR_CONT = (1 << 2),
+
+ /* The thread is to be continued backwards. */
+ BTHR_RCONT = (1 << 3),
+
+ /* The thread is to be moved. */
+ BTHR_MOVE = (BTHR_STEP | BTHR_RSTEP | BTHR_CONT | BTHR_RCONT)
+};
+
/* Branch trace information per thread.

This represents the branch trace configuration as well as the entry point
@@ -182,6 +201,9 @@ struct btrace_thread_info
becomes zero. */
int level;

+ /* A bit-vector of btrace_thread_flag. */
+ enum btrace_thread_flag flags;
+
/* The instruction history iterator. */
struct btrace_insn_history *insn_history;

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 3ee7538..5aac39b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6263,8 +6263,10 @@ replay implementation. This method allows replaying and reverse
execution.

@item btrace
-Hardware-supported instruction recording. This method does not allow
-replaying and reverse execution.
+Hardware-supported instruction recording. This method does not record
+data. Further, the data is collected in a ring buffer so old data will
+be overwritten when the buffer is full. It allows limited replay and
+reverse execution.

This recording method may not be available on all processors.
@end table
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 61caee4..67a7095 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -170,6 +170,9 @@ record_btrace_open (char *args, int from_tty)
if (!target_supports_btrace ())
error (_("Target does not support branch tracing."));

+ if (non_stop)
+ error (_("Record btrace can't debug inferior in non-stop mode."));
+
gdb_assert (record_btrace_thread_observer == NULL);

disable_chain = make_cleanup (null_cleanup, NULL);
@@ -1291,14 +1294,166 @@ const struct frame_unwind record_btrace_tailcall_frame_unwind =
record_btrace_frame_dealloc_cache
};

+/* Indicate that TP should be resumed according to FLAG. */
+
+static void
+record_btrace_resume_thread (struct thread_info *tp,
+ enum btrace_thread_flag flag)
+{
+ struct btrace_thread_info *btinfo;
+
+ DEBUG ("resuming %d (%s): %u", tp->num, target_pid_to_str (tp->ptid), flag);
+
+ btinfo = &tp->btrace;
+
+ if ((btinfo->flags & BTHR_MOVE) != 0)
+ error (_("Thread already moving."));
+
+ /* Fetch the latest branch trace. */
+ btrace_fetch (tp);
+
+ btinfo->flags |= flag;
+}
+
+/* Find the thread to resume given a PTID. */
+
+static struct thread_info *
+record_btrace_find_resume_thread (ptid_t ptid)
+{
+ struct thread_info *tp;
+
+ /* When asked to resume everything, we pick the current thread. */
+ if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
+ ptid = inferior_ptid;
+
+ return find_thread_ptid (ptid);
+}
+
+/* Start replaying a thread. */
+
+static struct btrace_insn_iterator *
+record_btrace_start_replaying (struct thread_info *tp)
+{
+ volatile struct gdb_exception except;
+ struct btrace_insn_iterator *replay;
+ struct btrace_thread_info *btinfo;
+ int executing;
+
+ btinfo = &tp->btrace;
+ replay = NULL;
+
+ /* We can't start replaying without trace. */
+ if (btinfo->begin == NULL)
+ return NULL;
+
+ /* Clear the executing flag to allow changes to the current frame.
+ We are not actually running, yet. We just started a reverse execution
+ command or a record goto command.
+ For the latter, EXECUTING is false and this has no effect.
+ For the former, EXECUTING is true and we're in to_wait, about to
+ move the thread. Since we need to recompute the stack, we temporarily
+ set EXECUTING to flase. */
+ executing = is_executing (tp->ptid);
+ set_executing (tp->ptid, 0);
+
+ /* GDB stores the current frame_id when stepping in order to detects steps
+ into subroutines.
+ Since frames are computed differently when we're replaying, we need to
+ recompute those stored frames and fix them up so we can still detect
+ subroutines after we started replaying. */
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct frame_info *frame;
+ struct frame_id frame_id;
+ int upd_step_frame_id, upd_step_stack_frame_id;
+
+ /* The current frame without replaying - computed via normal unwind. */
+ frame = get_current_frame ();
+ frame_id = get_frame_id (frame);
+
+ /* Check if we need to update any stepping-related frame id's. */
+ upd_step_frame_id = frame_id_eq (frame_id,
+ tp->control.step_frame_id);
+ upd_step_stack_frame_id = frame_id_eq (frame_id,
+ tp->control.step_stack_frame_id);
+
+ /* We start replaying at the end of the branch trace. This corresponds
+ to the current instruction. */
+ replay = xmalloc (sizeof (*replay));
+ btrace_insn_end (replay, btinfo);
+
+ /* We're not replaying, yet. */
+ gdb_assert (btinfo->replay == NULL);
+ btinfo->replay = replay;
+
+ /* Make sure we're not using any stale registers. */
+ registers_changed_ptid (tp->ptid);
+
+ /* The current frame with replaying - computed via btrace unwind. */
+ frame = get_current_frame ();
+ frame_id = get_frame_id (frame);
+
+ /* Replace stepping related frames where necessary. */
+ if (upd_step_frame_id)
+ tp->control.step_frame_id = frame_id;
+ if (upd_step_stack_frame_id)
+ tp->control.step_stack_frame_id = frame_id;
+ }
+
+ /* Restore the previous execution state. */
+ set_executing (tp->ptid, executing);
+
+ if (except.reason < 0)
+ {
+ xfree (btinfo->replay);
+ btinfo->replay = NULL;
+
+ registers_changed_ptid (tp->ptid);
+
+ throw_exception (except);
+ }
+
+ return replay;
+}
+
+/* Stop replaying a thread. */
+
+static void
+record_btrace_stop_replaying (struct thread_info *tp)
+{
+ struct btrace_thread_info *btinfo;
+
+ btinfo = &tp->btrace;
+
+ xfree (btinfo->replay);
+ btinfo->replay = NULL;
+
+ /* Make sure we're not leaving any stale registers. */
+ registers_changed_ptid (tp->ptid);
+}
+
/* The to_resume method of target record-btrace. */

static void
record_btrace_resume (struct target_ops *ops, ptid_t ptid, int step,
enum gdb_signal signal)
{
+ struct thread_info *tp, *other;
+ enum btrace_thread_flag flag;
+
+ DEBUG ("resume %s: %s", target_pid_to_str (ptid), step ? "step" : "cont");
+
+ tp = record_btrace_find_resume_thread (ptid);
+ if (tp == NULL)
+ error (_("Cannot find thread to resume."));
+
+ /* Stop replaying other threads if the thread to resume is not replaying. */
+ if (!btrace_is_replaying (tp) && execution_direction != EXEC_REVERSE)
+ ALL_THREADS (other)
+ record_btrace_stop_replaying (other);
+
/* As long as we're not replaying, just forward the request. */
- if (!record_btrace_is_replaying ())
+ if (!record_btrace_is_replaying () && execution_direction != EXEC_REVERSE)
{
for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
if (ops->to_resume != NULL)
@@ -1307,7 +1462,200 @@ record_btrace_resume (struct target_ops *ops, ptid_t ptid, int step,
error (_("Cannot find target for stepping."));
}

- error (_("You can't do this from here. Do 'record goto end', first."));
+ /* Compute the btrace thread flag for the requested move. */
+ if (step == 0)
+ flag = execution_direction == EXEC_REVERSE ? BTHR_RCONT : BTHR_CONT;
+ else
+ flag = execution_direction == EXEC_REVERSE ? BTHR_RSTEP : BTHR_STEP;
+
+ /* At the moment, we only move a single thread. We could also move
+ all threads in parallel by single-stepping each resumed thread
+ until the first runs into an event.
+ When we do that, we would want to continue all other threads.
+ For now, just resume one thread to not confuse to_wait. */
+ record_btrace_resume_thread (tp, flag);
+
+ /* We just indicate the resume intent here. The actual stepping happens in
+ record_btrace_wait below. */
+}
+
+/* Find a thread to move. */
+
+static struct thread_info *
+record_btrace_find_thread_to_move (ptid_t ptid)
+{
+ struct thread_info *tp;
+
+ /* First check the parameter thread. */
+ tp = find_thread_ptid (ptid);
+ if (tp != NULL && (tp->btrace.flags & BTHR_MOVE) != 0)
+ return tp;
+
+ /* Otherwise, find one other thread that has been resumed. */
+ ALL_THREADS (tp)
+ if ((tp->btrace.flags & BTHR_MOVE) != 0)
+ return tp;
+
+ return NULL;
+}
+
+/* Return a target_waitstatus indicating that we ran out of history. */
+
+static struct target_waitstatus
+btrace_step_no_history (void)
+{
+ struct target_waitstatus status;
+
+ status.kind = TARGET_WAITKIND_NO_HISTORY;
+
+ return status;
+}
+
+/* Return a target_waitstatus indicating that a step finished. */
+
+static struct target_waitstatus
+btrace_step_stopped (void)
+{
+ struct target_waitstatus status;
+
+ status.kind = TARGET_WAITKIND_STOPPED;
+ status.value.sig = GDB_SIGNAL_TRAP;
+
+ return status;
+}
+
+/* Clear the record histories. */
+
+static void
+record_btrace_clear_histories (struct btrace_thread_info *btinfo)
+{
+ xfree (btinfo->insn_history);
+ xfree (btinfo->call_history);
+
+ btinfo->insn_history = NULL;
+ btinfo->call_history = NULL;
+}
+
+/* Step a single thread. */
+
+static struct target_waitstatus
+record_btrace_step_thread (struct thread_info *tp)
+{
+ struct btrace_insn_iterator *replay, end;
+ struct btrace_thread_info *btinfo;
+ struct address_space *aspace;
+ struct inferior *inf;
+ enum btrace_thread_flag flags;
+ unsigned int steps;
+
+ btinfo = &tp->btrace;
+ replay = btinfo->replay;
+
+ flags = btinfo->flags & BTHR_MOVE;
+ btinfo->flags &= ~BTHR_MOVE;
+
+ DEBUG ("stepping %d (%s): %u", tp->num, target_pid_to_str (tp->ptid), flags);
+
+ switch (flags)
+ {
+ default:
+ internal_error (__FILE__, __LINE__, _("invalid stepping type."));
+
+ case BTHR_STEP:
+ /* We're done if we're not replaying. */
+ 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);
+
+ /* Determine the end of the instruction trace. */
+ btrace_insn_end (&end, btinfo);
+
+ /* We stop replaying if we reached the end of the trace. */
+ if (btrace_insn_cmp (replay, &end) == 0)
+ record_btrace_stop_replaying (tp);
+
+ return btrace_step_stopped ();
+
+ case BTHR_RSTEP:
+ /* Start replaying if we're not already doing so. */
+ 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 ();
+
+ return btrace_step_stopped ();
+
+ case BTHR_CONT:
+ /* We're done if we're not replaying. */
+ if (replay == NULL)
+ return btrace_step_no_history ();
+
+ inf = find_inferior_pid (ptid_get_pid (tp->ptid));
+ aspace = inf->aspace;
+
+ /* Determine the end of the instruction trace. */
+ btrace_insn_end (&end, btinfo);
+
+ for (;;)
+ {
+ 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 stop replaying if we reached the end of the trace. */
+ if (btrace_insn_cmp (replay, &end) == 0)
+ {
+ record_btrace_stop_replaying (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));
+
+ if (breakpoint_here_p (aspace, insn->pc))
+ return btrace_step_stopped ();
+ }
+
+ case BTHR_RCONT:
+ /* Start replaying if we're not already doing so. */
+ if (replay == NULL)
+ replay = record_btrace_start_replaying (tp);
+
+ inf = find_inferior_pid (ptid_get_pid (tp->ptid));
+ aspace = inf->aspace;
+
+ for (;;)
+ {
+ 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 ();
+
+ insn = btrace_insn_get (replay);
+ gdb_assert (insn);
+
+ DEBUG ("reverse-stepping %d (%s) ... %s", tp->num,
+ target_pid_to_str (tp->ptid),
+ core_addr_to_string_nz (insn->pc));
+
+ if (breakpoint_here_p (aspace, insn->pc))
+ return btrace_step_stopped ();
+ }
+ }
}

/* The to_wait method of target record-btrace. */
@@ -1316,8 +1664,12 @@ static ptid_t
record_btrace_wait (struct target_ops *ops, ptid_t ptid,
struct target_waitstatus *status, int options)
{
+ struct thread_info *tp, *other;
+
+ DEBUG ("wait %s (0x%x)", target_pid_to_str (ptid), options);
+
/* As long as we're not replaying, just forward the request. */
- if (!record_btrace_is_replaying ())
+ if (!record_btrace_is_replaying () && execution_direction != EXEC_REVERSE)
{
for (ops = ops->beneath; ops != NULL; ops = ops->beneath)
if (ops->to_wait != NULL)
@@ -1326,7 +1678,53 @@ record_btrace_wait (struct target_ops *ops, ptid_t ptid,
error (_("Cannot find target for waiting."));
}

- error (_("You can't do this from here. Do 'record goto end', first."));
+ /* Let's find a thread to move. */
+ tp = record_btrace_find_thread_to_move (ptid);
+ if (tp == NULL)
+ {
+ DEBUG ("wait %s: no thread", target_pid_to_str (ptid));
+
+ status->kind = TARGET_WAITKIND_IGNORE;
+ return minus_one_ptid;
+ }
+
+ /* We only move a single thread. We're not able to correlate threads. */
+ *status = record_btrace_step_thread (tp);
+
+ /* Stop all other threads. */
+ if (!non_stop)
+ ALL_THREADS (other)
+ other->btrace.flags &= ~BTHR_MOVE;
+
+ /* Start record histories anew from the current position. */
+ record_btrace_clear_histories (&tp->btrace);
+
+ /* We moved the replay position but did not update registers. */
+ registers_changed_ptid (tp->ptid);
+
+ return tp->ptid;
+}
+
+/* The to_can_execute_reverse method of target record-btrace. */
+
+static int
+record_btrace_can_execute_reverse (void)
+{
+ return 1;
+}
+
+/* The to_decr_pc_after_break method of target record-btrace. */
+
+static CORE_ADDR
+record_btrace_decr_pc_after_break (struct target_ops *ops,
+ struct gdbarch *gdbarch)
+{
+ /* When replaying, we do not actually execute the breakpoint instruction
+ so there is no need to adjust the PC after hitting a breakpoint. */
+ if (record_btrace_is_replaying ())
+ return 0;
+
+ return forward_target_decr_pc_after_break (ops->beneath, gdbarch);
}

/* The to_find_new_threads method of target record-btrace. */
@@ -1376,32 +1774,20 @@ record_btrace_set_replay (struct thread_info *tp,
btinfo = &tp->btrace;

if (it == NULL || it->function == NULL)
- {
- if (btinfo->replay == NULL)
- return;
-
- xfree (btinfo->replay);
- btinfo->replay = NULL;
- }
+ record_btrace_stop_replaying (tp);
else
{
if (btinfo->replay == NULL)
- btinfo->replay = xmalloc (sizeof (*btinfo->replay));
+ record_btrace_start_replaying (tp);
else if (btrace_insn_cmp (btinfo->replay, it) == 0)
return;

*btinfo->replay = *it;
+ registers_changed_ptid (tp->ptid);
}

- /* Clear the function call and instruction histories so we start anew
- from the new replay position. */
- xfree (btinfo->insn_history);
- xfree (btinfo->call_history);
-
- btinfo->insn_history = NULL;
- btinfo->call_history = NULL;
-
- registers_changed_ptid (tp->ptid);
+ /* Start anew from the new replay position. */
+ record_btrace_clear_histories (btinfo);
}

/* The to_goto_record_begin method of target record-btrace. */
@@ -1503,6 +1889,8 @@ init_record_btrace_ops (void)
ops->to_goto_record_begin = record_btrace_goto_begin;
ops->to_goto_record_end = record_btrace_goto_end;
ops->to_goto_record = record_btrace_goto;
+ ops->to_can_execute_reverse = record_btrace_can_execute_reverse;
+ ops->to_decr_pc_after_break = record_btrace_decr_pc_after_break;
ops->to_stratum = record_stratum;
ops->to_magic = OPS_MAGIC;
}
diff --git a/gdb/testsuite/gdb.btrace/Makefile.in b/gdb/testsuite/gdb.btrace/Makefile.in
index 2ae673a..ec00b59 100644
--- a/gdb/testsuite/gdb.btrace/Makefile.in
+++ b/gdb/testsuite/gdb.btrace/Makefile.in
@@ -2,7 +2,8 @@ VPATH = @srcdir@
srcdir = @srcdir@

EXECUTABLES = enable function_call_history instruction_history tailcall \
- exception unknown_functions record_goto delta
+ exception unknown_functions record_goto delta finish next nexti step \
+ stepi multi-thread-step rn-dl-bind data

MISCELLANEOUS =

diff --git a/gdb/testsuite/gdb.btrace/data.c b/gdb/testsuite/gdb.btrace/data.c
new file mode 100644
index 0000000..7368394
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/data.c
@@ -0,0 +1,36 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2013 Free Software Foundation, Inc.
+
+ Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+volatile static int glob;
+
+void
+test (void)
+{ /* test.1 */
+ volatile static int loc;
+
+ loc += 1; /* test.2 */
+ glob += loc; /* test.3 */
+} /* test.4 */
+
+int
+main (void)
+{ /* main.1 */
+ test (); /* main.2 */
+ return 0; /* main.3 */
+} /* main.4 */
diff --git a/gdb/testsuite/gdb.btrace/data.exp b/gdb/testsuite/gdb.btrace/data.exp
new file mode 100644
index 0000000..d6bc854
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/data.exp
@@ -0,0 +1,45 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile
+if [prepare_for_testing $testfile.exp $testfile $srcfile] {
+ return -1
+}
+if ![runto_main] {
+ return -1
+}
+
+# trace the test code
+gdb_test_no_output "record btrace"
+gdb_test "next" ".*main\.3.*"
+
+# reverse step into test
+gdb_test "reverse-step" ".*test\.4.*"
+
+# we can't read memory while we're replaying
+gdb_test "print glob" "Memory at .* unavailable\."
+gdb_test "print loc" "Memory at .* unavailable\."
+
+# stop replaying and try again
+gdb_test "record goto end"
+gdb_test "print glob" "1"
diff --git a/gdb/testsuite/gdb.btrace/delta.exp b/gdb/testsuite/gdb.btrace/delta.exp
index e606751..5c3505c 100644
--- a/gdb/testsuite/gdb.btrace/delta.exp
+++ b/gdb/testsuite/gdb.btrace/delta.exp
@@ -66,3 +66,18 @@ with_test_prefix "once" {
with_test_prefix "twice" {
check_trace
}
+
+# check that we can reverse-stepi that instruction
+gdb_test "reverse-stepi"
+gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 1 instructions in 1 functions for .*" \
+ "Replay in progress\. At instruction 1\." \
+ ] "\r\n"] "reverse-stepi"
+
+# and back
+gdb_test "stepi"
+gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 1 instructions in 1 functions for .*" \
+ ] "\r\n"] "and back"
diff --git a/gdb/testsuite/gdb.btrace/finish.exp b/gdb/testsuite/gdb.btrace/finish.exp
new file mode 100644
index 0000000..a27082e
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/finish.exp
@@ -0,0 +1,59 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing finish.exp $testfile $srcfile] {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+proc check_replay_at { insn } {
+ gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 40 instructions in 16 functions for .*" \
+ "Replay in progress\. At instruction $insn\." \
+ ] "\r\n"]
+}
+
+# let's go somewhere where we can finish
+gdb_test "record goto 32" ".*fun1\.1.*"
+with_test_prefix "at 32" { check_replay_at 32 }
+
+gdb_test "finish" ".*fun2\.3.*"
+with_test_prefix "finish into fun2" { check_replay_at 35 }
+
+gdb_test "reverse-finish" ".*fun3\.3.*"
+with_test_prefix "reverse-finish into fun3" { check_replay_at 27 }
+
+gdb_test "finish" ".*fun4\.5.*"
+with_test_prefix "finish into fun4" { check_replay_at 39 }
+
+gdb_test "reverse-finish" ".*main\.2.*"
+with_test_prefix "reverse-finish into main" { check_replay_at 1 }
diff --git a/gdb/testsuite/gdb.btrace/multi-thread-step.c b/gdb/testsuite/gdb.btrace/multi-thread-step.c
new file mode 100644
index 0000000..487565b
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/multi-thread-step.c
@@ -0,0 +1,53 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2013 Free Software Foundation, Inc.
+
+ Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <pthread.h>
+
+static pthread_barrier_t barrier;
+static int global;
+
+static void *
+test (void *arg)
+{
+ pthread_barrier_wait (&barrier);
+
+ global = 42; /* bp.1 */
+
+ pthread_barrier_wait (&barrier);
+
+ global = 42; /* bp.2 */
+
+ return arg;
+}
+
+int
+main (void)
+{
+ pthread_t th;
+
+ pthread_barrier_init (&barrier, NULL, 2);
+ pthread_create (&th, NULL, test, NULL);
+
+ test (NULL);
+
+ pthread_join (th, NULL);
+ pthread_barrier_destroy (&barrier);
+
+ return 0; /* bp.3 */
+}
diff --git a/gdb/testsuite/gdb.btrace/multi-thread-step.exp b/gdb/testsuite/gdb.btrace/multi-thread-step.exp
new file mode 100644
index 0000000..2c108b2
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/multi-thread-step.exp
@@ -0,0 +1,135 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile
+if {[gdb_compile_pthreads "$srcdir/$subdir/$srcfile" "$binfile" executable {debug}] != "" } {
+ return -1
+}
+clean_restart $testfile
+
+if ![runto_main] {
+ return -1
+}
+
+# set up breakpoints
+set bp_1 [gdb_get_line_number "bp.1" $srcfile]
+set bp_2 [gdb_get_line_number "bp.2" $srcfile]
+set bp_3 [gdb_get_line_number "bp.3" $srcfile]
+
+proc gdb_cont_to_line { line } {
+ gdb_breakpoint $line
+ gdb_continue_to_breakpoint "cont to $line" ".*$line\r\n.*"
+ delete_breakpoints
+}
+
+# trace the code between the two breakpoints
+delete_breakpoints
+gdb_cont_to_line $srcfile:$bp_1
+# make sure GDB knows about the new thread
+gdb_test "info threads" ".*"
+gdb_test_no_output "record btrace"
+gdb_cont_to_line $srcfile:$bp_2
+
+# navigate in the trace history for both threads
+with_test_prefix "navigate" {
+ gdb_test "thread 1" ".*"
+ with_test_prefix "thread 1" {
+ gdb_test "record goto begin" ".*"
+ gdb_test "info record" ".*Replay in progress\. At instruction 1\."
+ }
+ gdb_test "thread 2" ".*"
+ with_test_prefix "thread 2" {
+ gdb_test "record goto begin" ".*"
+ gdb_test "info record" ".*Replay in progress\. At instruction 1\."
+ }
+}
+
+# step both threads
+with_test_prefix "step" {
+ gdb_test "thread 1" ".*"
+ with_test_prefix "thread 1" {
+ gdb_test "info record" ".*Replay in progress\. At instruction 1\."
+ gdb_test "stepi" ".*"
+ gdb_test "info record" ".*Replay in progress\. At instruction 2\."
+ }
+ gdb_test "thread 2" ".*"
+ with_test_prefix "thread 2" {
+ gdb_test "info record" ".*Replay in progress\. At instruction 1\."
+ gdb_test "stepi" ".*"
+ gdb_test "info record" ".*Replay in progress\. At instruction 2\."
+ }
+}
+
+# run to the end of the history for both threads
+with_test_prefix "cont" {
+ gdb_test "thread 1" ".*"
+ with_test_prefix "thread 1" {
+ gdb_test "info record" ".*Replay in progress\. At instruction 2\."
+ gdb_test "continue" "No more reverse-execution history.*"
+ }
+ gdb_test "thread 2" ".*"
+ with_test_prefix "thread 2" {
+ gdb_test "info record" ".*Replay in progress\. At instruction 2\."
+ gdb_test "continue" "No more reverse-execution history.*"
+ }
+}
+
+# reverse-step both threads
+with_test_prefix "reverse-step" {
+ gdb_test "thread 1" ".*"
+ with_test_prefix "thread 1" {
+ gdb_test "reverse-stepi" ".*"
+ gdb_test "info record" ".*Replay in progress\..*"
+ }
+ gdb_test "thread 2" ".*"
+ with_test_prefix "thread 2" {
+ gdb_test "reverse-stepi" ".*"
+ gdb_test "info record" ".*Replay in progress\..*"
+ }
+}
+
+# both threads are still replaying
+with_test_prefix "check" {
+ gdb_test "thread 1" ".*"
+ with_test_prefix "thread 1" {
+ gdb_test "info record" ".*Replay in progress\..*"
+ }
+ gdb_test "thread 2" ".*"
+ with_test_prefix "thread 2" {
+ gdb_test "info record" ".*Replay in progress\..*"
+ }
+}
+
+# navigate back into the history for thread 1 and continue thread 2
+with_test_prefix "cont" {
+ gdb_test "thread 1" ".*"
+ with_test_prefix "thread 1" {
+ gdb_test "record goto begin" ".*"
+ gdb_test "info record" ".*Replay in progress\. At instruction 1\."
+ }
+ gdb_test "thread 2" ".*"
+ with_test_prefix "thread 2" {
+ gdb_test "record goto end" ".*"
+ gdb_cont_to_line $srcfile:$bp_3
+ }
+}
diff --git a/gdb/testsuite/gdb.btrace/next.exp b/gdb/testsuite/gdb.btrace/next.exp
new file mode 100644
index 0000000..b39eb79
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/next.exp
@@ -0,0 +1,76 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing next.exp $testfile $srcfile] {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+proc check_replay_at { insn } {
+ gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 40 instructions in 16 functions for .*" \
+ "Replay in progress\. At instruction $insn\." \
+ ] "\r\n"]
+}
+
+# we start with stepping to make sure that the trace is fetched automatically
+# the call is outside of our trace
+gdb_test "reverse-next" ".*main\.2.*"
+with_test_prefix "reverse-next - 1" { check_replay_at 1 }
+
+# we can't reverse-step any further
+gdb_test "reverse-next" "No more reverse-execution history\.\r\n.*main\.2.*"
+with_test_prefix "reverse-next - 2" { check_replay_at 1 }
+
+# but we can step back again
+gdb_test "next" ".*main\.3.*"
+gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+ ] "\r\n"] "next back"
+
+# let's go somewhere where we can step some more
+gdb_test "record goto 22" ".*fun3\.2.*"
+with_test_prefix "goto 22" { check_replay_at 22 }
+
+gdb_test "next" ".*fun3\.3.*"
+with_test_prefix "next to 27" { check_replay_at 27 }
+
+gdb_test "next" ".*fun3\.4.*"
+with_test_prefix "next to 37" { check_replay_at 37 }
+
+# and back again
+gdb_test "reverse-next" ".*fun3\.3.*"
+with_test_prefix "reverse-next to 27" { check_replay_at 27 }
+
+gdb_test "reverse-next" ".*fun3\.2.*"
+with_test_prefix "reverse-next to 22" { check_replay_at 22 }
diff --git a/gdb/testsuite/gdb.btrace/nexti.exp b/gdb/testsuite/gdb.btrace/nexti.exp
new file mode 100644
index 0000000..fd94df6
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/nexti.exp
@@ -0,0 +1,76 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing nexti.exp $testfile $srcfile] {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+proc check_replay_at { insn } {
+ gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 40 instructions in 16 functions for .*" \
+ "Replay in progress\. At instruction $insn\." \
+ ] "\r\n"]
+}
+
+# we start with stepping to make sure that the trace is fetched automatically
+# the call is outside of our trace
+gdb_test "reverse-nexti" ".*main\.2.*"
+with_test_prefix "reverse-nexti - 1" { check_replay_at 1 }
+
+# we can't reverse-step any further
+gdb_test "reverse-nexti" "No more reverse-execution history\.\r\n.*main\.2.*"
+with_test_prefix "reverse-nexti - 1" { check_replay_at 1 }
+
+# but we can step back again
+gdb_test "nexti" ".*main\.3.*" "next, 1.5"
+gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+ ] "\r\n"] "nexti back"
+
+# let's go somewhere where we can step some more
+gdb_test "record goto 22" ".*fun3\.2.*"
+with_test_prefix "goto 22" { check_replay_at 22 }
+
+gdb_test "nexti" ".*fun3\.3.*"
+with_test_prefix "nexti to 27" { check_replay_at 27 }
+
+gdb_test "nexti" ".*fun3\.4.*"
+with_test_prefix "nexti to 37" { check_replay_at 37 }
+
+# and back again
+gdb_test "reverse-nexti" ".*fun3\.3.*"
+with_test_prefix "reverse-nexti to 27" { check_replay_at 27 }
+
+gdb_test "reverse-nexti" ".*fun3\.2.*"
+with_test_prefix "reverse-nexti to 22" { check_replay_at 22 }
diff --git a/gdb/testsuite/gdb.btrace/record_goto.c b/gdb/testsuite/gdb.btrace/record_goto.c
index 1250708..90537f9 100644
--- a/gdb/testsuite/gdb.btrace/record_goto.c
+++ b/gdb/testsuite/gdb.btrace/record_goto.c
@@ -19,33 +19,33 @@

void
fun1 (void)
-{
-}
+{ /* fun1.1 */
+} /* fun1.2 */

void
fun2 (void)
-{
- fun1 ();
-}
+{ /* fun2.1 */
+ fun1 (); /* fun2.2 */
+} /* fun2.3 */

void
fun3 (void)
-{
- fun1 ();
- fun2 ();
-}
+{ /* fun3.1 */
+ fun1 (); /* fun3.2 */
+ fun2 (); /* fun3.3 */
+} /* fun3.4 */

void
fun4 (void)
-{
- fun1 ();
- fun2 ();
- fun3 ();
-}
+{ /* fun4.1 */
+ fun1 (); /* fun4.2 */
+ fun2 (); /* fun4.3 */
+ fun3 (); /* fun4.4 */
+} /* fun4.5 */

int
main (void)
-{
- fun4 ();
- return 0;
-}
+{ /* main.1 */
+ fun4 (); /* main.2 */
+ return 0; /* main.3 */
+} /* main.4 */
diff --git a/gdb/testsuite/gdb.btrace/rn-dl-bind.c b/gdb/testsuite/gdb.btrace/rn-dl-bind.c
new file mode 100644
index 0000000..4930297
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/rn-dl-bind.c
@@ -0,0 +1,37 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2013 Free Software Foundation, Inc.
+
+ Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+
+int test (void)
+{
+ int ret;
+
+ ret = strtoul ("42", NULL, 10); /* test.1 */
+ return ret; /* test.2 */
+} /* test.3 */
+
+int
+main (void)
+{
+ int ret;
+
+ ret = test (); /* main.1 */
+ return ret; /* main.2 */
+} /* main.3 */
diff --git a/gdb/testsuite/gdb.btrace/rn-dl-bind.exp b/gdb/testsuite/gdb.btrace/rn-dl-bind.exp
new file mode 100644
index 0000000..2f2250a
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/rn-dl-bind.exp
@@ -0,0 +1,52 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+# Test that we can reverse-next over the dynamic linker's symbol
+# lookup code.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile
+if [prepare_for_testing $testfile.exp $testfile $srcfile {c++ debug}] {
+ return -1
+}
+if ![runto_main] {
+ return -1
+}
+
+# trace the code for the call to test
+gdb_test_no_output "record btrace"
+gdb_test "next" ".*main\.2.*"
+
+# just dump the function-call-history to help debugging
+gdb_test_no_output "set record function-call-history-size 0"
+gdb_test "record function-call-history /cli 1" ".*"
+
+# check that we can reverse-next and next
+gdb_test "reverse-next" ".*main\.1.*"
+gdb_test "next" ".*main\.2.*"
+
+# now go into test and try to reverse-next and next over the library call
+gdb_test "reverse-step" ".*test\.3.*"
+gdb_test "reverse-step" ".*test\.2.*"
+gdb_test "reverse-next" ".*test\.1.*"
+gdb_test "next" ".*test\.2.*"
diff --git a/gdb/testsuite/gdb.btrace/step.exp b/gdb/testsuite/gdb.btrace/step.exp
new file mode 100644
index 0000000..7c34d25
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/step.exp
@@ -0,0 +1,89 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing step.exp $testfile $srcfile] {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+proc check_replay_at { insn } {
+ gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 40 instructions in 16 functions for .*" \
+ "Replay in progress\. At instruction $insn\." \
+ ] "\r\n"]
+}
+
+# let's start by stepping back into the function we just returned from
+gdb_test "reverse-step" ".*fun4\.5.*"
+with_test_prefix "reverse-step to 39" { check_replay_at 39 }
+
+# again
+gdb_test "reverse-step" ".*fun3\.4.*"
+with_test_prefix "reverse-step to 37" { check_replay_at 37 }
+
+# and again
+gdb_test "reverse-step" ".*fun2\.3.*"
+with_test_prefix "reverse-step to 35" { check_replay_at 35 }
+
+# once more
+gdb_test "reverse-step" ".*fun1\.2.*"
+with_test_prefix "reverse-step to 33" { check_replay_at 33 }
+
+# and out again the other side
+gdb_test "reverse-step" ".*fun2\.2.*"
+with_test_prefix "reverse-step to 30" { check_replay_at 30 }
+
+# once again
+gdb_test "reverse-step" ".*fun3\.3.*"
+with_test_prefix "reverse-step to 27" { check_replay_at 27 }
+
+# and back the way we came
+gdb_test "step" ".*fun2\.2.*"
+with_test_prefix "step to 30" { check_replay_at 30 }
+
+gdb_test "step" ".*fun1\.2.*"
+with_test_prefix "step to 33" { check_replay_at 33 }
+
+gdb_test "step" ".*fun2\.3.*"
+with_test_prefix "step to 35" { check_replay_at 35 }
+
+gdb_test "step" ".*fun3\.4.*"
+with_test_prefix "step to 37" { check_replay_at 37 }
+
+gdb_test "step" ".*fun4\.5.*"
+with_test_prefix "step to 39" { check_replay_at 39 }
+
+gdb_test "step" ".*main\.3.*"
+gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+ ] "\r\n"] "step to live"
diff --git a/gdb/testsuite/gdb.btrace/stepi.exp b/gdb/testsuite/gdb.btrace/stepi.exp
new file mode 100644
index 0000000..fb12e28
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/stepi.exp
@@ -0,0 +1,93 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <markus.t.metzger@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# check for btrace support
+if { [skip_btrace_tests] } { return -1 }
+
+# start inferior
+standard_testfile x86-record_goto.S
+if [prepare_for_testing stepi.exp $testfile $srcfile] {
+ return -1
+}
+
+global gdb_prompt
+
+if ![runto_main] {
+ return -1
+}
+
+proc check_replay_at { insn } {
+ gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 40 instructions in 16 functions for .*" \
+ "Replay in progress\. At instruction $insn\." \
+ ] "\r\n"]
+}
+
+# trace the call to the test function
+gdb_test_no_output "record btrace"
+gdb_test "next"
+
+# we start with stepping to make sure that the trace is fetched automatically
+gdb_test "reverse-stepi" ".*fun4\.5.*"
+gdb_test "reverse-stepi" ".*fun4\.5.*"
+
+# let's check where we are in the trace
+with_test_prefix "reverse-stepi to 39" { check_replay_at 39 }
+
+# let's step forward and check again
+gdb_test "stepi" ".*fun4\.5.*"
+with_test_prefix "stepi to 40" { check_replay_at 40 }
+
+# with the next step, we stop replaying
+gdb_test "stepi" ".*main\.3.*"
+gdb_test "info record" [join [list \
+ "Active record target: record-btrace" \
+ "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+ ] "\r\n"] "stepi to live"
+
+# let's step from a goto position somewhere in the middle
+gdb_test "record goto 22" ".*fun3\.2.*"
+with_test_prefix "goto 22" { check_replay_at 22 }
+
+gdb_test "stepi" ".*fun1\.1.*"
+with_test_prefix "stepi to 23" { check_replay_at 23 }
+
+# and back again
+gdb_test "reverse-stepi" ".*fun3\.2.*"
+gdb_test "reverse-stepi" ".*fun3\.1.*"
+with_test_prefix "reverse-stepi to 21" { check_replay_at 21 }
+
+# let's try to step off the left end
+gdb_test "record goto begin" ".*main\.2.*"
+with_test_prefix "goto begin" { check_replay_at 1 }
+
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r\n.*main\.2.*"
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r\n.*main\.2.*"
+with_test_prefix "reverse-stepi at begin" { check_replay_at 1 }
+
+# we can step forward, though
+gdb_test "stepi" ".*fun4\.1.*"
+with_test_prefix "stepi to 2" { check_replay_at 2 }
+
+# let's try to step off the left end again
+gdb_test "reverse-stepi" ".*main\.2.*"
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r\n.*main\.2.*"
+gdb_test "reverse-stepi" "No more reverse-execution history\.\r\n.*main\.2.*"
+with_test_prefix "reverse-stepi at begin" { check_replay_at 1 }
diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
index 23988f2..ce1f053 100644
--- a/gdb/testsuite/gdb.btrace/tailcall.exp
+++ b/gdb/testsuite/gdb.btrace/tailcall.exp
@@ -77,3 +77,16 @@ gdb_test "backtrace" [join [list \
# walk the backtrace
gdb_test "up" "#1\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*" "up to foo"
gdb_test "up" "#2\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*" "up to main"
+gdb_test "down" "#1\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*" "down to foo"
+
+# test stepping into and out of tailcalls.
+gdb_test "finish" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*"
+gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at x86-tailcall.c:24\r\n.*"
+gdb_test "reverse-finish" "\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*"
+gdb_test "reverse-step" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*"
+gdb_test "next" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:39\r\n.*"
+gdb_test "reverse-next" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*"
+gdb_test "step" "\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*"
+gdb_test "finish" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*"
+gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at x86-tailcall.c:24\r\n.*"
+gdb_test "finish" "\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*"
--
1.8.3.1