diff options
-rw-r--r-- | gdb/infrun.c | 158 | ||||
-rw-r--r-- | gdb/linux-nat.c | 248 | ||||
-rw-r--r-- | gdb/linux-nat.h | 2 | ||||
-rw-r--r-- | gdb/target-delegates.c | 24 | ||||
-rw-r--r-- | gdb/target.c | 7 | ||||
-rw-r--r-- | gdb/target.h | 2 | ||||
-rw-r--r-- | gdb/target/waitstatus.c | 1 | ||||
-rw-r--r-- | gdb/target/waitstatus.h | 31 |
8 files changed, 281 insertions, 192 deletions
diff --git a/gdb/infrun.c b/gdb/infrun.c index 0590310..f778667 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1583,6 +1583,7 @@ step_over_info_valid_p (void) /* Return true if THREAD is doing a displaced step. */ static bool +ATTRIBUTE_UNUSED displaced_step_in_progress_thread (thread_info *thread) { gdb_assert (thread != nullptr); @@ -1897,6 +1898,31 @@ static displaced_step_finish_status displaced_step_finish (thread_info *event_thread, const target_waitstatus &event_status) { + /* Check whether the parent is displaced stepping. */ + struct regcache *regcache = get_thread_regcache (event_thread); + struct gdbarch *gdbarch = regcache->arch (); + inferior *parent_inf = event_thread->inf; + + /* If this was a fork/vfork/clone, this event indicates that the + displaced stepping of the syscall instruction has been done, so + we perform cleanup for parent here. Also note that this + operation also cleans up the child for vfork, because their pages + are shared. */ + + /* If this is a fork (child gets its own address space copy) and + some displaced step buffers were in use at the time of the fork, + restore the displaced step buffer bytes in the child process. + + Architectures which support displaced stepping and fork events + must supply an implementation of + gdbarch_displaced_step_restore_all_in_ptid. This is not enforced + during gdbarch validation to support architectures which support + displaced stepping but not forks. */ + if (event_status.kind () == TARGET_WAITKIND_FORKED + && gdbarch_supports_displaced_stepping (gdbarch)) + gdbarch_displaced_step_restore_all_in_ptid + (gdbarch, parent_inf, event_status.child_ptid ()); + displaced_step_thread_state *displaced = &event_thread->displaced_step_state; /* Was this thread performing a displaced step? */ @@ -1916,8 +1942,39 @@ displaced_step_finish (thread_info *event_thread, /* Do the fixup, and release the resources acquired to do the displaced step. */ - return gdbarch_displaced_step_finish (displaced->get_original_gdbarch (), - event_thread, event_status); + displaced_step_finish_status status + = gdbarch_displaced_step_finish (displaced->get_original_gdbarch (), + event_thread, event_status); + + if (event_status.kind () == TARGET_WAITKIND_FORKED + || event_status.kind () == TARGET_WAITKIND_VFORKED + || event_status.kind () == TARGET_WAITKIND_THREAD_CLONED) + { + /* Since the vfork/fork/clone syscall instruction was executed + in the scratchpad, the child's PC is also within the + scratchpad. Set the child's PC to the parent's PC value, + which has already been fixed up. Note: we use the parent's + aspace here, although we're touching the child, because the + child hasn't been added to the inferior list yet at this + point. */ + + struct regcache *child_regcache + = get_thread_arch_aspace_regcache (parent_inf->process_target (), + event_status.child_ptid (), + gdbarch, + parent_inf->aspace); + /* Read PC value of parent. */ + CORE_ADDR parent_pc = regcache_read_pc (regcache); + + displaced_debug_printf ("write child pc from %s to %s", + paddress (gdbarch, + regcache_read_pc (child_regcache)), + paddress (gdbarch, parent_pc)); + + regcache_write_pc (child_regcache, parent_pc); + } + + return status; } /* Data to be passed around while handling an event. This data is @@ -5663,67 +5720,13 @@ handle_inferior_event (struct execution_control_state *ecs) case TARGET_WAITKIND_FORKED: case TARGET_WAITKIND_VFORKED: - /* Check whether the inferior is displaced stepping. */ - { - struct regcache *regcache = get_thread_regcache (ecs->event_thread); - struct gdbarch *gdbarch = regcache->arch (); - inferior *parent_inf = find_inferior_ptid (ecs->target, ecs->ptid); - - /* If this is a fork (child gets its own address space copy) - and some displaced step buffers were in use at the time of - the fork, restore the displaced step buffer bytes in the - child process. - - Architectures which support displaced stepping and fork - events must supply an implementation of - gdbarch_displaced_step_restore_all_in_ptid. This is not - enforced during gdbarch validation to support architectures - which support displaced stepping but not forks. */ - if (ecs->ws.kind () == TARGET_WAITKIND_FORKED - && gdbarch_supports_displaced_stepping (gdbarch)) - gdbarch_displaced_step_restore_all_in_ptid - (gdbarch, parent_inf, ecs->ws.child_ptid ()); - - /* If displaced stepping is supported, and thread ecs->ptid is - displaced stepping. */ - if (displaced_step_in_progress_thread (ecs->event_thread)) - { - struct regcache *child_regcache; - CORE_ADDR parent_pc; - - /* GDB has got TARGET_WAITKIND_FORKED or TARGET_WAITKIND_VFORKED, - indicating that the displaced stepping of syscall instruction - has been done. Perform cleanup for parent process here. Note - that this operation also cleans up the child process for vfork, - because their pages are shared. */ - displaced_step_finish (ecs->event_thread, ecs->ws); - /* Start a new step-over in another thread if there's one - that needs it. */ - start_step_over (); - - /* Since the vfork/fork syscall instruction was executed in the scratchpad, - the child's PC is also within the scratchpad. Set the child's PC - to the parent's PC value, which has already been fixed up. - FIXME: we use the parent's aspace here, although we're touching - the child, because the child hasn't been added to the inferior - list yet at this point. */ - - child_regcache - = get_thread_arch_aspace_regcache (parent_inf->process_target (), - ecs->ws.child_ptid (), - gdbarch, - parent_inf->aspace); - /* Read PC value of parent process. */ - parent_pc = regcache_read_pc (regcache); - - displaced_debug_printf ("write child pc from %s to %s", - paddress (gdbarch, - regcache_read_pc (child_regcache)), - paddress (gdbarch, parent_pc)); - - regcache_write_pc (child_regcache, parent_pc); - } - } + case TARGET_WAITKIND_THREAD_CLONED: + + displaced_step_finish (ecs->event_thread, ecs->ws); + + /* Start a new step-over in another thread if there's one that + needs it. */ + start_step_over (); context_switch (ecs); @@ -5739,7 +5742,7 @@ handle_inferior_event (struct execution_control_state *ecs) need to unpatch at follow/detach time instead to be certain that new breakpoints added between catchpoint hit time and vfork follow are detached. */ - if (ecs->ws.kind () != TARGET_WAITKIND_VFORKED) + if (ecs->ws.kind () == TARGET_WAITKIND_FORKED) { /* This won't actually modify the breakpoint list, but will physically remove the breakpoints from the child. */ @@ -5771,14 +5774,24 @@ handle_inferior_event (struct execution_control_state *ecs) if (!bpstat_causes_stop (ecs->event_thread->control.stop_bpstat)) { bool follow_child - = (follow_fork_mode_string == follow_fork_mode_child); + = (ecs->ws.kind () != TARGET_WAITKIND_THREAD_CLONED + && follow_fork_mode_string == follow_fork_mode_child); ecs->event_thread->set_stop_signal (GDB_SIGNAL_0); process_stratum_target *targ = ecs->event_thread->inf->process_target (); - bool should_resume = follow_fork (); + bool should_resume; + if (ecs->ws.kind () != TARGET_WAITKIND_THREAD_CLONED) + should_resume = follow_fork (); + else + { + should_resume = true; + inferior *inf = ecs->event_thread->inf; + inf->top_target ()->follow_clone (ecs->ws.child_ptid ()); + ecs->event_thread->pending_follow.set_spurious (); + } /* Note that one of these may be an invalid pointer, depending on detach_fork. */ @@ -5789,16 +5802,21 @@ handle_inferior_event (struct execution_control_state *ecs) child is marked stopped. */ /* If not resuming the parent, mark it stopped. */ - if (follow_child && !detach_fork && !non_stop && !sched_multi) + if (ecs->ws.kind () != TARGET_WAITKIND_THREAD_CLONED + && follow_child && !detach_fork && !non_stop && !sched_multi) parent->set_running (false); /* If resuming the child, mark it running. */ - if (follow_child || (!detach_fork && (non_stop || sched_multi))) + if (ecs->ws.kind () == TARGET_WAITKIND_THREAD_CLONED + || (follow_child || (!detach_fork && (non_stop || sched_multi)))) child->set_running (true); /* In non-stop mode, also resume the other branch. */ - if (!detach_fork && (non_stop - || (sched_multi && target_is_non_stop_p ()))) + if ((ecs->ws.kind () == TARGET_WAITKIND_THREAD_CLONED + && target_is_non_stop_p ()) + || (!detach_fork && (non_stop + || (sched_multi + && target_is_non_stop_p ())))) { if (follow_child) switch_to_thread (parent); diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 5ee3227..f3d02b7 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -1286,64 +1286,79 @@ get_detach_signal (struct lwp_info *lp) return 0; } -/* Detach from LP. If SIGNO_P is non-NULL, then it points to the - signal number that should be passed to the LWP when detaching. - Otherwise pass any pending signal the LWP may have, if any. */ +/* If LP has a pending fork/vfork/clone status, return it. */ -static void -detach_one_lwp (struct lwp_info *lp, int *signo_p) +static gdb::optional<target_waitstatus> +get_pending_child_status (lwp_info *lp) { - int lwpid = lp->ptid.lwp (); - int signo; - - gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status)); - - /* If the lwp/thread we are about to detach has a pending fork event, - there is a process GDB is attached to that the core of GDB doesn't know - about. Detach from it. */ - /* Check in lwp_info::status. */ if (WIFSTOPPED (lp->status) && linux_is_extended_waitstatus (lp->status)) { int event = linux_ptrace_get_extended_event (lp->status); - if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK) + if (event == PTRACE_EVENT_FORK + || event == PTRACE_EVENT_VFORK + || event == PTRACE_EVENT_CLONE) { unsigned long child_pid; int ret = ptrace (PTRACE_GETEVENTMSG, lp->ptid.lwp (), 0, &child_pid); if (ret == 0) - detach_one_pid (child_pid, 0); + { + target_waitstatus ws; + + if (event == PTRACE_EVENT_FORK) + ws.set_forked (ptid_t (child_pid, child_pid)); + else if (event == PTRACE_EVENT_VFORK) + ws.set_vforked (ptid_t (child_pid, child_pid)); + else if (event == PTRACE_EVENT_CLONE) + ws.set_thread_cloned (ptid_t (lp->ptid.pid (), child_pid)); + else + gdb_assert_not_reached ("unhandled"); + + return ws; + } else - perror_warning_with_name (_("Failed to detach fork child")); + { + perror_warning_with_name (_("Failed to retrieve event msg")); + return {}; + } } } /* Check in lwp_info::waitstatus. */ - if (lp->waitstatus.kind () == TARGET_WAITKIND_VFORKED - || lp->waitstatus.kind () == TARGET_WAITKIND_FORKED) - detach_one_pid (lp->waitstatus.child_ptid ().pid (), 0); - + if (is_new_child_status (lp->waitstatus.kind ())) + return lp->waitstatus; - /* Check in thread_info::pending_waitstatus. */ thread_info *tp = find_thread_ptid (linux_target, lp->ptid); - if (tp->has_pending_waitstatus ()) - { - const target_waitstatus &ws = tp->pending_waitstatus (); - if (ws.kind () == TARGET_WAITKIND_VFORKED - || ws.kind () == TARGET_WAITKIND_FORKED) - detach_one_pid (ws.child_ptid ().pid (), 0); - } + /* Check in thread_info::pending_waitstatus. */ + if (tp->has_pending_waitstatus () + && is_new_child_status (tp->pending_waitstatus ().kind ())) + return tp->pending_waitstatus (); /* Check in thread_info::pending_follow. */ - if (tp->pending_follow.kind () == TARGET_WAITKIND_VFORKED - || tp->pending_follow.kind () == TARGET_WAITKIND_FORKED) - detach_one_pid (tp->pending_follow.child_ptid ().pid (), 0); + if (is_new_child_status (tp->pending_follow.kind ())) + return tp->pending_follow; - if (lp->status != 0) - linux_nat_debug_printf ("Pending %s for %s on detach.", - strsignal (WSTOPSIG (lp->status)), - lp->ptid.to_string ().c_str ()); + return {}; +} + +/* Detach from LP. If SIGNO_P is non-NULL, then it points to the + signal number that should be passed to the LWP when detaching. + Otherwise pass any pending signal the LWP may have, if any. */ + +static void +detach_one_lwp (struct lwp_info *lp, int *signo_p) +{ + int lwpid = lp->ptid.lwp (); + int signo; + + /* If the lwp/thread we are about to detach has a pending fork/clone + event, there is a process/thread GDB is attached to that the core + of GDB doesn't know about. Detach from it. */ + + if (gdb::optional<target_waitstatus> ws = get_pending_child_status (lp)) + detach_one_pid (ws->child_ptid ().lwp (), 0); /* If there is a pending SIGSTOP, get rid of it. */ if (lp->signalled) @@ -1821,6 +1836,53 @@ linux_handle_syscall_trap (struct lwp_info *lp, int stopping) return 1; } +void +linux_nat_target::follow_clone (ptid_t child_ptid) +{ + lwp_info *new_lp = add_lwp (child_ptid); + new_lp->stopped = 1; + + /* If the thread_db layer is active, let it record the user + level thread id and status, and add the thread to GDB's + list. */ + if (!thread_db_notice_clone (inferior_ptid, new_lp->ptid)) + { + /* The process is not using thread_db. Add the LWP to + GDB's list. */ + add_thread (linux_target, new_lp->ptid); + } + + /* We just created NEW_LP so it cannot yet contain STATUS. */ + gdb_assert (new_lp->status == 0); + + if (!pull_pid_from_list (&stopped_pids, child_ptid.lwp (), &new_lp->status)) + internal_error (_("no saved status for clone lwp")); + + if (WSTOPSIG (new_lp->status) != SIGSTOP) + { + /* This can happen if someone starts sending signals to + the new thread before it gets a chance to run, which + have a lower number than SIGSTOP (e.g. SIGUSR1). + This is an unlikely case, and harder to handle for + fork / vfork than for clone, so we do not try - but + we handle it for clone events here. */ + + new_lp->signalled = 1; + + /* Save the wait status to report later. */ + linux_nat_debug_printf + ("waitpid of new LWP %ld, saving status %s", + (long) new_lp->ptid.lwp (), status_to_str (new_lp->status).c_str ()); + } + else + { + new_lp->status = 0; + + if (report_thread_events) + new_lp->waitstatus.set_thread_created (); + } +} + /* Handle a GNU/Linux extended wait response. If we see a clone event, we need to add the new LWP to our list (and not report the trap to higher layers). This function returns non-zero if the @@ -1861,11 +1923,9 @@ linux_handle_extended_wait (struct lwp_info *lp, int status) internal_error (_("wait returned unexpected status 0x%x"), status); } - ptid_t child_ptid (new_pid, new_pid); - if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK) { - open_proc_mem_file (child_ptid); + open_proc_mem_file (ptid_t (new_pid, new_pid)); /* The arch-specific native code may need to know about new forks even if those end up never mapped to an @@ -1902,66 +1962,18 @@ linux_handle_extended_wait (struct lwp_info *lp, int status) } if (event == PTRACE_EVENT_FORK) - ourstatus->set_forked (child_ptid); + ourstatus->set_forked (ptid_t (new_pid, new_pid)); else if (event == PTRACE_EVENT_VFORK) - ourstatus->set_vforked (child_ptid); + ourstatus->set_vforked (ptid_t (new_pid, new_pid)); else if (event == PTRACE_EVENT_CLONE) { - struct lwp_info *new_lp; - - ourstatus->set_ignore (); - linux_nat_debug_printf ("Got clone event from LWP %d, new child is LWP %ld", pid, new_pid); - new_lp = add_lwp (ptid_t (lp->ptid.pid (), new_pid)); - new_lp->stopped = 1; - new_lp->resumed = 1; + /* Save the status again, we'll use it in follow_clone. */ + add_to_pid_list (&stopped_pids, new_pid, status); - /* If the thread_db layer is active, let it record the user - level thread id and status, and add the thread to GDB's - list. */ - if (!thread_db_notice_clone (lp->ptid, new_lp->ptid)) - { - /* The process is not using thread_db. Add the LWP to - GDB's list. */ - add_thread (linux_target, new_lp->ptid); - } - - /* Even if we're stopping the thread for some reason - internal to this module, from the perspective of infrun - and the user/frontend, this new thread is running until - it next reports a stop. */ - set_running (linux_target, new_lp->ptid, true); - set_executing (linux_target, new_lp->ptid, true); - - if (WSTOPSIG (status) != SIGSTOP) - { - /* This can happen if someone starts sending signals to - the new thread before it gets a chance to run, which - have a lower number than SIGSTOP (e.g. SIGUSR1). - This is an unlikely case, and harder to handle for - fork / vfork than for clone, so we do not try - but - we handle it for clone events here. */ - - new_lp->signalled = 1; - - /* We created NEW_LP so it cannot yet contain STATUS. */ - gdb_assert (new_lp->status == 0); - - /* Save the wait status to report later. */ - linux_nat_debug_printf - ("waitpid of new LWP %ld, saving status %s", - (long) new_lp->ptid.lwp (), status_to_str (status).c_str ()); - new_lp->status = status; - } - else if (report_thread_events) - { - new_lp->waitstatus.set_thread_created (); - new_lp->status = status; - } - - return 1; + ourstatus->set_thread_cloned (ptid_t (lp->ptid.pid (), new_pid)); } return 0; @@ -3536,59 +3548,55 @@ kill_wait_callback (struct lwp_info *lp) return 0; } -/* Kill the fork children of any threads of inferior INF that are - stopped at a fork event. */ +/* Kill the fork/clone child of LP if it has an unfollowed child. */ -static void -kill_unfollowed_fork_children (struct inferior *inf) +static int +kill_unfollowed_child_callback (lwp_info *lp) { - for (thread_info *thread : inf->non_exited_threads ()) + if (gdb::optional<target_waitstatus> ws = get_pending_child_status (lp)) { - struct target_waitstatus *ws = &thread->pending_follow; - - if (ws->kind () == TARGET_WAITKIND_FORKED - || ws->kind () == TARGET_WAITKIND_VFORKED) - { - ptid_t child_ptid = ws->child_ptid (); - int child_pid = child_ptid.pid (); - int child_lwp = child_ptid.lwp (); + ptid_t child_ptid = ws->child_ptid (); + int child_pid = child_ptid.pid (); + int child_lwp = child_ptid.lwp (); - kill_one_lwp (child_lwp); - kill_wait_one_lwp (child_lwp); + kill_one_lwp (child_lwp); + kill_wait_one_lwp (child_lwp); - /* Let the arch-specific native code know this process is - gone. */ - linux_target->low_forget_process (child_pid); - } + /* Let the arch-specific native code know this process is + gone. */ + if (ws->kind () != TARGET_WAITKIND_THREAD_CLONED) + linux_target->low_forget_process (child_pid); } + + return 0; } void linux_nat_target::kill () { - /* If we're stopped while forking and we haven't followed yet, - kill the other task. We need to do this first because the + ptid_t pid_ptid (inferior_ptid.pid ()); + + /* If we're stopped while forking/cloning and we haven't followed + yet, kill the child task. We need to do this first because the parent will be sleeping if this is a vfork. */ - kill_unfollowed_fork_children (current_inferior ()); + iterate_over_lwps (pid_ptid, kill_unfollowed_child_callback); if (forks_exist_p ()) linux_fork_killall (); else { - ptid_t ptid = ptid_t (inferior_ptid.pid ()); - /* Stop all threads before killing them, since ptrace requires that the thread is stopped to successfully PTRACE_KILL. */ - iterate_over_lwps (ptid, stop_callback); + iterate_over_lwps (pid_ptid, stop_callback); /* ... and wait until all of them have reported back that they're no longer running. */ - iterate_over_lwps (ptid, stop_wait_callback); + iterate_over_lwps (pid_ptid, stop_wait_callback); /* Kill all LWP's ... */ - iterate_over_lwps (ptid, kill_callback); + iterate_over_lwps (pid_ptid, kill_callback); /* ... and wait until we've flushed all events. */ - iterate_over_lwps (ptid, kill_wait_callback); + iterate_over_lwps (pid_ptid, kill_wait_callback); } target_mourn_inferior (inferior_ptid); diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h index a9b91a5..3ed25cc 100644 --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -129,6 +129,8 @@ public: void follow_fork (inferior *, ptid_t, target_waitkind, bool, bool) override; + void follow_clone (ptid_t) override; + std::vector<static_tracepoint_marker> static_tracepoint_markers_by_strid (const char *id) override; diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c index daf4682..bee4660 100644 --- a/gdb/target-delegates.c +++ b/gdb/target-delegates.c @@ -76,6 +76,7 @@ struct dummy_target : public target_ops int insert_vfork_catchpoint (int arg0) override; int remove_vfork_catchpoint (int arg0) override; void follow_fork (inferior *arg0, ptid_t arg1, target_waitkind arg2, bool arg3, bool arg4) override; + void follow_clone (ptid_t arg0) override; int insert_exec_catchpoint (int arg0) override; int remove_exec_catchpoint (int arg0) override; void follow_exec (inferior *arg0, ptid_t arg1, const char *arg2) override; @@ -250,6 +251,7 @@ struct debug_target : public target_ops int insert_vfork_catchpoint (int arg0) override; int remove_vfork_catchpoint (int arg0) override; void follow_fork (inferior *arg0, ptid_t arg1, target_waitkind arg2, bool arg3, bool arg4) override; + void follow_clone (ptid_t arg0) override; int insert_exec_catchpoint (int arg0) override; int remove_exec_catchpoint (int arg0) override; void follow_exec (inferior *arg0, ptid_t arg1, const char *arg2) override; @@ -1545,6 +1547,28 @@ debug_target::follow_fork (inferior *arg0, ptid_t arg1, target_waitkind arg2, bo gdb_puts (")\n", gdb_stdlog); } +void +target_ops::follow_clone (ptid_t arg0) +{ + this->beneath ()->follow_clone (arg0); +} + +void +dummy_target::follow_clone (ptid_t arg0) +{ + default_follow_clone (this, arg0); +} + +void +debug_target::follow_clone (ptid_t arg0) +{ + gdb_printf (gdb_stdlog, "-> %s->follow_clone (...)\n", this->beneath ()->shortname ()); + this->beneath ()->follow_clone (arg0); + gdb_printf (gdb_stdlog, "<- %s->follow_clone (", this->beneath ()->shortname ()); + target_debug_print_ptid_t (arg0); + gdb_puts (")\n", gdb_stdlog); +} + int target_ops::insert_exec_catchpoint (int arg0) { diff --git a/gdb/target.c b/gdb/target.c index f781f7e..2fb09c2 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -2732,6 +2732,13 @@ default_follow_fork (struct target_ops *self, inferior *child_inf, internal_error (_("could not find a target to follow fork")); } +static void +default_follow_clone (struct target_ops *self, ptid_t child_ptid) +{ + /* Some target returned a clone event, but did not know how to follow it. */ + internal_error (_("could not find a target to follow clone")); +} + /* See target.h. */ void diff --git a/gdb/target.h b/gdb/target.h index 28aa927..aab390a 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -637,6 +637,8 @@ struct target_ops TARGET_DEFAULT_RETURN (1); virtual void follow_fork (inferior *, ptid_t, target_waitkind, bool, bool) TARGET_DEFAULT_FUNC (default_follow_fork); + virtual void follow_clone (ptid_t) + TARGET_DEFAULT_FUNC (default_follow_clone); virtual int insert_exec_catchpoint (int) TARGET_DEFAULT_RETURN (1); virtual int remove_exec_catchpoint (int) diff --git a/gdb/target/waitstatus.c b/gdb/target/waitstatus.c index ef432bb..3e45e4f 100644 --- a/gdb/target/waitstatus.c +++ b/gdb/target/waitstatus.c @@ -45,6 +45,7 @@ DIAGNOSTIC_ERROR_SWITCH case TARGET_WAITKIND_FORKED: case TARGET_WAITKIND_VFORKED: + case TARGET_WAITKIND_THREAD_CLONED: return string_appendf (str, ", child_ptid = %s", this->child_ptid ().to_string ().c_str ()); diff --git a/gdb/target/waitstatus.h b/gdb/target/waitstatus.h index 63bbd73..2072eb0 100644 --- a/gdb/target/waitstatus.h +++ b/gdb/target/waitstatus.h @@ -95,6 +95,13 @@ enum target_waitkind /* There are no resumed children left in the program. */ TARGET_WAITKIND_NO_RESUMED, + /* The thread was cloned. The event's ptid corresponds to the + cloned parent. The cloned child is held stopped at its entry + point, and its ptid is in the event's m_child_ptid. The target + must not add the cloned child to GDB's thread list until + target_ops::follow_clone() is called. */ + TARGET_WAITKIND_THREAD_CLONED, + /* The thread was created. */ TARGET_WAITKIND_THREAD_CREATED, @@ -102,6 +109,17 @@ enum target_waitkind TARGET_WAITKIND_THREAD_EXITED, }; +/* Determine if KIND represents an event with a new child - a fork, + vfork, or clone. */ + +static inline bool +is_new_child_status (target_waitkind kind) +{ + return (kind == TARGET_WAITKIND_FORKED + || kind == TARGET_WAITKIND_VFORKED + || kind == TARGET_WAITKIND_THREAD_CLONED); +} + /* Return KIND as a string. */ static inline const char * @@ -125,6 +143,8 @@ DIAGNOSTIC_ERROR_SWITCH return "FORKED"; case TARGET_WAITKIND_VFORKED: return "VFORKED"; + case TARGET_WAITKIND_THREAD_CLONED: + return "THREAD_CLONED"; case TARGET_WAITKIND_EXECD: return "EXECD"; case TARGET_WAITKIND_VFORK_DONE: @@ -325,6 +345,14 @@ struct target_waitstatus return *this; } + target_waitstatus &set_thread_cloned (ptid_t child_ptid) + { + this->reset (); + m_kind = TARGET_WAITKIND_THREAD_CLONED; + m_value.child_ptid = child_ptid; + return *this; + } + target_waitstatus &set_thread_created () { this->reset (); @@ -369,8 +397,7 @@ struct target_waitstatus ptid_t child_ptid () const { - gdb_assert (m_kind == TARGET_WAITKIND_FORKED - || m_kind == TARGET_WAITKIND_VFORKED); + gdb_assert (is_new_child_status (m_kind)); return m_value.child_ptid; } |