From c194fbe18b3dc65535031125bdaab04383f892f7 Mon Sep 17 00:00:00 2001 From: Mark Kettenis Date: Sun, 6 May 2001 17:00:44 +0000 Subject: [PATCH] Implement attach/detach for multi-threaded programs on Linux. * thread-db.c (keep_thread_db): Adjust comment. (deactivate_target): Removed. (thread_db_new_objfile): Don't call deactivate_target. Implement guts of deactivate_target inline instead. (attach_thread): Call ATTACH_LWP unconditionally if defined. (thread_db_attach): New function. (thread_db_detach): Don't call deactivate_target. Do necessary cleanup inline instead. Set inferior_ptid to LWP corresponding to the current user-level thread. (thread_db_kill): Set inferior_ptid to LWP corresponding to the current user-level thread. (thread_db_create_inferior): Deactivate target vector if KEEP_THREAD_DB is zero. (thread_db_mourn_inferior): Don't call deactivate_target. Do necessary cleanup inline instead. (init_thread_db_ops): Initialize to_attach field to thread_db_attach. * lin-lwp.c (lin_lwp_mourn_inferior): Remove prototype. (stop_wait_callback): Add prototype. (init_lwp_list): Add comment about when to re-initialize the LWP list. (lin_lwp_attach_lwp): Only call ptrace for cloned processes. Avoid adding publicates to the LWP list. Only mark an LWP as signalled if it doesn't correspond to a cloned process. (lin_lwp_attach): Add initial process to the LWP list. Make sure it's stopped and fake a SIGSTOP. (detach_callback): New function. (lin_lwp_detach): Implement. (lin_lwp_create_inferior): Don't re-initialize LWP list here. Call child_ops.to_create_inferior directly instead of via target_beneath local. (lin_lwp_mourn_inferior): Call child_ops.to_mourn_inferior directly instead of via target_beneath local. --- gdb/ChangeLog | 37 ++++++++++++++++ gdb/lin-lwp.c | 112 ++++++++++++++++++++++++++++++++++++------------ gdb/thread-db.c | 73 +++++++++++++++++++++---------- 3 files changed, 172 insertions(+), 50 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index fd5483ba33e..28555d9c160 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,40 @@ +2001-05-06 Mark Kettenis + + Implement attach/detach for multi-threaded programs on Linux. + * thread-db.c (keep_thread_db): Adjust comment. + (deactivate_target): Removed. + (thread_db_new_objfile): Don't call deactivate_target. Implement + guts of deactivate_target inline instead. + (attach_thread): Call ATTACH_LWP unconditionally if defined. + (thread_db_attach): New function. + (thread_db_detach): Don't call deactivate_target. Do necessary + cleanup inline instead. Set inferior_ptid to LWP corresponding to + the current user-level thread. + (thread_db_kill): Set inferior_ptid to LWP corresponding to the + current user-level thread. + (thread_db_create_inferior): Deactivate target vector if + KEEP_THREAD_DB is zero. + (thread_db_mourn_inferior): Don't call deactivate_target. Do + necessary cleanup inline instead. + (init_thread_db_ops): Initialize to_attach field to + thread_db_attach. + * lin-lwp.c (lin_lwp_mourn_inferior): Remove prototype. + (stop_wait_callback): Add prototype. + (init_lwp_list): Add comment about when to re-initialize the LWP + list. + (lin_lwp_attach_lwp): Only call ptrace for cloned processes. + Avoid adding publicates to the LWP list. Only mark an LWP as + signalled if it doesn't correspond to a cloned process. + (lin_lwp_attach): Add initial process to the LWP list. Make sure + it's stopped and fake a SIGSTOP. + (detach_callback): New function. + (lin_lwp_detach): Implement. + (lin_lwp_create_inferior): Don't re-initialize LWP list here. + Call child_ops.to_create_inferior directly instead of via + target_beneath local. + (lin_lwp_mourn_inferior): Call child_ops.to_mourn_inferior + directly instead of via target_beneath local. + 2001-05-06 Eli Zaretskii * symtab.c (lookup_symtab_1, lookup_partial_symtab): Use basename diff --git a/gdb/lin-lwp.c b/gdb/lin-lwp.c index 6df4d0dab3e..58ad3c5857e 100644 --- a/gdb/lin-lwp.c +++ b/gdb/lin-lwp.c @@ -151,10 +151,13 @@ static sigset_t blocked_mask; /* Prototypes for local functions. */ -static void lin_lwp_mourn_inferior (void); +static int stop_wait_callback (struct lwp_info *lp, void *data); -/* Initialize the list of LWPs. */ +/* Initialize the list of LWPs. Note that this module, contrary to + what GDB's generic threads layer does for its thread list, + re-initializes the LWP lists whenever we mourn or detach (which + doesn't involve mourning) the inferior. */ static void init_lwp_list (void) @@ -344,26 +347,96 @@ lin_lwp_attach_lwp (ptid_t ptid, int verbose) if (verbose) printf_filtered ("[New %s]\n", target_pid_to_str (ptid)); - if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0) + /* We assume that we're already tracing the initial process. */ + if (is_cloned (ptid) && ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0) error ("Can't attach %s: %s", target_pid_to_str (ptid), strerror (errno)); - lp = add_lwp (ptid); - lp->signalled = 1; + lp = find_lwp_pid (ptid); + if (lp == NULL) + lp = add_lwp (ptid); + + if (is_cloned (ptid)) + lp->signalled = 1; } static void lin_lwp_attach (char *args, int from_tty) { + struct lwp_info *lp; + /* FIXME: We should probably accept a list of process id's, and attach all of them. */ - error("Not implemented yet"); + child_ops.to_attach (args, from_tty); + + /* Add the initial process as the first LWP to the list. */ + lp = add_lwp (BUILD_LWP (inferior_ptid, inferior_ptid)); + + /* Make sure the initial process is stopped. The user-level threads + layer might want to poke around in the inferior, and that won't + work if things haven't stabilized yet. */ + lp->signalled = 1; + stop_wait_callback (lp, NULL); + gdb_assert (lp->status == 0); + + /* Fake the SIGSTOP that core GDB expects. */ + lp->status = W_STOPCODE (SIGSTOP); +} + +static int +detach_callback (struct lwp_info *lp, void *data) +{ + gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status)); + + if (debug_lin_lwp && lp->status) + fprintf_unfiltered (gdb_stdlog, "Pending %s for LWP %d on detach.\n", + strsignal (WSTOPSIG (lp->status)), GET_LWP (lp->ptid)); + + while (lp->signalled && lp->stopped) + { + if (ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, + WSTOPSIG (lp->status)) < 0) + error ("Can't continue %s: %s", target_pid_to_str (lp->ptid), + strerror (errno)); + + lp->stopped = 0; + lp->status = 0; + stop_wait_callback (lp, NULL); + + gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status)); + } + + if (is_cloned (lp->ptid)) + { + if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0, + WSTOPSIG (lp->status)) < 0) + error ("Can't detach %s: %s", target_pid_to_str (lp->ptid), + strerror (errno)); + + delete_lwp (lp->ptid); + } + + return 0; } static void lin_lwp_detach (char *args, int from_tty) { - /* FIXME: Provide implementation when we implement lin_lwp_attach. */ - error ("Not implemented yet"); + iterate_over_lwps (detach_callback, NULL); + + /* Only the initial (uncloned) process should be left right now. */ + gdb_assert (num_lwps == 1); + + trap_ptid = null_ptid; + + /* Destroy LWP info; it's no longer valid. */ + init_lwp_list (); + + /* Restore the original signal mask. */ + sigprocmask (SIG_SETMASK, &normal_mask, NULL); + sigemptyset (&blocked_mask); + + inferior_ptid = GET_PID (inferior_ptid); + child_ops.to_detach (args, from_tty); } @@ -899,37 +972,22 @@ lin_lwp_kill (void) static void lin_lwp_create_inferior (char *exec_file, char *allargs, char **env) { - struct target_ops *target_beneath; - - init_lwp_list (); - -#if 0 - target_beneath = find_target_beneath (&lin_lwp_ops); -#else - target_beneath = &child_ops; -#endif - target_beneath->to_create_inferior (exec_file, allargs, env); + child_ops.to_create_inferior (exec_file, allargs, env); } static void lin_lwp_mourn_inferior (void) { - struct target_ops *target_beneath; + trap_ptid = null_ptid; + /* Destroy LWP info; it's no longer valid. */ init_lwp_list (); - trap_ptid = null_ptid; - /* Restore the original signal mask. */ sigprocmask (SIG_SETMASK, &normal_mask, NULL); sigemptyset (&blocked_mask); -#if 0 - target_beneath = find_target_beneath (&lin_lwp_ops); -#else - target_beneath = &child_ops; -#endif - target_beneath->to_mourn_inferior (); + child_ops.to_mourn_inferior (); } static void diff --git a/gdb/thread-db.c b/gdb/thread-db.c index 8c1bdba4411..ab94429bb8e 100644 --- a/gdb/thread-db.c +++ b/gdb/thread-db.c @@ -56,7 +56,8 @@ static void (*target_new_objfile_chain) (struct objfile *objfile); /* Non-zero if we're using this module's target vector. */ static int using_thread_db; -/* Non-zero if we musn't deactivate this module's target vector. */ +/* Non-zero if we have to keep this module's target vector active + across re-runs. */ static int keep_thread_db; /* Non-zero if we have determined the signals used by the threads @@ -506,20 +507,6 @@ disable_thread_signals (void) #endif } -static void -deactivate_target (void) -{ - /* Forget about the child's process ID. We shouldn't need it - anymore. */ - proc_handle.pid = 0; - - if (! keep_thread_db) - { - using_thread_db = 0; - unpush_target (&thread_db_ops); - } -} - static void thread_db_new_objfile (struct objfile *objfile) { @@ -528,11 +515,15 @@ thread_db_new_objfile (struct objfile *objfile) if (objfile == NULL) { /* All symbols have been discarded. If the thread_db target is - active, deactivate it now, even if the application was linked - statically against the thread library. */ - keep_thread_db = 0; + active, deactivate it now. */ if (using_thread_db) - deactivate_target (); + { + gdb_assert (proc_handle.pid == 0); + unpush_target (&thread_db_ops); + using_thread_db = 0; + } + + keep_thread_db = 0; goto quit; } @@ -612,8 +603,7 @@ attach_thread (ptid_t ptid, const td_thrhandle_t *th_p, /* Under Linux, we have to attach to each and every thread. */ #ifdef ATTACH_LWP - if (ti_p->ti_lid != GET_PID (ptid)) - ATTACH_LWP (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid)), 0); + ATTACH_LWP (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid)), 0); #endif /* Enable thread event reporting for this thread. */ @@ -623,6 +613,23 @@ attach_thread (ptid_t ptid, const td_thrhandle_t *th_p, target_pid_to_str (ptid), thread_db_err_str (err)); } +static void +thread_db_attach (char *args, int from_tty) +{ + target_beneath->to_attach (args, from_tty); + + /* Destroy thread info; it's no longer valid. */ + init_thread_list (); + + /* The child process is now the actual multi-threaded + program. Snatch its process ID... */ + proc_handle.pid = GET_PID (inferior_ptid); + + /* ...and perform the remaining initialization steps. */ + enable_thread_event_reporting (); + thread_db_find_new_threads(); +} + static void detach_thread (ptid_t ptid, int verbose) { @@ -634,7 +641,14 @@ static void thread_db_detach (char *args, int from_tty) { disable_thread_event_reporting (); - deactivate_target (); + + /* There's no need to save & restore inferior_ptid here, since the + inferior is supposed to be survive this function call. */ + inferior_ptid = lwp_from_thread (inferior_ptid); + + /* Forget about the child's process ID. We shouldn't need it + anymore. */ + proc_handle.pid = 0; target_beneath->to_detach (args, from_tty); } @@ -860,12 +874,21 @@ thread_db_store_registers (int regno) static void thread_db_kill (void) { + /* There's no need to save & restore inferior_ptid here, since the + inferior isn't supposed to survive this function call. */ + inferior_ptid = lwp_from_thread (inferior_ptid); target_beneath->to_kill (); } static void thread_db_create_inferior (char *exec_file, char *allargs, char **env) { + if (! keep_thread_db) + { + unpush_target (&thread_db_ops); + using_thread_db = 0; + } + target_beneath->to_create_inferior (exec_file, allargs, env); } @@ -888,7 +911,10 @@ static void thread_db_mourn_inferior (void) { remove_thread_event_breakpoints (); - deactivate_target (); + + /* Forget about the child's process ID. We shouldn't need it + anymore. */ + proc_handle.pid = 0; target_beneath->to_mourn_inferior (); } @@ -996,6 +1022,7 @@ init_thread_db_ops (void) thread_db_ops.to_shortname = "multi-thread"; thread_db_ops.to_longname = "multi-threaded child process."; thread_db_ops.to_doc = "Threads and pthreads support."; + thread_db_ops.to_attach = thread_db_attach; thread_db_ops.to_detach = thread_db_detach; thread_db_ops.to_resume = thread_db_resume; thread_db_ops.to_wait = thread_db_wait; -- 2.30.2