Skip to content

[lldb-dap] In DAP unit tests, add helpers for loading a CoreFile. #140738

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lldb/tools/lldb-dap/Protocol/ProtocolBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ using Message = std::variant<Request, Response, Event>;
bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
llvm::json::Value toJSON(const Message &);

inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Message &V) {
OS << toJSON(V);
return OS;
}

/// On error (whenever `success` is false), the body can provide more details.
struct ErrorResponseBody {
/// A structured error message.
Expand Down
8 changes: 8 additions & 0 deletions lldb/unittests/DAP/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ add_lldb_unittest(DAPTests
VariablesTest.cpp

LINK_LIBS
liblldb
lldbDAP
lldbUtilityHelpers
LLVMTestingSupport
LINK_COMPONENTS
Support
)

set(test_inputs
linux-x86_64.out.yaml
linux-x86_64.core.yaml
)
add_unittest_inputs(DAPTests "${test_inputs}")
32 changes: 30 additions & 2 deletions lldb/unittests/DAP/Handler/DisconnectTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
#include "Handler/RequestHandler.h"
#include "Protocol/ProtocolBase.h"
#include "TestBase.h"
#include "lldb/API/SBDefines.h"
#include "lldb/lldb-enumerations.h"
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <memory>
#include <optional>
Expand All @@ -23,13 +26,38 @@ using namespace lldb_dap::protocol;

class DisconnectRequestHandlerTest : public DAPTestBase {};

TEST_F(DisconnectRequestHandlerTest, DisconnectingTriggersTerminated) {
TEST_F(DisconnectRequestHandlerTest, DisconnectTriggersTerminated) {
DisconnectRequestHandler handler(*dap);
EXPECT_FALSE(dap->disconnecting);
ASSERT_THAT_ERROR(handler.Run(std::nullopt), Succeeded());
EXPECT_TRUE(dap->disconnecting);
std::vector<Message> messages = DrainOutput();
EXPECT_THAT(messages,
testing::Contains(testing::VariantWith<Event>(testing::FieldsAre(
/*event=*/"terminated", /*body=*/std::nullopt))));
/*event=*/"terminated", /*body=*/testing::_))));
}

