Compare commits

...

8 Commits

Author SHA1 Message Date
Markus Metzger
31d025c07d btrace: record stop moves to the end of the trace
With the btrace record target the "record stop" command implicitly moves all
traced threads to the end of their respective history.  Unlike record full,
record btrace does not trace data and is therefore not able to resume debugging
at the current replay position.

We forgot to actually change the replay position before disabling tracing.  This
resulted in STOP_PC remaining at its old position if the current thread had been
replaying, which, in turn, resulted in GDB commands such as list or backtrace
using the wrong context.

Fix it by moving the selected thread to the end of its recorded history.
Together with the preceding patches, this will result in a front-end
notification for the selected thread if it had been replaying.  Stop replaying
other threads silently, i.e. without a front-end notification.

If the selected thread isn't replaying, notify front-ends without printing the
(unchanged) frame.  This results in a *stopped MI notification without any
thread information.

In non-stop mode, move all replaying threads to the end of their respective
histories.  This will result in a front-end notification and in the updated
location to be printed for each replaying thread.  We prefix the output with
the thread number like this:

    (gdb) record stop
    Thread 1 (Thread 0x7ffff7fcc740 (LWP 66711)) stopped replaying.
    test (arg=0x0) at gdb.btrace/multi-thread-step.c:34
    34        global = 42; /* bp.2 */
    Thread 2 (Thread 0x7ffff74fb700 (LWP 66716)) stopped replaying.
    test (arg=0x0) at gdb.btrace/multi-thread-step.c:34
    34        global = 42; /* bp.2 */
    Process record is stopped and all execution logs are deleted.

Thanks to Marc Khouzam <marc.khouzam@ericsson.com> for reporting this.

Signed-off-by: Markus Metzger  <markus.t.metzger@intel.com>

gdb/
	* record-btrace.c (record_btrace_set_replay)
	(record_btrace_stop_replaying): New declaration.
	(record_btrace_stop_recording): Call record_btrace_set_replay,
	record_btrace_stop_replaying, and observer_notify_normal_stop.

testsuite/
	* gdb.btrace/non-stop.exp: Test "record stop".
	* gdb.btrace/non-stop.c (test): Add statement to break at.
	* gdb.btrace/stop.exp: New.

Change-Id: I10565a8e4f8bc3c63f79c3ef6595e9f84e3d8100
2016-07-06 08:31:22 +02:00
Markus Metzger
8c62f01902 btrace-btrace: signal record-goto stop
When changing a thread's replay position, call record_signal_goto_stop instead
of printing the source location directly.  This will signal the stop to
front-ends and have them print the source location.

We update the STOP_PC if the change affects the selected thread.

Signed-off-by: Markus Metzger  <markus.t.metzger@intel.com>

gdb/
	* record-btrace.c (record_btrace_set_replay): Check ptid before updating
	STOP_PC.  Call record_signal_goto_stop.

Change-Id: If2cb2b9572396a2e5475d6611c7e9f79547c6f61
2016-07-06 08:31:22 +02:00
Markus Metzger
8e1542c8aa record-full: signal record-goto stop
When changing the replay position call record_signal_goto_stop instead of
printing the new source location directly.  This will signal the stop to
front-ends and have them print the source location.

Signed-off-by: Markus Metzger  <markus.t.metzger@intel.com>

gdb/
	* record-full.c (record_full_goto_entry): Call record_signal_goto_stop.

testsuite/
	* gdb.mi/mi-reverse.exp: Test record goto begin/end.

Change-Id: Ibae2736eb0cd3c70ba7d99d18836575eb0d23f33
2016-07-06 08:31:22 +02:00
Markus Metzger
8cca3e100f mi, testsuite: add another EXTRA option to mi_expect_stop
Allow the EXTRA argument of mi_expect_stop to contain a third pattern that must
occur directly before the GDB prompt at the end of the output.

Signed-off-by: Markus Metzger  <markus.t.metzger@intel.com>

testsuite/
	* lib/mi-support.exp (mi_expect_stop): Add third EXTRA field.

Change-Id: I7b492b72619265ea81200935adf22d94bb086806
2016-07-06 08:31:21 +02:00
Markus Metzger
41af4a217b record: signal a record goto stop to front-ends
The "record goto" command does not indicate the stop to front-ends.  Instead,
it prints the new location directly.

Add a function to signal a normal stop to observers and have them print the new
location.  This function temporarily switches to the stopped thread.

