record-btrace: add (reverse-)stepping support

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.

2014-01-16  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.
This commit is contained in:
Markus Metzger 2013-05-06 16:04:46 +02:00
parent 118e6252ca
commit 52834460bc
22 changed files with 1279 additions and 42 deletions

View File

@ -1,3 +1,21 @@
2014-01-16 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.
2014-01-16 Markus Metzger <markus.t.metzger@intel.com>
* target.h (struct target_ops) <to_decr_pc_after_break>: New.

View File

@ -22,6 +22,10 @@
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 remote packets
qXfer:btrace:read's annex

View File

@ -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;

View File

@ -1,3 +1,8 @@
2014-01-16 Markus Metzger <markus.t.metzger@intel.com>
* gdb.texinfo: Document limited reverse/replay support
for target record-btrace.
2014-01-16 Markus Metzger <markus.t.metzger@intel.com>
* gdb.texinfo (Process Record and Replay): Update documentation.

View File

@ -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

View File

@ -169,6 +169,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);
@ -1290,14 +1293,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)
@ -1306,7 +1461,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. */
@ -1315,8 +1663,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)
@ -1325,7 +1677,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. */
@ -1375,32 +1773,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. */
@ -1502,6 +1888,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;
}

View File

@ -1,3 +1,21 @@
2014-01-16 Markus Metzger <markus.t.metzger@intel.com>
* 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.
2014-01-16 Markus Metzger <markus.t.metzger@intel.com>
* gdb.btrace/Makefile.in (EXECUTABLES): Add delta.

View File

@ -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 =

View File

@ -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 */

View File

@ -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"

View File

@ -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"

View File

@ -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 }

View File

@ -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 */
}

View File

@ -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
}
}

View File

@ -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 }

View File

@ -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 }

View File

@ -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 */

View File

@ -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 */

View File

@ -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.*"

View File

@ -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"

View File

@ -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 }

View File

@ -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.*"