diff options
-rw-r--r-- | gdb/ChangeLog | 17 | ||||
-rw-r--r-- | gdb/doc/ChangeLog | 6 | ||||
-rw-r--r-- | gdb/doc/guile.texi | 3 | ||||
-rw-r--r-- | gdb/doc/python.texi | 3 | ||||
-rw-r--r-- | gdb/frame.c | 74 | ||||
-rw-r--r-- | gdb/frame.h | 15 | ||||
-rw-r--r-- | gdb/guile/lib/gdb.scm | 1 | ||||
-rw-r--r-- | gdb/stack.c | 4 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 6 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/amd64-invalid-stack-middle.exp | 48 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/amd64-invalid-stack-top.exp | 49 | ||||
-rw-r--r-- | gdb/unwind_stop_reasons.def | 5 |
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 |