[4/4] Remove ALL_BREAKPOINTS_SAFE

Message ID 20230416-remove-all-macros-v1-4-f118956faafe@tromey.com
State New
Headers
Series Remove remaining ALL_ iterator macros |

Commit Message

Tom Tromey April 16, 2023, 4:28 p.m. UTC
  There's just a single remaining use of the ALL_BREAKPOINTS_SAFE macro;
this patch replaces it with a for-each and an explicit temporary
variable.
---
 gdb/breakpoint.c | 134 ++++++++++++++++++++++++++-----------------------------
 1 file changed, 63 insertions(+), 71 deletions(-)
  

Comments

Simon Marchi May 8, 2023, 2:24 a.m. UTC | #1
On 4/16/23 12:28, Tom Tromey wrote:
> There's just a single remaining use of the ALL_BREAKPOINTS_SAFE macro;
> this patch replaces it with a for-each and an explicit temporary
> variable.

Starting with this patch, I see:

(gdb) PASS: gdb.base/stale-infcall.exp: continue to breakpoint: break-run1
print infcall ()
=================================================================
==47472==ERROR: AddressSanitizer: heap-use-after-free on address 0x611000034980 at pc 0x563f7012c7bc bp 0x7ffdf3804d70 sp 0x7ffdf3804d60
READ of size 8 at 0x611000034980 thread T0
    #0 0x563f7012c7bb in next_iterator<breakpoint>::operator++() /home/smarchi/src/binutils-gdb/gdb/../gdbsupport/next-iterator.h:66
    #1 0x563f702ce8c0 in basic_safe_iterator<next_iterator<breakpoint> >::operator++() /home/smarchi/src/binutils-gdb/gdb/../gdbsupport/safe-iterator.h:84
    #2 0x563f7021522a in check_longjmp_breakpoint_for_call_dummy(thread_info*) /home/smarchi/src/binutils-gdb/gdb/breakpoint.c:7611
    #3 0x563f714567b1 in process_event_stop_test /home/smarchi/src/binutils-gdb/gdb/infrun.c:6881
    #4 0x563f71454e07 in handle_signal_stop /home/smarchi/src/binutils-gdb/gdb/infrun.c:6769
    #5 0x563f7144b680 in handle_inferior_event /home/smarchi/src/binutils-gdb/gdb/infrun.c:6023
    #6 0x563f71436165 in fetch_inferior_event() /home/smarchi/src/binutils-gdb/gdb/infrun.c:4387
    #7 0x563f7136ff51 in inferior_event_handler(inferior_event_type) /home/smarchi/src/binutils-gdb/gdb/inf-loop.c:42
    #8 0x563f7168038d in handle_target_event /home/smarchi/src/binutils-gdb/gdb/linux-nat.c:4219
    #9 0x563f72fccb6d in handle_file_event /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:573
    #10 0x563f72fcd503 in gdb_wait_for_event /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:694
    #11 0x563f72fcaf2b in gdb_do_one_event(int) /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:217
    #12 0x563f7262b9bb in wait_sync_command_done() /home/smarchi/src/binutils-gdb/gdb/top.c:426
    #13 0x563f7137a7c3 in run_inferior_call /home/smarchi/src/binutils-gdb/gdb/infcall.c:650
    #14 0x563f71381295 in call_function_by_hand_dummy(value*, type*, gdb::array_view<value*>, void (*)(void*, int), void*) /home/smarchi/src/binutils-gdb/gdb/infcall.c:1332
    #15 0x563f7137c0e2 in call_function_by_hand(value*, type*, gdb::array_view<value*>) /home/smarchi/src/binutils-gdb/gdb/infcall.c:780
    #16 0x563f70fe5960 in evaluate_subexp_do_call(expression*, noside, value*, gdb::array_view<value*>, char const*, type*) /home/smarchi/src/binutils-gdb/gdb/eval.c:649
    #17 0x563f70fe6617 in expr::operation::evaluate_funcall(type*, expression*, noside, char const*, std::__debug::vector<std::unique_ptr<expr::operation, std::default_delete<expr::operation> >, std::allocator<std::unique_ptr<expr::operation, std::default_delete<expr::operation> > > > const&) /home/smarchi/src/binutils-gdb/gdb/eval.c:677
    #18 0x563f6fd19668 in expr::operation::evaluate_funcall(type*, expression*, noside, std::__debug::vector<std::unique_ptr<expr::operation, std::default_delete<expr::operation> >, std::allocator<std::unique_ptr<expr::operation, std::default_delete<expr::operation> > > > const&) /home/smarchi/src/binutils-gdb/gdb/expression.h:136
    #19 0x563f70fe6bba in expr::var_value_operation::evaluate_funcall(type*, expression*, noside, std::__debug::vector<std::unique_ptr<expr::operation, std::default_delete<expr::operation> >, std::allocator<std::unique_ptr<expr::operation, std::default_delete<expr::operation> > > > const&) /home/smarchi/src/binutils-gdb/gdb/eval.c:689
    #20 0x563f704b71dc in expr::funcall_operation::evaluate(type*, expression*, noside) /home/smarchi/src/binutils-gdb/gdb/expop.h:2219
    #21 0x563f70fe0f02 in expression::evaluate(type*, noside) /home/smarchi/src/binutils-gdb/gdb/eval.c:110
    #22 0x563f71b1373e in process_print_command_args /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1319
    #23 0x563f71b1391b in print_command_1 /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1332
    #24 0x563f71b147ec in print_command /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1465
    #25 0x563f706029b8 in do_simple_func /home/smarchi/src/binutils-gdb/gdb/cli/cli-decode.c:95
    #26 0x563f7061972a in cmd_func(cmd_list_element*, char const*, int) /home/smarchi/src/binutils-gdb/gdb/cli/cli-decode.c:2735
    #27 0x563f7262d0ef in execute_command(char const*, int) /home/smarchi/src/binutils-gdb/gdb/top.c:572
    #28 0x563f7100ed9c in command_handler(char const*) /home/smarchi/src/binutils-gdb/gdb/event-top.c:543
    #29 0x563f7101014b in command_line_handler(std::unique_ptr<char, gdb::xfree_deleter<char> >&&) /home/smarchi/src/binutils-gdb/gdb/event-top.c:779
    #30 0x563f72777942 in tui_command_line_handler /home/smarchi/src/binutils-gdb/gdb/tui/tui-interp.c:104
    #31 0x563f7100d059 in gdb_rl_callback_handler /home/smarchi/src/binutils-gdb/gdb/event-top.c:250
    #32 0x7f5a80418246 in rl_callback_read_char (/usr/lib/libreadline.so.8+0x3b246) (BuildId: 092e91fc4361b0ef94561e3ae03a75f69398acbb)
    #33 0x563f7100ca06 in gdb_rl_callback_read_char_wrapper_noexcept /home/smarchi/src/binutils-gdb/gdb/event-top.c:192
    #34 0x563f7100cc5e in gdb_rl_callback_read_char_wrapper /home/smarchi/src/binutils-gdb/gdb/event-top.c:225
    #35 0x563f728c70db in stdin_event_handler /home/smarchi/src/binutils-gdb/gdb/ui.c:155
    #36 0x563f72fccb6d in handle_file_event /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:573
    #37 0x563f72fcd503 in gdb_wait_for_event /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:694
    #38 0x563f72fcb15c in gdb_do_one_event(int) /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:264
    #39 0x563f7177ec1c in start_event_loop /home/smarchi/src/binutils-gdb/gdb/main.c:412
    #40 0x563f7177f12e in captured_command_loop /home/smarchi/src/binutils-gdb/gdb/main.c:476
    #41 0x563f717846e4 in captured_main /home/smarchi/src/binutils-gdb/gdb/main.c:1320
    #42 0x563f71784821 in gdb_main(captured_main_args*) /home/smarchi/src/binutils-gdb/gdb/main.c:1339
    #43 0x563f6fcedfbd in main /home/smarchi/src/binutils-gdb/gdb/gdb.c:32
    #44 0x7f5a7e43984f  (/usr/lib/libc.so.6+0x2384f) (BuildId: 2f005a79cd1a8e385972f5a102f16adba414d75e)
    #45 0x7f5a7e439909 in __libc_start_main (/usr/lib/libc.so.6+0x23909) (BuildId: 2f005a79cd1a8e385972f5a102f16adba414d75e)
    #46 0x563f6fcedd84 in _start (/home/smarchi/build/binutils-gdb/gdb/gdb+0xafb0d84) (BuildId: 50bd32e6e9d5e84543e9897b8faca34858ca3995)

