aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/infrun.c73
-rw-r--r--gdb/testsuite/gdb.threads/step-over-thread-exit.exp81
2 files changed, 118 insertions, 36 deletions
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 3987cdc..8ec8d0c 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1887,6 +1887,22 @@ displaced_step_prepare (thread_info *thread)
return status;
}
+/* True if any thread of TARGET that matches RESUME_PTID requires
+ target_thread_events enabled. This assumes TARGET does not support
+ target thread options. */
+
+static bool
+any_thread_needs_target_thread_events (process_stratum_target *target,
+ ptid_t resume_ptid)
+{
+ for (thread_info *tp : all_non_exited_threads (target, resume_ptid))
+ if (displaced_step_in_progress_thread (tp)
+ || schedlock_applies (tp)
+ || tp->thread_fsm () != nullptr)
+ return true;
+ return false;
+}
+
/* Maybe disable thread-{cloned,created,exited} event reporting after
a step-over (either in-line or displaced) finishes. */
@@ -1910,9 +1926,10 @@ update_thread_events_after_step_over (thread_info *event_thread,
else
{
/* We can only control the target-wide target_thread_events
- setting. Disable it, but only if other threads don't need it
- enabled. */
- if (!displaced_step_in_progress_any_thread ())
+ setting. Disable it, but only if other threads in the target
+ don't need it enabled. */
+ process_stratum_target *target = event_thread->inf->process_target ();
+ if (!any_thread_needs_target_thread_events (target, minus_one_ptid))
target_thread_events (false);
}
}
@@ -2489,12 +2506,25 @@ do_target_resume (ptid_t resume_ptid, bool step, enum gdb_signal sig)
else
target_thread_events (true);
}
+ else if (tp->thread_fsm () != nullptr)
+ {
+ gdb_thread_options options = GDB_THREAD_OPTION_EXIT;
+ if (target_supports_set_thread_options (options))
+ tp->set_thread_options (options);
+ else
+ target_thread_events (true);
+ }
else
{
if (target_supports_set_thread_options (0))
tp->set_thread_options (0);
- else if (!displaced_step_in_progress_any_thread ())
- target_thread_events (false);
+ else
+ {
+ process_stratum_target *resume_target = tp->inf->process_target ();
+ if (!any_thread_needs_target_thread_events (resume_target,
+ resume_ptid))
+ target_thread_events (false);
+ }
}
/* If we're resuming more than one thread simultaneously, then any
@@ -5725,6 +5755,13 @@ handle_thread_exited (execution_control_state *ecs)
ecs->event_thread->stepping_over_breakpoint = 0;
ecs->event_thread->stepping_over_watchpoint = 0;
+ /* If the thread had an FSM, then abort the command. But only after
+ finishing the step over, as in non-stop mode, aborting this
+ thread's command should not interfere with other threads. We
+ must check this before finish_step over, however, which may
+ update the thread list and delete the event thread. */
+ bool abort_cmd = (ecs->event_thread->thread_fsm () != nullptr);
+
/* Maybe the thread was doing a step-over, if so release
resources and start any further pending step-overs.
@@ -5738,6 +5775,13 @@ handle_thread_exited (execution_control_state *ecs)
the event thread has exited. */
gdb_assert (ret == 0);
+ if (abort_cmd)
+ {
+ delete_thread (ecs->event_thread);
+ ecs->event_thread = nullptr;
+ return false;
+ }
+
/* If finish_step_over started a new in-line step-over, don't
try to restart anything else. */
if (step_over_info_valid_p ())
@@ -9119,7 +9163,8 @@ normal_stop ()
if (inferior_ptid != null_ptid)
finish_ptid = ptid_t (inferior_ptid.pid ());
}
- else if (last.kind () != TARGET_WAITKIND_NO_RESUMED)
+ else if (last.kind () != TARGET_WAITKIND_NO_RESUMED
+ && last.kind () != TARGET_WAITKIND_THREAD_EXITED)
finish_ptid = inferior_ptid;
gdb::optional<scoped_finish_thread_state> maybe_finish_thread_state;
@@ -9162,7 +9207,8 @@ normal_stop ()
{
if ((last.kind () != TARGET_WAITKIND_SIGNALLED
&& last.kind () != TARGET_WAITKIND_EXITED
- && last.kind () != TARGET_WAITKIND_NO_RESUMED)
+ && last.kind () != TARGET_WAITKIND_NO_RESUMED
+ && last.kind () != TARGET_WAITKIND_THREAD_EXITED)
&& target_has_execution ()
&& previous_thread != inferior_thread ())
{
@@ -9178,7 +9224,8 @@ normal_stop ()
update_previous_thread ();
}
- if (last.kind () == TARGET_WAITKIND_NO_RESUMED)
+ if (last.kind () == TARGET_WAITKIND_NO_RESUMED
+ || last.kind () == TARGET_WAITKIND_THREAD_EXITED)
{
stop_print_frame = false;
@@ -9186,7 +9233,12 @@ normal_stop ()
if (current_ui->prompt_state == PROMPT_BLOCKED)
{
target_terminal::ours_for_output ();
- gdb_printf (_("No unwaited-for children left.\n"));
+ if (last.kind () == TARGET_WAITKIND_NO_RESUMED)
+ gdb_printf (_("No unwaited-for children left.\n"));
+ else if (last.kind () == TARGET_WAITKIND_THREAD_EXITED)
+ gdb_printf (_("Command aborted, thread exited.\n"));
+ else
+ gdb_assert_not_reached ("unhandled");
}
}
@@ -9271,7 +9323,8 @@ normal_stop ()
{
if (last.kind () != TARGET_WAITKIND_SIGNALLED
&& last.kind () != TARGET_WAITKIND_EXITED
- && last.kind () != TARGET_WAITKIND_NO_RESUMED)
+ && last.kind () != TARGET_WAITKIND_NO_RESUMED
+ && last.kind () != TARGET_WAITKIND_THREAD_EXITED)
/* Delete the breakpoint we stopped at, if it wants to be deleted.
Delete any breakpoint that is to be deleted at the next stop. */
breakpoint_auto_delete (inferior_thread ()->control.stop_bpstat);
diff --git a/gdb/testsuite/gdb.threads/step-over-thread-exit.exp b/gdb/testsuite/gdb.threads/step-over-thread-exit.exp
index ed8534c..615bd83 100644
--- a/gdb/testsuite/gdb.threads/step-over-thread-exit.exp
+++ b/gdb/testsuite/gdb.threads/step-over-thread-exit.exp
@@ -29,7 +29,7 @@ if { [build_executable "failed to prepare" $testfile \
# NS_STOP_ALL is only used if testing "set non-stop on", and indicates
# whether to have GDB explicitly stop all threads before continuing to
# thread exit.
-proc test {displaced-stepping non-stop target-non-stop schedlock ns_stop_all} {
+proc test {displaced-stepping non-stop target-non-stop schedlock cmd ns_stop_all} {
if {${non-stop} == "off" && $ns_stop_all} {
error "invalid arguments"
}
@@ -72,31 +72,58 @@ proc test {displaced-stepping non-stop target-non-stop schedlock ns_stop_all} {
gdb_test_no_output "set scheduler-locking ${schedlock}"
- gdb_test "continue" \
- "No unwaited-for children left." \
- "continue stops when thread exits"
+ if {$cmd == "continue"} {
+ gdb_test "continue" \
+ "No unwaited-for children left." \
+ "continue stops when thread exits"
+ } else {
+ gdb_test_multiple $cmd "command aborts when thread exits" {
+ -re "Command aborted, thread exited\\.\r\n$::gdb_prompt " {
+ pass $gdb_test_name
+ }
+ }
+ }
} else {
gdb_test_no_output "set scheduler-locking ${schedlock}"
- for { set i 0 } { $i < 100 } { incr i } {
- with_test_prefix "iter $i" {
- set ok 0
- set thread "<unknown>"
- gdb_test_multiple "continue" "" {
- -re -wrap "Thread ($::decimal) .*hit Breakpoint $::decimal.* my_exit_syscall .*" {
- set thread $expect_out(1,string)
- set ok 1
- }
+ if {$cmd != "continue"} {
+ set thread "<unknown>"
+ gdb_test_multiple "continue" "" {
+ -re -wrap "Thread ($::decimal) .*hit Breakpoint $::decimal.* my_exit_syscall .*" {
+ set thread $expect_out(1,string)
}
- if {!${ok}} {
- # Exit if there's a failure to avoid lengthy
- # timeouts.
- break
+ }
+ if {${non-stop}} {
+ gdb_test -nopass "thread $thread" "Switching to thread .*" \
+ "switch to event thread"
+ }
+
+ gdb_test_multiple $cmd "command aborts when thread exits" {
+ -re "Command aborted, thread exited\\.\r\n$::gdb_prompt " {
+ pass $gdb_test_name
}
+ }
+ } else {
+ for { set i 0 } { $i < 100 } { incr i } {
+ with_test_prefix "iter $i" {
+ set ok 0
+ set thread "<unknown>"
+ gdb_test_multiple "continue" "" {
+ -re -wrap "Thread ($::decimal) .*hit Breakpoint $::decimal.* my_exit_syscall .*" {
+ set thread $expect_out(1,string)
+ set ok 1
+ }
+ }
+ if {!${ok}} {
+ # Exit if there's a failure to avoid lengthy
+ # timeouts.
+ break
+ }
- if {${non-stop}} {
- gdb_test "thread $thread" "Switching to thread .*" \
- "switch to event thread"
+ if {${non-stop}} {
+ gdb_test -nopass "thread $thread" "Switching to thread .*" \
+ "switch to event thread"
+ }
}
}
}
@@ -112,13 +139,15 @@ foreach_with_prefix displaced-stepping {off auto} {
}
foreach_with_prefix schedlock {off on} {
- if {${non-stop} == "on"} {
- foreach_with_prefix ns_stop_all {0 1} {
- test ${displaced-stepping} ${non-stop} ${target-non-stop} \
- ${schedlock} ${ns_stop_all}
+ foreach_with_prefix cmd {"next" "continue"} {
+ if {${non-stop} == "on"} {
+ foreach_with_prefix ns_stop_all {0 1} {
+ test ${displaced-stepping} ${non-stop} ${target-non-stop} \
+ ${schedlock} ${cmd} ${ns_stop_all}
+ }
+ } else {
+ test ${displaced-stepping} ${non-stop} ${target-non-stop} ${schedlock} ${cmd} 0
}
- } else {
- test ${displaced-stepping} ${non-stop} ${target-non-stop} ${schedlock} 0
}
}
}