From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) by sourceware.org (Postfix) with ESMTPS id 08C803858C83 for ; Tue, 16 May 2023 15:42:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 08C803858C83 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=1684251780; x=1715787780; h=from:to:subject:date:message-id:references:in-reply-to: mime-version:content-transfer-encoding; bh=NIAE11JewGpGsLhNEBG0tHw4CDk+qmrKvOcqyT1c7Dg=; b=NJ5voTGyJjNvMRqWc1V5q963LvcPwsALha8jj1HHcFMT1VSAzVkz2/zQ 1RAz5AfHsGi1WDMxJf1+Zzt2zmqyaYyMUBRw8AuHbqLtj4yPdItbIvtxv L61shT3bfioc0JDLbfyBLdqOJX7xS9K436q+dTlukg2J9xqjKdw0IfO81 99TZCoJ42W9PVLY5LnJiYicE8bfdChZ5Al4oV8bhH6Gv5Hir3yftEYz+y IYb+R5yGYK5WxMHm5920eQRaH4Ein3k1v0uoKqsZBkDUj/bvr6DSmz+j5 QmS1wvNIjfLRWwSKq9r6Xr0JTXW0UwQp48mWxvnpWJxdAPIq+6YlmIGcO A==; X-IronPort-AV: E=McAfee;i="6600,9927,10712"; a="437858067" X-IronPort-AV: E=Sophos;i="5.99,278,1677571200"; d="scan'208";a="437858067" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 16 May 2023 08:42:30 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10712"; a="845729068" X-IronPort-AV: E=Sophos;i="5.99,278,1677571200"; d="scan'208";a="845729068" Received: from orsmsx602.amr.corp.intel.com ([10.22.229.15]) by fmsmga001.fm.intel.com with ESMTP; 16 May 2023 08:42:30 -0700 Received: from orsmsx610.amr.corp.intel.com (10.22.229.23) by ORSMSX602.amr.corp.intel.com (10.22.229.15) 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:42:29 -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:42:29 -0700 Received: from NAM11-CO1-obe.outbound.protection.outlook.com (104.47.56.170) 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:42:29 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=GzA8dlnMWzKLsGKOGhWtijZIfLVBMu/+NLy+m27DzV8DM1G2dpWKMLXLN4CLc99cwzzePExlr7MIex8vfMBdwrbDINqIFMDpl9dM3xGxDNeRj3lYQA9Np6PZoSJWctF/Ya022kz3XpNLA+5qjWTfbBlGvrypJk5aaKaisrvb2CxhzA7E+0VfWZ+cCyE2dkYWBfYAeJfL5LeUwzkKj2q+JRcYlfsaLOjTPKhsClG6dyf3zZIErL1mAwZLVXuRSdD9ty3dprdVWQhTEnqk4tjdBNJKoejYsD/XzEkoBl1zJAo8jS53ON7E8wTNLb2Dhqn6NAKviKMTJSz1WN/Il+ed7Q== 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=L8wKfUbUCN7iTcygO5XFWIj4gTZl7IGOv0ZA1beuGps=; b=QzDnDl75y4tG7mMj+zypPKd1DUqPeAxL1HkIr+D0Kd5JxE5vxVslhYZ/zlEMcTu1LLbu2Z0RqW8xVvwGLK1gF9+H/cdODOaX3diPmOz93NPFfK2biE74l55r/kKxnzYcWg/8t5yhxx7whoA3IxNcTfZUUoHQLLCJ+Bz0lXh7FRlkZHT5LtHysBIxiPMIehIW8S/4lWbao+pcUdoDzxjG9g7BL2clJZD427lgObDE8E/epCbLw4O0oBQIB2BGRQm/UpJH8wuNVSUOWAVwzsjNOi7ayvsLG1pqubBQtdilq3KKZ4CJKhQVagJspU6y6iI6HSdUeD7UF7WZx91kH6pQ3A== 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 SJ1PR11MB6201.namprd11.prod.outlook.com (2603:10b6:a03:45c::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6387.30; Tue, 16 May 2023 15:42:24 +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:42:24 +0000 From: "Aktemur, Tankut Baris" To: Andrew Burgess , "gdb-patches@sourceware.org" Subject: RE: [PATCHv7 3/6] gdb: add timeouts for inferior function calls Thread-Topic: [PATCHv7 3/6] gdb: add timeouts for inferior function calls Thread-Index: AQHZh2K8sDKB63U74UiumeC7zqo0Ua9dBqYQ Date: Tue, 16 May 2023 15:42:23 +0000 Message-ID: References: <76da2ce247b046aaf197b034392b3b60858e2937.1684178293.git.aburgess@redhat.com> In-Reply-To: <76da2ce247b046aaf197b034392b3b60858e2937.1684178293.git.aburgess@redhat.com> 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_|SJ1PR11MB6201:EE_ x-ms-office365-filtering-correlation-id: e32c41b1-9972-44d8-bfe6-08db562424c7 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: /VnSmqTXqx7KmOZ74pqh+Ty4DihJ9rC0I2cEoPqrA/f/Rl1f3YBU91+WaoSrYIxOgGa4tQeNcdKItzWY03dFeTS1T3lFkKMWxc4WXydDWi0W5BOv9BSRqDcxpw7WU9DTv8SkRnlDay3lVGFEz6Xou9HQdEnFHULEnrS81Gu+gCrdLvwj3zcKExmemuoy6Ak7vqqFMVtDpuu9Y9Fa/zKZpVf246nAGH7GMn6dhU0fs0gnST7NQVvcpYHl32SwAbTP4Fm4R7AM+lPtrJcyArr0sexd92jVySYiGaSvfoaBk3p19jq76xwikT5jJW3QScyuNWaDqxyhv6lpiDRtIVkYFgJm9CVv50AnNuMLJFu/RWfKeiWyrQiq98MTLoGgVVDGKFHOFTSpLviNMSmT1uyFqmnEefQfEZQe+HJ6i4KffesOI9AhQhMECZ8/pxHQ7LL/Xf+6Va1gOBeokJlGmD7YS5RVeZK9QaZe5Yq3Dphoyo3uOk0oxuUPzBk1uBIcqSR92bULWc2RYhtdJxQndwP5EOeUt0j1idoKCs1LBIncSKwg2HOERpbw9ALympOUmvuMCdc0x9m5Mb35MGc+FSleG1dwbE6ntyeswvbWoaU0cbCIa2jy+CS6fejlMbUky18J 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)(39860400002)(396003)(346002)(366004)(376002)(136003)(451199021)(84970400001)(86362001)(122000001)(38070700005)(38100700002)(82960400001)(33656002)(55016003)(5660300002)(71200400001)(316002)(8676002)(9686003)(52536014)(53546011)(186003)(6506007)(8936002)(26005)(41300700001)(2906002)(66446008)(66476007)(7696005)(30864003)(64756008)(66946007)(478600001)(110136005)(966005)(76116006)(66556008)(83380400001)(2004002)(559001)(579004);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?NwTOOSyEwXIR80ePrhewMNf5g5SfKXCFf7RuafZdIo3BN+JDjmMPvZk2TfBE?= =?us-ascii?Q?Rg5JMwxnDGtRt0fuS7MMuxuyiioer2BMRoVQam5HRxmFmozRw4adkEUOT8K1?= =?us-ascii?Q?GDWpDnlikypCTgvfsNWinXsLu5J+OmHfHwuUwL+eVu0o4FUB2ZuNQX8LocfW?= =?us-ascii?Q?WHP/R1r0xw/dwjdRXqigzIS7x085Skg6IoD48wmlQm0C8IUMRoZGuGaXK+IN?= =?us-ascii?Q?TwNLvpATXuAI5AxpUfOL8P84zdCWcYvGAmSpcagLl5PdIyboYGBFoi15bqyS?= =?us-ascii?Q?XPL+zOPhh3MpVOtRRNQH/emfonLixTlwAZJJSeZFdReSXmvMbF3Bmg14UwyK?= =?us-ascii?Q?xZGaz0Vwi1XhpV8O464wtlLILgq+3Mt/bcJETorLQAfitTTg2kkON9QpxsRd?= =?us-ascii?Q?H11CvZ4fpCR60lnRMpRO9QALg3jniXb5fjPxezYAXF3dD4gAGR/nOsvEiRY9?= =?us-ascii?Q?D3UAV7TTE8vqcKtoUDCloCfCQzd9pPD1U6GfvEq2FuFSn31KhnkvjxGrzJ+e?= =?us-ascii?Q?7QqdFn8BQUEUmCLhl20F/g5Qhj939rIswLTgLlawIFqNj300Ksk5e59FUNQR?= =?us-ascii?Q?iJk3FMJdysu5F6+nOaSdljU34x7C4sH+SgQ7PGx0dPRfdwKKD2aWplYig4ZF?= =?us-ascii?Q?a24Tq65i6qrZkCZG1kI4WwDSOQaBLQ/JB8wWWDekSDf3E3TaAUpMxOaopxKl?= =?us-ascii?Q?/NWk7qd62yrsdMjehvaPgzQmuXy0slUqK2N74da974nQM+k1qQja02Xej1dR?= =?us-ascii?Q?1jjVKAbXZf/JAMAA/2B3hec1R10g6d9deC5D154Rb7AMxVF+Tn0X876ieo8x?= =?us-ascii?Q?6Mu7vHt4ZMCxksL/STgeDisT5nw5KbLYtFF2vccRfc477NGF5PIdsWL7NAle?= =?us-ascii?Q?+WHpXv4Oso0rdL+HRfSZYdIIuaY7zCFSXS5WBlv5fmn9Cs8QILKESEFX4ZRy?= =?us-ascii?Q?I3osSbM5k9a27xxfan+PuQG0wG6vsZ5Z748D5FVY13mjb8WFIa1LBG0CdGVU?= =?us-ascii?Q?5cTwcN9Ty1j29Ns17n717fYzDU3V9Ywtpwg6+IayLx8FpcCk0CONVwoCone5?= =?us-ascii?Q?wIrcVKvCBxixPJpLwsU++RVgj3GKUeELlgnHHgzx0HsEVh81rhUYbfzSuk9J?= =?us-ascii?Q?cGMFAT4Ifkgl9rtLM8lEFxB2u2RhZO0kl7se+0tms/cQBoTFTV0L7uXCeZu/?= =?us-ascii?Q?ARz2KxJrha4x7nnu/yoqyQtrPDBqQZF8Ow+Mxjnb3ON5fMvE101assqd9xpB?= =?us-ascii?Q?tXZG9mkk1bYD+3ckNCrpkMdTeDvKzmqTCHCBcEZOjO+epirDP2xg3n2AA8T4?= =?us-ascii?Q?b9EGmvlZ0ftBJ7hkQZoaMtM2uRJbz8//WwHl8RNN+lXW3Z0jiH0KmxIkCUEW?= =?us-ascii?Q?rgds9VwveOE+UzaMEj2+rDNMiJvJIwmoYnap1aM1CyL6wYgKLNao3LNlE6lI?= =?us-ascii?Q?dk/nOq8PffkWecuCX7v6Wj7L/FHIzfQ3gKyqUxItrmLa+XCHBtxfBawCDRWi?= =?us-ascii?Q?EFXmvndw9Ge28zI44ZWhryS1Cj6cAFKLaCO6Gd9xpktpxU7nN3a+abZVKQus?= =?us-ascii?Q?RNhpdvPVZ1UuGWsZXpr97z3s3FyOJwSxv/xJzg1xtZrhDRl6f0v8pYepIyfe?= =?us-ascii?Q?6w=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: e32c41b1-9972-44d8-bfe6-08db562424c7 X-MS-Exchange-CrossTenant-originalarrivaltime: 16 May 2023 15:42:23.8495 (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: 6vmdvNJ1/aJeS+6za2XmDlb8aviwZt5f/x6CcC9XfpEH5JN66NaftqG6QkzbYKBsCfqxhSz/Ju0RD4EPspDdxfLSbVkC7q3KtyytnnRsJAg= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SJ1PR11MB6201 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,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: > Eli already approved the docs part: > https://sourceware.org/pipermail/gdb-patches/2023-January/196462.html > = > --- > = > In the previous commits I have been working on improving inferior > function call support. One thing that worries me about using inferior > function calls from a conditional breakpoint is: what happens if the > inferior function call fails? > = > If the failure is obvious, e.g. the thread performing the call > crashes, or hits a breakpoint, then this case is already well handled, > and the error is reported to the user. > = > But what if the thread performing the inferior call just deadlocks? > If the user made the call from a 'print' or 'call' command, then the > user might have some expectation of when the function call should > complete, and, when this time limit is exceeded, the user > will (hopefully) interrupt GDB and regain control of the debug > session. > = > But, when the inferior function call is from a breakpoint condition it > is much harder to understand that GDB is deadlocked within an inferior > call. Maybe the breakpoint hasn't been hit yet? Or maybe the > condition was always false? Or maybe GDB is deadlocked in an inferior > call? The only way to know for sure is to periodically interrupt GDB, Did you mean "interrupt the inferior"? > check on all the threads, and then continue. > = > Additionally, the focus of the previous commit was inferior function > calls, from a conditional breakpoint, in a multi-threaded inferior. > This opens up a whole new set of potential failure conditions. For > example, what if the function called relies on interaction with some > other thread, and the other thread crashes? Or hits a breakpoint? > Given how inferior function calls work (in a synchronous manner), a > stop event in some other thread is going to be ignored while the > inferior function call is being executed as part of a breakpoint > condition, and this means that GDB could get stuck waiting for the > original condition thread, which will now never complete. > = > In this commit I propose a solution to this problem. A timeout. For > targets that support async-mode we can install an event-loop timer > before starting the inferior function call. When the timer expires we > will stop the thread performing the inferior function call. With this > mechanism in place a user can be sure that any inferior call they make > will either complete, or timeout eventually. > = > Adding a timer like this is obviously a change in behaviour for the > more common 'call' and 'print' uses of inferior function calls, so, in > this patch, I propose having two different timers. One I call the > 'direct-call-timeout', which is used for 'call' and 'print' commands. > This timeout is by default set to unlimited, which, not surprisingly, > means there is no timeout in place. > = > A second timer, which I've called 'indirect-call-timeout', is used for > inferior function calls from breakpoint conditions. This timeout has > a default value of 30 seconds. This is a reasonably long time to > wait, and hopefully should be enough in most cases to allow the > inferior call to complete. An inferior call that takes more than 30 > seconds, which is installed on a breakpoint condition is really going > to slow down the debug session, so hopefully this is not a common use > case. > = > The user is, of course, free to reduce, or increase the timeout value, > and can always use Ctrl-c to interrupt an inferior function call, but > this timeout will ensure that GDB will stop at some point. > = > The new commands added by this commit are: > = > set direct-call-timeout SECONDS > show direct-call-timeout > set indirect-call-timeout SECONDS > show indirect-call-timeout > = > These new timeouts do depend on async-mode, so, if async-mode is > disabled (maint set target-async off), or not supported (e.g. target > sim), then the timeout is treated as unlimited (that is, no timeout is > set). > = > For targets that "fake" non-async mode, e.g. Linux native, where > non-async mode is really just async mode, but then we park the target > in a sissuspend, we could easily fix things so that the timeouts still > work, however, for targets that really are not async aware, like the > simulator, fixing things so that timeouts work correctly would be a > much bigger task - that effort would be better spent just making the > target async-aware. And so, I'm happy for now that this feature will > only work on async targets. > = > The two new show commands will display slightly different text if the > current target is a non-async target, which should allow users to > understand what's going on. > = > There's a somewhat random test adjustment needed in gdb.base/help.exp, > the test uses a regexp with the apropos command, and expects to find a > single result. Turns out the new settings I added also matched the > regexp, which broke the test. I've updated the regexp a little to > exclude my new settings. > = > In infcall.c you'll notice the thread_info::stop_requested flag being > set when a timeout occurs. This flag setting is not required as part > of this commit, but will be needed in a later commit. However, it > seemed like setting this flag fitted better with this commit, which is > why the change is added here. > --- > gdb/NEWS | 18 ++ > gdb/doc/gdb.texinfo | 66 ++++++ > gdb/infcall.c | 221 +++++++++++++++++- > gdb/testsuite/gdb.base/help.exp | 2 +- > gdb/testsuite/gdb.base/infcall-timeout.c | 36 +++ > gdb/testsuite/gdb.base/infcall-timeout.exp | 82 +++++++ > .../infcall-from-bp-cond-timeout.c | 169 ++++++++++++++ > .../infcall-from-bp-cond-timeout.exp | 156 +++++++++++++ > 8 files changed, 745 insertions(+), 5 deletions(-) > create mode 100644 gdb/testsuite/gdb.base/infcall-timeout.c > create mode 100644 gdb/testsuite/gdb.base/infcall-timeout.exp > create mode 100644 gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeou= t.c > create mode 100644 gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeou= t.exp > = > diff --git a/gdb/NEWS b/gdb/NEWS > index 6aa0d5171f2..216c3a95d09 100644 > --- a/gdb/NEWS > +++ b/gdb/NEWS > @@ -100,6 +100,24 @@ info main > $2 =3D 1 > (gdb) break func if $_shell("some command") =3D=3D 0 > = > +set direct-call-timeout SECONDS > +show direct-call-timeout > +set indirect-call-timeout SECONDS > +show indirect-call-timeout > + These new settings can be used to limit how long GDB will wait for > + an inferior function call to complete. The direct timeout is used > + for inferior function calls from e.g. 'call' and 'print' commands, > + while the indirect timeout is used for inferior function calls from > + within a conditional breakpoint expression. > + > + The default for the direct timeout is unlimited, while the default > + for the indirect timeout is 30 seconds. > + > + These timeouts will only have an effect for targets that are > + operating in async mode. For non-async targets the timeouts are > + ignored, GDB will wait indefinitely for an inferior function to > + complete, unless interrupted by the user using Ctrl-C. > + > * MI changes > = > ** mi now reports 'no-history' as a stop reason when hitting the end of = the > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo > index 531147f6e6b..54668d812cb 100644 > --- a/gdb/doc/gdb.texinfo > +++ b/gdb/doc/gdb.texinfo > @@ -20903,6 +20903,72 @@ > @code{step}, etc). In this case, when the inferior finally returns to > the dummy-frame, @value{GDBN} will once again halt the inferior. > = > +On targets that support asynchronous execution (@pxref{Background > +Execution}) @value{GDBN} can place a timeout on any functions called > +from @value{GDBN}. If the timeout expires and the function call is > +still ongoing, then @value{GDBN} will interrupt the program. > + > +For targets that don't support asynchronous execution > +(@pxref{Background Execution}) then timeouts for functions called from > +@value{GDBN} are not supported, the timeout settings described below > +will be treated as @code{unlimited}, meaning @value{GDBN} will wait > +indefinitely for function call to complete, unless interrupted by the > +user using @kbd{Ctrl-C}. > + > +@table @code > +@item set direct-call-timeout @var{seconds} > +@kindex set direct-call-timeout > +@cindex timeout for called functions > +Set the timeout used when calling functions in the program to > +@var{seconds}, which should be an integer greater than zero, or the > +special value @code{unlimited}, which indicates no timeout should be > +used. The default for this setting is @code{unlimited}. > + > +This setting is used when the user calls a function directly from the > +command prompt, for example with a @code{call} or @code{print} > +command. > + > +This setting only works for targets that support asynchronous > +execution (@pxref{Background Execution}), for any other target the > +setting is treated as @code{unlimited}. > + > +@item show direct-call-timeout > +@kindex show direct-call-timeout > +@cindex timeout for called functions > +Show the timeout used when calling functions in the program with a > +@code{call} or @code{print} command. > +@end table > + > +It is also possible to call functions within the program from the > +condition of a conditional breakpoint (@pxref{Conditions, ,Break > +Conditions}). A different setting controls the timeout used for > +function calls made from a breakpoint condition. > + > +@table @code > +@item set indirect-call-timeout @var{seconds} > +@kindex set indirect-call-timeout > +@cindex timeout for called functions > +Set the timeout used when calling functions in the program from a > +breakpoint or watchpoint condition to @var{seconds}, which should be > +an integer greater than zero, or the special value @code{unlimited}, > +which indicates no timeout should be used. The default for this > +setting is @code{30} seconds. > + > +This setting only works for targets that support asynchronous > +execution (@pxref{Background Execution}), for any other target the > +setting is treated as @code{unlimited}. > + > +If a function called from a breakpoint or watchpoint condition times > +out, then @value{GDBN} will stop at the point where the timeout > +occurred. The breakpoint condition evaluation will be abandoned. > + > +@item show indirect-call-timeout > +@kindex show indirect-call-timeout > +@cindex timeout for called functions > +Show the timeout used when calling functions in the program from a > +breakpoint or watchpoint condition. > +@end table > + > @subsection Calling functions with no debug info > = > @cindex no debug info functions > diff --git a/gdb/infcall.c b/gdb/infcall.c > index 49c88add394..dea7dc83062 100644 > --- a/gdb/infcall.c > +++ b/gdb/infcall.c > @@ -96,6 +96,53 @@ show_may_call_functions_p (struct ui_file *file, int f= rom_tty, > value); > } > = > +/* A timeout (in seconds) for direct inferior calls. A direct inferior > + call is one the user triggers from the prompt, e.g. with a 'call' or > + 'print' command. Compare with the definition of indirect calls below= . */ > + > +static unsigned int direct_call_timeout =3D UINT_MAX; > + > +/* Implement 'show direct-call-timeout'. */ > + > +static void > +show_direct_call_timeout (struct ui_file *file, int from_tty, > + struct cmd_list_element *c, const char *value) > +{ > + if (target_has_execution () && !target_can_async_p ()) > + gdb_printf (file, _("Current target does not support async mode, tim= eout " > + "for direct inferior calls is \"unlimited\".\n")); > + else if (direct_call_timeout =3D=3D UINT_MAX) > + gdb_printf (file, _("Timeout for direct inferior function calls " > + "is \"unlimited\".\n")); > + else > + gdb_printf (file, _("Timeout for direct inferior function calls " > + "is \"%s seconds\".\n"), value); > +} > + > +/* A timeout (in seconds) for indirect inferior calls. An indirect infe= rior > + call is one that originates from within GDB, for example, when > + evaluating an expression for a conditional breakpoint. Compare with > + the definition of direct calls above. */ > + > +static unsigned int indirect_call_timeout =3D 30; > + > +/* Implement 'show indirect-call-timeout'. */ > + > +static void > +show_indirect_call_timeout (struct ui_file *file, int from_tty, > + struct cmd_list_element *c, const char *value) > +{ > + if (target_has_execution () && !target_can_async_p ()) > + gdb_printf (file, _("Current target does not support async mode, tim= eout " > + "for indirect inferior calls is \"unlimited\".\n")); > + else if (indirect_call_timeout =3D=3D UINT_MAX) > + gdb_printf (file, _("Timeout for indirect inferior function calls " > + "is \"unlimited\".\n")); > + else > + gdb_printf (file, _("Timeout for indirect inferior function calls " > + "is \"%s seconds\".\n"), value); > +} > + > /* How you should pass arguments to a function depends on whether it > was defined in K&R style or prototype style. If you define a > function using the K&R syntax that takes a `float' argument, then > @@ -590,6 +637,86 @@ call_thread_fsm::should_notify_stop () > return true; > } > = > +/* A class to control creation of a timer that will interrupt a thread > + during an inferior call. */ > +struct infcall_timer_controller > +{ > + /* Setup an event-loop timer that will interrupt PTID if the inferior > + call takes too long. DIRECT_CALL_P is true when this inferior call= is > + a result of the user using a 'print' or 'call' command, and false w= hen > + this inferior call is a result of e.g. a conditional breakpoint > + expression, this is used to select which timeout to use. */ > + infcall_timer_controller (thread_info *thr, bool direct_call_p) > + : m_thread (thr) > + { > + unsigned int timeout > + =3D direct_call_p ? direct_call_timeout : indirect_call_timeout; > + if (timeout < UINT_MAX && target_can_async_p ()) > + { > + int ms =3D timeout * 1000; > + int id =3D create_timer (ms, infcall_timer_controller::timed_out, this); > + m_timer_id.emplace (id); > + infcall_debug_printf ("Setting up infcall timeout timer for " > + "ptid %s: %d milliseconds", > + m_thread->ptid.to_string ().c_str (), ms); > + } > + } > + > + /* Destructor. Ensure that the timer is removed from the event loop. = */ > + ~infcall_timer_controller () > + { > + /* If the timer has already triggered, then it will have already been > + deleted from the event loop. If the timer has not triggered, then > + delete it now. */ > + if (m_timer_id.has_value () && !m_triggered) > + delete_timer (*m_timer_id); > + > + /* Just for clarity, discard the timer id now. */ > + m_timer_id.reset (); > + } > + > + /* Return true if there was a timer in place, and the timer triggered, > + otherwise, return false. */ > + bool triggered_p () > + { > + gdb_assert (!m_triggered || m_timer_id.has_value ()); > + return m_triggered; > + } > + > +private: > + /* The thread we should interrupt. */ > + thread_info *m_thread; > + > + /* Set true when the timer is triggered. */ > + bool m_triggered =3D false; > + > + /* Given a value when a timer is in place. */ > + gdb::optional m_timer_id; > + > + /* Callback for the timer, forwards to ::trigger below. */ > + static void > + timed_out (gdb_client_data context) > + { > + infcall_timer_controller *ctrl > + =3D static_cast (context); > + ctrl->trigger (); > + } > + > + /* Called when the timer goes off. Stop thread m_thread. */ > + void > + trigger () > + { > + m_triggered =3D true; > + > + scoped_disable_commit_resumed disable_commit_resumed ("infcall timeo= ut"); > + > + infcall_debug_printf ("Stopping thread %s", > + m_thread->ptid.to_string ().c_str ()); > + target_stop (m_thread->ptid); > + m_thread->stop_requested =3D true; > + } > +}; > + > /* Subroutine of call_function_by_hand to simplify it. > Start up the inferior and wait for it to stop. > Return the exception if there's an error, or an exception with > @@ -600,13 +727,15 @@ call_thread_fsm::should_notify_stop () > = > static struct gdb_exception > run_inferior_call (std::unique_ptr sm, > - struct thread_info *call_thread, CORE_ADDR real_pc) > + struct thread_info *call_thread, CORE_ADDR real_pc, > + bool *timed_out_p) > { > INFCALL_SCOPED_DEBUG_ENTER_EXIT; > = > struct gdb_exception caught_error; > ptid_t call_thread_ptid =3D call_thread->ptid; > int was_running =3D call_thread->state =3D=3D THREAD_RUNNING; > + *timed_out_p =3D false; > = > infcall_debug_printf ("call function at %s in thread %s, was_running = =3D %d", > core_addr_to_string (real_pc), > @@ -618,6 +747,16 @@ run_inferior_call (std::unique_ptr = sm, > scoped_restore restore_in_infcall > =3D make_scoped_restore (&call_thread->control.in_infcall, 1); > = > + /* If the thread making the inferior call stops with a time out then t= he "time out" -> "timeout" > + stop_requested flag will be set. However, we don't want changes to > + this flag to leak back to our caller, we might be here to handle an > + inferior call from a breakpoint condition, so leaving this flag set > + would appear that the breakpoint stop was actually a requested stop, > + which is not true, and will cause GDB to print extra messages to the > + output. */ > + scoped_restore restore_stop_requested > + =3D make_scoped_restore (&call_thread->stop_requested, false); > + > clear_proceed_status (0); > = > /* Associate the FSM with the thread after clear_proceed_status > @@ -651,11 +790,23 @@ run_inferior_call (std::unique_ptr= sm, > infrun_debug_show_threads ("non-exited threads after proceed for i= nferior-call", > all_non_exited_threads ()); > = > + /* Setup a timer (if possible, and if the settings allow) to preve= nt > + the inferior call running forever. */ > + bool direct_call_p =3D !call_thread->control.in_cond_eval; > + infcall_timer_controller infcall_timer (call_thread, direct_call_p= ); > + > /* Inferior function calls are always synchronous, even if the > target supports asynchronous execution. */ > wait_sync_command_done (); > = > - infcall_debug_printf ("inferior call completed successfully"); > + /* If the timer triggered then the inferior call failed. */ > + if (infcall_timer.triggered_p ()) > + { > + infcall_debug_printf ("inferior call timed out"); > + *timed_out_p =3D true; > + } > + else > + infcall_debug_printf ("inferior call completed successfully"); > } > catch (gdb_exception &e) > { > @@ -1309,6 +1460,10 @@ call_function_by_hand_dummy (struct value *functio= n, > scoped_restore restore_stopped_by_random_signal > =3D make_scoped_restore (&stopped_by_random_signal, 0); > = > + /* Set to true by the call to run_inferior_call below if the inferior > + call is artificially interrupted by GDB due to taking too long. */ > + bool timed_out_p =3D false; > + > /* - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - > If you're looking to implement asynchronous dummy-frames, then > just below is the place to chop this function in two.. */ > @@ -1335,7 +1490,8 @@ call_function_by_hand_dummy (struct value *function, > struct_addr); > { > std::unique_ptr sm_up (sm); > - e =3D run_inferior_call (std::move (sm_up), call_thread.get (), re= al_pc); > + e =3D run_inferior_call (std::move (sm_up), call_thread.get (), re= al_pc, > + &timed_out_p); > } > = > if (e.reason < 0) > @@ -1487,7 +1643,10 @@ When the function is done executing, GDB will sile= ntly stop."), > std::string name =3D get_function_name (funaddr, name_buf, > sizeof (name_buf)); > = > - if (stopped_by_random_signal) > + /* If the inferior call timed out then it will have been interrupt= ed > + by a signal, but we want to report this differently to the user, > + which is done later in this function. */ > + if (stopped_by_random_signal && !timed_out_p) > { > /* We stopped inside the FUNCTION because of a random > signal. Further execution of the FUNCTION is not > @@ -1531,6 +1690,36 @@ GDB remains in the frame where the signal was rece= ived.\n\ > To change this behavior use \"set unwindonsignal on\".\n\ > Evaluation of the expression containing the function\n\ > (%s) will be abandoned.\n\ > +When the function is done executing, GDB will silently stop."), > + name.c_str ()); > + } > + } > + > + if (timed_out_p) > + { > + /* A timeout results in a signal being sent to the inferior. */ > + gdb_assert (stopped_by_random_signal); > + > + /* Indentation is weird here. A later patch is going to move the > + following block into an if/else, so I'm leaving the indentation > + here to minimise the later patch. > + > + Also, the error message used below refers to 'set > + unwind-on-timeout' which doesn't exist yet. This will be added > + in a later commit, I'm leaving this in for now to minimise the > + churn caused by the commit that adds unwind-on-timeout. */ > + { > + /* The user wants to stay in the frame where we stopped > + (default). Discard inferior status, we're not at the same > + point we started at. */ > + discard_infcall_control_state (inf_status.release ()); > + > + error (_("\ > +The program being debugged timed out while in a function called from GDB= .\n\ > +GDB remains in the frame where the timeout occurred.\n\ > +To change this behavior use \"set unwind-on-timeout on\".\n\ > +Evaluation of the expression containing the function\n\ > +(%s) will be abandoned.\n\ > When the function is done executing, GDB will silently stop."), > name.c_str ()); > } > @@ -1644,6 +1833,30 @@ The default is to unwind the frame."), > show_unwind_on_terminating_exception_p, > &setlist, &showlist); > = > + add_setshow_uinteger_cmd ("direct-call-timeout", no_class, > + &direct_call_timeout, _("\ > +Set the timeout, for direct calls to inferior function calls."), _("\ > +Show the timeout, for direct calls to inferior function calls."), _("\ > +If running on a target that supports, and is running in, async mode\n\ > +then this timeout is used for any inferior function calls triggered\n\ > +directly from the prompt, i.e. from a 'call' or 'print' command. The\n\ > +timeout is specified in seconds."), > + nullptr, > + show_direct_call_timeout, > + &setlist, &showlist); > + > + add_setshow_uinteger_cmd ("indirect-call-timeout", no_class, > + &indirect_call_timeout, _("\ > +Set the timeout, for indirect calls to inferior function calls."), _("\ > +Show the timeout, for indirect calls to inferior function calls."), _("\ > +If running on a target that supports, and is running in, async mode\n\ > +then this timeout is used for any inferior function calls triggered\n\ > +indirectly, i.e. being made as part of a breakpoint, or watchpoint,\n\ > +condition expression. The timeout is specified in seconds."), > + nullptr, > + show_indirect_call_timeout, > + &setlist, &showlist); > + > add_setshow_boolean_cmd > ("infcall", class_maintenance, &debug_infcall, > _("Set inferior call debugging."), > diff --git a/gdb/testsuite/gdb.base/help.exp b/gdb/testsuite/gdb.base/hel= p.exp > index 87919a819ab..504bf90cc15 100644 > --- a/gdb/testsuite/gdb.base/help.exp > +++ b/gdb/testsuite/gdb.base/help.exp > @@ -121,7 +121,7 @@ gdb_test "help info bogus-gdb-command" "Undefined inf= o command: \"bogus- > gdb-comm > gdb_test "help gotcha" "Undefined command: \"gotcha\"\. Try \"help\"\." > = > # Test apropos regex. > -gdb_test "apropos \\\(print\[\^\[ bsiedf\\\".-\]\\\)" "handle -- Specify= how to handle > signals\." > +gdb_test "apropos \\\(print\[\^\[ bsiedf\\\"'.-\]\\\)" "handle -- Specif= y how to handle > signals\." > # Test apropos >1 word string. > gdb_test "apropos handle signal" "handle -- Specify how to handle signal= s\." > # Test apropos apropos. > diff --git a/gdb/testsuite/gdb.base/infcall-timeout.c b/gdb/testsuite/gdb= .base/infcall- > timeout.c > new file mode 100644 > index 00000000000..12774ca2599 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/infcall-timeout.c > @@ -0,0 +1,36 @@ > +/* 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 > + > +/* This function is called from GDB. */ > +int > +function_that_never_returns () > +{ > + while (1) > + sleep (1); > + > + return 0; > +} > + > +int > +main () > +{ > + alarm (300); > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.base/infcall-timeout.exp b/gdb/testsuite/g= db.base/infcall- > timeout.exp > new file mode 100644 > index 00000000000..5e9cdc2fa0e > --- /dev/null > +++ b/gdb/testsuite/gdb.base/infcall-timeout.exp > @@ -0,0 +1,82 @@ > +# 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 GDB's direct-call-timeout setting, that is, ensure that if an > +# inferior function call, invoked from e.g. a 'print' command, takes > +# too long, then GDB can interrupt it, and return control to the user. > + > +standard_testfile > + > +if { [build_executable "failed to prepare" ${binfile} "${srcfile}" \ > + {debug}] =3D=3D -1 } { > + return > +} > + > +# Start GDB according to TARGET_ASYNC and TARGET_NON_STOP, then adjust > +# the direct-call-timeout, and make an inferior function call that > +# will never return. GDB should eventually timeout and stop the > +# inferior. > +proc_with_prefix run_test { 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" runto_main already emits a fail, so no need for this. > + return > + } > + > + gdb_test_no_output "set direct-call-timeout 5" > + > + # When non-stop mode is off we get slightly different output from GD= B. > + if { [gdb_is_remote_or_extended_remote_target] && !$target_non_stop = } { > + set stopped_line_pattern "Program received signal SIGINT, Interrupt\\." > + } else { > + set stopped_line_pattern "Program stopped\\." > + } > + > + gdb_test "print function_that_never_returns ()" \ > + [multi_line \ > + $stopped_line_pattern \ > + ".*" \ > + "The program being debugged timed out while in a function called f= rom GDB\\." \ > + "GDB remains in the frame where the timeout occurred\\." \ > + "To change this behavior use \"set unwind-on-timeout on\"\\." \ > + "Evaluation of the expression containing the function" \ > + "\\(function_that_never_returns\\) will be abandoned\\." \ > + "When the function is done executing, GDB will silently stop\\."] > + > + gdb_test "bt" ".* function_that_never_returns .*.*" > +} > + > +foreach_with_prefix target_async { "on" "off" } { > + > + if { !$target_async } { > + # GDB can't timeout while waiting for a thread if the target > + # runs with async-mode turned off; once the target is running > + # GDB is effectively blocked until the target stops for some > + # reason. > + continue > + } > + > + foreach_with_prefix target_non_stop { "on" "off" } { > + run_test $target_async $target_non_stop > + } > +} > diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.c > b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.c > new file mode 100644 > index 00000000000..4da4245746e > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.c > @@ -0,0 +1,169 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + 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 .= */ > + > +#include > +#include > +#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; > +sem_t thread_1_semaphore; > +sem_t thread_2_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 > +condition_func () > +{ > + /* Let thread 2 run. */ > + if (sem_post (&thread_2_semaphore) !=3D 0) > + abort (); > + > + /* Wait for thread 2 to complete its actions. */ > + if (sem_wait (&thread_1_semaphore) !=3D 0) > + abort (); > + > + return 1; > +} > + > +void > +do_segfault () > +{ > + volatile int *p =3D 0; > + *p =3D 0; /* Segfault here. */ > +} > + > +void * > +worker_func (void *arg) > +{ > + int tid =3D *((int *) arg); > + > + /* Let the main thread know that this worker has started. */ > + if (sem_post (&startup_semaphore) !=3D 0) > + abort (); > + > + 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; > + > + case 1: > + if (sem_wait (&thread_2_semaphore) !=3D 0) > + abort (); > + do_segfault (); > + if (sem_post (&thread_1_semaphore) !=3D 0) > + abort (); > + > + /* Fall through. */ > + default: > + /* Wait until we are allowed to finish. */ > + if (sem_wait (&finish_semaphore) !=3D 0) > + abort (); > + break; > + } > +} > + > +void > +stop_marker () > +{ > + global_var =3D 99; /* Stop marker. */ > +} > + > +/* The main program entry point. */ > + > +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 (); > + if (sem_init (&thread_1_semaphore, 0, 0) !=3D 0) > + abort (); > + if (sem_init (&thread_2_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 to start. */ > + for (int i =3D 0; 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); > + sem_destroy (&thread_1_semaphore); > + sem_destroy (&thread_2_semaphore); > + > + stop_marker (); > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp > b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp > new file mode 100644 > index 00000000000..4159288a39c > --- /dev/null > +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp > @@ -0,0 +1,156 @@ > +# 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 . > + > +# Tests inferior calls executed from a breakpoint condition in > +# a multi-threaded program. > +# > +# This test has the inferior function call timeout, and checks how GDB > +# handles this situation. > + > +standard_testfile > + > +if { [build_executable "failed to prepare" ${binfile} "${srcfile}" \ > + {debug pthreads}] } { > + return > +} > + > +set cond_bp_line [gdb_get_line_number "Conditional breakpoint here"] > +set final_bp_line [gdb_get_line_number "Stop marker"] > +set segfault_line [gdb_get_line_number "Segfault here"] > + > +# Setup GDB based on TARGET_ASYNC and TARGET_NON_STOP. Setup some > +# breakpoints in the inferior, one of which has an inferior call > +# within its condition. > +# > +# Continue GDB, the breakpoint with inferior call will be hit, but the > +# inferior call will never return. We expect GDB to timeout. > +# > +# The reason that the inferior call never completes is that a second > +# thread, on which the inferior call relies, either hits a breakpoint > +# (when OTHER_THREAD_BP is true), or crashes (when OTHER_THREAD_BP is > +# false). > +proc run_test { target_async target_non_stop other_thread_bp } { > + 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" runto_main already emits a fail, so no need for this. > + return > + } > + > + # The default timeout for indirect inferior calls (e.g. inferior > + # calls for conditional breakpoint expressions) is pretty high. > + # We don't want the test to take too long, so reduce this. > + # > + # However, the test relies on a second thread hitting some event > + # (either a breakpoint or signal) before this timeout expires. > + # > + # There is a chance that on a really slow system this might not > + # happen, in which case the test might fail. > + # > + # However, we still allocate 5 seconds, which feels like it should > + # be enough time in most cases, but maybe we need to do something > + # smarter here? Possibly we could have some initial run where the > + # inferior doesn't timeout, but does perform the same interaction > + # between threads, we could time that, and use that as the basis > + # for this timeout. For now though, we just hope 5 seconds is > + # enough. > + gdb_test_no_output "set indirect-call-timeout 5" > + > + gdb_breakpoint \ > + "${::srcfile}:${::cond_bp_line} if (condition_func ())" > + set bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number for conditional breakpoint"] > + > + gdb_breakpoint "${::srcfile}:${::final_bp_line}" > + set final_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number for final breakpoint"] > + > + # The thread performing an inferior call relies on a second > + # thread. The second thread will segfault unless it hits a > + # breakpoint first. In either case the initial thread will not > + # complete its inferior call. > + if { $other_thread_bp } { > + gdb_breakpoint "${::srcfile}:${::segfault_line}" > + set segfault_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ > + "get number for segfault breakpoint"] > + } > + > + # When non-stop mode is off we get slightly different output from GD= B. > + if { [gdb_is_remote_or_extended_remote_target] && !$target_non_stop}= { > + set stopped_line_pattern "Thread ${::decimal} \"\[^\r\n\"\]+\" received= signal > SIGINT, Interrupt\\." > + } else { > + set stopped_line_pattern "Thread ${::decimal} \"\[^\r\n\"\]+\" stopped\= \." > + } > + > + gdb_test "continue" \ > + [multi_line \ > + $stopped_line_pattern \ > + ".*" \ > + "Error in testing condition for breakpoint ${bp_num}:" \ > + "The program being debugged timed out while in a function called f= rom GDB\\." \ > + "GDB remains in the frame where the timeout occurred\\." \ > + "To change this behavior use \"set unwind-on-timeout on\"\\." \ > + "Evaluation of the expression containing the function" \ > + "\\(condition_func\\) will be abandoned\\." \ > + "When the function is done executing, GDB will silently stop\\."] \ > + "expected timeout waiting for inferior call to complete" > + > + # Remember that other thread that either crashed (with a segfault) > + # or hit a breakpoint? Now that the inferior call has timed out, > + # if we try to resume then we should see the pending event from > + # that other thread. > + if { $other_thread_bp } { > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + ".*" \ > + "" \ > + "Thread ${::decimal} \"\[^\"\r\n\]+\" hit Breakpoint ${segfault_bp_nu= m}, > do_segfault \[^\r\n\]+:${::segfault_line}" \ > + "${::decimal}\\s+\[^\r\n\]+Segfault here\[^\r\n\]+"] \ > + "hit the segfault breakpoint" > + } else { > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + ".*" \ > + "Thread ${::decimal} \"infcall-from-bp\" received signal SIGSEGV, > Segmentation fault\\." \ > + "\\\[Switching to Thread \[^\r\n\]+\\\]" \ > + "${::hex} in do_segfault \\(\\) at \[^\r\n\]+:${::segfault_line}" \ > + "${::decimal}\\s+\[^\r\n\]+Segfault here\[^\r\n\]+"] \ > + "hit the segfault" > + } > +} > + > +foreach_with_prefix target_async {"on" "off" } { > + > + if { !$target_async } { > + # GDB can't timeout while waiting for a thread if the target > + # runs with async-mode turned off; once the target is running > + # GDB is effectively blocked until the target stops for some > + # reason. > + continue > + } > + > + foreach_with_prefix target_non_stop {"off" "on"} { > + foreach_with_prefix other_thread_bp { true false } { > + run_test $target_async $target_non_stop $other_thread_bp > + } > + } > +} > -- > 2.25.4 Reviewed-By: Tankut Baris Aktemur Thanks. -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