From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-io1-xd30.google.com (mail-io1-xd30.google.com [IPv6:2607:f8b0:4864:20::d30]) by sourceware.org (Postfix) with ESMTPS id 24ADB3858C2B for ; Tue, 7 Nov 2023 18:29:35 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 24ADB3858C2B Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=adacore.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=adacore.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 24ADB3858C2B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::d30 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1699381777; cv=none; b=O1OG3zEk4kRWbhRVHL7ooaKhdjJxAIZTIKenYtnu5cMvIiW356UkQbqofcp1Sc+WjyakX1+92LroAyPdTjNqHIkFkI4ne2Ysq3ZKGbsPIOfB1MzLqByO2rdPXtRWT59duNubbLDE7C63VkDuBN/2g+I4TDV0bHnRvzLzwVJwtpY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1699381777; c=relaxed/simple; bh=Tkxw3jsaeCYuosTKBundiiHcAhFwTk5HSWOuZ8CyUf4=; h=DKIM-Signature:From:Date:Subject:MIME-Version:Message-Id:To; b=FwUKLxXS/+Agts5M78pKeBUUHCt4ZqImQ4c4/86HtuAyrLHJDLufQ6vfCTjHoXtZF+PYcGSS6GJkDvwSRoB2rzKUzNfoFVJ4zBaCbI/2A1kq5j5em8fSdGq1LIgM8P5OQEiM3JzQaIUofkIETaeSgzZ4U14MKqVyjnJV/CvnfXA= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-io1-xd30.google.com with SMTP id ca18e2360f4ac-7a68d1698adso155664339f.1 for ; Tue, 07 Nov 2023 10:29:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=adacore.com; s=google; t=1699381774; x=1699986574; darn=sourceware.org; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=sYP+CeYbL7ULW+j6fSPH5QdjZqs4E+p2GpeI3+HozIE=; b=Pv9tt//c9br47+YYaD4atzCp5iF7PObFx1AF64zx+zRl5jqGg2xs7y5i8tnYVM0g+1 GB5a6jCo75ZVGG+u4SpsPPjZgleeOL/h7Y7/vzM2kxcvz5EVyQ0cvix/1n+rVAaa2L9P bI+xIn+VSOuqigo4NY9LVWbkGT6p54Lta4PnM+VgtKTLEwL2/zsZyivbpitTEJLCZPQv jOAOag8ocWI0b0W3LMZHPcYKvCsKqueYnMrd2qF6W43FfwxOHM9OT0Q/h9h0/say20aV iDriuIn5KqlMXz2102fEGbvsFk6YGCQYZQO6p23vHlAOKAQptmmGMBStfq9f1GYgOA99 iGrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699381774; x=1699986574; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=sYP+CeYbL7ULW+j6fSPH5QdjZqs4E+p2GpeI3+HozIE=; b=aU3Kaym3qjDK2wxEbZO/xBComj9lrg1nj0Z/r9o5MLAgIL49bSOYWe64SiEiFiHB7R Uq3iPrcrnWmw2Q6lfTeFrrdPtKO/glS+xKdRviWoQJdj5eOnSS4Y6wYND9NxYgIXGVW4 sMd9Wdp8/bckeo7kpJTSXqyFAZwJc56wVXawkwCYko25tCjE0iI1c4c1xJUpaeNUjnHi NrQVWN51WOKuwpaiBmnUSi3n6Li1hxcWuXe0G20fbDXUT4MBDutyP85nWW5v/XPeyD1A QTUL9kXd0ploGfMPMtKWAnVi8abQ17if7aauLZghbKlIiDD7ARsyYPSlcniDtmeAPtmU MqkQ== X-Gm-Message-State: AOJu0Yw07YKU3TWsRgNeIkj0ataFgdAdZ7E1aen41HpQnjwpi+zGRVrW VTT+2yafVpOUtJM3tl9C6KleEBatjd1/zXE07XjZNQ== X-Google-Smtp-Source: AGHT+IGuxEzzyrmajQL548KY36/2aJy4b//xFcYEuBLiqGQMw1tohPS6A9kUkslMSACSIKffAa7d4w== X-Received: by 2002:a05:6602:1409:b0:79d:1b4e:fb8a with SMTP id t9-20020a056602140900b0079d1b4efb8amr4004748iov.9.1699381774270; Tue, 07 Nov 2023 10:29:34 -0800 (PST) Received: from localhost.localdomain (97-122-77-73.hlrn.qwest.net. [97.122.77.73]) by smtp.gmail.com with ESMTPSA id gs25-20020a0566382d9900b0042b2f0b77aasm2776726jab.95.2023.11.07.10.29.33 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 07 Nov 2023 10:29:33 -0800 (PST) From: Tom Tromey Date: Tue, 07 Nov 2023 11:29:31 -0700 Subject: [PATCH 3/3] Implement the notStopped DAP response MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20231107-dap-not-stopped-v1-3-3d91c935255d@adacore.com> References: <20231107-dap-not-stopped-v1-0-3d91c935255d@adacore.com> In-Reply-To: <20231107-dap-not-stopped-v1-0-3d91c935255d@adacore.com> To: gdb-patches@sourceware.org X-Mailer: b4 0.12.4 X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,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: DAP specifies that a request can fail with the "notStopped" message if the inferior is running but the request requires that it first be stopped. This patch implements this for gdb. Most requests are assumed to require a stopped inferior, and the exceptions are noted by a new 'request' parameter. You may notice that the implementation is a bit racy. I think this is inherent -- unless the client waits for a stop event before sending a request, the request may be processed at any time relative to a stop. https://sourceware.org/bugzilla/show_bug.cgi?id=31037 --- gdb/python/lib/gdb/dap/events.py | 15 +++++++++++++++ gdb/python/lib/gdb/dap/pause.py | 2 +- gdb/python/lib/gdb/dap/server.py | 38 +++++++++++++++++++++++++++++++++++--- gdb/testsuite/gdb.dap/pause.exp | 7 +++++++ 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/gdb/python/lib/gdb/dap/events.py b/gdb/python/lib/gdb/dap/events.py index 09214ec3dc8..bfc3f9ee1dc 100644 --- a/gdb/python/lib/gdb/dap/events.py +++ b/gdb/python/lib/gdb/dap/events.py @@ -21,8 +21,17 @@ from .startup import exec_and_log, in_gdb_thread, log from .modules import is_module, make_module +# True when the inferior is thought to be running, False otherwise. +# This may be accessed from any thread, which can be racy. However, +# this unimportant because this global is only used for the +# 'notStopped' response, which itself is inherently racy. +inferior_running = False + + @in_gdb_thread def _on_exit(event): + global inferior_running + inferior_running = False code = 0 if hasattr(event, "exit_code"): code = event.exit_code @@ -48,6 +57,8 @@ def thread_event(event, reason): @in_gdb_thread def _new_thread(event): + global inferior_running + inferior_running = True thread_event(event, "started") @@ -85,6 +96,8 @@ _suppress_cont = False @in_gdb_thread def _cont(event): + global inferior_running + inferior_running = True global _suppress_cont if _suppress_cont: log("_suppress_cont case") @@ -123,6 +136,8 @@ def exec_and_expect_stop(cmd, reason): @in_gdb_thread def _on_stop(event): + global inferior_running + inferior_running = False log("entering _on_stop: " + repr(event)) global _expected_stop obj = { diff --git a/gdb/python/lib/gdb/dap/pause.py b/gdb/python/lib/gdb/dap/pause.py index d276ab1cb92..3d9b4ae108e 100644 --- a/gdb/python/lib/gdb/dap/pause.py +++ b/gdb/python/lib/gdb/dap/pause.py @@ -17,6 +17,6 @@ from .events import StopKinds, exec_and_expect_stop from .server import request -@request("pause", response=False) +@request("pause", response=False, must_be_stopped=False) def pause(**args): exec_and_expect_stop("interrupt -a", StopKinds.PAUSE) diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py index d2980ad4031..d3b7f75c224 100644 --- a/gdb/python/lib/gdb/dap/server.py +++ b/gdb/python/lib/gdb/dap/server.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import functools import inspect import json import queue @@ -163,7 +164,27 @@ def send_event(event, body=None): _server.send_event(event, body) -def request(name: str, *, response: bool = True, server: bool = False): +# A helper decorator that checks whether the inferior is running. +def _check_not_running(func): + @functools.wraps(func) + def check(*args, **kwargs): + # Import this as late as possible. FIXME. + from .events import inferior_running + + if inferior_running: + raise Exception("notStopped") + return func(*args, **kwargs) + + return check + + +def request( + name: str, + *, + response: bool = True, + server: bool = False, + must_be_stopped: bool = True +): """A decorator for DAP requests. This registers the function as the implementation of the DAP @@ -178,6 +199,11 @@ def request(name: str, *, response: bool = True, server: bool = False): If SERVER is True, the function will be invoked in the DAP thread. When SERVER is True, RESPONSE may not be False. + + If MUST_BE_STOPPED is True (the default), then the request will + fail with the 'notStopped' reason if it is processed while the + inferior is running. When MUST_BE_STOPPED is False, the request + will proceed regardless of the inferior's state. """ # Validate the parameters. @@ -217,6 +243,12 @@ def request(name: str, *, response: bool = True, server: bool = False): cmd = non_sync_call + # If needed, check that the inferior is not running. This + # wrapping is done last, so the check is done first, before + # trying to dispatch the request to another thread. + if must_be_stopped: + cmd = _check_not_running(cmd) + global _commands _commands[name] = cmd return cmd @@ -255,13 +287,13 @@ def initialize(**args): return _capabilities.copy() -@request("terminate") +@request("terminate", must_be_stopped=False) @capability("supportsTerminateRequest") def terminate(**args): exec_and_log("kill") -@request("disconnect", server=True) +@request("disconnect", server=True, must_be_stopped=False) @capability("supportTerminateDebuggee") def disconnect(*, terminateDebuggee: bool = False, **args): if terminateDebuggee: diff --git a/gdb/testsuite/gdb.dap/pause.exp b/gdb/testsuite/gdb.dap/pause.exp index 27955d31526..558ede982ee 100644 --- a/gdb/testsuite/gdb.dap/pause.exp +++ b/gdb/testsuite/gdb.dap/pause.exp @@ -32,6 +32,13 @@ if {[dap_launch $testfile] == ""} { dap_check_request_and_response "start inferior" configurationDone dap_wait_for_event_and_check "inferior started" thread "body reason" started +set resp [lindex [dap_request_and_response evaluate {o expression [s 23]}] \ + 0] +gdb_assert {[dict get $resp success] == "false"} \ + "evaluate failed while inferior executing" +gdb_assert {[dict get $resp message] == "notStopped"} \ + "evaluate issued notStopped" + dap_check_request_and_response pause pause \ {o threadId [i 1]} -- 2.41.0