We use the TARGET_WAITKIND_NO_RESUMED wait status for this purpose.  This should
result in a stop notification without giving a stop reason.  We could also
invent a new wait status but this doesn't seem necessary at this point.

Signed-off-by: Markus Metzger  <markus.t.metzger@intel.com>

gdb/
	* record.h (record_signal_goto_stop): New.
	* record.c (record_signal_goto_stop): New.

Change-Id: I0b196be68779f9e81abca78df5bc39e917023581
2016-07-06 08:31:21 +02:00
Markus Metzger
037ca1addd infrun: export clear_proceed_status_thread
Signed-off-by: Markus Metzger  <markus.t.metzger@intel.com>

gdb/
	* infrun.h (clear_proceed_status_thread): New.
	* infrun.c (clear_proceed_status_thread): Export.

Change-Id: I23e762997359b9769856450e89626707bad1365e
2016-07-06 08:31:21 +02:00
Markus Metzger
b323ffa153 record: do not allow record goto on a running thread
We can't start replaying if the selected thread is currently running.  Throw an
error in this case.

Signed-off-by: Markus Metzger  <markus.t.metzger@intel.com>

gdb/
	* record.c: Include gdbthread.h
	(require_not_running): New.
	(record_goto, cmd_record_goto_begin, cmd_record_goto_end): Call
	require_not_running.

Change-Id: I15888d668b6011217337cf3a63d3618fb044c023
2016-07-06 08:31:20 +02:00
Markus Metzger
e0139aa5f2 btrace: check if we're replaying when setting the replay position to the end
When setting the replay position to the end there is nothing to do if we are
not replaying.  Check that and return immediately.

This avoids printing the current location if we're already at the end.

Signed-off-by: Markus Metzger  <markus.t.metzger@intel.com>

gdb/
	* record-btrace.c (record_btrace_set_replay): Check if replaying.

testsuite/
	* gdb.btrace/record_goto.exp: Test "record goto end" twice.

Change-Id: I4878892408bab293261bbea40d6af28440ff3ec5
2016-07-06 08:31:20 +02:00
12 changed files with 233 additions and 22 deletions

View File

