From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id cs2rEyyZ+2kgSBsAWB0awg (envelope-from ) for ; Wed, 06 May 2026 15:40:28 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=dtUzFB9W; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 400EF1E0BA; Wed, 06 May 2026 15:40:28 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 5EFF21E067 for ; Wed, 06 May 2026 15:40:26 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A74E74BA23E8 for ; Wed, 6 May 2026 19:40:25 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A74E74BA23E8 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=dtUzFB9W Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 33D6A4BA23D0 for ; Wed, 6 May 2026 19:39:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 33D6A4BA23D0 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 33D6A4BA23D0 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1778096394; cv=none; b=ppDChFO/woO+FaUiKNgPwU/HP+LhuviMJZEa7bquR3y/3jfdvNdYvUDGglvrvyUVb1meORK/mVdzcmWz5qUEZA9NOCrQDFC1eDInKWysLNVsgwaR7N7vTqT+1zQaHONZbaJi+je40acE8LN3C/YySlobRRMzUFCRf5HC+r6M8ks= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1778096394; c=relaxed/simple; bh=khtq9ZuMj10kCml1hA9hmZxX+HD/eM3KGhKkgfJk6zQ=; h=DKIM-Signature:Message-ID:Date:MIME-Version:From:Subject:To; b=N+qP+XcwIoWN1zFoDLorOraIfiPGA/IVrWRf9RaCAflhwjVlyTG6HbQknaKi1oyshVs+ZXxOdVQYEKjTNOrjep5z1g/9KxKompZDg1IZWaSFPnovnPXBf4GFJ0il35pj6/e9yo9RTSkJO9hK7GSiFGfLjcVluD+9xi8V+b1R/PE= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=dtUzFB9W DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 33D6A4BA23D0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1778096393; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=rt7cLbEVe3VU9OWpoSps2WIadmxDEB9qwg/4SDiisPo=; b=dtUzFB9WkN+CZZBRCiCmrchScrugNbEmo26d7BWTWIzmu0b2i1vDWbyPV7yKmWfUDiUjRd 0+Tc6oLF7pLBDM9q6fm9tKSmyopcd6SwNrSJuiW4ap5Je6kWCuRqT/nZ7RyfiTleYg0ZNA BkJX5K43Tqm6ikAng76zQyEuCHYXPSg= Received: from mail-qk1-f197.google.com (mail-qk1-f197.google.com [209.85.222.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-28-cQxnSjoaMa6gGX4XuRzEwA-1; Wed, 06 May 2026 15:39:52 -0400 X-MC-Unique: cQxnSjoaMa6gGX4XuRzEwA-1 X-Mimecast-MFC-AGG-ID: cQxnSjoaMa6gGX4XuRzEwA_1778096392 Received: by mail-qk1-f197.google.com with SMTP id af79cd13be357-8eabf08affaso30822085a.1 for ; Wed, 06 May 2026 12:39:52 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778096392; x=1778701192; h=content-transfer-encoding:in-reply-to:content-language:references :to:subject:from:user-agent:mime-version:date:message-id:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=rt7cLbEVe3VU9OWpoSps2WIadmxDEB9qwg/4SDiisPo=; b=f/vg9Mjt0moeobEkW78Ne0ZvaXc0bnJeF38HhqDno4CQmWHFWZ8J3UuV7QixRtiAHR hQ1DbooUVXIxZ2jqTRu3KBsFeYlrGnZgIPFFtmrpdJHV4lRu0SYJseMPD/kLXkfFF3po Za/YHiAWb748HzubbTLlfVtVhbdnqzcTyxOxmPqq1fuZNVgQheuAv8jI+13qfZ9HkzTt BvrovSJAe5IHyqyNNryIjvVrQ/0LC1g5CiycEfFu9WTExFdig0Jx73rXD3XyCdq8SuGd Z1wJmnQBaNIji2cJ7VC9RDmSyScQvpnHyXD7KT8r/gH20tI0s6ithhjQhzszC8F227Z1 1BoA== X-Forwarded-Encrypted: i=1; AFNElJ9oMI6WfT0EkIi0mGkPvsVqSd1XoGpzxi9wULqJ6jhZKfoL25xJtMzMCVaVkXi2xo/RnESufdYhlmITvA==@sourceware.org X-Gm-Message-State: AOJu0YxpUb5LUkkGvgkgcYX+xsVl8jx1M0mcd6Qq+YTOdPPzC6aKYZ9Q yKqk365zEZMII4eUbjDOMiXnqELb7wIGkleOulTgZgRQbAK/sFeUvenvpP/cZQm5Bbput7fiJmT CJZ18Fn2M5EB4sBA2L9LDhcID26xWvZoUcO1+ZRnfOKu+9SqDPm6wc6E3nVQ/lOo= X-Gm-Gg: AeBDietP2xClBihcvFHOqreLXrvx9rhMsrmW0jHeQ34ZL3VZSlYWtHy463pBbAPni7y PoKIVoh12Hz3z6iaPHZ3qxp3wfHxoP9gZL97m4t188JRFh2iE7O3jeAuuMc+JbbKB5WLRf2leWX JDNzdiLU6cRzXiRGfndTbhiaf8c5i71/xADAIbSrp61e2v4ylaPg1T91B/Wnes2INxkjY2kD+0a BNCCAbwhZfFbStdb9VDZcgkSyCSLfLR4uwcu3DqwuCfBHl5I36EWSEJOdTZrwpYdvHrTD7VnEBt /38cAnWdzRAZJB29H6qYvB4aD9zTjGK9er0QNtCR4P/uyYb/l8SePR6h7V3/yhXzpvmjmLikINY 7ekSi+5Ky/v6KCII9CbsFsMyYpzLh/knJoP0m6F3TReaVXjmD2g== X-Received: by 2002:a05:620a:40d0:b0:8dd:b4fc:d561 with SMTP id af79cd13be357-904d438db5dmr720929685a.10.1778096391696; Wed, 06 May 2026 12:39:51 -0700 (PDT) X-Received: by 2002:a05:620a:40d0:b0:8dd:b4fc:d561 with SMTP id af79cd13be357-904d438db5dmr720924085a.10.1778096390972; Wed, 06 May 2026 12:39:50 -0700 (PDT) Received: from [150.1.200.157] ([172.56.105.115]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8b53c0e7f27sm220983756d6.32.2026.05.06.12.39.49 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 06 May 2026 12:39:50 -0700 (PDT) Message-ID: <01800f03-8d4b-494d-9731-0127ac542b0d@redhat.com> Date: Wed, 6 May 2026 12:39:48 -0700 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird From: Keith Seitz Subject: Re: [PATCH 3/3] gdb: improve error handling for thread-db thread adoption To: Andrew Burgess , gdb-patches@sourceware.org References: <63e2a1079d44737c020efc5bc177da21feab0ce5.1775557188.git.aburgess@redhat.com> In-Reply-To: <63e2a1079d44737c020efc5bc177da21feab0ce5.1775557188.git.aburgess@redhat.com> X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: h3VnvQSmYe9O-VxKZeiCEZ51SI6rcWZFYhEEb3AkZWo_1778096392 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Hi, On 4/7/26 3:22 AM, Andrew Burgess wrote: > > We can imagine many bugs that might cause this to occur, some of which > could even be outside GDB. But without additional information I'm not > inclined to start looking for bugs outside of GDB. So I started > looking for ways that the bug could be triggered from within GDB. I don't blame you one iota. :-) > One way the bug could occur is if GDB fails to create a thread_info > for a new thread. Thank you for the detailed explanation! It really helps thread-handling novices like me. > My solution for this is to convert the `error` calls into warnings, > and just have thread_from_lwp return NULL if thread-db cannot adopt > the thread. > > This potentially makes the use of FORWARD_SCOPE_EXIT in ::follow_clone > unnecessary, I no longer expect any exceptions to be thrown from > thread_db_notice_clone. However, I think maintaining the use of > FORWARD_SCOPE_EXIT is still a good idea. If I've missed any exception > routes, or any are added in the future, then this will ensure the > threads are always added to GDB correctly. I don't mind the defensive strategy here > Tested on GNU/Linux on x86-64, AArch64, RISC-V, and PPC64le using > unix, native-gdbserver, and native-extended-gdbserver boards. Did you try -m32 on x86_64? I am seeing a bunch of the new clone-bad-tls.exp tests fail. On native, we hit an assert ("`clone_pid > 0' failed") in the test program. On gdbservers all of the "info thread" commands show the current frame as "__kernel_vsyscall". I discovered no other regressions on architectures supported by Fedora 43 (except ppc64le where gdbserver currently isn't building). One largely rhetorical question below... > diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c > index 1807352ed76..1ae9323108a 100644 > --- a/gdb/linux-thread-db.c > +++ b/gdb/linux-thread-db.c > @@ -413,13 +413,19 @@ thread_from_lwp (thread_info *stopped, ptid_t ptid) > err = info->td_ta_map_lwp2thr_p (info->thread_agent, ptid.lwp (), > &th); > if (err != TD_OK) > - error (_("Cannot find user-level thread for LWP %ld: %s"), > - ptid.lwp (), thread_db_err_str (err)); > + { > + warning (_("Cannot find user-level thread for LWP %ld: %s"), > + ptid.lwp (), thread_db_err_str (err)); > + return nullptr; > + } > > err = info->td_thr_get_info_p (&th, &ti); > if (err != TD_OK) > - error (_("thread_get_info_callback: cannot get thread info: %s"), > - thread_db_err_str (err)); > + { > + warning (_("thread_get_info_callback: cannot get thread info: %s"), > + thread_db_err_str (err)); > + return nullptr; > + } I have to confess, I am a bit apprehensive about these new warnings. From a technical perspective, I certainly understand the issue here and that they can largely be ignored (what other choice is there?). However, I wonder how a "developer just trying to debug some code" would react to seeing these warnings? Mind you, I'm not 100% sure how to improve wording, and I"m not even sure it is worth further effort, given that this should be pretty edge-case-y. So consider this largely rhetorical unless something obvious comes to mind. Keith > > /* Fill the cache. */ > tp = stopped->inf->process_target ()->find_thread (ptid); > @@ -450,8 +456,8 @@ thread_db_notice_clone (ptid_t parent, ptid_t child) > thread_info *parent_info = thread_from_lwp (stopped, parent); > gdb_assert (parent_info == stopped); > > - thread_from_lwp (stopped, child); > - return true; > + thread_info *child_info = thread_from_lwp (stopped, child); > + return child_info != nullptr; > } > > static void * > diff --git a/gdb/testsuite/gdb.threads/clone-bad-tls.c b/gdb/testsuite/gdb.threads/clone-bad-tls.c > new file mode 100644 > index 00000000000..dfb3ff79c17 > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/clone-bad-tls.c > @@ -0,0 +1,193 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2026 Free Software Foundation, Inc. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see . > + > + Test that GDB doesn't lose an event for a thread it didn't know > + about, until an event is reported for it. */ > + > +#define _GNU_SOURCE > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* Global synchronization variables. */ > +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; > +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; > +int ready = 0; > + > +#define STACK_SIZE 0x1000 > + > +/* Wrap futex syscall. See 'man 2 futex'. */ > + > +static int > +futex (int *uaddr, int futex_op, int val, const struct timespec *timeout, > + int *uaddr2, int val3) > +{ > + return syscall (SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); > +} > + > +/* Function to run in the raw clone thread. This just spins until the > + integer flag pointed to by ARG is set to non-zero. */ > + > +static int > +clone_fn (void *arg) > +{ > + int *thread_exit = (int *) arg; /* Break inside raw thread. */ > + *thread_exit = 0; > + > + while (*thread_exit == 0) > + ; > +} > + > +/* A variable into which the kernel will write the child thread-id. This > + will be cleared when the thread exits. This only gets used when > + USE_FUTEX is defined. */ > + > +static int thread_tid; > + > +/* Create a thread using a raw clone call. */ > + > +void * > +do_raw_clone () > +{ > + unsigned char *stack; > + int res; > + int clone_pid; > + > + /* This flag is shared with the raw thread. It is initially -1, then > + set to 0 in the raw thread to show that the thread has started up. > + This flag is then set to 1 by the main thread to indicate that the > + raw thread should exit. Using a volatile for thread synchronisation > + is not great, but this avoids having to make library calls from the > + raw thread, which might trigger a need for TLS to be setup correctly > + on some targets. */ > + volatile int thread_exit = -1; > + > + stack = malloc (STACK_SIZE); > + assert (stack != NULL); /* Break before raw thread created. */ > + > +#ifdef USE_FUTEX > +#define TID_FLAGS (CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID) > +#else > +#define TID_FLAGS (0) > +#endif > + > +#define CLONE_FLAGS (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM \ > + | CLONE_SETTLS | TID_FLAGS) > + > + clone_pid = clone (clone_fn, stack + STACK_SIZE, CLONE_FLAGS, > + (void *) &thread_exit, &thread_tid, > + NULL, &thread_tid); > + > + assert (clone_pid > 0); /* Immediately after the clone. */ > + > + while (thread_exit != 0) > + usleep (1000); > + > + /* Trigger the clone thread to exit. */ > + thread_exit = 1; /* Break after raw thread created. */ > + > +#ifdef USE_FUTEX > + /* In this mode we rely on the futex to notify us when the thread has > + exited. In a broken GDB we used to deadlock at this point as GDB > + would fail to restart the raw thread, and so the kernel would never > + wake this futex. */ > + while (thread_tid != 0) > + futex (&thread_tid, FUTEX_WAIT, clone_pid, NULL, NULL, 0); > + > + /* We do know that the raw thread has completed at this point, so we > + could free its stack. We don't though, just to be consistent with the > + no futex path. This doesn't really matter, this is just a small > + test. */ > +#else > + /* There's no attempt to synchronise with the raw clone thread on this > + path. In a broken GDB the raw thread might not be resumed, in which > + case this timeout will expire. */ > + sleep (2); > + > + /* We cannot be sure that the raw thread has finished by this point, so > + we cannot free the stack. This isn't critical, this is just a small > + test case. */ > +#endif > + > + return NULL; /* Break after raw thread exits. */ > +} > + > +/* Something for our pthread thread to do. This just blocks until the main > + thread releases it, at which point this thread will exit. */ > + > +void * > +worker_thread (void *arg) > +{ > + pthread_mutex_lock(&mutex); > + > + /* Let the main thread know we are live by clearing the ready flag. */ > + ready = 0; > + pthread_cond_signal(&cond); > + > + /* Now spin until the main thread sets this flag back to non-zero. */ > + while (ready == 0) > + { > + /* The thread will block here until main signals 'cond' */ > + pthread_cond_wait (&cond, &mutex); > + } > + > + pthread_mutex_unlock (&mutex); > + > + return NULL; > +} > + > +int > +main (void) > +{ > + pthread_t thread_id; > + volatile int flag = 0; > + int res; > + > + /* The pthread will set the READY flag back to zero. */ > + ready = 1; > + > + alarm (300); > + > + res = pthread_create (&thread_id, NULL, worker_thread, (void *)&flag); > + assert (res == 0); > + > + /* Wait for the pthread to set READY to zero. */ > + pthread_mutex_lock(&mutex); > + while (ready == 1) > + pthread_cond_wait (&cond, &mutex); > + pthread_mutex_unlock (&mutex); > + > + /* Create a thread using a raw clone call. */ > + do_raw_clone (); /* Break before do_raw_clone. */ > + > + /* Unblock the pthread. */ > + pthread_mutex_lock (&mutex); > + ready = 1; > + pthread_cond_signal (&cond); > + pthread_mutex_unlock (&mutex); > + > + /* Wait for the worker to finish. */ > + pthread_join (thread_id, NULL); > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.threads/clone-bad-tls.exp b/gdb/testsuite/gdb.threads/clone-bad-tls.exp > new file mode 100644 > index 00000000000..2de16c63b6e > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/clone-bad-tls.exp > @@ -0,0 +1,294 @@ > +# This testcase is part of GDB, the GNU debugger. > + > +# Copyright 2026 Free Software Foundation, Inc. > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 3 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see . > + > +# This test creates a raw thread using a clone() call without going > +# through the pthread library. Further, the thread that is created > +# deliberately sets its TLS pointer to NULL. This used to cause > +# problems for GDB as GDB would fail to recognize the new thread, > +# which means no thread_info would be created, which would lead to > +# segfaults and assertion failures. > +# > +# When running this test on a remote target we do see some warnings > +# from gdbserver about failure to find the raw thread. This is > +# because the thread is never owned by thread-db. In a GDB only world > +# we don't warn about such threads, we just silently handle them. > +# Maybe we should do the same in gdbserver? For now though, we just > +# ignore the warnings. Functionally, gdbserver handles the raw thread > +# just fine, it just gives a warning each time we try to look it up in > +# thread-db (and fail). > + > +# This only works on targets with the Linux kernel, and the source > +# makes use of the clone call, which has a different API on IA64. > +require {!istarget "ia64-*-*"} > +require {istarget "*-*-linux*"} > + > +standard_testfile > + > +# When debugging on a remote target GDB sees the thread as a full > +# thread, so we get the 'Thread ...' type message. When using the > +# native linux target within GDB, as this thread is not owned by > +# thread-db we get a 'LWP ...' type message. Setup a global to be > +# used in the test patterns below. > +if {[gdb_protocol_is_remote]} { > + set raw_thread_re "Thread $::decimal\\.$::decimal" > +} else { > + set raw_thread_re "LWP $::decimal" > +} > + > +# Restart GDB using TESTFILE, the basename of an executable. Run to > +# the function 'do_raw_clone', which is where we start all the tests. > +# Then use BREAK_LINE_PATTERN to create a breakpoint on the matching > +# line. > +proc setup_for_test { testfile break_line_pattern } { > + clean_restart $testfile > + > + # Run until the do_raw_clone function. By this point the pthread > + # will have been created. > + if {![runto do_raw_clone]} { > + return > + } > + > + gdb_breakpoint [gdb_get_line_number $break_line_pattern] > +} > + > +# Use TESTFILE as the basename of the executable to run. Place a > +# breakpoint after the creation of a raw thread, in the parent thread, > +# not the raw thread. Run until the breakpoint is hit, check we see > +# the raw thread announced, and that the thread shows up in the 'info > +# threads' output. > +proc_with_prefix stop_after_creating_raw_thread { testfile } { > + # Start GDB. Run to do_raw_clone. Place a breakpoint after the > + # raw thread has been created. > + setup_for_test $testfile "Break after raw thread created." > + > + # Continue until the raw thread has been created. Check we see > + # the raw thread announced, and that we hit the expected breakpoint. > + set saw_breakpoint_line false > + set saw_source_line false > + set saw_new_thread_line false > + gdb_test_multiple "continue" "" { > + -re "^\\\[New $::raw_thread_re \\(id 3\\)\\\]\r\n" { > + set saw_new_thread_line true > + exp_continue > + } > + > + -re "^Thread 1 \[^\r\n\]+ hit Breakpoint $::decimal, do_raw_clone \\(\\)\[^\r\n\]+\r\n" { > + set saw_breakpoint_line true > + exp_continue > + } > + > + -re "^$::decimal\\s+\[^\r\n\]+Break after raw thread created\\.\[^\r\n\]+\r\n" { > + set saw_source_line true > + exp_continue > + } > + > + -re "^$::gdb_prompt $" { > + gdb_assert { $saw_breakpoint_line && $saw_source_line \ > + && $saw_new_thread_line } $gdb_test_name > + } > + > + -re "^\[^\r\n\]*\r\n" { > + exp_continue > + } > + } > + > + # Ensure the raw thread shows up in the 'info threads' output. > + gdb_test "info threads" \ > + "\r\n\\s+3\\s+$::raw_thread_re \[^\r\n\]+ clone_fn \\(arg=$::hex\\) \[^\r\n\]+" > + > + gdb_test "thread 3" > + gdb_test "bt" > +} > + > +# Use TESTFILE as the basename of the executable to run. Place a > +# breakpoint within the raw thread function. Run until the breakpoint > +# is hit, check we see the raw thread announced, the switch to the raw > +# thread, and that the thread shows up in the 'info threads' output. > +proc_with_prefix stop_in_raw_thread { testfile } { > + setup_for_test $testfile "Break inside raw thread." > + > + # Continue until the raw thread has been created. Check we see > + # the raw thread announced, and that we hit the expected breakpoint. > + set saw_breakpoint_line false > + set saw_source_line false > + set saw_new_thread_line false > + set saw_thread_switch false > + gdb_test_multiple "continue" "" { > + -re "^\\\[New $::raw_thread_re \\(id 3\\)\\\]\r\n" { > + set saw_new_thread_line true > + exp_continue > + } > + > + -re "^\\\[Switching to $::raw_thread_re\\\]\r\n" { > + set saw_thread_switch true > + exp_continue > + } > + > + -re "^Thread 3 \[^\r\n\]+ hit Breakpoint $::decimal, clone_fn \\(arg=$::hex\\)\[^\r\n\]+\r\n" { > + set saw_breakpoint_line true > + exp_continue > + } > + > + -re "^$::decimal\\s+\[^\r\n\]+Break inside raw thread\\.\[^\r\n\]+\r\n" { > + set saw_source_line true > + exp_continue > + } > + > + -re "^$::gdb_prompt $" { > + gdb_assert { $saw_breakpoint_line && $saw_source_line \ > + && $saw_thread_switch && $saw_new_thread_line } \ > + $gdb_test_name > + } > + > + -re "^\[^\r\n\]*\r\n" { > + exp_continue > + } > + } > + > + # Ensure the raw thread shows up in the 'info threads' output. > + gdb_test "info threads" \ > + "\r\n\\*\\s+3\\s+$::raw_thread_re \[^\r\n\]+ clone_fn \\(arg=$::hex\\) \[^\r\n\]+" > +} > + > +# Use TESTFILE as the basename of the executable to run. Break before > +# the raw thread is created, set scheduler-locking mode on to stop GDB > +# setting the raw thread running, then step (using 'next') over the > +# creation of the raw thread. Once we have stepped past the clone > +# turn scheduler-locking off again and run until the inferior exits, > +# we expect to see both the pthread and the raw thread exit. > +# > +# In broken GDB, in the delay build (no futex) when the inferior > +# exits, the raw thread will also exit. GDB knows nothing about the > +# raw thread, and an assertion is triggered. > +# > +# In broken GDB, in futex mode the inferior will deadlock waiting on > +# the futex, this is because GDB doesn't know about the raw thread, > +# and never set it running again. As such the futex condition > +# variable is never cleared, and so the inferior never exits. > +# > +# This should all now be fixed, GDB should see the raw thread > +# correctly, and the inferior should exit in both modes. > +proc_with_prefix next_over_raw_thread_creation { testfile } { > + setup_for_test $testfile "Break before raw thread created." > + > + gdb_continue_to_breakpoint "before raw thread created" > + > + gdb_test_no_output "set scheduler-locking on" > + > + set saw_thread_announced false > + set reached_line_after_clone false > + for { set i 0 } { $i < 10 } { incr i } { > + gdb_test_multiple "next" "next, $i" { > + -re "^\\\[New $::raw_thread_re \\(id 3\\)\\\]\r\n" { > + set saw_thread_announced true > + exp_continue > + } > + > + -re "^$::decimal\\s+\[^\r\n\]+Immediately after the clone\\.\[^\r\n\]+\r\n" { > + set reached_line_after_clone true > + exp_continue > + } > + > + -re "^$::gdb_prompt " { > + # Nothing. > + } > + > + -re "^\[^\r\n\]*\r\n" { > + exp_continue > + } > + } > + > + if { $reached_line_after_clone } { > + break > + } > + } > + > + gdb_assert { $reached_line_after_clone && $saw_thread_announced } \ > + "have passed clone and seen raw thread" > + > + # Ensure the raw thread shows up in the 'info threads' output. > + # Due to scheduler-locking the new thread will not have made any > + # progress yet. Having 'clone ()' in the expected output might be > + # risky; on some targets it's possible this could show up as some > + # other function name. > + gdb_test "info threads" \ > + "\r\n\\s+3\\s+$::raw_thread_re \[^\r\n\]+ clone \\(\\) \[^\r\n\]+" > + > + gdb_test_no_output "set scheduler-locking off" > + > + # Run until the inferior exits. We want to do this because if GDB > + # failed to track the raw thread, then when the inferior exits the > + # raw thread will also exit, and this thread exit event used to > + # trigger an assertion in GDB. > + set saw_raw_thread_exit false > + set saw_pthread_exit false > + set saw_inferior_exit false > + gdb_test_multiple "continue" "continue to exit" { > + -re "^\\\[$::raw_thread_re \\(id 3\\) exited\\\]\r\n" { > + set saw_raw_thread_exit true > + exp_continue > + } > + > + -re "^\\\[Thread \[^\r\n\]+ \\(id 2\\) exited\\\]\r\n" { > + set saw_pthread_exit true > + exp_continue > + } > + > + -re "^\\\[Inferior 1 \[^\r\n\]+ exited normally\\\]\r\n" { > + set saw_inferior_exit true > + exp_continue > + } > + > + -re "^$::gdb_prompt " { > + # Remote targets don't send thread exit events for every > + # thread when the entire inferior exits. Maybe GDB itself > + # should synthesize these events? For now though just > + # invert the state of the thread exit flags when testing > + # with a remote protocol. > + if {[gdb_protocol_is_remote]} { > + set saw_raw_thread_exit [expr {!$saw_raw_thread_exit}] > + set saw_pthread_exit [expr {!$saw_pthread_exit}] > + } > + > + gdb_assert { $saw_raw_thread_exit && $saw_pthread_exit \ > + && $saw_inferior_exit } $gdb_test_name > + } > + > + -re "^\[^\r\n\]*\r\n" { > + exp_continue > + } > + } > +} > + > + > +# Run the tests. > +foreach_with_prefix mode { delay futex } { > + set the_testfile $testfile-$mode > + > + set flags {debug pthreads} > + if { $mode eq "futex" } { > + lappend flags "additional_flags=-DUSE_FUTEX" > + } > + > + if {[build_executable "failed to build" $the_testfile $srcfile $flags]} { > + return > + } > + > + stop_after_creating_raw_thread $the_testfile > + stop_in_raw_thread $the_testfile > + next_over_raw_thread_creation $the_testfile > +} > diff --git a/gdbserver/thread-db.cc b/gdbserver/thread-db.cc > index 0bdcda2ced8..b15d5e472a0 100644 > --- a/gdbserver/thread-db.cc > +++ b/gdbserver/thread-db.cc > @@ -178,14 +178,20 @@ find_one_thread (ptid_t ptid) > td_err_e err = thread_db->td_ta_map_lwp2thr_p (thread_db->thread_agent, lwpid, > &th); > if (err != TD_OK) > - error ("Cannot get thread handle for LWP %d: %s", > - lwpid, thread_db_err_str (err)); > + { > + warning ("Cannot get thread handle for LWP %d: %s", > + lwpid, thread_db_err_str (err)); > + return 0; > + } > > td_thrinfo_t ti; > err = thread_db->td_thr_get_info_p (&th, &ti); > if (err != TD_OK) > - error ("Cannot get thread info for LWP %d: %s", > - lwpid, thread_db_err_str (err)); > + { > + warning ("Cannot get thread info for LWP %d: %s", > + lwpid, thread_db_err_str (err)); > + return 0; > + } > > threads_debug_printf ("Found thread %ld (LWP %d)", > (unsigned long) ti.ti_tid, ti.ti_lid);