diff mbox

[2/2] Report stop locations in inlined functions.

Message ID 20171030211841.15394-1-keiths@redhat.com
State New
Headers show

Commit Message

Keith Seitz Oct. 30, 2017, 9:18 p.m. UTC
On 10/27/2017 05:37 AM, Pedro Alves wrote:
> 
> Can you send me these as full patches, or send me the
> patch they apply on top of too?  I'll need to play with
> it a bit to understand it all better.

Sure, here they are. The two are from my stgit sandbox so #2 applies on top
of #1. [That allowed me to easily flip between the two and maintain tests.]

First up is the more "complex" solution, which does /not/ move the call
to bpstat_stop_status, but instead uses (a new function) build_bpstat_chain.
This is used to get at the current breakpoint (if any) to pass to
skip_inline_frames.

---
 gdb/breakpoint.c                       | 114 ++++++++++++++++-----------------
 gdb/breakpoint.h                       |  28 +++++++-
 gdb/infrun.c                           |  11 +++-
 gdb/inline-frame.c                     |  22 ++++++-
 gdb/inline-frame.h                     |   2 +-
 gdb/testsuite/gdb.opt/inline-break.c   |  50 +++++++++++++++
 gdb/testsuite/gdb.opt/inline-break.exp |  35 ++++++++++
 7 files changed, 199 insertions(+), 63 deletions(-)

Comments

Pedro Alves Dec. 1, 2017, 7:50 p.m. UTC | #1
Hi Keith,

On 10/30/2017 09:18 PM, Keith Seitz wrote:
> On 10/27/2017 05:37 AM, Pedro Alves wrote:
>>
>> Can you send me these as full patches, or send me the
>> patch they apply on top of too?  I'll need to play with
>> it a bit to understand it all better.
> 
> Sure, here they are. The two are from my stgit sandbox so #2 applies on top
> of #1. [That allowed me to easily flip between the two and maintain tests.]

Finally managed to play a bit with this.

> 
> First up is the more "complex" solution, which does /not/ move the call
> to bpstat_stop_status, but instead uses (a new function) build_bpstat_chain.
> This is used to get at the current breakpoint (if any) to pass to
> skip_inline_frames.

Yes, let's use this one.  The "simpler" one is too ad hoc.

>  	{
> -	  skip_inline_frames (ecs->ptid);
> +	  struct breakpoint *bpt = NULL;
> +
> +	  stop_chain = build_bpstat_chain (aspace, stop_pc, &ecs->ws);
> +	  if (stop_chain != NULL)
> +	    bpt = stop_chain->breakpoint_at;
> +
> +	  skip_inline_frames (ecs->ptid, bpt);

I think you should pass down the stop_chain directly to
skip_inline_frames.  This is because a stop can be explained
by more than one breakpoint (that's why it's a chain), and
the user breakpoint may not be the head of the chain.

>  
>  	  /* Re-fetch current thread's frame in case that invalidated
>  	     the frame cache.  */
> @@ -5945,7 +5952,7 @@ handle_signal_stop (struct execution_control_state *ecs)
>       handles this event.  */
>    ecs->event_thread->control.stop_bpstat
>      = bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
> -			  stop_pc, ecs->ptid, &ecs->ws);
> +			  stop_pc, ecs->ptid, &ecs->ws, stop_chain);
>  
>    /* Following in case break condition called a
>       function.  */
> diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
> index b6154cdcc5..f06ecf61a6 100644
> --- a/gdb/inline-frame.c
> +++ b/gdb/inline-frame.c
> @@ -301,7 +301,7 @@ block_starting_point_at (CORE_ADDR pc, const struct block *block)
>     user steps into them.  */
>  
>  void
> -skip_inline_frames (ptid_t ptid)
> +skip_inline_frames (ptid_t ptid, struct breakpoint *bpt)
>  {
>    CORE_ADDR this_pc;
>    const struct block *frame_block, *cur_block;
> @@ -327,7 +327,25 @@ skip_inline_frames (ptid_t ptid)
>  	      if (BLOCK_START (cur_block) == this_pc
>  		  || block_starting_point_at (this_pc, cur_block))
>  		{
> -		  skip_count++;
> +		  bool skip_this_frame = true;
> +
> +		  if (bpt != NULL
> +		      && user_breakpoint_p (bpt)
> +		      && breakpoint_address_is_meaningful (bpt))
> +		    {
> +		      for (bp_location *loc = bpt->loc; loc != NULL;
> +			   loc = loc->next)
> +			{
> +			  if (loc->address == this_pc)
> +			    {
> +			      skip_this_frame = false;
> +			      break;
> +			    }
> +			}
> +		    }

So here you'd check each breakpoint in the bpstat chain, not
just the head.

Also, to look at the locations, you should look at
bpstat->bp_location_at, not at the locations of the breakpoint,
because some of the locations of the breakpoint may be
disabled/not-inserted, for example.  There's one bpstat entry for
each _location_ that actually caused a stop, so checking bp_location_at
directly saves one loop.  (Though you'll add one loop back to walk
the bpstat chain, so it's a wash).  Careful to not
bpstat->bp_location_at->owner though, see comments in breakpoint.h.

(I think you could look at bpstat->bp_location_at->loc_type,
match against bp_loc_software_breakpoint / bp_loc_hardware_breakpoint,
instead of exposing breakpoint_address_is_meaningful.)

> +
> +		  if (skip_this_frame)
> +		    skip_count++;
>  		  last_sym = BLOCK_FUNCTION (cur_block);

Couldn't this break out of the outer loop if !skip_this_frame ?

Like:

		  if (!skip_this_frame)
		    break;

		  skip_count++;
		  last_sym = BLOCK_FUNCTION (cur_block);

?

Thanks,
Pedro Alves
diff mbox

Patch

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 10fccb8d2d..8d0a1cd2b7 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5401,58 +5401,25 @@  need_moribund_for_location_type (struct bp_location *loc)
 	      && !target_supports_stopped_by_hw_breakpoint ()));
 }
 
