aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog17
-rw-r--r--gdb/doc/ChangeLog6
-rw-r--r--gdb/doc/guile.texi3
-rw-r--r--gdb/doc/python.texi3
-rw-r--r--gdb/frame.c74
-rw-r--r--gdb/frame.h15
-rw-r--r--gdb/guile/lib/gdb.scm1
-rw-r--r--gdb/stack.c4
-rw-r--r--gdb/testsuite/ChangeLog6
-rw-r--r--gdb/testsuite/gdb.arch/amd64-invalid-stack-middle.exp48
-rw-r--r--gdb/testsuite/gdb.arch/amd64-invalid-stack-top.exp49
-rw-r--r--gdb/unwind_stop_reasons.def5
12 files changed, 141 insertions, 90 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 11c1364..04e368c 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,22 @@
2014-05-30 Andrew Burgess <aburgess@broadcom.com>
+ * frame.c (struct frame_info): Add stop_string field.
+ (get_prev_frame_always_1): Renamed from get_prev_frame_always.
+ (get_prev_frame_always): Old content moved into
+ get_prev_frame_always_1. Call get_prev_frame_always_1 inside
+ TRY_CATCH, handle MEMORY_ERROR exceptions.
+ (frame_stop_reason_string): New function definition.
+ * frame.h (unwind_stop_reason_to_string): Extend comment to
+ mention frame_stop_reason_string.
+ (frame_stop_reason_string): New function declaration.
+ * stack.c (frame_info): Switch to frame_stop_reason_string.
+ (backtrace_command_1): Switch to frame_stop_reason_string.
+ * unwind_stop_reason.def: Add UNWIND_MEMORY_ERROR.
+ (LAST_ENTRY): Changed to UNWIND_MEMORY_ERROR.
+ * guile/lib/gdb.scm: Add FRAME_UNWIND_MEMORY_ERROR to export list.
+
+2014-05-30 Andrew Burgess <aburgess@broadcom.com>
+
* frame.c (frame_stop_reason_string): Rename to ...
(unwind_stop_reason_to_string): this.
* frame.h (frame_stop_reason_string): Rename to ...
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 18fb241..1a07a82 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,9 @@
+2014-05-30 Andrew Burgess <aburgess@broadcom.com>
+
+ * guile.texi (Frames In Guile): Mention FRAME_UNWIND_MEMORY_ERROR.
+ * python.texi (Frames In Python): Mention
+ gdb.FRAME_UNWIND_MEMORY_ERROR.
+
2014-05-29 Pedro Alves <palves@redhat.com>
Tom Tromey <tromey@redhat.com>
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index 3e03c7c..bc2a2ce 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -1827,6 +1827,9 @@ stack corruption.
The frame unwinder did not find any saved PC, but we needed
one to unwind further.
+@item FRAME_UNWIND_MEMORY_ERROR
+The frame unwinder caused an error while trying to access memory.
+
@item FRAME_UNWIND_FIRST_ERROR
Any stop reason greater or equal to this value indicates some kind
of error. This special value facilitates writing code that tests
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index ce8ec78..006b873 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -3199,6 +3199,9 @@ stack corruption.
The frame unwinder did not find any saved PC, but we needed
one to unwind further.
+@item gdb.FRAME_UNWIND_MEMORY_ERROR
+The frame unwinder caused an error while trying to access memory.
+
@item gdb.FRAME_UNWIND_FIRST_ERROR
Any stop reason greater or equal to this value indicates some kind
of error. This special value facilitates writing code that tests
diff --git a/gdb/frame.c b/gdb/frame.c
index f44cf50..8dea9c4 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -145,6 +145,10 @@ struct frame_info
/* The reason why we could not set PREV, or UNWIND_NO_REASON if we
could. Only valid when PREV_P is set. */
enum unwind_stop_reason stop_reason;
+
+ /* A frame specific string describing the STOP_REASON in more detail.
+ Only valid when PREV_P is set, but even then may still be NULL. */
+ const char *stop_string;
};
/* A frame stash used to speed up frame lookups. Create a hash table
@@ -1798,14 +1802,12 @@ get_prev_frame_if_no_cycle (struct frame_info *this_frame)
return prev_frame;
}
-/* Return a "struct frame_info" corresponding to the frame that called
- THIS_FRAME. Returns NULL if there is no such frame.
-
- Unlike get_prev_frame, this function always tries to unwind the
- frame. */
+/* Helper function for get_prev_frame_always, this is called inside a
+ TRY_CATCH block. Return the frame that called THIS_FRAME or NULL if
+ there is no such frame. This may throw an exception. */
-struct frame_info *
-get_prev_frame_always (struct frame_info *this_frame)
+static struct frame_info *
+get_prev_frame_always_1 (struct frame_info *this_frame)
{
struct gdbarch *gdbarch;
@@ -1955,6 +1957,50 @@ get_prev_frame_always (struct frame_info *this_frame)
return get_prev_frame_if_no_cycle (this_frame);
}
+/* Return a "struct frame_info" corresponding to the frame that called
+ THIS_FRAME. Returns NULL if there is no such frame.
+
+ Unlike get_prev_frame, this function always tries to unwind the
+ frame. */
+
+struct frame_info *
+get_prev_frame_always (struct frame_info *this_frame)
+{
+ volatile struct gdb_exception ex;
+ struct frame_info *prev_frame = NULL;
+
+ TRY_CATCH (ex, RETURN_MASK_ERROR)
+ {
+ prev_frame = get_prev_frame_always_1 (this_frame);
+ }
+ if (ex.reason < 0)
+ {
+ if (ex.error == MEMORY_ERROR)
+ {
+ this_frame->stop_reason = UNWIND_MEMORY_ERROR;
+ if (ex.message != NULL)
+ {
+ char *stop_string;
+ size_t size;
+
+ /* The error needs to live as long as the frame does.
+ Allocate using stack local STOP_STRING then assign the
+ pointer to the frame, this allows the STOP_STRING on the
+ frame to be of type 'const char *'. */
+ size = strlen (ex.message) + 1;
+ stop_string = frame_obstack_zalloc (size);
+ memcpy (stop_string, ex.message, size);
+ this_frame->stop_string = stop_string;
+ }
+ prev_frame = NULL;
+ }
+ else
+ throw_exception (ex);
+ }
+
+ return prev_frame;
+}
+
/* Construct a new "struct frame_info" and link it previous to
this_frame. */
@@ -2576,6 +2622,20 @@ unwind_stop_reason_to_string (enum unwind_stop_reason reason)
}
}
+const char *
+frame_stop_reason_string (struct frame_info *fi)
+{
+ gdb_assert (fi->prev_p);
+ gdb_assert (fi->prev == NULL);
+
+ /* Return the specific string if we have one. */
+ if (fi->stop_string != NULL)
+ return fi->stop_string;
+
+ /* Return the generic string if we have nothing better. */
+ return unwind_stop_reason_to_string (fi->stop_reason);
+}
+
/* Return the enum symbol name of REASON as a string, to use in debug
output. */
diff --git a/gdb/frame.h b/gdb/frame.h
index 79881521d..59564f9 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -501,10 +501,23 @@ enum unwind_stop_reason
enum unwind_stop_reason get_frame_unwind_stop_reason (struct frame_info *);
-/* Translate a reason code to an informative string. */
+/* Translate a reason code to an informative string. This converts the
+ generic stop reason codes into a generic string describing the code.
+ For a possibly frame specific string explaining the stop reason, use
+ FRAME_STOP_REASON_STRING instead. */
const char *unwind_stop_reason_to_string (enum unwind_stop_reason);
+/* Return a possibly frame specific string explaining why the unwind
+ stopped here. E.g., if unwinding tripped on a memory error, this
+ will return the error description string, which includes the address
+ that we failed to access. If there's no specific reason stored for
+ a frame then a generic reason string will be returned.
+
+ Should only be called for frames that don't have a previous frame. */
+
+const char *frame_stop_reason_string (struct frame_info *);
+
/* Unwind the stack frame so that the value of REGNUM, in the previous
(up, older) frame is returned. If VALUEP is NULL, don't
fetch/compute the value. Instead just return the location of the
diff --git a/gdb/guile/lib/gdb.scm b/gdb/guile/lib/gdb.scm
index ec739c7..abc4a67 100644
--- a/gdb/guile/lib/gdb.scm
+++ b/gdb/guile/lib/gdb.scm
@@ -169,6 +169,7 @@
FRAME_UNWIND_INNER_ID
FRAME_UNWIND_SAME_ID
FRAME_UNWIND_NO_SAVED_PC
+ FRAME_UNWIND_MEMORY_ERROR
frame?
frame-valid?
diff --git a/gdb/stack.c b/gdb/stack.c
index 630a363..4f16238 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1529,7 +1529,7 @@ frame_info (char *addr_exp, int from_tty)
reason = get_frame_unwind_stop_reason (fi);
if (reason != UNWIND_NO_REASON)
printf_filtered (_(" Outermost frame: %s\n"),
- unwind_stop_reason_to_string (reason));
+ frame_stop_reason_string (fi));
}
else if (get_frame_type (fi) == TAILCALL_FRAME)
puts_filtered (" tail call frame");
@@ -1848,7 +1848,7 @@ backtrace_command_1 (char *count_exp, int show_locals, int no_filters,
reason = get_frame_unwind_stop_reason (trailing);
if (reason >= UNWIND_FIRST_ERROR)
printf_filtered (_("Backtrace stopped: %s\n"),
- unwind_stop_reason_to_string (reason));
+ frame_stop_reason_string (trailing));
}
}
}
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index a8a98f5..a931862 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,11 @@
2014-05-30 Andrew Burgess <aburgess@broadcom.com>
+ * gdb.arch/amd64-invalid-stack-middle.exp: Update expected
+ results.
+ * gdb.arch/amd64-invalid-stack-top.exp: Likewise.
+
+2014-05-30 Andrew Burgess <aburgess@broadcom.com>
+
* gdb.arch/amd64-invalid-stack-middle.S: New file.
* gdb.arch/amd64-invalid-stack-middle.c: New file.
* gdb.arch/amd64-invalid-stack-middle.exp: New file.
diff --git a/gdb/testsuite/gdb.arch/amd64-invalid-stack-middle.exp b/gdb/testsuite/gdb.arch/amd64-invalid-stack-middle.exp
index 8ad5b18..b53ad41 100644
--- a/gdb/testsuite/gdb.arch/amd64-invalid-stack-middle.exp
+++ b/gdb/testsuite/gdb.arch/amd64-invalid-stack-middle.exp
@@ -43,27 +43,11 @@ if ![runto breakpt] {
return -1
}
-gdb_test "bt" "^bt\r\n#0 +breakpt *\\(\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in func5\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in func4\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in func3\[^\r\n\]*\r\nCannot access memory at address 0x\[0-9a-f\]+" \
+gdb_test "bt" "^bt\r\n#0 +breakpt *\\(\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in func5\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in func4\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in func3\[^\r\n\]*\r\nBacktrace stopped: Cannot access memory at address 0x\[0-9a-f\]+" \
"first backtrace, with error message"
-send_gdb "bt\n"
-gdb_expect {
- -re "^bt\r\n#0 +breakpt *\\(\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in func5\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in func4\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in func3\[^\r\n\]*\r\nCannot access memory at address 0x\[0-9a-f\]+\r\n$gdb_prompt $" {
- # Currently gdb will not display the error message associated with
- # the truncated backtrace after the first backtrace has been
- # completed. Ideally, we would do this. If this case is ever hit
- # then we have started to display the backtrace in all cases and
- # the xpass should becomd a pass, and the previous pass case below
- # should be removed, or changed to a fail.
- xpass "second backtrace, with error message"
- }
- -re "^bt\r\n#0 +breakpt *\\(\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in func5\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in func4\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in func3\[^\r\n\]*\r\n$gdb_prompt $" {
- pass "second backtrace, without error message"
- }
- timeout {
- fail "second backtrace (timeout)"
- }
-}
+gdb_test "bt" "^bt\r\n#0 +breakpt *\\(\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in func5\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in func4\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in func3\[^\r\n\]*\r\nBacktrace stopped: Cannot access memory at address 0x\[0-9a-f\]+" \
+ "second backtrace, with error message"
clean_restart ${binfile}
@@ -71,16 +55,9 @@ if ![runto breakpt] {
return -1
}
-set test_name "check mi -stack-info-depth command, first time"
-send_gdb "interpreter-exec mi \"-stack-info-depth\"\n"
-gdb_expect {
- -re "\\^done,depth=\"4\"\r\n$gdb_prompt $" {
- pass $test_name
- }
- -re "\\^error,msg=\"Cannot access memory at address $hex\"\r\n$gdb_prompt $" {
- xfail $test_name
- }
-}
+gdb_test "interpreter-exec mi \"-stack-info-depth\"" \
+ "\\^done,depth=\"4\"" \
+ "check mi -stack-info-depth command, first time"
gdb_test "interpreter-exec mi \"-stack-info-depth\"" \
"\\^done,depth=\"4\"" \
@@ -92,16 +69,9 @@ if ![runto breakpt] {
return -1
}
-set test_name "check mi -stack-list-frames command, first time"
-send_gdb "interpreter-exec mi \"-stack-list-frames\"\n"
-gdb_expect {
- -re "\\^done,stack=\\\[frame=\{level=\"0\",addr=\"$hex\",func=\"breakpt\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\},frame=\{level=\"1\",addr=\"$hex\",func=\"func5\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\},frame=\{level=\"2\",addr=\"$hex\",func=\"func4\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\},frame=\{level=\"3\",addr=\"$hex\",func=\"func3\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\}\\\]\r\n$gdb_prompt $" {
- pass $test_name
- }
- -re "\\^error,msg=\"Cannot access memory at address $hex\"\r\n$gdb_prompt $" {
- xfail $test_name
- }
-}
+gdb_test "interpreter-exec mi \"-stack-list-frames\"" \
+ "\\^done,stack=\\\[frame=\{level=\"0\",addr=\"$hex\",func=\"breakpt\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\},frame=\{level=\"1\",addr=\"$hex\",func=\"func5\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\},frame=\{level=\"2\",addr=\"$hex\",func=\"func4\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\},frame=\{level=\"3\",addr=\"$hex\",func=\"func3\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\}\\\]" \
+ "check mi -stack-list-frames command, first time"
gdb_test "interpreter-exec mi \"-stack-list-frames\"" \
"\\^done,stack=\\\[frame=\{level=\"0\",addr=\"$hex\",func=\"breakpt\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\},frame=\{level=\"1\",addr=\"$hex\",func=\"func5\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\},frame=\{level=\"2\",addr=\"$hex\",func=\"func4\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\},frame=\{level=\"3\",addr=\"$hex\",func=\"func3\",file=\"\[^\"\]+\",fullname=\"\[^\"\]+\",line=\"${decimal}\"\}\\\]" \
diff --git a/gdb/testsuite/gdb.arch/amd64-invalid-stack-top.exp b/gdb/testsuite/gdb.arch/amd64-invalid-stack-top.exp
index 0225326..17c79d7 100644
--- a/gdb/testsuite/gdb.arch/amd64-invalid-stack-top.exp
+++ b/gdb/testsuite/gdb.arch/amd64-invalid-stack-top.exp
@@ -45,27 +45,11 @@ if ![runto breakpt] {
# Use 'bt no-filters' here as the python filters will raise their own
# error during initialisation, the no-filters case is simpler.
-gdb_test "bt no-filters" "^bt no-filters\r\n#0 +$hex in func2 \\(\\)\r\nCannot access memory at address 0x\[0-9a-f\]+" \
+gdb_test "bt no-filters" "^bt no-filters\r\n#0 +$hex in func2 \\(\\)\r\nBacktrace stopped: Cannot access memory at address 0x\[0-9a-f\]+" \
"first backtrace, with error message"
-send_gdb "bt no-filters\n"
-gdb_expect {
- -re "^bt no-filters\r\n#0 +$hex in func2 \\(\\)\r\nCannot access memory at address 0x\[0-9a-f\]+\r\n$gdb_prompt $" {
- # Currently gdb will not display the error message associated with
- # the truncated backtrace after the first backtrace has been
- # completed. Ideally, we would do this. If this case is ever hit
- # then we have started to display the backtrace in all cases and
- # the xpass should becomd a pass, and the previous pass case below
- # should be removed, or changed to a fail.
- xpass "second backtrace, with error message"
- }
- -re "^bt no-filters\r\n#0 +$hex in func2 \\(\\)\r\n$gdb_prompt $" {
- pass "second backtrace, without error message"
- }
- timeout {
- fail "second backtrace (timeout)"
- }
-}
+gdb_test "bt no-filters" "^bt no-filters\r\n#0 +$hex in func2 \\(\\)\r\nBacktrace stopped: Cannot access memory at address 0x\[0-9a-f\]+" \
+ "second backtrace, with error message"
clean_restart ${binfile}
@@ -73,16 +57,9 @@ if ![runto breakpt] {
return -1
}
-set test_name "check mi -stack-info-depth command, first time"
-send_gdb "interpreter-exec mi \"-stack-info-depth\"\n"
-gdb_expect {
- -re "\\^done,depth=\"1\"\r\n$gdb_prompt $" {
- pass $test_name
- }
- -re "\\^error,msg=\"Cannot access memory at address $hex\"\r\n$gdb_prompt $" {
- xfail $test_name
- }
-}
+gdb_test "interpreter-exec mi \"-stack-info-depth\"" \
+ "\\^done,depth=\"1\"" \
+ "check mi -stack-info-depth command, first time"
gdb_test "interpreter-exec mi \"-stack-info-depth\"" \
"\\^done,depth=\"1\"" \
@@ -94,17 +71,9 @@ if ![runto breakpt] {
return -1
}
-set test_name "check mi -stack-list-frames command, first time"
-send_gdb "interpreter-exec mi \"-stack-list-frames\"\n"
-gdb_expect {
- -re "\\^done,stack=\\\[frame=\{level=\"0\",addr=\"$hex\",func=\"func2\"\}\\\]\r\n$gdb_prompt $" {
- pass $test_name
- }
- -re "\\^error,msg=\"Cannot access memory at address $hex\"\r\n$gdb_prompt $" {
- xfail $test_name
- }
-}
-
+gdb_test "interpreter-exec mi \"-stack-list-frames\"" \
+ "\\^done,stack=\\\[frame=\{level=\"0\",addr=\"$hex\",func=\"func2\"\}\\\]" \
+ "check mi -stack-list-frames command, first time"
gdb_test "interpreter-exec mi \"-stack-list-frames\"" \
"\\^done,stack=\\\[frame=\{level=\"0\",addr=\"$hex\",func=\"func2\"\}\\\]" \
diff --git a/gdb/unwind_stop_reasons.def b/gdb/unwind_stop_reasons.def
index d7da7ea..8cef537 100644
--- a/gdb/unwind_stop_reasons.def
+++ b/gdb/unwind_stop_reasons.def
@@ -60,6 +60,9 @@ SET (UNWIND_SAME_ID, "previous frame identical to this frame (corrupt stack?)")
one to unwind further. */
SET (UNWIND_NO_SAVED_PC, "frame did not save the PC")
+/* There was an error accessing memory while unwinding this frame. */
+SET (UNWIND_MEMORY_ERROR, "memory error while unwinding")
+
#endif /* SET */
@@ -72,5 +75,5 @@ FIRST_ENTRY (UNWIND_NO_REASON)
#endif
#ifdef LAST_ENTRY
-LAST_ENTRY (UNWIND_NO_SAVED_PC)
+LAST_ENTRY (UNWIND_MEMORY_ERROR)
#endif