diff options
-rw-r--r-- | gdb/breakpoint.c | 12 | ||||
-rw-r--r-- | gdb/infcall.c | 41 | ||||
-rw-r--r-- | gdb/infrun.c | 16 | ||||
-rw-r--r-- | gdb/testsuite/gdb.mi/mi-condbreak-fail.c | 39 | ||||
-rw-r--r-- | gdb/testsuite/gdb.mi/mi-condbreak-fail.exp | 67 |
5 files changed, 152 insertions, 23 deletions
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index d898167..93634bd 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -5535,7 +5535,6 @@ bpstat_check_breakpoint_conditions (bpstat *bs, thread_info *thread) else within_current_scope = false; } - CORE_ADDR pc_before_check = get_frame_pc (get_selected_frame (nullptr)); if (within_current_scope) { try @@ -5555,17 +5554,6 @@ bpstat_check_breakpoint_conditions (bpstat *bs, thread_info *thread) (gdb_stderr, ex, "Error in testing condition for breakpoint %d:\n", b->number); - - /* If the pc value changed as a result of evaluating the - condition then we probably stopped within an inferior - function call due to some unexpected stop, e.g. the thread - hit another breakpoint, or the thread received an - unexpected signal. In this case we don't want to also - print the information about this breakpoint. */ - CORE_ADDR pc_after_check - = get_frame_pc (get_selected_frame (nullptr)); - if (pc_before_check != pc_after_check) - bs->print = 0; } } else diff --git a/gdb/infcall.c b/gdb/infcall.c index ddf325a..bea5b18 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -564,10 +564,13 @@ call_thread_fsm::should_stop (struct thread_info *thread) call.. */ return_value = get_call_return_value (&return_meta_info); - /* Break out of wait_sync_command_done. */ + /* Break out of wait_sync_command_done. This is similar to the + async_enable_stdin call in normal_stop (which we don't call), + however, in this case we only change the WAITING_UI. This is + enough for wait_sync_command_done. */ scoped_restore save_ui = make_scoped_restore (¤t_ui, waiting_ui); - target_terminal::ours (); - waiting_ui->prompt_state = PROMPT_NEEDED; + gdb_assert (current_ui->prompt_state == PROMPT_BLOCKED); + async_enable_stdin (); } return true; @@ -661,14 +664,32 @@ run_inferior_call (std::unique_ptr<call_thread_fsm> sm, infcall_debug_printf ("thread is now: %s", inferior_ptid.to_string ().c_str ()); - /* If GDB has the prompt blocked before, then ensure that it remains - so. normal_stop calls async_enable_stdin, so reset the prompt - state again here. In other cases, stdin will be re-enabled by - inferior_event_handler, when an exception is thrown. */ + /* After the inferior call finished, async_enable_stdin has been + called, either from normal_stop or from + call_thread_fsm::should_stop, and the prompt state has been + restored by the scoped_restore in the try block above. + + If the inferior call finished successfully, then we should + disable stdin as we don't know yet whether the inferior will be + stopping. Calling async_disable_stdin restores things to how + they were when this function was called. + + If the inferior call didn't complete successfully, then + normal_stop has already been called, and we know for sure that we + are going to present this stop to the user. In this case, we + call async_enable_stdin. This changes the prompt state to + PROMPT_NEEDED. + + If the previous prompt state was PROMPT_NEEDED, then as + async_enable_stdin has already been called, nothing additional + needs to be done here. */ if (current_ui->prompt_state == PROMPT_BLOCKED) - current_ui->unregister_file_handler (); - else - current_ui->register_file_handler (); + { + if (call_thread->thread_fsm ()->finished_p ()) + async_disable_stdin (); + else + async_enable_stdin (); + } /* If the infcall does NOT succeed, normal_stop will have already finished the thread states. However, on success, normal_stop diff --git a/gdb/infrun.c b/gdb/infrun.c index 7efa061..8286026 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -4458,6 +4458,8 @@ fetch_inferior_event () auto defer_delete_threads = make_scope_exit (delete_just_stopped_threads_infrun_breakpoints); + int stop_id = get_stop_id (); + /* Now figure out what to do with the result of the result. */ handle_inferior_event (&ecs); @@ -4485,7 +4487,19 @@ fetch_inferior_event () clean_up_just_stopped_threads_fsms (&ecs); - if (thr != nullptr && thr->thread_fsm () != nullptr) + if (stop_id != get_stop_id ()) + { + /* If the stop-id has changed then a stop has already been + presented to the user in handle_inferior_event, this is + likely a failed inferior call. As the stop has already + been announced then we should not notify again. + + Also, if the prompt state is not PROMPT_NEEDED then GDB + will not be ready for user input after this function. */ + should_notify_stop = false; + gdb_assert (current_ui->prompt_state == PROMPT_NEEDED); + } + else if (thr != nullptr && thr->thread_fsm () != nullptr) should_notify_stop = thr->thread_fsm ()->should_notify_stop (); diff --git a/gdb/testsuite/gdb.mi/mi-condbreak-fail.c b/gdb/testsuite/gdb.mi/mi-condbreak-fail.c new file mode 100644 index 0000000..94bd248 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-condbreak-fail.c @@ -0,0 +1,39 @@ +/* Copyright 2023 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +volatile int global_counter = 0; + +int +cond_fail () +{ + volatile int *p = 0; + return *p; /* Crash here. */ +} + +int +foo () +{ + global_counter += 1; /* Set breakpoint here. */ + return 0; +} + +int +main () +{ + int res = foo (); + return res; +} diff --git a/gdb/testsuite/gdb.mi/mi-condbreak-fail.exp b/gdb/testsuite/gdb.mi/mi-condbreak-fail.exp new file mode 100644 index 0000000..34be4b9 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-condbreak-fail.exp @@ -0,0 +1,67 @@ +# Copyright (C) 2023 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Check that when GDB fails to evaluate the condition of a conditional +# breakpoint we only get one *stopped notification. + +load_lib mi-support.exp +set MIFLAGS "-i=mi" + +standard_testfile + +if [build_executable ${testfile}.exp ${binfile} ${srcfile}] { + return -1 +} + +if {[mi_clean_restart $binfile]} { + return +} + +if {[mi_runto_main] == -1} { + return +} + +# Create the conditional breakpoint. +set bp_location [gdb_get_line_number "Set breakpoint here"] +mi_create_breakpoint "-c \"cond_fail ()\" $srcfile:$bp_location" \ + "insert conditional breakpoint" \ + -func foo -file ".*$srcfile" -line "$bp_location" \ + -cond "cond_fail \\(\\)" + +# Number of the previous breakpoint. +set bpnum [mi_get_valueof "/d" "\$bpnum" "INVALID" \ + "get number for breakpoint"] + +# The line where we expect the inferior to crash. +set crash_linenum [gdb_get_line_number "Crash here"] + +# Run the inferior and wait for it to stop. +mi_send_resuming_command "exec-continue" "continue the inferior" +mi_gdb_test "" \ + [multi_line \ + "~\"\\\\nProgram\"" \ + "~\" received signal SIGSEGV, Segmentation fault\\.\\\\n\"" \ + "~\"$hex in cond_fail \\(\\) at \[^\r\n\]+\"" \ + "~\"${crash_linenum}\\\\t\\s+return \\*p;\[^\r\n\]+\\\\n\"" \ + "\\*stopped,reason=\"signal-received\",signal-name=\"SIGSEGV\"\[^\r\n\]+" \ + "&\"Error in testing condition for breakpoint $bpnum:\\\\n\"" \ + "&\"The program being debugged was signaled while in a function called from GDB\\.\\\\n\"" \ + "&\"GDB remains in the frame where the signal was received\\.\\\\n\"" \ + "&\"To change this behavior use \\\\\"set unwindonsignal on\\\\\"\\.\\\\n\"" \ + "&\"Evaluation of the expression containing the function\\\\n\"" \ + "&\"\\(cond_fail\\) will be abandoned\\.\\\\n\"" \ + "&\"When the function is done executing, GDB will silently stop\\.\\\\n\"" \ + "=breakpoint-modified,bkpt={number=\"$bpnum\",type=\"breakpoint\",\[^\r\n\]+times=\"1\",\[^\r\n\]+}"] \ + "wait for stop" |