-
-/* Get a bpstat associated with having just stopped at address
-   BP_ADDR in thread PTID.
-
-   Determine whether we stopped at a breakpoint, etc, or whether we
-   don't understand this stop.  Result is a chain of bpstat's such
-   that:
-
-   if we don't understand the stop, the result is a null pointer.
-
-   if we understand why we stopped, the result is not null.
-
-   Each element of the chain refers to a particular breakpoint or
-   watchpoint at which we have stopped.  (We may have stopped for
-   several reasons concurrently.)
-
-   Each element of the chain has valid next, breakpoint_at,
-   commands, FIXME??? fields.  */
+/* See breakpoint.h.  */
 
 bpstat
-bpstat_stop_status (const address_space *aspace,
-		    CORE_ADDR bp_addr, ptid_t ptid,
+build_bpstat_chain (const address_space *aspace, CORE_ADDR bp_addr,
 		    const struct target_waitstatus *ws)
 {
-  struct breakpoint *b = NULL;
-  struct bp_location *bl;
-  struct bp_location *loc;
-  /* First item of allocated bpstat's.  */
+  struct breakpoint *b;
   bpstat bs_head = NULL, *bs_link = &bs_head;
-  /* Pointer to the last thing in the chain currently.  */
-  bpstat bs;
-  int ix;
-  int need_remove_insert;
-  int removed_any;
-
-  /* First, build the bpstat chain with locations that explain a
-     target stop, while being careful to not set the target running,
-     as that may invalidate locations (in particular watchpoint
-     locations are recreated).  Resuming will happen here with
-     breakpoint conditions or watchpoint expressions that include
-     inferior function calls.  */
 
   ALL_BREAKPOINTS (b)
     {
       if (!breakpoint_enabled (b))
 	continue;
 
-      for (bl = b->loc; bl != NULL; bl = bl->next)
+      for (bp_location *bl = b->loc; bl != NULL; bl = bl->next)
 	{
 	  /* For hardware watchpoints, we look only at the first
 	     location.  The watchpoint_check function will work on the
-	     entire expression, not the individual locations.  For
+
 	     read watchpoints, the watchpoints_triggered function has
 	     checked all locations already.  */
 	  if (b->type == bp_hardware_watchpoint && bl != b->loc)
@@ -5467,8 +5434,8 @@  bpstat_stop_status (const address_space *aspace,
 	  /* Come here if it's a watchpoint, or if the break address
 	     matches.  */
 
-	  bs = new bpstats (bl, &bs_link);	/* Alloc a bpstat to
-						   explain stop.  */
+	  bpstat bs = new bpstats (bl, &bs_link);	/* Alloc a bpstat to
+							   explain stop.  */
 
 	  /* Assume we stop.  Should we find a watchpoint that is not
 	     actually triggered, or if the condition of the breakpoint
@@ -5493,12 +5460,15 @@  bpstat_stop_status (const address_space *aspace,
   if (!target_supports_stopped_by_sw_breakpoint ()
       || !target_supports_stopped_by_hw_breakpoint ())
     {
-      for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
+      struct bp_location *loc;
+
+      for (int ix = 0;
+	   VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
 	{
 	  if (breakpoint_location_address_match (loc, aspace, bp_addr)
 	      && need_moribund_for_location_type (loc))
 	    {
-	      bs = new bpstats (loc, &bs_link);
+	      bpstat bs = new bpstats (loc, &bs_link);
 	      /* For hits of moribund locations, we should just proceed.  */
 	      bs->stop = 0;
 	      bs->print = 0;
@@ -5507,6 +5477,49 @@  bpstat_stop_status (const address_space *aspace,
 	}
     }
 
+  return bs_head;
+}
+
+/* Get a bpstat associated with having just stopped at address
+   BP_ADDR in thread PTID.
+
+   Determine whether we stopped at a breakpoint, etc, or whether we
+   don't understand this stop.  Result is a chain of bpstat's such
+   that:
+
+   if we don't understand the stop, the result is a null pointer.
+
+   if we understand why we stopped, the result is not null.
+
+   Each element of the chain refers to a particular breakpoint or
+   watchpoint at which we have stopped.  (We may have stopped for
+   several reasons concurrently.)
+
+   Each element of the chain has valid next, breakpoint_at,
+   commands, FIXME??? fields.  */
+
+bpstat
+bpstat_stop_status (const address_space *aspace,
+		    CORE_ADDR bp_addr, ptid_t ptid,
+		    const struct target_waitstatus *ws,
+		    bpstat stop_chain)
+{
+  struct breakpoint *b = NULL;
+  /* First item of allocated bpstat's.  */
+  bpstat bs_head = stop_chain;
+  bpstat bs;
+  int need_remove_insert;
+  int removed_any;
+
+  /* First, build the bpstat chain with locations that explain a
+     target stop, while being careful to not set the target running,
+     as that may invalidate locations (in particular watchpoint
+     locations are recreated).  Resuming will happen here with
+     breakpoint conditions or watchpoint expressions that include
+     inferior function calls.  */
+  if (bs_head == NULL)
+    bs_head = build_bpstat_chain (aspace, bp_addr, ws);
+
   /* A bit of special processing for shlib breakpoints.  We need to
      process solib loading here, so that the lists of loaded and
      unloaded libraries are correct before we handle "catch load" and
@@ -6827,22 +6840,9 @@  describe_other_breakpoints (struct gdbarch *gdbarch,
 }
 
 
-/* Return true iff it is meaningful to use the address member of
-   BPT locations.  For some breakpoint types, the locations' address members
-   are irrelevant and it makes no sense to attempt to compare them to other
-   addresses (or use them for any other purpose either).
-
-   More specifically, each of the following breakpoint types will
-   always have a zero valued location address and we don't want to mark
-   breakpoints of any of these types to be a duplicate of an actual
-   breakpoint location at address zero:
-
-      bp_watchpoint
-      bp_catchpoint
-
-*/
+/* See breakpoint.h.  */
 
-static int
+bool
 breakpoint_address_is_meaningful (struct breakpoint *bpt)
 {
   enum bptype type = bpt->type;
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 9a9433bd66..df23d442e9 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -917,9 +917,35 @@  extern void bpstat_clear (bpstat *);
    is part of the bpstat is copied as well.  */
 extern bpstat bpstat_copy (bpstat);
 
+/* Build the bstat chain for the stop information given by ASPACE,
+   BP_ADDR, and WS.  Returns the head of the bpstat chain.  */
+
+extern bpstat build_bpstat_chain (const address_space *aspace,
+				  CORE_ADDR bp_addr,
+				  const struct target_waitstatus *ws);
+
 extern bpstat bpstat_stop_status (const address_space *aspace,
 				  CORE_ADDR pc, ptid_t ptid,
-				  const struct target_waitstatus *ws);
+				  const struct target_waitstatus *ws,
+				  bpstat stop_chain = NULL);
+
+/* Return true iff it is meaningful to use the address member of
+   BPT locations.  For some breakpoint types, the locations' address members
+   are irrelevant and it makes no sense to attempt to compare them to other
+   addresses (or use them for any other purpose either).
+
+   More specifically, each of the following breakpoint types will
+   always have a zero valued location address and we don't want to mark
+   breakpoints of any of these types to be a duplicate of an actual
+   breakpoint location at address zero:
+
+      bp_watchpoint
+      bp_catchpoint
+
+*/
+
+extern bool breakpoint_address_is_meaningful (struct breakpoint *bpt);
+
 
 /* This bpstat_what stuff tells wait_for_inferior what to do with a
    breakpoint (a challenging task).
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 858ffa1175..5f009334fd 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5865,6 +5865,7 @@  handle_signal_stop (struct execution_control_state *ecs)
   ecs->event_thread->control.stop_step = 0;
   stop_print_frame = 1;
   stopped_by_random_signal = 0;
+  bpstat stop_chain = NULL;
 
   /* Hide inlined functions starting here, unless we just performed stepi or
      nexti.  After stepi and nexti, always show the innermost frame (not any
@@ -5896,7 +5897,13 @@  handle_signal_stop (struct execution_control_state *ecs)
 					     ecs->event_thread->prev_pc,
 					     &ecs->ws)))
 	{
-	  skip_inline_frames (ecs->ptid);
+	  struct breakpoint *bpt = NULL;
+
+	  stop_chain = build_bpstat_chain (aspace, stop_pc, &ecs->ws);
+	  if (stop_chain != NULL)
+	    bpt = stop_chain->breakpoint_at;
+
+	  skip_inline_frames (ecs->ptid, bpt);
 
 	  /* Re-fetch current thread's frame in case that invalidated
 	     the frame cache.  */
@@ -5945,7 +5952,7 @@  handle_signal_stop (struct execution_control_state *ecs)
      handles this event.  */
   ecs->event_thread->control.stop_bpstat
     = bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
-			  stop_pc, ecs->ptid, &ecs->ws);
+			  stop_pc, ecs->ptid, &ecs->ws, stop_chain);
 
   /* Following in case break condition called a
      function.  */
diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
index b6154cdcc5..f06ecf61a6 100644
--- a/gdb/inline-frame.c
+++ b/gdb/inline-frame.c
@@ -301,7 +301,7 @@  block_starting_point_at (CORE_ADDR pc, const struct block *block)
    user steps into them.  */
 
 void
-skip_inline_frames (ptid_t ptid)
+skip_inline_frames (ptid_t ptid, struct breakpoint *bpt)
 {
   CORE_ADDR this_pc;
   const struct block *frame_block, *cur_block;
@@ -327,7 +327,25 @@  skip_inline_frames (ptid_t ptid)
 	      if (BLOCK_START (cur_block) == this_pc
 		  || block_starting_point_at (this_pc, cur_block))
 		{
-		  skip_count++;
+		  bool skip_this_frame = true;
+
+		  if (bpt != NULL
+		      && user_breakpoint_p (bpt)
+		      && breakpoint_address_is_meaningful (bpt))
+		    {
+		      for (bp_location *loc = bpt->loc; loc != NULL;
+			   loc = loc->next)
+			{
+			  if (loc->address == this_pc)
+			    {
+			      skip_this_frame = false;
+			      break;
+			    }
+			}
+		    }
+
+		  if (skip_this_frame)
+		    skip_count++;
 		  last_sym = BLOCK_FUNCTION (cur_block);
 		}
 	      else
diff --git a/gdb/inline-frame.h b/gdb/inline-frame.h
index e6fe29db1d..8afaf6c945 100644
--- a/gdb/inline-frame.h
+++ b/gdb/inline-frame.h
@@ -31,7 +31,7 @@  extern const struct frame_unwind inline_frame_unwind;
    Frames for the hidden functions will not appear in the backtrace until the
    user steps into them.  */
 
-void skip_inline_frames (ptid_t ptid);
+void skip_inline_frames (ptid_t ptid, struct breakpoint *bpt);
 
 /* Forget about any hidden inlined functions in PTID, which is new or
    about to be resumed.  If PTID is minus_one_ptid, forget about all
diff --git a/gdb/testsuite/gdb.opt/inline-break.c b/gdb/testsuite/gdb.opt/inline-break.c
index 2616a0e3d7..a42bc0bb44 100644
--- a/gdb/testsuite/gdb.opt/inline-break.c
+++ b/gdb/testsuite/gdb.opt/inline-break.c
@@ -128,6 +128,54 @@  func8a (int x)
   return func8b (x * 31);
 }
 
+static inline ATTR int
+inline_func1 (int x)
+{
+  int y = 1;			/* inline_func1  */
+
+  return y + x;
+}
+
+static int
+not_inline_func1 (int x)
+{
+  int y = 2;			/* not_inline_func1  */
+
+  return y + inline_func1 (x);
+}
+
+inline ATTR int
+inline_func2 (int x)
+{
+  int y = 3;			/* inline_func2  */
+
+  return y + not_inline_func1 (x);
+}
+
+int
+not_inline_func2 (int x)
+{
+  int y = 4;			/* not_inline_func2  */
+
+  return y + inline_func2 (x);
+}
+
+static inline ATTR int
+inline_func3 (int x)
+{
+  int y = 5;			/* inline_func3  */
+
+  return y + not_inline_func2 (x);
+}
+
+static int
+not_inline_func3 (int x)
+{
+  int y = 6;			/* not_inline_func3  */
+
+  return y + inline_func3 (x);
+}
+
 /* Entry point.  */
 
 int
@@ -155,5 +203,7 @@  main (int argc, char *argv[])
 
   x = func8a (x) + func8b (x);
 
+  x = not_inline_func3 (-21);
+
   return x;
 }
diff --git a/gdb/testsuite/gdb.opt/inline-break.exp b/gdb/testsuite/gdb.opt/inline-break.exp
index 20bae071b3..0a990fbfd5 100644
--- a/gdb/testsuite/gdb.opt/inline-break.exp
+++ b/gdb/testsuite/gdb.opt/inline-break.exp
@@ -185,4 +185,39 @@  for {set i 1} {$i <= [array size results]} {incr i} {
     gdb_test "info break $i" $results($i)
 }
 
+# Start us running.
+if {![runto main]} {
+    untested "could not run to main"
+    return -1
+}
+
+# Insert breakpoints for all inline_func? and not_inline_func? and check
+# that we actually stop where we think we should.
+
+for {set i 1} {$i < 4} {incr i} {
+    foreach inline {"not_inline" "inline"} {
+	gdb_breakpoint "${inline}_func$i" message
+    }
+}
+
+set ws {[\r\n\t ]+}
+set backtrace [list "(in|at)? main"]
+for {set i 3} {$i > 0} {incr i -1} {
+
+    foreach inline {"not_inline" "inline"} {
+
+	# Check that we stop at the correct location and print out
+	# the (possibly) inlined frames.
+	set num [gdb_get_line_number "/* ${inline}_func$i  */"]
+	set pattern ".*/$srcfile:$num${ws}.*$num${ws}int y = $decimal;"
+	append pattern "${ws}/\\\* ${inline}_func$i  \\\*/"
+	send_log "Expecting $pattern\n"
+	gdb_continue_to_breakpoint "${inline}_func$i" $pattern
+
+	# Also check for the correct backtrace.
+	set backtrace [linsert $backtrace 0 "(in|at)?${ws}${inline}_func$i"]
+	gdb_test_sequence "bt" "bt stopped in ${inline}_func$i" $backtrace
+    }
+}
+
 unset -nocomplain results
-- 
2.13.6


The second "simpler" patch does move the call to bpstat_stop_status, but
since this will call bpstat_check_breakpoint_conditions, a call to
clear_inline_frame_state needed to be inserted (as previously described).

---
 gdb/breakpoint.c | 99 +++++++++++++++++++++++++-------------------------------
 gdb/breakpoint.h | 11 +------
 gdb/infrun.c     | 18 +++++------
 3 files changed, 53 insertions(+), 75 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 8d0a1cd2b7..8510d4bda0 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -68,6 +68,7 @@ 
 #include "format.h"
 #include "thread-fsm.h"
 #include "tid-parse.h"
+#include "inline-frame.h"
 
 /* readline include files */
 #include "readline/readline.h"
@@ -5359,6 +5360,7 @@  bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
 	    }
 	  CATCH (ex, RETURN_MASK_ALL)
 	    {
+	      clear_inline_frame_state (ptid);
 	      exception_fprintf (gdb_stderr, ex,
 				 "Error in testing breakpoint condition:\n");
 	    }
