This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PR11557: Stale register and frame info in non-stop/async modes, just after resuming a thread


I've applied this patch to fix PR 11557.  It also adds a new
test.

In non-stop/async modes, just after a thread is resumed GDB still
shows the thread's register state as if it was still stopped were it
was before you resumed it.  The bug is present in 7.0, 7.1, and
mainline.  Vis, with GDB 7.0:

 $gdb-7.0 -q -ex "set target-async 1" -ex "set non-stop 1" -ex "set pagination
off" stale-regcache
 Reading symbols from stale-regcache...done.
 (gdb) start
 Temporary breakpoint 1 at 0x400563: file ns-stale-regcache.c, line 24.
 Starting program: stale-regcache

 Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe0b8) at stale-regcache.c:24
 24        volatile int my_number = 1;
 (gdb) p $pc
 $1 = (void (*)()) 0x400563 <main+15>

This is where it gets interesting:

 (gdb) c&
 Continuing.
 (gdb) info threads
 * 1 process 11017  (running)
 (gdb) p $pc
 $2 = (void (*)()) 0x400563 <main+15>
 (gdb) frame
# 0  main (argc=Cannot access memory at address 0x7fffffffdfbc) at
stale-regcache.c:24
 24        volatile int my_number = 1;

That PC and frame is of course, stale and bogus.  Notice how we failed
to read ARGC (because the thread is actually running).

We need to get rid of the thread's register cache just
after resuming it.

This was never an issue with non async debugging, because we
can't issue commands while threads were running in that mode,
and also not an issue with the old (pre 7.0) async support, which
only supported -exec-interrupt while threads were running,
nothing else.

-- 
Pedro Alves

2010-04-29  Pedro Alves  <pedro@codesourcery.com>

	PR gdb/11557

	gdb/
	* regcache.c (registers_changed): Rename to ...
	(registers_changed_ptid): ... this, and only delete register cache
	entries matching the ptid filter argument.
	(registers_changed): Reimplement on top of registers_changed_ptid.
	* regcache.h (registers_changed_ptid): Declare.
	* target.c (target_resume): Flush register caches.

	gdb/testsuite/
	* gdb.mi/mi-ns-stale-regcache.exp, gdb.mi/ns-stale-regcache.c: New
	files.

---
 gdb/regcache.c |   29 +++++++++++++++++++++++------
 gdb/regcache.h |    1 +
 gdb/target.c   |    1 +
 3 files changed, 25 insertions(+), 6 deletions(-)

