From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id pY57Ez2u5mnZiy0AWB0awg (envelope-from ) for ; Mon, 20 Apr 2026 18:52:45 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=FBITa2LG; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 34C2B1E0C3; Mon, 20 Apr 2026 18:52:45 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id A0A8E1E093 for ; Mon, 20 Apr 2026 18:52:43 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 3911E4AA5192 for ; Mon, 20 Apr 2026 22:52:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3911E4AA5192 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=FBITa2LG 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 378084CCCA30 for ; Mon, 20 Apr 2026 22:52:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 378084CCCA30 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 378084CCCA30 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=1776725531; cv=none; b=KeSTdMDAnjGEu0Dogs26xBC/rHqD/mC6oA46Oj2Z1MoXr9BUzINknjbylNlBe0Efp7Ryv3zew2sVqvUxn/UMdOgANfdDoq4ROfNhZIm7b1nRY6sNmq4uAnjbeBoCEVXM38bit6OQ5NyN3gLi4J5VExyQBbqFXHr755weFQCrhDI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776725531; c=relaxed/simple; bh=5rjlKZl3ITRGaES1bQjMFDuvBrqpV/kIK/gFPWWAUKM=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=YGm2gWhp3LnRD2YDWAAk0J+Z7NKbBZX7/b7VKAqkp2ScnAUz0tP0jvFpP566xzyTJMhnIcy93Eh5F60ugvHyLCBIlpeabfZ/clOakc0o05UqJlQFJ5DPYSwRTHv9dSGRdsTWrcmsrQjlXTf4m6SRnVsmsUDimh2xxdNW5zDr1Fo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 378084CCCA30 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1776725530; 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=ZxUB6pXX7tnZxHPH6oFSHMMHu88YokSwEZtlGzPkK78=; b=FBITa2LGajKfWN+/e5PjEzUjUru4ioKGDvTwGaLQHIUyb1Sd48Ro6mxKcA6QHW91k/kvm5 7+k7vwnlpT8HDEE+rWo6TUy4CNH9+vWJKn8X5SRDDeGVJhzA12R2qpIzvaiNLDtRe1vDs2 qZ2eFhIlBcbVsCze2dAkOffndOBA7CE= Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-605-26wNEhfQOIeJ2kvBIijXfQ-1; Mon, 20 Apr 2026 18:52:09 -0400 X-MC-Unique: 26wNEhfQOIeJ2kvBIijXfQ-1 X-Mimecast-MFC-AGG-ID: 26wNEhfQOIeJ2kvBIijXfQ_1776725528 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-43d780757eeso2044213f8f.1 for ; Mon, 20 Apr 2026 15:52:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776725528; x=1777330328; 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=ZxUB6pXX7tnZxHPH6oFSHMMHu88YokSwEZtlGzPkK78=; b=XlAvo7F5IEWKPtMwm6scY4yry2jGlSRP3CJYZtYPUvUCqTsEOvrYDYoH1fG/mgFQF1 uLFLt55ioloETlI8Gb16VsLnfqZ0nk9hg8KM7bP0AkZF7X7TpwAxh8YflQ3ODQjtlZzq JsWDStAL+9IUHpF5qfaLaa1srfrsoRH5y/eMirkFlSeFSPBfwlCXMk3lBI742RKKm3Fw s2h6qrA3kvX6rmYFF2DwUCU5Xzp2xHik1bdpYMYAsgh0wVcm+u+xkppGg9kWNw5EQEMK MdYSvUlHMP1Mk0yswT0MPldJfXY28bcYIApV52BB8lsWgc/mX+8DsVIFVC+eH9p3E7ES djPg== X-Gm-Message-State: AOJu0Yyh078QSRpG+UNLjLUSX4k0sBHcSDG3A1vEHYuifC0ejVzHQA/n Mw0L7UxZ5XfI0wbr17843Tkd0wyBzfKwjP/tv1l5I4VkdnJdeDQNopyuEW9KvP/kEnuIbq84T6N 3z5QZeVHPlCFjnsQBh20KFa69dV0cIqyTZUnWbWsV+GXiRQ6gMoHD8iherbWs2C3Fm9+0YMd+XG hTtDi0i1uX/dPjNKjglzr+HWAGsCWm0puckErdOoIGpx7Pr/c= X-Gm-Gg: AeBDiet3XgXRBcCbdQFz0ITWKPBpYhesX1mdDf2tjuoNnymW7RTGE2f4is5UvjJCzWH q1967/XWIXYBSALb8CxgyExQXqxNWLiGupTAcbK4JfwIsuB2rgxo1bqwIkbZ0i25ni32XBGv1Gw g1koc+odAE0IYIl5uSs578qcaDUYXCI5/rWG4hREM9oHIvTv8FLWGzPkxzkUZVidrAcJLaI5GnV gXU+DcItZg4hxUwLzsSyS3dgpAGzzAhFLesECiQVoCFsZXH1QYrRgPiNayxV889tvgkOZRuivOZ Ys4Dp0fP5r/Uk2sworTzg6PM1hzBzScym1za2W35je3rMZIqRlNj1KyVjKaBwUlh6iMnpmGt4TT D5k+QK/bWRYadXQ70GG5EvxtGldc= X-Received: by 2002:a05:6000:2d06:b0:43d:210:2b2d with SMTP id ffacd0b85a97d-43fe409fb42mr16428465f8f.31.1776725527349; Mon, 20 Apr 2026 15:52:07 -0700 (PDT) X-Received: by 2002:a05:6000:2d06:b0:43d:210:2b2d with SMTP id ffacd0b85a97d-43fe409fb42mr16428429f8f.31.1776725526558; Mon, 20 Apr 2026 15:52:06 -0700 (PDT) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43fe4e4d5b1sm36146994f8f.30.2026.04.20.15.52.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 Apr 2026 15:52:06 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH] gdb/dap: add support for opening core files Date: Mon, 20 Apr 2026 23:52:03 +0100 Message-Id: X-Mailer: git-send-email 2.25.4 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: uwft3rYhduoKkxOZoSc4o-UVJ33gzmt1kT9R3Bo7mhQ_1776725528 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true 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~public-inbox=simark.ca@sourceware.org This patch adds core file support to GDB's DAP interface. Core files are supported as a GDB specific argument to 'attach', the new argument is 'coreFile', the name of the core file to debug. I think handling core files via attach makes the most sense; attach is for connecting to existing processes, but these targets are (usually) stopped as soon as GDB attaches, and that's what a core file looks like, a target that was running, but is now stopped. It just happens that core file targets are special in that the target cannot be resumed again, nor can the user modify the program state (e.g. write to memory or registers). 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. In order to be compatible with lldb I used the same argument name with the same capitalisation. The new argument is added to the documentation and mentioned in NEWS. I had to make some changes to testsuite/lib/dap-support.exp to support this new feature. There's a new dap_corefile proc to handle setting up the initial connection. This seemed cleaner that overloading dap_attach, even though under the hood it is still an 'attach' request that gets sent. The new test tries to write to memory and registers with the core file target in place, neither of these requests succeed, which is what we want, but the exceptions are logged into the dap log file. The dap_shutdown proc calls dap_check_log_file to check the log for exceptions, and these two exceptions are spotted and trigger a FAIL. To avoid this I've added a new "expected_exception_count" argument for dap_shutdown. Now we check that we see the expected number of exceptions. We don't check for the specific exception types right now, but as the test is already checking that the expected requests fail, I think we're OK. --- gdb/NEWS | 2 + gdb/doc/gdb.texinfo | 10 +- gdb/python/lib/gdb/dap/events.py | 26 ++++ gdb/python/lib/gdb/dap/launch.py | 16 ++- gdb/python/lib/gdb/dap/server.py | 5 +- gdb/testsuite/gdb.dap/corefile.c | 45 +++++++ gdb/testsuite/gdb.dap/corefile.exp | 199 +++++++++++++++++++++++++++++ gdb/testsuite/lib/dap-support.exp | 42 ++++-- 8 files changed, 328 insertions(+), 17 deletions(-) create mode 100644 gdb/testsuite/gdb.dap/corefile.c create mode 100644 gdb/testsuite/gdb.dap/corefile.exp diff --git a/gdb/NEWS b/gdb/NEWS index 6b5c2f6a46a..18f1b6308da 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -155,6 +155,8 @@ maint print psymbols ** The launch and attach requests now accept the adaSourceCharset parameter. + ** The attach request now accepts the coreFile parameter. + ** Constants are now returned in scopes. * Changed remote packets diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 1fc052bc1e6..82306072e8c 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -40371,9 +40371,9 @@ Debugger Adapter Protocol @end table @value{GDBN} defines some additional parameters that can be passed to -the @code{attach} request. Either @code{pid} or @code{target} must be -specified, but if both are specified then @code{target} will be -ignored. +the @code{attach} request. One of @code{pid}, @code{target}, or +@code{coreFile} must be specified. If multiple are specified, they +are checked for in that order, and the first one found is used. @table @code @item pid @@ -40382,6 +40382,10 @@ Debugger Adapter Protocol @item target The target to which @value{GDBN} should connect. This is a string and is passed to the @code{target remote} command. @xref{Connecting}. + +@item coreFile +A string that specifies a core file to use. This corresponds to the +@kbd{core-file} command. @xref{core-file command}. @end table In response to the @code{disassemble} request, DAP allows the client diff --git a/gdb/python/lib/gdb/dap/events.py b/gdb/python/lib/gdb/dap/events.py index 91d75c81610..27e3b5ab8bc 100644 --- a/gdb/python/lib/gdb/dap/events.py +++ b/gdb/python/lib/gdb/dap/events.py @@ -276,6 +276,31 @@ def _on_inferior_call(event): send_event("stopped", obj) +@in_gdb_thread +def _on_corefile_changed(event): + # Ignore events relating to corefile being unloaded. + if event.inferior.corefile is None: + return + + # Corefiles are usually attached via the 'attach' request, which + # sets the global _expected_stop_reason to 'attach'. It is + # because of this that it is safe to forward to _on_stop, as when + # _expected_stop_reason is set _on_stop doesn't read the + # event.details, which EVENT doesn't have. + # + # However, if the user loads a core file via some mechanism other + # than the 'attach' request, e.g. they use the repl to issue a GDB + # 'core-file' command, then when we get here _expected_stop_reason + # will not be set. + # + # So, in either case, set _expected_stop_reason now. + global _expected_stop_reason + _expected_stop_reason = "attach" + + # A corefile was loaded, announce that the inferior has stopped. + _on_stop(event) + + gdb.events.stop.connect(_on_stop) gdb.events.exited.connect(_on_exit) gdb.events.new_thread.connect(_new_thread) @@ -284,3 +309,4 @@ gdb.events.cont.connect(_cont) gdb.events.new_objfile.connect(_new_objfile) gdb.events.free_objfile.connect(_objfile_removed) gdb.events.inferior_call.connect(_on_inferior_call) +gdb.events.corefile_changed.connect(_on_corefile_changed) diff --git a/gdb/python/lib/gdb/dap/launch.py b/gdb/python/lib/gdb/dap/launch.py index 6fde3396ee9..1ad5c693f8c 100644 --- a/gdb/python/lib/gdb/dap/launch.py +++ b/gdb/python/lib/gdb/dap/launch.py @@ -59,12 +59,16 @@ class _LaunchOrAttachDeferredRequest(DeferredRequest): super().reschedule() +# Handle whitespace, quotes, and backslashes here. Exactly what +# to quote depends on libiberty's buildargv and safe-ctype. +def escape_filename(filename): + return re.sub("[ \t\n\r\f\v\\\\'\"]", "\\\\\\g<0>", filename) + + # A wrapper for the 'file' command that correctly quotes its argument. @in_gdb_thread def file_command(program): - # Handle whitespace, quotes, and backslashes here. Exactly what - # to quote depends on libiberty's buildargv and safe-ctype. - program = re.sub("[ \t\n\r\f\v\\\\'\"]", "\\\\\\g<0>", program) + program = escape_filename(program) exec_and_log("file " + program) @@ -136,6 +140,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 +154,14 @@ def attach( cmd = "attach " + str(pid) elif target is not None: cmd = "target remote " + target + elif coreFile is not None: + cmd = "core-file " + escape_filename(coreFile) else: - raise DAPException("attach requires either 'pid' or 'target'") + raise DAPException("attach requires either 'pid', 'target', or 'coreFile'") expect_process("attach") expect_stop("attach") exec_and_log(cmd) + # 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..d94036e03e5 100644 --- a/gdb/python/lib/gdb/dap/server.py +++ b/gdb/python/lib/gdb/dap/server.py @@ -629,7 +629,10 @@ def _disconnect_or_kill(terminate: Optional[bool]): # The default depends on whether the inferior was attached or # launched. terminate = not inf.was_attached - if terminate: + + if inf.corefile is not None: + exec_and_log("core-file") + elif terminate: exec_and_log("kill") elif inf.was_attached: exec_and_log("detach") 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..088cc82d4c8 --- /dev/null +++ b/gdb/testsuite/gdb.dap/corefile.exp @@ -0,0 +1,199 @@ +# 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 . + +# 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 +} + +set other_corefile [standard_output_file "${testfile} with spaces.core"] +remote_exec build "cp \"$corefile\" \"$other_corefile\"" + +# 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] +set reply [lindex [dap_check_request_and_response "fetch all registers" \ + "variables" \ + [format {o variablesReference [i %d] count [i %d]} $num\ + [dict get $reg_scope namedVariables]]] 0] + +# Find the name and value of a register, we'll use this to try setting +# a register below. +set regs [dict get $reply body variables] +gdb_assert {[llength $regs] > 0} "got at least one register" +set reg_name [dict get [lindex $regs 0] name] +set reg_value [dict get [lindex $regs 0] value] + +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" +set expected_exception_count 1 + +# Try setting a register, this should fail as registers are not +# writable for a core file target. We need to write back a different +# register value, so we add one to the current value. This means we +# can only run the test if the current register value is an integer. +if {[string is integer -strict $reg_value]} { + set new_value [expr {$reg_value + 1}] + set obj [dap_request_and_response setExpression \ + {o expression [s \$$reg_name] value [s $new_value]}] + set response [lindex $obj 0] + gdb_assert { [dict get $response success] == "false" } \ + "set register fails" + incr expected_exception_count +} + +dap_shutdown false $expected_exception_count + +# Reconnect to the core file. This time when we shutdown we will +# request that the target be terminated, GDB should still just +# disconnect though as core file targets cannot be killed. +with_test_prefix "reattach" { + 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 + + # Request the target be terminated. This doesn't make sense for + # core file targets (which cannot be killed), but GDB should + # handle this gracefully and just disconnect. + dap_shutdown true +} + +# Test loading a core file with spaces in its name. +with_test_prefix "core file with spaces" { + gdb_exit + + # Test that attaching to a core file works at all. + set attach_id [dap_corefile $other_corefile] + + 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 + + # Use the repl to issue an 'info inferiors' command. + set obj [dap_check_request_and_response "command repl" \ + evaluate {o expression [s "info inferiors"] context [s repl]}] + set response [lindex $obj 0] + set result [dict get $response body result] + set result [string map {\\n \n \\t \t} $result] + verbose -log "Info Inferiors Output:\n$result\n\n" + + # Check that the output contains the header line, the executable + # name, and the core file name. We didn't pass the executable + # name when loading the core file, but GDB should have been able + # to find the executable from the core file. + gdb_assert { [regexp "Num\\s+Description\\s+Connection\\s+Executable" $result] } \ + "info inferiors column headers found" + + gdb_assert { [regexp "[string_to_regexp $binfile]\\s*\n" $result] } \ + "executable name was set during core file load" + + gdb_assert { [regexp "core file [string_to_regexp $other_corefile]\\s*$" $result] } \ + "core file name displayed in info inferiors output" + + dap_shutdown +} + +# Test loading a core file via the repl. +with_test_prefix "load core file via repl" { + gdb_exit + + if {[dap_initialize] == ""} { + return + } + + # Use the repl to issue a 'core-file' command. + set obj [dap_check_request_and_response "command repl" \ + evaluate [format {o expression [s "core-file %s"] context [s repl]} $corefile]] + set response [lindex $obj 0] + set result [dict get $response body result] + set result [string map {\\n \n \\t \t} $result] + verbose -log "Core-File Command Output:\n$result\n\n" + + # By the time we got the response from the 'core-file' command, + # the stopped event, sent when we attach to a core file, should + # already have been seen, check for it now. + set ok false + foreach d [lindex $obj 1] { + if {[dict get $d type] != "event" + || [dict get $d event] != "stopped"} { + continue + } + if {[dict get $d body reason] == "attach"} { + set ok true + break + } + } + gdb_assert { $ok } "saw stopped event" + + dap_shutdown +} diff --git a/gdb/testsuite/lib/dap-support.exp b/gdb/testsuite/lib/dap-support.exp index 54d9178648f..1d9d0d1f40a 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 to. 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. @@ -379,26 +395,33 @@ proc dap_target_remote {target} { return [dap_send_request attach [format {o target [s %s]} $target]] } -# Read the most recent DAP log file and check it for exceptions. -proc dap_check_log_file {} { +# Read the most recent DAP log file and check it for exceptions. We +# expect to see exactly EXPECTED_EXCEPTION_COUNT exceptions in the log. +proc dap_check_log_file { {expected_exception_count 0} } { set fd [open [current_dap_log_file]] set contents [read $fd] close $fd - set ok 1 + set exception_count 0 foreach line [split $contents "\n"] { if {[regexp "^Traceback" $line]} { - set ok 0 - break + incr exception_count + if { $exception_count > $expected_exception_count} { + break + } } } - if {$ok} { + if {$exception_count == $expected_exception_count} { pass "exceptions in log file" } else { verbose -log -- "--- DAP LOG START ---" verbose -log -- $contents verbose -log -- "--- DAP LOG END ---" + if { $expected_exception_count > 0 } { + verbose -log -- [join [list "Expected $expected_exception_count" \ + "exception(s), saw $exception_count"]] + } fail "exceptions in log file" } } @@ -414,8 +437,9 @@ proc dap_check_log_file_re { re } { } # Cleanly shut down gdb. TERMINATE is passed as the terminateDebuggee -# parameter to the request. -proc dap_shutdown {{terminate false}} { +# parameter to the request. The EXPECTED_EXCEPTION_COUNT is the +# number of exceptions that we expect to see in the latest DAP log. +proc dap_shutdown {{terminate false} {expected_exception_count 0}} { dap_check_request_and_response "shutdown" disconnect \ [format {o terminateDebuggee [l %s]} $terminate] @@ -427,7 +451,7 @@ proc dap_shutdown {{terminate false}} { clear_gdb_spawn_id - dap_check_log_file + dap_check_log_file $expected_exception_count } # Search the event list EVENTS for an output event matching the regexp base-commit: 4ecaac39c26eb424a39a7427a524036c5fb85af6 -- 2.25.4