0x611000034980 is located 0 bytes inside of 208-byte region [0x611000034980,0x611000034a50)
freed by thread T0 here:
    #0 0x7f5a7fce312a in operator delete(void*, unsigned long) /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_new_delete.cpp:164
    #1 0x563f702bd1fa in momentary_breakpoint::~momentary_breakpoint() /home/smarchi/src/binutils-gdb/gdb/breakpoint.c:304
    #2 0x563f702771c5 in delete_breakpoint(breakpoint*) /home/smarchi/src/binutils-gdb/gdb/breakpoint.c:12404
    #3 0x563f702150a7 in check_longjmp_breakpoint_for_call_dummy(thread_info*) /home/smarchi/src/binutils-gdb/gdb/breakpoint.c:7673
    #4 0x563f714567b1 in process_event_stop_test /home/smarchi/src/binutils-gdb/gdb/infrun.c:6881
    #5 0x563f71454e07 in handle_signal_stop /home/smarchi/src/binutils-gdb/gdb/infrun.c:6769
    #6 0x563f7144b680 in handle_inferior_event /home/smarchi/src/binutils-gdb/gdb/infrun.c:6023
    #7 0x563f71436165 in fetch_inferior_event() /home/smarchi/src/binutils-gdb/gdb/infrun.c:4387
    #8 0x563f7136ff51 in inferior_event_handler(inferior_event_type) /home/smarchi/src/binutils-gdb/gdb/inf-loop.c:42
    #9 0x563f7168038d in handle_target_event /home/smarchi/src/binutils-gdb/gdb/linux-nat.c:4219
    #10 0x563f72fccb6d in handle_file_event /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:573
    #11 0x563f72fcd503 in gdb_wait_for_event /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:694
    #12 0x563f72fcaf2b in gdb_do_one_event(int) /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:217
    #13 0x563f7262b9bb in wait_sync_command_done() /home/smarchi/src/binutils-gdb/gdb/top.c:426
    #14 0x563f7137a7c3 in run_inferior_call /home/smarchi/src/binutils-gdb/gdb/infcall.c:650
    #15 0x563f71381295 in call_function_by_hand_dummy(value*, type*, gdb::array_view<value*>, void (*)(void*, int), void*) /home/smarchi/src/binutils-gdb/gdb/infcall.c:1332
    #16 0x563f7137c0e2 in call_function_by_hand(value*, type*, gdb::array_view<value*>) /home/smarchi/src/binutils-gdb/gdb/infcall.c:780
    #17 0x563f70fe5960 in evaluate_subexp_do_call(expression*, noside, value*, gdb::array_view<value*>, char const*, type*) /home/smarchi/src/binutils-gdb/gdb/eval.c:649
    #18 0x563f70fe6617 in expr::operation::evaluate_funcall(type*, expression*, noside, char const*, std::__debug::vector<std::unique_ptr<expr::operation, std::default_delete<expr::operation> >, std::allocator<std::unique_ptr<expr::operation, std::default_delete<expr::operation> > > > const&) /home/smarchi/src/binutils-gdb/gdb/eval.c:677
    #19 0x563f6fd19668 in expr::operation::evaluate_funcall(type*, expression*, noside, std::__debug::vector<std::unique_ptr<expr::operation, std::default_delete<expr::operation> >, std::allocator<std::unique_ptr<expr::operation, std::default_delete<expr::operation> > > > const&) /home/smarchi/src/binutils-gdb/gdb/expression.h:136
    #20 0x563f70fe6bba in expr::var_value_operation::evaluate_funcall(type*, expression*, noside, std::__debug::vector<std::unique_ptr<expr::operation, std::default_delete<expr::operation> >, std::allocator<std::unique_ptr<expr::operation, std::default_delete<expr::operation> > > > const&) /home/smarchi/src/binutils-gdb/gdb/eval.c:689
    #21 0x563f704b71dc in expr::funcall_operation::evaluate(type*, expression*, noside) /home/smarchi/src/binutils-gdb/gdb/expop.h:2219
    #22 0x563f70fe0f02 in expression::evaluate(type*, noside) /home/smarchi/src/binutils-gdb/gdb/eval.c:110
    #23 0x563f71b1373e in process_print_command_args /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1319
    #24 0x563f71b1391b in print_command_1 /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1332
    #25 0x563f71b147ec in print_command /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1465
    #26 0x563f706029b8 in do_simple_func /home/smarchi/src/binutils-gdb/gdb/cli/cli-decode.c:95
    #27 0x563f7061972a in cmd_func(cmd_list_element*, char const*, int) /home/smarchi/src/binutils-gdb/gdb/cli/cli-decode.c:2735
    #28 0x563f7262d0ef in execute_command(char const*, int) /home/smarchi/src/binutils-gdb/gdb/top.c:572
    #29 0x563f7100ed9c in command_handler(char const*) /home/smarchi/src/binutils-gdb/gdb/event-top.c:543

