-stack-info-depth should always return a count.

Message ID 53342599.9000206@broadcom.com
State Rejected
Headers

Commit Message

Andrew Burgess March 27, 2014, 1:20 p.m. UTC
  The MI command -stack-info-depth return the "depth" field in most
cases.  However, if due to stack corruption, gdb tries to access
some invalid memory then the result is an error and no depth.

I believe that the current behaviour would be better if gdb always
returned a depth number, indicating the number of frames that are
available before the stack corruption, the patch below does this.

I have also added an new "reason" field to the -stack-info-depth
reply, the reason is a string that tries to  explain why gdb is
giving this depth as an answer.

For stacks without any corruption the only two possible reason
strings are "All frames" (this is the total stack depth), or
"Reached frame limit" (a LIMIT was passed to -stack-info-depth
and reached).  If there is stack corruption then the reason will
be the error string from gdb.

OK to apply?

Thanks,
Andrew

gdb/ChangeLog:

2014-03-27  Andrew Burgess  <aburgess@broadcom.com>

	* mi/mi-cmd-stack.c (mi_cmd_stack_info_depth): Add TRY_CATCH
	around the stack unwind.  Add a reason field to explain the given
	depth.
	* NEWS: Mention changes to -stack-info-depth command.

gdb/testsuite/ChangeLog:

2014-03-27  Andrew Burgess  <aburgess@broadcom.com>

	* gdb.mi/mi-stack.exp (test_stack_info_depth): Expect new reason
	field.
gdb/doc/ChangeLog:

2014-03-27  Andrew Burgess  <aburgess@broadcom.com>

	* gdb.texinfo (gdb/mi Stack Manipulation): Add information and
	examples explaining new reason field for -stack-info-depth
	command.
  

Comments

Eli Zaretskii March 27, 2014, 5:09 p.m. UTC | #1
> Date: Thu, 27 Mar 2014 13:20:25 +0000
> From: Andrew Burgess <aburgess@broadcom.com>
> 
> +* MI changes
> +
> +  ** The -stack-info-depth command will now always return a depth even if
> +     stack corruption is encountered.  A new reason string is also returned
> +     that explains why gdb is reporting this depth.
                          ^^^
"GDB", in caps.

> +Return the depth of the stack.  If the integer argument
> +@var{max-depth} is specified, do not count beyond @var{max-depth}
> +frames.  The reason why gdb returns the answer it does is also given.
                           ^^^
@value{GDBN}

> +Possible reasons are @var{All frames}, @var{Reached frame limit}, or
> +an error message.

@var is incorrect here, please use @samp instead.

> +An example where there are 3 valid stack frames, after which the stack
> +pointer is corrupted and gdb is unable to unwind the stack further:
                            ^^^
@value{GDBN}

The documentation parts are OK with those changes.

Thanks.
  
Keith Seitz March 28, 2014, 7:39 p.m. UTC | #2
Hi,

Thank you for the patch. A few comments below.

On 03/27/2014 06:20 AM, Andrew Burgess wrote:

> I believe that the current behaviour would be better if gdb always
> returned a depth number, indicating the number of frames that are
> available before the stack corruption, the patch below does this.

Out of curiosity, how does -stack-list-frames behave in this scenario? 
IMO both these commands should offer complimentary data, i.e., if 
-stack-list-frames outputs N frames before an error, then 
-stack-info-depth should return N. If -stack-list-frames outputs no 
frames, then -stack-info-depth should return zero.

If, as I suspect, -stack-list-frames and -stack-info-depth output 
different numbers of frames for this situation, then I agree. This patch 
is needed.

If not, well, I don't see any harm, but I have no idea if this kind of 
change might cause problems for current MI consumers. [To be honest, I'm 
not sure what value -stack-info-depth has to begin with.] A global/MI 
maintainer will have to give a recommendation in that case.

> diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
> index 7bc8114..9a4eec7 100644
> --- a/gdb/mi/mi-cmd-stack.c
> +++ b/gdb/mi/mi-cmd-stack.c
> @@ -170,8 +170,10 @@ void
>   mi_cmd_stack_info_depth (char *command, char **argv, int argc)
>   {
>     int frame_high;
> -  int i;
> +  int i = 0;
>     struct frame_info *fi;
> +  volatile struct gdb_exception except;
> +  const char *reason;
>
>     if (argc > 1)
>       error (_("-stack-info-depth: Usage: [MAX_DEPTH]"));
> @@ -183,12 +185,29 @@ mi_cmd_stack_info_depth (char *command, char **argv, int argc)
>          the stack.  */
>       frame_high = -1;
>
> -  for (i = 0, fi = get_current_frame ();
> -       fi && (i < frame_high || frame_high == -1);
> -       i++, fi = get_prev_frame (fi))
> -    QUIT;
> +  TRY_CATCH (except, RETURN_MASK_ERROR)
> +    {
> +      for (i = 0, fi = get_current_frame ();
> +	   fi && (i < frame_high || frame_high == -1);
> +	   i++, fi = get_prev_frame (fi))
> +	QUIT;
> +    }
> +
> +  if (except.message)

Maintainers recently agreed to do explicit checks for NULL. [if 
(except.message != NULL) ...]

> +    {
> +      /* Due to the lazy way state is fetched from the target, if we have
> +	 an exception our depth count will be one greater than it should
> +	 be.  */
> +      reason = except.message;
> +      --i;
> +    }
> +  else if (frame_high == -1 || i < frame_high)
> +    reason = _("All frames");
> +  else
> +    reason = _("Reached frame limit");
>
>     ui_out_field_int (current_uiout, "depth", i);
> +  ui_out_field_string (current_uiout, "reason", reason);
>   }
>
>   /* Print a list of the locals for the current frame.  With argument of
>   2014-03-26  Yao Qi  <yao@codesourcery.com>
>
>   	* lib/gdb.exp (readline_is_used): New proc.

I presume this is a cut-n-paste-o...

> diff --git a/gdb/testsuite/gdb.mi/mi-stack.exp b/gdb/testsuite/gdb.mi/mi-stack.exp
> index ac7db7a..4977342 100644
> --- a/gdb/testsuite/gdb.mi/mi-stack.exp
> +++ b/gdb/testsuite/gdb.mi/mi-stack.exp
> @@ -137,15 +137,15 @@ proc test_stack_info_depth {} {
>       # -stack-info-depth 99
>
>       mi_gdb_test "231-stack-info-depth" \
> -	    "231\\^done,depth=\"5\"" \
> +        "231\\^done,depth=\"5\",reason=\"\[^\"\]+\"" \
>                   "stack info-depth"
>
>       mi_gdb_test "231-stack-info-depth 3" \
> -	    "231\\^done,depth=\"3\"" \
> +	    "231\\^done,depth=\"3\",reason=\"\[^\"\]+\"" \
>                   "stack info-depth 3"
>
>       mi_gdb_test "231-stack-info-depth 99" \
> -	    "231\\^done,depth=\"5\"" \
> +	    "231\\^done,depth=\"5\",reason=\"\[^\"\]+\"" \
>                   "stack info-depth 99"
>
>       mi_gdb_test "231-stack-info-depth 99 99" \
>

Is there any reason to omit the actual reason="..." in these tests? I 
don't see where the reason="..." is tested otherwise. I think this 
necessary. I'm a ever-growing fan of coverage testing.

Is there any way to reliably test the error case/reason="<some error 
message>"?

Keith
  
Tom Tromey May 15, 2014, 8:15 p.m. UTC | #3
>>>>> "Andrew" == Andrew Burgess <aburgess@broadcom.com> writes:

Andrew> I have also added an new "reason" field to the -stack-info-depth
Andrew> reply, the reason is a string that tries to  explain why gdb is
Andrew> giving this depth as an answer.

Andrew> OK to apply?

It seems like a reasonable approach to me.

I wonder whether any MI users are likely to have issues.  It would be
nice if we had better communication channels here :( Anyway, have you
looked at any of them?  Or, does a particular program motivate this
patch?

I didn't see any answer to Keith's questions.

Tom
  
Vladimir Prus May 16, 2014, 6:22 a.m. UTC | #4
On 03/27/2014 05:20 PM, Andrew Burgess wrote:

> -^done,depth="12"
> +^done,depth="12",reason="All frames"
>   (gdb)
>   -stack-info-depth 4
> -^done,depth="4"
> +^done,depth="4",reason="Reached frame limit"
>   (gdb)
>   -stack-info-depth 12
> -^done,depth="12"
> +^done,depth="12",reason="Reached frame limit"
>   (gdb)
>   -stack-info-depth 11
> -^done,depth="11"
> +^done,depth="11",reason="Reached frame limit"
>   (gdb)
>   -stack-info-depth 13
> -^done,depth="12"
> +^done,depth="12",reason="All frames"
> +(gdb)
> +@end smallexample
> +
> +An example where there are 3 valid stack frames, after which the stack
> +pointer is corrupted and gdb is unable to unwind the stack further:
> +
> +@smallexample
> +(gdb)
> +-stack-info-depth
> +^done,depth="3",reason="Cannot access memory at address 0xffffffff"
> +(gdb)
> +-stack-info-depth 4
> +^done,depth="3",reason="Cannot access memory at address 0xffffffff"
> +(gdb)
> +-stack-info-depth 3
> +^done,depth="3",reason="Reached frame limit"
>   (gdb)
>   @end smallexample

I think using human readable reasons like is a bit inconvenient, given that the consumer is
a computer. Can we use has_more field, like we do somewhere else, to indicate that more
frames might be available in case of specified depth limit? And use 'error' field in case
we run into actual error (in which case, has_more field will not be provided)?

Thanks,
Volodya
  

Patch

diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 7bc8114..9a4eec7 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -170,8 +170,10 @@  void
 mi_cmd_stack_info_depth (char *command, char **argv, int argc)
 {
   int frame_high;
-  int i;
+  int i = 0;
   struct frame_info *fi;
+  volatile struct gdb_exception except;
+  const char *reason;
 
   if (argc > 1)
     error (_("-stack-info-depth: Usage: [MAX_DEPTH]"));
@@ -183,12 +185,29 @@  mi_cmd_stack_info_depth (char *command, char **argv, int argc)
        the stack.  */
     frame_high = -1;
 
