Blob Blame History Raw
  NOTE: The testcase part of this patch has been removed.

commit 5ce0145de764dc9c6e92daf2f843fa6e496c49d0
Author: Pedro Alves <palves@redhat.com>
Date:   Tue Dec 17 20:47:36 2013 +0000

    "tfind" across unavailable-stack frames.
    
    Like when stepping, the current stack frame location is expected to be
    printed as result of tfind command, if that results in moving to a
    different function.  In tfind_1 we see:
    
      if (from_tty
          && (has_stack_frames () || traceframe_number >= 0))
        {
          enum print_what print_what;
    
          /* NOTE: in imitation of the step command, try to determine
             whether we have made a transition from one function to
             another.  If so, we'll print the "stack frame" (ie. the new
             function and it's arguments) -- otherwise we'll just show the
             new source line.  */
    
          if (frame_id_eq (old_frame_id,
                           get_frame_id (get_current_frame ())))
            print_what = SRC_LINE;
          else
            print_what = SRC_AND_LOC;
    
          print_stack_frame (get_selected_frame (NULL), 1, print_what, 1);
          do_displays ();
        }
    
    However, when we haven't collected any registers in the tracepoint
    (collect $regs), that doesn't actually work:
    
     (gdb) tstart
     (gdb) info tracepoints
     Num     Type           Disp Enb Address    What
     1       tracepoint     keep y   0x080483b7 in func0
                                                at ../.././../git/gdb/testsuite/gdb.trace/circ.c:28
             collect testload
         installed on target
     2       tracepoint     keep y   0x080483bc in func1
                                                at ../.././../git/gdb/testsuite/gdb.trace/circ.c:32
             collect testload
         installed on target
     (gdb) c
     Continuing.
    
     Breakpoint 3, end () at ../.././../git/gdb/testsuite/gdb.trace/circ.c:72
     72    }
     (gdb) tstop
     (gdb) tfind start
     Found trace frame 0, tracepoint 1
     #0  func0 () at ../.././../git/gdb/testsuite/gdb.trace/circ.c:28
     28    }
     (gdb) tfind
     Found trace frame 1, tracepoint 2
     32    }
     (gdb)
    
    When we don't have info about the stack available
    (UNWIND_UNAVAILABLE), frames end up with outer_frame_id as frame ID.
    And in the scenario above, the issue is that both frames before and
    after the second tfind (the frames for func0 an func1) have the same
    id (outer_frame_id), so the frame_id_eq check returns false, even
    though the frames were of different functions.  GDB knows that,
    because the PC is inferred from the tracepoint's address, even if no
    registers were collected.
    
    To fix this, this patch adds support for frame ids with a valid code
    address, but <unavailable> stack address, and then makes the unwinders
    use that instead of the catch-all outer_frame_id for such frames.  The
    frame_id_eq check in tfind_1 then automatically does the right thing
    as expected.
    
    I tested with --directory=gdb.trace/ , before/after the patch, and
    compared the resulting gdb.logs, then adjusted the tests to expect the
    extra output that came out.  Turns out that was only circ.exp, the
    original test that actually brought this issue to light.
    
    Tested on x86_64 Fedora 17, native and gdbserver.
    
    gdb/
    2013-12-17  Pedro Alves  <palves@redhat.com>
    
    	* frame.h (enum frame_id_stack_status): New enum.
    	(struct frame_id) <stack_addr>: Adjust comment.
    	<stack_addr_p>: Delete field, replaced with ...
    	<stack_status>: ... this new field.
    	(frame_id_build_unavailable_stack): Declare.
    	* frame.c (frame_addr_hash, fprint_field, outer_frame_id)
    	(frame_id_build_special): Adjust.
    	(frame_id_build_unavailable_stack): New function.
    	(frame_id_build, frame_id_build_wild): Adjust.
    	(frame_id_p, frame_id_eq, frame_id_inner): Adjust to take into
    	account frames with unavailable stack.
    
    	* amd64-tdep.c (amd64_frame_this_id)
    	(amd64_sigtramp_frame_this_id, amd64_epilogue_frame_this_id): Use
    	frame_id_build_unavailable_stack.
    	* dwarf2-frame.c (dwarf2_frame_this_id): Likewise.
    	* i386-tdep.c (i386_frame_this_id, i386_epilogue_frame_this_id)
    	(i386_sigtramp_frame_this_id):  Likewise.
    
    gdb/testsuite/
    2013-12-17  Pedro Alves  <palves@redhat.com>
    
    	* gdb.trace/circ.exp: Expect frame info to be printed when
    	switching between frames with unavailable stack, but different
    	functions.

