From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) by sourceware.org (Postfix) with ESMTPS id 2115A3858409 for ; Fri, 20 Jan 2023 07:13:25 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2115A3858409 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=1674198805; x=1705734805; h=from:to:cc:subject:date:message-id:references: in-reply-to:mime-version:content-transfer-encoding; bh=ROhNIs7JOD3O6IvlqQzsAFmxPaAxtqf7rX8VOSF76rc=; b=VlGeBh/g2XMeBMi2E/uHtmV5xERBkAv7sdnqCbMkn/puUq0Mskrq7+eZ QRX8Yd5fwzyiqiPvQH66jNcOmktwg27RPvS/UhNXavMhAzg68RBNVVCeA Z9VPQ25sgYLf/Pj6QvusiGJCzeKY9KNG/2cdzBzBpcmSd/d0eVT2k7KI7 2Q2bp/FJ1mpq6pN0Bf1GyvXGjiupokGqhqPZioRPucm8ihJLhASvuuzbi Ig0Dx/Uos/nZV30vCmR5RM/+y/AQVRjerN2pIS+hUXcynzBytkdlWQMWQ uQGBSYZ4IPlnMMLQJmYWb9kAH0FJT5rZ2sm1KwMIYDrG8xO3g5aOV6Yvc w==; X-IronPort-AV: E=McAfee;i="6500,9779,10595"; a="305887729" X-IronPort-AV: E=Sophos;i="5.97,231,1669104000"; d="scan'208";a="305887729" Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Jan 2023 23:13:22 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6500,9779,10595"; a="989316522" X-IronPort-AV: E=Sophos;i="5.97,231,1669104000"; d="scan'208";a="989316522" Received: from fmsmsx603.amr.corp.intel.com ([10.18.126.83]) by fmsmga005.fm.intel.com with ESMTP; 19 Jan 2023 23:13:22 -0800 Received: from fmsmsx603.amr.corp.intel.com (10.18.126.83) by fmsmsx603.amr.corp.intel.com (10.18.126.83) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.16; Thu, 19 Jan 2023 23:13:21 -0800 Received: from FMSEDG603.ED.cps.intel.com (10.1.192.133) by fmsmsx603.amr.corp.intel.com (10.18.126.83) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.16 via Frontend Transport; Thu, 19 Jan 2023 23:13:21 -0800 Received: from NAM11-DM6-obe.outbound.protection.outlook.com (104.47.57.174) by edgegateway.intel.com (192.55.55.68) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.16; Thu, 19 Jan 2023 23:13:21 -0800 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=gfhZyZzGLijIrY7xslQPvlurCX3tbpCsJsxmqCniUJqU5xUI86LPdoDTCdgVGeq6+aUqJ2r4Kz2TxbEEwPI3sgWiqXC2SHf8kAgqmwQrn4RmyKjzDJM+DnOdRdF+e0LDp7/vowCGzs1B3dhW8wsanW7trThrWzb/ZX+VTzAEZGww/LF608e/UhwZxiHhupYfx5CGGP5LC1qCsM9WaO9cYR9reFVNvOUiWSbYqC4tLpbsHBRe5oc/aoZGw6cZHdOqE8YJfrV8mYZQXzsxHquTxxpRWSI5/+mvFqvThNd7Y9yuev8vKFC/YlDySs1Ze0zDPMIy7w7af9/zV0dukflKxg== 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=5zZ2XOP0zFcGve8Z80ZzTb+oNXjGr7LW7emTaXIZOj4=; b=oYNI1b3+Uo3c9qJvK+gs2SALOgTBtcH80nz5IVAo6CNm7C/aZL3zTJT0CFzZy/pJBCR+cPD9yx/Xhn4Dh9HRaZKXXXZyAscyMkyuN8PsqXKU2dW4jAyMsKSboLiKFi6K18eWVAYHN2sFwLGwbOW+aAZ4FI92UQttxxt/4/WKM/h8d/v6vjYVYbv2H92qJnr7rrMnCQCLY+97mU7MGRD3SqLT0XM+kW/lTYmIfHpgjBslZLuq7TS9lTSFsiEc7tQWSZPHIxLOmT+hHiURiSd6MF2s9YgyURjp+7gSn9iBjbPvHLPMUwA0yqAPTzjBZ+K3PfHlBA+3lPSykOExnUN+Cw== 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 PH7PR11MB8009.namprd11.prod.outlook.com (2603:10b6:510:248::18) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6002.13; Fri, 20 Jan 2023 07:13:13 +0000 Received: from DM4PR11MB7303.namprd11.prod.outlook.com ([fe80::dec6:d57d:f767:c5f2]) by DM4PR11MB7303.namprd11.prod.outlook.com ([fe80::dec6:d57d:f767:c5f2%4]) with mapi id 15.20.6002.026; Fri, 20 Jan 2023 07:13:12 +0000 From: "Aktemur, Tankut Baris" To: Andrew Burgess , "gdb-patches@sourceware.org" CC: "Saiapova, Natalia" Subject: RE: [PATCHv2 09/13] gdb: fix b/p conditions with infcalls in multi-threaded inferiors Thread-Topic: [PATCHv2 09/13] gdb: fix b/p conditions with infcalls in multi-threaded inferiors Thread-Index: AQHZK1iNfSDO79wbd0W86vTFy3aBY66llsOw Date: Fri, 20 Jan 2023 07:13:12 +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_|PH7PR11MB8009:EE_ x-ms-office365-filtering-correlation-id: 7b3c3f9a-010e-4b98-9f42-08dafab5ca91 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: JhlPqjO3rvtSIZc5mvPv61Z+YHeYoI7d4+f8bHf+PKmsSY0hcdzbPsABGwFQg2zFKuxoC1iTdTtvmAJleUUPse66mXwag7MPItdjoUAFsZztaX1VE3zHUxfO78CfojmJL2y1guHANUB4WZQr2/k51shEPsi21hxq/zeCsJnX5qzmzi2CWNFJwu73ABH4CENI3id75PHQetFOO1cY0bhivkz4EazVqsucSQvlqO2Il3pvEZTMvhfVNPygsPOa+hbAsBjhs+1pdXKLH5YsbLD1Yh4Ff1fpOZH9Z+msipNhCaeJIsmvduuzyOKuslWLt0iaysPl1J4qpRmcVLsO8wxdVTGSDbyb/sdR902r1AqX3R891gu1vfW7i1D1b1Az+PGPERLD3jNfBvGlnjXR/tqoDZStapEJsP9I6SHVoZ8VM0XVf42F1QE1sm3hfRkB80l4+2gwT4CoSk0e8u2OgmP7XqnSsmaXBwZPUUscq2Ajx+lVbv9QMdl2GBXU3OKLYTRJgyRELKE086YUOkOt200qsNsoc7yWErFlGQ35YM4QeXmL87TyA6OupctX4jDWkJmLz3PmYTp7ZPHw5d5nlZhOeY9eIY8Lij+IYRAj+A5Z9dUZ7Sk/qTXRKbnim0xCUeGYs1dVYXuPh4q6FIsNnOHVggIwwVwZOtpKDv5DUq8fQGAHm14Csz+NZvAYI8+AMvQibOAmKGLMNf6y2qULJ1HLqAEp4VX0vgmXRCDe1Q7G94kWnygG+pV4gNmKgQnKbOCQ 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:(13230022)(366004)(346002)(396003)(39860400002)(136003)(376002)(451199015)(38070700005)(55016003)(66946007)(2906002)(66556008)(52536014)(30864003)(8936002)(66476007)(76116006)(33656002)(5660300002)(82960400001)(122000001)(38100700002)(71200400001)(110136005)(107886003)(7696005)(53546011)(966005)(6506007)(478600001)(316002)(86362001)(83380400001)(41300700001)(8676002)(4326008)(64756008)(186003)(66446008)(26005)(9686003)(2004002)(559001)(579004);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?QMyLc80KdyGS8dnEApZchTtPCya8zoDtnbuBqm2G0RK+bcS218HQk68EUuKg?= =?us-ascii?Q?EUeURN6JElEBPwwZUOgSJgvYCCjK8LuRp0aypWMZbBb6VZgNsbQQPUnpdn9K?= =?us-ascii?Q?6ovXiuWqJ9N/gSgUgI4PIv+B0ySr0ia/aPkElv+GkSs2AKqT6ST6D3j2axQQ?= =?us-ascii?Q?fMt8RT5yMjRYgZ9VhPPq86eRwy9CTkF2XsGe3K5af35e0TZco+WqX7H63kvY?= =?us-ascii?Q?5ColC8/DYqcRwgzQlb0uLniNHNZBsx0P53PkOm1r577THpYLXC3G3ux7uex7?= =?us-ascii?Q?dLIPHsNN2pqFFIFskZcjmL3t5pHZ4hnPFmgRhgA35N2TusBeofppPEE+NfbM?= =?us-ascii?Q?i1+28aCbQ46rzbd75u5fZO7oc4fU4lCSbB5yGAP9jb8S4rIeZnNTZdRgFvAG?= =?us-ascii?Q?99Rs/LKlIvcO1YW17Hsmy5/OvNU9iSyIesxVYVnXa0czZcZB2z4jrl7jzBXy?= =?us-ascii?Q?h5lMRBixiPZqU9Jvf4N2Q1wfPZGl2/N4/Skkr0s/8CyKAiW8PMFmXs6gl5T/?= =?us-ascii?Q?3rHwiLfwtjHCd8G3FyLMkhnuWzzfFltqWPCa57AenNrJ/yuThJA9ToLaImOA?= =?us-ascii?Q?n4sg96w5NBMzssfKrgtPru3yn7/qsWjTkEoUo8o3A9wF9RrBP9HkceXIujN5?= =?us-ascii?Q?mpmgkZ+an4+Y/fot0WCf8sl8H+hmxU5vVY9UzFW0NPhrw2SYnwVmw+MKirTG?= =?us-ascii?Q?tDYSM3ZUDrFgClH7ZZgSBRF9QCCT1L0ONYdgSUcBhHkDZ5XwhDM49kxz2F1M?= =?us-ascii?Q?lgDGsUOiG41xhmGRebnszWdBqphBldV4HFIz31+L++wSjsN5OHq3+AaTZdSv?= =?us-ascii?Q?p0X9E5CuhduJvvdL/9MPReOzYYrmX6ZMdjmURmJKpHxtixsJuDm5lUAPPHDk?= =?us-ascii?Q?qsysqBzwDpn9QS/0DWZq2LfR3FDY+Ea9lMeyIoedqnDoeoGYPW/oiLYyqKCm?= =?us-ascii?Q?q9g61zu8Dzf1NBEz8IEKzvHQgjBKf0ltQYVcxgIlqvb/lB1Wltw18fDKJkxE?= =?us-ascii?Q?v3D4ATQujGZh9mIPr3rOAvfOtQ63olj3SZQtVtXdX8uzEEf2yeZ7hLy98lI9?= =?us-ascii?Q?PXlxxwzGAlyR9dOnA6sxSuf2CyDOZ26491KOTVze+wfu7Tbx9eYKvZhIWcKk?= =?us-ascii?Q?hie+eEX9fxD00RNOdJFWUkYqSpUr/P4Z9DmLAiRQCYZKY0+ETrpJONZZ4Exi?= =?us-ascii?Q?RhqpjkcelkfPanQ0vzw09+CSoEDF4r/U52idA+4j5WaVrRZfFwt6wC7ZiUQ1?= =?us-ascii?Q?mCdiYJ5n2c/fielogVccU/4IhZkZYE4kVf2s1Cxmws/hcVHaVsf9fsmBvLm4?= =?us-ascii?Q?bIB0vEIc5kMwagnALxNFIkqd+OOVEdVwk5HZWqxihWWlb66GOXmIOYnI00HN?= =?us-ascii?Q?7qZhS9lByrs2W2PigoJMPeZCaX/a8JCGbxQF+f5SevJXnKosEZa2ZEzrbZDS?= =?us-ascii?Q?+hKP1tN/Zi+l2+ouxXRZdC5qEFYgy7+G94ujChKPvJCLaSBr1J22Xu2tHthU?= =?us-ascii?Q?QG1X1vZtdMwoSb8NkWd4n1XBd+pYtUzxUOg8B7oA0KO71d10xBJ6Z7yp4xQZ?= =?us-ascii?Q?jseIR+a+jZhqsjsuKqrZ8+3+tiHOAKMRxNW5wb2sduOB2v4NoZpJwzIv3/V3?= =?us-ascii?Q?eg=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: 7b3c3f9a-010e-4b98-9f42-08dafab5ca91 X-MS-Exchange-CrossTenant-originalarrivaltime: 20 Jan 2023 07:13:12.0443 (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: Xxz7nH/mNz4zkm85mTk7dD0PT2wnOrl021/o2rmAjl99e/LrFAfM/8NHSw1suEDHpYzibcFYNXP28f0D+qszhu2r6cAjOo2sEWDCrRDYtLc= X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH7PR11MB8009 X-OriginatorOrg: intel.com Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-11.9 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 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 Wednesday, January 18, 2023 5:18 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). > = > The fix in do_target_wait is because previously, we only ever waited > for the general any-thread, minus_one_ptid, for which matching Is there something missing in this sentence? > against the inferior::pid would always succeed. However, now we might > wait against a specific ptid value, in which case we need to ensure we > only compare the pid part of the ptid. > = > 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, this allows GDB to collect resume requests into a single "this allows" -> "which allows" > 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. Thank you very much for taking it further. > 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 > --- > gdb/breakpoint.c | 2 + > gdb/gdbthread.h | 3 + > gdb/infcall.c | 6 + > gdb/infrun.c | 46 +++- > gdb/infrun.h | 2 +- > .../infcall-from-bp-cond-other-thread-event.c | 135 ++++++++++ > ...nfcall-from-bp-cond-other-thread-event.exp | 175 +++++++++++++ > .../gdb.threads/infcall-from-bp-cond-simple.c | 89 +++++++ > .../infcall-from-bp-cond-simple.exp | 236 ++++++++++++++++++ > .../gdb.threads/infcall-from-bp-cond-single.c | 139 +++++++++++ > .../infcall-from-bp-cond-single.exp | 119 +++++++++ > 11 files changed, 940 insertions(+), 12 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 c4fec7e8e55..0cb4382ba5b 100644 > --- a/gdb/breakpoint.c > +++ b/gdb/breakpoint.c > @@ -5538,6 +5538,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 &ex) > diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h > index 11d69fceab0..b0421bac95f 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 e1b785e437b..8a48909bc2c 100644 > --- a/gdb/infcall.c > +++ b/gdb/infcall.c > @@ -641,6 +641,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 5af8635a1ab..861be6eae2f 100644 > --- a/gdb/infrun.c > +++ b/gdb/infrun.c > @@ -2149,6 +2149,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 > @@ -2861,7 +2869,7 @@ schedlock_applies (struct thread_info *tp) > pending events. */ > = > static void > -maybe_set_commit_resumed_all_targets () > +maybe_set_commit_resumed_all_targets (bool force_p) > { > scoped_restore_current_thread restore_thread; > = > @@ -2890,7 +2898,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", > @@ -2900,7 +2908,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", > @@ -2993,7 +3001,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 > { > @@ -3026,7 +3034,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) > { > @@ -3038,7 +3046,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 (); > } > @@ -3752,10 +3760,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 NULL > - && ptid_t (inf->pid).matches (wait_ptid)); > + && ptid_t (inf->pid).matches (wait_ptid_pid)); > }; > = > /* First see how many matching inferiors we have. */ > @@ -4171,7 +4180,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 fr= om Nit: ... current thread. Otherwise, ... > + 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 (); > @@ -4223,7 +4242,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); > = > @@ -4238,7 +4262,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 43fd1b44f5a..ced2ec5843c 100644 > --- a/gdb/infrun.h > +++ b/gdb/infrun.h > @@ -395,7 +395,7 @@ 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..a6abdeb29a6 > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.c > @@ -0,0 +1,135 @@ > +/* Copyright 2022 Free Software Foundation, Inc. 2022-2023? More instances below. > + > + 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..0c8ef728009 > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.e= xp > @@ -0,0 +1,175 @@ > +# Copyright 2022 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]} { > + fail "run to main" Similar comments that I wrote in previous patches: The other if-statements in this file put spaces around the condition. We don't need to emit an explicit fail. There are instances in the other test files below, too. > + 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 > +# 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 Do we need this? We already do clean_restart. > +} > + > +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..f2a24a52b01 > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.c > @@ -0,0 +1,89 @@ > +/* Copyright 2022 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..8d94d24f9b3 > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp > @@ -0,0 +1,236 @@ > +# Copyright 2022 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. > + > +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]} { > + fail "run to main" > + return -1 > + } > + > + return 0 > +} > + > +# Run a test of GDB's conditional breakpoints, where the conditions incl= ude > +# inferior function calls. > +# > +# CONDITION is combined (with &&) to some additional logic, and 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 Nit: ... evaluated. This ... > + # per thread. > + gdb_test "set \$n_cond_eval =3D 0" > + > + # Setup the conditional breakpoint. > + gdb_breakpoint \ > + "${::srcfile}:${::cond_bp_line} if ((++\$n_cond_eval) ${condition})" IMHO, it would be cleaner if '&&' was done here and the user of this proc did not have to include it in the 'condition' argument. Update: I had written the comment as above, but later I saw you pass a "|| = ..." condition, too. So, I think the comment at the procedure header was mislea= ding and can be revised. > + > + # 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 twice" twice -> three times > + } > +} > + > +# 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" ".*" > +} > + > +# 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..c87f1f2bfc6 > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.c > @@ -0,0 +1,139 @@ > +/* Copyright 2022 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..aa1e94190cd > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.exp > @@ -0,0 +1,119 @@ > +# Copyright 2022 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]} { > + fail "run to main" > + return -1 > + } > + > + return 0 > +} > + > +# Run a test of GDB's conditional breakpoints, where the conditions incl= ude > +# inferior function calls. > +# > +# CONDITION is combined (with &&) to some additional logic, and used as = the > +# breakpoint condition. There is no conjunction with '&&' this time. The comment seems incorrect. > +# > +# 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 { 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 Regards -Baris 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