414 lines
18 KiB
Diff
414 lines
18 KiB
Diff
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
|
|
From: Andrew Burgess <aburgess@redhat.com>
|
|
Date: Wed, 18 Jan 2023 10:17:57 +0000
|
|
Subject: gdb-rhel-13298-inferior-funcall-bp-condition-4-of-5.patch
|
|
|
|
;;gdb: introduce unwind-on-timeout setting
|
|
;;(Andrew Burgess, RHEL-13298)
|
|
|
|
Now that inferior function calls can timeout (see the recent
|
|
introduction of direct-call-timeout and indirect-call-timeout), this
|
|
commit adds a new setting unwind-on-timeout.
|
|
|
|
This new setting is just like the existing unwindonsignal and
|
|
unwind-on-terminating-exception, but the new setting will cause GDB to
|
|
unwind the stack if an inferior function call times out.
|
|
|
|
The existing inferior function call timeout tests have been updated to
|
|
cover the new setting.
|
|
|
|
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
|
|
Tested-By: Luis Machado <luis.machado@arm.com>
|
|
Tested-By: Keith Seitz <keiths@redhat.com>
|
|
|
|
diff --git a/gdb/NEWS b/gdb/NEWS
|
|
--- a/gdb/NEWS
|
|
+++ b/gdb/NEWS
|
|
@@ -72,6 +72,15 @@ show indirect-call-timeout
|
|
ignored, GDB will wait indefinitely for an inferior function to
|
|
complete, unless interrupted by the user using Ctrl-C.
|
|
|
|
+set unwind-on-timeout on|off
|
|
+show unwind-on-timeout
|
|
+ These commands control whether GDB should unwind the stack when a
|
|
+ timeout occurs during an inferior function call. The default is
|
|
+ off, in which case the inferior will remain in the frame where the
|
|
+ timeout occurred. When on, GDB will unwind the stack removing the
|
|
+ dummy frame that was added for the inferior call, and restoring the
|
|
+ inferior state to how it was before the inferior call started.
|
|
+
|
|
* New features in the GDB remote stub, GDBserver
|
|
|
|
** The --remote-debug and --event-loop-debug command line options
|
|
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
|
|
--- a/gdb/doc/gdb.texinfo
|
|
+++ b/gdb/doc/gdb.texinfo
|
|
@@ -20932,6 +20932,22 @@ the default C@t{++} exception handler and the inferior terminated.
|
|
Show the current setting of stack unwinding in the functions called by
|
|
@value{GDBN}.
|
|
|
|
+@anchor{set unwind-on-timeout}
|
|
+@item set unwind-on-timeout
|
|
+@kindex set unwind-on-timeout
|
|
+@cindex unwind stack in called functions when timing out
|
|
+@cindex call dummy stack unwinding on timeout.
|
|
+Set unwinding of the stack if a function called from @value{GDBN}
|
|
+times out. If set to @code{off} (the default), @value{GDBN} stops in
|
|
+the frame where the timeout occurred. If set to @code{on},
|
|
+@value{GDBN} unwinds the stack it created for the call and restores
|
|
+the context to what it was before the call.
|
|
+
|
|
+@item show unwind-on-timeout
|
|
+@kindex show unwind-on-timeout
|
|
+Show whether @value{GDBN} will unwind the stack if a function called
|
|
+from @value{GDBN} times out.
|
|
+
|
|
@item set may-call-functions
|
|
@kindex set may-call-functions
|
|
@cindex disabling calling functions in the program
|
|
@@ -20963,11 +20979,11 @@ call by typing the interrupt character (often @kbd{Ctrl-c}).
|
|
|
|
If a called function is interrupted for any reason, including hitting
|
|
a breakpoint, or triggering a watchpoint, and the stack is not unwound
|
|
-due to @code{set unwind-on-terminating-exception on} or @code{set
|
|
-unwindonsignal on} (@pxref{stack unwind settings}),
|
|
-then the dummy-frame, created by @value{GDBN} to facilitate the call
|
|
-to the program function, will be visible in the backtrace, for example
|
|
-frame @code{#3} in the following backtrace:
|
|
+due to @code{set unwind-on-terminating-exception on}, @code{set
|
|
+unwind-on-timeout on}, or @code{set unwindonsignal on} (@pxref{stack
|
|
+unwind settings}), then the dummy-frame, created by @value{GDBN} to
|
|
+facilitate the call to the program function, will be visible in the
|
|
+backtrace, for example frame @code{#3} in the following backtrace:
|
|
|
|
@smallexample
|
|
(@value{GDBP}) backtrace
|
|
@@ -20992,6 +21008,11 @@ Execution}) @value{GDBN} can place a timeout on any functions called
|
|
from @value{GDBN}. If the timeout expires and the function call is
|
|
still ongoing, then @value{GDBN} will interrupt the program.
|
|
|
|
+If a function called from @value{GDBN} is interrupted by a timeout,
|
|
+then by default the inferior is left in the frame where the timeout
|
|
+occurred, this behaviour can be adjusted with @samp{set
|
|
+unwind-on-timeout} (@pxref{set unwind-on-timeout}).
|
|
+
|
|
For targets that don't support asynchronous execution
|
|
(@pxref{Background Execution}) then timeouts for functions called from
|
|
@value{GDBN} are not supported, the timeout settings described below
|
|
diff --git a/gdb/infcall.c b/gdb/infcall.c
|
|
--- a/gdb/infcall.c
|
|
+++ b/gdb/infcall.c
|
|
@@ -218,6 +218,27 @@ show_unwind_on_terminating_exception_p (struct ui_file *file, int from_tty,
|
|
value);
|
|
}
|
|
|
|
+/* This boolean tells GDB what to do if an inferior function, called from
|
|
+ GDB, times out. If true, GDB unwinds the stack and restores the context
|
|
+ to what it was before the call. When false, GDB leaves the thread as it
|
|
+ is at the point of the timeout.
|
|
+
|
|
+ The default is to stop in the frame where the timeout occurred. */
|
|
+
|
|
+static bool unwind_on_timeout_p = false;
|
|
+
|
|
+/* Implement 'show unwind-on-timeout'. */
|
|
+
|
|
+static void
|
|
+show_unwind_on_timeout_p (struct ui_file *file, int from_tty,
|
|
+ struct cmd_list_element *c, const char *value)
|
|
+{
|
|
+ gdb_printf (file,
|
|
+ _("Unwinding of stack if a timeout occurs "
|
|
+ "while in a call dummy is %s.\n"),
|
|
+ value);
|
|
+}
|
|
+
|
|
/* Perform the standard coercions that are specified
|
|
for arguments to be passed to C, Ada or Fortran functions.
|
|
|
|
@@ -574,6 +595,16 @@ struct call_thread_fsm : public thread_fsm
|
|
bool should_stop (struct thread_info *thread) override;
|
|
|
|
bool should_notify_stop () override;
|
|
+
|
|
+ /* Record that this thread timed out while performing an infcall. */
|
|
+ void timed_out ()
|
|
+ {
|
|
+ m_timed_out = true;
|
|
+ }
|
|
+
|
|
+private:
|
|
+ /* Set true if the thread timed out while performing an infcall. */
|
|
+ bool m_timed_out = false;
|
|
};
|
|
|
|
/* Allocate a new call_thread_fsm object. */
|
|
@@ -649,7 +680,8 @@ call_thread_fsm::should_notify_stop ()
|
|
|
|
infcall_debug_printf ("inferior call didn't complete fully");
|
|
|
|
- if (stopped_by_random_signal && unwind_on_signal_p)
|
|
+ if ((stopped_by_random_signal && unwind_on_signal_p)
|
|
+ || (m_timed_out && unwind_on_timeout_p))
|
|
{
|
|
infcall_debug_printf ("unwind-on-signal is on, don't notify");
|
|
return false;
|
|
@@ -742,6 +774,9 @@ struct infcall_timer_controller
|
|
|
|
infcall_debug_printf ("Stopping thread %s",
|
|
m_thread->ptid.to_string ().c_str ());
|
|
+ call_thread_fsm *fsm
|
|
+ = gdb::checked_static_cast<call_thread_fsm *> (m_thread->thread_fsm ());
|
|
+ fsm->timed_out ();
|
|
target_stop (m_thread->ptid);
|
|
}
|
|
};
|
|
@@ -1744,14 +1779,27 @@ When the function is done executing, GDB will silently stop."),
|
|
/* A timeout results in a signal being sent to the inferior. */
|
|
gdb_assert (stopped_by_random_signal);
|
|
|
|
- /* Indentation is weird here. A later patch is going to move the
|
|
- following block into an if/else, so I'm leaving the indentation
|
|
- here to minimise the later patch.
|
|
+ if (unwind_on_timeout_p)
|
|
+ {
|
|
+ /* The user wants the context restored. */
|
|
+
|
|
+ /* We must get back to the frame we were before the
|
|
+ dummy call. */
|
|
+ dummy_frame_pop (dummy_id, call_thread.get ());
|
|
|
|
- Also, the error message used below refers to 'set
|
|
- unwind-on-timeout' which doesn't exist yet. This will be added
|
|
- in a later commit, I'm leaving this in for now to minimise the
|
|
- churn caused by the commit that adds unwind-on-timeout. */
|
|
+ /* We also need to restore inferior status to that before the
|
|
+ dummy call. */
|
|
+ restore_infcall_control_state (inf_status.release ());
|
|
+
|
|
+ error (_("\
|
|
+The program being debugged timed out while in a function called from GDB.\n\
|
|
+GDB has restored the context to what it was before the call.\n\
|
|
+To change this behavior use \"set unwind-on-timeout off\".\n\
|
|
+Evaluation of the expression containing the function\n\
|
|
+(%s) will be abandoned."),
|
|
+ name.c_str ());
|
|
+ }
|
|
+ else
|
|
{
|
|
/* The user wants to stay in the frame where we stopped
|
|
(default). Discard inferior status, we're not at the same
|
|
@@ -1877,6 +1925,20 @@ The default is to unwind the frame."),
|
|
show_unwind_on_terminating_exception_p,
|
|
&setlist, &showlist);
|
|
|
|
+ add_setshow_boolean_cmd ("unwind-on-timeout", no_class,
|
|
+ &unwind_on_timeout_p, _("\
|
|
+Set unwinding of stack if a timeout occurs while in a call dummy."), _("\
|
|
+Show unwinding of stack if a timeout occurs while in a call dummy."),
|
|
+ _("\
|
|
+The unwind on timeout flag lets the user determine what gdb should do if\n\
|
|
+gdb times out while in a function called from gdb. If set, gdb unwinds\n\
|
|
+the stack and restores the context to what it was before the call. If\n\
|
|
+unset, gdb leaves the inferior in the frame where the timeout occurred.\n\
|
|
+The default is to stop in the frame where the timeout occurred."),
|
|
+ NULL,
|
|
+ show_unwind_on_timeout_p,
|
|
+ &setlist, &showlist);
|
|
+
|
|
add_setshow_uinteger_cmd ("direct-call-timeout", no_class,
|
|
&direct_call_timeout, _("\
|
|
Set the timeout, for direct calls to inferior function calls."), _("\
|
|
diff --git a/gdb/testsuite/gdb.base/infcall-timeout.exp b/gdb/testsuite/gdb.base/infcall-timeout.exp
|
|
--- a/gdb/testsuite/gdb.base/infcall-timeout.exp
|
|
+++ b/gdb/testsuite/gdb.base/infcall-timeout.exp
|
|
@@ -28,7 +28,11 @@ if { [build_executable "failed to prepare" ${binfile} "${srcfile}" \
|
|
# then adjust the direct-call-timeout, and make an inferior function
|
|
# call that will never return. GDB should eventually timeout and stop
|
|
# the inferior.
|
|
-proc run_test { target_async target_non_stop non_stop } {
|
|
+#
|
|
+# When UNWIND is "off" the inferior wil be left in the frame where the
|
|
+# timeout occurs, otherwise, when UNWIND is "on", GDB should unwind
|
|
+# back to the frame where the inferior call was made.
|
|
+proc run_test { target_async target_non_stop non_stop unwind } {
|
|
save_vars { ::GDBFLAGS } {
|
|
append ::GDBFLAGS \
|
|
" -ex \"maint set target-non-stop $target_non_stop\""
|
|
@@ -45,28 +49,43 @@ proc run_test { target_async target_non_stop non_stop } {
|
|
}
|
|
|
|
gdb_test_no_output "set direct-call-timeout 5"
|
|
+ gdb_test_no_output "set unwind-on-timeout $unwind"
|
|
+
|
|
+ if { $unwind } {
|
|
+ gdb_test "print function_that_never_returns ()" \
|
|
+ [multi_line \
|
|
+ "The program being debugged timed out while in a function called from GDB\\." \
|
|
+ "GDB has restored the context to what it was before the call\\." \
|
|
+ "To change this behavior use \"set unwind-on-timeout off\"\\." \
|
|
+ "Evaluation of the expression containing the function" \
|
|
+ "\\(function_that_never_returns\\) will be abandoned\\."]
|
|
|
|
- # When non-stop mode is off we get slightly different output from GDB.
|
|
- if { ([target_info gdb_protocol] == "remote"
|
|
- || [target_info gdb_protocol] == "extended-remote")
|
|
- && !$target_non_stop } {
|
|
- set stopped_line_pattern "Program received signal SIGINT, Interrupt\\."
|
|
+ gdb_test "bt" \
|
|
+ "#0\\s+main \\(\\).*"
|
|
} else {
|
|
- set stopped_line_pattern "Program stopped\\."
|
|
- }
|
|
+ # When non-stop mode is off we get slightly different output from GDB.
|
|
+ if { ([target_info gdb_protocol] == "remote"
|
|
+ || [target_info gdb_protocol] == "extended-remote")
|
|
+ && !$target_non_stop } {
|
|
+ set stopped_line_pattern "Program received signal SIGINT, Interrupt\\."
|
|
+ } else {
|
|
+ set stopped_line_pattern "Program stopped\\."
|
|
+ }
|
|
|
|
- gdb_test "print function_that_never_returns ()" \
|
|
- [multi_line \
|
|
- $stopped_line_pattern \
|
|
- ".*" \
|
|
- "The program being debugged timed out while in a function called from GDB\\." \
|
|
- "GDB remains in the frame where the timeout occurred\\." \
|
|
- "To change this behavior use \"set unwind-on-timeout on\"\\." \
|
|
- "Evaluation of the expression containing the function" \
|
|
- "\\(function_that_never_returns\\) will be abandoned\\." \
|
|
- "When the function is done executing, GDB will silently stop\\."]
|
|
-
|
|
- gdb_test "bt" ".* function_that_never_returns .*<function called from gdb>.*"
|
|
+ gdb_test "print function_that_never_returns ()" \
|
|
+ [multi_line \
|
|
+ $stopped_line_pattern \
|
|
+ ".*" \
|
|
+ "The program being debugged timed out while in a function called from GDB\\." \
|
|
+ "GDB remains in the frame where the timeout occurred\\." \
|
|
+ "To change this behavior use \"set unwind-on-timeout on\"\\." \
|
|
+ "Evaluation of the expression containing the function" \
|
|
+ "\\(function_that_never_returns\\) will be abandoned\\." \
|
|
+ "When the function is done executing, GDB will silently stop\\."]
|
|
+
|
|
+ gdb_test "bt" \
|
|
+ ".* function_that_never_returns .*<function called from gdb>.*"
|
|
+ }
|
|
}
|
|
|
|
foreach_with_prefix target_async { "on" "off" } {
|
|
@@ -88,7 +107,9 @@ foreach_with_prefix target_async { "on" "off" } {
|
|
continue
|
|
}
|
|
|
|
- run_test $target_async $target_non_stop $non_stop
|
|
+ foreach_with_prefix unwind { "on" "off" } {
|
|
+ run_test $target_async $target_non_stop $non_stop $unwind
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp
|
|
--- a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp
|
|
+++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp
|
|
@@ -41,7 +41,12 @@ set segfault_line [gdb_get_line_number "Segfault here"]
|
|
# thread, on which the inferior call relies, either hits a breakpoint
|
|
# (when OTHER_THREAD_BP is true), or crashes (when OTHER_THREAD_BP is
|
|
# false).
|
|
-proc run_test { target_async target_non_stop non_stop other_thread_bp } {
|
|
+#
|
|
+# When UNWIND is "on" GDB will unwind the thread which performed the
|
|
+# inferior function call back to the state where the inferior call was
|
|
+# made (when the inferior call times out). Otherwise, when UNWIND is
|
|
+# "off", the inferior is left in the frame where the timeout occurred.
|
|
+proc run_test { target_async target_non_stop non_stop other_thread_bp unwind } {
|
|
save_vars { ::GDBFLAGS } {
|
|
append ::GDBFLAGS " -ex \"maint set target-non-stop $target_non_stop\""
|
|
append ::GDBFLAGS " -ex \"maint non-stop $non_stop\""
|
|
@@ -72,6 +77,7 @@ proc run_test { target_async target_non_stop non_stop other_thread_bp } {
|
|
# for this timeout. For now though, we just hope 5 seconds is
|
|
# enough.
|
|
gdb_test_no_output "set indirect-call-timeout 5"
|
|
+ gdb_test_no_output "set unwind-on-timeout $unwind"
|
|
|
|
gdb_breakpoint \
|
|
"${::srcfile}:${::cond_bp_line} if (condition_func ())"
|
|
@@ -92,27 +98,43 @@ proc run_test { target_async target_non_stop non_stop other_thread_bp } {
|
|
"get number for segfault breakpoint"]
|
|
}
|
|
|
|
- # When non-stop mode is off we get slightly different output from GDB.
|
|
- if { ([target_info gdb_protocol] == "remote"
|
|
- || [target_info gdb_protocol] == "extended-remote")
|
|
- && !$target_non_stop} {
|
|
- set stopped_line_pattern "Thread ${::decimal} \"\[^\r\n\"\]+\" received signal SIGINT, Interrupt\\."
|
|
+ if { $unwind } {
|
|
+ gdb_test "continue" \
|
|
+ [multi_line \
|
|
+ "Error in testing condition for breakpoint ${bp_num}:" \
|
|
+ "The program being debugged timed out while in a function called from GDB\\." \
|
|
+ "GDB has restored the context to what it was before the call\\." \
|
|
+ "To change this behavior use \"set unwind-on-timeout off\"\\." \
|
|
+ "Evaluation of the expression containing the function" \
|
|
+ "\\(condition_func\\) will be abandoned\\.(\r\n\\\[New Thread \[^\r\n\]+\\\])*" \
|
|
+ "" \
|
|
+ "Thread ${::decimal}\[^\r\n\]*hit Breakpoint ${bp_num}, \[^\r\n\]+" \
|
|
+ "\[^\r\n\]+ Conditional breakpoint here\\. \[^\r\n\]+"] \
|
|
+ "expected timeout waiting for inferior call to complete"
|
|
} else {
|
|
- set stopped_line_pattern "Thread ${::decimal} \"\[^\r\n\"\]+\" stopped\\."
|
|
- }
|
|
+ # When non-stop mode is off we get slightly different output from GDB.
|
|
+ if { ([target_info gdb_protocol] == "remote"
|
|
+ || [target_info gdb_protocol] == "extended-remote")
|
|
+ && !$target_non_stop} {
|
|
+ set stopped_line_pattern \
|
|
+ "Thread ${::decimal} \"\[^\r\n\"\]+\" received signal SIGINT, Interrupt\\."
|
|
+ } else {
|
|
+ set stopped_line_pattern "Thread ${::decimal} \"\[^\r\n\"\]+\" stopped\\."
|
|
+ }
|
|
|
|
- gdb_test "continue" \
|
|
- [multi_line \
|
|
- $stopped_line_pattern \
|
|
- ".*" \
|
|
- "Error in testing condition for breakpoint ${bp_num}:" \
|
|
- "The program being debugged timed out while in a function called from GDB\\." \
|
|
- "GDB remains in the frame where the timeout occurred\\." \
|
|
- "To change this behavior use \"set unwind-on-timeout on\"\\." \
|
|
- "Evaluation of the expression containing the function" \
|
|
- "\\(condition_func\\) will be abandoned\\." \
|
|
- "When the function is done executing, GDB will silently stop\\."] \
|
|
- "expected timeout waiting for inferior call to complete"
|
|
+ gdb_test "continue" \
|
|
+ [multi_line \
|
|
+ "$stopped_line_pattern" \
|
|
+ ".*" \
|
|
+ "Error in testing condition for breakpoint ${bp_num}:" \
|
|
+ "The program being debugged timed out while in a function called from GDB\\." \
|
|
+ "GDB remains in the frame where the timeout occurred\\." \
|
|
+ "To change this behavior use \"set unwind-on-timeout on\"\\." \
|
|
+ "Evaluation of the expression containing the function" \
|
|
+ "\\(condition_func\\) will be abandoned\\." \
|
|
+ "When the function is done executing, GDB will silently stop\\."] \
|
|
+ "expected timeout waiting for inferior call to complete"
|
|
+ }
|
|
|
|
# Remember that other thread that either crashed (with a segfault)
|
|
# or hit a breakpoint? Now that the inferior call has timed out,
|
|
@@ -158,8 +180,11 @@ foreach_with_prefix target_async {"on" "off" } {
|
|
# disabled.
|
|
continue
|
|
}
|
|
- foreach_with_prefix other_thread_bp { true false } {
|
|
- run_test $target_async $target_non_stop $non_stop $other_thread_bp
|
|
+ foreach_with_prefix unwind {"off" "on"} {
|
|
+ foreach_with_prefix other_thread_bp { true false } {
|
|
+ run_test $target_async $target_non_stop $non_stop \
|
|
+ $other_thread_bp $unwind
|
|
+ }
|
|
}
|
|
}
|
|
}
|