@@ -2785,10 +2785,9 @@ new_stop_id (void)
current_stop_id++;
}
/* Clear out all variables saying what to do when inferior is continued.
First do this, then set the ones you want, then call `proceed'. */
/* See infrun.h. */
static void
void
clear_proceed_status_thread (struct thread_info *tp)
{
if (debug_infrun)

View File

@@ -80,6 +80,9 @@ extern void start_remote (int from_tty);
step/stepi command. */
extern void clear_proceed_status (int step);
/* Clear out the proceed status of TP. */
extern void clear_proceed_status_thread (struct thread_info *tp);
extern void proceed (CORE_ADDR, enum gdb_signal);
/* The `resume' routine should only be called in special circumstances.

View File

@@ -97,6 +97,11 @@ static struct cmd_list_element *show_record_btrace_pt_cmdlist;
} \
while (0)
static void record_btrace_set_replay (struct thread_info *tp,
const struct btrace_insn_iterator *it);
static void record_btrace_stop_replaying (struct thread_info *tp);
/* Update the branch trace for the current thread and return a pointer to its
thread_info.
@@ -252,9 +257,57 @@ record_btrace_stop_recording (struct target_ops *self)
record_btrace_auto_disable ();
ALL_NON_EXITED_THREADS (tp)
if (tp->btrace.target != NULL)
btrace_disable (tp);
/* In non-stop mode, we indicate the implicit move of each replaying thread.
In stop-all mode, we indicate the implicit move of the selected thread if
it is replaying. Other threads are silently moved. The MI notification
will indicate that all threads have been stopped which should be enough to
indicate this implicit move to front-ends. */
if (non_stop)
{
ALL_NON_EXITED_THREADS (tp)
if (tp->btrace.target != NULL)
{
if (btrace_is_replaying (tp))
{
printf_filtered (_("Thread %s (%s) stopped replaying.\n"),
print_thread_id (tp),
target_pid_to_str (tp->ptid));
record_btrace_set_replay (tp, NULL);
}
btrace_disable (tp);
}
}
else
{
int send_stopped = 0;
/* If the selected thread is replaying, we do an implicit "record goto
end" to make it stop replaying and indicate this to front-ends. This
causes the updated location to be printed for the selected thread.
If it isn't replaying, we will send an unspecific stopped notification
to front-ends at the end. This doesn't print the (unchanged) location
but indicates to front-ends that other thread's locations may have
changed. */
tp = inferior_thread ();
if (tp != NULL && btrace_is_replaying (tp))
record_btrace_set_replay (tp, NULL);
else
send_stopped = 1;
ALL_NON_EXITED_THREADS (tp)
if (tp->btrace.target != NULL)
{
/* Stop replaying before we disable tracing to clear TP's register
state in addition to the btrace state. */
record_btrace_stop_replaying (tp);
btrace_disable (tp);
}
if (send_stopped)
observer_notify_normal_stop (NULL, 0);
}
}
/* The to_close method of target record-btrace. */
@@ -2704,7 +2757,12 @@ record_btrace_set_replay (struct thread_info *tp,
btinfo = &tp->btrace;
if (it == NULL || it->function == NULL)
record_btrace_stop_replaying (tp);
{
if (!btrace_is_replaying (tp))
return;
record_btrace_stop_replaying (tp);
}
else
{
if (btinfo->replay == NULL)
@@ -2719,8 +2777,12 @@ record_btrace_set_replay (struct thread_info *tp,
/* Start anew from the new replay position. */
record_btrace_clear_histories (btinfo);
stop_pc = regcache_read_pc (get_current_regcache ());
print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
/* We changed the PC of TP. Update the global state if TP is the selected
thread. */
if (ptid_equal (tp->ptid, inferior_ptid))
stop_pc = regcache_read_pc (get_current_regcache ());
record_signal_goto_stop (tp);
}
/* The to_goto_record_begin method of target record-btrace. */

View File

@@ -1905,7 +1905,7 @@ record_full_goto_entry (struct record_full_entry *p)
registers_changed ();
reinit_frame_cache ();
stop_pc = regcache_read_pc (get_current_regcache ());
print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
record_signal_goto_stop (inferior_thread ());
}
/* The "to_goto_record_begin" target method. */

View File

@@ -26,6 +26,7 @@
#include "common/common-utils.h"
#include "cli/cli-utils.h"
#include "disasm.h"
#include "gdbthread.h"
#include <ctype.h>
@@ -80,6 +81,16 @@ require_record_target (void)
return t;
}
/* Check that the inferior thread is not running. Throw an error if it is. */
static void
require_not_running (void)
{
if (is_running (inferior_ptid))
error (_("Cannot execute this command while "
"the selected thread is running."));
}
/* See record.h. */
void
@@ -331,6 +342,27 @@ cmd_record_save (char *args, int from_tty)
/* See record.h. */
void
record_signal_goto_stop (struct thread_info *tp)
{
struct target_waitstatus ws;
struct cleanup *cleanup;
clear_proceed_status_thread (tp);
cleanup = make_cleanup_restore_current_thread ();
switch_to_thread (tp->ptid);
ws.kind = TARGET_WAITKIND_NO_RESUMED;
set_last_target_status (tp->ptid, ws);
observer_notify_normal_stop (NULL, 1);
do_cleanups (cleanup);
}
/* See record.h. */
void
record_goto (const char *arg)
{
@@ -342,6 +374,7 @@ record_goto (const char *arg)
insn = parse_and_eval_long (arg);
require_record_target ();
require_not_running ();
target_goto_record (insn);
}
@@ -365,6 +398,7 @@ cmd_record_goto_begin (char *arg, int from_tty)
error (_("Junk after argument: %s."), arg);
require_record_target ();
require_not_running ();
target_goto_record_begin ();
}
@@ -377,6 +411,7 @@ cmd_record_goto_end (char *arg, int from_tty)
error (_("Junk after argument: %s."), arg);
require_record_target ();
require_not_running ();
target_goto_record_end ();
}

View File

@@ -91,4 +91,7 @@ extern struct target_ops *find_record_target (void);
it does anything. */
extern void record_preopen (void);
/* Signal a record-goto stop of TP to front-ends. */
extern void record_signal_goto_stop (struct thread_info *tp);
#endif /* _RECORD_H_ */

View File

@@ -27,7 +27,9 @@ test (void *arg)
i = 0; /* bp.1 */
for (; i < 10; ++i) global += i; /* loop */
return arg; /* bp.2 */
global *= 2; /* bp.2 */
return arg; /* bp.3 */
}
int
@@ -41,5 +43,5 @@ main (void)
pthread_join (th, NULL);
return 0; /* bp.3 */
return 0;
}