previously allocated by thread T0 here:
    #0 0x7f5a7fce2012 in operator new(unsigned long) /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_new_delete.cpp:95
    #1 0x563f7029a9a3 in new_momentary_breakpoint<program_space*&, frame_id&, int&> /home/smarchi/src/binutils-gdb/gdb/breakpoint.c:8129
    #2 0x563f702212f6 in momentary_breakpoint_from_master /home/smarchi/src/binutils-gdb/gdb/breakpoint.c:8169
    #3 0x563f70212db1 in set_longjmp_breakpoint_for_call_dummy() /home/smarchi/src/binutils-gdb/gdb/breakpoint.c:7582
    #4 0x563f713804db in call_function_by_hand_dummy(value*, type*, gdb::array_view<value*>, void (*)(void*, int), void*) /home/smarchi/src/binutils-gdb/gdb/infcall.c:1260
    #5 0x563f7137c0e2 in call_function_by_hand(value*, type*, gdb::array_view<value*>) /home/smarchi/src/binutils-gdb/gdb/infcall.c:780
    #6 0x563f70fe5960 in evaluate_subexp_do_call(expression*, noside, value*, gdb::array_view<value*>, char const*, type*) /home/smarchi/src/binutils-gdb/gdb/eval.c:649
    #7 0x563f70fe6617 in expr::operation::evaluate_funcall(type*, expression*, noside, char const*, std::__debug::vector<std::unique_ptr<expr::operation, std::default_delete<expr::operation> >, std::allocator<std::unique_ptr<expr::operation, std::default_delete<expr::operation> > > > const&) /home/smarchi/src/binutils-gdb/gdb/eval.c:677
    #8 0x563f6fd19668 in expr::operation::evaluate_funcall(type*, expression*, noside, std::__debug::vector<std::unique_ptr<expr::operation, std::default_delete<expr::operation> >, std::allocator<std::unique_ptr<expr::operation, std::default_delete<expr::operation> > > > const&) /home/smarchi/src/binutils-gdb/gdb/expression.h:136
    #9 0x563f70fe6bba in expr::var_value_operation::evaluate_funcall(type*, expression*, noside, std::__debug::vector<std::unique_ptr<expr::operation, std::default_delete<expr::operation> >, std::allocator<std::unique_ptr<expr::operation, std::default_delete<expr::operation> > > > const&) /home/smarchi/src/binutils-gdb/gdb/eval.c:689
    #10 0x563f704b71dc in expr::funcall_operation::evaluate(type*, expression*, noside) /home/smarchi/src/binutils-gdb/gdb/expop.h:2219
    #11 0x563f70fe0f02 in expression::evaluate(type*, noside) /home/smarchi/src/binutils-gdb/gdb/eval.c:110
    #12 0x563f71b1373e in process_print_command_args /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1319
    #13 0x563f71b1391b in print_command_1 /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1332
    #14 0x563f71b147ec in print_command /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1465
    #15 0x563f706029b8 in do_simple_func /home/smarchi/src/binutils-gdb/gdb/cli/cli-decode.c:95
    #16 0x563f7061972a in cmd_func(cmd_list_element*, char const*, int) /home/smarchi/src/binutils-gdb/gdb/cli/cli-decode.c:2735
    #17 0x563f7262d0ef in execute_command(char const*, int) /home/smarchi/src/binutils-gdb/gdb/top.c:572
    #18 0x563f7100ed9c in command_handler(char const*) /home/smarchi/src/binutils-gdb/gdb/event-top.c:543
    #19 0x563f7101014b in command_line_handler(std::unique_ptr<char, gdb::xfree_deleter<char> >&&) /home/smarchi/src/binutils-gdb/gdb/event-top.c:779
    #20 0x563f72777942 in tui_command_line_handler /home/smarchi/src/binutils-gdb/gdb/tui/tui-interp.c:104
    #21 0x563f7100d059 in gdb_rl_callback_handler /home/smarchi/src/binutils-gdb/gdb/event-top.c:250
    #22 0x7f5a80418246 in rl_callback_read_char (/usr/lib/libreadline.so.8+0x3b246) (BuildId: 092e91fc4361b0ef94561e3ae03a75f69398acbb)

Simon
  
Simon Marchi May 8, 2023, 2:59 p.m. UTC | #2
On 5/7/23 22:24, Simon Marchi via Gdb-patches wrote:
> On 4/16/23 12:28, Tom Tromey wrote:
>> There's just a single remaining use of the ALL_BREAKPOINTS_SAFE macro;
>> this patch replaces it with a for-each and an explicit temporary
>> variable.
> 
> Starting with this patch, I see:
> 
> (gdb) PASS: gdb.base/stale-infcall.exp: continue to breakpoint: break-run1
> print infcall ()
> =================================================================
> ==47472==ERROR: AddressSanitizer: heap-use-after-free on address 0x611000034980 at pc 0x563f7012c7bc bp 0x7ffdf3804d70 sp 0x7ffdf3804d60
> READ of size 8 at 0x611000034980 thread T0

I just sent a patch for this:

https://inbox.sourceware.org/gdb-patches/20230508145913.29359-1-simon.marchi@efficios.com/T/#u

Simon
  

Patch

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 46287da5f87..b1460b51b0c 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -610,15 +610,6 @@  static int overlay_events_enabled;
 /* See description in breakpoint.h. */
 bool target_exact_watchpoints = false;
 
-/* Walk the following statement or block through all breakpoints.
-   ALL_BREAKPOINTS_SAFE does so even if the statement deletes the
-   current breakpoint.  */
-
-#define ALL_BREAKPOINTS_SAFE(B,TMP)	\
-	for (B = breakpoint_chain;	\
-	     B ? (TMP=B->next, 1): 0;	\
-	     B = TMP)
-
 /* Chains of all breakpoints defined.  */
 
 static struct breakpoint *breakpoint_chain;
