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]

Re: [patch] Fix internal error on breaking at a multi-locations caller


> On Fri, 24 Apr 2009 03:00:50 +0200, Tom Tromey wrote:
> > Based on the documentation of `break', and also my mental model of
> > debugging with gdb, I think that the best behavior here would be to
> > simply set a single breakpoint here -- the one corresponding to the
> > instance that is currently being executed.

info '(gdb)Set Breaks'
`break'
     When called without any arguments, `break' sets a breakpoint at
     the next instruction to be executed in the selected stack frame
[...]

Do you refer here to the "selected stack frame" part of the doc?


On Tue, 28 Apr 2009 22:32:35 +0200, Joel Brobecker wrote:
> That's a good point. I actually had a different interpretation
> of the "break" command without arguments, but the documentation
> is very specific about it. I agree with you.

Attaching here such a patch variant; it at least prints a warning in such case.


Regression tested on x86_64-unknown-linux-gnu.


Thanks,
Jan

gdb/
2009-05-01  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Fix internal error on breaking at a multi-locations caller source line.
	* breakpoint.c (expand_line_sal_maybe): Remove the variable `found'.
	(expand_line_sal_maybe <original_pc && expanded.nelts >= 2>): New
	initialized variable `best'.  Trim `expanded.sals' for the caller lines.

gdb/testsuite/
2009-05-01  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.cp/expand-sals.exp, gdb.cp/expand-sals.cc: New.