TEST_F(DisconnectRequestHandlerTest, DisconnectTriggersTerminateCommands) {
CreateDebugger();

if (!GetDebuggerSupportsTarget("X86"))
GTEST_SKIP() << "Unsupported platform";

LoadCore();

DisconnectRequestHandler handler(*dap);

EXPECT_FALSE(dap->disconnecting);
dap->configuration.terminateCommands = {"?script print(1)",
"script print(2)"};
EXPECT_EQ(dap->target.GetProcess().GetState(), lldb::eStateStopped);
ASSERT_THAT_ERROR(handler.Run(std::nullopt), Succeeded());
EXPECT_TRUE(dap->disconnecting);
std::vector<Message> messages = DrainOutput();
EXPECT_THAT(messages, testing::ElementsAre(
OutputMatcher("Running terminateCommands:\n"),
OutputMatcher("(lldb) script print(2)\n"),
OutputMatcher("2\n"),
testing::VariantWith<Event>(testing::FieldsAre(
/*event=*/"terminated", /*body=*/testing::_))));
}
49 changes: 49 additions & 0 deletions lldb/unittests/DAP/Inputs/linux-x86_64.core.yaml

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions lldb/unittests/DAP/Inputs/linux-x86_64.out.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_X86_64
Entry: 0x400144
ProgramHeaders:
- Type: PT_LOAD
Flags: [ PF_X, PF_R ]
FirstSec: .note.gnu.build-id
LastSec: .eh_frame
VAddr: 0x400000
Align: 0x200000
Offset: 0x0
- Type: PT_NOTE
Flags: [ PF_R ]
FirstSec: .note.gnu.build-id
LastSec: .note.gnu.build-id
VAddr: 0x4000E8
Align: 0x4
Offset: 0xE8
- Type: PT_GNU_STACK
Flags: [ PF_W, PF_R ]
Align: 0x10
Offset: 0x0
Sections:
- Name: .note.gnu.build-id
Type: SHT_NOTE
Flags: [ SHF_ALLOC ]
Address: 0x4000E8
AddressAlign: 0x4
Notes:
- Name: GNU
Desc: 01DF54A6045E657D3F8FFB9CE111878914F8BD6D
Type: NT_PRPSINFO
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x40010C
AddressAlign: 0x1
Content: 554889E548897DE8C645FF62488B45E8C6002F5DC3554889E54883EC2048897DE8488975E0C645FF66488B55E8488B45E04889D7FFD0C9C3554889E54883EC10C645FF5FBE0C014000BF00000000E8C2FFFFFFC9C3
- Name: .eh_frame
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0x400168
AddressAlign: 0x8
Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C00000084FFFFFF1500000000410E108602430D06500C07080000001C0000003C00000079FFFFFF2300000000410E108602430D065E0C07080000001C0000005C0000007CFFFFFF1D00000000410E108602430D06580C0708000000
- Name: .comment
Type: SHT_PROGBITS
Flags: [ SHF_MERGE, SHF_STRINGS ]
AddressAlign: 0x1
EntSize: 0x1
Content: 4743433A20285562756E747520342E382E342D327562756E7475317E31342E30342920342E382E3400
- Name: .debug_info
Type: SHT_PROGBITS
AddressAlign: 0x1
Content: EC00000004000000000008011300000001070000003F0000000C01400000000000550000000000000000000000026261720001010C014000000000001500000000000000019C65000000030E00000001016500000002915804460001036B00000002916F0005086B0000000601065800000007666F6F00010721014000000000002300000000000000019CB8000000030E00000001076500000002915803000000000107C300000002915004460001096B00000002916F0008C30000000965000000000508B80000000A51000000010D44014000000000001D00000000000000019C044600010F6B00000002916F0000
- Name: .debug_abbrev
Type: SHT_PROGBITS
AddressAlign: 0x1
Content: 011101250E130B030E1B0E1101120710170000022E0103083A0B3B0B271911011207401897421901130000030500030E3A0B3B0B49130218000004340003083A0B3B0B491302180000050F000B0B491300000624000B0B3E0B030E0000072E0103083A0B3B0B271911011207401896421901130000081501271901130000090500491300000A2E013F19030E3A0B3B0B2719110112074018964219000000
- Name: .debug_line
Type: SHT_PROGBITS
AddressAlign: 0x1
Content: 3F00000002001D0000000101FB0E0D000101010100000001000001006D61696E2E6300000000000009020C0140000000000013834B7531F34BC931834BE50202000101
Symbols:
- Name: .note.gnu.build-id
Type: STT_SECTION
Section: .note.gnu.build-id
Value: 0x4000E8
- Name: .text
Type: STT_SECTION
Section: .text
Value: 0x40010C
- Name: .eh_frame
Type: STT_SECTION
Section: .eh_frame
Value: 0x400168
- Name: .comment
Type: STT_SECTION
Section: .comment
- Name: .debug_aranges
Type: STT_SECTION
Section: .debug_aranges
- Name: .debug_info
Type: STT_SECTION
Section: .debug_info
- Name: .debug_abbrev
Type: STT_SECTION
Section: .debug_abbrev
- Name: .debug_line
Type: STT_SECTION
Section: .debug_line
- Name: .debug_str
Type: STT_SECTION
Section: .debug_str
- Name: main.c
Type: STT_FILE
Index: SHN_ABS
- Name: bar
Type: STT_FUNC
Section: .text
Value: 0x40010C
Size: 0x15
- Name: foo
Type: STT_FUNC
Section: .text
Value: 0x400121
Size: 0x23
- Type: STT_FILE
Index: SHN_ABS
- Name: _start
Type: STT_FUNC
Section: .text
Binding: STB_GLOBAL
Value: 0x400144
Size: 0x1D
- Name: __bss_start
Section: .eh_frame
Binding: STB_GLOBAL
Value: 0x601000
- Name: _edata
Section: .eh_frame
Binding: STB_GLOBAL
Value: 0x601000
- Name: _end
Section: .eh_frame
Binding: STB_GLOBAL
Value: 0x601000
DWARF:
debug_str:
- boomer
- main.c
- boom
- 'GNU C 4.8.4 -mtune=generic -march=x86-64 -g'
- '/home/labath/test'
- _start
- char
debug_aranges:
- Length: 0x2C
Version: 2
CuOffset: 0x0
AddressSize: 0x8
Descriptors:
- Address: 0x40010C
Length: 0x55
...
63 changes: 63 additions & 0 deletions lldb/unittests/DAP/TestBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@

#include "TestBase.h"
#include "Protocol/ProtocolBase.h"
#include "TestingSupport/TestUtilities.h"
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBStructuredData.h"
#include "lldb/Host/File.h"
#include "lldb/Host/Pipe.h"
#include "lldb/lldb-forward.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <memory>

using namespace llvm;
using namespace lldb;
Expand Down Expand Up @@ -55,6 +62,62 @@ void DAPTestBase::SetUp() {
/*transport=*/*to_dap);
}

void DAPTestBase::TearDown() {
if (core)
ASSERT_THAT_ERROR(core->discard(), Succeeded());
if (binary)
ASSERT_THAT_ERROR(binary->discard(), Succeeded());
}

void DAPTestBase::SetUpTestSuite() {
lldb::SBError error = SBDebugger::InitializeWithErrorHandling();
EXPECT_TRUE(error.Success());
}
void DAPTestBase::TeatUpTestSuite() { SBDebugger::Terminate(); }

