[PATCH v2] [gdb] Handle bp deletion in Breakpoint.stop
Tom de Vries
tdevries@suse.de
Tue Jan 21 10:29:08 GMT 2025
In the documentation of gdb.Breakpoint.stop [1] we read:
...
You should not alter the execution state of the inferior (i.e., step, next,
etc.), alter the current frame context (i.e., change the current active
frame), or alter, add or delete any breakpoint.
...
PR python/32423 reports that the failure mode of attempting to delete a
breakpoint in gdb.Breakpoint.stop is:
- actually deleting the breakpoint, followed by
- a segfault.
Improve the failure mode, by instead:
- not deleting the breakpoint, and
- throwing an error.
Add two new tests to test-case gdb.python/py-breakpoint.exp:
- one test checking that trying to self-delete from gdb.Breakpoint.stop throws
an error, and
- one test checking that using the workaround mentioned here [2]: using
"gdb.post_event(self.delete)" works as expected.
OTOH, while the documentation forbids it, test-case
gdb.python/py-finish-breakpoint.exp creates a breakpoint in
gdb.Breakpoint.stop, so this seems to be at least somewhat supported.
Fix this by no longer forbidding to create a breakpoint in
gdb.Breakpoint.stop.
Tested on x86_64-linux.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32423
[1] https://sourceware.org/gdb/current/onlinedocs/gdb.html/Breakpoints-In-Python.html#index-Breakpoint_002estop
[2] https://sourceware.org/bugzilla/show_bug.cgi?id=12802#c4
---
gdb/breakpoint.c | 15 +++
gdb/breakpoint.h | 4 +
gdb/doc/python.texi | 2 +-
gdb/infcall.c | 6 ++
gdb/testsuite/gdb.python/py-breakpoint.exp | 113 +++++++++++++++++++++
5 files changed, 139 insertions(+), 1 deletion(-)
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index e7fdeca91ff..f98a6578185 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5644,6 +5644,10 @@ bpstat_check_watchpoint (bpstat *bs)
}
}
+/* See breakpoint.h. */
+
+bool prevent_breakpoint_deletion;
+
/* For breakpoints that are currently marked as telling gdb to stop,
check conditions (condition proper, frame, thread and ignore count)
of breakpoint referred to by BS. If we should not stop for this
@@ -5654,6 +5658,11 @@ bpstat_check_breakpoint_conditions (bpstat *bs, thread_info *thread)
{
INFRUN_SCOPED_DEBUG_ENTER_EXIT;
+ /* Breakpoints are not allowed to be deleted while checking breakpoint
+ conditions. */
+ scoped_restore restore_in_gdb_breakpoint_stop
+ = make_scoped_restore (&prevent_breakpoint_deletion, true);
+
const struct bp_location *bl;
struct breakpoint *b;
/* Assume stop. */
@@ -12581,6 +12590,12 @@ delete_breakpoint (struct breakpoint *bpt)
{
gdb_assert (bpt != NULL);
+ if (prevent_breakpoint_deletion)
+ {
+ error (_("Cannot delete breakpoint"));
+ return;
+ }
+
/* Has this bp already been deleted? This can happen because
multiple lists can hold pointers to bp's. bpstat lists are
especial culprits.
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 94fba1cf602..f255b92e469 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -2103,4 +2103,8 @@ extern void enable_disable_bp_location (bp_location *loc, bool enable);
extern void notify_breakpoint_modified (breakpoint *b);
+/* Whether breakpoints should not be deleted. */
+
+extern bool prevent_breakpoint_deletion;
+
#endif /* GDB_BREAKPOINT_H */
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index d66cae6f997..a1e64256f53 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6702,7 +6702,7 @@ if one of the methods returns @code{True} but the others return
You should not alter the execution state of the inferior (i.e.@:, step,
next, etc.), alter the current frame context (i.e.@:, change the current
-active frame), or alter, add or delete any breakpoint. As a general
+active frame), or alter or delete any breakpoint. As a general
rule, you should not alter any data within @value{GDBN} or the inferior
at this time.
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 6399278c6ae..ead9b27bd3b 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -1036,6 +1036,12 @@ call_function_by_hand_dummy (struct value *function,
{
INFCALL_SCOPED_DEBUG_ENTER_EXIT;
+ /* Breakpoints are not allowed to be deleted while checking breakpoint
+ conditions, but inferior calls during breakpoint conditions require
+ setting and deleting breakpoints. */
+ scoped_restore restore_in_gdb_breakpoint_stop
+ = make_scoped_restore (&prevent_breakpoint_deletion, false);
+
CORE_ADDR sp;
struct type *target_values_type;
function_call_return_method return_method = return_method_normal;
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index 6f9245c75c6..22b26ce2371 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -950,6 +950,117 @@ proc_with_prefix test_bkpt_auto_disable { } {
gdb_test "continue" "False.*" "auto-disabling after enable count reached"
}
+proc_with_prefix test_bkpt_stop_delete { } {
+
+ foreach_with_prefix type { gdb.Breakpoint gdb.FinishBreakpoint } {
+ # Start with a fresh gdb.
+ clean_restart $::testfile
+
+ if { ![runto_main] } {
+ return 0
+ }
+
+ delete_breakpoints
+
+ gdb_test_multiline "Sub-class a breakpoint" \
+ "python" "" \
+ "class basic ($type):" "" \
+ " def stop(self):" "" \
+ " self.delete()" "" \
+ " return True" "" \
+ "end" ""
+
+ set line_marker "Break at function add.";
+ set bp_location [gdb_get_line_number $line_marker]
+
+ if { $type == "gdb.Breakpoint" } {
+ gdb_py_test_silent_cmd "python basic(\"$bp_location\")" \
+ "Set breakpoint" 0
+ } else {
+ gdb_breakpoint $bp_location
+ gdb_continue_to_breakpoint "continue to function add" \
+ .*[string_to_regexp $line_marker].*
+
+ gdb_py_test_silent_cmd "python basic()" \
+ "Set breakpoint" 0
+ }
+
+ set err_msg_prefix "Python Exception <class 'gdb.error'>"
+ set err_msg "Cannot delete breakpoint"
+ gdb_test "continue" \
+ [string_to_regexp "$err_msg_prefix: $err_msg"].* \
+ $err_msg
+ }
+}
+
+proc_with_prefix test_bkpt_stop_post_event_delete { } {
+
+ foreach_with_prefix type { gdb.Breakpoint gdb.FinishBreakpoint } {
+ # Start with a fresh gdb.
+ clean_restart $::testfile
+
+ if { ![runto_main] } {
+ return 0
+ }
+
+ delete_breakpoints
+
+ gdb_test_multiline "Sub-class a breakpoint" \
+ "python" "" \
+ "class basic ($type):" "" \
+ " def stop(self):" "" \
+ " gdb.post_event(self.delete)" "" \
+ " return True" "" \
+ "end" ""
+
+ set line_marker "Break at function add.";
+ set bp_location [gdb_get_line_number $line_marker]
+
+ if { $type == "gdb.Breakpoint" } {
+ gdb_py_test_silent_cmd "python bp = basic(\"$bp_location\")" \
+ "Set breakpoint" 0
+ } else {
+ gdb_breakpoint $bp_location
+ gdb_continue_to_breakpoint "continue to function add" \
+ .*[string_to_regexp $line_marker].*
+
+ gdb_py_test_silent_cmd "python bp = basic()" \
+ "Set breakpoint" 0
+ }
+
+ set bp_num ""
+ gdb_test_multiple "python print (bp.number)" "" {
+ -re -wrap "($::decimal)" {
+ set bp_num $expect_out(1,string)
+ pass $gdb_test_name
+ }
+ }
+
+ if { $bp_num == "" } {
+ continue
+ }
+
+ if { $type == "gdb.Breakpoint" } {
+ gdb_test "continue" "Breakpoint $::decimal, .*"
+ } else {
+ # The gdb.FinishBreakpoint is a temporary breakpoint, so it's
+ # already auto-deleted by the time self.delete tries to delete it.
+ # Check for the error message.
+ set err_msg_prefix \
+ [string_to_regexp "Python Exception <class 'RuntimeError'>"]
+ set err_msg "Breakpoint $::decimal is invalid"
+ set dot [string_to_regexp .]
+ set re "$err_msg_prefix: $err_msg$dot\r\n"
+ gdb_test -prompt "$::gdb_prompt $re$" \
+ "continue"
+ }
+
+ gdb_test "info breakpoints $bp_num" \
+ [string_to_regexp \
+ "No breakpoint, watchpoint, tracepoint, or catchpoint matching '$bp_num'."]
+ }
+}
+
test_bkpt_basic
test_bkpt_deletion
test_bkpt_cond_and_cmds
@@ -968,3 +1079,5 @@ test_bkpt_explicit_loc
test_bkpt_qualified
test_bkpt_probe
test_bkpt_auto_disable
+test_bkpt_stop_delete
+test_bkpt_stop_post_event_delete
base-commit: 2c77375c02bd09ff91308bfc52de9412fdf3035b
--
2.43.0
More information about the Gdb-patches
mailing list