diff options
-rw-r--r-- | gdb/ChangeLog | 31 | ||||
-rw-r--r-- | gdb/NEWS | 7 | ||||
-rw-r--r-- | gdb/breakpoint.c | 90 | ||||
-rw-r--r-- | gdb/breakpoint.h | 2 | ||||
-rw-r--r-- | gdb/doc/ChangeLog | 6 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 13 | ||||
-rw-r--r-- | gdb/infrun.c | 5 | ||||
-rw-r--r-- | gdb/remote.c | 5 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.threads/break-while-running.c | 101 | ||||
-rw-r--r-- | gdb/testsuite/gdb.threads/break-while-running.exp | 151 |
11 files changed, 361 insertions, 55 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index b91dc72..90a991d 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,36 @@ 2014-09-22 Pedro Alves <palves@redhat.com> + * NEWS: Mention merge of "breakpoint always-inserted" modes "off" + and "auto" merged. + * breakpoint.c (enum ugll_insert_mode): New enum. + (always_inserted_mode): Now a plain boolean. + (show_always_inserted_mode): No longer handle AUTO_BOOLEAN_AUTO. + (breakpoints_always_inserted_mode): Delete. + (breakpoints_should_be_inserted_now): New function. + (insert_breakpoints): Pass UGLL_INSERT to + update_global_location_list instead of calling + insert_breakpoint_locations manually. + (create_solib_event_breakpoint_1): New, factored out from ... + (create_solib_event_breakpoint): ... this. + (create_and_insert_solib_event_breakpoint): Use + create_solib_event_breakpoint_1 instead of calling + insert_breakpoint_locations manually. + (update_global_location_list): Change parameter type from boolean + to enum ugll_insert_mode. All callers adjusted. Adjust to use + breakpoints_should_be_inserted_now and handle UGLL_INSERT. + (update_global_location_list_nothrow): Change parameter type from + boolean to enum ugll_insert_mode. + (_initialize_breakpoint): "breakpoint always-inserted" option is + now a boolean command. Update help text. + * breakpoint.h (breakpoints_always_inserted_mode): Delete declaration. + (breakpoints_should_be_inserted_now): New declaration. + * infrun.c (handle_inferior_event) <TARGET_WAITKIND_LOADED>: + Remove breakpoints_always_inserted_mode check. + (normal_stop): Adjust to use breakpoints_should_be_inserted_now. + * remote.c (remote_start_remote): Likewise. + +2014-09-22 Pedro Alves <palves@redhat.com> + * breakpoint.c (enum ugll_insert_mode): Add UGLL_INSERT. (insert_breakpoints): Don't call insert_breakpoint_locations here. Instead, pass UGLL_INSERT to update_global_location_list. @@ -33,6 +33,13 @@ queue-signal signal-name-or-number confirmation if the program had stopped for a signal and the user switched threads meanwhile. +* "breakpoint always-inserted" modes "off" and "auto" merged. + + Now, when 'breakpoint always-inserted mode' is set to "off", GDB + won't remove breakpoints from the target until all threads stop, + even in non-stop mode. The "auto" mode has been removed, and "off" + is now the default mode. + *** Changes in GDB 7.8 * New command line options diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 3f372de..3675b4f 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -258,12 +258,17 @@ enum ugll_insert_mode the inferior. */ UGLL_DONT_INSERT, - /* May insert breakpoints if breakpoints_always_inserted_mode is - true. */ + /* May insert breakpoints iff breakpoints_should_be_inserted_now + claims breakpoints should be inserted now. */ UGLL_MAY_INSERT, - /* Insert locations now, even if breakpoints_always_inserted_mode is - false. */ + /* Insert locations now, irrespective of + breakpoints_should_be_inserted_now. E.g., say all threads are + stopped right now, and the user did "continue". We need to + insert breakpoints _before_ resuming the target, but + UGLL_MAY_INSERT wouldn't insert them, because + breakpoints_should_be_inserted_now returns false at that point, + as no thread is running yet. */ UGLL_INSERT }; @@ -451,34 +456,51 @@ show_automatic_hardware_breakpoints (struct ui_file *file, int from_tty, value); } -/* If on, gdb will keep breakpoints inserted even as inferior is - stopped, and immediately insert any new breakpoints. If off, gdb - will insert breakpoints into inferior only when resuming it, and - will remove breakpoints upon stop. If auto, GDB will behave as ON - if in non-stop mode, and as OFF if all-stop mode.*/ - -static enum auto_boolean always_inserted_mode = AUTO_BOOLEAN_AUTO; +/* If on, GDB keeps breakpoints inserted even if the inferior is + stopped, and immediately inserts any new breakpoints as soon as + they're created. If off (default), GDB keeps breakpoints off of + the target as long as possible. That is, it delays inserting + breakpoints until the next resume, and removes them again when the + target fully stops. This is a bit safer in case GDB crashes while + processing user input. */ +static int always_inserted_mode = 0; static void show_always_inserted_mode (struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value) { - if (always_inserted_mode == AUTO_BOOLEAN_AUTO) - fprintf_filtered (file, - _("Always inserted breakpoint " - "mode is %s (currently %s).\n"), - value, - breakpoints_always_inserted_mode () ? "on" : "off"); - else - fprintf_filtered (file, _("Always inserted breakpoint mode is %s.\n"), - value); + fprintf_filtered (file, _("Always inserted breakpoint mode is %s.\n"), + value); } int -breakpoints_always_inserted_mode (void) +breakpoints_should_be_inserted_now (void) { - return (always_inserted_mode == AUTO_BOOLEAN_TRUE - || (always_inserted_mode == AUTO_BOOLEAN_AUTO && non_stop)); + if (gdbarch_has_global_breakpoints (target_gdbarch ())) + { + /* If breakpoints are global, they should be inserted even if no + thread under gdb's control is running, or even if there are + no threads under GDB's control yet. */ + return 1; + } + else if (target_has_execution) + { + struct thread_info *tp; + + if (always_inserted_mode) + { + /* The user wants breakpoints inserted even if all threads + are stopped. */ + return 1; + } + + ALL_NON_EXITED_THREADS (tp) + { + if (tp->executing) + return 1; + } + } + return 0; } static const char condition_evaluation_both[] = "host or target"; @@ -12930,10 +12952,7 @@ update_global_location_list (enum ugll_insert_mode insert_mode) "a permanent breakpoint")); } - if (insert_mode == UGLL_INSERT - || (breakpoints_always_inserted_mode () - && (have_live_inferiors () - || (gdbarch_has_global_breakpoints (target_gdbarch ()))))) + if (insert_mode == UGLL_INSERT || breakpoints_should_be_inserted_now ()) { if (insert_mode != UGLL_DONT_INSERT) insert_breakpoint_locations (); @@ -17020,18 +17039,15 @@ a warning will be emitted for such breakpoints."), &breakpoint_set_cmdlist, &breakpoint_show_cmdlist); - add_setshow_auto_boolean_cmd ("always-inserted", class_support, - &always_inserted_mode, _("\ + add_setshow_boolean_cmd ("always-inserted", class_support, + &always_inserted_mode, _("\ Set mode for inserting breakpoints."), _("\ Show mode for inserting breakpoints."), _("\ -When this mode is off, breakpoints are inserted in inferior when it is\n\ -resumed, and removed when execution stops. When this mode is on,\n\ -breakpoints are inserted immediately and removed only when the user\n\ -deletes the breakpoint. When this mode is auto (which is the default),\n\ -the behaviour depends on the non-stop setting (see help set non-stop).\n\ -In this case, if gdb is controlling the inferior in non-stop mode, gdb\n\ -behaves as if always-inserted mode is on; if gdb is controlling the\n\ -inferior in all-stop mode, gdb behaves as if always-inserted mode is off."), +When this mode is on, breakpoints are inserted immediately as soon as\n\ +they're created, kept inserted even when execution stops, and removed\n\ +only when the user deletes them. When this mode is off (the default),\n\ +breakpoints are inserted only when execution continues, and removed\n\ +when execution stops."), NULL, &show_always_inserted_mode, &breakpoint_set_cmdlist, diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 00c8802..e191c10 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -1491,7 +1491,7 @@ extern void breakpoint_xfer_memory (gdb_byte *readbuf, gdb_byte *writebuf, const gdb_byte *writebuf_org, ULONGEST memaddr, LONGEST len); -extern int breakpoints_always_inserted_mode (void); +extern int breakpoints_should_be_inserted_now (void); /* Called each time new event from target is processed. Retires previously deleted breakpoint locations that diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index b5fac5a..d77a80e 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,9 @@ +2014-09-22 Pedro Alves <palves@redhat.com> + + * gdb.texinfo (Set Breaks): Document that "set breakpoint + always-inserted off" is the default mode now. Delete + documentation of "set breakpoint always-inserted auto". + 2014-09-18 Doug Evans <dje@google.com> * python.texi (Symbol Tables In Python): Document "producer" diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 1bb1c0c..537fae8 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -3850,22 +3850,13 @@ This behavior can be controlled with the following commands:: @item set breakpoint always-inserted off All breakpoints, including newly added by the user, are inserted in the target only when the target is resumed. All breakpoints are -removed from the target when it stops. +removed from the target when it stops. This is the default mode. @item set breakpoint always-inserted on Causes all breakpoints to be inserted in the target at all times. If the user adds a new breakpoint, or changes an existing breakpoint, the breakpoints in the target are updated immediately. A breakpoint is -removed from the target only when breakpoint itself is removed. - -@cindex non-stop mode, and @code{breakpoint always-inserted} -@item set breakpoint always-inserted auto -This is the default mode. If @value{GDBN} is controlling the inferior -in non-stop mode (@pxref{Non-Stop Mode}), gdb behaves as if -@code{breakpoint always-inserted} mode is on. If @value{GDBN} is -controlling the inferior in all-stop mode, @value{GDBN} behaves as if -@code{breakpoint always-inserted} mode is off. -@end table +removed from the target only when breakpoint itself is deleted. @value{GDBN} handles conditional breakpoints by evaluating these conditions when a breakpoint breaks. If the condition is true, then the process being diff --git a/gdb/infrun.c b/gdb/infrun.c index c18267f..3725f2d 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -3448,8 +3448,7 @@ handle_inferior_event (struct execution_control_state *ecs) { /* Loading of shared libraries might have changed breakpoint addresses. Make sure new breakpoints are inserted. */ - if (stop_soon == NO_STOP_QUIETLY - && !breakpoints_always_inserted_mode ()) + if (stop_soon == NO_STOP_QUIETLY) insert_breakpoints (); resume (0, GDB_SIGNAL_0); prepare_to_wait (ecs); @@ -6110,7 +6109,7 @@ normal_stop (void) printf_filtered (_("No unwaited-for children left.\n")); } - if (!breakpoints_always_inserted_mode () && target_has_execution) + if (!breakpoints_should_be_inserted_now () && target_has_execution) { if (remove_breakpoints ()) { diff --git a/gdb/remote.c b/gdb/remote.c index 357e9f2..41ea012 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -3604,9 +3604,8 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p) up. */ rs->starting_up = 0; - /* If breakpoints are global, insert them now. */ - if (gdbarch_has_global_breakpoints (target_gdbarch ()) - && breakpoints_always_inserted_mode ()) + /* Maybe breakpoints are global and need to be inserted now. */ + if (breakpoints_should_be_inserted_now ()) insert_breakpoints (); } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 67c7f82..539a983 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2014-09-22 Pedro Alves <palves@redhat.com> + + * gdb.threads/break-while-running.exp: New file. + * gdb.threads/break-while-running.c: New file. + 2014-09-19 Yao Qi <yao@codesourcery.com> * gdb.dwarf2/dw2-var-zero-addr.exp: Move test into new proc test. diff --git a/gdb/testsuite/gdb.threads/break-while-running.c b/gdb/testsuite/gdb.threads/break-while-running.c new file mode 100644 index 0000000..20695ee --- /dev/null +++ b/gdb/testsuite/gdb.threads/break-while-running.c @@ -0,0 +1,101 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2014 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/>. */ + +#include <pthread.h> +#include <assert.h> +#include <unistd.h> +#include <stdlib.h> + +pthread_barrier_t barrier; + +volatile int second_child; + +void +breakpoint_function (void) +{ + /* GDB sets a breakpoint in this function. */ +} + +void * +child_function_0 (void *arg) +{ + volatile unsigned int counter = 1; + + pthread_barrier_wait (&barrier); + + while (counter > 0) + { + counter++; + usleep (1); + + if (second_child) + continue; + + breakpoint_function (); + } + + pthread_exit (NULL); +} + +void * +child_function_1 (void *arg) +{ + volatile unsigned int counter = 1; + + pthread_barrier_wait (&barrier); + + while (counter > 0) + { + counter++; + usleep (1); + + if (!second_child) + continue; + + breakpoint_function (); + } + + pthread_exit (NULL); +} + +static int +wait_threads (void) +{ + return 1; /* in wait_threads */ +} + +int +main (void) +{ + pthread_t child_thread[2]; + int res; + + pthread_barrier_init (&barrier, NULL, 3); + + res = pthread_create (&child_thread[0], NULL, child_function_0, NULL); + assert (res == 0); + res = pthread_create (&child_thread[1], NULL, child_function_1, NULL); + assert (res == 0); + + pthread_barrier_wait (&barrier); + wait_threads (); /* set wait-thread breakpoint here */ + + pthread_join (child_thread[0], NULL); + pthread_join (child_thread[1], NULL); + + exit (EXIT_SUCCESS); +} diff --git a/gdb/testsuite/gdb.threads/break-while-running.exp b/gdb/testsuite/gdb.threads/break-while-running.exp new file mode 100644 index 0000000..690d6ab --- /dev/null +++ b/gdb/testsuite/gdb.threads/break-while-running.exp @@ -0,0 +1,151 @@ +# Copyright (C) 2014 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/>. + +# Test that: +# +# - setting a breakpoint while a thread is running results in the +# breakpoint being inserted immediately. +# +# - if breakpoint always-inserted mode is off, GDB doesn't remove +# breakpoints from the target when a thread stops, if there are +# still threads running. + +standard_testfile + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug pthreads}] == -1} { + return -1 +} + +# The test proper. NON_STOP indicates whether we're testing in +# non-stop, or all-stop mode. ALWAYS_INSERTED indicates whether +# testing in "breakpoint always-inserted" mode. + +proc test { always_inserted non_stop } { + global srcfile binfile + global gdb_prompt + global decimal + + clean_restart $binfile + + gdb_test_no_output "set non-stop $non_stop" + gdb_test_no_output "set breakpoint always-inserted $always_inserted" + + if ![runto_main] { + return -1 + } + + # In all-stop, check whether we're testing with the remote or + # extended-remote targets. If so, skip the tests, as with the + # RSP, we can't issue commands until the target replies to vCont. + # Not an issue with the non-stop RSP variant, which has a + # non-blocking vCont. + if {$non_stop=="off" && [gdb_is_target_remote]} { + return -1 + } + + gdb_breakpoint [gdb_get_line_number "set wait-thread breakpoint here"] + gdb_continue_to_breakpoint "run to wait-thread breakpoint" + + delete_breakpoints + + # Leave the main thread stopped, so GDB can poke at memory freely. + if {$non_stop == "off"} { + gdb_test_no_output "set scheduler-locking on" + gdb_test "thread 2" "Switching to .*" + gdb_test "continue &" "Continuing\." "continuing thread 2" + gdb_test "thread 3" "Switching to .*" + gdb_test "continue &" "Continuing\." "continuing thread 3" + gdb_test "thread 1" "Switching to .*" "switch back to main thread" + } + + gdb_test "info threads" \ + "\\\(running\\\).*\\\(running\\\).* main .*" \ + "only main stopped" + + # Don't use gdb_test as it's racy in this case -- gdb_test matches + # the prompt with an end anchor. Sometimes expect will manage to + # read the breakpoint hit output while still processing this test, + # defeating the anchor. + set test "set breakpoint while a thread is running" + gdb_test_multiple "break breakpoint_function" $test { + -re "Breakpoint $decimal at .*: file .*$srcfile.*\r\n$gdb_prompt " { + pass $test + } + -re "$gdb_prompt " { + fail $test + } + } + + # Check that the breakpoint is hit. Can't use gdb_test here, as + # no prompt is expected to come out. + set test "breakpoint is hit" + gdb_test_multiple "" $test { + -re "Breakpoint .*, breakpoint_function \[^\r\n\]+" { + pass $test + } + } + + if {$non_stop == "on"} { + gdb_test "info threads" \ + "\\\(running\\\).* breakpoint_function .* main .*" \ + "one thread running" + + # Unblock the other thread, which should then trip on the same + # breakpoint, unless GDB removed it by mistake. Can't use + # gdb_test here for the same reasons as above. + set test "unblock second thread" + gdb_test_multiple "print second_child = 1" $test { + -re " = 1\r\n$gdb_prompt " { + pass $test + } + -re "$gdb_prompt " { + fail $test + } + } + + set test "breakpoint on second child is hit" + gdb_test_multiple "" $test { + -re "Breakpoint .*, breakpoint_function \[^\r\n\]+" { + pass $test + } + } + + gdb_test "info threads" \ + " breakpoint_function .* breakpoint_function .* main .*" \ + "all threads stopped" + } else { + # This test is not merged with the non-stop one because in + # all-stop we don't know where the other thread stops (inside + # usleep, for example). + set test "all threads stopped" + gdb_test_multiple "info threads" "$test" { + -re "\\\(running\\\).*$gdb_prompt $" { + fail $test + } + -re "breakpoint_function .* main .*$gdb_prompt $" { + pass $test + } + } + } +} + +foreach always_inserted { "off" "on" } { + foreach non_stop { "off" "on" } { + set stop_mode [expr ($non_stop=="off")?"all-stop":"non-stop"] + with_test_prefix "always-inserted $always_inserted: $stop_mode" { + test $always_inserted $non_stop + } + } +} |