bool DAPTestBase::GetDebuggerSupportsTarget(llvm::StringRef platform) {
EXPECT_TRUE(dap->debugger);

lldb::SBStructuredData data = dap->debugger.GetBuildConfiguration()
.GetValueForKey("targets")
.GetValueForKey("value");
for (size_t i = 0; i < data.GetSize(); i++) {
char buf[100] = {0};
size_t size = data.GetItemAtIndex(i).GetStringValue(buf, sizeof(buf));
if (llvm::StringRef(buf, size) == platform)
return true;
}

return false;
}

void DAPTestBase::CreateDebugger() {
dap->debugger = lldb::SBDebugger::Create();
ASSERT_TRUE(dap->debugger);
}

void DAPTestBase::LoadCore() {
ASSERT_TRUE(dap->debugger);
llvm::Expected<lldb_private::TestFile> binary_yaml =
lldb_private::TestFile::fromYamlFile(k_linux_binary);
ASSERT_THAT_EXPECTED(binary_yaml, Succeeded());
llvm::Expected<llvm::sys::fs::TempFile> binary_file =
binary_yaml->writeToTemporaryFile();
ASSERT_THAT_EXPECTED(binary_file, Succeeded());
binary = std::move(*binary_file);
dap->target = dap->debugger.CreateTarget(binary->TmpName.data());
ASSERT_TRUE(dap->target);
llvm::Expected<lldb_private::TestFile> core_yaml =
lldb_private::TestFile::fromYamlFile(k_linux_core);
ASSERT_THAT_EXPECTED(core_yaml, Succeeded());
llvm::Expected<llvm::sys::fs::TempFile> core_file =
core_yaml->writeToTemporaryFile();
ASSERT_THAT_EXPECTED(core_file, Succeeded());
this->core = std::move(*core_file);
SBProcess process = dap->target.LoadCore(this->core->TmpName.data());
ASSERT_TRUE(process);
}

std::vector<Message> DAPTestBase::DrainOutput() {
std::vector<Message> msgs;
output.CloseWriteFileDescriptor();
Expand Down
22 changes: 22 additions & 0 deletions lldb/unittests/DAP/TestBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "Protocol/ProtocolBase.h"
#include "Transport.h"
#include "lldb/Host/Pipe.h"
#include "llvm/ADT/StringRef.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace lldb_dap_tests {
Expand All @@ -33,12 +35,32 @@ class TransportBase : public PipeBase {
void SetUp() override;
};

/// Matches an "output" event.
inline auto OutputMatcher(const llvm::StringRef output,
const llvm::StringRef category = "console") {
return testing::VariantWith<lldb_dap::protocol::Event>(testing::FieldsAre(
/*event=*/"output", /*body=*/testing::Optional<llvm::json::Value>(
llvm::json::Object{{"category", category}, {"output", output}})));
}

/// A base class for tests that interact with a `lldb_dap::DAP` instance.
class DAPTestBase : public TransportBase {
protected:
std::unique_ptr<lldb_dap::DAP> dap;
std::optional<llvm::sys::fs::TempFile> core;
std::optional<llvm::sys::fs::TempFile> binary;

static constexpr llvm::StringLiteral k_linux_binary = "linux-x86_64.out.yaml";
static constexpr llvm::StringLiteral k_linux_core = "linux-x86_64.core.yaml";

static void SetUpTestSuite();
static void TeatUpTestSuite();
void SetUp() override;
void TearDown() override;

bool GetDebuggerSupportsTarget(llvm::StringRef platform);
void CreateDebugger();
void LoadCore();

/// Closes the DAP output pipe and returns the remaining protocol messages in
/// the buffer.
Expand Down
9 changes: 9 additions & 0 deletions lldb/unittests/TestingSupport/TestUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,12 @@ llvm::Expected<TestFile> TestFile::fromYamlFile(const llvm::Twine &Name) {
return llvm::errorCodeToError(BufferOrError.getError());
return fromYaml(BufferOrError.get()->getBuffer());
}

llvm::Expected<llvm::sys::fs::TempFile> TestFile::writeToTemporaryFile() {
llvm::Expected<llvm::sys::fs::TempFile> Temp =
llvm::sys::fs::TempFile::create("temp%%%%%%%%%%%%%%%%");
if (!Temp)
return Temp.takeError();
llvm::raw_fd_ostream(Temp->FD, /*shouldClose=*/false) << Buffer;
return std::move(*Temp);
}
3 changes: 3 additions & 0 deletions lldb/unittests/TestingSupport/TestUtilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include <string>

Expand Down Expand Up @@ -45,6 +46,8 @@ class TestFile {
return ModuleSpec(FileSpec(), UUID(), dataBuffer());
}

llvm::Expected<llvm::sys::fs::TempFile> writeToTemporaryFile();

private:
TestFile(std::string &&Buffer) : Buffer(std::move(Buffer)) {}

Expand Down
Loading