@@ -7616,72 +7607,73 @@  set_longjmp_breakpoint_for_call_dummy (void)
 void
 check_longjmp_breakpoint_for_call_dummy (struct thread_info *tp)
 {
-  struct breakpoint *b, *b_tmp;
+  for (struct breakpoint *b : all_breakpoints_safe ())
+    {
+      struct breakpoint *b_tmp = b->next;
+      if (b->type == bp_longjmp_call_dummy && b->thread == tp->global_num)
+	{
+	  struct breakpoint *dummy_b = b->related_breakpoint;
 
-  ALL_BREAKPOINTS_SAFE (b, b_tmp)
-    if (b->type == bp_longjmp_call_dummy && b->thread == tp->global_num)
-      {
-	struct breakpoint *dummy_b = b->related_breakpoint;
-
-	/* Find the bp_call_dummy breakpoint in the list of breakpoints
-	   chained off b->related_breakpoint.  */
-	while (dummy_b != b && dummy_b->type != bp_call_dummy)
-	  dummy_b = dummy_b->related_breakpoint;
-
-	/* If there was no bp_call_dummy breakpoint then there's nothing
-	   more to do.  Or, if the dummy frame associated with the
-	   bp_call_dummy is still on the stack then we need to leave this
-	   bp_call_dummy in place.  */
-	if (dummy_b->type != bp_call_dummy
-	    || frame_find_by_id (dummy_b->frame_id) != NULL)
-	  continue;
+	  /* Find the bp_call_dummy breakpoint in the list of breakpoints
+	     chained off b->related_breakpoint.  */
+	  while (dummy_b != b && dummy_b->type != bp_call_dummy)
+	    dummy_b = dummy_b->related_breakpoint;
 
-	/* We didn't find the dummy frame on the stack, this could be
-	   because we have longjmp'd to a stack frame that is previous to
-	   the dummy frame, or it could be because the stack unwind is
-	   broken at some point between the longjmp frame and the dummy
-	   frame.
-
-	   Next we figure out why the stack unwind stopped.  If it looks
-	   like the unwind is complete then we assume the dummy frame has
-	   been jumped over, however, if the unwind stopped for an
-	   unexpected reason then we assume the stack unwind is currently
-	   broken, and that we will (eventually) return to the dummy
-	   frame.
-
-	   It might be tempting to consider using frame_id_inner here, but
-	   that is not safe.   There is no guarantee that the stack frames
-	   we are looking at here are even on the same stack as the
-	   original dummy frame, hence frame_id_inner can't be used.  See
-	   the comments on frame_id_inner for more details.  */
-	bool unwind_finished_unexpectedly = false;
-	for (frame_info_ptr fi = get_current_frame (); fi != nullptr; )
-	  {
-	    frame_info_ptr prev = get_prev_frame (fi);
-	    if (prev == nullptr)
-	      {
-		/* FI is the last stack frame.  Why did this frame not
-		   unwind further?  */
-		auto stop_reason = get_frame_unwind_stop_reason (fi);
-		if (stop_reason != UNWIND_NO_REASON
-		    && stop_reason != UNWIND_OUTERMOST)
-		  unwind_finished_unexpectedly = true;
-	      }
-	    fi = prev;
-	  }
-	if (unwind_finished_unexpectedly)
-	  continue;
+	  /* If there was no bp_call_dummy breakpoint then there's nothing
+	     more to do.  Or, if the dummy frame associated with the
+	     bp_call_dummy is still on the stack then we need to leave this
+	     bp_call_dummy in place.  */
+	  if (dummy_b->type != bp_call_dummy
+	      || frame_find_by_id (dummy_b->frame_id) != NULL)
+	    continue;
 
-	dummy_frame_discard (dummy_b->frame_id, tp);
+	  /* We didn't find the dummy frame on the stack, this could be
+	     because we have longjmp'd to a stack frame that is previous to
+	     the dummy frame, or it could be because the stack unwind is
+	     broken at some point between the longjmp frame and the dummy
+	     frame.
+
+	     Next we figure out why the stack unwind stopped.  If it looks
+	     like the unwind is complete then we assume the dummy frame has
+	     been jumped over, however, if the unwind stopped for an
+	     unexpected reason then we assume the stack unwind is currently
+	     broken, and that we will (eventually) return to the dummy
+	     frame.
+
+	     It might be tempting to consider using frame_id_inner here, but
+	     that is not safe.   There is no guarantee that the stack frames
+	     we are looking at here are even on the same stack as the
+	     original dummy frame, hence frame_id_inner can't be used.  See
+	     the comments on frame_id_inner for more details.  */
+	  bool unwind_finished_unexpectedly = false;
+	  for (frame_info_ptr fi = get_current_frame (); fi != nullptr; )
+	    {
+	      frame_info_ptr prev = get_prev_frame (fi);
+	      if (prev == nullptr)
+		{
+		  /* FI is the last stack frame.  Why did this frame not
+		     unwind further?  */
+		  auto stop_reason = get_frame_unwind_stop_reason (fi);
+		  if (stop_reason != UNWIND_NO_REASON
+		      && stop_reason != UNWIND_OUTERMOST)
+		    unwind_finished_unexpectedly = true;
+		}
+	      fi = prev;
+	    }
+	  if (unwind_finished_unexpectedly)
+	    continue;
 
-	while (b->related_breakpoint != b)
-	  {
-	    if (b_tmp == b->related_breakpoint)
-	      b_tmp = b->related_breakpoint->next;
-	    delete_breakpoint (b->related_breakpoint);
-	  }
-	delete_breakpoint (b);
-      }
+	  dummy_frame_discard (dummy_b->frame_id, tp);
+
+	  while (b->related_breakpoint != b)
+	    {
+	      if (b_tmp == b->related_breakpoint)
+		b_tmp = b->related_breakpoint->next;
+	      delete_breakpoint (b->related_breakpoint);
+	    }
+	  delete_breakpoint (b);
+	}
+    }
 }
 
 void