-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[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
Conversation
This allows us to have a SBTarget and SBProcess for creating unit tests.
@llvm/pr-subscribers-lldb Author: John Harrison (ashgti) ChangesThis allows us to have a SBTarget and SBProcess for creating unit tests. Full diff: https://github.com/llvm/llvm-project/pull/140738.diff 7 Files Affected:
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
index 1cb9cb13dd0da..724da59b50cd2 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
@@ -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.
diff --git a/lldb/unittests/DAP/CMakeLists.txt b/lldb/unittests/DAP/CMakeLists.txt
index cd421401f167b..16a3c2b736ee6 100644
--- a/lldb/unittests/DAP/CMakeLists.txt
+++ b/lldb/unittests/DAP/CMakeLists.txt
@@ -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
+ linux-x86_64.core
+ )
+add_unittest_inputs(DAPTests "${test_inputs}")
diff --git a/lldb/unittests/DAP/Handler/DisconnectTest.cpp b/lldb/unittests/DAP/Handler/DisconnectTest.cpp
index 6f3470239e974..73e0f84f93af3 100644
--- a/lldb/unittests/DAP/Handler/DisconnectTest.cpp
+++ b/lldb/unittests/DAP/Handler/DisconnectTest.cpp
@@ -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>
@@ -23,7 +26,7 @@ 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());
@@ -31,5 +34,30 @@ TEST_F(DisconnectRequestHandlerTest, DisconnectingTriggersTerminated) {
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(DAPTestBase::k_linux_binary, DAPTestBase::k_linux_core);
+
+ 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::_))));
}
diff --git a/lldb/unittests/DAP/Inputs/linux-x86_64.core b/lldb/unittests/DAP/Inputs/linux-x86_64.core
new file mode 100644
index 0000000000000..2675eadd6a7f6
Binary files /dev/null and b/lldb/unittests/DAP/Inputs/linux-x86_64.core differ
diff --git a/lldb/unittests/DAP/Inputs/linux-x86_64.out b/lldb/unittests/DAP/Inputs/linux-x86_64.out
new file mode 100755
index 0000000000000..842402fd519d2
Binary files /dev/null and b/lldb/unittests/DAP/Inputs/linux-x86_64.out differ
diff --git a/lldb/unittests/DAP/TestBase.cpp b/lldb/unittests/DAP/TestBase.cpp
index eb146cb2fa9f4..1c58c351776ec 100644
--- a/lldb/unittests/DAP/TestBase.cpp
+++ b/lldb/unittests/DAP/TestBase.cpp
@@ -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;
@@ -55,6 +62,43 @@ void DAPTestBase::SetUp() {
/*transport=*/*to_dap);
}
+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(llvm::StringRef binary, llvm::StringRef core) {
+ ASSERT_TRUE(dap->debugger);
+ dap->target =
+ dap->debugger.CreateTarget(lldb_private::GetInputFilePath(binary).data());
+ ASSERT_TRUE(dap->target);
+ SBProcess process =
+ dap->target.LoadCore(lldb_private::GetInputFilePath(core).data());
+ ASSERT_TRUE(process);
+}
+
std::vector<Message> DAPTestBase::DrainOutput() {
std::vector<Message> msgs;
output.CloseWriteFileDescriptor();
diff --git a/lldb/unittests/DAP/TestBase.h b/lldb/unittests/DAP/TestBase.h
index c789adf53c225..c6c261fa2b64c 100644
--- a/lldb/unittests/DAP/TestBase.h
+++ b/lldb/unittests/DAP/TestBase.h
@@ -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 {
@@ -33,13 +35,30 @@ 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;
+ static constexpr llvm::StringLiteral k_linux_binary = "linux-x86_64.out";
+ static constexpr llvm::StringLiteral k_linux_core = "linux-x86_64.core";
+
+ static void SetUpTestSuite();
+ static void TeatUpTestSuite();
void SetUp() override;
+ bool GetDebuggerSupportsTarget(llvm::StringRef platform);
+ void CreateDebugger();
+ void LoadCore(llvm::StringRef binary, llvm::StringRef core);
+
/// Closes the DAP output pipe and returns the remaining protocol messages in
/// the buffer.
std::vector<lldb_dap::protocol::Message> DrainOutput();
|
I copied the TestDAP_coreFile.py's linux-x86_64.core and linux-x86_64.out for the inputs. |
lldb/unittests/DAP/CMakeLists.txt
Outdated
linux-x86_64.out | ||
linux-x86_64.core |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we obj2yaml these and programmatically yaml2obj them again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yaml2obj on core files doesn't really work. It should be possible to do that with minidump files though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created yaml dumps of both the core and out files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume Pavel's talking about the memory just being bytes. I'd say the YAML file is marginally better than the binaries. A minidump would be even better but I don't feel like we should block this PR on that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used minidump to make the linux-x86_64.core.yaml file (it starts with --- !minidump
). I just needed to load the existing target + core and use (lldb) process save-core --plugin-name=minidump <path>
to get the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume Pavel's talking about the memory just being bytes.
No, that's actually the best you can get (without inventing something completely new), even with minidumps. With core files you just get nothing because they don't fit yaml2obj's model of elf files. elf2yaml's main use case is (unlinked) object files produced by the compiler, which have only section headers (no program headers). OTOH, core files consist of only program headers, and the tool just doesn't handle that. These days I think it's possible to generate a somewhat realistic core file with yaml2obj, but you have to write the yaml by hand. obj2yaml won't generate it for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL, thanks for the explanation.
✅ With the latest revision this PR passed the C/C++ code formatter. |
This allows us to have a SBTarget and SBProcess for creating unit tests.