Fix DAP 'disconnect' implementation

Message ID 20251217182239.3663898-1-tromey@adacore.com
State New
Headers
Series Fix DAP 'disconnect' implementation |

Commit Message

Tom Tromey Dec. 17, 2025, 6:22 p.m. UTC
  gdb's implementation of the DAP 'disconnect' request was incorrect in
a few ways.

First, the 'terminateDebuggee' field is optional, and has a special
meaning when not supplied: it should do whatever the default is.

Second, if the inferior was attached, it should detach rather than
terminate by default.

Finally, if the inferior was not started at all, it seems reasonable
for this request to simply succeed silently -- currently it returns
"success: false" with the reason being that the inferior isn't
running.
---
 gdb/python/lib/gdb/dap/server.py              | 22 ++++++++++++++---
 .../gdb.dap/immediate-disconnect.exp          | 24 +++++++++++++++++++
 2 files changed, 43 insertions(+), 3 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dap/immediate-disconnect.exp


base-commit: e607549f2495ed68da314642d5c4c51bdd470933
  

Comments

Andrew Burgess Dec. 22, 2025, 1:32 p.m. UTC | #1
Tom Tromey <tromey@adacore.com> writes:

> gdb's implementation of the DAP 'disconnect' request was incorrect in
> a few ways.
>
> First, the 'terminateDebuggee' field is optional, and has a special
> meaning when not supplied: it should do whatever the default is.
>
> Second, if the inferior was attached, it should detach rather than
> terminate by default.
>
> Finally, if the inferior was not started at all, it seems reasonable
> for this request to simply succeed silently -- currently it returns
> "success: false" with the reason being that the inferior isn't
> running.

LGTM.

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew


> ---
>  gdb/python/lib/gdb/dap/server.py              | 22 ++++++++++++++---
>  .../gdb.dap/immediate-disconnect.exp          | 24 +++++++++++++++++++
>  2 files changed, 43 insertions(+), 3 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.dap/immediate-disconnect.exp
>
> diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py
> index bc56a721e98..64ad247a77a 100644
> --- a/gdb/python/lib/gdb/dap/server.py
> +++ b/gdb/python/lib/gdb/dap/server.py
> @@ -19,6 +19,7 @@ import inspect
>  import json
>  import threading
>  from contextlib import contextmanager
> +from typing import Optional
>  
>  import gdb
>  
> @@ -618,11 +619,26 @@ def terminate(**args):
>      exec_and_log("kill")
>  
>  
> +@in_gdb_thread
> +def _disconnect_or_kill(terminate: Optional[bool]):
> +    inf = gdb.selected_inferior()
> +    if inf.connection is None:
> +        # Nothing to do here.
> +        return
> +    if terminate is None:
> +        # The default depends on whether the inferior was attached or
> +        # launched.
> +        terminate = not inf.was_attached
> +    if terminate:
> +        exec_and_log("kill")
> +    elif inf.was_attached:
> +        exec_and_log("detach")
> +
> +
>  @request("disconnect", on_dap_thread=True, expect_stopped=False)
>  @capability("supportTerminateDebuggee")
> -def disconnect(*, terminateDebuggee: bool = False, **args):
> -    if terminateDebuggee:
> -        send_gdb_with_response(lambda: exec_and_log("kill"))
> +def disconnect(*, terminateDebuggee: Optional[bool] = None, **args):
> +    send_gdb_with_response(lambda: _disconnect_or_kill(terminateDebuggee))
>      _server.shutdown()
>  
>  
> diff --git a/gdb/testsuite/gdb.dap/immediate-disconnect.exp b/gdb/testsuite/gdb.dap/immediate-disconnect.exp
> new file mode 100644
> index 00000000000..529bf74f0bb
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dap/immediate-disconnect.exp
> @@ -0,0 +1,24 @@
> +# Copyright 2025 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 <http://www.gnu.org/licenses/>.
> +
> +load_lib dap-support.exp
> +
> +require allow_dap_tests
> +
> +if {[dap_initialize] == ""} {
> +    return
> +}
> +
> +dap_shutdown true
>
> base-commit: e607549f2495ed68da314642d5c4c51bdd470933
> -- 
> 2.52.0
  

Patch

diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py
index bc56a721e98..64ad247a77a 100644
--- a/gdb/python/lib/gdb/dap/server.py
+++ b/gdb/python/lib/gdb/dap/server.py
@@ -19,6 +19,7 @@  import inspect
 import json
 import threading
 from contextlib import contextmanager
+from typing import Optional
 
 import gdb
 
@@ -618,11 +619,26 @@  def terminate(**args):
     exec_and_log("kill")
 
 
+@in_gdb_thread
+def _disconnect_or_kill(terminate: Optional[bool]):
+    inf = gdb.selected_inferior()
+    if inf.connection is None:
+        # Nothing to do here.
+        return
+    if terminate is None:
+        # The default depends on whether the inferior was attached or
+        # launched.
+        terminate = not inf.was_attached
+    if terminate:
+        exec_and_log("kill")
+    elif inf.was_attached:
+        exec_and_log("detach")
+
+
 @request("disconnect", on_dap_thread=True, expect_stopped=False)
 @capability("supportTerminateDebuggee")
-def disconnect(*, terminateDebuggee: bool = False, **args):
-    if terminateDebuggee:
-        send_gdb_with_response(lambda: exec_and_log("kill"))
+def disconnect(*, terminateDebuggee: Optional[bool] = None, **args):
+    send_gdb_with_response(lambda: _disconnect_or_kill(terminateDebuggee))
     _server.shutdown()
 
 
diff --git a/gdb/testsuite/gdb.dap/immediate-disconnect.exp b/gdb/testsuite/gdb.dap/immediate-disconnect.exp
new file mode 100644
index 00000000000..529bf74f0bb
--- /dev/null
+++ b/gdb/testsuite/gdb.dap/immediate-disconnect.exp
@@ -0,0 +1,24 @@ 
+# Copyright 2025 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 <http://www.gnu.org/licenses/>.
+
+load_lib dap-support.exp
+
+require allow_dap_tests
+
+if {[dap_initialize] == ""} {
+    return
+}
+
+dap_shutdown true