--- gdb/breakpoint.c	29 Apr 2009 19:31:58 -0000	1.392
+++ gdb/breakpoint.c	1 May 2009 08:54:09 -0000
@@ -5336,7 +5336,6 @@ expand_line_sal_maybe (struct symtab_and
   struct symtabs_and_lines expanded;
   CORE_ADDR original_pc = sal.pc;
   char *original_function = NULL;
-  int found;
   int i;
 
   /* If we have explicit pc, don't expand.
@@ -5412,14 +5411,56 @@ expand_line_sal_maybe (struct symtab_and
 
   if (original_pc)
     {
-      found = 0;
+      /* There are multiple PCs for this line of code with multiple instances
+	 (locations).  If the instruction is in the middle of an instruction
+	 block for source line GDB cannot safely find the same instruction in
+	 the other compiled instances of the same source line because the other
+	 instances may have been compiled completely differently.
+
+	 The testcase gdb.cp/expand-sals.exp shows that breaking at the return
+	 address in a caller of the current frame works for the current
+	 instance but the breakpoint cannot catch the point (instruction) where
+	 the callee returns in the other compiled instances of this source line.
+
+	 The current implementation will place the breakpoint at the expected
+	 returning address of the current instance of the caller.  But the
+	 other instances get no breakpoint at all.
+
+	 One possibility would be to put the breakpoint at first instruction of
+	 the same source line - therefore before the call would be made.
+	 Another possibility would be to place the breakpoint in the other
+	 instances at the start of the next source line.
+
+	 A possible heuristics would compare the instructions length of each of
+	 the instances of the current source line and if it matches it would
+	 place the breakpoint at the same offset.  Unfortunately a mistaken
+	 guess would possibly crash the inferior.  */
+
+      CORE_ADDR best = -1;
+
+      /* Find the nearest preceding PC and set it to ORIGINAL_PC.  */
       for (i = 0; i < expanded.nelts; ++i)
-	if (expanded.sals[i].pc == original_pc)
-	  {
-	    found = 1;
-	    break;
-	  }
-      gdb_assert (found);
+	if (expanded.sals[i].pc <= original_pc
+	    && (best == -1 || expanded.sals[best].pc < expanded.sals[i].pc))
+	  best = i;
+
+      if (best == -1)
+	error (_("Cannot find the best address for %s out of the %d locations"),
+	       paddr (original_pc), expanded.nelts);
+
+      /* ORIGINAL_PC is set even for regular `break LINENO' commands which
+	 should cover all the locations.  Catch specifically the
+	 `up'-into-caller case where SAL.PC does not match the first
+	 instruction of the line but still SAL.EXPLICIT_PC is not set.  */
+
+      if (expanded.sals[best].pc != original_pc)
+	{
+	  warning (_("Breakpoint has been set only in the current location "
+	             "out of %d existing ones for this line."), expanded.nelts);
+	  expanded.sals[best].pc = original_pc;
+	  expanded.sals[0] = expanded.sals[best];
+	  expanded.nelts = 1;
+	}
     }
 
   return expanded;
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.cp/expand-sals.cc	1 May 2009 08:54:10 -0000
@@ -0,0 +1,53 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+int
+func ()
+{
+  return 42;	/* func-line */
+}
+
+volatile int global_x;
+
+class A
+{
+public:
+  A ()
+    {
+      global_x = func ();	/* caller-line */
+    }
+};
+
+/* class B is here just to make the `func' calling line above having multiple
+   instances - multiple locations.  Template cannot be used as its instances
+   would have different function names which get discarded by GDB
+   expand_line_sal_maybe.  */
+
+class B : public A
+{
+};
+
+int
+main (void)
+{
+  A a;
+  B b;
+
+  return 0;	/* exit-line */
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.cp/expand-sals.exp	1 May 2009 08:54:10 -0000
@@ -0,0 +1,86 @@
+# Copyright 2009 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/>.
+
+if { [skip_cplus_tests] } { continue }
+
+set srcfile expand-sals.cc
+if { [prepare_for_testing expand-sals.exp expand-sals $srcfile {debug c++}] } {
+    return -1
+}
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "exit-line"]
+
+gdb_breakpoint [gdb_get_line_number "func-line"]
+gdb_continue_to_breakpoint "func" ".*func-line.*"
+
+gdb_test "up" "caller-line.*"
+
+# PC should not be at the boundary of source lines to make the original bug
+# exploitable.
+
+set test "p/x \$pc"
+set pc {}
+gdb_test_multiple $test $test {
+    -re "\\$\[0-9\]+ = (0x\[0-9a-f\]+)\r\n$gdb_prompt $" {
+	set pc $expect_out(1,string)
+	pass $test
+    }
+}
+
+set test "info line"
+set end {}
+gdb_test_multiple $test $test {
+    -re "Line \[0-9\]+ of .* starts at address 0x\[0-9a-f\]+.* and ends at (0x\[0-9a-f\]+).*\\.\r\n$gdb_prompt $" {
+	set end $expect_out(1,string)
+	pass $test
+    }
+}
+
+set test "caller line has trailing code"
+if {$pc != $end} {
+    pass $test
+} else {
+    fail $test
+}
+
+# Original problem was an internal error here.
+set test "break"
+gdb_test_multiple $test $test {
+    -re "Breakpoint \[0-9\]+ at .*, line \[0-9\]+\\. \\(\[2-9\] locations\\)\r\n$gdb_prompt $" {
+	fail $test
+    }
+    -re "warning: Breakpoint has been set only in the current location out of 2 existing ones for this line\\.\r\nBreakpoint \[0-9\]+ at .*, line \[0-9\]+\\.\r\n$gdb_prompt $" {
+	pass $test
+    }
+    -re "Breakpoint \[0-9\]+ at .*, line \[0-9\]+\\.\r\n$gdb_prompt $" {
+	fail $test
+    }
+}
+
+gdb_continue_to_breakpoint "caller" ".*caller-line.*"
+
+# Test GDB caught this return call and not the next one through B::B()
+gdb_test "bt" \
+	 "#0 \[^\r\n\]* A \[^\r\n\]*\r\n#1 \[^\r\n\]* main \[^\r\n\]*" \
+	 "bt from A"
+
+gdb_continue_to_breakpoint "next caller func" ".*func-line.*"
+
+# Verify GDB really could not catch any other breakpoint location.
+
+gdb_continue_to_breakpoint "uncaught return" ".*exit-line.*"


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