Index: gdb-7.6.1/gdb/amd64-tdep.c
===================================================================
--- gdb-7.6.1.orig/gdb/amd64-tdep.c
+++ gdb-7.6.1/gdb/amd64-tdep.c
@@ -2403,15 +2403,16 @@ amd64_frame_this_id (struct frame_info *
   struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (this_frame));
 
   if (!cache->base_p)
-    return;
-
-  /* This marks the outermost frame.  */
-  if (cache->base == 0)
-    return;
-
-  /* Detect OS dependent outermost frames; such as `clone'.  */
-  if (tdep->outermost_frame_p && tdep->outermost_frame_p (this_frame))
-    return;
+    (*this_id) = frame_id_build_unavailable_stack (cache->pc);
+  else if (cache->base == 0)
+    {
+      /* This marks the outermost frame.  */
+      return;
+    }
+  else
+    /* Detect OS dependent outermost frames; such as `clone'.  */
+    if (tdep->outermost_frame_p && tdep->outermost_frame_p (this_frame))
+      return;
 
   (*this_id) = frame_id_build (cache->base + 16, cache->pc);
 }
@@ -2528,9 +2529,14 @@ amd64_sigtramp_frame_this_id (struct fra
     amd64_sigtramp_frame_cache (this_frame, this_cache);
 
   if (!cache->base_p)
-    return;
-
-  (*this_id) = frame_id_build (cache->base + 16, get_frame_pc (this_frame));
+    (*this_id) = frame_id_build_unavailable_stack (get_frame_pc (this_frame));
+  else if (cache->base == 0)
+    {
+      /* This marks the outermost frame.  */
+      return;
+    }
+  else
+    (*this_id) = frame_id_build (cache->base + 16, get_frame_pc (this_frame));
 }
 
 static struct value *
@@ -2699,9 +2705,9 @@ amd64_epilogue_frame_this_id (struct fra
 							       this_cache);
 
   if (!cache->base_p)
-    return;
-
-  (*this_id) = frame_id_build (cache->base + 8, cache->pc);
+    (*this_id) = frame_id_build_unavailable_stack (cache->pc);
+  else
+    (*this_id) = frame_id_build (cache->base + 8, cache->pc);
 }
 
 static const struct frame_unwind amd64_epilogue_frame_unwind =
Index: gdb-7.6.1/gdb/dwarf2-frame.c
===================================================================
--- gdb-7.6.1.orig/gdb/dwarf2-frame.c
+++ gdb-7.6.1/gdb/dwarf2-frame.c
@@ -1265,12 +1265,11 @@ dwarf2_frame_this_id (struct frame_info
     dwarf2_frame_cache (this_frame, this_cache);
 
   if (cache->unavailable_retaddr)
+    (*this_id) = frame_id_build_unavailable_stack (get_frame_func (this_frame));
+  else if (cache->undefined_retaddr)
     return;
-
-  if (cache->undefined_retaddr)
-    return;
-
-  (*this_id) = frame_id_build (cache->cfa, get_frame_func (this_frame));
+  else
+    (*this_id) = frame_id_build (cache->cfa, get_frame_func (this_frame));
 }
 
 static struct value *
Index: gdb-7.6.1/gdb/frame.c
===================================================================
--- gdb-7.6.1.orig/gdb/frame.c
+++ gdb-7.6.1/gdb/frame.c
@@ -147,10 +147,11 @@ frame_addr_hash (const void *ap)
   const struct frame_id f_id = frame->this_id.value;
   hashval_t hash = 0;
 
