From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) by sourceware.org (Postfix) with ESMTPS id C73A23858C83 for ; Tue, 16 May 2023 15:09:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C73A23858C83 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=intel.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1684249760; x=1715785760; h=from:to:cc:subject:date:message-id:references: in-reply-to:mime-version:content-transfer-encoding; bh=oWWy6AlK4PAOQNZ5OgWHC6CIjmYWmvxqXWJg1IbEDq0=; b=GRC+yFUz2JgZlvf5vr75Oeg+tGuXK5Y1Nye9Ejg3iP7x9A/QAViA09tv aI4HbV+BHPvZyWpmgIhVNHTilFyoZMtCUDBThqG7RJaP/h2drsFH+kla2 XaoSAO1xUxYOsmgY1eb1r4Ze02tpuuMKpbynHwgHIWhPwbAXxiGusl9n7 JkYP1nXPe9xom9IhhK1vC++2vM+dHispc0iXNPHTe4CLCbJYfwH2qec5q IFHaeayaJ5rgJswiZcEn38AF4GtZO/QjUfEOv+IWMH2SmkQh8AhNMncgV RPSrQLcRitRbFikIsQ4g6++lxFzz+DAr3jQ6sxegTCzes9lcF5GUh4GtU A==; X-IronPort-AV: E=McAfee;i="6600,9927,10711"; a="350341298" X-IronPort-AV: E=Sophos;i="5.99,278,1677571200"; d="scan'208";a="350341298" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 16 May 2023 08:09:18 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10711"; a="695489998" X-IronPort-AV: E=Sophos;i="5.99,278,1677571200"; d="scan'208";a="695489998" Received: from orsmsx603.amr.corp.intel.com ([10.22.229.16]) by orsmga007.jf.intel.com with ESMTP; 16 May 2023 08:09:18 -0700 Received: from orsmsx611.amr.corp.intel.com (10.22.229.24) by ORSMSX603.amr.corp.intel.com (10.22.229.16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Tue, 16 May 2023 08:09:17 -0700 Received: from orsmsx610.amr.corp.intel.com (10.22.229.23) by ORSMSX611.amr.corp.intel.com (10.22.229.24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Tue, 16 May 2023 08:09:17 -0700 Received: from ORSEDG601.ED.cps.intel.com (10.7.248.6) by orsmsx610.amr.corp.intel.com (10.22.229.23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23 via Frontend Transport; Tue, 16 May 2023 08:09:17 -0700 Received: from NAM11-CO1-obe.outbound.protection.outlook.com (104.47.56.177) by edgegateway.intel.com (134.134.137.102) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.23; Tue, 16 May 2023 08:09:17 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=H0W+7xWPMlF1pAjpkhXSCe35FYzoa6paTBbwdUjvYQAqUx1HIA2cqXo1wpwnnT6WqcXvEXk8pSm3cO8Vsdl8b/Fibrte/Qk4CFIEmVW1hotw0/qceZNHueu/ILY8WN5qcGo7zyJfLVftFm4hukkmFFvY8V5IRilOpUDbXzVBjyzOMxKKdrRUSP7xvSODmToV9eSGse5MzovFGlm1PF9AQ6dSrP4ialVaaQLzn6wZa2D9+8i/vGP43r9nuayElD0qDgGiNc26mhsFPUOuPJaBR+dWkcUtE1XbSpsGko9V2cn7LSbblB7UdFn2uLNZupO+dsBuCRKKYNGvLXQW8WFDYw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=YIbHQHc52hyts3EgtnC+K275aXomLBXvgt7wVennugc=; b=DHJgcZ4wpCmlTAq2iEw8qhxvNeZqYq2bDqbRLIwfId3/71Q2aY2gFriv7rJpuSv8u3UzShgMVDK/wuy6WREHJOVtfRRPr0u4SlsIw6nV3f0I/sTqcXP1oWkyWueAZH1G6FsfyHAj5a1qWNZ+YI4q2qAI1IfPEoAV9y/x8F1du+WNux34Z/bFod85QVRg6UabGlmOsIjnMJIZy5RAPh3kZWr+87S1tI6dgOtm5+iJHucXFkr/rEuVvh3ssFUKAcGMFAHfkakrwMdtlcSpfiHa4/26DTBp1x/+4CtYFa84XadkLxsuGFfVierhVIi1FoPEYVO1IsjoZOD8vSBp7g5jqQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=intel.com; dmarc=pass action=none header.from=intel.com; dkim=pass header.d=intel.com; arc=none Received: from DM4PR11MB7303.namprd11.prod.outlook.com (2603:10b6:8:108::21) by DM8PR11MB5623.namprd11.prod.outlook.com (2603:10b6:8:25::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6387.33; Tue, 16 May 2023 15:09:14 +0000 Received: from DM4PR11MB7303.namprd11.prod.outlook.com ([fe80::830b:dc92:e628:ab8c]) by DM4PR11MB7303.namprd11.prod.outlook.com ([fe80::830b:dc92:e628:ab8c%2]) with mapi id 15.20.6387.030; Tue, 16 May 2023 15:09:14 +0000 From: "Aktemur, Tankut Baris" To: Andrew Burgess , "gdb-patches@sourceware.org" CC: "Saiapova, Natalia" Subject: RE: [PATCHv7 2/6] gdb: fix b/p conditions with infcalls in multi-threaded inferiors Thread-Topic: [PATCHv7 2/6] gdb: fix b/p conditions with infcalls in multi-threaded inferiors Thread-Index: AQHZh2KctMG4CEGs9EqrDHPWieK+Fa9c4+wQ Date: Tue, 16 May 2023 15:09:14 +0000 Message-ID: References: In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=intel.com; x-ms-publictraffictype: Email x-ms-traffictypediagnostic: DM4PR11MB7303:EE_|DM8PR11MB5623:EE_ x-ms-office365-filtering-correlation-id: 56b66b59-0623-48c2-a45c-08db561f82e6 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: ppCCmBX9rqyVnSzchq4OHyGBUh0d0zfNJTh6z50uPoB4SDHyKqhZcn5+/LexQj2gvR9LRSskazCwmL9lSSoqrRMafO86lIUt0e3GCPkNI7UPOa7u/ykjtWStLgwiofaEGkJIDpv4cI56cCqxyrQi0uAPOoubx4R62q4ZPmKmfCmuqG/MwSxDOPI3FTANtam8g+CVZG9lBDpxi5dlLPG/fvj+zsF/eSUJHa/tqdZQEAMJZ4qOIU/5YWd8xBDmEngErWQksqA9JSYt+m44ih33GE1HaGATCVsE2NMiv1QSqnXvsevkBiw2OUAHi7a0g0l3HkMHjp85asfP5W1uJQxVgBHzgslNYM/+1jlJi6Hj1gHxcob41JZDNn5h7OoNCwBYer3+al1r7D15OJ8NQ4wJHu8CqFuns7VMtB37YRZ8GCyX40VK3KuvlVOMe/ZbfuIgd4CwIOXgCKTRzLOlAawYRWNFnoLjRQ+XMDzi54NccWkmFF+6PQOV+aG6twBWKeeRKvTCl8cA3JGYQdAY8ar9uoojwFhEHSUTGIGyRK6KO/8FOdX+Skeit/KeXc1MvjXxDb1DBNsgjD6EQD58MrjCCm2JuT8FiCbLBKzGTiVIYowXJZICrNhj6ysozynr2DIp x-forefront-antispam-report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:DM4PR11MB7303.namprd11.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230028)(136003)(376002)(346002)(39860400002)(396003)(366004)(451199021)(38070700005)(33656002)(86362001)(7696005)(110136005)(316002)(76116006)(66556008)(66476007)(66446008)(66946007)(4326008)(64756008)(966005)(478600001)(55016003)(71200400001)(8936002)(8676002)(5660300002)(52536014)(41300700001)(30864003)(2906002)(38100700002)(122000001)(82960400001)(6506007)(107886003)(186003)(53546011)(9686003)(26005)(83380400001)(2004002)(579004)(559001);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?Tf0GVyWATsQ+dQnOWUW8rd1oHaIWXbn6xuhYnxd+MNOyHSs8wilXNSJW5Ki7?= =?us-ascii?Q?XPhjo5xK1XYCN2R7w9ePogYw1Vq466z7Qp1clW0LyebYYhgn8Gpo9Xj/YOxZ?= =?us-ascii?Q?IyxTvEkH5w9DerMRdQ+o9XRMOGRIbv0szuWuPElxj9W8lUegmgVvQ939EzU8?= =?us-ascii?Q?CFzKfcLx91laMarDnQJKABiDVWbjRvXvthex5s1zhEfl84rMVX9a0xn9uZq9?= =?us-ascii?Q?N/Shg9FSjTR6Tf/LIdFAksSFajGTn7C9kVwXQvIF97xv194LULKKByd/YP/B?= =?us-ascii?Q?gTdS7zZ6bhH8Z9KFtI3SfghiX5wqwrVTOlYxr2RzbmyQ3umyUwaDNqRIkGga?= =?us-ascii?Q?gkes+tdZWj51DbW9uCPAcVtynS1HbPkQZKZ5Rg7wPLqJCIc9s1Vddk9CoSai?= =?us-ascii?Q?r6drNh+YpmfUcr7N/VhMi5aWKEqgyZnqQudB4nlePskNUh3ViuSSbFN6/zls?= =?us-ascii?Q?v+VEatiEJrccFiTR1TAZYj0TEEueLeXsU3S2RdbhyxP1qlg0e09Vg/buTBhq?= =?us-ascii?Q?s18cAvmzKijId/wJTMYlAakmZJKUt713hNyZWC8IKbCjSe0kyFUL7K/pVPgl?= =?us-ascii?Q?MVF2TazXs4s5b5Tu6yzaOmHpnlEtDZ/uOfYNyAyqMDPO5vBgUkoTTcBY1SHb?= =?us-ascii?Q?eO/N2hFY0KAMxm9eaSU3WyJ3RBrrRmaVz6LABqgUxYEkRC7cQqbE8KE3FtN/?= =?us-ascii?Q?MI5v5ETLvXwhGABOOZGNKEDbZvk4rODqlmeGP6iLLDWlF1X9NttRuxi9jV/5?= =?us-ascii?Q?uuuCVX6fxNNbeA1s413v3iPgRGKB1nKedent+h7tEkbAojPBkWoJVKQw6M3G?= =?us-ascii?Q?YqVMZr4PGleS/LDCKr51vjPwgtZa2Ah3EUucrXjlSVfsG6nRxDBVsi4mPJMx?= =?us-ascii?Q?InycE/gF1xTAwOpprwlPfullL5buhjDGk25NoUsQMzYrXh+pnRd1XzYcPbGQ?= =?us-ascii?Q?GDOcCE/IQs4cIg72LA36QK5XVy218aCj9J9aVwDM1KhxSMR6764Z9eE/uglE?= =?us-ascii?Q?yVZVGlwA2/O4hp/IHC/mSG/cm/d+xcvBg4m93hQYOpqj1GrjYQ/FUv4G9+Bh?= =?us-ascii?Q?OL8j/D82MBSgC1WbJ86DNuF/ezrzcA/ZXl9dSQmEzLzYO6G/8U2sXhbvt4Le?= =?us-ascii?Q?s0o8IQzxOvXCdlywm4MoZqsuqHjxY0VBc9xx2O3kWhDS8D+CHerX3/I6JWC2?= =?us-ascii?Q?Jeod8jtnDU5lV9unF/SvAvmvaV1XvukzmF/QHr+yHmcJq8qj4rCkVs6nHl16?= =?us-ascii?Q?aMoZ1S7wVjquAKmzsNcQ/ozJF/hwF/5zlujV2KS1I1jsCIu22JuQJQSyy6kF?= =?us-ascii?Q?lvWCQzcYIV6Spo4aV0gmKJRf0AOVNhTv6NbXD2XThpBP15sxUyd5SLRVaelF?= =?us-ascii?Q?aaI4fg+C3RmC3zVAtzOM6MsvGSDfmbx6j9NW8k/rezNVhVb53V6JQdkD6mZB?= =?us-ascii?Q?qXaVE/Q+KNU8CD2vwPD6rlXZPpMhgwhTYF2IANxnsVheMtoH9r1o28jxOJOw?= =?us-ascii?Q?xKJb4geo/vc4VI9aV99qGGWAQ3ZzNC+obLuS8F83jCyjzBiEHJlKF9iq2juz?= =?us-ascii?Q?f5kXbvP4GMkQp9gv2+hgj0PeJQ+CaqV5nzPFIQYLBU5W49S82q3h5EbFKRex?= =?us-ascii?Q?Zw=3D=3D?= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM4PR11MB7303.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 56b66b59-0623-48c2-a45c-08db561f82e6 X-MS-Exchange-CrossTenant-originalarrivaltime: 16 May 2023 15:09:14.2925 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 46c98d88-e344-4ed4-8496-4ed7712e255d X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: GWxi58fpZmU4SkbUE1Cq5F9tR2EQqcYGNDxHZg6iXIOY1I69qhWRi4B8i5T/XfHbCWfd2gHu9qjeT579TAbjQKEO6r2D62CM+AOcczXnfdU= X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM8PR11MB5623 X-OriginatorOrg: intel.com Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-11.8 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Monday, May 15, 2023 9:22 PM, Andrew Burgess wrote: > This commit fixes bug PR 28942, that is, creating a conditional > breakpoint in a multi-threaded inferior, where the breakpoint > condition includes an inferior function call. > = > Currently, when a user tries to create such a breakpoint, then GDB > will fail with: > = > (gdb) break infcall-from-bp-cond-single.c:61 if (return_true ()) > Breakpoint 2 at 0x4011fa: file > /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.threads/infcall-f= rom-bp-cond- > single.c, line 61. > (gdb) continue > Continuing. > [New Thread 0x7ffff7c5d700 (LWP 2460150)] > [New Thread 0x7ffff745c700 (LWP 2460151)] > [New Thread 0x7ffff6c5b700 (LWP 2460152)] > [New Thread 0x7ffff645a700 (LWP 2460153)] > [New Thread 0x7ffff5c59700 (LWP 2460154)] > Error in testing breakpoint condition: > Couldn't get registers: No such process. > An error occurred while in a function called from GDB. > Evaluation of the expression containing the function > (return_true) will be abandoned. > When the function is done executing, GDB will silently stop. > Selected thread is running. > (gdb) > = > Or, in some cases, like this: > = > (gdb) break infcall-from-bp-cond-simple.c:56 if (is_matching_tid (arg, = 1)) > Breakpoint 2 at 0x401194: file > /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.threads/infcall-f= rom-bp-cond- > simple.c, line 56. > (gdb) continue > Continuing. > [New Thread 0x7ffff7c5d700 (LWP 2461106)] > [New Thread 0x7ffff745c700 (LWP 2461107)] > ../../src.release/gdb/nat/x86-linux-dregs.c:146: internal-error: > x86_linux_update_debug_registers: Assertion `lwp_is_stopped (lwp)' failed. > A problem internal to GDB has been detected, > further debugging may prove unreliable. > = > The precise error depends on the exact thread state; so there's race > conditions depending on which threads have fully started, and which > have not. But the underlying problem is always the same; when GDB > tries to execute the inferior function call from within the breakpoint > condition, GDB will, incorrectly, try to resume threads that are > already running - GDB doesn't realise that some threads might already > be running. > = > The solution proposed in this patch requires an additional member > variable thread_info::in_cond_eval. This flag is set to true (in > breakpoint.c) when GDB is evaluating a breakpoint condition. > = > In user_visible_resume_ptid (infrun.c), when the in_cond_eval flag is > true, then GDB will only try to resume the current thread, that is, > the thread for which the breakpoint condition is being evaluated. > This solves the problem of GDB trying to resume threads that are > already running. > = > The next problem is that inferior function calls are assumed to be > synchronous, that is, GDB doesn't expect to start an inferior function > call in thread #1, then receive a stop from thread #2 for some other, > unrelated reason. To prevent GDB responding to an event from another > thread, we update fetch_inferior_event and do_target_wait in infrun.c, > so that, when an inferior function call (on behalf of a breakpoint > condition) is in progress, we only wait for events from the current > thread (the one evaluating the condition). > = > In do_target_wait I had to change the inferior_matches lambda > function, which is used to select which inferior to wait on. > Previously the logic was this: > = > auto inferior_matches =3D [&wait_ptid] (inferior *inf) > { > return (inf->process_target () !=3D nullptr > && ptid_t (inf->pid).matches (wait_ptid)); > }; > = > This compares the pid of the inferior against the complete ptid we > want to wait on. Before this commit wait_ptid was only ever > minus_one_ptid (which is special, and means any process), and so every > inferior would match. > = > After this commit though wait_ptid might represent a specific thread > in a specific inferior. If we compare the pid of the inferior to a > specific ptid then these will not match. The fix is to compare > against the pid extracted from the wait_ptid, not against the complete > wait_ptid itself. > = > In fetch_inferior_event, after receiving the event, we only want to > stop all the other threads, and call inferior_event_handler with > INF_EXEC_COMPLETE, if we are not evaluating a conditional breakpoint. > If we are, then all the other threads should be left doing whatever > they were before. The inferior_event_handler call will be performed > once the breakpoint condition has finished being evaluated, and GDB > decides to stop or not. > = > The final problem that needs solving relates to GDB's commit-resume > mechanism, which allows GDB to collect resume requests into a single > packet in order to reduce traffic to a remote target. > = > The problem is that the commit-resume mechanism will not send any > resume requests for an inferior if there are already events pending on > the GDB side. > = > Imagine an inferior with two threads. Both threads hit a breakpoint, > maybe the same conditional breakpoint. At this point there are two > pending events, one for each thread. > = > GDB selects one of the events and spots that this is a conditional > breakpoint, GDB evaluates the condition. > = > The condition includes an inferior function call, so GDB sets up for > the call and resumes the one thread, the resume request is added to > the commit-resume queue. > = > When the commit-resume queue is committed GDB sees that there is a > pending event from another thread, and so doesn't send any resume > requests to the actual target, GDB is assuming that when we wait we > will select the event from the other thread. > = > However, as this is an inferior function call for a condition > evaluation, we will not select the event from the other thread, we > only care about events from the thread that is evaluating the > condition - and the resume for this thread was never sent to the > target. > = > And so, GDB hangs, waiting for an event from a thread that was never > fully resumed. > = > To fix this issue I have added the concept of "forcing" the > commit-resume queue. When enabling commit resume, if the force flag > is true, then any resumes will be committed to the target, even if > there are other threads with pending events. > = > A note on authorship: this patch was based on some work done by > Natalia Saiapova and Tankut Baris Aktemur from Intel[1]. I have made > some changes to their work in this version. > = > Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=3D28942 > = > [1] https://sourceware.org/pipermail/gdb-patches/2020-October/172454.html > = > Co-authored-by: Natalia Saiapova > Co-authored-by: Tankut Baris Aktemur Thanks again for this. I have a few minor comments inlined below. Except those, it looks good to = me. There is already Co-authored-by above, but in case you'd like to also add: Reviewed-By: Tankut Baris Aktemur Regards -Baris > --- > gdb/breakpoint.c | 2 + > gdb/gdbthread.h | 3 + > gdb/infcall.c | 6 + > gdb/infrun.c | 64 +++-- > gdb/infrun.h | 3 +- > .../infcall-from-bp-cond-other-thread-event.c | 135 ++++++++++ > ...nfcall-from-bp-cond-other-thread-event.exp | 174 +++++++++++++ > .../gdb.threads/infcall-from-bp-cond-simple.c | 89 +++++++ > .../infcall-from-bp-cond-simple.exp | 235 ++++++++++++++++++ > .../gdb.threads/infcall-from-bp-cond-single.c | 139 +++++++++++ > .../infcall-from-bp-cond-single.exp | 117 +++++++++ > 11 files changed, 952 insertions(+), 15 deletions(-) > create mode 100644 gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-= thread-event.c > create mode 100644 gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-= thread-event.exp > create mode 100644 gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple= .c > create mode 100644 gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple= .exp > create mode 100644 gdb/testsuite/gdb.threads/infcall-from-bp-cond-single= .c > create mode 100644 gdb/testsuite/gdb.threads/infcall-from-bp-cond-single= .exp > = > diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c > index fdb184ae81f..6fceed8f408 100644 > --- a/gdb/breakpoint.c > +++ b/gdb/breakpoint.c > @@ -5548,6 +5548,8 @@ bpstat_check_breakpoint_conditions (bpstat *bs, thr= ead_info *thread) > { > try > { > + scoped_restore reset_in_cond_eval > + =3D make_scoped_restore (&thread->control.in_cond_eval, true); > condition_result =3D breakpoint_cond_eval (cond); > } > catch (const gdb_exception_error &ex) > diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h > index 7135515bf45..897752f2691 100644 > --- a/gdb/gdbthread.h > +++ b/gdb/gdbthread.h > @@ -171,6 +171,9 @@ struct thread_control_state > command. This is used to decide whether "set scheduler-locking > step" behaves like "on" or "off". */ > int stepping_command =3D 0; > + > + /* True if the thread is evaluating a BP condition. */ > + bool in_cond_eval =3D false; > }; > = > /* Inferior thread specific part of `struct infcall_suspend_state'. */ > diff --git a/gdb/infcall.c b/gdb/infcall.c > index 233ef5f29e9..49c88add394 100644 > --- a/gdb/infcall.c > +++ b/gdb/infcall.c > @@ -642,6 +642,12 @@ run_inferior_call (std::unique_ptr = sm, > = > proceed (real_pc, GDB_SIGNAL_0); > = > + /* Enable commit resume, but pass true for the force flag. This > + ensures any thread we set running in proceed will actually be > + committed to the target, even if some other thread in the current > + target has a pending event. */ > + scoped_enable_commit_resumed enable ("infcall", true); > + > infrun_debug_show_threads ("non-exited threads after proceed for i= nferior-call", > all_non_exited_threads ()); > = > diff --git a/gdb/infrun.c b/gdb/infrun.c > index 07ef3c7c187..ea7ab6187ee 100644 > --- a/gdb/infrun.c > +++ b/gdb/infrun.c > @@ -2275,6 +2275,14 @@ user_visible_resume_ptid (int step) > mode. */ > resume_ptid =3D inferior_ptid; > } > + else if (inferior_ptid !=3D null_ptid > + && inferior_thread ()->control.in_cond_eval) > + { > + /* The inferior thread is evaluating a BP condition. Other threads > + might be stopped or running and we do not want to change their > + state, thus, resume only the current thread. */ > + resume_ptid =3D inferior_ptid; > + } > else if (!sched_multi && target_supports_multi_process ()) > { > /* Resume all threads of the current process (and none of other > @@ -2992,12 +3000,24 @@ schedlock_applies (struct thread_info *tp) > execution_direction))); > } > = > -/* Set process_stratum_target::COMMIT_RESUMED_STATE in all target > - stacks that have threads executing and don't have threads with > - pending events. */ > +/* When FORCE_P is false, set process_stratum_target::COMMIT_RESUMED_STA= TE > + in all target stacks that have threads executing and don't have threa= ds > + with pending events. > + > + When FORCE_P is true, set process_stratum_target::COMMIT_RESUMED_STATE > + in all target stacks that have threads executing regardless of whether > + there are pending events or not. > + > + Passing FORCE_P as false makes sense when GDB is going to wait for > + events from all threads and will therefore spot the pending events. > + However, if GDB is only going to wait for events from select threads > + (i.e. when performing an inferior call) then a pending event on some > + other thread will not be spotted, and if we fail to commit the resume > + state for the thread performing the inferior call, then the inferior > + call will never complete (or even start). */ There is one extra space of indentation above. Is that intentional? > static void > -maybe_set_commit_resumed_all_targets () > +maybe_set_commit_resumed_all_targets (bool force_p) > { > scoped_restore_current_thread restore_thread; > = > @@ -3026,7 +3046,7 @@ maybe_set_commit_resumed_all_targets () > status to report, handle it before requiring the target to > commit its resumed threads: handling the status might lead to > resuming more threads. */ > - if (proc_target->has_resumed_with_pending_wait_status ()) > + if (!force_p && proc_target->has_resumed_with_pending_wait_status = ()) > { > infrun_debug_printf ("not requesting commit-resumed for target %s, a" > " thread has a pending waitstatus", > @@ -3036,7 +3056,7 @@ maybe_set_commit_resumed_all_targets () > = > switch_to_inferior_no_thread (inf); > = > - if (target_has_pending_events ()) > + if (!force_p && target_has_pending_events ()) > { > infrun_debug_printf ("not requesting commit-resumed for target %s, " > "target has pending events", > @@ -3129,7 +3149,7 @@ scoped_disable_commit_resumed::reset () > { > /* This is the outermost instance, re-enable > COMMIT_RESUMED_STATE on the targets where it's possible. */ > - maybe_set_commit_resumed_all_targets (); > + maybe_set_commit_resumed_all_targets (false); > } > else > { > @@ -3162,7 +3182,7 @@ scoped_disable_commit_resumed::reset_and_commit () > /* See infrun.h. */ > = > scoped_enable_commit_resumed::scoped_enable_commit_resumed > - (const char *reason) > + (const char *reason, bool force_p) > : m_reason (reason), > m_prev_enable_commit_resumed (enable_commit_resumed) > { > @@ -3174,7 +3194,7 @@ scoped_enable_commit_resumed::scoped_enable_commit_= resumed > = > /* Re-enable COMMIT_RESUMED_STATE on the targets where it's > possible. */ > - maybe_set_commit_resumed_all_targets (); > + maybe_set_commit_resumed_all_targets (force_p); > = > maybe_call_commit_resumed_all_targets (); > } > @@ -3880,10 +3900,11 @@ do_target_wait (ptid_t wait_ptid, execution_contr= ol_state *ecs, > polling the rest of the inferior list starting from that one in a > circular fashion until the whole list is polled once. */ > = > - auto inferior_matches =3D [&wait_ptid] (inferior *inf) > + ptid_t wait_ptid_pid {wait_ptid.pid ()}; > + auto inferior_matches =3D [&wait_ptid_pid] (inferior *inf) > { > return (inf->process_target () !=3D nullptr > - && ptid_t (inf->pid).matches (wait_ptid)); > + && ptid_t (inf->pid).matches (wait_ptid_pid)); > }; > = > /* First see how many matching inferiors we have. */ > @@ -4352,7 +4373,17 @@ fetch_inferior_event () > the event. */ > scoped_disable_commit_resumed disable_commit_resumed ("handling even= t"); > = > - if (!do_target_wait (minus_one_ptid, &ecs, TARGET_WNOHANG)) > + /* Is the current thread performing an inferior function call as part > + of a breakpoint condition evaluation? */ > + bool in_cond_eval =3D (inferior_ptid !=3D null_ptid > + && inferior_thread ()->control.in_cond_eval); > + > + /* If the thread is in the middle of the condition evaluation, wait = for > + an event from the current thread. Otherwise, wait for an event f= rom > + any thread. */ > + ptid_t waiton_ptid =3D in_cond_eval ? inferior_ptid : minus_one_ptid; > + > + if (!do_target_wait (waiton_ptid, &ecs, TARGET_WNOHANG)) > { > infrun_debug_printf ("do_target_wait returned no event"); > disable_commit_resumed.reset_and_commit (); > @@ -4408,7 +4439,12 @@ fetch_inferior_event () > bool should_notify_stop =3D true; > bool proceeded =3D false; > = > - stop_all_threads_if_all_stop_mode (); > + /* If the thread that stopped just completed an inferior > + function call as part of a condition evaluation, then we > + don't want to stop all the other threads. */ > + if (ecs.event_thread =3D=3D nullptr > + || !ecs.event_thread->control.in_cond_eval) > + stop_all_threads_if_all_stop_mode (); > = > clean_up_just_stopped_threads_fsms (&ecs); > = > @@ -4423,7 +4459,7 @@ fetch_inferior_event () > proceeded =3D normal_stop (); > } > = > - if (!proceeded) > + if (!proceeded && !in_cond_eval) > { > inferior_event_handler (INF_EXEC_COMPLETE); > cmd_done =3D 1; > diff --git a/gdb/infrun.h b/gdb/infrun.h > index 9513bc570e4..1a6743aa45e 100644 > --- a/gdb/infrun.h > +++ b/gdb/infrun.h > @@ -395,7 +395,8 @@ extern void maybe_call_commit_resumed_all_targets (); > = > struct scoped_enable_commit_resumed > { > - explicit scoped_enable_commit_resumed (const char *reason); > + explicit scoped_enable_commit_resumed (const char *reason, > + bool force_p =3D false); > ~scoped_enable_commit_resumed (); > = > DISABLE_COPY_AND_ASSIGN (scoped_enable_commit_resumed); > diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-= event.c > b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.c > new file mode 100644 > index 00000000000..e2a8ccb4ebe > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.c > @@ -0,0 +1,135 @@ > +/* Copyright 2022-2023 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + 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 .= */ > + > +#include > +#include > +#include > +#include > + > +#define NUM_THREADS 2 > + > +pthread_mutex_t mutex =3D PTHREAD_MUTEX_INITIALIZER; > + > +/* Some global variables to poke, just for something to do. */ > +volatile int global_var_0 =3D 0; > +volatile int global_var_1 =3D 0; > + > +/* This flag is updated from GDB. */ > +volatile int raise_signal =3D 0; > + > +/* Implement the breakpoint condition function. Release the other thread > + and try to give the other thread a chance to run. Then return ANSWER= . */ > +int > +condition_core_func (int answer) > +{ > + /* This unlock should release the other thread. */ > + if (pthread_mutex_unlock (&mutex) !=3D 0) > + abort (); > + > + /* And this yield and sleep should (hopefully) give the other thread a > + chance to run. This isn't guaranteed of course, but once the other > + thread does run it should hit a breakpoint, which GDB should > + (temporarily) ignore, so there's no easy way for us to know the oth= er > + thread has done what it needs to, thus, yielding and sleeping is the > + best we can do. */ > + sched_yield (); > + sleep (2); > + > + return answer; > +} > + > +void > +stop_marker () > +{ > + int a =3D 100; /* Final breakpoint here. */ > +} > + > +/* A breakpoint condition function that always returns true. */ > +int > +condition_true_func () > +{ > + return condition_core_func (1); > +} > + > +/* A breakpoint condition function that always returns false. */ > +int > +condition_false_func () > +{ > + return condition_core_func (0); > +} > + > +void * > +worker_func (void *arg) > +{ > + volatile int *ptr =3D 0; > + int tid =3D *((int *) arg); > + > + switch (tid) > + { > + case 0: > + global_var_0 =3D 11; /* First thread breakpoint. */ > + break; > + > + case 1: > + if (pthread_mutex_lock (&mutex) !=3D 0) > + abort (); > + if (raise_signal) > + global_var_1 =3D *ptr; /* Signal here. */ > + else > + global_var_1 =3D 99; /* Other thread breakpoint. */ > + break; > + > + default: > + abort (); > + } > + > + return NULL; > +} > + > +int > +main () > +{ > + pthread_t threads[NUM_THREADS]; > + int args[NUM_THREADS]; > + > + /* Set an alarm, just in case the test deadlocks. */ > + alarm (300); > + > + /* We want the mutex to start locked. */ > + if (pthread_mutex_lock (&mutex) !=3D 0) > + abort (); > + > + for (int i =3D 0; i < NUM_THREADS; i++) > + { > + args[i] =3D i; > + pthread_create (&threads[i], NULL, worker_func, &args[i]); > + } > + > + for (int i =3D 0; i < NUM_THREADS; i++) > + { > + void *retval; > + pthread_join (threads[i], &retval); > + } > + > + /* Unlock once we're done, just for cleanliness. */ > + if (pthread_mutex_unlock (&mutex) !=3D 0) > + abort (); > + > + stop_marker (); > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-= event.exp > b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.exp > new file mode 100644 > index 00000000000..6d4e1e13ab2 > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.e= xp > @@ -0,0 +1,174 @@ > +# Copyright 2022-2023 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 for conditional breakpoints where the breakpoint condition includ= es > +# an inferior function call. > +# > +# The tests in this script are testing what happens when an event arrive= s in > +# another thread while GDB is waiting for the inferior function call (in= the > +# breakpoint condition) to finish. > +# > +# The expectation is that GDB will queue events for other threads and wa= it > +# for the inferior function call to complete, if the condition is true, = then > +# the conditional breakpoint should be reported first. The other thread > +# event should of course, not get lost, and should be reported as soon as > +# the user tries to continue the inferior. > +# > +# If the conditional breakpoint ends up not being taken (the condition is > +# false), then the other thread event should be reported immediately. > +# > +# This script tests what happens when the other thread event is (a) the > +# other thread hitting a breakpoint, and (b) the other thread taking a > +# signal (SIGSEGV in this case). > + > +standard_testfile > + > +if { [build_executable "failed to prepare" ${binfile} "${srcfile}" \ > + {debug pthreads}] =3D=3D -1 } { > + return > +} > + > +set cond_bp_line [gdb_get_line_number "First thread breakpoint"] > +set other_bp_line [gdb_get_line_number "Other thread breakpoint"] > +set final_bp_line [gdb_get_line_number "Final breakpoint here"] > +set signal_line [gdb_get_line_number "Signal here"] > + > +# Start GDB based on TARGET_ASYNC and TARGET_NON_STOP, and then runto ma= in. > +proc start_gdb_and_runto_main { target_async target_non_stop } { > + save_vars { ::GDBFLAGS } { > + append ::GDBFLAGS \ > + " -ex \"maint set target-non-stop $target_non_stop\"" > + append ::GDBFLAGS \ > + " -ex \"maintenance set target-async ${target_async}\"" > + > + clean_restart ${::binfile} > + } > + > + if { ![runto_main] } { > + return -1 > + } > + > + return 0 > +} > + > +# Run a test of GDB's conditional breakpoints, where the conditions incl= ude > +# inferior function calls. While the inferior function call is executing > +# another thread will hit a breakpoint (when OTHER_THREAD_SIGNAL is fals= e), > +# or receive a signal (when OTHER_THREAD_SIGNAL is true). GDB should re= port > +# the conditional breakpoint first (if the condition is true), and then > +# report the second thread event once the inferior is continued again. > +# > +# When STOP_AT_COND is true then the conditional breakpoint will have a > +# condition that evaluates to true (and the GDB will stop at the Nit: No "the" before "GDB". > +# breakpoint), otherwise, the condition will evaluate to false (and GDB = will > +# not stop at the breakpoint). > +proc run_condition_test { stop_at_cond other_thread_signal \ > + target_async target_non_stop } { > + if { [start_gdb_and_runto_main $target_async \ > + $target_non_stop] =3D=3D -1 } { > + return > + } > + > + # Setup the conditional breakpoint. > + if { $stop_at_cond } { > + set cond_func "condition_true_func" > + } else { > + set cond_func "condition_false_func" > + } > + gdb_breakpoint \ > + "${::srcfile}:${::cond_bp_line} if (${cond_func} ())" > + set cond_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number for conditional breakpoint"] > + > + if { $other_thread_signal } { > + # Arrange for the other thread to raise a signal while GDB is > + # evaluating the breakpoint condition. > + gdb_test_no_output "set raise_signal =3D 1" > + } else { > + # And a breakpoint that will be hit by another thread only once the > + # breakpoint condition starts to be evaluated. > + gdb_breakpoint "${::srcfile}:${::other_bp_line}" > + set other_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number for other breakpoint"] > + } > + > + # A final breakpoint once the test has completed. > + gdb_breakpoint "${::srcfile}:${::final_bp_line}" > + set final_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number for final breakpoint"] > + > + if { $stop_at_cond } { > + # Continue. The first breakpoint we hit should be the conditional > + # breakpoint. The other thread will have hit its breakpoint, but > + # that will have been deferred until the conditional breakpoint is > + # reported. > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + ".*" \ > + "" \ > + "Thread ${::decimal} \"\[^\"\r\n\]+\" hit Breakpoint ${cond_bp_num}, > worker_func \[^\r\n\]+:${::cond_bp_line}" \ > + "${::decimal}\\s+\[^\r\n\]+First thread breakpoint\[^\r\n\]+"] \ > + "hit the conditional breakpoint" > + } > + > + if { $other_thread_signal } { > + # Now continue again, the other thread will now report that it > + # received a signal. > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + ".*" \ > + "Thread ${::decimal} \"\[^\"\r\n\]+\" received signal SIGSEGV, Segmen= tation > fault\\." \ > + "\\\[Switching to Thread \[^\r\n\]+\\\]" \ > + "${::hex} in worker_func \[^\r\n\]+:${::signal_line}" \ > + "${::decimal}\\s+\[^\r\n\]+Signal here\[^\r\n\]+"] \ > + "received signal in other thread" > + } else { > + # Now continue again, the other thread will now report its > + # breakpoint. > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + ".*" \ > + "" \ > + "Thread ${::decimal} \"\[^\"\r\n\]+\" hit Breakpoint ${other_bp_num}, > worker_func \[^\r\n\]+:${::other_bp_line}" \ > + "${::decimal}\\s+\[^\r\n\]+Other thread breakpoint\[^\r\n\]+"] \ > + "hit the breakpoint in other thread" > + > + # Run to the stop marker. > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + ".*" \ > + "" \ > + "Thread ${::decimal} \"\[^\"\r\n\]+\" hit Breakpoint ${final_bp_num}, > stop_marker \[^\r\n\]+:${::final_bp_line}" \ > + "${::decimal}\\s+\[^\r\n\]+Final breakpoint here\[^\r\n\]+"] \ > + "hit the final breakpoint" > + } > + > + gdb_exit Is this really necessary? We do clean_restart at the beginning of a test, which exits GDB as the first thing. > +} > + > +foreach_with_prefix target_async { "on" "off" } { > + foreach_with_prefix target_non_stop { "on" "off" } { > + foreach_with_prefix other_thread_signal { true false } { > + foreach_with_prefix stop_at_cond { true false } { > + run_condition_test $stop_at_cond $other_thread_signal \ > + $target_async $target_non_stop > + } > + } > + } > +} > diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.c > b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.c > new file mode 100644 > index 00000000000..9d746d8be49 > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.c > @@ -0,0 +1,89 @@ > +/* Copyright 2022-2023 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + 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 .= */ > + > +#include > +#include > + > +#define NUM_THREADS 3 > + > +int > +is_matching_tid (int *tid_ptr, int tid_value) > +{ > + return *tid_ptr =3D=3D tid_value; > +} > + > +int > +return_true () > +{ > + return 1; > +} > + > +int > +return_false () > +{ > + return 0; > +} > + > +int > +function_that_segfaults () > +{ > + int *p =3D 0; > + *p =3D 1; /* Segfault happens here. */ > +} > + > +int > +function_with_breakpoint () > +{ > + return 1; /* Nested breakpoint. */ > +} > + > +void * > +worker_func (void *arg) > +{ > + int a =3D 42; /* Breakpoint here. */ > +} > + > +void > +stop_marker () > +{ > + int b =3D 99; /* Stop marker. */ > +} > + > +int > +main () > +{ > + pthread_t threads[NUM_THREADS]; > + int args[NUM_THREADS]; > + > + alarm (300); > + > + for (int i =3D 0; i < NUM_THREADS; i++) > + { > + args[i] =3D i; > + pthread_create (&threads[i], NULL, worker_func, &args[i]); > + } > + > + for (int i =3D 0; i < NUM_THREADS; i++) > + { > + void *retval; > + pthread_join (threads[i], &retval); > + } > + > + stop_marker (); > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp > b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp > new file mode 100644 > index 00000000000..37e1b64d9a4 > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp > @@ -0,0 +1,235 @@ > +# Copyright 2022-2023 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 . > + > +# Some simple tests of inferior function calls from breakpoint > +# conditions, in multi-threaded inferiors. > +# > +# This test sets up a multi-threaded inferior, and places a breakpoint > +# at a location that many of the threads will reach. We repeat the > +# test with different conditions, sometimes a single thread should > +# stop at the breakpoint, sometimes multiple threads should stop, and > +# sometime no threads should stop. Typo: sometime -> sometimes > + > +standard_testfile > + > +if { [build_executable "failed to prepare" ${binfile} "${srcfile}" \ > + {debug pthreads}] =3D=3D -1 } { > + return > +} > + > +set cond_bp_line [gdb_get_line_number "Breakpoint here"] > +set stop_bp_line [gdb_get_line_number "Stop marker"] > +set nested_bp_line [gdb_get_line_number "Nested breakpoint"] > +set segv_line [gdb_get_line_number "Segfault happens here"] > + > +# Start GDB based on TARGET_ASYNC and TARGET_NON_STOP, and then runto ma= in. > +proc start_gdb_and_runto_main { target_async target_non_stop } { > + save_vars { ::GDBFLAGS } { > + append ::GDBFLAGS \ > + " -ex \"maint set target-non-stop $target_non_stop\"" > + append ::GDBFLAGS \ > + " -ex \"maintenance set target-async ${target_async}\"" > + > + clean_restart ${::binfile} > + } > + > + if { ![runto_main] } { > + return -1 > + } > + > + return 0 > +} > + > +# Run a test of GDB's conditional breakpoints, where the conditions incl= ude > +# inferior function calls. > +# > +# CONDITION is the expression to be used as the breakpoint condition. > +# > +# N_EXPECTED_HITS is the number of threads that we expect to stop due to > +# CONDITON. > +# > +# MESSAGE is used as a test name prefix. > +proc run_condition_test { message n_expected_hits condition \ > + target_async target_non_stop } { > + with_test_prefix $message { > + > + if { [start_gdb_and_runto_main $target_async \ > + $target_non_stop] =3D=3D -1 } { > + return > + } > + > + # Use this convenience variable to track how often the > + # breakpoint condition has been evaluated. This should be > + # once per thread. > + gdb_test "set \$n_cond_eval =3D 0" > + > + # Setup the conditional breakpoint. > + gdb_breakpoint \ > + "${::srcfile}:${::cond_bp_line} if ((++\$n_cond_eval) && (${conditi= on}))" > + > + # And a breakpoint that we hit when the test is over, this one is > + # not conditional. Only the main thread gets here once all the > + # other threads have finished. > + gdb_breakpoint "${::srcfile}:${::stop_bp_line}" > + > + # The number of times we stop at the conditional breakpoint. > + set n_hit_condition 0 > + > + # Now keep 'continue'-ing GDB until all the threads have finished > + # and we reach the stop_marker breakpoint. > + gdb_test_multiple "continue" "spot all breakpoint hits" { > + -re " worker_func > \[^\r\n\]+${::srcfile}:${::cond_bp_line}\r\n${::decimal}\\s+\[^\r\n\]+Bre= akpoint > here\[^\r\n\]+\r\n${::gdb_prompt} $" { > + incr n_hit_condition > + send_gdb "continue\n" > + exp_continue > + } > + > + -re " stop_marker > \[^\r\n\]+${::srcfile}:${::stop_bp_line}\r\n${::decimal}\\s+\[^\r\n\]+Stop > marker\[^\r\n\]+\r\n${::gdb_prompt} $" { > + pass $gdb_test_name > + } > + } > + > + gdb_assert { $n_hit_condition =3D=3D $n_expected_hits } \ > + "stopped at breakpoint the expected number of times" > + > + # Ensure the breakpoint condition was evaluated once per thread. > + gdb_test "print \$n_cond_eval" "=3D 3" \ > + "condition was evaluated in each thread" > + } > +} > + > +# Check that after handling a conditional breakpoint (where the condition > +# includes an inferior call), it is still possible to kill the running > +# inferior, and then restart the inferior. > +# > +# At once point doing this would result in GDB giving an assertion error. > +proc_with_prefix run_kill_and_restart_test { target_async target_non_sto= p } { > + # This test relies on the 'start' command, which is not possible with > + # the plain 'remote' target. > + if { [target_info gdb_protocol] =3D=3D "remote" } { > + return > + } > + > + if { [start_gdb_and_runto_main $target_async \ > + $target_non_stop] =3D=3D -1 } { > + return > + } > + > + # Setup the conditional breakpoint. > + gdb_breakpoint \ > + "${::srcfile}:${::cond_bp_line} if (is_matching_tid (arg, 1))" > + gdb_continue_to_breakpoint "worker_func" > + > + # Now kill the program being debugged. > + gdb_test "kill" "" "kill process" \ > + "Kill the program being debugged.*y or n. $" "y" > + > + # Check we can restart the inferior. At one point this would trigge= r an > + # assertion. > + gdb_test "start" ".*" I believe using 'gdb_start_cmd' is preferred. > +} > + > +# Create a conditional breakpoint which includes a call to a function th= at > +# segfaults. Run GDB and check what happens when the inferior segfaults > +# during the inferior call. > +proc_with_prefix run_bp_cond_segfaults { target_async target_non_stop } { > + if { [start_gdb_and_runto_main $target_async \ > + $target_non_stop] =3D=3D -1 } { > + return > + } > + > + # This test relies on the inferior segfaulting when trying to > + # access address zero. > + if { [is_address_zero_readable] } { > + return > + } > + > + # Setup the conditional breakpoint, include a call to > + # 'function_that_segfaults', which triggers the segfault. > + gdb_breakpoint \ > + "${::srcfile}:${::cond_bp_line} if (is_matching_tid (arg, 0) && > function_that_segfaults ())" > + set bp_1_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number of conditional breakpoint"] > + > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + ".*" \ > + "Thread ${::decimal} \"infcall-from-bp\" received signal SIGSEGV, = Segmentation > fault\\." \ > + "${::hex} in function_that_segfaults \\(\\) at \[^\r\n\]+:${::segv= _line}" \ > + "${::decimal}\\s+\[^\r\n\]+Segfault happens here\[^\r\n\]+" \ > + "Error in testing condition for breakpoint ${bp_1_num}:" \ > + "The program being debugged was signaled while in a function calle= d from GDB\\." > \ > + "GDB remains in the frame where the signal was received\\." \ > + "To change this behavior use \"set unwindonsignal on\"\\." \ > + "Evaluation of the expression containing the function" \ > + "\\(function_that_segfaults\\) will be abandoned\\." \ > + "When the function is done executing, GDB will silently stop\\."] > +} > + > +# Create a conditional breakpoint which includes a call to a function th= at > +# itself has a breakpoint set within it. Run GDB and check what happens > +# when GDB hits the nested breakpoint. > +proc_with_prefix run_bp_cond_hits_breakpoint { target_async target_non_s= top } { > + if { [start_gdb_and_runto_main $target_async \ > + $target_non_stop] =3D=3D -1 } { > + return > + } > + > + # Setup the conditional breakpoint, include a call to > + # 'function_with_breakpoint' in which we will shortly place a > + # breakpoint. > + gdb_breakpoint \ > + "${::srcfile}:${::cond_bp_line} if (is_matching_tid (arg, 0) && > function_with_breakpoint ())" > + set bp_1_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number of conditional breakpoint"] > + > + gdb_breakpoint "${::srcfile}:${::nested_bp_line}" > + set bp_2_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number of nested breakpoint"] > + > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + ".*" \ > + "Thread ${::decimal} \"infcall-from-bp\" hit Breakpoint ${bp_2_num= }, > function_with_breakpoint \\(\\) at \[^\r\n\]+:${::nested_bp_line}" \ > + "${::decimal}\\s+\[^\r\n\]+Nested breakpoint\[^\r\n\]+" \ > + "Error in testing condition for breakpoint ${bp_1_num}:" \ > + "The program being debugged stopped while in a function called fro= m GDB\\." \ > + "Evaluation of the expression containing the function" \ > + "\\(function_with_breakpoint\\) will be abandoned\\." \ > + "When the function is done executing, GDB will silently stop\\."] > +} > + > +foreach_with_prefix target_async { "on" "off" } { > + foreach_with_prefix target_non_stop { "on" "off" } { > + run_condition_test "exactly one thread is hit" \ > + 1 "is_matching_tid (arg, 1)" \ > + $target_async $target_non_stop > + run_condition_test "exactly two threads are hit" \ > + 2 "(is_matching_tid (arg, 0) || is_matching_tid (arg, 2))" \ > + $target_async $target_non_stop > + run_condition_test "all three threads are hit" \ > + 3 "return_true ()" \ > + $target_async $target_non_stop > + run_condition_test "no thread is hit" \ > + 0 "return_false ()" \ > + $target_async $target_non_stop > + > + run_kill_and_restart_test $target_async $target_non_stop > + run_bp_cond_segfaults $target_async $target_non_stop > + run_bp_cond_hits_breakpoint $target_async $target_non_stop > + } > +} > diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.c > b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.c > new file mode 100644 > index 00000000000..835c72f03cf > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.c > @@ -0,0 +1,139 @@ > +/* Copyright 2022-2023 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + 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 .= */ > + > +#include > +#include > +#include > +#include > + > +#define NUM_THREADS 5 > + > +/* Semaphores, used to track when threads have started, and to control > + when the threads finish. */ > +sem_t startup_semaphore; > +sem_t finish_semaphore; > + > +/* Mutex to control when the first worker thread hit a breakpoint > + location. */ > +pthread_mutex_t mutex =3D PTHREAD_MUTEX_INITIALIZER; > + > +/* Global variable to poke, just so threads have something to do. */ > +volatile int global_var =3D 0; > + > +int > +return_true () > +{ > + return 1; > +} > + > +int > +return_false () > +{ > + return 0; > +} > + > +void * > +worker_func (void *arg) > +{ > + int tid =3D *((int *) arg); > + > + switch (tid) > + { > + case 0: > + /* Wait for MUTEX to become available, then pass through the > + conditional breakpoint location. */ > + if (pthread_mutex_lock (&mutex) !=3D 0) > + abort (); > + global_var =3D 99; /* Conditional breakpoint here. */ > + if (pthread_mutex_unlock (&mutex) !=3D 0) > + abort (); > + break; > + > + default: > + /* Notify the main thread that the thread has started, then wait f= or > + the main thread to tell us to finish. */ > + sem_post (&startup_semaphore); > + if (sem_wait (&finish_semaphore) !=3D 0) > + abort (); > + break; > + } > +} > + > +void > +stop_marker () > +{ > + global_var =3D 99; /* Stop marker. */ > +} > + > +int > +main () > +{ > + pthread_t threads[NUM_THREADS]; > + int args[NUM_THREADS]; > + void *retval; > + > + /* An alarm, just in case the thread deadlocks. */ > + alarm (300); > + > + /* Semaphore initialization. */ > + if (sem_init (&startup_semaphore, 0, 0) !=3D 0) > + abort (); > + if (sem_init (&finish_semaphore, 0, 0) !=3D 0) > + abort (); > + > + /* Lock MUTEX, this prevents the first worker thread from rushing ahea= d. */ > + if (pthread_mutex_lock (&mutex) !=3D 0) > + abort (); > + > + /* Worker thread creation. */ > + for (int i =3D 0; i < NUM_THREADS; i++) > + { > + args[i] =3D i; > + pthread_create (&threads[i], NULL, worker_func, &args[i]); > + } > + > + /* Wait for every thread (other than the first) to tell us it has star= ted > + up. */ > + for (int i =3D 1; i < NUM_THREADS; i++) > + { > + if (sem_wait (&startup_semaphore) !=3D 0) > + abort (); > + } > + > + /* Unlock the first thread so it can proceed. */ > + if (pthread_mutex_unlock (&mutex) !=3D 0) > + abort (); > + > + /* Wait for the first thread only. */ > + pthread_join (threads[0], &retval); > + > + /* Now post FINISH_SEMAPHORE to allow all the other threads to finish.= */ > + for (int i =3D 1; i < NUM_THREADS; i++) > + sem_post (&finish_semaphore); > + > + /* Now wait for the remaining threads to complete. */ > + for (int i =3D 1; i < NUM_THREADS; i++) > + pthread_join (threads[i], &retval); > + > + /* Semaphore cleanup. */ > + sem_destroy (&finish_semaphore); > + sem_destroy (&startup_semaphore); > + > + stop_marker (); > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.exp > b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.exp > new file mode 100644 > index 00000000000..787dee3aa8e > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.exp > @@ -0,0 +1,117 @@ > +# Copyright 2022-2023 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 reprocuces bug gdb/28942, performing an inferior function > +# call from a breakpoint condition in a multi-threaded inferior. > +# > +# The important part of this test is that, when the conditional > +# breakpoint is hit, and the condition (which includes an inferior > +# function call) is evaluated, the other threads are running. > + > +standard_testfile > + > +if { [build_executable "failed to prepare" ${binfile} "${srcfile}" \ > + {debug pthreads}] =3D=3D -1 } { > + return > +} > + > +set cond_bp_line [gdb_get_line_number "Conditional breakpoint here"] > +set final_bp_line [gdb_get_line_number "Stop marker"] > + > +# Start GDB based on TARGET_ASYNC and TARGET_NON_STOP, and then runto ma= in. > +proc start_gdb_and_runto_main { target_async target_non_stop } { > + save_vars { ::GDBFLAGS } { > + append ::GDBFLAGS \ > + " -ex \"maint set target-non-stop $target_non_stop\"" > + append ::GDBFLAGS \ > + " -ex \"maintenance set target-async ${target_async}\"" > + > + clean_restart ${::binfile} > + } > + > + if { ![runto_main] } { > + return -1 > + } > + > + return 0 > +} > + > +# Run a test of GDB's conditional breakpoints, where the conditions incl= ude > +# inferior function calls. > +# > +# TARGET_ASYNC and TARGET_NON_STOP are used when starting up GDB. > +# > +# When STOP_AT_COND is true the breakpoint condtion will evaluate to > +# true, and GDB will stop at the breakpoint. Otherwise, the > +# breakpoint condition will evaluate to false and GDB will not stop at > +# the breakpoint. > +proc run_condition_test { stop_at_cond \ > + target_async target_non_stop } { > + if { [start_gdb_and_runto_main $target_async \ > + $target_non_stop] =3D=3D -1 } { > + return > + } > + > + # Setup the conditional breakpoint. > + if { $stop_at_cond } { > + set cond_func "return_true" > + } else { > + set cond_func "return_false" > + } > + gdb_breakpoint \ > + "${::srcfile}:${::cond_bp_line} if (${cond_func} ())" > + set cond_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number for conditional breakpoint"] > + > + # And a breakpoint that we hit when the test is over, this one is > + # not conditional. > + gdb_breakpoint "${::srcfile}:${::final_bp_line}" > + set final_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number for final breakpoint"] > + > + if { $stop_at_cond } { > + # Continue. The first breakpoint we hit should be the conditional > + # breakpoint. The other thread will have hit its breakpoint, but > + # that will have been deferred until the conditional breakpoint is > + # reported. > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + ".*" \ > + "" \ > + "Thread ${::decimal} \"\[^\"\r\n\]+\" hit Breakpoint ${cond_bp_num}, > worker_func \[^\r\n\]+:${::cond_bp_line}" \ > + "${::decimal}\\s+\[^\r\n\]+Conditional breakpoint here\[^\r\n\]+"] \ > + "hit the conditional breakpoint" > + } > + > + # Run to the stop marker. > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + ".*" \ > + "" \ > + "Thread ${::decimal} \"\[^\"\r\n\]+\" hit Breakpoint ${final_bp_nu= m}, > stop_marker \[^\r\n\]+:${::final_bp_line}" \ > + "${::decimal}\\s+\[^\r\n\]+Stop marker\[^\r\n\]+"] \ > + "hit the final breakpoint" > +} > + > +foreach_with_prefix target_async { "on" "off" } { > + foreach_with_prefix target_non_stop { "on" "off" } { > + foreach_with_prefix stop_at_cond { true false } { > + run_condition_test $stop_at_cond \ > + $target_async $target_non_stop > + } > + } > +} > -- > 2.25.4 Intel Deutschland GmbH Registered Address: Am Campeon 10, 85579 Neubiberg, Germany Tel: +49 89 99 8853-0, www.intel.de Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva = Chairperson of the Supervisory Board: Nicole Lau Registered Office: Munich Commercial Register: Amtsgericht Muenchen HRB 186928