-  for (i = 0, fi = get_current_frame ();
-       fi && (i < frame_high || frame_high == -1);
-       i++, fi = get_prev_frame (fi))
-    QUIT;
+  TRY_CATCH (except, RETURN_MASK_ERROR)
+    {
+      for (i = 0, fi = get_current_frame ();
+	   fi && (i < frame_high || frame_high == -1);
+	   i++, fi = get_prev_frame (fi))
+	QUIT;
+    }
+
+  if (except.message)
+    {
+      /* Due to the lazy way state is fetched from the target, if we have
+	 an exception our depth count will be one greater than it should
+	 be.  */
+      reason = except.message;
+      --i;
+    }
+  else if (frame_high == -1 || i < frame_high)
+    reason = _("All frames");
+  else
+    reason = _("Reached frame limit");
 
   ui_out_field_int (current_uiout, "depth", i);
+  ui_out_field_string (current_uiout, "reason", reason);
 }
 
 /* Print a list of the locals for the current frame.  With argument of
 2014-03-26  Yao Qi  <yao@codesourcery.com>
 
 	* lib/gdb.exp (readline_is_used): New proc.
diff --git a/gdb/testsuite/gdb.mi/mi-stack.exp b/gdb/testsuite/gdb.mi/mi-stack.exp
index ac7db7a..4977342 100644
--- a/gdb/testsuite/gdb.mi/mi-stack.exp
+++ b/gdb/testsuite/gdb.mi/mi-stack.exp
@@ -137,15 +137,15 @@  proc test_stack_info_depth {} {
     # -stack-info-depth 99
 
     mi_gdb_test "231-stack-info-depth" \
-	    "231\\^done,depth=\"5\"" \
+        "231\\^done,depth=\"5\",reason=\"\[^\"\]+\"" \
                 "stack info-depth"
 
     mi_gdb_test "231-stack-info-depth 3" \
-	    "231\\^done,depth=\"3\"" \
+	    "231\\^done,depth=\"3\",reason=\"\[^\"\]+\"" \
                 "stack info-depth 3"
 
     mi_gdb_test "231-stack-info-depth 99" \
-	    "231\\^done,depth=\"5\"" \
+	    "231\\^done,depth=\"5\",reason=\"\[^\"\]+\"" \
                 "stack info-depth 99"
 
     mi_gdb_test "231-stack-info-depth 99 99" \

diff --git a/gdb/NEWS b/gdb/NEWS
index 2a384ba..172101e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -95,6 +95,12 @@  PowerPC64 GNU/Linux little-endian	powerpc64le-*-linux*
   and "assf"), have been deprecated.  Use the "sharedlibrary" command, or
   its alias "share", instead.
 
+* MI changes
+
+  ** The -stack-info-depth command will now always return a depth even if
+     stack corruption is encountered.  A new reason string is also returned
+     that explains why gdb is reporting this depth.
+
 *** Changes in GDB 7.7
 
 * Improved support for process record-replay and reverse debugging on
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 1df3ca0..4f4ece8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -27534,8 +27534,11 @@  fullname="/home/foo/bar/devo/gdb/testsuite/gdb.mi/basics.c",line="17"@}
  -stack-info-depth [ @var{max-depth} ]
 @end smallexample
 
-Return the depth of the stack.  If the integer argument @var{max-depth}
-is specified, do not count beyond @var{max-depth} frames.
+Return the depth of the stack.  If the integer argument
+@var{max-depth} is specified, do not count beyond @var{max-depth}
+frames.  The reason why gdb returns the answer it does is also given.
+Possible reasons are @var{All frames}, @var{Reached frame limit}, or
+an error message.
 
 @subsubheading @value{GDBN} Command
 
@@ -27548,19 +27551,35 @@  For a stack with frame levels 0 through 11:
 @smallexample
 (gdb)
 -stack-info-depth
-^done,depth="12"
+^done,depth="12",reason="All frames"
 (gdb)
 -stack-info-depth 4
-^done,depth="4"
+^done,depth="4",reason="Reached frame limit"
 (gdb)
 -stack-info-depth 12
-^done,depth="12"
+^done,depth="12",reason="Reached frame limit"
 (gdb)
 -stack-info-depth 11
-^done,depth="11"
+^done,depth="11",reason="Reached frame limit"
 (gdb)
 -stack-info-depth 13
-^done,depth="12"
+^done,depth="12",reason="All frames"
+(gdb)
+@end smallexample
+
+An example where there are 3 valid stack frames, after which the stack
+pointer is corrupted and gdb is unable to unwind the stack further:
+
+@smallexample
+(gdb)
+-stack-info-depth
+^done,depth="3",reason="Cannot access memory at address 0xffffffff"
+(gdb)
+-stack-info-depth 4
+^done,depth="3",reason="Cannot access memory at address 0xffffffff"
+(gdb)
+-stack-info-depth 3
+^done,depth="3",reason="Reached frame limit"
 (gdb)
 @end smallexample