From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 680 invoked by alias); 19 Mar 2008 16:03:29 -0000 Received: (qmail 663 invoked by uid 22791); 19 Mar 2008 16:03:23 -0000 X-Spam-Check-By: sourceware.org Received: from NaN.false.org (HELO nan.false.org) (208.75.86.248) by sourceware.org (qpsmtpd/0.31) with ESMTP; Wed, 19 Mar 2008 16:03:02 +0000 Received: from nan.false.org (localhost [127.0.0.1]) by nan.false.org (Postfix) with ESMTP id 3FBA498149; Wed, 19 Mar 2008 16:03:00 +0000 (GMT) Received: from caradoc.them.org (22.svnf5.xdsl.nauticom.net [209.195.183.55]) by nan.false.org (Postfix) with ESMTP id 96A0C98119; Wed, 19 Mar 2008 16:02:59 +0000 (GMT) Received: from drow by caradoc.them.org with local (Exim 4.69) (envelope-from ) id 1Jc0kj-0008IK-Od; Wed, 19 Mar 2008 12:02:57 -0400 Date: Wed, 19 Mar 2008 16:03:00 -0000 From: Daniel Jacobowitz To: Pedro Alves , GDB Patches , Vladimir Prus Subject: Re: [linux] fix stepping over fork in follow-child mode. Message-ID: <20080319160257.GA31587@caradoc.them.org> Mail-Followup-To: Pedro Alves , GDB Patches , Vladimir Prus References: <200803182046.46420.pedro@codesourcery.com> <20080318234805.GA14989@caradoc.them.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20080318234805.GA14989@caradoc.them.org> User-Agent: Mutt/1.5.17 (2007-12-11) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2008-03/txt/msg00281.txt.bz2 On Tue, Mar 18, 2008 at 07:48:05PM -0400, Daniel Jacobowitz wrote: > Here's an alternative fix. Vladimir, this is also the patch I was > talking about earlier on IRC. Not tested or finished yet. Tested (x86_64-linux) and finished now. A nice side effect of this patch is that "break main; run" on a threaded program no longer displays the "[Switching to Thread ...]" message on GNU/Linux; this was an artifact of adding the TID to inferior_ptid. Any comments? Otherwise I'll commit this in a few days. Vladimir, this patch should allow you to add the initial thread to the thread list. The best place is probably linux_nat_attach and linux_nat_create_inferior. Then "info threads" will always work for the Linux native target. Similarly for remote if it doesn't already. I think we should fix this one target at a time, since changing it in the GDB core is likely to break other threading targets. -- Daniel Jacobowitz CodeSourcery 2008-03-19 Daniel Jacobowitz * gdbthread.h (add_thread_with_info): New. * linux-thread-db.c: Add some documentation. (GET_THREAD, is_lwp, is_thread): Delete. (struct private_thread_info): Remove th_valid and ti_valid. Replace ti with tid. (thread_get_info_callback): Do not add TID to the new ptid. Do not cache th or ti. (thread_db_map_id2thr, lwp_from_thread): Delete functions. (thread_from_lwp): Assert that the LWP is set. Do not add TID to the new PTID. (attach_thread): Handle an already-existing thread. Use add_thread_with_info. Cache the th and tid. (detach_thread): Verify that private was set. Remove verbose argument and printing. Update caller. (thread_db_detach): Do not adjust inferior_ptid. (clear_lwpid_callback, thread_db_resume, thread_db_kill): Delete. (check_event, find_new_threads_callback): Do not add TID to the new PTID. (thread_db_wait): Do not use lwp_from_thread. (thread_db_pid_to_str): Use the cached TID. (thread_db_extra_thread_info): Check that private is set. (same_ptid_callback): Delete. (thread_db_get_thread_local_address): Do not use it or check is_thread. Check that private is set. Assume that the thread handle is already cached. (init_thread_db_ops): Remove to_resume and to_kill. * thread.c (add_thread_with_info): New. (add_thread): Use it. * linux-nat.c (find_thread_from_lwp): Delete. (exit_lwp): Do not use it. Check print_thread_events. Print before deleting the thread. * inf-ttrace.c (inf_ttrace_wait): Use print_thread_events and printf_unfiltered for thread exits. * procfs.c (procfs_wait): Likewise. 2008-03-19 Pedro Alves * gdb.threads/fork-child-threads.exp: Test next over fork. Index: gdbthread.h =================================================================== RCS file: /cvs/src/src/gdb/gdbthread.h,v retrieving revision 1.20 diff -u -p -r1.20 gdbthread.h --- gdbthread.h 15 Mar 2008 13:53:25 -0000 1.20 +++ gdbthread.h 19 Mar 2008 15:55:36 -0000 @@ -81,6 +81,10 @@ extern struct thread_info *add_thread (p about new thread. */ extern struct thread_info *add_thread_silent (ptid_t ptid); +/* Same as add_thread, and sets the private info. */ +extern struct thread_info *add_thread_with_info (ptid_t ptid, + struct private_thread_info *); + /* Delete an existing thread list entry. */ extern void delete_thread (ptid_t); Index: inf-ttrace.c =================================================================== RCS file: /cvs/src/src/gdb/inf-ttrace.c,v retrieving revision 1.27 diff -u -p -r1.27 inf-ttrace.c --- inf-ttrace.c 29 Jan 2008 21:11:24 -0000 1.27 +++ inf-ttrace.c 19 Mar 2008 15:55:36 -0000 @@ -964,7 +964,8 @@ inf_ttrace_wait (ptid_t ptid, struct tar break; case TTEVT_LWP_EXIT: - printf_filtered(_("[%s exited]\n"), target_pid_to_str (ptid)); + if (print_thread_events) + printf_unfiltered (_("[%s exited]\n"), target_pid_to_str (ptid)); ti = find_thread_pid (ptid); gdb_assert (ti != NULL); ((struct inf_ttrace_private_thread_info *)ti->private)->dying = 1; Index: linux-nat.c =================================================================== RCS file: /cvs/src/src/gdb/linux-nat.c,v retrieving revision 1.76 diff -u -p -r1.76 linux-nat.c --- linux-nat.c 17 Mar 2008 14:54:07 -0000 1.76 +++ linux-nat.c 19 Mar 2008 15:55:36 -0000 @@ -813,20 +813,6 @@ prune_lwps (void) p = &(*p)->next; } -/* Callback for iterate_over_threads that finds a thread corresponding - to the given LWP. */ - -static int -find_thread_from_lwp (struct thread_info *thr, void *dummy) -{ - ptid_t *ptid_p = dummy; - - if (GET_LWP (thr->ptid) && GET_LWP (thr->ptid) == GET_LWP (*ptid_p)) - return 1; - else - return 0; -} - /* Handle the exit of a single thread LP. */ static void @@ -834,32 +820,14 @@ exit_lwp (struct lwp_info *lp) { if (in_thread_list (lp->ptid)) { + if (print_thread_events) + printf_unfiltered (_("[%s exited]\n"), target_pid_to_str (lp->ptid)); + /* Core GDB cannot deal with us deleting the current thread. */ if (!ptid_equal (lp->ptid, inferior_ptid)) delete_thread (lp->ptid); else record_dead_thread (lp->ptid); - printf_unfiltered (_("[%s exited]\n"), - target_pid_to_str (lp->ptid)); - } - else - { - /* Even if LP->PTID is not in the global GDB thread list, the - LWP may be - with an additional thread ID. We don't need - to print anything in this case; thread_db is in use and - already took care of that. But it didn't delete the thread - in order to handle zombies correctly. */ - - struct thread_info *thr; - - thr = iterate_over_threads (find_thread_from_lwp, &lp->ptid); - if (thr) - { - if (!ptid_equal (thr->ptid, inferior_ptid)) - delete_thread (thr->ptid); - else - record_dead_thread (thr->ptid); - } } delete_lwp (lp->ptid); Index: linux-thread-db.c =================================================================== RCS file: /cvs/src/src/gdb/linux-thread-db.c,v retrieving revision 1.37 diff -u -p -r1.37 linux-thread-db.c --- linux-thread-db.c 23 Jan 2008 11:26:28 -0000 1.37 +++ linux-thread-db.c 19 Mar 2008 15:55:36 -0000 @@ -48,6 +48,32 @@ #define LIBTHREAD_DB_SO "libthread_db.so.1" #endif +/* GNU/Linux libthread_db support. + + libthread_db is a library, provided along with libpthread.so, which + exposes the internals of the thread library to a debugger. It + allows GDB to find existing threads, new threads as they are + created, thread IDs (usually, the result of pthread_self), and + thread-local variables. + + The libthread_db interface originates on Solaris, where it is + both more powerful and more complicated. This implementation + only works for LinuxThreads and NPTL, the two glibc threading + libraries. It assumes that each thread is permanently assigned + to a single light-weight process (LWP). + + libthread_db-specific information is stored in the "private" field + of struct thread_info. When the field is NULL we do not yet have + information about the new thread; this could be temporary (created, + but the thread library's data structures do not reflect it yet) + or permanent (created using clone instead of pthread_create). + + Process IDs managed by linux-thread-db.c match those used by + linux-nat.c: a common PID for all processes, an LWP ID for each + thread, and no TID. We save the TID in private. Keeping it out + of the ptid_t prevents thread IDs changing when libpthread is + loaded or unloaded. */ + /* If we're running on GNU/Linux, we must explicitly attach to any new threads. */ @@ -119,17 +145,13 @@ 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); -static void detach_thread (ptid_t ptid, int verbose); +static void detach_thread (ptid_t ptid); /* Building process ids. */ #define GET_PID(ptid) ptid_get_pid (ptid) #define GET_LWP(ptid) ptid_get_lwp (ptid) -#define GET_THREAD(ptid) ptid_get_tid (ptid) - -#define is_lwp(ptid) (GET_LWP (ptid) != 0) -#define is_thread(ptid) (GET_THREAD (ptid) != 0) #define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0) @@ -143,11 +165,8 @@ struct private_thread_info unsigned int dying:1; /* Cached thread state. */ - unsigned int th_valid:1; - unsigned int ti_valid:1; - td_thrhandle_t th; - td_thrinfo_t ti; + thread_t tid; }; @@ -257,7 +276,7 @@ thread_get_info_callback (const td_thrha thread_db_err_str (err)); /* Fill the cache. */ - thread_ptid = ptid_build (GET_PID (inferior_ptid), ti.ti_lid, ti.ti_tid); + thread_ptid = ptid_build (GET_PID (inferior_ptid), ti.ti_lid, 0); thread_info = find_thread_pid (thread_ptid); /* In the case of a zombie thread, don't continue. We don't want to @@ -266,13 +285,6 @@ thread_get_info_callback (const td_thrha { if (infop != NULL) *(struct thread_info **) infop = thread_info; - if (thread_info != NULL) - { - memcpy (&thread_info->private->th, thp, sizeof (*thp)); - thread_info->private->th_valid = 1; - memcpy (&thread_info->private->ti, &ti, sizeof (ti)); - thread_info->private->ti_valid = 1; - } return TD_THR_ZOMBIE; } @@ -284,39 +296,11 @@ thread_get_info_callback (const td_thrha gdb_assert (thread_info != NULL); } - memcpy (&thread_info->private->th, thp, sizeof (*thp)); - thread_info->private->th_valid = 1; - memcpy (&thread_info->private->ti, &ti, sizeof (ti)); - thread_info->private->ti_valid = 1; - if (infop != NULL) *(struct thread_info **) infop = thread_info; return 0; } - -/* Accessor functions for the thread_db information, with caching. */ - -static void -thread_db_map_id2thr (struct thread_info *thread_info, int fatal) -{ - td_err_e err; - - if (thread_info->private->th_valid) - return; - - err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (thread_info->ptid), - &thread_info->private->th); - if (err != TD_OK) - { - if (fatal) - error (_("Cannot find thread %ld: %s"), - (long) GET_THREAD (thread_info->ptid), - thread_db_err_str (err)); - } - else - thread_info->private->th_valid = 1; -} /* Convert between user-level thread ids and LWP ids. */ @@ -328,10 +312,9 @@ thread_from_lwp (ptid_t ptid) struct thread_info *thread_info; ptid_t thread_ptid; - if (GET_LWP (ptid) == 0) - ptid = BUILD_LWP (GET_PID (ptid), GET_PID (ptid)); - - gdb_assert (is_lwp (ptid)); + /* This ptid comes from linux-nat.c, which should always fill in the + LWP. */ + gdb_assert (GET_LWP (ptid) != 0); err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), &th); if (err != TD_OK) @@ -352,16 +335,8 @@ thread_from_lwp (ptid_t ptid) && thread_info == NULL) return pid_to_ptid (-1); - gdb_assert (thread_info && thread_info->private->ti_valid); - - return ptid_build (GET_PID (ptid), GET_LWP (ptid), - thread_info->private->ti.ti_tid); -} - -static ptid_t -lwp_from_thread (ptid_t ptid) -{ - return BUILD_LWP (GET_LWP (ptid), GET_PID (ptid)); + gdb_assert (ptid_get_tid (ptid) == 0); + return ptid; } @@ -672,7 +647,8 @@ static void attach_thread (ptid_t ptid, const td_thrhandle_t *th_p, const td_thrinfo_t *ti_p) { - struct thread_info *tp; + struct private_thread_info *private; + struct thread_info *tp = NULL; td_err_e err; /* If we're being called after a TD_CREATE event, we may already @@ -690,10 +666,21 @@ attach_thread (ptid_t ptid, const td_thr tp = find_thread_pid (ptid); gdb_assert (tp != NULL); - if (!tp->private->dying) - return; + /* If tp->private is NULL, then GDB is already attached to this + thread, but we do not know anything about it. We can learn + about it here. This can only happen if we have some other + way besides libthread_db to notice new threads (i.e. + PTRACE_EVENT_CLONE); assume the same mechanism notices thread + exit, so this can not be a stale thread recreated with the + same ID. */ + if (tp->private != NULL) + { + if (!tp->private->dying) + return; - delete_thread (ptid); + delete_thread (ptid); + tp = NULL; + } } check_thread_signals (); @@ -702,13 +689,28 @@ attach_thread (ptid_t ptid, const td_thr return; /* A zombie thread -- do not attach. */ /* Under GNU/Linux, we have to attach to each and every thread. */ - if (lin_lwp_attach_lwp (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid))) < 0) + if (tp == NULL + && lin_lwp_attach_lwp (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid))) < 0) return; + /* Construct the thread's private data. */ + private = xmalloc (sizeof (struct private_thread_info)); + memset (private, 0, sizeof (struct private_thread_info)); + + /* A thread ID of zero may mean the thread library has not initialized + yet. But we shouldn't even get here if that's the case. FIXME: + if we change GDB to always have at least one thread in the thread + list this will have to go somewhere else; maybe private == NULL + until the thread_db target claims it. */ + gdb_assert (ti_p->ti_tid != 0); + private->th = *th_p; + private->tid = ti_p->ti_tid; + /* Add the thread to GDB's thread list. */ - tp = add_thread (ptid); - tp->private = xmalloc (sizeof (struct private_thread_info)); - memset (tp->private, 0, sizeof (struct private_thread_info)); + if (tp == NULL) + tp = add_thread_with_info (ptid, private); + else + tp->private = private; /* Enable thread event reporting for this thread. */ err = td_thr_event_enable_p (th_p, 1); @@ -718,22 +720,20 @@ attach_thread (ptid_t ptid, const td_thr } static void -detach_thread (ptid_t ptid, int verbose) +detach_thread (ptid_t ptid) { 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. */ + something re-uses its thread ID. We'll report the thread exit + when the underlying LWP dies. */ thread_info = find_thread_pid (ptid); - gdb_assert (thread_info != NULL); + gdb_assert (thread_info != NULL && thread_info->private != NULL); thread_info->private->dying = 1; } @@ -742,47 +742,12 @@ thread_db_detach (char *args, int from_t { disable_thread_event_reporting (); - /* There's no need to save & restore inferior_ptid here, since the - inferior is not supposed to survive this function call. */ - inferior_ptid = lwp_from_thread (inferior_ptid); - target_beneath->to_detach (args, from_tty); /* Should this be done by detach_command? */ target_mourn_inferior (); } -static int -clear_lwpid_callback (struct thread_info *thread, void *dummy) -{ - /* If we know that our thread implementation is 1-to-1, we could save - a certain amount of information; it's not clear how much, so we - are always conservative. */ - - thread->private->th_valid = 0; - thread->private->ti_valid = 0; - - return 0; -} - -static void -thread_db_resume (ptid_t ptid, int step, enum target_signal signo) -{ - struct cleanup *old_chain = save_inferior_ptid (); - - if (GET_PID (ptid) == -1) - inferior_ptid = lwp_from_thread (inferior_ptid); - else if (is_thread (ptid)) - ptid = lwp_from_thread (ptid); - - /* Clear cached data which may not be valid after the resume. */ - iterate_over_threads (clear_lwpid_callback, NULL); - - target_beneath->to_resume (ptid, step, signo); - - do_cleanups (old_chain); -} - /* Check if PID is currently stopped at the location of a thread event breakpoint location. If it is, read the event message and act upon the event. */ @@ -833,7 +798,7 @@ check_event (ptid_t ptid) if (err != TD_OK) error (_("Cannot get thread info: %s"), thread_db_err_str (err)); - ptid = ptid_build (GET_PID (ptid), ti.ti_lid, ti.ti_tid); + ptid = ptid_build (GET_PID (ptid), ti.ti_lid, 0); switch (msg.event) { @@ -849,7 +814,7 @@ check_event (ptid_t ptid) if (!in_thread_list (ptid)) error (_("Spurious thread death event.")); - detach_thread (ptid, print_thread_events); + detach_thread (ptid); break; @@ -865,9 +830,6 @@ thread_db_wait (ptid_t ptid, struct targ { extern ptid_t trap_ptid; - if (GET_PID (ptid) != -1 && is_thread (ptid)) - ptid = lwp_from_thread (ptid); - ptid = target_beneath->to_wait (ptid, ourstatus); if (ourstatus->kind == TARGET_WAITKIND_EXITED @@ -913,15 +875,6 @@ thread_db_wait (ptid_t ptid, struct targ } 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_mourn_inferior (void) { /* Forget about the child's process ID. We shouldn't need it @@ -954,7 +907,7 @@ find_new_threads_callback (const td_thrh if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE) return 0; /* A zombie -- ignore. */ - ptid = ptid_build (GET_PID (inferior_ptid), ti.ti_lid, ti.ti_tid); + ptid = ptid_build (GET_PID (inferior_ptid), ti.ti_lid, 0); if (ti.ti_tid == 0) { @@ -994,18 +947,17 @@ thread_db_find_new_threads (void) static char * thread_db_pid_to_str (ptid_t ptid) { - if (is_thread (ptid)) + struct thread_info *thread_info = find_thread_pid (ptid); + + if (thread_info != NULL && thread_info->private != NULL) { static char buf[64]; - struct thread_info *thread_info; + thread_t tid; + tid = thread_info->private->tid; thread_info = find_thread_pid (ptid); - if (thread_info == NULL) - snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld) (Missing)", - GET_THREAD (ptid), GET_LWP (ptid)); - else - snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld)", - GET_THREAD (ptid), GET_LWP (ptid)); + snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld)", + tid, GET_LWP (ptid)); return buf; } @@ -1022,22 +974,15 @@ thread_db_pid_to_str (ptid_t ptid) static char * thread_db_extra_thread_info (struct thread_info *info) { + if (info->private == NULL) + return NULL; + if (info->private->dying) return "Exiting"; return NULL; } -/* Return 1 if this thread has the same LWP as the passed PTID. */ - -static int -same_ptid_callback (struct thread_info *thread, void *arg) -{ - ptid_t *ptid_p = arg; - - return GET_LWP (thread->ptid) == GET_LWP (*ptid_p); -} - /* Get the address of the thread local variable in load module LM which is stored at OFFSET within the thread local storage for thread PTID. */ @@ -1046,26 +991,19 @@ thread_db_get_thread_local_address (ptid CORE_ADDR lm, CORE_ADDR offset) { + struct thread_info *thread_info; + /* If we have not discovered any threads yet, check now. */ - if (!is_thread (ptid) && !have_threads ()) + if (!have_threads ()) thread_db_find_new_threads (); - /* Try to find a matching thread if we still have the LWP ID instead - of the thread ID. */ - if (!is_thread (ptid)) - { - struct thread_info *thread; - - thread = iterate_over_threads (same_ptid_callback, &ptid); - if (thread != NULL) - ptid = thread->ptid; - } + /* Find the matching thread. */ + thread_info = find_thread_pid (ptid); - if (is_thread (ptid)) + if (thread_info != NULL && thread_info->private != NULL) { td_err_e err; void *address; - struct thread_info *thread_info; /* glibc doesn't provide the needed interface. */ if (!td_thr_tls_get_addr_p) @@ -1075,11 +1013,6 @@ thread_db_get_thread_local_address (ptid /* Caller should have verified that lm != 0. */ gdb_assert (lm != 0); - /* Get info about the thread. */ - thread_info = find_thread_pid (ptid); - gdb_assert (thread_info); - thread_db_map_id2thr (thread_info, 1); - /* Finally, get the address of the variable. */ err = td_thr_tls_get_addr_p (&thread_info->private->th, (void *)(size_t) lm, @@ -1122,9 +1055,7 @@ init_thread_db_ops (void) thread_db_ops.to_longname = "multi-threaded child process."; thread_db_ops.to_doc = "Threads and pthreads support."; thread_db_ops.to_detach = thread_db_detach; - thread_db_ops.to_resume = thread_db_resume; thread_db_ops.to_wait = thread_db_wait; - thread_db_ops.to_kill = thread_db_kill; thread_db_ops.to_mourn_inferior = thread_db_mourn_inferior; thread_db_ops.to_find_new_threads = thread_db_find_new_threads; thread_db_ops.to_pid_to_str = thread_db_pid_to_str; Index: procfs.c =================================================================== RCS file: /cvs/src/src/gdb/procfs.c,v retrieving revision 1.86 diff -u -p -r1.86 procfs.c --- procfs.c 12 Mar 2008 20:00:21 -0000 1.86 +++ procfs.c 19 Mar 2008 15:55:37 -0000 @@ -4034,8 +4034,9 @@ wait_again: case PR_SYSENTRY: if (syscall_is_lwp_exit (pi, what)) { - printf_filtered (_("[%s exited]\n"), - target_pid_to_str (retval)); + if (print_thread_events) + printf_unfiltered (_("[%s exited]\n"), + target_pid_to_str (retval)); delete_thread (retval); status->kind = TARGET_WAITKIND_SPURIOUS; return retval; @@ -4165,8 +4166,9 @@ wait_again: } else if (syscall_is_lwp_exit (pi, what)) { - printf_filtered (_("[%s exited]\n"), - target_pid_to_str (retval)); + if (print_thread_events) + printf_unfiltered (_("[%s exited]\n"), + target_pid_to_str (retval)); delete_thread (retval); status->kind = TARGET_WAITKIND_SPURIOUS; return retval; Index: thread.c =================================================================== RCS file: /cvs/src/src/gdb/thread.c,v retrieving revision 1.63 diff -u -p -r1.63 thread.c --- thread.c 17 Mar 2008 18:41:29 -0000 1.63 +++ thread.c 19 Mar 2008 15:55:37 -0000 @@ -132,10 +132,12 @@ add_thread_silent (ptid_t ptid) } struct thread_info * -add_thread (ptid_t ptid) +add_thread_with_info (ptid_t ptid, struct private_thread_info *private) { struct thread_info *result = add_thread_silent (ptid); + result->private = private; + if (print_thread_events) printf_unfiltered (_("[New %s]\n"), target_pid_to_str (ptid)); @@ -144,6 +146,12 @@ add_thread (ptid_t ptid) return result; } +struct thread_info * +add_thread (ptid_t ptid) +{ + return add_thread_with_info (ptid, NULL); +} + void delete_thread (ptid_t ptid) { Index: testsuite/gdb.threads/fork-child-threads.exp =================================================================== RCS file: /cvs/src/src/gdb/testsuite/gdb.threads/fork-child-threads.exp,v retrieving revision 1.1 diff -u -p -r1.1 fork-child-threads.exp --- testsuite/gdb.threads/fork-child-threads.exp 2 Jan 2008 13:36:38 -0000 1.1 +++ testsuite/gdb.threads/fork-child-threads.exp 19 Mar 2008 15:55:37 -0000 @@ -38,6 +38,10 @@ if ![runto_main] then { gdb_test "set follow-fork-mode child" gdb_breakpoint "start" + +# Make sure we can step over fork without losing our breakpoint. +gdb_test "next" ".*pthread_create \\(&thread, NULL, start, NULL\\);.*" "next over fork" + gdb_test "continue" "Breakpoint 2, start.*" "get to the spawned thread" # Wrong: