* 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 Mark Kettenis <kettenis@gnu.org>
+
+ 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 <eliz@is.elta.co.il>
* symtab.c (lookup_symtab_1, lookup_partial_symtab): Use basename
\f
/* Prototypes for local functions. */
-static void lin_lwp_mourn_inferior (void);
+static int stop_wait_callback (struct lwp_info *lp, void *data);
\f
-/* 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)
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);
}
\f
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
/* 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
#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)
{
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;
}
/* 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. */
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)
{
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);
}
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);
}
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 ();
}
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;