Index: src/gdb/regcache.c
===================================================================
--- src.orig/gdb/regcache.c	2010-04-29 15:04:42.000000000 +0100
+++ src/gdb/regcache.c	2010-04-29 15:48:06.000000000 +0100
@@ -518,15 +518,27 @@ regcache_thread_ptid_changed (ptid_t old
    Indicate that registers may have changed, so invalidate the cache.  */
 
 void
-registers_changed (void)
+registers_changed_ptid (ptid_t ptid)
 {
-  struct regcache_list *list, *next;
+  struct regcache_list *list, **list_link;
 
-  for (list = current_regcache; list; list = next)
+  list = current_regcache;
+  list_link = &current_regcache;
+  while (list)
     {
-      next = list->next;
-      regcache_xfree (list->regcache);
-      xfree (list);
+      if (ptid_match (list->regcache->ptid, ptid))
+	{
+	  struct regcache_list *dead = list;
+
+	  *list_link = list->next;
+	  regcache_xfree (list->regcache);
+	  list = *list_link;
+	  xfree (dead);
+	  continue;
+	}
+
+      list_link = &list->next;
+      list = *list_link;
     }
 
   current_regcache = NULL;
@@ -545,6 +557,11 @@ registers_changed (void)
   alloca (0);
 }
 
+void
+registers_changed (void)
+{
+  registers_changed_ptid (minus_one_ptid);
+}
 
 void
 regcache_raw_read (struct regcache *regcache, int regnum, gdb_byte *buf)
Index: src/gdb/regcache.h
===================================================================
--- src.orig/gdb/regcache.h	2010-04-29 15:04:42.000000000 +0100
+++ src/gdb/regcache.h	2010-04-29 15:48:06.000000000 +0100
@@ -159,5 +159,6 @@ extern void regcache_cpy (struct regcach
 extern void regcache_cpy_no_passthrough (struct regcache *dest, struct regcache *src);
 
 extern void registers_changed (void);
+extern void registers_changed_ptid (ptid_t);
 
 #endif /* REGCACHE_H */
Index: src/gdb/target.c
===================================================================
--- src.orig/gdb/target.c	2010-04-29 15:04:42.000000000 +0100
+++ src/gdb/target.c	2010-04-29 15:48:06.000000000 +0100
@@ -2192,6 +2192,7 @@ target_resume (ptid_t ptid, int step, en
 				step ? "step" : "continue",
 				target_signal_to_name (signal));
 
+	  registers_changed_ptid (ptid);
 	  set_executing (ptid, 1);
 	  set_running (ptid, 1);
 	  clear_inline_frame_state (ptid);

---
 gdb/testsuite/gdb.mi/mi-ns-stale-regcache.exp |   88 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.mi/ns-stale-regcache.c      |   30 ++++++++
 2 files changed, 118 insertions(+)

Index: src/gdb/testsuite/gdb.mi/mi-ns-stale-regcache.exp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.mi/mi-ns-stale-regcache.exp	2010-04-29 15:47:31.000000000 +0100
@@ -0,0 +1,88 @@
+# Copyright 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010
+# 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/>.
+
+# Regression test for PR11557.  Make sure we don't end up with a stale
+# register cache just after resuming a thread.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if {[mi_gdb_start]} {
+    continue
+}
+
+proc mi_nonstop_resume { command test } {
+    if { [mi_send_resuming_command $command $test] != 0 } {
+	# If a resume fails, assume non-stop is broken or unsupported
+	# for this target.  We have logged a FAIL or UNSUPPORTED; skip
+	# the remaining tests to limit timeouts.
+	return -code continue
+    }
+}
+
+#
+# Start here
+#
+set testfile "ns-stale-regcache"
+set srcfile "$testfile.c"
+set binfile "$objdir/$subdir/mi-$testfile"
+
+set options [list debug incdir=$objdir]
+if {[gdb_compile "$srcdir/$subdir/$srcfile" \
+	 $binfile executable $options] != "" } {
+    return -1
+}
+
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load $binfile
+
+set supported 0
+send_gdb "-gdb-show non-stop\n"
+gdb_expect {
+    -re ".*\\^done,value=\"off\",supported=\"(\[^\"\]+)\"\r\n$mi_gdb_prompt$" {
+	if { $expect_out(1,string) == "1" } {
+	    set supported 1
+	}
+    }
+    -re ".$mi_gdb_prompt$" {
+    }
+}
+
+mi_gdb_test "-gdb-set non-stop 1" ".*"
+mi_gdb_test "-gdb-set target-async 1" ".*"
+detect_async
+
+if { [mi_run_to_main] < 0 } {
+    perror "mi-ns-stale-regcache.exp tests suppressed"
+    continue
+}
+
+# Check that register and stack info don't end up stale after resuming
+# a thread.
+mi_nonstop_resume "exec-continue" "resume thread"
+
+mi_gdb_test "-data-evaluate-expression \$pc" \
+    "\\^error,msg=\".*\"" "no stale register cache of resumed thread"
+
+mi_gdb_test "-stack-info-frame" \
+    "\\^error,msg=\".*\"" "no stale frame info of resumed thread"
+
+# Check that the thread is still running.  If the above tests passed,
+# we want it to be for the right reasons.
+mi_check_thread_states {"running"} "main thread still running"
+
+mi_gdb_exit
Index: src/gdb/testsuite/gdb.mi/ns-stale-regcache.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.mi/ns-stale-regcache.c	2010-04-29 14:59:50.000000000 +0100
@@ -0,0 +1,30 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2010 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/>.
+*/
+
+#include <unistd.h>
+
+int
+main (int argc, char **argv)
+{
+  volatile int my_number = 1;
+
+  while (my_number > 0)
+    {
+      usleep (1);
+    }
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]