From patchwork Tue Mar 24 11:21:05 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 132217 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id C05744BB3BE9 for ; Tue, 24 Mar 2026 11:21:51 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C05744BB3BE9 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=MaGQcEkJ X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id BEEB64BAD177 for ; Tue, 24 Mar 2026 11:21:13 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org BEEB64BAD177 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org BEEB64BAD177 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774351273; cv=none; b=c1LboeBDtEoD7L5DHt+1gxhugl8dXn77Dz6LnfK8UPrjWIGjQPPkU0x6dz57A1fYNT1veOmUKOaXOmA1+noe3yjalFRhEkRgM+ORxR+VFhxM5YZrxqr/ckrEPtOcVBc2KjLhLo2Esc/Oa0E4KzLAeqNuFkjwPolpvWGkheZrIJc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774351273; c=relaxed/simple; bh=SjJ60JNLH9U56FrUxeTv8Wv/yZFZL8fCBY/FnCFOEIk=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=M4XOdU5wJUPeFd+8BbLTGqLwF/+5NLwNwZJVBdM0caaExTPKWdS/YlCcpSeXCj9l6oy+gMY9Cdu7mvX3d1a6ejRBIYPCDZgWvrgJFSYxYDp0EW2oEjxlnqSUaRUWIuzG/9iT4cqzURID13YEqJu5yRgH7bWRcfNiYDVwsNdP3rk= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BEEB64BAD177 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774351273; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=IcrEctlB0PT2CrQtBXnpYeuVbIBlpxXvG6zh3Ty58XU=; b=MaGQcEkJiMdjaHQg1uG9YF+DL09i8emMVSb8HVW7VWw/6cEBq0wLFtfHLmGe65wz/EDRAG uda5mCIs2PEzq9pzXPzkW1PQQEQlItDl4Mro30cQwNWckdqBjNycyizUS6R4yaOq3icfhU tJAXjHnPQt+sXHc75P5eVAYeDstUrIc= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-241-3GD9UU1UNEiFNPgXqbU9yg-1; Tue, 24 Mar 2026 07:21:12 -0400 X-MC-Unique: 3GD9UU1UNEiFNPgXqbU9yg-1 X-Mimecast-MFC-AGG-ID: 3GD9UU1UNEiFNPgXqbU9yg_1774351271 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-43b40c22eaeso5145496f8f.2 for ; Tue, 24 Mar 2026 04:21:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774351270; x=1774956070; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=IcrEctlB0PT2CrQtBXnpYeuVbIBlpxXvG6zh3Ty58XU=; b=eJf4DHoX7IZn4+dz4A2KDPgWcez1E3ChUFzm7Ct+n4c0cx5iAvPOZgyS0rX5VaxgRX QwR6b//BzRQ7r2Z18VEbYLdwzvqpwaUY1gPHBXP82bzxgr16SaEB3XhMXIWoMkxf0qh4 cGys6fEp5nVcWOlngtx//2DDHt6LQvCLA+O/MDCeK/9TlgXu7aOkd5AhL8eVx8TXARom ZGRBdv+4TlWNH+C0IuVGEWyWRdwAv+s/xQ2g4U/baCx1iSc8eHWzWfKVF2P6ZqJUlU0i r+gMNHMTetgTwRaPtCS27GFcaQ2BjDYx+BTUaLQcKU5xah0HDelCuHKECOpJoMDjb18D 18HA== X-Gm-Message-State: AOJu0YxiYBUwdMbPm7qEHnbGQEDxVJXdMhp+Lxphvnyy/3/Zgsxsu+WX 5i0hBvG8RrmezF6YwEE2ET1nlPb8vVlJE0hPqaAvObwfzWf1A9t95+htr/NRXioCBr6MTRHhRg2 qFgyheER4917xMiPjMjic2r4MFi7wMi410sYFbVsJ40d90nWMzCvLf4me/7FHdkOkSNYlNURMRS iTFQd9ahZJbpJfwNmPNXWFQ62YpvpAxnKrINXr0rMXatS2yCA= X-Gm-Gg: ATEYQzyXhl2EHTdQXK0WlXj3HvxSuCl5Duvd2ugfiANcIVEEE4OZdEoxK1FkL+GIHqH j6deVtyNCWrmVMOar1tvtiqXRpOejRnUn+H82dotRrv/drq8w7IxGpHHNRhSzsCK4IF/B931OHJ 1VMjojmPKWhfM1oZ84N6a16xovcYVirry4r6KvOo6M5XUYOkbe3O4k0yRiRE5abXb+E11YrmEwE 59F+oKmcGUfkoQfONQNiep5/BE29d4CFjDTv7ANR2Jj7GUJUPtXfEq5iq7J/Y6KlCPhq9Wzm5tn AM3CWawzZULhoy0u+ynEKQcn1qJGuUZlEh18hlp7uG9UkY0yUn9KZPo4g3Rx4d2ycbzM6MITUus Ukad1M4NUf8luUeIc X-Received: by 2002:a05:6000:2889:b0:439:b339:7eed with SMTP id ffacd0b85a97d-43b6424baf9mr26336158f8f.20.1774351270026; Tue, 24 Mar 2026 04:21:10 -0700 (PDT) X-Received: by 2002:a05:6000:2889:b0:439:b339:7eed with SMTP id ffacd0b85a97d-43b6424baf9mr26336031f8f.20.1774351268458; Tue, 24 Mar 2026 04:21:08 -0700 (PDT) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43b64703c7fsm37260877f8f.23.2026.03.24.04.21.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Mar 2026 04:21:08 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [RFC] gdb/dap: add support for opening core files Date: Tue, 24 Mar 2026 11:21:05 +0000 Message-Id: <77d2d959dbadd7112b3dae58679913fabd1d5bbb.1774351223.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: Fl1tEhtBHl6UjP_bAR6wHn8z7dLa4sL6c8XAFGjU4sA_1774351271 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~patchwork=sourceware.org@sourceware.org This is a rough attempt at adding core file support to GDB's DAP interface. I'm pretty new to DAP, so some of what I say here might be wrong. That's why this is RFC: I'd like to know what I've gotten wrong. As such this patch is still a very early draft. Later in this commit message I detail all of the things which I know need cleaning up and adding. What I'm looking for right now is thoughts on the general approach taken here, and a review of my 'todo' list below, is there anything else I need to do? As I understand it, DAP has two ways to start an inferior, 'launch' or 'attach'. Of the two, 'attach' seems closer to how we might think about a core file. The 'attach' is for attaching to an already running inferior, which, upon attach, will be stopped at some point. This is kind-of like a core file. We "attach" (open) a core file, and we have a view into an inferior which is part way through running. The only strange thing is that, for a core file inferior, we cannot resume the inferior, or edit it's memory or registers. My plan then is to make opening a core file, a variation of "attach". To do this I add "coreFile" as a new argument that can be passed to the "attach" request, this triggers the 'core-file' command to open the core file. The todo list: 1. The send_corefile_stop_event function is needed because we don't have any event that triggers when a new core file is loaded. We do already have an observer though (core_file_changed), so I plan to make this visible via the Python API, and then in the DAP code this will emit a DAP stop event. In this way, when we open a new core file the DAP client will see the inferior stop. I did toy with the idea of having GDB emit a stop event when opening a core file; an actual 'normal_stop' observable event, but this seemed like overkill given we already have the core_file_changed observer, and I think that can be made to do everything we need. This will cleanup the changes in launch.py. 2. In server.py, we should probably unload the core file in _disconnect_or_kill rather than just doing nothing. The current bodge leaves the core file loaded, which will, I think, mean that future attempts to reuse this debug session might fail, or act weird, as the core file will already be loaded. 3. In the test I have to override dap_check_log_file because calling dap_shutdown checks the log file for assertions. However, one of my tests is that an attempt to write to the inferior when we are debugging a core file, will fail cleanly. The test passes, but GDB still logs the memory error exception into the log file, which causes dap_shutdown to throw an exception. What I'd really like is a way to tag some exceptions in the log as "expected" so dap_shutdown will still give a fail if there are any unexpected exceptions. But if that's too tricky then I'll settle for adding an extra flag to dap_shutdown to just say don't check for exceptions. Or maybe an "expected" exception count, that might work. Prior to starting this work I took a look at what lldb does. The documentation is not super clear, but this page seems to indicate that lldb might also use the 'coreFile' argument to 'attach': https://lldb.llvm.org/use/lldbdap.html#configuration-settings-reference Like I said, it's not very clear, but search for "coreFile" and you'll see it mentioned, just once, under the "attach" header. I'd already decided that supporting core files through attach made the most sense to me, but I chose 'coreFile' as the name, with that capitalisation, in order to be compatible with lldb. --- gdb/python/lib/gdb/dap/events.py | 13 +++++ gdb/python/lib/gdb/dap/launch.py | 12 +++- gdb/python/lib/gdb/dap/server.py | 2 +- gdb/testsuite/gdb.dap/corefile.c | 45 +++++++++++++++ gdb/testsuite/gdb.dap/corefile.exp | 92 ++++++++++++++++++++++++++++++ gdb/testsuite/lib/dap-support.exp | 16 ++++++ 6 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 gdb/testsuite/gdb.dap/corefile.c create mode 100644 gdb/testsuite/gdb.dap/corefile.exp base-commit: 37af6f1ea8184510a479ccf018e19c844fb9a338 diff --git a/gdb/python/lib/gdb/dap/events.py b/gdb/python/lib/gdb/dap/events.py index 91d75c81610..f6e1e95c238 100644 --- a/gdb/python/lib/gdb/dap/events.py +++ b/gdb/python/lib/gdb/dap/events.py @@ -275,6 +275,19 @@ def _on_inferior_call(event): _expected_pause = False send_event("stopped", obj) +@in_gdb_thread +def send_corefile_stop_event(): + global inferior_running + inferior_running = False + + obj = { + "threadId": gdb.selected_thread().global_num, + "allThreadsStopped": True, + "reason": "attach", + } + global _expected_pause + _expected_pause = False + send_event("stopped", obj) gdb.events.stop.connect(_on_stop) gdb.events.exited.connect(_on_exit) diff --git a/gdb/python/lib/gdb/dap/launch.py b/gdb/python/lib/gdb/dap/launch.py index 6fde3396ee9..6b0cb1d5861 100644 --- a/gdb/python/lib/gdb/dap/launch.py +++ b/gdb/python/lib/gdb/dap/launch.py @@ -20,7 +20,7 @@ from typing import Mapping, Optional, Sequence import gdb -from .events import exec_and_expect_stop, expect_process, expect_stop +from .events import exec_and_expect_stop, expect_process, expect_stop, send_corefile_stop_event from .server import ( DeferredRequest, call_function_later, @@ -136,6 +136,7 @@ def attach( pid: Optional[int] = None, target: Optional[str] = None, adaSourceCharset: Optional[str] = None, + coreFile: Optional[str] = None, **args, ): # The actual attach is handled by this function. @@ -149,11 +150,20 @@ def attach( cmd = "attach " + str(pid) elif target is not None: cmd = "target remote " + target + elif coreFile is not None: + cmd = "core-file " + coreFile else: raise DAPException("attach requires either 'pid' or 'target'") expect_process("attach") expect_stop("attach") exec_and_log(cmd) + + # Report a stop event when connecting to a core file. + # Currently GDB doesn't trigger a stop event when opening a + # new core file, so we need to emit one now. + if coreFile is not None: + send_corefile_stop_event() + # Attach response does not have a body. return None diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py index 01c190c797e..e7d08ce662c 100644 --- a/gdb/python/lib/gdb/dap/server.py +++ b/gdb/python/lib/gdb/dap/server.py @@ -622,7 +622,7 @@ def terminate(**args): @in_gdb_thread def _disconnect_or_kill(terminate: Optional[bool]): inf = gdb.selected_inferior() - if inf.connection is None: + if inf.connection is None or inf.corefile is not None: # Nothing to do here. return if terminate is None: diff --git a/gdb/testsuite/gdb.dap/corefile.c b/gdb/testsuite/gdb.dap/corefile.c new file mode 100644 index 00000000000..ce1d43b5e51 --- /dev/null +++ b/gdb/testsuite/gdb.dap/corefile.c @@ -0,0 +1,45 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2026 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 + +int global_var = 0; + +void +baz (void) +{ + abort (); +} + +void +bar (void) +{ + baz (); +} + +void +foo (void) +{ + bar (); +} + +int +main (void) +{ + foo (); + return 0; +} diff --git a/gdb/testsuite/gdb.dap/corefile.exp b/gdb/testsuite/gdb.dap/corefile.exp new file mode 100644 index 00000000000..83772f7f511 --- /dev/null +++ b/gdb/testsuite/gdb.dap/corefile.exp @@ -0,0 +1,92 @@ +# Copyright 2023-2026 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 using "attach" in DAP for opening a core file. + +require allow_dap_tests + +load_lib dap-support.exp + +standard_testfile + +if {[build_executable ${testfile}.exp $testfile] == -1} { + return +} + +set corefile [core_find $binfile {}] +if {$corefile == ""} { + untested "unable to create or find corefile" + return +} + +# Test that attaching to a core file works at all. +set attach_id [dap_corefile $corefile $binfile] + +dap_check_request_and_response "configurationDone" configurationDone + +dap_check_response "attach response" attach $attach_id + +dap_wait_for_event_and_check "stopped" stopped \ + "body reason" attach + +# Try 'continue', this should fail. +set obj [dap_request_and_response continue \ + {o threadId [i 1]}] +set response [lindex $obj 0] +gdb_assert { [dict get $response success] == "false" } \ + "continue with core file target" + +# Get a backtrace from the core file. +set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \ + {o threadId [i 1]}] 0] +set frame_id [dict get [lindex [dict get $bt body stackFrames] 0] id] + +# Get all scopes for frame 0. Search through scopes to find the +# register scope. +set scopes [dap_check_request_and_response "get scopes" scopes \ + [format {o frameId [i %d]} $frame_id]] +set scopes [dict get [lindex $scopes 0] body scopes] +set reg_scope "" +foreach s $scopes { + if {[dict get $s name] == "Registers"} { + set reg_scope $s + } +} +gdb_assert { $reg_scope ne "" } "found register scope" + +# Read all the registers from the register scope. +set num [dict get $reg_scope variablesReference] +lassign [dap_check_request_and_response "fetch all registers" \ + "variables" \ + [format {o variablesReference [i %d] count [i %d]} $num\ + [dict get $reg_scope namedVariables]]] \ + val events + +set obj [dap_request_and_response setExpression \ + {o expression [s global_var] value [s 23]}] +set response [lindex $obj 0] +gdb_assert { [dict get $response success] == "false" } \ + "set global variable fails" + +# The call to dap_shutdown will check the log file for exceptions. +# The problem is that the attempt to write to a global variable above +# will trigger an exception, which is recorded in the log file, which +# then causes the log file check to fail. +# +# We really need a better way to handle this. +proc empty_dap_check_log_file {} {} +with_override dap_check_log_file empty_dap_check_log_file { + dap_shutdown true +} diff --git a/gdb/testsuite/lib/dap-support.exp b/gdb/testsuite/lib/dap-support.exp index 54d9178648f..926d5891b0b 100644 --- a/gdb/testsuite/lib/dap-support.exp +++ b/gdb/testsuite/lib/dap-support.exp @@ -369,6 +369,22 @@ proc dap_attach {pid {prog ""}} { return [dap_send_request attach $args] } +# Start gdb, send a DAP initialize request, and then an attach request +# specifying COREFILE as the core file to attach too. Returns the +# empty string on failure, or the attach request sequence ID. +proc dap_corefile {corefile {prog ""}} { + if {[dap_initialize "startup - initialize"] == ""} { + return "" + } + + set args [format {o coreFile [s %s]} $corefile] + if {$prog != ""} { + append args [format { program [s %s]} $prog] + } + + return [dap_send_request attach $args] +} + # Start gdb, send a DAP initialize request, and then an attach request # specifying TARGET as the remote target. Returns the empty string on # failure, or the attach request sequence ID.