View File

@@ -239,7 +239,22 @@ with_test_prefix "no progress" {
}
# now that both threads stopped replaying we may resume recording
with_test_prefix "cont to end" {
with_test_prefix "resume recording" {
gdb_breakpoint $bp_3
gdb_cont_to_bp_line "$srcfile:$bp_3" all 1
gdb_cont_to_bp_line "$srcfile:$bp_3" all 2
}
# when we stop recording we get notifications for replaying threads
with_test_prefix "stop" {
gdb_test "thread 1" ".*"
gdb_test "thread apply 2 record goto begin" ".*"
gdb_test "record stop" [multi_line \
"Thread 2 \[^\\\r\\\n\]* stopped replaying\." \
"\[^\\\r\\\n\]*$srcfile:$bp_3" \
"$bp_3\[^\\\r\\\n\]* bp\.3 \[^\\\r\\\n\]*" \
"Process record is stopped and all execution logs are deleted\." \
]
gdb_test "info record" "No record target is currently active\."
}

View File

@@ -160,6 +160,9 @@ gdb_test "record instruction-history -" [multi_line \
# check that we can go to the end of the trace
gdb_test "record goto end" ".*main \\(\\) at record_goto.c:50.*"
# check that we don't repeat the current location if we go to the end again
gdb_test_no_output "record goto end" "goto end again"
# check that we're filling up the context correctly
gdb_test "record function-call-history /ci" [multi_line \
"14\t fun2\tinst 35,36" \

View File

@@ -0,0 +1,70 @@
# This testcase is part of GDB, the GNU debugger.
#
# Copyright 2016 Free Software Foundation, Inc.
#
# 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 }
standard_testfile record_goto.c
# start inferior
if [prepare_for_testing $testfile.exp $testfile $srcfile] {
return -1
}
# we use the list command to check the current source location
gdb_test "set listsize 1"
if ![runto_main] {
return -1
}
# trace the call to the test function
gdb_test_no_output "record btrace" "move: enable"
gdb_test "next" ".*main\.3.*" "move: trace"
# move to the beginning of the trace
gdb_test "record goto begin" ".*main\.2.*" "move: navigate"
# when we stop recording, we move back to the end of the trace
gdb_test "record stop" "main\.3.*Process record is stopped.*" "move: stop"
# check that we're really there
gdb_test "list" ".*main\.3.*" "move: at end of trace"
if ![runto_main] {
return -1
}
# trace the call to the test function
gdb_test_no_output "record btrace" "already: enable"
gdb_test "next" ".*main\.3.*" "already: trace"
# we're already at the end so we didn't have to move
gdb_test_multiple "record stop" "already: stop" {
-re "main.*$gdb_prompt $" {
fail "already: stop"
}
-re "Process record is stopped\[^\\\r\\\n\]*\r\n$gdb_prompt $" {
pass "already: stop"
}
-re "$gdb_prompt $" {
fail "already: stop"
}
}
# check that we're really there
gdb_test "list" ".*main\.3.*" "already: at end of trace"

View File

@@ -93,6 +93,7 @@ proc test_controlled_execution_reverse {} {
global line_callee1_head line_callee1_body line_callee1_close
global line_main_head line_main_body
global line_main_hello line_main_callme_1
global line_callme_body
# Test exec-reverse-finish
@@ -166,6 +167,14 @@ proc test_controlled_execution_reverse {} {
"" "main" "" \
"basics.c" $line_main_body "" \
"reverse-continue at main"
send_gdb "record goto end\n"
mi_expect_stop "" "callme" "\{name=\"i\",value=\"1\"\}" "basics.c" \
$line_callme_body { "" "" "\\^done\r\n" } "record goto end"
send_gdb "record goto begin\n"
mi_expect_stop "" "main" "" "basics.c" $line_main_body \
{ "" "" "\\^done\r\n" } "record goto begin"
}
test_controlled_execution_reverse

View File

@@ -1127,6 +1127,8 @@ proc mi_detect_async {} {
# output right after *stopped, and the second element is output
# right after reason field. The regex after reason should not include
# the comma separating it from the following fields.
# If EXTRA is a list of three elements, the first two are for the above
# and the third element is for output right before GDB prompt.
#
# When we fail to match output at all, -1 is returned. If FILE does
# match and the target system has no debug info for FILE return 0.
@@ -1150,7 +1152,15 @@ proc mi_expect_stop { reason func args file line extra test } {
set after_stopped ""
set after_reason ""
if { [llength $extra] == 2 } {
set before_prompt ""
if { [llength $extra] == 3 } {
set after_stopped [lindex $extra 0]
set after_reason [lindex $extra 1]
if { $after_reason != "" } {
set after_reason "${after_reason},"
}
set before_prompt [lindex $extra 2]
} elseif { [llength $extra] == 2 } {
set after_stopped [lindex $extra 0]
set after_reason [lindex $extra 1]
set after_reason "${after_reason},"
@@ -1166,7 +1176,7 @@ proc mi_expect_stop { reason func args file line extra test } {
if { $reason == "really-no-reason" } {
gdb_expect {
-re "\\*stopped\r\n$prompt_re" {
-re "\\*stopped\r\n$before_prompt$prompt_re" {
pass "$test"
}
timeout {
@@ -1179,7 +1189,7 @@ proc mi_expect_stop { reason func args file line extra test } {
if { $reason == "exited-normally" } {
gdb_expect {
-re "\\*stopped,reason=\"exited-normally\"\r\n$prompt_re" {
-re "\\*stopped,reason=\"exited-normally\"\r\n$before_prompt$prompt_re" {
pass "$test"
}
-re ".*$mi_gdb_prompt$" {fail "continue to end (2)"}
@@ -1191,7 +1201,7 @@ proc mi_expect_stop { reason func args file line extra test } {
}
if { $reason == "exited" } {
gdb_expect {
-re "\\*stopped,reason=\"exited\",exit-code=\"\[0-7\]+\"\r\n$prompt_re" {
-re "\\*stopped,reason=\"exited\",exit-code=\"\[0-7\]+\"\r\n$before_prompt$prompt_re" {
pass "$test"
}
-re ".*$mi_gdb_prompt$" {
@@ -1205,7 +1215,7 @@ proc mi_expect_stop { reason func args file line extra test } {
}
if { $reason == "solib-event" } {
set pattern "\\*stopped,reason=\"solib-event\",thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re"
set pattern "\\*stopped,reason=\"solib-event\",thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$before_prompt$prompt_re"
verbose -log "mi_expect_stop: expecting: $pattern"
gdb_expect {
-re "$pattern" {
@@ -1235,9 +1245,9 @@ proc mi_expect_stop { reason func args file line extra test } {
set a $after_reason
verbose -log "mi_expect_stop: expecting: \\*stopped,${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$func\",args=$args,(?:file=\"$any$file\",fullname=\"${fullname_syntax}$file\",line=\"$line\"|from=\"$file\")\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re"
verbose -log "mi_expect_stop: expecting: \\*stopped,${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$func\",args=$args,(?:file=\"$any$file\",fullname=\"${fullname_syntax}$file\",line=\"$line\"|from=\"$file\")\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$before_prompt$prompt_re"
gdb_expect {
-re "\\*stopped,${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$func\",args=$args,(?:file=\"$any$file\",fullname=\"${fullname_syntax}$file\",line=\"($line)\"|from=\"$file\")\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re" {
-re "\\*stopped,${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$func\",args=$args,(?:file=\"$any$file\",fullname=\"${fullname_syntax}$file\",line=\"($line)\"|from=\"$file\")\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$before_prompt$prompt_re" {
pass "$test"
if {[array names expect_out "2,string"] != ""} {
return $expect_out(2,string)
@@ -1245,7 +1255,7 @@ proc mi_expect_stop { reason func args file line extra test } {
# No debug info available but $file does match.
return 0
}
-re "\\*stopped,${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$any\",args=\[\\\[\{\]$any\[\\\]\}\],file=\"$any\",fullname=\"${fullname_syntax}$any\",line=\"\[0-9\]*\"\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re" {
-re "\\*stopped,${r}${a}${bn}frame=\{addr=\"$hex\",func=\"$any\",args=\[\\\[\{\]$any\[\\\]\}\],file=\"$any\",fullname=\"${fullname_syntax}$any\",line=\"\[0-9\]*\"\}$after_stopped,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$before_prompt$prompt_re" {
verbose -log "got $expect_out(buffer)"
fail "$test (stopped at wrong place)"
return -1