@@ -5401,25 +5403,58 @@  need_moribund_for_location_type (struct bp_location *loc)
 	      && !target_supports_stopped_by_hw_breakpoint ()));
 }
 
-/* See breakpoint.h.  */
+
+/* Get a bpstat associated with having just stopped at address
+   BP_ADDR in thread PTID.
+
+   Determine whether we stopped at a breakpoint, etc, or whether we
+   don't understand this stop.  Result is a chain of bpstat's such
+   that:
+
+   if we don't understand the stop, the result is a null pointer.
+
+   if we understand why we stopped, the result is not null.
+
+   Each element of the chain refers to a particular breakpoint or
+   watchpoint at which we have stopped.  (We may have stopped for
+   several reasons concurrently.)
+
+   Each element of the chain has valid next, breakpoint_at,
+   commands, FIXME??? fields.  */
 
 bpstat
-build_bpstat_chain (const address_space *aspace, CORE_ADDR bp_addr,
+bpstat_stop_status (const address_space *aspace,
+		    CORE_ADDR bp_addr, ptid_t ptid,
 		    const struct target_waitstatus *ws)
 {
-  struct breakpoint *b;
+  struct breakpoint *b = NULL;
+  struct bp_location *bl;
+  struct bp_location *loc;
+  /* First item of allocated bpstat's.  */
   bpstat bs_head = NULL, *bs_link = &bs_head;
+  /* Pointer to the last thing in the chain currently.  */
+  bpstat bs;
+  int ix;
+  int need_remove_insert;
+  int removed_any;
+
+  /* First, build the bpstat chain with locations that explain a
+     target stop, while being careful to not set the target running,
+     as that may invalidate locations (in particular watchpoint
+     locations are recreated).  Resuming will happen here with
+     breakpoint conditions or watchpoint expressions that include
+     inferior function calls.  */
 
   ALL_BREAKPOINTS (b)
     {
       if (!breakpoint_enabled (b))
 	continue;
 
-      for (bp_location *bl = b->loc; bl != NULL; bl = bl->next)
+      for (bl = b->loc; bl != NULL; bl = bl->next)
 	{
 	  /* For hardware watchpoints, we look only at the first
 	     location.  The watchpoint_check function will work on the
-
+	     entire expression, not the individual locations.  For
 	     read watchpoints, the watchpoints_triggered function has
 	     checked all locations already.  */
 	  if (b->type == bp_hardware_watchpoint && bl != b->loc)
@@ -5434,8 +5469,8 @@  build_bpstat_chain (const address_space *aspace, CORE_ADDR bp_addr,
 	  /* Come here if it's a watchpoint, or if the break address
 	     matches.  */
 
-	  bpstat bs = new bpstats (bl, &bs_link);	/* Alloc a bpstat to
-							   explain stop.  */
+	  bs = new bpstats (bl, &bs_link);	/* Alloc a bpstat to
+						   explain stop.  */
 
 	  /* Assume we stop.  Should we find a watchpoint that is not
 	     actually triggered, or if the condition of the breakpoint
@@ -5460,15 +5495,12 @@  build_bpstat_chain (const address_space *aspace, CORE_ADDR bp_addr,
   if (!target_supports_stopped_by_sw_breakpoint ()
       || !target_supports_stopped_by_hw_breakpoint ())
     {
-      struct bp_location *loc;
-
-      for (int ix = 0;
-	   VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
+      for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
 	{
 	  if (breakpoint_location_address_match (loc, aspace, bp_addr)
 	      && need_moribund_for_location_type (loc))
 	    {
-	      bpstat bs = new bpstats (loc, &bs_link);
+	      bs = new bpstats (loc, &bs_link);
 	      /* For hits of moribund locations, we should just proceed.  */
 	      bs->stop = 0;
 	      bs->print = 0;
@@ -5477,49 +5509,6 @@  build_bpstat_chain (const address_space *aspace, CORE_ADDR bp_addr,
 	}
     }
 
-  return bs_head;
-}
-
-/* Get a bpstat associated with having just stopped at address
-   BP_ADDR in thread PTID.
-
-   Determine whether we stopped at a breakpoint, etc, or whether we
-   don't understand this stop.  Result is a chain of bpstat's such
-   that:
-
-   if we don't understand the stop, the result is a null pointer.
-
-   if we understand why we stopped, the result is not null.
-
-   Each element of the chain refers to a particular breakpoint or
-   watchpoint at which we have stopped.  (We may have stopped for
-   several reasons concurrently.)
-
-   Each element of the chain has valid next, breakpoint_at,
-   commands, FIXME??? fields.  */
-
-bpstat
-bpstat_stop_status (const address_space *aspace,
-		    CORE_ADDR bp_addr, ptid_t ptid,
-		    const struct target_waitstatus *ws,
-		    bpstat stop_chain)
-{
-  struct breakpoint *b = NULL;
-  /* First item of allocated bpstat's.  */
-  bpstat bs_head = stop_chain;
-  bpstat bs;
-  int need_remove_insert;
-  int removed_any;
-
-  /* First, build the bpstat chain with locations that explain a
-     target stop, while being careful to not set the target running,
-     as that may invalidate locations (in particular watchpoint
-     locations are recreated).  Resuming will happen here with
-     breakpoint conditions or watchpoint expressions that include
-     inferior function calls.  */
-  if (bs_head == NULL)
-    bs_head = build_bpstat_chain (aspace, bp_addr, ws);
-
   /* A bit of special processing for shlib breakpoints.  We need to
      process solib loading here, so that the lists of loaded and
      unloaded libraries are correct before we handle "catch load" and
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index df23d442e9..495b961bd3 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -917,17 +917,9 @@  extern void bpstat_clear (bpstat *);
    is part of the bpstat is copied as well.  */
 extern bpstat bpstat_copy (bpstat);
 
-/* Build the bstat chain for the stop information given by ASPACE,
-   BP_ADDR, and WS.  Returns the head of the bpstat chain.  */
-
-extern bpstat build_bpstat_chain (const address_space *aspace,
-				  CORE_ADDR bp_addr,
-				  const struct target_waitstatus *ws);
-
 extern bpstat bpstat_stop_status (const address_space *aspace,
 				  CORE_ADDR pc, ptid_t ptid,
-				  const struct target_waitstatus *ws,
-				  bpstat stop_chain = NULL);
+				  const struct target_waitstatus *ws);
 
 /* Return true iff it is meaningful to use the address member of
    BPT locations.  For some breakpoint types, the locations' address members
@@ -943,7 +935,6 @@  extern bpstat bpstat_stop_status (const address_space *aspace,
       bp_catchpoint
 
 */
-
 extern bool breakpoint_address_is_meaningful (struct breakpoint *bpt);
 
 
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 5f009334fd..0071f7e7b4 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5865,7 +5865,12 @@  handle_signal_stop (struct execution_control_state *ecs)
   ecs->event_thread->control.stop_step = 0;
   stop_print_frame = 1;
   stopped_by_random_signal = 0;
-  bpstat stop_chain = NULL;
+
+  /* See if there is a breakpoint/watchpoint/catchpoint/etc. that
+     handles this event.  */
+  ecs->event_thread->control.stop_bpstat
+    = bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
+			  stop_pc, ecs->ptid, &ecs->ws);
 
   /* Hide inlined functions starting here, unless we just performed stepi or
      nexti.  After stepi and nexti, always show the innermost frame (not any
@@ -5899,9 +5904,8 @@  handle_signal_stop (struct execution_control_state *ecs)
 	{
 	  struct breakpoint *bpt = NULL;
 
-	  stop_chain = build_bpstat_chain (aspace, stop_pc, &ecs->ws);
-	  if (stop_chain != NULL)
-	    bpt = stop_chain->breakpoint_at;
+	  if (ecs->event_thread->control.stop_bpstat != NULL)
+	    bpt = ecs->event_thread->control.stop_bpstat->breakpoint_at;
 
 	  skip_inline_frames (ecs->ptid, bpt);
 
@@ -5948,12 +5952,6 @@  handle_signal_stop (struct execution_control_state *ecs)
 	}
     }
 
-  /* See if there is a breakpoint/watchpoint/catchpoint/etc. that
-     handles this event.  */
-  ecs->event_thread->control.stop_bpstat
-    = bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
-			  stop_pc, ecs->ptid, &ecs->ws, stop_chain);
-
   /* Following in case break condition called a
      function.  */
   stop_print_frame = 1;