+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 ...
+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>
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
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
/* 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
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;
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. */
}
}
+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. */
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
FRAME_UNWIND_INNER_ID
FRAME_UNWIND_SAME_ID
FRAME_UNWIND_NO_SAVED_PC
+ FRAME_UNWIND_MEMORY_ERROR
frame?
frame-valid?
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");
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));
}
}
}
+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.
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}
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\"" \
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}\"\}\\\]" \
# 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}
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\"" \
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\"\}\\\]" \
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 */
#endif
#ifdef LAST_ENTRY
-LAST_ENTRY (UNWIND_NO_SAVED_PC)
+LAST_ENTRY (UNWIND_MEMORY_ERROR)
#endif