* Makefile.in (linux_nat_h): Update dependencies.

* configure.in: Check for <gnu/libc-version.h>.
	* configure: Regenerate.
	* config.in: Regenerate.
	* linux-nat.h: Include "target.h".  Add waitstatus field to
	struct lwp_info.
	* lin-lwp.c (add_lwp): Initialize waitstatus.kind.
	(lin_lwp_attach_lwp): Don't attach to LWPs we have already attached
	to.
	(lin_lwp_handle_extended): New function.  Handle clone events.
	(wait_lwp): Use lin_lwp_handle_extended.  Update comment about
	thread exit events.
	(child_wait): Handle clone events.
	(lin_lwp_wait: Use lin_lwp_handle_extended and handle clone events.
	* linux-nat.c (linux_enable_event_reporting): Turn on
	PTRACE_O_TRACECLONE.
	(linux_handle_extended_wait): Handle clone events.
	* thread-db.c: Include <gnu/libc-version.h>.
	(struct private_thread_info): Add dying flag.
	(enable_thread_event_reporting): Enable TD_DEATH for glibc 2.2 and
	higher.
	(attach_thread): Update comments.  Handle dying threads.
	(detach_thread): Set the dying flag.
	(check_event): Always call attach_thread.
This commit is contained in:
Daniel Jacobowitz 2004-03-29 18:07:14 +00:00
parent 8d5f9dcb37
commit a2f23071c9
9 changed files with 221 additions and 46 deletions

View File

@ -1,3 +1,30 @@
2004-03-29 Daniel Jacobowitz <drow@mvista.com>
* Makefile.in (linux_nat_h): Update dependencies.
* configure.in: Check for <gnu/libc-version.h>.
* configure: Regenerate.
* config.in: Regenerate.
* linux-nat.h: Include "target.h". Add waitstatus field to
struct lwp_info.
* lin-lwp.c (add_lwp): Initialize waitstatus.kind.
(lin_lwp_attach_lwp): Don't attach to LWPs we have already attached
to.
(lin_lwp_handle_extended): New function. Handle clone events.
(wait_lwp): Use lin_lwp_handle_extended. Update comment about
thread exit events.
(child_wait): Handle clone events.
(lin_lwp_wait: Use lin_lwp_handle_extended and handle clone events.
* linux-nat.c (linux_enable_event_reporting): Turn on
PTRACE_O_TRACECLONE.
(linux_handle_extended_wait): Handle clone events.
* thread-db.c: Include <gnu/libc-version.h>.
(struct private_thread_info): Add dying flag.
(enable_thread_event_reporting): Enable TD_DEATH for glibc 2.2 and
higher.
(attach_thread): Update comments. Handle dying threads.
(detach_thread): Set the dying flag.
(check_event): Always call attach_thread.
2004-03-29 Daniel Jacobowitz <drow@mvista.com>
* mips-tdep.c (mips_pdr_data): New.

View File

@ -699,7 +699,7 @@ kod_h = kod.h
language_h = language.h
libunwind_frame_h = libunwind-frame.h $(libunwind_h)
linespec_h = linespec.h
linux_nat_h = linux-nat.h
linux_nat_h = linux-nat.h $(target_h)
m2_lang_h = m2-lang.h
m68k_tdep_h = m68k-tdep.h
macroexp_h = macroexp.h

View File

@ -266,6 +266,9 @@
/* Define if you have the <dirent.h> header file. */
#undef HAVE_DIRENT_H
/* Define if you have the <gnu/libc-version.h> header file. */
#undef HAVE_GNU_LIBC_VERSION_H
/* Define if you have the <libunwind-ia64.h> header file. */
#undef HAVE_LIBUNWIND_IA64_H

2
gdb/configure vendored
View File

@ -4756,7 +4756,7 @@ else
fi
done
for ac_hdr in proc_service.h thread_db.h
for ac_hdr in proc_service.h thread_db.h gnu/libc-version.h
do
ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6

View File

@ -342,7 +342,7 @@ AC_CHECK_HEADERS(link.h)
AC_CHECK_HEADERS(machine/reg.h)
AC_CHECK_HEADERS(nlist.h)
AC_CHECK_HEADERS(poll.h sys/poll.h)
AC_CHECK_HEADERS(proc_service.h thread_db.h)
AC_CHECK_HEADERS(proc_service.h thread_db.h gnu/libc-version.h)
AC_CHECK_HEADERS(stddef.h)
AC_CHECK_HEADERS(stdlib.h)
AC_CHECK_HEADERS(stdint.h)

View File

@ -1,5 +1,5 @@
/* Multi-threaded debugging support for GNU/Linux (LWP layer).
Copyright 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
Copyright 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
This file is part of GDB.
@ -183,6 +183,8 @@ add_lwp (ptid_t ptid)
memset (lp, 0, sizeof (struct lwp_info));
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
lp->ptid = ptid;
lp->next = lwp_list;
@ -278,7 +280,7 @@ lin_lwp_open (char *args, int from_tty)
void
lin_lwp_attach_lwp (ptid_t ptid, int verbose)
{
struct lwp_info *lp;
struct lwp_info *lp, *found_lp;
gdb_assert (is_lwp (ptid));
@ -293,13 +295,17 @@ lin_lwp_attach_lwp (ptid_t ptid, int verbose)
if (verbose)
printf_filtered ("[New %s]\n", target_pid_to_str (ptid));
lp = find_lwp_pid (ptid);
found_lp = lp = find_lwp_pid (ptid);
if (lp == NULL)
lp = add_lwp (ptid);
/* We assume that we're already attached to any LWP that has an
id equal to the overall process id. */
if (GET_LWP (ptid) != GET_PID (ptid))
/* We assume that we're already attached to any LWP that has an id
equal to the overall process id, and to any LWP that is already
in our list of LWPs. If we're not seeing exit events from threads
and we've had PID wraparound since we last tried to stop all threads,
this assumption might be wrong; fortunately, this is very unlikely
to happen. */
if (GET_LWP (ptid) != GET_PID (ptid) && found_lp == NULL)
{
pid_t pid;
int status;
@ -590,6 +596,41 @@ kill_lwp (int lwpid, int signo)
return kill (lwpid, signo);
}
/* Handle a GNU/Linux extended wait response. Most of the work we
just pass off to linux_handle_extended_wait, but if it reports 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 event should be ignored and we should wait again. */
static int
lin_lwp_handle_extended (struct lwp_info *lp, int status)
{
linux_handle_extended_wait (GET_LWP (lp->ptid), status,
&lp->waitstatus);
/* TARGET_WAITKIND_SPURIOUS is used to indicate clone events. */
if (lp->waitstatus.kind == TARGET_WAITKIND_SPURIOUS)
{
struct lwp_info *new_lp;
new_lp = add_lwp (BUILD_LWP (lp->waitstatus.value.related_pid,
GET_PID (inferior_ptid)));
new_lp->cloned = 1;
new_lp->stopped = 1;
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
if (debug_lin_lwp)
fprintf_unfiltered (gdb_stdlog,
"LLHE: Got clone event from LWP %ld, resuming\n",
GET_LWP (lp->ptid));
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
return 1;
}
return 0;
}
/* Wait for LP to stop. Returns the wait status, or 0 if the LWP has
exited. */
@ -609,9 +650,11 @@ wait_lwp (struct lwp_info *lp)
pid = waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
if (pid == -1 && errno == ECHILD)
{
/* The thread has previously exited. We need to delete it now
because in the case of NPTL threads, there won't be an
exit event unless it is the main thread. */
/* The thread has previously exited. We need to delete it
now because, for some vendor 2.4 kernels with NPTL
support backported, there won't be an exit event unless
it is the main thread. 2.6 kernels will report an exit
event for each thread that exits, as expected. */
thread_dead = 1;
if (debug_lin_lwp)
fprintf_unfiltered (gdb_stdlog, "WL: %s vanished.\n",
@ -658,6 +701,17 @@ wait_lwp (struct lwp_info *lp)
gdb_assert (WIFSTOPPED (status));
/* Handle GNU/Linux's extended waitstatus for trace events. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
{
if (debug_lin_lwp)
fprintf_unfiltered (gdb_stdlog,
"WL: Handling extended status 0x%06x\n",
status);
if (lin_lwp_handle_extended (lp, status))
return wait_lwp (lp);
}
return status;
}
@ -1097,6 +1151,8 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
int status;
pid_t pid;
ourstatus->kind = TARGET_WAITKIND_IGNORE;
do
{
set_sigint_trap (); /* Causes SIGINT to be passed on to the
@ -1143,6 +1199,25 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
save_errno = EINTR;
}
/* Handle GNU/Linux's extended waitstatus for trace events. */
if (pid != -1 && WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP
&& status >> 16 != 0)
{
linux_handle_extended_wait (pid, status, ourstatus);
/* If we see a clone event, detach the child, and don't
report the event. It would be nice to offer some way to
switch into a non-thread-db based threaded mode at this
point. */
if (ourstatus->kind == TARGET_WAITKIND_SPURIOUS)
{
ptrace (PTRACE_DETACH, ourstatus->value.related_pid, 0, 0);
ourstatus->kind = TARGET_WAITKIND_IGNORE;
pid = -1;
save_errno = EINTR;
}
}
clear_sigio_trap ();
clear_sigint_trap ();
}
@ -1159,11 +1234,9 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
return minus_one_ptid;
}
/* Handle GNU/Linux's extended waitstatus for trace events. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
return linux_handle_extended_wait (pid, status, ourstatus);
if (ourstatus->kind == TARGET_WAITKIND_IGNORE)
store_waitstatus (ourstatus, status);
store_waitstatus (ourstatus, status);
return pid_to_ptid (pid);
}
@ -1371,6 +1444,20 @@ retry:
}
}
/* Handle GNU/Linux's extended waitstatus for trace events. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
{
if (debug_lin_lwp)
fprintf_unfiltered (gdb_stdlog,
"LLW: Handling extended status 0x%06x\n",
status);
if (lin_lwp_handle_extended (lp, status))
{
status = 0;
continue;
}
}
/* Check if the thread has exited. */
if ((WIFEXITED (status) || WIFSIGNALED (status)) && num_lwps > 1)
{
@ -1588,14 +1675,14 @@ retry:
else
trap_ptid = null_ptid;
/* Handle GNU/Linux's extended waitstatus for trace events. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
if (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
{
linux_handle_extended_wait (GET_LWP (lp->ptid), status, ourstatus);
return trap_ptid;
*ourstatus = lp->waitstatus;
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
}
else
store_waitstatus (ourstatus, status);
store_waitstatus (ourstatus, status);
return (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid)));
}

View File

@ -1,5 +1,5 @@
/* GNU/Linux native-dependent code common to multiple platforms.
Copyright (C) 2003 Free Software Foundation, Inc.
Copyright (C) 2003, 2004 Free Software Foundation, Inc.
This file is part of GDB.
@ -224,7 +224,8 @@ linux_enable_event_reporting (ptid_t ptid)
if (! linux_supports_tracefork ())
return;
options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC;
options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC
| PTRACE_O_TRACECLONE;
if (linux_supports_tracevforkdone ())
options |= PTRACE_O_TRACEVFORKDONE;
@ -391,11 +392,8 @@ linux_handle_extended_wait (int pid, int status,
{
int event = status >> 16;
if (event == PTRACE_EVENT_CLONE)
internal_error (__FILE__, __LINE__,
"unexpected clone event");
if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
|| event == PTRACE_EVENT_CLONE)
{
unsigned long new_pid;
int ret;
@ -406,12 +404,10 @@ linux_handle_extended_wait (int pid, int status,
if (! pull_pid_from_list (&stopped_pids, new_pid))
{
/* The new child has a pending SIGSTOP. We can't affect it until it
hits the SIGSTOP, but we're already attached.
It won't be a clone (we didn't ask for clones in the event mask)
so we can just call waitpid and wait for the SIGSTOP. */
hits the SIGSTOP, but we're already attached. */
do {
ret = waitpid (new_pid, &status, 0);
ret = waitpid (new_pid, &status,
(event == PTRACE_EVENT_CLONE) ? __WCLONE : 0);
} while (ret == -1 && errno == EINTR);
if (ret == -1)
perror_with_name ("waiting for new child");
@ -423,8 +419,13 @@ linux_handle_extended_wait (int pid, int status,
"wait returned unexpected status 0x%x", status);
}
ourstatus->kind = (event == PTRACE_EVENT_FORK)
? TARGET_WAITKIND_FORKED : TARGET_WAITKIND_VFORKED;
if (event == PTRACE_EVENT_FORK)
ourstatus->kind = TARGET_WAITKIND_FORKED;
else if (event == PTRACE_EVENT_VFORK)
ourstatus->kind = TARGET_WAITKIND_VFORKED;
else
ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
ourstatus->value.related_pid = new_pid;
return inferior_ptid;
}

View File

@ -1,5 +1,5 @@
/* Native debugging support for GNU/Linux (LWP layer).
Copyright 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
Copyright 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
This file is part of GDB.
@ -18,6 +18,8 @@
Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "target.h"
/* Structure describing an LWP. */
struct lwp_info
@ -52,6 +54,11 @@ struct lwp_info
/* Non-zero if we were stepping this LWP. */
int step;
/* If WAITSTATUS->KIND != TARGET_WAITKIND_SPURIOUS, the waitstatus
for this LWP's last event. This may correspond to STATUS above,
or to a local variable in lin_lwp_wait. */
struct target_waitstatus waitstatus;
/* Next LWP in list. */
struct lwp_info *next;
};
@ -60,7 +67,6 @@ struct lwp_info
system". */
struct mem_attrib;
struct target_ops;
struct target_waitstatus;
extern int linux_proc_xfer_memory (CORE_ADDR addr, char *myaddr, int len,
int write, struct mem_attrib *attrib,

View File

@ -1,6 +1,6 @@
/* libthread_db assisted debugging support, generic parts.
Copyright 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
Copyright 1999, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.
This file is part of GDB.
@ -35,6 +35,10 @@
#include "regcache.h"
#include "solib-svr4.h"
#ifdef HAVE_GNU_LIBC_VERSION_H
#include <gnu/libc-version.h>
#endif
#ifndef LIBTHREAD_DB_SO
#define LIBTHREAD_DB_SO "libthread_db.so.1"
#endif
@ -130,6 +134,7 @@ static CORE_ADDR td_death_bp_addr;
static void thread_db_find_new_threads (void);
static void attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
const td_thrinfo_t *ti_p, int verbose);
static void detach_thread (ptid_t ptid, int verbose);
/* Building process ids. */
@ -150,6 +155,9 @@ static void attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
struct private_thread_info
{
/* Flag set when we see a TD_DEATH event for this thread. */
unsigned int dying:1;
/* Cached thread state. */
unsigned int th_valid:1;
unsigned int ti_valid:1;
@ -491,6 +499,10 @@ enable_thread_event_reporting (void)
td_thr_events_t events;
td_notify_t notify;
td_err_e err;
#ifdef HAVE_GNU_LIBC_VERSION_H
const char *libc_version;
int libc_major, libc_minor;
#endif
/* We cannot use the thread event reporting facility if these
functions aren't available. */
@ -501,12 +513,16 @@ enable_thread_event_reporting (void)
/* Set the process wide mask saying which events we're interested in. */
td_event_emptyset (&events);
td_event_addset (&events, TD_CREATE);
#if 0
#ifdef HAVE_GNU_LIBC_VERSION_H
/* FIXME: kettenis/2000-04-23: The event reporting facility is
broken for TD_DEATH events in glibc 2.1.3, so don't enable it for
now. */
td_event_addset (&events, TD_DEATH);
libc_version = gnu_get_libc_version ();
if (sscanf (libc_version, "%d.%d", &libc_major, &libc_minor) == 2
&& (libc_major > 2 || (libc_major == 2 && libc_minor > 1)))
#endif
td_event_addset (&events, TD_DEATH);
err = td_ta_set_event_p (thread_agent, &events);
if (err != TD_OK)
@ -689,6 +705,10 @@ quit:
target_new_objfile_chain (objfile);
}
/* Attach to a new thread. This function is called when we receive a
TD_CREATE event or when we iterate over all threads and find one
that wasn't already in our list. */
static void
attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
const td_thrinfo_t *ti_p, int verbose)
@ -696,6 +716,27 @@ attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
struct thread_info *tp;
td_err_e err;
/* If we're being called after a TD_CREATE event, we may already
know about this thread. There are two ways this can happen. We
may have iterated over all threads between the thread creation
and the TD_CREATE event, for instance when the user has issued
the `info threads' command before the SIGTRAP for hitting the
thread creation breakpoint was reported. Alternatively, the
thread may have exited and a new one been created with the same
thread ID. In the first case we don't need to do anything; in
the second case we should discard information about the dead
thread and attach to the new one. */
if (in_thread_list (ptid))
{
tp = find_thread_pid (ptid);
gdb_assert (tp != NULL);
if (!tp->private->dying)
return;
delete_thread (ptid);
}
check_thread_signals ();
/* Add the thread to GDB's thread list. */
@ -741,8 +782,21 @@ thread_db_attach (char *args, int from_tty)
static void
detach_thread (ptid_t ptid, int verbose)
{
struct thread_info *thread_info;
if (verbose)
printf_unfiltered ("[%s exited]\n", target_pid_to_str (ptid));
/* Don't delete the thread now, because it still reports as active
until it has executed a few instructions after the event
breakpoint - if we deleted it now, "info threads" would cause us
to re-attach to it. Just mark it as having had a TD_DEATH
event. This means that we won't delete it from our thread list
until we notice that it's dead (via prune_threads), or until
something re-uses its thread ID. */
thread_info = find_thread_pid (ptid);
gdb_assert (thread_info != NULL);
thread_info->private->dying = 1;
}
static void
@ -847,12 +901,9 @@ check_event (ptid_t ptid)
switch (msg.event)
{
case TD_CREATE:
/* We may already know about this thread, for instance when the
user has issued the `info threads' command before the SIGTRAP
for hitting the thread creation breakpoint was reported. */
if (!in_thread_list (ptid))
attach_thread (ptid, msg.th_p, &ti, 1);
/* Call attach_thread whether or not we already know about a
thread with this thread ID. */
attach_thread (ptid, msg.th_p, &ti, 1);
break;