aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/NEWS9
-rw-r--r--gdb/doc/gdb.texinfo31
-rw-r--r--gdb/infcall.c78
-rw-r--r--gdb/testsuite/gdb.base/infcall-timeout.exp63
-rw-r--r--gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp69
5 files changed, 194 insertions, 56 deletions
diff --git a/gdb/NEWS b/gdb/NEWS
index a41a72b..72ec979 100644
--- 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
index 5df88be5..f87c2c7 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20949,6 +20949,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
@@ -20980,11 +20996,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
@@ -21009,6 +21025,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
index 7dc1324..bbe9064 100644
--- 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 @@ private:
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);
}
};
@@ -1749,14 +1784,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
@@ -1882,6 +1930,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
index e6a03b2..ad4dbe3 100644
--- 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
index 67dabce..8f17f13 100644
--- 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\\." \
+ "" \
+ "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
+ }
}
}
}