-  gdb_assert (f_id.stack_addr_p || f_id.code_addr_p
+  gdb_assert (f_id.stack_status != FID_STACK_INVALID
+	      || f_id.code_addr_p
 	      || f_id.special_addr_p);
 
-  if (f_id.stack_addr_p)
+  if (f_id.stack_status == FID_STACK_VALID)
     hash = iterative_hash (&f_id.stack_addr,
 			   sizeof (f_id.stack_addr), hash);
   if (f_id.code_addr_p)
@@ -290,13 +291,23 @@ void
 fprint_frame_id (struct ui_file *file, struct frame_id id)
 {
   fprintf_unfiltered (file, "{");
-  fprint_field (file, "stack", id.stack_addr_p, id.stack_addr);
+
+  if (id.stack_status == FID_STACK_INVALID)
+    fprintf_unfiltered (file, "!stack");
+  else if (id.stack_status == FID_STACK_UNAVAILABLE)
+    fprintf_unfiltered (file, "stack=<unavailable>");
+  else
+    fprintf_unfiltered (file, "stack=%s", hex_string (id.stack_addr));
   fprintf_unfiltered (file, ",");
+
   fprint_field (file, "code", id.code_addr_p, id.code_addr);
   fprintf_unfiltered (file, ",");
+
   fprint_field (file, "special", id.special_addr_p, id.special_addr);
+
   if (id.artificial_depth)
     fprintf_unfiltered (file, ",artificial=%d", id.artificial_depth);
+
   fprintf_unfiltered (file, "}");
 }
 
@@ -446,7 +457,7 @@ frame_unwind_caller_id (struct frame_inf
 }
 
 const struct frame_id null_frame_id; /* All zeros.  */
-const struct frame_id outer_frame_id = { 0, 0, 0, 0, 0, 1, 0 };
+const struct frame_id outer_frame_id = { 0, 0, 0, FID_STACK_INVALID, 0, 1, 0 };
 
 struct frame_id
 frame_id_build_special (CORE_ADDR stack_addr, CORE_ADDR code_addr,
@@ -455,7 +466,7 @@ frame_id_build_special (CORE_ADDR stack_
   struct frame_id id = null_frame_id;
 
   id.stack_addr = stack_addr;
-  id.stack_addr_p = 1;
+  id.stack_status = FID_STACK_VALID;
   id.code_addr = code_addr;
   id.code_addr_p = 1;
   id.special_addr = special_addr;
@@ -463,13 +474,26 @@ frame_id_build_special (CORE_ADDR stack_
   return id;
 }
 
+/* See frame.h.  */
+
+struct frame_id
+frame_id_build_unavailable_stack (CORE_ADDR code_addr)
+{
+  struct frame_id id = null_frame_id;
+
+  id.stack_status = FID_STACK_UNAVAILABLE;
+  id.code_addr = code_addr;
+  id.code_addr_p = 1;
+  return id;
+}
+
 struct frame_id
 frame_id_build (CORE_ADDR stack_addr, CORE_ADDR code_addr)
 {
   struct frame_id id = null_frame_id;
 
   id.stack_addr = stack_addr;
-  id.stack_addr_p = 1;
+  id.stack_status = FID_STACK_VALID;
   id.code_addr = code_addr;
   id.code_addr_p = 1;
   return id;
@@ -481,7 +505,7 @@ frame_id_build_wild (CORE_ADDR stack_add
   struct frame_id id = null_frame_id;
 
   id.stack_addr = stack_addr;
-  id.stack_addr_p = 1;
+  id.stack_status = FID_STACK_VALID;
   return id;
 }
 
@@ -491,7 +515,7 @@ frame_id_p (struct frame_id l)
   int p;
 
   /* The frame is valid iff it has a valid stack address.  */
-  p = l.stack_addr_p;
+  p = l.stack_status != FID_STACK_INVALID;
   /* outer_frame_id is also valid.  */
   if (!p && memcmp (&l, &outer_frame_id, sizeof (l)) == 0)
     p = 1;
@@ -518,19 +542,20 @@ frame_id_eq (struct frame_id l, struct f
 {
   int eq;
 
-  if (!l.stack_addr_p && l.special_addr_p
-      && !r.stack_addr_p && r.special_addr_p)
+  if (l.stack_status == FID_STACK_INVALID && l.special_addr_p
+      && r.stack_status == FID_STACK_INVALID && r.special_addr_p)
     /* The outermost frame marker is equal to itself.  This is the
        dodgy thing about outer_frame_id, since between execution steps
        we might step into another function - from which we can't
        unwind either.  More thought required to get rid of
        outer_frame_id.  */
     eq = 1;
-  else if (!l.stack_addr_p || !r.stack_addr_p)
+  else if (l.stack_status == FID_STACK_INVALID
+	   || r.stack_status == FID_STACK_INVALID)
     /* Like a NaN, if either ID is invalid, the result is false.
        Note that a frame ID is invalid iff it is the null frame ID.  */
     eq = 0;
-  else if (l.stack_addr != r.stack_addr)
+  else if (l.stack_status != r.stack_status || l.stack_addr != r.stack_addr)
     /* If .stack addresses are different, the frames are different.  */
     eq = 0;
   else if (l.code_addr_p && r.code_addr_p && l.code_addr != r.code_addr)
@@ -597,8 +622,9 @@ frame_id_inner (struct gdbarch *gdbarch,
 {
   int inner;
 
-  if (!l.stack_addr_p || !r.stack_addr_p)
-    /* Like NaN, any operation involving an invalid ID always fails.  */
+  if (l.stack_status != FID_STACK_VALID || r.stack_status != FID_STACK_VALID)
+    /* Like NaN, any operation involving an invalid ID always fails.
+       Likewise if either ID has an unavailable stack address.  */
     inner = 0;
   else if (l.artificial_depth > r.artificial_depth
 	   && l.stack_addr == r.stack_addr
Index: gdb-7.6.1/gdb/frame.h
===================================================================
--- gdb-7.6.1.orig/gdb/frame.h
+++ gdb-7.6.1/gdb/frame.h
@@ -76,6 +76,23 @@ struct block;
 struct gdbarch;
 struct ui_file;
 
+/* Status of a given frame's stack.  */
+
+enum frame_id_stack_status
+{
+  /* Stack address is invalid.  E.g., this frame is the outermost
+     (i.e., _start), and the stack hasn't been setup yet.  */
+  FID_STACK_INVALID = 0,
+
+  /* Stack address is valid, and is found in the stack_addr field.  */
+  FID_STACK_VALID = 1,
+
+  /* Stack address is unavailable.  I.e., there's a valid stack, but
+     we don't know where it is (because memory or registers we'd
+     compute it from were not collected).  */
+  FID_STACK_UNAVAILABLE = -1
+};
+
 /* The frame object.  */
 
 struct frame_info;
@@ -97,8 +114,9 @@ struct frame_id
      function pointer register or stack pointer register.  They are
      wrong.
 
-     This field is valid only if stack_addr_p is true.  Otherwise, this
-     frame represents the null frame.  */
+     This field is valid only if frame_id.stack_status is
+     FID_STACK_VALID.  It will be 0 for other
+     FID_STACK_... statuses.  */
   CORE_ADDR stack_addr;
 
   /* The frame's code address.  This shall be constant through out the
@@ -129,7 +147,7 @@ struct frame_id
   CORE_ADDR special_addr;
 
   /* Flags to indicate the above fields have valid contents.  */
-  unsigned int stack_addr_p : 1;
+  ENUM_BITFIELD(frame_id_stack_status) stack_status : 2;
   unsigned int code_addr_p : 1;
   unsigned int special_addr_p : 1;
 
@@ -169,6 +187,12 @@ extern struct frame_id frame_id_build_sp
 					       CORE_ADDR code_addr,
 					       CORE_ADDR special_addr);
 
+/* Construct a frame ID representing a frame where the stack address
+   exists, but is unavailable.  CODE_ADDR is the frame's constant code
+   address (typically the entry point).  The special identifier
+   address is set to indicate a wild card.  */
+extern struct frame_id frame_id_build_unavailable_stack (CORE_ADDR code_addr);
+
 /* Construct a wild card frame ID.  The parameter is the frame's constant
    stack address (typically the outer-bound).  The code address as well
    as the special identifier address are set to indicate wild cards.  */
Index: gdb-7.6.1/gdb/i386-tdep.c
===================================================================
--- gdb-7.6.1.orig/gdb/i386-tdep.c
+++ gdb-7.6.1/gdb/i386-tdep.c
@@ -1846,12 +1846,17 @@ i386_frame_this_id (struct frame_info *t
 {
   struct i386_frame_cache *cache = i386_frame_cache (this_frame, this_cache);
 
-  /* This marks the outermost frame.  */
-  if (cache->base == 0)
-    return;
-
-  /* See the end of i386_push_dummy_call.  */
-  (*this_id) = frame_id_build (cache->base + 8, cache->pc);
+  if (!cache->base_p)
+    (*this_id) = frame_id_build_unavailable_stack (cache->pc);
+  else if (cache->base == 0)
+    {
+      /* This marks the outermost frame.  */
+    }
+  else
+    {
+      /* See the end of i386_push_dummy_call.  */
+      (*this_id) = frame_id_build (cache->base + 8, cache->pc);
+    }
 }
 
 static enum unwind_stop_reason
@@ -2032,9 +2037,9 @@ i386_epilogue_frame_this_id (struct fram
     i386_epilogue_frame_cache (this_frame, this_cache);
 
   if (!cache->base_p)
-    return;
-
-  (*this_id) = frame_id_build (cache->base + 8, cache->pc);
+    (*this_id) = frame_id_build_unavailable_stack (cache->pc);
+  else
+    (*this_id) = frame_id_build (cache->base + 8, cache->pc);
 }
 
 static struct value *
@@ -2226,10 +2231,12 @@ i386_sigtramp_frame_this_id (struct fram
     i386_sigtramp_frame_cache (this_frame, this_cache);
 
   if (!cache->base_p)
-    return;
-
-  /* See the end of i386_push_dummy_call.  */
-  (*this_id) = frame_id_build (cache->base + 8, get_frame_pc (this_frame));
+    (*this_id) = frame_id_build_unavailable_stack (get_frame_pc (this_frame));
+  else
+    {
+      /* See the end of i386_push_dummy_call.  */
+      (*this_id) = frame_id_build (cache->base + 8, get_frame_pc (this_frame));
+    }
 }
 
 static struct value *