aboutsummaryrefslogtreecommitdiff
path: root/lldb/unittests
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/unittests')
-rw-r--r--lldb/unittests/Core/CMakeLists.txt2
-rw-r--r--lldb/unittests/Core/MangledTest.cpp10
-rw-r--r--lldb/unittests/Core/ModuleListTest.cpp178
-rw-r--r--lldb/unittests/Core/ModuleTest.cpp125
-rw-r--r--lldb/unittests/DAP/CMakeLists.txt4
-rw-r--r--lldb/unittests/DAP/ClientLauncherTest.cpp71
-rw-r--r--lldb/unittests/DAP/DAPLogTest.cpp53
-rw-r--r--lldb/unittests/DAP/DAPSessionManagerTest.cpp103
-rw-r--r--lldb/unittests/DAP/ProtocolRequestsTest.cpp195
-rw-r--r--lldb/unittests/DAP/ProtocolTypesTest.cpp47
-rw-r--r--lldb/unittests/DAP/TestBase.cpp10
-rw-r--r--lldb/unittests/DAP/TestBase.h3
-rw-r--r--lldb/unittests/Editline/EditlineTest.cpp86
-rw-r--r--lldb/unittests/Expression/CMakeLists.txt1
-rw-r--r--lldb/unittests/Expression/ClangParserTest.cpp4
-rw-r--r--lldb/unittests/Expression/DWARFExpressionTest.cpp738
-rw-r--r--lldb/unittests/Expression/ValueMatcher.cpp205
-rw-r--r--lldb/unittests/Expression/ValueMatcher.h155
-rw-r--r--lldb/unittests/Host/FileTest.cpp22
-rw-r--r--lldb/unittests/Host/common/CMakeLists.txt1
-rw-r--r--lldb/unittests/Host/common/DiagnosticsRenderingTest.cpp (renamed from lldb/unittests/Utility/DiagnosticsRenderingTest.cpp)2
-rw-r--r--lldb/unittests/Interpreter/TestOptions.cpp6
-rw-r--r--lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp14
-rw-r--r--lldb/unittests/Language/ObjC/ObjCLanguageTest.cpp43
-rw-r--r--lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp34
-rw-r--r--lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp15
-rw-r--r--lldb/unittests/Signals/UnixSignalsTest.cpp12
-rw-r--r--lldb/unittests/Symbol/LineTableTest.cpp10
-rw-r--r--lldb/unittests/Symbol/TestClangASTImporter.cpp2
-rw-r--r--lldb/unittests/Symbol/TestTypeSystemClang.cpp91
-rw-r--r--lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp667
-rw-r--r--lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp52
-rw-r--r--lldb/unittests/Target/LocateModuleCallbackTest.cpp20
-rw-r--r--lldb/unittests/Target/MemoryTest.cpp73
-rw-r--r--lldb/unittests/Target/RemoteAwarePlatformTest.cpp17
-rw-r--r--lldb/unittests/TestingSupport/TestUtilities.cpp5
-rw-r--r--lldb/unittests/TestingSupport/TestUtilities.h4
-rw-r--r--lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp230
-rw-r--r--lldb/unittests/Utility/CMakeLists.txt2
-rw-r--r--lldb/unittests/Utility/RegisterValueTest.cpp298
-rw-r--r--lldb/unittests/Utility/VirtualDataExtractorTest.cpp583
41 files changed, 3580 insertions, 613 deletions
diff --git a/lldb/unittests/Core/CMakeLists.txt b/lldb/unittests/Core/CMakeLists.txt
index 6e609a6..d69432d 100644
--- a/lldb/unittests/Core/CMakeLists.txt
+++ b/lldb/unittests/Core/CMakeLists.txt
@@ -7,7 +7,9 @@ add_lldb_unittest(LLDBCoreTests
DumpRegisterInfoTest.cpp
FormatEntityTest.cpp
MangledTest.cpp
+ ModuleListTest.cpp
ModuleSpecTest.cpp
+ ModuleTest.cpp
PluginManagerTest.cpp
ProgressReportTest.cpp
RichManglingContextTest.cpp
diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp
index cbc0c5d..706e678 100644
--- a/lldb/unittests/Core/MangledTest.cpp
+++ b/lldb/unittests/Core/MangledTest.cpp
@@ -636,6 +636,16 @@ DemanglingPartsTestCase g_demangling_parts_test_cases[] = {
/*.basename=*/"operator()",
/*.scope=*/"dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const::$_0::",
/*.qualifiers=*/" const",
+ },
+ {"_Z4funcILN3foo4EnumE1EEvv",
+ {
+ /*.BasenameRange=*/{5, 9}, /*.TemplateArgumentsRange=*/{9, 23}, /*.ScopeRange=*/{5, 5},
+ /*.ArgumentsRange=*/{23, 25}, /*.QualifiersRange=*/{25, 25}, /*.NameQualifiersRange=*/{0, 0},
+ /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+ },
+ /*.basename=*/"func",
+ /*.scope=*/"",
+ /*.qualifiers=*/"",
}
// clang-format on
};
diff --git a/lldb/unittests/Core/ModuleListTest.cpp b/lldb/unittests/Core/ModuleListTest.cpp
new file mode 100644
index 0000000..3c70b0a
--- /dev/null
+++ b/lldb/unittests/Core/ModuleListTest.cpp
@@ -0,0 +1,178 @@
+//===-- ModuleListTest.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ModuleList.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/UUID.h"
+
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+
+#include "gtest/gtest.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Test that when we already have a module in the shared_module_list with a
+// specific UUID, the next call to GetSharedModule with a module_spec with the
+// same UUID should return the existing module instead of creating a new one.
+TEST(ModuleListTest, GetSharedModuleReusesExistingModuleWithSameUUID) {
+ SubsystemRAII<FileSystem, ObjectFileELF> subsystems;
+
+ auto ExpectedFile = TestFile::fromYaml(R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x0000000000000010
+...
+)");
+ ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
+
+ // First, let's verify that calling GetSharedModule twice with the same
+ // module_spec returns the same module pointer
+
+ ModuleSP first_module;
+ bool first_did_create = false;
+ Status error_first =
+ ModuleList::GetSharedModule(ExpectedFile->moduleSpec(), first_module,
+ nullptr, &first_did_create, false);
+
+ // Second call with the same spec
+ ModuleSP second_module;
+ bool second_did_create = false;
+ Status error_second =
+ ModuleList::GetSharedModule(ExpectedFile->moduleSpec(), second_module,
+ nullptr, &second_did_create, false);
+
+ if (error_first.Success() && error_second.Success()) {
+ // If both succeeded, verify they're the same module
+ EXPECT_EQ(first_module.get(), second_module.get())
+ << "GetSharedModule should return the same module for the same spec";
+ EXPECT_TRUE(first_did_create) << "First call should create the module";
+ EXPECT_FALSE(second_did_create)
+ << "Second call should reuse the existing module";
+ }
+}
+
+// Test that UUID-based lookup finds existing modules
+TEST(ModuleListTest, FindSharedModuleByUUID) {
+ SubsystemRAII<FileSystem, ObjectFileELF> subsystems;
+
+ auto ExpectedFile = TestFile::fromYaml(R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x0000000000000010
+...
+)");
+ ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
+
+ // Create and add a module to the shared module list using the moduleSpec()
+ ModuleSP created_module;
+ bool did_create = false;
+ Status error = ModuleList::GetSharedModule(
+ ExpectedFile->moduleSpec(), created_module, nullptr, &did_create, false);
+
+ if (error.Success() && created_module) {
+ // Get the UUID of the created module
+ UUID module_uuid = created_module->GetUUID();
+
+ if (module_uuid.IsValid()) {
+ // Now try to find the module by UUID
+ ModuleSP found_module = ModuleList::FindSharedModule(module_uuid);
+
+ ASSERT_NE(found_module.get(), nullptr)
+ << "FindSharedModule should find the module by UUID";
+ EXPECT_EQ(found_module.get(), created_module.get())
+ << "FindSharedModule should return the same module instance";
+ EXPECT_EQ(found_module->GetUUID(), module_uuid)
+ << "Found module should have the same UUID";
+ }
+ }
+}
+
+// Test that GetSharedModule with UUID finds existing module even with different
+// path
+TEST(ModuleListTest, GetSharedModuleByUUIDIgnoresPath) {
+ SubsystemRAII<FileSystem, ObjectFileELF> subsystems;
+
+ auto ExpectedFile = TestFile::fromYaml(R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x0000000000000010
+...
+)");
+ ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
+
+ // Create and add a module to the shared module list
+ ModuleSP first_module;
+ bool first_did_create = false;
+ Status first_error =
+ ModuleList::GetSharedModule(ExpectedFile->moduleSpec(), first_module,
+ nullptr, &first_did_create, false);
+
+ if (first_error.Success() && first_module) {
+ UUID module_uuid = first_module->GetUUID();
+
+ if (module_uuid.IsValid()) {
+ // Now try to get a module with the same UUID but different path
+ ModuleSpec second_spec;
+ second_spec.GetFileSpec() = FileSpec("/different/path/to/module.so");
+ second_spec.GetArchitecture() = ArchSpec("x86_64-pc-linux");
+ second_spec.GetUUID() = module_uuid;
+
+ ModuleSP second_module;
+ bool second_did_create = false;
+ Status second_error = ModuleList::GetSharedModule(
+ second_spec, second_module, nullptr, &second_did_create, false);
+
+ if (second_error.Success() && second_module) {
+ // If we got a module back, check if it's the same one
+ bool is_same_module = (second_module.get() == first_module.get());
+
+ // Document the behavior: ideally UUID should take precedence
+ // and return the existing module
+ EXPECT_TRUE(is_same_module)
+ << "GetSharedModule with matching UUID should return existing "
+ "module, "
+ << "even with different path (per PR #160199)";
+
+ if (is_same_module) {
+ EXPECT_FALSE(second_did_create)
+ << "Should not create a new module when UUID matches";
+ }
+ }
+ }
+ }
+}
diff --git a/lldb/unittests/Core/ModuleTest.cpp b/lldb/unittests/Core/ModuleTest.cpp
new file mode 100644
index 0000000..011554d
--- /dev/null
+++ b/lldb/unittests/Core/ModuleTest.cpp
@@ -0,0 +1,125 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Module.h"
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Language.h"
+#include "gtest/gtest.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Test that Module::FindFunctions correctly finds C++ mangled symbols
+// even when multiple language plugins are registered.
+TEST(ModuleTest, FindFunctionsCppMangledName) {
+ // Create a mock language. The point of this language is to return something
+ // in GetFunctionNameInfo that would interfere with the C++ language plugin,
+ // were they sharing the same LookupInfo.
+ class MockLanguageWithBogusLookupInfo : public Language {
+ public:
+ MockLanguageWithBogusLookupInfo() = default;
+ ~MockLanguageWithBogusLookupInfo() override = default;
+
+ lldb::LanguageType GetLanguageType() const override {
+ // The language here doesn't really matter, it just has to be something
+ // that is not C/C++/ObjC.
+ return lldb::eLanguageTypeSwift;
+ }
+
+ llvm::StringRef GetPluginName() override { return "mock-bogus-language"; }
+
+ bool IsSourceFile(llvm::StringRef file_path) const override {
+ return file_path.ends_with(".swift");
+ }
+
+ std::pair<lldb::FunctionNameType, std::optional<ConstString>>
+ GetFunctionNameInfo(ConstString name) const override {
+ // Say that every function is a selector.
+ return {lldb::eFunctionNameTypeSelector, ConstString("BOGUS_BASENAME")};
+ }
+
+ static void Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), "Mock Language",
+ CreateInstance);
+ }
+
+ static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }
+
+ static lldb_private::Language *CreateInstance(lldb::LanguageType language) {
+ if (language == lldb::eLanguageTypeSwift)
+ return new MockLanguageWithBogusLookupInfo();
+ return nullptr;
+ }
+
+ static llvm::StringRef GetPluginNameStatic() {
+ return "mock-bogus-language";
+ }
+ };
+ SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, SymbolFileSymtab,
+ CPlusPlusLanguage, MockLanguageWithBogusLookupInfo>
+ subsystems;
+
+ // Create a simple ELF module with std::vector::size() as the only symbol.
+ auto ExpectedFile = TestFile::fromYaml(R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ AddressAlign: 0x10
+ Size: 0x100
+Symbols:
+ - Name: _ZNSt6vectorIiE4sizeEv
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x1030
+ Size: 0x20
+...
+)");
+ ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
+
+ auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
+
+ // Verify both C++ and our mock language are registered.
+ Language *cpp_lang = Language::FindPlugin(lldb::eLanguageTypeC_plus_plus);
+ Language *mock_lang = Language::FindPlugin(lldb::eLanguageTypeSwift);
+ ASSERT_NE(cpp_lang, nullptr) << "C++ language plugin should be registered";
+ ASSERT_NE(mock_lang, nullptr)
+ << "Mock Swift language plugin should be registered";
+
+ ModuleFunctionSearchOptions function_options;
+ function_options.include_symbols = true;
+
+ ConstString symbol_name("_ZNSt6vectorIiE4sizeEv");
+ SymbolContextList results;
+ module_sp->FindFunctions(symbol_name, CompilerDeclContext(),
+ eFunctionNameTypeAuto, function_options, results);
+
+ // Assert that we found one symbol.
+ ASSERT_EQ(results.GetSize(), 1u);
+
+ auto result = results[0];
+ auto name = result.GetFunctionName();
+ // Assert that the symbol we found is what we expected.
+ ASSERT_EQ(name, "std::vector<int>::size()");
+ ASSERT_EQ(result.GetLanguage(), eLanguageTypeC_plus_plus);
+}
diff --git a/lldb/unittests/DAP/CMakeLists.txt b/lldb/unittests/DAP/CMakeLists.txt
index a08414c..9fef37e 100644
--- a/lldb/unittests/DAP/CMakeLists.txt
+++ b/lldb/unittests/DAP/CMakeLists.txt
@@ -1,5 +1,8 @@
add_lldb_unittest(DAPTests
+ ClientLauncherTest.cpp
DAPErrorTest.cpp
+ DAPLogTest.cpp
+ DAPSessionManagerTest.cpp
DAPTest.cpp
DAPTypesTest.cpp
FifoFilesTest.cpp
@@ -7,6 +10,7 @@ add_lldb_unittest(DAPTests
Handler/ContinueTest.cpp
JSONUtilsTest.cpp
LLDBUtilsTest.cpp
+ ProtocolRequestsTest.cpp
ProtocolTypesTest.cpp
ProtocolUtilsTest.cpp
TestBase.cpp
diff --git a/lldb/unittests/DAP/ClientLauncherTest.cpp b/lldb/unittests/DAP/ClientLauncherTest.cpp
new file mode 100644
index 0000000..dbaf9ee
--- /dev/null
+++ b/lldb/unittests/DAP/ClientLauncherTest.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClientLauncher.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include <optional>
+
+using namespace lldb_dap;
+using namespace llvm;
+
+TEST(ClientLauncherTest, GetClientFromVSCode) {
+ std::optional<ClientLauncher::Client> result =
+ ClientLauncher::GetClientFrom("vscode");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(ClientLauncher::VSCode, result.value());
+}
+
+TEST(ClientLauncherTest, GetClientFromVSCodeUpperCase) {
+ std::optional<ClientLauncher::Client> result =
+ ClientLauncher::GetClientFrom("VSCODE");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(ClientLauncher::VSCode, result.value());
+}
+
+TEST(ClientLauncherTest, GetClientFromVSCodeMixedCase) {
+ std::optional<ClientLauncher::Client> result =
+ ClientLauncher::GetClientFrom("VSCode");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(ClientLauncher::VSCode, result.value());
+}
+
+TEST(ClientLauncherTest, GetClientFromInvalidString) {
+ std::optional<ClientLauncher::Client> result =
+ ClientLauncher::GetClientFrom("invalid");
+ EXPECT_FALSE(result.has_value());
+}
+
+TEST(ClientLauncherTest, GetClientFromEmptyString) {
+ std::optional<ClientLauncher::Client> result =
+ ClientLauncher::GetClientFrom("");
+ EXPECT_FALSE(result.has_value());
+}
+
+TEST(ClientLauncherTest, URLEncode) {
+ EXPECT_EQ("", VSCodeLauncher::URLEncode(""));
+ EXPECT_EQ(
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~",
+ VSCodeLauncher::URLEncode("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST"
+ "UVWXYZ0123456789-_.~"));
+ EXPECT_EQ("hello%20world", VSCodeLauncher::URLEncode("hello world"));
+ EXPECT_EQ("hello%21%40%23%24", VSCodeLauncher::URLEncode("hello!@#$"));
+ EXPECT_EQ("%2Fpath%2Fto%2Ffile", VSCodeLauncher::URLEncode("/path/to/file"));
+ EXPECT_EQ("key%3Dvalue%26key2%3Dvalue2",
+ VSCodeLauncher::URLEncode("key=value&key2=value2"));
+ EXPECT_EQ("100%25complete", VSCodeLauncher::URLEncode("100%complete"));
+ EXPECT_EQ("file_name%20with%20spaces%20%26%20special%21.txt",
+ VSCodeLauncher::URLEncode("file_name with spaces & special!.txt"));
+ EXPECT_EQ("%00%01%02",
+ VSCodeLauncher::URLEncode(llvm::StringRef("\x00\x01\x02", 3)));
+ EXPECT_EQ("test-file_name.txt~",
+ VSCodeLauncher::URLEncode("test-file_name.txt~"));
+
+ // UTF-8 encoded characters should be percent-encoded byte by byte.
+ EXPECT_EQ("%C3%A9", VSCodeLauncher::URLEncode("é"));
+}
diff --git a/lldb/unittests/DAP/DAPLogTest.cpp b/lldb/unittests/DAP/DAPLogTest.cpp
new file mode 100644
index 0000000..2756e77
--- /dev/null
+++ b/lldb/unittests/DAP/DAPLogTest.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAPLog.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_dap;
+using namespace llvm;
+using namespace testing;
+
+static llvm::StringRef last_line(llvm::StringRef str) {
+ size_t index = str.find_last_of('\n', str.size() - 1);
+ if (index == llvm::StringRef::npos)
+ return str;
+ return str.substr(index + 1);
+}
+
+#define TIMESTAMP_PATTERN "\\[[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}\\] "
+
+TEST(DAPLog, Emit) {
+ Log::Mutex mux;
+ std::string outs;
+ raw_string_ostream os(outs);
+ Log log(os, mux);
+ Log inner_log = log.WithPrefix("my_prefix:");
+
+ log.Emit("Hi");
+ EXPECT_THAT(last_line(outs), MatchesRegex(TIMESTAMP_PATTERN "Hi\n"));
+
+ inner_log.Emit("foobar");
+ EXPECT_THAT(last_line(outs),
+ MatchesRegex(TIMESTAMP_PATTERN "my_prefix: foobar\n"));
+
+ log.Emit("Hello from a file/line.", "file.cpp", 42);
+ EXPECT_THAT(
+ last_line(outs),
+ MatchesRegex(TIMESTAMP_PATTERN "file.cpp:42 Hello from a file/line.\n"));
+
+ inner_log.Emit("Hello from a file/line.", "file.cpp", 42);
+ EXPECT_THAT(last_line(outs),
+ MatchesRegex(TIMESTAMP_PATTERN
+ "file.cpp:42 my_prefix: Hello from a file/line.\n"));
+
+ log.WithPrefix("a").WithPrefix("b").WithPrefix("c").Emit("msg");
+ EXPECT_THAT(last_line(outs), MatchesRegex(TIMESTAMP_PATTERN "a b c msg\n"));
+}
diff --git a/lldb/unittests/DAP/DAPSessionManagerTest.cpp b/lldb/unittests/DAP/DAPSessionManagerTest.cpp
new file mode 100644
index 0000000..b840d31
--- /dev/null
+++ b/lldb/unittests/DAP/DAPSessionManagerTest.cpp
@@ -0,0 +1,103 @@
+//===-- DAPSessionManagerTest.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAPSessionManager.h"
+#include "TestBase.h"
+#include "lldb/API/SBDebugger.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_dap;
+using namespace lldb;
+using namespace lldb_dap_tests;
+
+class DAPSessionManagerTest : public DAPTestBase {};
+
+TEST_F(DAPSessionManagerTest, GetInstanceReturnsSameSingleton) {
+ DAPSessionManager &instance1 = DAPSessionManager::GetInstance();
+ DAPSessionManager &instance2 = DAPSessionManager::GetInstance();
+
+ EXPECT_EQ(&instance1, &instance2);
+}
+
+// UnregisterSession uses std::notify_all_at_thread_exit, so it must be called
+// from a separate thread to properly release the mutex on thread exit.
+TEST_F(DAPSessionManagerTest, RegisterAndUnregisterSession) {
+ DAPSessionManager &manager = DAPSessionManager::GetInstance();
+
+ // Initially not registered.
+ std::vector<DAP *> sessions_before = manager.GetActiveSessions();
+ EXPECT_EQ(
+ std::count(sessions_before.begin(), sessions_before.end(), dap.get()), 0);
+
+ manager.RegisterSession(&loop, dap.get());
+
+ // Should be in active sessions after registration.
+ std::vector<DAP *> sessions_after = manager.GetActiveSessions();
+ EXPECT_EQ(std::count(sessions_after.begin(), sessions_after.end(), dap.get()),
+ 1);
+
+ // Unregister.
+ std::thread unregister_thread([&]() { manager.UnregisterSession(&loop); });
+
+ unregister_thread.join();
+
+ // There should no longer be active sessions.
+ std::vector<DAP *> sessions_final = manager.GetActiveSessions();
+ EXPECT_EQ(std::count(sessions_final.begin(), sessions_final.end(), dap.get()),
+ 0);
+}
+
+TEST_F(DAPSessionManagerTest, DisconnectAllSessions) {
+ DAPSessionManager &manager = DAPSessionManager::GetInstance();
+
+ manager.RegisterSession(&loop, dap.get());
+
+ std::vector<DAP *> sessions = manager.GetActiveSessions();
+ EXPECT_EQ(std::count(sessions.begin(), sessions.end(), dap.get()), 1);
+
+ manager.DisconnectAllSessions();
+
+ // DisconnectAllSessions shutdown but doesn't wait for
+ // sessions to complete or remove them from the active sessions map.
+ sessions = manager.GetActiveSessions();
+ EXPECT_EQ(std::count(sessions.begin(), sessions.end(), dap.get()), 1);
+
+ std::thread unregister_thread([&]() { manager.UnregisterSession(&loop); });
+ unregister_thread.join();
+}
+
+TEST_F(DAPSessionManagerTest, WaitForAllSessionsToDisconnect) {
+ DAPSessionManager &manager = DAPSessionManager::GetInstance();
+
+ manager.RegisterSession(&loop, dap.get());
+
+ std::vector<DAP *> sessions = manager.GetActiveSessions();
+ EXPECT_EQ(std::count(sessions.begin(), sessions.end(), dap.get()), 1);
+
+ // Unregister after a delay to test blocking behavior.
+ std::thread unregister_thread([&]() {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ manager.UnregisterSession(&loop);
+ });
+
+ // WaitForAllSessionsToDisconnect should block until unregistered.
+ auto start = std::chrono::steady_clock::now();
+ llvm::Error err = manager.WaitForAllSessionsToDisconnect();
+ EXPECT_FALSE(err);
+ auto duration = std::chrono::steady_clock::now() - start;
+
+ // Verify it waited at least 100ms.
+ EXPECT_GE(duration, std::chrono::milliseconds(100));
+
+ // Session should be unregistered now.
+ sessions = manager.GetActiveSessions();
+ EXPECT_EQ(std::count(sessions.begin(), sessions.end(), dap.get()), 0);
+
+ unregister_thread.join();
+}
diff --git a/lldb/unittests/DAP/ProtocolRequestsTest.cpp b/lldb/unittests/DAP/ProtocolRequestsTest.cpp
new file mode 100644
index 0000000..c830690
--- /dev/null
+++ b/lldb/unittests/DAP/ProtocolRequestsTest.cpp
@@ -0,0 +1,195 @@
+//===-- ProtocolRequestsTest.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Protocol/ProtocolRequests.h"
+#include "Protocol/ProtocolTypes.h"
+#include "TestingSupport/TestUtilities.h"
+#include "llvm/Testing/Support/Error.h"
+#include <gtest/gtest.h>
+
+using namespace llvm;
+using namespace lldb_dap::protocol;
+using lldb_private::PrettyPrint;
+using llvm::json::parse;
+
+TEST(ProtocolRequestsTest, ExceptionInfoArguments) {
+ llvm::Expected<ExceptionInfoArguments> expected =
+ parse<ExceptionInfoArguments>(R"({
+ "threadId": 3434
+ })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(expected->threadId, 3434U);
+
+ // Check required keys;
+ EXPECT_THAT_EXPECTED(parse<ExceptionInfoArguments>(R"({})"),
+ FailedWithMessage("missing value at (root).threadId"));
+
+ EXPECT_THAT_EXPECTED(parse<ExceptionInfoArguments>(R"({"id": 10})"),
+ FailedWithMessage("missing value at (root).threadId"));
+}
+
+TEST(ProtocolRequestsTest, ExceptionInfoResponseBody) {
+ ExceptionInfoResponseBody body;
+ body.exceptionId = "signal";
+ body.breakMode = eExceptionBreakModeAlways;
+
+ // Check required keys.
+ Expected<json::Value> expected = parse(
+ R"({
+ "exceptionId": "signal",
+ "breakMode": "always"
+ })");
+
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body));
+
+ // Check optional keys.
+ body.description = "SIGNAL SIGWINCH";
+ body.breakMode = eExceptionBreakModeNever;
+ body.details = ExceptionDetails{};
+ body.details->message = "some message";
+
+ Expected<json::Value> expected_opt = parse(
+ R"({
+ "exceptionId": "signal",
+ "description": "SIGNAL SIGWINCH",
+ "breakMode": "never",
+ "details": {
+ "message": "some message"
+ }
+ })");
+
+ ASSERT_THAT_EXPECTED(expected_opt, llvm::Succeeded());
+ EXPECT_EQ(PrettyPrint(*expected_opt), PrettyPrint(body));
+}
+
+TEST(ProtocolRequestsTest, EvaluateArguments) {
+ llvm::Expected<EvaluateArguments> expected = parse<EvaluateArguments>(R"({
+ "expression": "hello world",
+ "context": "repl"
+ })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(expected->expression, "hello world");
+ EXPECT_EQ(expected->context, eEvaluateContextRepl);
+
+ // Check required keys.
+ EXPECT_THAT_EXPECTED(parse<EvaluateArguments>(R"({})"),
+ FailedWithMessage("missing value at (root).expression"));
+}
+
+TEST(ProtocolRequestsTest, EvaluateResponseBody) {
+ EvaluateResponseBody body;
+ body.result = "hello world";
+ body.variablesReference = 7;
+
+ // Check required keys.
+ Expected<json::Value> expected = parse(R"({
+ "result": "hello world",
+ "variablesReference": 7
+ })");
+
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body));
+
+ // Check optional keys.
+ body.result = "'abc'";
+ body.type = "string";
+ body.variablesReference = 42;
+ body.namedVariables = 1;
+ body.indexedVariables = 2;
+ body.memoryReference = "0x123";
+ body.valueLocationReference = 22;
+
+ Expected<json::Value> expected_opt = parse(R"({
+ "result": "'abc'",
+ "type": "string",
+ "variablesReference": 42,
+ "namedVariables": 1,
+ "indexedVariables": 2,
+ "memoryReference": "0x123",
+ "valueLocationReference": 22
+ })");
+
+ ASSERT_THAT_EXPECTED(expected_opt, llvm::Succeeded());
+ EXPECT_EQ(PrettyPrint(*expected_opt), PrettyPrint(body));
+}
+
+TEST(ProtocolRequestsTest, InitializeRequestArguments) {
+ llvm::Expected<InitializeRequestArguments> expected =
+ parse<InitializeRequestArguments>(R"({"adapterID": "myid"})");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(expected->adapterID, "myid");
+
+ // Check optional keys.
+ expected = parse<InitializeRequestArguments>(R"({
+ "adapterID": "myid",
+ "clientID": "myclientid",
+ "clientName": "lldb-dap-unit-tests",
+ "locale": "en-US",
+ "linesStartAt1": true,
+ "columnsStartAt1": true,
+ "pathFormat": "uri",
+ "supportsVariableType": true,
+ "supportsVariablePaging": true,
+ "supportsRunInTerminalRequest": true,
+ "supportsMemoryReferences": true,
+ "supportsProgressReporting": true,
+ "supportsInvalidatedEvent": true,
+ "supportsMemoryEvent": true,
+ "supportsArgsCanBeInterpretedByShell": true,
+ "supportsStartDebuggingRequest": true,
+ "supportsANSIStyling": true
+ })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(expected->adapterID, "myid");
+ EXPECT_EQ(expected->clientID, "myclientid");
+ EXPECT_EQ(expected->clientName, "lldb-dap-unit-tests");
+ EXPECT_EQ(expected->locale, "en-US");
+ EXPECT_EQ(expected->linesStartAt1, true);
+ EXPECT_EQ(expected->columnsStartAt1, true);
+ EXPECT_EQ(expected->pathFormat, ePathFormatURI);
+ EXPECT_EQ(expected->supportedFeatures.contains(eClientFeatureVariableType),
+ true);
+ EXPECT_EQ(
+ expected->supportedFeatures.contains(eClientFeatureRunInTerminalRequest),
+ true);
+ EXPECT_EQ(
+ expected->supportedFeatures.contains(eClientFeatureMemoryReferences),
+ true);
+ EXPECT_EQ(
+ expected->supportedFeatures.contains(eClientFeatureProgressReporting),
+ true);
+ EXPECT_EQ(
+ expected->supportedFeatures.contains(eClientFeatureInvalidatedEvent),
+ true);
+ EXPECT_EQ(expected->supportedFeatures.contains(eClientFeatureMemoryEvent),
+ true);
+ EXPECT_EQ(expected->supportedFeatures.contains(
+ eClientFeatureArgsCanBeInterpretedByShell),
+ true);
+ EXPECT_EQ(
+ expected->supportedFeatures.contains(eClientFeatureStartDebuggingRequest),
+ true);
+ EXPECT_EQ(expected->supportedFeatures.contains(eClientFeatureANSIStyling),
+ true);
+
+ // Check required keys.
+ EXPECT_THAT_EXPECTED(parse<InitializeRequestArguments>(R"({})"),
+ FailedWithMessage("missing value at (root).adapterID"));
+}
+
+TEST(ProtocolRequestsTest, PauseRequestArguments) {
+ llvm::Expected<PauseArguments> expected =
+ parse<PauseArguments>(R"({"threadId": 123})");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(expected->threadId, 123U);
+
+ // Check required keys.
+ EXPECT_THAT_EXPECTED(parse<PauseArguments>(R"({})"),
+ FailedWithMessage("missing value at (root).threadId"));
+}
diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp
index 8170abd..6a4620a 100644
--- a/lldb/unittests/DAP/ProtocolTypesTest.cpp
+++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp
@@ -1129,3 +1129,50 @@ TEST(ProtocolTypesTest, DataBreakpointInfoArguments) {
EXPECT_THAT_EXPECTED(parse<DataBreakpointInfoArguments>(R"({"name":"data"})"),
llvm::Succeeded());
}
+
+TEST(ProtocolTypesTest, ExceptionBreakMode) {
+ const std::vector<std::pair<ExceptionBreakMode, llvm::StringRef>> test_cases =
+ {{ExceptionBreakMode::eExceptionBreakModeAlways, "always"},
+ {ExceptionBreakMode::eExceptionBreakModeNever, "never"},
+ {ExceptionBreakMode::eExceptionBreakModeUnhandled, "unhandled"},
+ {ExceptionBreakMode::eExceptionBreakModeUserUnhandled, "userUnhandled"}};
+
+ for (const auto [value, expected] : test_cases) {
+ json::Value const serialized = toJSON(value);
+ ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String);
+ EXPECT_EQ(serialized.getAsString(), expected);
+ }
+}
+
+TEST(ProtocolTypesTest, ExceptionDetails) {
+ ExceptionDetails details;
+
+ // Check required keys.
+ Expected<json::Value> expected = parse(R"({})");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(pp(*expected), pp(details));
+
+ // Check optional keys.
+ details.message = "SIGABRT exception";
+ details.typeName = "signal";
+ details.fullTypeName = "SIGABRT";
+ details.evaluateName = "process handle SIGABRT";
+ details.stackTrace = "some stacktrace";
+ ExceptionDetails inner_details;
+ inner_details.message = "inner message";
+ details.innerException = {std::move(inner_details)};
+
+ Expected<json::Value> expected_opt = parse(R"({
+ "message": "SIGABRT exception",
+ "typeName": "signal",
+ "fullTypeName": "SIGABRT",
+ "evaluateName": "process handle SIGABRT",
+ "stackTrace": "some stacktrace",
+ "innerException": [{
+ "message": "inner message"
+ }]
+ })");
+
+ ASSERT_THAT_EXPECTED(expected_opt, llvm::Succeeded());
+ EXPECT_EQ(pp(*expected_opt), pp(details));
+}
diff --git a/lldb/unittests/DAP/TestBase.cpp b/lldb/unittests/DAP/TestBase.cpp
index 8cb4599..e57963e 100644
--- a/lldb/unittests/DAP/TestBase.cpp
+++ b/lldb/unittests/DAP/TestBase.cpp
@@ -7,7 +7,10 @@
//===----------------------------------------------------------------------===//
#include "TestBase.h"
+#include "DAP.h"
#include "DAPLog.h"
+#include "Handler/RequestHandler.h"
+#include "Handler/ResponseHandler.h"
#include "TestingSupport/TestUtilities.h"
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBStructuredData.h"
@@ -19,7 +22,6 @@
#include "gtest/gtest.h"
#include <cstdio>
#include <memory>
-#include <system_error>
using namespace llvm;
using namespace lldb;
@@ -35,10 +37,9 @@ using lldb_private::Pipe;
void TransportBase::SetUp() {
std::tie(to_client, to_server) = TestDAPTransport::createPair();
- std::error_code EC;
- log = std::make_unique<Log>("-", EC);
+ log = std::make_unique<Log>(llvm::outs(), log_mutex);
dap = std::make_unique<DAP>(
- /*log=*/log.get(),
+ /*log=*/*log,
/*default_repl_mode=*/ReplMode::Auto,
/*pre_init_commands=*/std::vector<std::string>(),
/*no_lldbinit=*/false,
@@ -72,6 +73,7 @@ void DAPTestBase::TearDown() {
void DAPTestBase::SetUpTestSuite() {
lldb::SBError error = SBDebugger::InitializeWithErrorHandling();
+ EXPECT_TRUE(error.IsValid());
EXPECT_TRUE(error.Success());
}
void DAPTestBase::TeatUpTestSuite() { SBDebugger::Terminate(); }
diff --git a/lldb/unittests/DAP/TestBase.h b/lldb/unittests/DAP/TestBase.h
index c32f3a7..f1c7e6b 100644
--- a/lldb/unittests/DAP/TestBase.h
+++ b/lldb/unittests/DAP/TestBase.h
@@ -8,6 +8,8 @@
#include "DAP.h"
#include "DAPLog.h"
+#include "Handler/RequestHandler.h"
+#include "Handler/ResponseHandler.h"
#include "Protocol/ProtocolBase.h"
#include "TestingSupport/Host/JSONTransportTestUtilities.h"
#include "TestingSupport/SubsystemRAII.h"
@@ -60,6 +62,7 @@ protected:
lldb_private::MainLoop::ReadHandleUP handles[2];
std::unique_ptr<lldb_dap::Log> log;
+ lldb_dap::Log::Mutex log_mutex;
std::unique_ptr<TestDAPTransport> to_client;
MockMessageHandler<lldb_dap::ProtocolDescriptor> client;
diff --git a/lldb/unittests/Editline/EditlineTest.cpp b/lldb/unittests/Editline/EditlineTest.cpp
index 2875f4e..2afc336 100644
--- a/lldb/unittests/Editline/EditlineTest.cpp
+++ b/lldb/unittests/Editline/EditlineTest.cpp
@@ -9,6 +9,8 @@
#include "lldb/Host/Config.h"
#include "lldb/Host/File.h"
#include "lldb/Host/HostInfo.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/Testing/Support/Error.h"
#if LLDB_ENABLE_LIBEDIT
@@ -25,7 +27,6 @@
#include "TestingSupport/SubsystemRAII.h"
#include "lldb/Host/Editline.h"
#include "lldb/Host/FileSystem.h"
-#include "lldb/Host/Pipe.h"
#include "lldb/Host/PseudoTerminal.h"
#include "lldb/Host/StreamFile.h"
#include "lldb/Utility/Status.h"
@@ -37,27 +38,6 @@ namespace {
const size_t TIMEOUT_MILLIS = 5000;
}
-class FilePointer {
-public:
- FilePointer() = delete;
-
- FilePointer(const FilePointer &) = delete;
-
- FilePointer(FILE *file_p) : _file_p(file_p) {}
-
- ~FilePointer() {
- if (_file_p != nullptr) {
- const int close_result = fclose(_file_p);
- EXPECT_EQ(0, close_result);
- }
- }
-
- operator FILE *() { return _file_p; }
-
-private:
- FILE *_file_p;
-};
-
/**
Wraps an Editline class, providing a simple way to feed
input (as if from the keyboard) and receive output from Editline.
@@ -90,44 +70,39 @@ private:
std::recursive_mutex output_mutex;
std::unique_ptr<lldb_private::Editline> _editline_sp;
- PseudoTerminal _pty;
- int _pty_primary_fd = -1;
- int _pty_secondary_fd = -1;
-
- std::unique_ptr<FilePointer> _el_secondary_file;
+ lldb::FileSP _el_primary_file;
+ lldb::FileSP _el_secondary_file;
};
-EditlineAdapter::EditlineAdapter()
- : _editline_sp(), _pty(), _el_secondary_file() {
+EditlineAdapter::EditlineAdapter() : _editline_sp(), _el_secondary_file() {
lldb_private::Status error;
+ PseudoTerminal pty;
// Open the first primary pty available.
- EXPECT_THAT_ERROR(_pty.OpenFirstAvailablePrimary(O_RDWR), llvm::Succeeded());
+ EXPECT_THAT_ERROR(pty.OpenFirstAvailablePrimary(O_RDWR), llvm::Succeeded());
+ // Open the corresponding secondary pty.
+ EXPECT_THAT_ERROR(pty.OpenSecondary(O_RDWR), llvm::Succeeded());
// Grab the primary fd. This is a file descriptor we will:
// (1) write to when we want to send input to editline.
// (2) read from when we want to see what editline sends back.
- _pty_primary_fd = _pty.GetPrimaryFileDescriptor();
+ _el_primary_file.reset(
+ new NativeFile(pty.ReleasePrimaryFileDescriptor(),
+ lldb_private::NativeFile::eOpenOptionReadWrite, true));
- // Open the corresponding secondary pty.
- EXPECT_THAT_ERROR(_pty.OpenSecondary(O_RDWR), llvm::Succeeded());
- _pty_secondary_fd = _pty.GetSecondaryFileDescriptor();
-
- _el_secondary_file.reset(new FilePointer(fdopen(_pty_secondary_fd, "rw")));
- EXPECT_FALSE(nullptr == *_el_secondary_file);
- if (*_el_secondary_file == nullptr)
- return;
+ _el_secondary_file.reset(
+ new NativeFile(pty.ReleaseSecondaryFileDescriptor(),
+ lldb_private::NativeFile::eOpenOptionReadWrite, true));
lldb::LockableStreamFileSP output_stream_sp =
- std::make_shared<LockableStreamFile>(*_el_secondary_file,
- NativeFile::Unowned, output_mutex);
+ std::make_shared<LockableStreamFile>(_el_secondary_file, output_mutex);
lldb::LockableStreamFileSP error_stream_sp =
- std::make_shared<LockableStreamFile>(*_el_secondary_file,
- NativeFile::Unowned, output_mutex);
+ std::make_shared<LockableStreamFile>(_el_secondary_file, output_mutex);
// Create an Editline instance.
_editline_sp.reset(new lldb_private::Editline(
- "gtest editor", *_el_secondary_file, output_stream_sp, error_stream_sp,
+ "gtest editor", _el_secondary_file->GetStream(), output_stream_sp,
+ error_stream_sp,
/*color=*/false));
_editline_sp->SetPrompt("> ");
@@ -140,7 +115,7 @@ EditlineAdapter::EditlineAdapter()
void EditlineAdapter::CloseInput() {
if (_el_secondary_file != nullptr)
- _el_secondary_file.reset(nullptr);
+ _el_secondary_file->Close();
}
bool EditlineAdapter::SendLine(const std::string &line) {
@@ -148,19 +123,14 @@ bool EditlineAdapter::SendLine(const std::string &line) {
if (!IsValid())
return false;
+ std::string out = line + "\n";
+
// Write the line out to the pipe connected to editline's input.
- ssize_t input_bytes_written =
- ::write(_pty_primary_fd, line.c_str(),
- line.length() * sizeof(std::string::value_type));
-
- const char *eoln = "\n";
- const size_t eoln_length = strlen(eoln);
- input_bytes_written =
- ::write(_pty_primary_fd, eoln, eoln_length * sizeof(char));
-
- EXPECT_NE(-1, input_bytes_written) << strerror(errno);
- EXPECT_EQ(eoln_length * sizeof(char), size_t(input_bytes_written));
- return eoln_length * sizeof(char) == size_t(input_bytes_written);
+ size_t num_bytes = out.length() * sizeof(std::string::value_type);
+ EXPECT_THAT_ERROR(_el_primary_file->Write(out.c_str(), num_bytes).takeError(),
+ llvm::Succeeded());
+ EXPECT_EQ(num_bytes, out.length() * sizeof(std::string::value_type));
+ return true;
}
bool EditlineAdapter::SendLines(const std::vector<std::string> &lines) {
@@ -215,7 +185,7 @@ bool EditlineAdapter::IsInputComplete(lldb_private::Editline *editline,
}
void EditlineAdapter::ConsumeAllOutput() {
- FilePointer output_file(fdopen(_pty_primary_fd, "r"));
+ FILE *output_file = _el_primary_file->GetStream();
int ch;
while ((ch = fgetc(output_file)) != EOF) {
diff --git a/lldb/unittests/Expression/CMakeLists.txt b/lldb/unittests/Expression/CMakeLists.txt
index 2600557..0e0b002 100644
--- a/lldb/unittests/Expression/CMakeLists.txt
+++ b/lldb/unittests/Expression/CMakeLists.txt
@@ -10,6 +10,7 @@ add_lldb_unittest(ExpressionTests
DWARFExpressionTest.cpp
CppModuleConfigurationTest.cpp
ExpressionTest.cpp
+ ValueMatcher.cpp
LINK_COMPONENTS
Support
diff --git a/lldb/unittests/Expression/ClangParserTest.cpp b/lldb/unittests/Expression/ClangParserTest.cpp
index fab4487..c949026 100644
--- a/lldb/unittests/Expression/ClangParserTest.cpp
+++ b/lldb/unittests/Expression/ClangParserTest.cpp
@@ -8,7 +8,7 @@
#include "clang/Basic/Version.h"
#include "clang/Config/config.h"
-#include "clang/Driver/Driver.h"
+#include "clang/Options/OptionUtils.h"
#include "Plugins/ExpressionParser/Clang/ClangHost.h"
#include "TestingSupport/SubsystemRAII.h"
@@ -43,7 +43,7 @@ TEST_F(ClangHostTest, ComputeClangResourceDirectory) {
std::string path_to_liblldb = "C:\\foo\\bar\\lib\\";
#endif
std::string path_to_clang_dir =
- clang::driver::Driver::GetResourcesPath(path_to_liblldb + "liblldb");
+ clang::GetResourcesPath(path_to_liblldb + "liblldb");
llvm::SmallString<256> path_to_clang_lib_dir_real;
llvm::sys::fs::real_path(path_to_clang_dir, path_to_clang_lib_dir_real);
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index 9d11060..f264fb3 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -5,8 +5,9 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-
#include "lldb/Expression/DWARFExpression.h"
+#include "ValueMatcher.h"
+#include <unordered_map>
#ifdef ARCH_AARCH64
#include "Plugins/ABI/AArch64/ABISysV_arm64.h"
#endif
@@ -39,32 +40,128 @@ using namespace lldb_private;
using namespace llvm::dwarf;
namespace {
-struct MockProcess : Process {
- MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
- : Process(target_sp, listener_sp) {}
+/// A mock implementation of DWARFExpression::Delegate for testing.
+/// This class provides default implementations of all delegate methods,
+/// with the DWARF version being configurable via the constructor.
+class MockDwarfDelegate : public DWARFExpression::Delegate {
+public:
+ static constexpr uint16_t DEFAULT_DWARF_VERSION = 5;
+ static MockDwarfDelegate Dwarf5() { return MockDwarfDelegate(5); }
+ static MockDwarfDelegate Dwarf2() { return MockDwarfDelegate(2); }
- llvm::StringRef GetPluginName() override { return "mock process"; }
+ MockDwarfDelegate() : MockDwarfDelegate(DEFAULT_DWARF_VERSION) {}
+ explicit MockDwarfDelegate(uint16_t version) : m_dwarf_version(version) {}
- bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
- return false;
- };
+ uint16_t GetVersion() const override { return m_dwarf_version; }
- Status DoDestroy() override { return {}; }
+ dw_addr_t GetBaseAddress() const override { return 0; }
- void RefreshStateAfterStop() override {}
+ uint8_t GetAddressByteSize() const override { return 4; }
- bool DoUpdateThreadList(ThreadList &old_thread_list,
- ThreadList &new_thread_list) override {
+ llvm::Expected<std::pair<uint64_t, bool>>
+ GetDIEBitSizeAndSign(uint64_t relative_die_offset) const override {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "GetDIEBitSizeAndSign not implemented");
+ }
+
+ dw_addr_t ReadAddressFromDebugAddrSection(uint32_t index) const override {
+ return 0;
+ }
+
+ lldb::offset_t GetVendorDWARFOpcodeSize(const DataExtractor &data,
+ const lldb::offset_t data_offset,
+ const uint8_t op) const override {
+ return LLDB_INVALID_OFFSET;
+ }
+
+ bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes,
+ lldb::offset_t &offset, RegisterContext *reg_ctx,
+ lldb::RegisterKind reg_kind,
+ DWARFExpression::Stack &stack) const override {
return false;
+ }
+
+private:
+ uint16_t m_dwarf_version;
+};
+
+/// Mock memory implementation for testing.
+/// Stores predefined memory contents indexed by {address, size} pairs.
+class MockMemory {
+public:
+ /// Represents a memory read request with an address and size.
+ /// Used as a key in the memory map to look up predefined test data.
+ struct Request {
+ lldb::addr_t addr;
+ size_t size;
+
+ bool operator==(const Request &other) const {
+ return addr == other.addr && size == other.size;
+ }
+
+ /// Hash function for Request to enable its use in unordered_map.
+ struct Hash {
+ size_t operator()(const Request &req) const {
+ size_t h1 = std::hash<lldb::addr_t>{}(req.addr);
+ size_t h2 = std::hash<size_t>{}(req.size);
+ return h1 ^ (h2 << 1);
+ }
+ };
};
- size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ typedef std::unordered_map<Request, std::vector<uint8_t>, Request::Hash> Map;
+ MockMemory() = default;
+ MockMemory(Map memory) : m_memory(std::move(memory)) {
+ // Make sure the requested memory size matches the returned value.
+ for ([[maybe_unused]] auto &[req, bytes] : m_memory) {
+ assert(bytes.size() == req.size);
+ }
+ }
+
+ llvm::Expected<std::vector<uint8_t>> ReadMemory(lldb::addr_t addr,
+ size_t size) {
+ if (!m_memory.count({addr, size})) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "MockMemory::ReadMemory {address=0x%" PRIx64 ", size=%zu} not found",
+ addr, size);
+ }
+ return m_memory[{addr, size}];
+ }
+
+private:
+ std::unordered_map<Request, std::vector<uint8_t>, Request::Hash> m_memory;
+};
+
+/// A Process whose `ReadMemory` override queries MockMemory.
+struct MockProcess : Process {
+ using addr_t = lldb::addr_t;
+
+ MockMemory m_memory;
+
+ MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ MockMemory memory)
+ : Process(target_sp, listener_sp), m_memory(std::move(memory)) {}
+ size_t DoReadMemory(addr_t vm_addr, void *buf, size_t size,
Status &error) override {
- for (size_t i = 0; i < size; ++i)
- ((char *)buf)[i] = (vm_addr + i) & 0xff;
- error.Clear();
+ auto expected_memory = m_memory.ReadMemory(vm_addr, size);
+ if (!expected_memory) {
+ error = Status::FromError(expected_memory.takeError());
+ return 0;
+ }
+ assert(expected_memory->size() == size);
+ std::memcpy(buf, expected_memory->data(), expected_memory->size());
return size;
}
+ size_t ReadMemory(addr_t addr, void *buf, size_t size,
+ Status &status) override {
+ return DoReadMemory(addr, buf, size, status);
+ }
+ bool CanDebug(lldb::TargetSP, bool) override { return true; }
+ Status DoDestroy() override { return Status(); }
+ llvm::StringRef GetPluginName() override { return ""; }
+ void RefreshStateAfterStop() override {}
+ bool DoUpdateThreadList(ThreadList &, ThreadList &) override { return false; }
};
class MockThread : public Thread {
@@ -135,40 +232,19 @@ private:
};
} // namespace
-static llvm::Expected<Scalar> Evaluate(llvm::ArrayRef<uint8_t> expr,
- lldb::ModuleSP module_sp = {},
- DWARFUnit *unit = nullptr,
- ExecutionContext *exe_ctx = nullptr,
- RegisterContext *reg_ctx = nullptr) {
- DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle,
- /*addr_size*/ 4);
-
- llvm::Expected<Value> result = DWARFExpression::Evaluate(
- exe_ctx, reg_ctx, module_sp, extractor, unit, lldb::eRegisterKindLLDB,
- /*initial_value_ptr=*/nullptr,
- /*object_address_ptr=*/nullptr);
- if (!result)
- return result.takeError();
-
- switch (result->GetValueType()) {
- case Value::ValueType::Scalar:
- return result->GetScalar();
- case Value::ValueType::LoadAddress:
- return LLDB_INVALID_ADDRESS;
- case Value::ValueType::HostAddress: {
- // Convert small buffers to scalars to simplify the tests.
- DataBufferHeap &buf = result->GetBuffer();
- if (buf.GetByteSize() <= 8) {
- uint64_t val = 0;
- memcpy(&val, buf.GetBytes(), buf.GetByteSize());
- return Scalar(llvm::APInt(buf.GetByteSize() * 8, val, false));
- }
- }
- [[fallthrough]];
- default:
- break;
- }
- return llvm::createStringError("unsupported value type");
+static llvm::Expected<Value> Evaluate(llvm::ArrayRef<uint8_t> expr,
+ lldb::ModuleSP module_sp = {},
+ DWARFExpression::Delegate *unit = nullptr,
+ ExecutionContext *exe_ctx = nullptr,
+ RegisterContext *reg_ctx = nullptr) {
+ DataExtractor extractor(
+ expr.data(), expr.size(), lldb::eByteOrderLittle,
+ /*addr_size*/ exe_ctx ? exe_ctx->GetAddressByteSize() : 4);
+
+ return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, extractor, unit,
+ lldb::eRegisterKindLLDB,
+ /*initial_value_ptr=*/nullptr,
+ /*object_address_ptr=*/nullptr);
}
class DWARFExpressionTester : public YAMLModuleTester {
@@ -177,18 +253,11 @@ public:
: YAMLModuleTester(yaml_data, cu_index) {}
using YAMLModuleTester::YAMLModuleTester;
- llvm::Expected<Scalar> Eval(llvm::ArrayRef<uint8_t> expr) {
+ llvm::Expected<Value> Eval(llvm::ArrayRef<uint8_t> expr) {
return ::Evaluate(expr, m_module_sp, m_dwarf_unit);
}
};
-/// Unfortunately Scalar's operator==() is really picky.
-static Scalar GetScalar(unsigned bits, uint64_t value, bool sign) {
- Scalar scalar(value);
- scalar.TruncOrExtendTo(bits, sign);
- return scalar;
-}
-
/// This is needed for the tests that use a mock process.
class DWARFExpressionMockProcessTest : public ::testing::Test {
public:
@@ -204,46 +273,23 @@ public:
}
};
-struct PlatformTargetDebugger {
- lldb::PlatformSP platform_sp;
- lldb::TargetSP target_sp;
- lldb::DebuggerSP debugger_sp;
-};
-
-/// A helper function to create <Platform, Target, Debugger> objects with the
-/// "aarch64-pc-linux" ArchSpec.
-static PlatformTargetDebugger CreateTarget() {
- ArchSpec arch("aarch64-pc-linux");
- Platform::SetHostPlatform(
- platform_linux::PlatformLinux::CreateInstance(true, &arch));
- lldb::PlatformSP platform_sp;
- lldb::TargetSP target_sp;
- lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
- debugger_sp->GetTargetList().CreateTarget(
- *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
- return PlatformTargetDebugger{platform_sp, target_sp, debugger_sp};
-}
-
-// NB: This class doesn't use the override keyword to avoid
-// -Winconsistent-missing-override warnings from the compiler. The
-// inconsistency comes from the overriding definitions in the MOCK_*** macros.
+/// Mock target implementation for testing.
+/// Provides predefined memory contents via MockMemory instead of reading from
+/// a real process.
class MockTarget : public Target {
public:
MockTarget(Debugger &debugger, const ArchSpec &target_arch,
- const lldb::PlatformSP &platform_sp)
- : Target(debugger, target_arch, platform_sp, true) {}
-
- MOCK_METHOD2(ReadMemory,
- llvm::Expected<std::vector<uint8_t>>(lldb::addr_t addr,
- size_t size));
+ const lldb::PlatformSP &platform_sp, MockMemory memory)
+ : Target(debugger, target_arch, platform_sp, true),
+ m_memory(std::move(memory)) {}
size_t ReadMemory(const Address &addr, void *dst, size_t dst_len,
Status &error, bool force_live_memory = false,
lldb::addr_t *load_addr_ptr = nullptr,
- bool *did_read_live_memory = nullptr) /*override*/ {
- auto expected_memory = this->ReadMemory(addr.GetOffset(), dst_len);
+ bool *did_read_live_memory = nullptr) override {
+ auto expected_memory = m_memory.ReadMemory(addr.GetOffset(), dst_len);
if (!expected_memory) {
- llvm::consumeError(expected_memory.takeError());
+ error = Status::FromError(expected_memory.takeError());
return 0;
}
const size_t bytes_read = expected_memory->size();
@@ -251,52 +297,110 @@ public:
std::memcpy(dst, expected_memory->data(), bytes_read);
return bytes_read;
}
+
+private:
+ MockMemory m_memory;
};
+struct TestContext {
+ lldb::PlatformSP platform_sp;
+ lldb::TargetSP target_sp;
+ lldb::DebuggerSP debugger_sp;
+ lldb::ProcessSP process_sp;
+ lldb::ThreadSP thread_sp;
+ lldb::RegisterContextSP reg_ctx_sp;
+};
+
+/// A helper function to create TestContext objects with the
+/// given triple, memory, and register contents.
+static bool CreateTestContext(TestContext *ctx, llvm::StringRef triple,
+ std::optional<RegisterValue> reg_value = {},
+ std::optional<MockMemory> process_memory = {},
+ std::optional<MockMemory> target_memory = {}) {
+ ArchSpec arch(triple);
+ lldb::PlatformSP platform_sp =
+ platform_linux::PlatformLinux::CreateInstance(true, &arch);
+ Platform::SetHostPlatform(platform_sp);
+ lldb::TargetSP target_sp;
+ lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
+
+ Status status;
+ if (target_memory)
+ target_sp = std::make_shared<MockTarget>(*debugger_sp, arch, platform_sp,
+ std::move(*target_memory));
+ else
+ status = debugger_sp->GetTargetList().CreateTarget(
+ *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
+
+ EXPECT_TRUE(status.Success());
+ if (!status.Success())
+ return false;
+
+ lldb::ProcessSP process_sp;
+ if (!process_memory)
+ process_memory = MockMemory();
+ process_sp = std::make_shared<MockProcess>(
+ target_sp, Listener::MakeListener("dummy"), std::move(*process_memory));
+
+ auto thread_sp = std::make_shared<MockThread>(*process_sp);
+
+ process_sp->GetThreadList().AddThread(thread_sp);
+
+ lldb::RegisterContextSP reg_ctx_sp;
+ if (reg_value) {
+ reg_ctx_sp = std::make_shared<MockRegisterContext>(*thread_sp, *reg_value);
+ thread_sp->SetRegisterContext(reg_ctx_sp);
+ }
+
+ *ctx = TestContext{platform_sp, target_sp, debugger_sp,
+ process_sp, thread_sp, reg_ctx_sp};
+ return true;
+}
+
TEST(DWARFExpression, DW_OP_pick) {
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 0}),
- llvm::HasValue(0));
+ ExpectScalar(0));
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 1}),
- llvm::HasValue(1));
+ ExpectScalar(1));
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 2}),
llvm::Failed());
}
TEST(DWARFExpression, DW_OP_const) {
// Extend to address size.
- EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x88}), llvm::HasValue(0x88));
+ EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x88}), ExpectScalar(0x88));
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1s, 0x88}),
- llvm::HasValue(0xffffff88));
+ ExpectScalar(0xffffff88));
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u, 0x47, 0x88}),
- llvm::HasValue(0x8847));
+ ExpectScalar(0x8847));
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2s, 0x47, 0x88}),
- llvm::HasValue(0xffff8847));
+ ExpectScalar(0xffff8847));
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4u, 0x44, 0x42, 0x47, 0x88}),
- llvm::HasValue(0x88474244));
+ ExpectScalar(0x88474244));
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4s, 0x44, 0x42, 0x47, 0x88}),
- llvm::HasValue(0x88474244));
+ ExpectScalar(0x88474244));
// Truncate to address size.
EXPECT_THAT_EXPECTED(
Evaluate({DW_OP_const8u, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}),
- llvm::HasValue(0x33221100));
+ ExpectScalar(0x33221100));
EXPECT_THAT_EXPECTED(
Evaluate({DW_OP_const8s, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}),
- llvm::HasValue(0x33221100));
+ ExpectScalar(0x33221100));
// Don't truncate to address size for compatibility with clang (pr48087).
EXPECT_THAT_EXPECTED(
Evaluate({DW_OP_constu, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}),
- llvm::HasValue(0x01010101010101));
+ ExpectScalar(0x01010101010101));
EXPECT_THAT_EXPECTED(
Evaluate({DW_OP_consts, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}),
- llvm::HasValue(0xffff010101010101));
+ ExpectScalar(0xffff010101010101));
}
TEST(DWARFExpression, DW_OP_skip) {
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x42, DW_OP_skip, 0x02, 0x00,
DW_OP_const1u, 0xff}),
- llvm::HasValue(0x42));
+ ExpectScalar(0x42));
}
TEST(DWARFExpression, DW_OP_bra) {
@@ -309,7 +413,7 @@ TEST(DWARFExpression, DW_OP_bra) {
DW_OP_const1u, 0xff, // push 0xff
}),
// clang-format on
- llvm::HasValue(0x42));
+ ExpectScalar(0x42));
EXPECT_THAT_ERROR(Evaluate({DW_OP_bra, 0x01, 0x00}).takeError(),
llvm::Failed());
@@ -414,42 +518,42 @@ DWARF:
EXPECT_THAT_EXPECTED(
t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, //
DW_OP_convert, offs_uint32_t, DW_OP_stack_value}),
- llvm::HasValue(GetScalar(64, 0x44332211, not_signed)));
+ ExpectScalar(64, 0x44332211, not_signed));
// Zero-extend to 64 bits.
EXPECT_THAT_EXPECTED(
t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, //
DW_OP_convert, offs_uint64_t, DW_OP_stack_value}),
- llvm::HasValue(GetScalar(64, 0x44332211, not_signed)));
+ ExpectScalar(64, 0x44332211, not_signed));
// Sign-extend to 64 bits.
EXPECT_THAT_EXPECTED(
t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //
DW_OP_convert, offs_sint64_t, DW_OP_stack_value}),
- llvm::HasValue(GetScalar(64, 0xffffffffffeeddcc, is_signed)));
+ ExpectScalar(64, 0xffffffffffeeddcc, is_signed));
// Sign-extend, then truncate.
EXPECT_THAT_EXPECTED(
t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //
DW_OP_convert, offs_sint64_t, //
DW_OP_convert, offs_uint32_t, DW_OP_stack_value}),
- llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed)));
+ ExpectScalar(32, 0xffeeddcc, not_signed));
// Truncate to default unspecified (pointer-sized) type.
EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //
DW_OP_convert, offs_sint64_t, //
DW_OP_convert, 0x00, DW_OP_stack_value}),
- llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed)));
+ ExpectScalar(32, 0xffeeddcc, not_signed));
// Truncate to 8 bits.
EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 'A', 'B', 'C', 'D', DW_OP_convert,
offs_uchar, DW_OP_stack_value}),
- llvm::HasValue(GetScalar(8, 'A', not_signed)));
+ ExpectScalar(8, 'A', not_signed));
// Also truncate to 8 bits.
EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 'A', 'B', 'C', 'D', DW_OP_convert,
offs_schar, DW_OP_stack_value}),
- llvm::HasValue(GetScalar(8, 'A', is_signed)));
+ ExpectScalar(8, 'A', is_signed));
//
// Errors.
@@ -476,36 +580,41 @@ TEST(DWARFExpression, DW_OP_stack_value) {
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_stack_value}), llvm::Failed());
}
+// This test shows that the dwarf version is used by the expression evaluation.
+// Note that the different behavior tested here is not meant to imply that this
+// is the correct interpretation of dwarf2 vs. dwarf5, but rather it was picked
+// as an easy example that evaluates differently based on the dwarf version.
+TEST(DWARFExpression, dwarf_version) {
+ MockDwarfDelegate dwarf2 = MockDwarfDelegate::Dwarf2();
+ MockDwarfDelegate dwarf5 = MockDwarfDelegate::Dwarf5();
+
+ // In dwarf2 the constant on top of the stack is treated as a value.
+ EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1}, {}, &dwarf2), ExpectScalar(1));
+
+ // In dwarf5 the constant on top of the stack is implicitly converted to an
+ // address.
+ EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1}, {}, &dwarf5),
+ ExpectLoadAddress(1));
+}
+
TEST(DWARFExpression, DW_OP_piece) {
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u, 0x11, 0x22, DW_OP_piece, 2,
DW_OP_const2u, 0x33, 0x44, DW_OP_piece, 2}),
- llvm::HasValue(GetScalar(32, 0x44332211, true)));
+ ExpectHostAddress({0x11, 0x22, 0x33, 0x44}));
EXPECT_THAT_EXPECTED(
Evaluate({DW_OP_piece, 1, DW_OP_const1u, 0xff, DW_OP_piece, 1}),
// Note that the "00" should really be "undef", but we can't
// represent that yet.
- llvm::HasValue(GetScalar(16, 0xff00, true)));
-}
-
-TEST(DWARFExpression, DW_OP_piece_host_address) {
- static const uint8_t expr_data[] = {DW_OP_lit2, DW_OP_stack_value,
- DW_OP_piece, 40};
- llvm::ArrayRef<uint8_t> expr(expr_data, sizeof(expr_data));
- DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle, 4);
+ ExpectHostAddress({0x00, 0xff}));
// This tests if ap_int is extended to the right width.
// expect 40*8 = 320 bits size.
- llvm::Expected<Value> result =
- DWARFExpression::Evaluate(nullptr, nullptr, nullptr, extractor, nullptr,
- lldb::eRegisterKindDWARF, nullptr, nullptr);
- ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
- ASSERT_EQ(result->GetValueType(), Value::ValueType::HostAddress);
- ASSERT_EQ(result->GetBuffer().GetByteSize(), 40ul);
- const uint8_t *data = result->GetBuffer().GetBytes();
- ASSERT_EQ(data[0], 2);
- for (int i = 1; i < 40; i++) {
- ASSERT_EQ(data[i], 0);
- }
+ std::vector<uint8_t> expected_host_buffer(40, 0);
+ expected_host_buffer[0] = 2;
+
+ EXPECT_THAT_EXPECTED(
+ Evaluate({{DW_OP_lit2, DW_OP_stack_value, DW_OP_piece, 40}}),
+ ExpectHostAddress(expected_host_buffer));
}
TEST(DWARFExpression, DW_OP_implicit_value) {
@@ -513,7 +622,7 @@ TEST(DWARFExpression, DW_OP_implicit_value) {
EXPECT_THAT_EXPECTED(
Evaluate({DW_OP_implicit_value, bytes, 0x11, 0x22, 0x33, 0x44}),
- llvm::HasValue(GetScalar(8 * bytes, 0x44332211, true)));
+ ExpectHostAddress({0x11, 0x22, 0x33, 0x44}));
}
TEST(DWARFExpression, DW_OP_unknown) {
@@ -527,72 +636,36 @@ TEST_F(DWARFExpressionMockProcessTest, DW_OP_deref) {
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit0, DW_OP_deref}), llvm::Failed());
// Set up a mock process.
- ArchSpec arch("i386-pc-linux");
- Platform::SetHostPlatform(
- platform_linux::PlatformLinux::CreateInstance(true, &arch));
- lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
- ASSERT_TRUE(debugger_sp);
- lldb::TargetSP target_sp;
- lldb::PlatformSP platform_sp;
- debugger_sp->GetTargetList().CreateTarget(
- *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
- ASSERT_TRUE(target_sp);
- ASSERT_TRUE(target_sp->GetArchitecture().IsValid());
- ASSERT_TRUE(platform_sp);
- lldb::ListenerSP listener_sp(Listener::MakeListener("dummy"));
- lldb::ProcessSP process_sp =
- std::make_shared<MockProcess>(target_sp, listener_sp);
- ASSERT_TRUE(process_sp);
-
- ExecutionContext exe_ctx(process_sp);
+ MockMemory::Map memory = {
+ {{0x4, 4}, {0x4, 0x5, 0x6, 0x7}},
+ };
+ TestContext test_ctx;
+ ASSERT_TRUE(
+ CreateTestContext(&test_ctx, "i386-pc-linux", {}, std::move(memory)));
+
+ ExecutionContext exe_ctx(test_ctx.process_sp);
// Implicit location: *0x4.
EXPECT_THAT_EXPECTED(
Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value}, {}, {}, &exe_ctx),
- llvm::HasValue(GetScalar(32, 0x07060504, false)));
+ ExpectScalar(32, 0x07060504, false));
// Memory location: *(*0x4).
- // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4, DW_OP_deref}, {}, {}, &exe_ctx),
- llvm::HasValue(Scalar(LLDB_INVALID_ADDRESS)));
+ ExpectLoadAddress(0x07060504));
// Memory location: *0x4.
- // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4}, {}, {}, &exe_ctx),
- llvm::HasValue(Scalar(4)));
- // Implicit location: *0x4.
- // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
- EXPECT_THAT_EXPECTED(
- Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value}, {}, {}, &exe_ctx),
- llvm::HasValue(GetScalar(32, 0x07060504, false)));
+ ExpectScalar(Scalar(4)));
}
TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr) {
// Set up a wasm target
- ArchSpec arch("wasm32-unknown-unknown-wasm");
- lldb::PlatformSP host_platform_sp =
- platform_linux::PlatformLinux::CreateInstance(true, &arch);
- ASSERT_TRUE(host_platform_sp);
- Platform::SetHostPlatform(host_platform_sp);
- lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
- ASSERT_TRUE(debugger_sp);
- lldb::TargetSP target_sp;
- lldb::PlatformSP platform_sp;
- debugger_sp->GetTargetList().CreateTarget(*debugger_sp, "", arch,
- lldb_private::eLoadDependentsNo,
- platform_sp, target_sp);
+ TestContext test_ctx;
+ ASSERT_TRUE(CreateTestContext(&test_ctx, "wasm32-unknown-unknown-wasm"));
- ExecutionContext exe_ctx(target_sp, false);
+ ExecutionContext exe_ctx(test_ctx.target_sp, false);
// DW_OP_addr takes a single operand of address size width:
- uint8_t expr[] = {DW_OP_addr, 0x40, 0x0, 0x0, 0x0};
- DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
- /*addr_size*/ 4);
-
- llvm::Expected<Value> result = DWARFExpression::Evaluate(
- &exe_ctx, /*reg_ctx*/ nullptr, /*module_sp*/ {}, extractor,
- /*unit*/ nullptr, lldb::eRegisterKindLLDB,
- /*initial_value_ptr*/ nullptr,
- /*object_address_ptr*/ nullptr);
-
- ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
- ASSERT_EQ(result->GetValueType(), Value::ValueType::LoadAddress);
+ EXPECT_THAT_EXPECTED(
+ Evaluate({DW_OP_addr, 0x40, 0x0, 0x0, 0x0}, {}, {}, &exe_ctx),
+ ExpectLoadAddress(0x40));
}
TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr_index) {
@@ -644,20 +717,9 @@ DWARF:
dwarf_cu->ExtractDIEsIfNeeded();
// Set up a wasm target
- ArchSpec arch("wasm32-unknown-unknown-wasm");
- lldb::PlatformSP host_platform_sp =
- platform_linux::PlatformLinux::CreateInstance(true, &arch);
- ASSERT_TRUE(host_platform_sp);
- Platform::SetHostPlatform(host_platform_sp);
- lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
- ASSERT_TRUE(debugger_sp);
- lldb::TargetSP target_sp;
- lldb::PlatformSP platform_sp;
- debugger_sp->GetTargetList().CreateTarget(*debugger_sp, "", arch,
- lldb_private::eLoadDependentsNo,
- platform_sp, target_sp);
-
- ExecutionContext exe_ctx(target_sp, false);
+ TestContext test_ctx;
+ ASSERT_TRUE(CreateTestContext(&test_ctx, "wasm32-unknown-unknown-wasm"));
+ ExecutionContext exe_ctx(test_ctx.target_sp, false);
auto evaluate = [&](DWARFExpression &expr) -> llvm::Expected<Value> {
DataExtractor extractor;
@@ -676,15 +738,11 @@ DWARF:
DWARFExpression expr(extractor);
llvm::Expected<Value> result = evaluate(expr);
- ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
- ASSERT_EQ(result->GetValueType(), Value::ValueType::LoadAddress);
- ASSERT_EQ(result->GetScalar().UInt(), 0x5678u);
+ EXPECT_THAT_EXPECTED(result, ExpectLoadAddress(0x5678u));
ASSERT_TRUE(expr.Update_DW_OP_addr(dwarf_cu, 0xdeadbeef));
result = evaluate(expr);
- ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
- ASSERT_EQ(result->GetValueType(), Value::ValueType::LoadAddress);
- ASSERT_EQ(result->GetScalar().UInt(), 0xdeadbeefu);
+ EXPECT_THAT_EXPECTED(result, ExpectLoadAddress(0xdeadbeefu));
}
class CustomSymbolFileDWARF : public SymbolFileDWARF {
@@ -778,11 +836,12 @@ static auto testExpressionVendorExtensions(lldb::ModuleSP module_sp,
RegisterContext *reg_ctx) {
// Test that expression extensions can be evaluated, for example
// DW_OP_WASM_location which is not currently handled by DWARFExpression:
- EXPECT_THAT_EXPECTED(Evaluate({DW_OP_WASM_location, 0x03, // WASM_GLOBAL:0x03
- 0x04, 0x00, 0x00, // index:u32
- 0x00, DW_OP_stack_value},
- module_sp, &dwarf_unit, nullptr, reg_ctx),
- llvm::HasValue(GetScalar(32, 42, false)));
+ EXPECT_THAT_EXPECTED(
+ Evaluate({DW_OP_WASM_location, 0x03, // WASM_GLOBAL:0x03
+ 0x04, 0x00, 0x00, // index:u32
+ 0x00, DW_OP_stack_value},
+ module_sp, &dwarf_unit, nullptr, reg_ctx),
+ ExpectScalar(32, 42, false, Value::ContextType::RegisterInfo));
// Test that searches for opcodes work in the presence of extensions:
uint8_t expr[] = {DW_OP_WASM_location, 0x03, 0x04, 0x00, 0x00, 0x00,
@@ -883,28 +942,10 @@ Sections:
subsystems;
// Set up a wasm target.
- ArchSpec arch("wasm32-unknown-unknown-wasm");
- lldb::PlatformSP host_platform_sp =
- platform_linux::PlatformLinux::CreateInstance(true, &arch);
- ASSERT_TRUE(host_platform_sp);
- Platform::SetHostPlatform(host_platform_sp);
- lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
- ASSERT_TRUE(debugger_sp);
- lldb::TargetSP target_sp;
- lldb::PlatformSP platform_sp;
- debugger_sp->GetTargetList().CreateTarget(*debugger_sp, "", arch,
- lldb_private::eLoadDependentsNo,
- platform_sp, target_sp);
- // Set up a mock process and thread.
- lldb::ListenerSP listener_sp(Listener::MakeListener("dummy"));
- lldb::ProcessSP process_sp =
- std::make_shared<MockProcess>(target_sp, listener_sp);
- ASSERT_TRUE(process_sp);
- MockThread thread(*process_sp);
+ TestContext test_ctx;
const uint32_t kExpectedValue = 42;
- lldb::RegisterContextSP reg_ctx_sp = std::make_shared<MockRegisterContext>(
- thread, RegisterValue(kExpectedValue));
- thread.SetRegisterContext(reg_ctx_sp);
+ ASSERT_TRUE(CreateTestContext(&test_ctx, "wasm32-unknown-unknown-wasm",
+ RegisterValue(kExpectedValue)));
llvm::Expected<TestFile> file = TestFile::fromYaml(yamldata);
EXPECT_THAT_EXPECTED(file, llvm::Succeeded());
@@ -913,7 +954,8 @@ Sections:
SymbolFileWasm sym_file_wasm(obj_file_sp, nullptr);
auto *dwarf_unit = sym_file_wasm.DebugInfo().GetUnitAtIndex(0);
- testExpressionVendorExtensions(module_sp, *dwarf_unit, reg_ctx_sp.get());
+ testExpressionVendorExtensions(module_sp, *dwarf_unit,
+ test_ctx.reg_ctx_sp.get());
}
TEST(DWARFExpression, ExtensionsSplitSymbols) {
@@ -1082,28 +1124,10 @@ Sections:
subsystems;
// Set up a wasm target.
- ArchSpec arch("wasm32-unknown-unknown-wasm");
- lldb::PlatformSP host_platform_sp =
- platform_linux::PlatformLinux::CreateInstance(true, &arch);
- ASSERT_TRUE(host_platform_sp);
- Platform::SetHostPlatform(host_platform_sp);
- lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
- ASSERT_TRUE(debugger_sp);
- lldb::TargetSP target_sp;
- lldb::PlatformSP platform_sp;
- debugger_sp->GetTargetList().CreateTarget(*debugger_sp, "", arch,
- lldb_private::eLoadDependentsNo,
- platform_sp, target_sp);
- // Set up a mock process and thread.
- lldb::ListenerSP listener_sp(Listener::MakeListener("dummy"));
- lldb::ProcessSP process_sp =
- std::make_shared<MockProcess>(target_sp, listener_sp);
- ASSERT_TRUE(process_sp);
- MockThread thread(*process_sp);
+ TestContext test_ctx;
const uint32_t kExpectedValue = 42;
- lldb::RegisterContextSP reg_ctx_sp = std::make_shared<MockRegisterContext>(
- thread, RegisterValue(kExpectedValue));
- thread.SetRegisterContext(reg_ctx_sp);
+ ASSERT_TRUE(CreateTestContext(&test_ctx, "wasm32-unknown-unknown-wasm",
+ RegisterValue(kExpectedValue)));
llvm::Expected<TestFile> skeleton_file =
TestFile::fromYaml(skeleton_yamldata);
@@ -1119,7 +1143,8 @@ Sections:
SymbolFileWasm sym_file_wasm(obj_file_sp, nullptr);
auto *dwarf_unit = sym_file_wasm.DebugInfo().GetUnitAtIndex(0);
- testExpressionVendorExtensions(sym_module_sp, *dwarf_unit, reg_ctx_sp.get());
+ testExpressionVendorExtensions(sym_module_sp, *dwarf_unit,
+ test_ctx.reg_ctx_sp.get());
}
TEST_F(DWARFExpressionMockProcessTest, DW_OP_piece_file_addr) {
@@ -1128,66 +1153,23 @@ TEST_F(DWARFExpressionMockProcessTest, DW_OP_piece_file_addr) {
using ::testing::Return;
// Set up a mock process.
- ArchSpec arch("i386-pc-linux");
- Platform::SetHostPlatform(
- platform_linux::PlatformLinux::CreateInstance(true, &arch));
- lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
- ASSERT_TRUE(debugger_sp);
- lldb::PlatformSP platform_sp;
- auto target_sp =
- std::make_shared<MockTarget>(*debugger_sp, arch, platform_sp);
- ASSERT_TRUE(target_sp);
- ASSERT_TRUE(target_sp->GetArchitecture().IsValid());
-
- EXPECT_CALL(*target_sp, ReadMemory(0x40, 1))
- .WillOnce(Return(ByMove(std::vector<uint8_t>{0x11})));
- EXPECT_CALL(*target_sp, ReadMemory(0x50, 1))
- .WillOnce(Return(ByMove(std::vector<uint8_t>{0x22})));
+ TestContext test_ctx;
+ MockMemory::Map memory = {
+ {{0x40, 1}, {0x11}},
+ {{0x50, 1}, {0x22}},
+ };
+ ASSERT_TRUE(
+ CreateTestContext(&test_ctx, "i386-pc-linux", {}, {}, std::move(memory)));
+ ASSERT_TRUE(test_ctx.target_sp->GetArchitecture().IsValid());
- ExecutionContext exe_ctx(static_cast<lldb::TargetSP>(target_sp), false);
+ ExecutionContext exe_ctx(test_ctx.target_sp, false);
uint8_t expr[] = {DW_OP_addr, 0x40, 0x0, 0x0, 0x0, DW_OP_piece, 1,
DW_OP_addr, 0x50, 0x0, 0x0, 0x0, DW_OP_piece, 1};
- DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
- /*addr_size=*/4);
- llvm::Expected<Value> result = DWARFExpression::Evaluate(
- &exe_ctx, /*reg_ctx=*/nullptr, /*module_sp=*/{}, extractor,
- /*unit=*/nullptr, lldb::eRegisterKindLLDB,
- /*initial_value_ptr=*/nullptr,
- /*object_address_ptr=*/nullptr);
-
- ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
- ASSERT_EQ(result->GetValueType(), Value::ValueType::HostAddress);
- ASSERT_THAT(result->GetBuffer().GetData(), ElementsAre(0x11, 0x22));
+ EXPECT_THAT_EXPECTED(Evaluate(expr, {}, {}, &exe_ctx),
+ ExpectHostAddress({0x11, 0x22}));
}
-/// A Process whose `ReadMemory` override queries a DenseMap.
-struct MockProcessWithMemRead : Process {
- using addr_t = lldb::addr_t;
-
- llvm::DenseMap<addr_t, addr_t> memory_map;
-
- MockProcessWithMemRead(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
- llvm::DenseMap<addr_t, addr_t> &&memory_map)
- : Process(target_sp, listener_sp), memory_map(memory_map) {}
- size_t DoReadMemory(addr_t vm_addr, void *buf, size_t size,
- Status &error) override {
- assert(memory_map.contains(vm_addr));
- assert(size == sizeof(addr_t));
- *reinterpret_cast<addr_t *>(buf) = memory_map[vm_addr];
- return sizeof(addr_t);
- }
- size_t ReadMemory(addr_t addr, void *buf, size_t size,
- Status &status) override {
- return DoReadMemory(addr, buf, size, status);
- }
- bool CanDebug(lldb::TargetSP, bool) override { return true; }
- Status DoDestroy() override { return Status(); }
- llvm::StringRef GetPluginName() override { return ""; }
- void RefreshStateAfterStop() override {}
- bool DoUpdateThreadList(ThreadList &, ThreadList &) override { return false; }
-};
-
class DWARFExpressionMockProcessTestWithAArch
: public DWARFExpressionMockProcessTest {
public:
@@ -1213,43 +1195,129 @@ public:
/// The expression DW_OP_breg22, 0, DW_OP_deref should produce that same value,
/// without clearing the top byte 0xff.
TEST_F(DWARFExpressionMockProcessTestWithAArch, DW_op_deref_no_ptr_fixing) {
- llvm::DenseMap<lldb::addr_t, lldb::addr_t> memory;
constexpr lldb::addr_t expected_value = ((0xffULL) << 56) | 0xabcdefULL;
constexpr lldb::addr_t addr = 42;
- memory[addr] = expected_value;
+ MockMemory::Map memory = {
+ {{addr, sizeof(addr)}, {0xef, 0xcd, 0xab, 0x00, 0x00, 0x00, 0x00, 0xff}}};
- PlatformTargetDebugger test_setup = CreateTarget();
- lldb::ProcessSP process_sp = std::make_shared<MockProcessWithMemRead>(
- test_setup.target_sp, Listener::MakeListener("dummy"), std::move(memory));
- auto thread = std::make_shared<MockThread>(*process_sp);
- lldb::RegisterContextSP reg_ctx_sp =
- std::make_shared<MockRegisterContext>(*thread, RegisterValue(addr));
- thread->SetRegisterContext(reg_ctx_sp);
- process_sp->GetThreadList().AddThread(thread);
+ TestContext test_ctx;
+ ASSERT_TRUE(CreateTestContext(&test_ctx, "aarch64-pc-linux",
+ RegisterValue(addr), std::move(memory)));
auto evaluate_expr = [&](auto &expr_data) {
- DataExtractor extractor(expr_data, sizeof(expr_data),
- lldb::eByteOrderLittle,
- /*addr_size*/ 8);
- DWARFExpression expr(extractor);
-
- ExecutionContext exe_ctx(process_sp);
- llvm::Expected<Value> result = DWARFExpression::Evaluate(
- &exe_ctx, reg_ctx_sp.get(), /*module_sp*/ nullptr, extractor,
- /*unit*/ nullptr, lldb::eRegisterKindLLDB,
- /*initial_value_ptr=*/nullptr,
- /*object_address_ptr=*/nullptr);
- return result;
+ ExecutionContext exe_ctx(test_ctx.process_sp);
+ return Evaluate(expr_data, {}, {}, &exe_ctx, test_ctx.reg_ctx_sp.get());
};
uint8_t expr_reg[] = {DW_OP_breg22, 0};
llvm::Expected<Value> result_reg = evaluate_expr(expr_reg);
- ASSERT_THAT_EXPECTED(result_reg, llvm::Succeeded());
- ASSERT_EQ(result_reg->GetValueType(), Value::ValueType::LoadAddress);
- ASSERT_EQ(result_reg->GetScalar().ULongLong(), addr);
+ EXPECT_THAT_EXPECTED(result_reg, ExpectLoadAddress(addr));
uint8_t expr_deref[] = {DW_OP_breg22, 0, DW_OP_deref};
llvm::Expected<Value> result_deref = evaluate_expr(expr_deref);
- ASSERT_THAT_EXPECTED(result_deref, llvm::Succeeded());
- ASSERT_EQ(result_deref->GetScalar().ULongLong(), expected_value);
+ EXPECT_THAT_EXPECTED(result_deref, ExpectLoadAddress(expected_value));
+}
+
+TEST_F(DWARFExpressionMockProcessTest, deref_register) {
+ TestContext test_ctx;
+ constexpr uint32_t reg_r0 = 0x504;
+ MockMemory::Map memory = {
+ {{0x004, 4}, {0x1, 0x2, 0x3, 0x4}},
+ {{0x504, 4}, {0xa, 0xb, 0xc, 0xd}},
+ {{0x505, 4}, {0x5, 0x6, 0x7, 0x8}},
+ };
+ ASSERT_TRUE(CreateTestContext(&test_ctx, "i386-pc-linux",
+ RegisterValue(reg_r0), memory, memory));
+
+ ExecutionContext exe_ctx(test_ctx.process_sp);
+ MockDwarfDelegate delegate = MockDwarfDelegate::Dwarf5();
+ auto Eval = [&](llvm::ArrayRef<uint8_t> expr_data) {
+ ExecutionContext exe_ctx(test_ctx.process_sp);
+ return Evaluate(expr_data, {}, &delegate, &exe_ctx,
+ test_ctx.reg_ctx_sp.get());
+ };
+
+ // Reads from the register r0.
+ // Sets the context to RegisterInfo so we know this is a register location.
+ EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0}),
+ ExpectScalar(reg_r0, Value::ContextType::RegisterInfo));
+
+ // Reads from the location(register r0).
+ // Clears the context so we know this is a value not a location.
+ EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0, DW_OP_deref}),
+ ExpectLoadAddress(reg_r0, Value::ContextType::Invalid));
+
+ // Reads from the location(register r0) and adds the value to the host buffer.
+ // The evaluator should implicitly convert it to a memory location when
+ // added to a composite value and should add the contents of memory[r0]
+ // to the host buffer.
+ EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0, DW_OP_deref, DW_OP_piece, 4}),
+ ExpectHostAddress({0xa, 0xb, 0xc, 0xd}));
+
+ // Reads from the location(register r0) and truncates the value to one byte.
+ // Clears the context so we know this is a value not a location.
+ EXPECT_THAT_EXPECTED(
+ Eval({DW_OP_reg0, DW_OP_deref_size, 1}),
+ ExpectLoadAddress(reg_r0 & 0xff, Value::ContextType::Invalid));
+
+ // Reads from the location(register r0) and truncates to one byte then adds
+ // the value to the host buffer. The evaluator should implicitly convert it to
+ // a memory location when added to a composite value and should add the
+ // contents of memory[r0 & 0xff] to the host buffer.
+ EXPECT_THAT_EXPECTED(Eval({DW_OP_reg0, DW_OP_deref_size, 1, DW_OP_piece, 4}),
+ ExpectHostAddress({0x1, 0x2, 0x3, 0x4}));
+
+ // Reads from the register r0 + 1.
+ EXPECT_THAT_EXPECTED(
+ Eval({DW_OP_breg0, 1}),
+ ExpectLoadAddress(reg_r0 + 1, Value::ContextType::Invalid));
+
+ // Reads from address r0 + 1, which contains the bytes [5,6,7,8].
+ EXPECT_THAT_EXPECTED(
+ Eval({DW_OP_breg0, 1, DW_OP_deref}),
+ ExpectLoadAddress(0x08070605, Value::ContextType::Invalid));
+}
+
+TEST_F(DWARFExpressionMockProcessTest, deref_implicit_value) {
+ TestContext test_ctx;
+ MockMemory::Map memory = {
+ {{0x4, 1}, {0x1}},
+ {{0x4, 4}, {0x1, 0x2, 0x3, 0x4}},
+ };
+ ASSERT_TRUE(CreateTestContext(&test_ctx, "i386-pc-linux", {}, memory));
+
+ ExecutionContext exe_ctx(test_ctx.process_sp);
+ MockDwarfDelegate delegate = MockDwarfDelegate::Dwarf5();
+ auto Eval = [&](llvm::ArrayRef<uint8_t> expr_data) {
+ ExecutionContext exe_ctx(test_ctx.process_sp);
+ return Evaluate(expr_data, {}, &delegate, &exe_ctx,
+ test_ctx.reg_ctx_sp.get());
+ };
+
+ // Creates an implicit location with a value of 4.
+ EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_stack_value}),
+ ExpectScalar(0x4));
+
+ // Creates an implicit location with a value of 4. The deref reads the value
+ // out of the location and implicitly converts it to a load address.
+ EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_stack_value, DW_OP_deref}),
+ ExpectLoadAddress(0x4));
+
+ // Creates an implicit location with a value of 0x504 (uleb128(0x504) =
+ // 0xa84). The deref reads the low byte out of the location and implicitly
+ // converts it to a load address.
+ EXPECT_THAT_EXPECTED(
+ Eval({DW_OP_constu, 0x84, 0xa, DW_OP_stack_value, DW_OP_deref_size, 1}),
+ ExpectLoadAddress(0x4));
+
+ // The tests below are similar to the ones above, but there is no implicit
+ // location created by a stack_value operation. They are provided here as a
+ // reference to contrast with the above tests.
+ EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4}), ExpectLoadAddress(0x4));
+
+ EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_deref}),
+ ExpectLoadAddress(0x04030201));
+
+ EXPECT_THAT_EXPECTED(Eval({DW_OP_lit4, DW_OP_deref_size, 1}),
+ ExpectLoadAddress(0x01));
}
diff --git a/lldb/unittests/Expression/ValueMatcher.cpp b/lldb/unittests/Expression/ValueMatcher.cpp
new file mode 100644
index 0000000..ee7ccae
--- /dev/null
+++ b/lldb/unittests/Expression/ValueMatcher.cpp
@@ -0,0 +1,205 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ValueMatcher.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InterleavedRange.h"
+#include "llvm/Support/raw_os_ostream.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace lldb_private;
+
+static void FormatValueDetails(llvm::raw_ostream &os,
+ Value::ValueType value_type,
+ Value::ContextType context_type,
+ const Scalar &scalar,
+ llvm::ArrayRef<uint8_t> buffer_data) {
+ os << "Value(";
+ os << "value_type=" << Value::GetValueTypeAsCString(value_type);
+ os << ", context_type=" << Value::GetContextTypeAsCString(context_type);
+
+ if (value_type == Value::ValueType::HostAddress) {
+ auto bytes_to_print = buffer_data.take_front(16);
+ os << ", buffer=[";
+ llvm::interleave(
+ bytes_to_print,
+ [&](uint8_t byte) {
+ os << llvm::format("%02x", static_cast<unsigned>(byte));
+ },
+ [&]() { os << " "; });
+ if (buffer_data.size() > 16)
+ os << " ...";
+ os << "] (" << buffer_data.size() << " bytes)";
+ } else {
+ os << ", value=" << scalar;
+ }
+ os << ")";
+}
+
+void lldb_private::PrintTo(const Value &val, std::ostream *os) {
+ if (!os)
+ return;
+
+ llvm::raw_os_ostream raw_os(*os);
+ FormatValueDetails(raw_os, val.GetValueType(), val.GetContextType(),
+ val.GetScalar(), val.GetBuffer().GetData());
+}
+
+bool ValueMatcher::MatchAndExplain(const Value &val,
+ std::ostream *stream) const {
+ if (stream) {
+ llvm::raw_os_ostream os(*stream);
+ return MatchAndExplainImpl(val, os);
+ }
+
+ llvm::raw_null_ostream os;
+ return MatchAndExplainImpl(val, os);
+}
+
+// Match the provided value and explain any mismatches using
+// the raw_ostream. We use the llvm::raw_ostream here to simplify the formatting
+// of Scalar values which already know how to print themselves to that stream.
+bool ValueMatcher::MatchAndExplainImpl(const Value &val,
+ llvm::raw_ostream &os) const {
+ if (val.GetValueType() != m_value_type) {
+ os << "value_type mismatch: expected "
+ << Value::GetValueTypeAsCString(m_value_type) << ", got "
+ << Value::GetValueTypeAsCString(val.GetValueType()) << " ";
+ return false;
+ }
+
+ if (val.GetContextType() != m_context_type) {
+ os << "context_type mismatch: expected "
+ << Value::GetContextTypeAsCString(m_context_type) << ", got "
+ << Value::GetContextTypeAsCString(val.GetContextType()) << " ";
+ return false;
+ }
+
+ if (m_value_type == Value::ValueType::HostAddress) {
+ const DataBufferHeap &buffer = val.GetBuffer();
+ const size_t buffer_size = buffer.GetByteSize();
+ if (buffer_size != m_expected_bytes.size()) {
+ os << "buffer size mismatch: expected " << m_expected_bytes.size()
+ << ", got " << buffer_size << " ";
+ return false;
+ }
+
+ const uint8_t *data = buffer.GetBytes();
+ for (size_t i = 0; i < buffer_size; ++i) {
+ if (data[i] != m_expected_bytes[i]) {
+ os << "byte mismatch at index " << i << ": expected "
+ << llvm::format("0x%02x", static_cast<unsigned>(m_expected_bytes[i]))
+ << ", got " << llvm::format("0x%02x", static_cast<unsigned>(data[i]))
+ << " ";
+ return false;
+ }
+ }
+ } else {
+ // For Scalar, FileAddress, and LoadAddress compare m_value.
+ const Scalar &actual_scalar = val.GetScalar();
+ if (actual_scalar != m_expected_scalar) {
+ os << "scalar value mismatch: expected " << m_expected_scalar << ", got "
+ << actual_scalar;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ValueMatcher::DescribeTo(std::ostream *os) const {
+ if (!os)
+ return;
+ llvm::raw_os_ostream raw_os(*os);
+ FormatValueDetails(raw_os, m_value_type, m_context_type, m_expected_scalar,
+ m_expected_bytes);
+}
+
+void ValueMatcher::DescribeNegationTo(std::ostream *os) const {
+ if (!os)
+ return;
+ *os << "value does not match";
+}
+
+testing::Matcher<Value>
+lldb_private::MatchScalarValue(Value::ValueType value_type,
+ const Scalar &expected_scalar,
+ Value::ContextType context_type) {
+ return ValueMatcher(value_type, expected_scalar, context_type);
+}
+
+testing::Matcher<Value>
+lldb_private::MatchHostValue(Value::ValueType value_type,
+ const std::vector<uint8_t> &expected_bytes,
+ Value::ContextType context_type) {
+ return ValueMatcher(value_type, expected_bytes, context_type);
+}
+
+testing::Matcher<Value>
+lldb_private::IsScalar(const Scalar &expected_scalar,
+ Value::ContextType context_type) {
+ return MatchScalarValue(Value::ValueType::Scalar, expected_scalar,
+ context_type);
+}
+
+testing::Matcher<Value>
+lldb_private::IsLoadAddress(const Scalar &expected_address,
+ Value::ContextType context_type) {
+ return MatchScalarValue(Value::ValueType::LoadAddress, expected_address,
+ context_type);
+}
+
+testing::Matcher<Value>
+lldb_private::IsFileAddress(const Scalar &expected_address,
+ Value::ContextType context_type) {
+ return MatchScalarValue(Value::ValueType::FileAddress, expected_address,
+ context_type);
+}
+
+testing::Matcher<Value>
+lldb_private::IsHostValue(const std::vector<uint8_t> &expected_bytes,
+ Value::ContextType context_type) {
+ return MatchHostValue(Value::ValueType::HostAddress, expected_bytes,
+ context_type);
+}
+
+Scalar lldb_private::GetScalar(unsigned bits, uint64_t value, bool sign) {
+ Scalar scalar(value);
+ scalar.TruncOrExtendTo(bits, sign);
+ return scalar;
+}
+
+llvm::detail::ValueMatchesPoly<testing::Matcher<Value>>
+lldb_private::ExpectScalar(const Scalar &expected_scalar,
+ Value::ContextType context_type) {
+ return llvm::HasValue(IsScalar(expected_scalar, context_type));
+}
+
+llvm::detail::ValueMatchesPoly<testing::Matcher<Value>>
+lldb_private::ExpectScalar(unsigned bits, uint64_t value, bool sign,
+ Value::ContextType context_type) {
+ return ExpectScalar(GetScalar(bits, value, sign), context_type);
+}
+
+llvm::detail::ValueMatchesPoly<testing::Matcher<Value>>
+lldb_private::ExpectLoadAddress(const Scalar &expected_address,
+ Value::ContextType context_type) {
+ return llvm::HasValue(IsLoadAddress(expected_address, context_type));
+}
+
+llvm::detail::ValueMatchesPoly<testing::Matcher<Value>>
+lldb_private::ExpectFileAddress(const Scalar &expected_address,
+ Value::ContextType context_type) {
+ return llvm::HasValue(IsFileAddress(expected_address, context_type));
+}
+
+llvm::detail::ValueMatchesPoly<testing::Matcher<Value>>
+lldb_private::ExpectHostAddress(const std::vector<uint8_t> &expected_bytes,
+ Value::ContextType context_type) {
+ return llvm::HasValue(IsHostValue(expected_bytes, context_type));
+}
diff --git a/lldb/unittests/Expression/ValueMatcher.h b/lldb/unittests/Expression/ValueMatcher.h
new file mode 100644
index 0000000..3ca7b15
--- /dev/null
+++ b/lldb/unittests/Expression/ValueMatcher.h
@@ -0,0 +1,155 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file contains the definition of the ValueMatcher class which is a used
+/// to match lldb_private::Value in gtest assert/expect macros. It also contains
+/// several helper functions to create matchers for common Value types.
+///
+/// The ValueMatcher class was created using the gtest guide found here:
+// https://google.github.io/googletest/gmock_cook_book.html#writing-new-monomorphic-matchers
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UNITTESTS_EXPRESSION_VALUEMATCHER_H
+#define LLDB_UNITTESTS_EXPRESSION_VALUEMATCHER_H
+
+#include "lldb/Core/Value.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+#include <cstdint>
+#include <vector>
+
+namespace lldb_private {
+
+/// Custom printer for Value objects to make test failures more readable.
+void PrintTo(const Value &val, std::ostream *os);
+
+/// Custom matcher for Value.
+///
+/// It matches against an expected value_type, and context_type.
+/// For HostAddress value types it will match the expected contents of
+/// the host buffer. For other value types it matches against an expected
+/// scalar value.
+class ValueMatcher {
+public:
+ ValueMatcher(Value::ValueType value_type, const Scalar &expected_scalar,
+ Value::ContextType context_type)
+ : m_value_type(value_type), m_context_type(context_type),
+ m_expected_scalar(expected_scalar) {
+ assert(value_type == Value::ValueType::Scalar ||
+ value_type == Value::ValueType::FileAddress ||
+ value_type == Value::ValueType::LoadAddress);
+ }
+
+ ValueMatcher(Value::ValueType value_type,
+ const std::vector<uint8_t> &expected_bytes,
+ Value::ContextType context_type)
+ : m_value_type(value_type), m_context_type(context_type),
+ m_expected_bytes(expected_bytes) {
+ assert(value_type == Value::ValueType::HostAddress);
+ }
+
+ // Typedef to hook into the gtest matcher machinery.
+ using is_gtest_matcher = void;
+
+ bool MatchAndExplain(const Value &val, std::ostream *os) const;
+
+ void DescribeTo(std::ostream *os) const;
+
+ void DescribeNegationTo(std::ostream *os) const;
+
+private:
+ Value::ValueType m_value_type = Value::ValueType::Invalid;
+ Value::ContextType m_context_type = Value::ContextType::Invalid;
+ Scalar m_expected_scalar;
+ std::vector<uint8_t> m_expected_bytes;
+
+ bool MatchAndExplainImpl(const Value &val, llvm::raw_ostream &os) const;
+};
+
+/// Matcher for Value with Scalar, FileAddress, or LoadAddress types.
+/// Use with llvm::HasValue() to match Expected<Value>:
+/// EXPECT_THAT_EXPECTED(result, llvm::HasValue(MatchScalarValue(...)));
+testing::Matcher<Value> MatchScalarValue(Value::ValueType value_type,
+ const Scalar &expected_scalar,
+ Value::ContextType context_type);
+
+/// Matcher for Value with HostAddress type.
+/// Use with llvm::HasValue() to match Expected<Value>:
+/// EXPECT_THAT_EXPECTED(result, llvm::HasValue(MatchHostValue(...)));
+testing::Matcher<Value>
+MatchHostValue(Value::ValueType value_type,
+ const std::vector<uint8_t> &expected_bytes,
+ Value::ContextType context_type);
+
+/// Helper to match a Scalar value and context type.
+/// Use with llvm::HasValue() to match Expected<Value>:
+/// EXPECT_THAT_EXPECTED(result, llvm::HasValue(IsScalar(42)));
+testing::Matcher<Value> IsScalar(const Scalar &expected_scalar,
+ Value::ContextType context_type);
+
+/// Helper to match a LoadAddress value and context type.
+/// Use with llvm::HasValue() to match Expected<Value>:
+/// EXPECT_THAT_EXPECTED(result, llvm::HasValue(IsLoadAddress(0x1000)));
+testing::Matcher<Value> IsLoadAddress(const Scalar &expected_address,
+ Value::ContextType context_type);
+
+/// Helper to match a FileAddress value and context type.
+/// Use with llvm::HasValue() to match Expected<Value>:
+/// EXPECT_THAT_EXPECTED(result, llvm::HasValue(IsFileAddress(Scalar(0x1000))));
+testing::Matcher<Value> IsFileAddress(const Scalar &expected_address,
+ Value::ContextType context_type);
+
+/// Helper to match a HostAddress value and context type.
+/// Use with llvm::HasValue() to match Expected<Value>:
+/// EXPECT_THAT_EXPECTED(result, llvm::HasValue(IsHostValue({0x11, 0x22})));
+testing::Matcher<Value> IsHostValue(const std::vector<uint8_t> &expected_bytes,
+ Value::ContextType context_type);
+
+/// Helper to create a scalar because Scalar's operator==() is really picky.
+Scalar GetScalar(unsigned bits, uint64_t value, bool sign);
+
+/// Helper that combines IsScalar with llvm::HasValue for Expected<Value>.
+/// Use it on an Expected<Value> like this:
+/// EXPECT_THAT_EXPECTED(result, ExpectScalar(42));
+llvm::detail::ValueMatchesPoly<testing::Matcher<Value>>
+ExpectScalar(const Scalar &expected_scalar,
+ Value::ContextType context_type = Value::ContextType::Invalid);
+
+/// Helper that combines GetScalar with ExpectScalar to get a precise scalar.
+/// Use it on an Expected<Value> like this:
+/// EXPECT_THAT_EXPECTED(result, ExpectScalar(8, 42, true));
+llvm::detail::ValueMatchesPoly<testing::Matcher<Value>>
+ExpectScalar(unsigned bits, uint64_t value, bool sign,
+ Value::ContextType context_type = Value::ContextType::Invalid);
+
+/// Helper that combines IsLoadAddress with llvm::HasValue for Expected<Value>.
+/// Use it on an Expected<Value> like this:
+/// EXPECT_THAT_EXPECTED(result, ExpectLoadAddress(0x1000));
+llvm::detail::ValueMatchesPoly<testing::Matcher<Value>> ExpectLoadAddress(
+ const Scalar &expected_address,
+ Value::ContextType context_type = Value::ContextType::Invalid);
+
+/// Helper that combines IsFileAddress with llvm::HasValue for Expected<Value>.
+/// Use it on an Expected<Value> like this:
+/// EXPECT_THAT_EXPECTED(result, ExpectFileAddress(Scalar(0x2000)));
+llvm::detail::ValueMatchesPoly<testing::Matcher<Value>> ExpectFileAddress(
+ const Scalar &expected_address,
+ Value::ContextType context_type = Value::ContextType::Invalid);
+
+/// Helper that combines IsHostValue with llvm::HasValue for Expected<Value>.
+/// Use it on an Expected<Value> like this:
+/// EXPECT_THAT_EXPECTED(result, ExpectHostAddress({0x11, 0x22}));
+llvm::detail::ValueMatchesPoly<testing::Matcher<Value>> ExpectHostAddress(
+ const std::vector<uint8_t> &expected_bytes,
+ Value::ContextType context_type = Value::ContextType::Invalid);
+
+} // namespace lldb_private
+
+#endif // LLDB_UNITTESTS_EXPRESSION_VALUEMATCHER_H
diff --git a/lldb/unittests/Host/FileTest.cpp b/lldb/unittests/Host/FileTest.cpp
index d973d19..85697c4 100644
--- a/lldb/unittests/Host/FileTest.cpp
+++ b/lldb/unittests/Host/FileTest.cpp
@@ -8,6 +8,7 @@
#include "lldb/Host/File.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Path.h"
@@ -35,7 +36,7 @@ TEST(File, GetWaitableHandleFileno) {
FILE *stream = fdopen(fd, "r");
ASSERT_TRUE(stream);
- NativeFile file(stream, true);
+ NativeFile file(stream, File::eOpenOptionReadWrite, true);
#ifdef _WIN32
EXPECT_EQ(file.GetWaitableHandle(), (HANDLE)_get_osfhandle(fd));
#else
@@ -67,3 +68,22 @@ TEST(File, GetStreamFromDescriptor) {
EXPECT_EQ(file.GetWaitableHandle(), (file_t)fd);
#endif
}
+
+TEST(File, ReadOnlyModeNotWritable) {
+ const auto *Info = testing::UnitTest::GetInstance()->current_test_info();
+ llvm::SmallString<128> name;
+ int fd;
+ llvm::sys::fs::createTemporaryFile(llvm::Twine(Info->test_case_name()) + "-" +
+ Info->name(),
+ "test", fd, name);
+
+ llvm::FileRemover remover(name);
+ ASSERT_GE(fd, 0);
+
+ NativeFile file(fd, File::eOpenOptionReadOnly, true);
+ ASSERT_TRUE(file.IsValid());
+ llvm::StringLiteral buf = "Hello World";
+ size_t bytes_written = buf.size();
+ Status error = file.Write(buf.data(), bytes_written);
+ EXPECT_EQ(error.Fail(), true);
+}
diff --git a/lldb/unittests/Host/common/CMakeLists.txt b/lldb/unittests/Host/common/CMakeLists.txt
index 2934e6f0..8aa2dfb 100644
--- a/lldb/unittests/Host/common/CMakeLists.txt
+++ b/lldb/unittests/Host/common/CMakeLists.txt
@@ -1,4 +1,5 @@
set (FILES
+ DiagnosticsRenderingTest.cpp
ZipFileResolverTest.cpp
)
diff --git a/lldb/unittests/Utility/DiagnosticsRenderingTest.cpp b/lldb/unittests/Host/common/DiagnosticsRenderingTest.cpp
index 4e5e0bb..851b478 100644
--- a/lldb/unittests/Utility/DiagnosticsRenderingTest.cpp
+++ b/lldb/unittests/Host/common/DiagnosticsRenderingTest.cpp
@@ -1,4 +1,4 @@
-#include "lldb/Utility/DiagnosticsRendering.h"
+#include "lldb/Host/common/DiagnosticsRendering.h"
#include "lldb/Utility/StreamString.h"
#include "gtest/gtest.h"
diff --git a/lldb/unittests/Interpreter/TestOptions.cpp b/lldb/unittests/Interpreter/TestOptions.cpp
index 93474e3..e23dc0d 100644
--- a/lldb/unittests/Interpreter/TestOptions.cpp
+++ b/lldb/unittests/Interpreter/TestOptions.cpp
@@ -17,13 +17,13 @@ TEST(OptionsTest, CreateOptionParsingError) {
ASSERT_THAT_ERROR(
CreateOptionParsingError("yippee", 'f', "fun",
"unable to convert 'yippee' to boolean"),
- llvm::FailedWithMessage("Invalid value ('yippee') for -f (fun): unable "
+ llvm::FailedWithMessage("invalid value ('yippee') for -f (fun): unable "
"to convert 'yippee' to boolean"));
ASSERT_THAT_ERROR(
CreateOptionParsingError("52", 'b', "bean-count"),
- llvm::FailedWithMessage("Invalid value ('52') for -b (bean-count)"));
+ llvm::FailedWithMessage("invalid value ('52') for -b (bean-count)"));
ASSERT_THAT_ERROR(CreateOptionParsingError("c", 'm'),
- llvm::FailedWithMessage("Invalid value ('c') for -m"));
+ llvm::FailedWithMessage("invalid value ('c') for -m"));
}
diff --git a/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp b/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp
index 8c36f54..41df35f 100644
--- a/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp
+++ b/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp
@@ -30,6 +30,10 @@ TEST(CPlusPlusLanguage, MethodNameParsing) {
{"foo::~bar(baz)", "", "foo", "~bar", "(baz)", "", "foo::~bar"},
{"a::b::c::d(e,f)", "", "a::b::c", "d", "(e,f)", "", "a::b::c::d"},
{"void f(int)", "void", "", "f", "(int)", "", "f"},
+ {"std::vector<int>foo::bar()", "std::vector<int>", "foo", "bar", "()", "",
+ "foo::bar"},
+ {"int foo::bar::func01(int a, double b)", "int", "foo::bar", "func01",
+ "(int a, double b)", "", "foo::bar::func01"},
// Operators
{"std::basic_ostream<char, std::char_traits<char> >& "
@@ -65,6 +69,12 @@ TEST(CPlusPlusLanguage, MethodNameParsing) {
"const",
"std::__1::ranges::__begin::__fn::operator()[abi:v160000]<char const, "
"18ul>"},
+ {"bool Ball[abi:BALL]<int>::operator<<[abi:operator]<int>(int)", "bool",
+ "Ball[abi:BALL]<int>", "operator<<[abi:operator]<int>", "(int)", "",
+ "Ball[abi:BALL]<int>::operator<<[abi:operator]<int>"},
+ {"bool Ball[abi:BALL]<int>::operator>>[abi:operator]<int>(int)", "bool",
+ "Ball[abi:BALL]<int>", "operator>>[abi:operator]<int>", "(int)", "",
+ "Ball[abi:BALL]<int>::operator>>[abi:operator]<int>"},
// Internal classes
{"operator<<(Cls, Cls)::Subclass::function()", "",
"operator<<(Cls, Cls)::Subclass", "function", "()", "",
@@ -101,6 +111,8 @@ TEST(CPlusPlusLanguage, MethodNameParsing) {
"std::forward<decltype(nullptr)>"},
// Templates
+ {"vector<int > foo::bar::func(int)", "vector<int >", "foo::bar", "func",
+ "(int)", "", "foo::bar::func"},
{"void llvm::PM<llvm::Module, llvm::AM<llvm::Module>>::"
"addPass<llvm::VP>(llvm::VP)",
"void", "llvm::PM<llvm::Module, llvm::AM<llvm::Module>>",
@@ -341,7 +353,7 @@ TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) {
llvm::StringRef context, basename;
for (const auto &test : test_cases) {
EXPECT_TRUE(CPlusPlusLanguage::ExtractContextAndIdentifier(
- test.input.c_str(), context, basename));
+ test.input, context, basename));
EXPECT_EQ(test.context, context.str());
EXPECT_EQ(test.basename, basename.str());
}
diff --git a/lldb/unittests/Language/ObjC/ObjCLanguageTest.cpp b/lldb/unittests/Language/ObjC/ObjCLanguageTest.cpp
index 70baa7e..4b018a29 100644
--- a/lldb/unittests/Language/ObjC/ObjCLanguageTest.cpp
+++ b/lldb/unittests/Language/ObjC/ObjCLanguageTest.cpp
@@ -112,3 +112,46 @@ TEST(ObjCLanguage, InvalidMethodNameParsing) {
EXPECT_FALSE(lax_method.has_value());
}
}
+
+struct ObjCMethodTestCase {
+ llvm::StringRef name;
+ bool is_valid;
+};
+
+struct ObjCMethodNameTextFiture
+ : public testing::TestWithParam<ObjCMethodTestCase> {};
+
+static ObjCMethodTestCase g_objc_method_name_test_cases[] = {
+ {"", false},
+ {"+[Uh oh!", false},
+ {"-[Definitely not...", false},
+ {"[Nice try ] :)", false},
+ {"+MaybeIfYouSquintYourEyes]", false},
+ {"?[Tricky]", false},
+ {"[]", false},
+ {"-[a", false},
+ {"+[a", false},
+ {"-]a]", false},
+ {"+]a]", false},
+
+ // FIXME: should these count as valid?
+ {"+[]", true},
+ {"-[]", true},
+ {"-[[]", true},
+ {"+[[]", true},
+ {"+[a ]", true},
+ {"-[a ]", true},
+
+ // Valid names
+ {"+[a a]", true},
+ {"-[a a]", true},
+};
+
+TEST_P(ObjCMethodNameTextFiture, TestIsPossibleObjCMethodName) {
+ // Tests ObjCLanguage::IsPossibleObjCMethodName
+ auto [name, expect_valid] = GetParam();
+ EXPECT_EQ(ObjCLanguage::IsPossibleObjCMethodName(name), expect_valid);
+}
+
+INSTANTIATE_TEST_SUITE_P(ObjCMethodNameTests, ObjCMethodNameTextFiture,
+ testing::ValuesIn(g_objc_method_name_test_cases));
diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
index 012eae0..966b37e 100644
--- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
+++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
@@ -326,7 +326,7 @@ TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) {
TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
const lldb::addr_t addr = 0xa000;
- MemoryRegionInfo region_info;
+ lldb_private::MemoryRegionInfo region_info;
std::future<Status> result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
});
@@ -343,13 +343,16 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
EXPECT_TRUE(result.get().Success());
EXPECT_EQ(addr, region_info.GetRange().GetRangeBase());
EXPECT_EQ(0x2000u, region_info.GetRange().GetByteSize());
- EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetReadable());
- EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetWritable());
- EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetExecutable());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.GetReadable());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.GetWritable());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.GetExecutable());
EXPECT_EQ("/foo/bar.so", region_info.GetName().GetStringRef());
- EXPECT_EQ(MemoryRegionInfo::eDontKnow, region_info.GetMemoryTagged());
- EXPECT_EQ(MemoryRegionInfo::eDontKnow, region_info.IsStackMemory());
- EXPECT_EQ(MemoryRegionInfo::eDontKnow, region_info.IsShadowStack());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eDontKnow,
+ region_info.GetMemoryTagged());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eDontKnow,
+ region_info.IsStackMemory());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eDontKnow,
+ region_info.IsShadowStack());
result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
@@ -358,9 +361,9 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
HandlePacket(server, "qMemoryRegionInfo:a000",
"start:a000;size:2000;flags:;type:stack;");
EXPECT_TRUE(result.get().Success());
- EXPECT_EQ(MemoryRegionInfo::eNo, region_info.GetMemoryTagged());
- EXPECT_EQ(MemoryRegionInfo::eYes, region_info.IsStackMemory());
- EXPECT_EQ(MemoryRegionInfo::eNo, region_info.IsShadowStack());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.GetMemoryTagged());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.IsStackMemory());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.IsShadowStack());
result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
@@ -369,9 +372,10 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
HandlePacket(server, "qMemoryRegionInfo:a000",
"start:a000;size:2000;flags: mt zz mt ss ;type:ha,ha,stack;");
EXPECT_TRUE(result.get().Success());
- EXPECT_EQ(MemoryRegionInfo::eYes, region_info.GetMemoryTagged());
- EXPECT_EQ(MemoryRegionInfo::eYes, region_info.IsStackMemory());
- EXPECT_EQ(MemoryRegionInfo::eYes, region_info.IsShadowStack());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes,
+ region_info.GetMemoryTagged());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.IsStackMemory());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eYes, region_info.IsShadowStack());
result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
@@ -380,12 +384,12 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
HandlePacket(server, "qMemoryRegionInfo:a000",
"start:a000;size:2000;type:heap;");
EXPECT_TRUE(result.get().Success());
- EXPECT_EQ(MemoryRegionInfo::eNo, region_info.IsStackMemory());
+ EXPECT_EQ(lldb_private::MemoryRegionInfo::eNo, region_info.IsStackMemory());
}
TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) {
const lldb::addr_t addr = 0x4000;
- MemoryRegionInfo region_info;
+ lldb_private::MemoryRegionInfo region_info;
std::future<Status> result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
});
diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index 3d0e2d8..5694aee 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -137,6 +137,11 @@ lldb_private::python::LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data) {
}
void *
+lldb_private::python::LLDBSWIGPython_CastPyObjectToSBThread(PyObject *data) {
+ return nullptr;
+}
+
+void *
lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrame(PyObject *data) {
return nullptr;
}
@@ -161,6 +166,11 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBExecutionContext(
return nullptr;
}
+void *
+lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrameList(PyObject *data) {
+ return nullptr;
+}
+
lldb::ValueObjectSP
lldb_private::python::SWIGBridge::LLDBSWIGPython_GetValueObjectSPFromSBValue(
void *data) {
@@ -329,6 +339,11 @@ lldb_private::python::SWIGBridge::ToSWIGWrapper(lldb::ProcessSP) {
return python::PythonObject();
}
+python::PythonObject
+lldb_private::python::SWIGBridge::ToSWIGWrapper(lldb::StackFrameListSP) {
+ return python::PythonObject();
+}
+
python::PythonObject lldb_private::python::SWIGBridge::ToSWIGWrapper(
const lldb_private::StructuredDataImpl &) {
return python::PythonObject();
diff --git a/lldb/unittests/Signals/UnixSignalsTest.cpp b/lldb/unittests/Signals/UnixSignalsTest.cpp
index 582e441..3bd4aed 100644
--- a/lldb/unittests/Signals/UnixSignalsTest.cpp
+++ b/lldb/unittests/Signals/UnixSignalsTest.cpp
@@ -148,6 +148,18 @@ TEST(UnixSignalsTest, GetAsString) {
signals.GetSignalDescription(16, 3, 0x1233, 0x1234, 0x5678));
}
+TEST(UnixSignalsTest, GetNumberDescription) {
+ TestSignals signals;
+
+ ASSERT_EQ("DESC2", signals.GetSignalNumberDescription(2));
+ ASSERT_EQ("DESC4", signals.GetSignalNumberDescription(4));
+ ASSERT_EQ("DESC8", signals.GetSignalNumberDescription(8));
+ ASSERT_EQ("DESC16", signals.GetSignalNumberDescription(16));
+
+ // Unknown signal number.
+ ASSERT_EQ("", signals.GetSignalNumberDescription(100));
+}
+
TEST(UnixSignalsTest, VersionChange) {
TestSignals signals;
diff --git a/lldb/unittests/Symbol/LineTableTest.cpp b/lldb/unittests/Symbol/LineTableTest.cpp
index eadab40..ca63c6f 100644
--- a/lldb/unittests/Symbol/LineTableTest.cpp
+++ b/lldb/unittests/Symbol/LineTableTest.cpp
@@ -176,10 +176,12 @@ Sections:
if (!text_sp)
return createStringError("No .text");
- auto cu_up = std::make_unique<CompileUnit>(module_sp, /*user_data=*/nullptr,
- /*support_file_sp=*/nullptr,
- /*uid=*/0, eLanguageTypeC,
- /*is_optimized=*/eLazyBoolNo);
+ auto cu_up = std::make_unique<CompileUnit>(
+ module_sp,
+ /*user_data=*/nullptr,
+ /*support_file_nsp=*/std::make_shared<SupportFile>(),
+ /*uid=*/0, eLanguageTypeC,
+ /*is_optimized=*/eLazyBoolNo);
LineTable *line_table = new LineTable(cu_up.get(), std::move(line_sequences));
cu_up->SetLineTable(line_table);
cast<FakeSymbolFile>(module_sp->GetSymbolFile())
diff --git a/lldb/unittests/Symbol/TestClangASTImporter.cpp b/lldb/unittests/Symbol/TestClangASTImporter.cpp
index f1b3d79..07c4208 100644
--- a/lldb/unittests/Symbol/TestClangASTImporter.cpp
+++ b/lldb/unittests/Symbol/TestClangASTImporter.cpp
@@ -287,7 +287,7 @@ TEST_F(TestClangASTImporter, RecordLayoutFromOrigin) {
clang_utils::SourceASTWithRecord source;
auto *dwarf_parser =
- static_cast<DWARFASTParserClang *>(source.ast->GetDWARFParser());
+ llvm::cast<DWARFASTParserClang>(source.ast->GetDWARFParser());
auto &importer = dwarf_parser->GetClangASTImporter();
// Set the layout for the origin decl in the origin ClangASTImporter.
diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
index 4de595f..155fc74 100644
--- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp
+++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
@@ -52,6 +52,12 @@ protected:
return ClangUtil::GetQualType(
m_ast->GetBuiltinTypeByName(ConstString(name)));
}
+
+ CompilerType GetBuiltinTypeForDWARFEncodingAndBitSize(
+ llvm::StringRef type_name, uint32_t encoding, uint32_t bit_size) const {
+ return m_ast->GetBuiltinTypeForDWARFEncodingAndBitSize(type_name, encoding,
+ bit_size);
+ }
};
TEST_F(TestTypeSystemClang, TestGetBasicTypeFromEnum) {
@@ -238,6 +244,91 @@ TEST_F(TestTypeSystemClang, TestBuiltinTypeForEncodingAndBitSize) {
VerifyEncodingAndBitSize(*m_ast, eEncodingIEEE754, 64);
}
+TEST_F(TestTypeSystemClang, TestGetBuiltinTypeForDWARFEncodingAndBitSize) {
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "_BitIn", llvm::dwarf::DW_ATE_signed, 2)
+ .IsValid());
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "BitInt", llvm::dwarf::DW_ATE_signed, 2)
+ .IsValid());
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "_BitInt(2)", llvm::dwarf::DW_ATE_signed_char, 2)
+ .IsValid());
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "_BitInt", llvm::dwarf::DW_ATE_signed_char, 2)
+ .IsValid());
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "_BitInt(2)", llvm::dwarf::DW_ATE_unsigned, 2)
+ .IsValid());
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "_BitInt", llvm::dwarf::DW_ATE_unsigned, 2)
+ .IsValid());
+
+ EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "_BitInt(2)", llvm::dwarf::DW_ATE_signed, 2)
+ .GetTypeName(),
+ "_BitInt(2)");
+ EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "_BitInt", llvm::dwarf::DW_ATE_signed, 2)
+ .GetTypeName(),
+ "_BitInt(2)");
+ EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "_BitInt(129)", llvm::dwarf::DW_ATE_signed, 129)
+ .GetTypeName(),
+ "_BitInt(129)");
+ EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "_BitInt", llvm::dwarf::DW_ATE_signed, 129)
+ .GetTypeName(),
+ "_BitInt(129)");
+
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned _BitIn", llvm::dwarf::DW_ATE_unsigned, 2)
+ .IsValid());
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned BitInt", llvm::dwarf::DW_ATE_unsigned, 2)
+ .IsValid());
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned _BitInt(2)", llvm::dwarf::DW_ATE_unsigned_char, 2)
+ .IsValid());
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned _BitInt", llvm::dwarf::DW_ATE_unsigned_char, 2)
+ .IsValid());
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned _BitInt(2)", llvm::dwarf::DW_ATE_signed, 2)
+ .IsValid());
+ EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned _BitInt", llvm::dwarf::DW_ATE_signed, 2)
+ .IsValid());
+
+ EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned _BitInt(2)", llvm::dwarf::DW_ATE_unsigned, 2)
+ .GetTypeName(),
+ "unsigned _BitInt(2)");
+ EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned _BitInt", llvm::dwarf::DW_ATE_unsigned, 2)
+ .GetTypeName(),
+ "unsigned _BitInt(2)");
+ EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned _BitInt(129)", llvm::dwarf::DW_ATE_unsigned, 129)
+ .GetTypeName(),
+ "unsigned _BitInt(129)");
+ EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned _BitInt", llvm::dwarf::DW_ATE_unsigned, 129)
+ .GetTypeName(),
+ "unsigned _BitInt(129)");
+}
+
+TEST_F(TestTypeSystemClang, TestBitIntTypeInfo) {
+ EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "_BitInt", llvm::dwarf::DW_ATE_signed, 2)
+ .GetTypeInfo(),
+ eTypeIsSigned | eTypeIsScalar | eTypeHasValue | eTypeIsInteger);
+ EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
+ "unsigned _BitInt", llvm::dwarf::DW_ATE_unsigned, 2)
+ .GetTypeInfo(),
+ eTypeIsScalar | eTypeHasValue | eTypeIsInteger);
+}
+
TEST_F(TestTypeSystemClang, TestBuiltinTypeForEmptyTriple) {
// Test that we can access type-info of builtin Clang AST
// types without crashing even when the target triple is
diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
index 064ed6d..6a753b6 100644
--- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
@@ -42,6 +42,52 @@ public:
return keys;
}
};
+
+/// Helper structure for DWARFASTParserClang tests that want to parse DWARF
+/// generated using yaml2obj. On construction parses the supplied YAML data
+/// into a DWARF module and thereafter vends a DWARFASTParserClang and
+/// TypeSystemClang that are guaranteed to live for the duration of this object.
+class DWARFASTParserClangYAMLTester {
+public:
+ DWARFASTParserClangYAMLTester(llvm::StringRef yaml_data)
+ : m_module_tester(yaml_data) {}
+
+ DWARFDIE GetCUDIE() {
+ DWARFUnit *unit = m_module_tester.GetDwarfUnit();
+ assert(unit);
+
+ const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
+ assert(cu_entry->Tag() == DW_TAG_compile_unit);
+
+ return DWARFDIE(unit, cu_entry);
+ }
+
+ DWARFASTParserClang &GetParser() {
+ auto *parser = GetTypeSystem().GetDWARFParser();
+
+ assert(llvm::isa_and_nonnull<DWARFASTParserClang>(parser));
+
+ return *llvm::cast<DWARFASTParserClang>(parser);
+ }
+
+ TypeSystemClang &GetTypeSystem() {
+ ModuleSP module_sp = m_module_tester.GetModule();
+ assert(module_sp);
+
+ SymbolFile *symfile = module_sp->GetSymbolFile();
+ assert(symfile);
+
+ TypeSystemSP ts_sp = llvm::cantFail(
+ symfile->GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+
+ assert(llvm::isa_and_nonnull<TypeSystemClang>(ts_sp.get()));
+
+ return llvm::cast<TypeSystemClang>(*ts_sp);
+ }
+
+private:
+ YAMLModuleTester m_module_tester;
+};
} // namespace
// If your implementation needs to dereference the dummy pointers we are
@@ -99,7 +145,6 @@ DWARF:
- Value: 0x0000000000000001
- AbbrCode: 0x00000000
)";
-
YAMLModuleTester t(yamldata);
ASSERT_TRUE((bool)t.GetDwarfUnit());
@@ -248,17 +293,9 @@ DWARF:
- AbbrCode: 0x0
...
)";
- YAMLModuleTester t(yamldata);
+ DWARFASTParserClangYAMLTester tester(yamldata);
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- DWARFDIE cu_die(unit, cu_entry);
-
- auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
- auto &ast_ctx = *holder->GetAST();
- DWARFASTParserClangStub ast_parser(ast_ctx);
+ DWARFDIE cu_die = tester.GetCUDIE();
std::vector<std::string> found_function_types;
// The DWARF above is just a list of functions. Parse all of them to
@@ -267,7 +304,8 @@ DWARF:
ASSERT_EQ(func.Tag(), DW_TAG_subprogram);
SymbolContext sc;
bool new_type = false;
- lldb::TypeSP type = ast_parser.ParseTypeFromDWARF(sc, func, &new_type);
+ lldb::TypeSP type =
+ tester.GetParser().ParseTypeFromDWARF(sc, func, &new_type);
found_function_types.push_back(
type->GetForwardCompilerType().GetTypeName().AsCString());
}
@@ -394,18 +432,9 @@ DWARF:
- AbbrCode: 0x00 # end of child tags of 0x0c
...
)";
- YAMLModuleTester t(yamldata);
-
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- DWARFDIE cu_die(unit, cu_entry);
-
- auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
- auto &ast_ctx = *holder->GetAST();
- DWARFASTParserClangStub ast_parser(ast_ctx);
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
DWARFDIE ptrauth_variable = cu_die.GetFirstChild();
ASSERT_EQ(ptrauth_variable.Tag(), DW_TAG_variable);
DWARFDIE ptrauth_type =
@@ -415,7 +444,7 @@ DWARF:
SymbolContext sc;
bool new_type = false;
lldb::TypeSP type_sp =
- ast_parser.ParseTypeFromDWARF(sc, ptrauth_type, &new_type);
+ tester.GetParser().ParseTypeFromDWARF(sc, ptrauth_type, &new_type);
CompilerType compiler_type = type_sp->GetForwardCompilerType();
ASSERT_EQ(compiler_type.GetPtrAuthKey(), 0U);
ASSERT_EQ(compiler_type.GetPtrAuthAddressDiversity(), false);
@@ -554,24 +583,17 @@ TEST_F(DWARFASTParserClangTests, TestDefaultTemplateParamParsing) {
auto BufferOrError = llvm::MemoryBuffer::getFile(
GetInputFilePath("DW_AT_default_value-test.yaml"), /*IsText=*/true);
ASSERT_TRUE(BufferOrError);
- YAMLModuleTester t(BufferOrError.get()->getBuffer());
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- DWARFDIE cu_die(unit, cu_entry);
-
- auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
- auto &ast_ctx = *holder->GetAST();
- DWARFASTParserClangStub ast_parser(ast_ctx);
+ DWARFASTParserClangYAMLTester tester(BufferOrError.get()->getBuffer());
+ DWARFDIE cu_die = tester.GetCUDIE();
llvm::SmallVector<lldb::TypeSP, 2> types;
for (DWARFDIE die : cu_die.children()) {
if (die.Tag() == DW_TAG_class_type) {
SymbolContext sc;
bool new_type = false;
- types.push_back(ast_parser.ParseTypeFromDWARF(sc, die, &new_type));
+ types.push_back(
+ tester.GetParser().ParseTypeFromDWARF(sc, die, &new_type));
}
}
@@ -605,23 +627,14 @@ TEST_F(DWARFASTParserClangTests, TestSpecDeclExistsError) {
auto BufferOrError = llvm::MemoryBuffer::getFile(
GetInputFilePath("DW_AT_spec_decl_exists-test.yaml"), /*IsText=*/true);
ASSERT_TRUE(BufferOrError);
- YAMLModuleTester t(BufferOrError.get()->getBuffer());
-
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- DWARFDIE cu_die(unit, cu_entry);
-
- auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
- auto &ast_ctx = *holder->GetAST();
- DWARFASTParserClangStub ast_parser(ast_ctx);
+ DWARFASTParserClangYAMLTester tester(BufferOrError.get()->getBuffer());
+ DWARFDIE cu_die = tester.GetCUDIE();
llvm::SmallVector<lldb::TypeSP, 2> specializations;
for (DWARFDIE die : cu_die.children()) {
SymbolContext sc;
bool new_type = false;
- auto type = ast_parser.ParseTypeFromDWARF(sc, die, &new_type);
+ auto type = tester.GetParser().ParseTypeFromDWARF(sc, die, &new_type);
llvm::StringRef die_name = llvm::StringRef(die.GetName());
if (die_name.starts_with("_Optional_payload")) {
specializations.push_back(std::move(type));
@@ -730,18 +743,8 @@ DWARF:
- AbbrCode: 0x00 # end of child tags of 0x0c
...
)";
- YAMLModuleTester t(yamldata);
-
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
- DWARFDIE cu_die(unit, cu_entry);
-
- auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
- auto &ast_ctx = *holder->GetAST();
- DWARFASTParserClangStub ast_parser(ast_ctx);
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
DWARFDIE decl_die;
DWARFDIE def_die;
@@ -762,6 +765,8 @@ DWARF:
ParsedDWARFTypeAttributes attrs(def_die);
ASSERT_TRUE(attrs.decl.IsValid());
+ DWARFASTParserClang &ast_parser = tester.GetParser();
+
SymbolContext sc;
bool new_type = false;
lldb::TypeSP type_sp = ast_parser.ParseTypeFromDWARF(sc, decl_die, &new_type);
@@ -906,18 +911,8 @@ DWARF:
- AbbrCode: 0x0
...
)";
- YAMLModuleTester t(yamldata);
-
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
- DWARFDIE cu_die(unit, cu_entry);
-
- auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
- auto &ast_ctx = *holder->GetAST();
- DWARFASTParserClangStub ast_parser(ast_ctx);
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
auto context_die = cu_die.GetFirstChild();
ASSERT_TRUE(context_die.IsValid());
@@ -932,7 +927,8 @@ DWARF:
auto param_die = decl_die.GetFirstChild();
ASSERT_TRUE(param_die.IsValid());
- EXPECT_EQ(param_die, ast_parser.GetObjectParameter(decl_die, context_die));
+ EXPECT_EQ(param_die,
+ tester.GetParser().GetObjectParameter(decl_die, context_die));
}
{
@@ -945,8 +941,8 @@ DWARF:
auto param_die = subprogram_definition.GetFirstChild();
ASSERT_TRUE(param_die.IsValid());
- EXPECT_EQ(param_die, ast_parser.GetObjectParameter(subprogram_definition,
- context_die));
+ EXPECT_EQ(param_die, tester.GetParser().GetObjectParameter(
+ subprogram_definition, context_die));
}
}
@@ -1076,18 +1072,8 @@ DWARF:
- AbbrCode: 0x0
...
)";
- YAMLModuleTester t(yamldata);
-
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
- DWARFDIE cu_die(unit, cu_entry);
-
- auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
- auto &ast_ctx = *holder->GetAST();
- DWARFASTParserClangStub ast_parser(ast_ctx);
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
auto context_die = cu_die.GetFirstChild();
ASSERT_TRUE(context_die.IsValid());
@@ -1105,7 +1091,7 @@ DWARF:
auto param_die = subprogram_definition.GetFirstChild();
ASSERT_TRUE(param_die.IsValid());
EXPECT_EQ(param_die,
- ast_parser.GetObjectParameter(subprogram_definition, {}));
+ tester.GetParser().GetObjectParameter(subprogram_definition, {}));
}
TEST_F(DWARFASTParserClangTests, TestParseSubroutine_ExplicitObjectParameter) {
@@ -1243,21 +1229,15 @@ DWARF:
- AbbrCode: 0x0
...
)";
- YAMLModuleTester t(yamldata);
-
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
- DWARFDIE cu_die(unit, cu_entry);
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
auto ts_or_err =
cu_die.GetDWARF()->GetTypeSystemForLanguage(eLanguageTypeC_plus_plus);
ASSERT_TRUE(static_cast<bool>(ts_or_err));
llvm::consumeError(ts_or_err.takeError());
auto *parser =
- static_cast<DWARFASTParserClang *>((*ts_or_err)->GetDWARFParser());
+ llvm::cast<DWARFASTParserClang>((*ts_or_err)->GetDWARFParser());
auto context_die = cu_die.GetFirstChild();
ASSERT_TRUE(context_die.IsValid());
@@ -1419,22 +1399,8 @@ DWARF:
- AbbrCode: 0x0
...
)";
- YAMLModuleTester t(yamldata);
-
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
- DWARFDIE cu_die(unit, cu_entry);
-
- auto ts_or_err =
- cu_die.GetDWARF()->GetTypeSystemForLanguage(eLanguageTypeC_plus_plus);
- ASSERT_TRUE(static_cast<bool>(ts_or_err));
- llvm::consumeError(ts_or_err.takeError());
-
- auto *ts = static_cast<TypeSystemClang *>(ts_or_err->get());
- auto *parser = static_cast<DWARFASTParserClang *>(ts->GetDWARFParser());
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
auto subprogram = cu_die.GetFirstChild();
ASSERT_TRUE(subprogram.IsValid());
@@ -1442,11 +1408,13 @@ DWARF:
SymbolContext sc;
bool new_type;
- auto type_sp = parser->ParseTypeFromDWARF(sc, subprogram, &new_type);
+ auto type_sp =
+ tester.GetParser().ParseTypeFromDWARF(sc, subprogram, &new_type);
ASSERT_NE(type_sp, nullptr);
- auto result = ts->GetTranslationUnitDecl()->lookup(
- clang_utils::getDeclarationName(*ts, "func"));
+ TypeSystemClang &ts = tester.GetTypeSystem();
+ auto result = ts.GetTranslationUnitDecl()->lookup(
+ clang_utils::getDeclarationName(ts, "func"));
ASSERT_TRUE(result.isSingleResult());
auto const *func = llvm::cast<clang::FunctionDecl>(result.front());
@@ -1609,19 +1577,8 @@ DWARF:
- AbbrCode: 0x0
...
)";
-
- YAMLModuleTester t(yamldata);
-
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
- DWARFDIE cu_die(unit, cu_entry);
-
- auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
- auto &ast_ctx = *holder->GetAST();
- DWARFASTParserClangStub ast_parser(ast_ctx);
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
auto context_die = cu_die.GetFirstChild();
ASSERT_TRUE(context_die.IsValid());
@@ -1640,7 +1597,8 @@ DWARF:
auto param_die = sub1.GetFirstChild().GetSibling();
ASSERT_TRUE(param_die.IsValid());
- EXPECT_EQ(param_die, ast_parser.GetObjectParameter(sub1, context_die));
+ EXPECT_EQ(param_die,
+ tester.GetParser().GetObjectParameter(sub1, context_die));
}
// Object parameter is at constant index 0
@@ -1648,7 +1606,8 @@ DWARF:
auto param_die = sub2.GetFirstChild();
ASSERT_TRUE(param_die.IsValid());
- EXPECT_EQ(param_die, ast_parser.GetObjectParameter(sub2, context_die));
+ EXPECT_EQ(param_die,
+ tester.GetParser().GetObjectParameter(sub2, context_die));
}
}
@@ -1711,19 +1670,8 @@ DWARF:
- Value: 0x02
...
)";
-
- YAMLModuleTester t(yamldata);
-
- DWARFUnit *unit = t.GetDwarfUnit();
- ASSERT_NE(unit, nullptr);
- const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
- ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
- ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
- DWARFDIE cu_die(unit, cu_entry);
-
- auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
- auto &ast_ctx = *holder->GetAST();
- DWARFASTParserClangStub ast_parser(ast_ctx);
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
auto type_die = cu_die.GetFirstChild();
ASSERT_TRUE(type_die.IsValid());
@@ -1734,10 +1682,439 @@ DWARF:
EXPECT_EQ(attrs.data_bit_size.value_or(0), 2U);
SymbolContext sc;
- auto type_sp =
- ast_parser.ParseTypeFromDWARF(sc, type_die, /*type_is_new_ptr=*/nullptr);
+ auto type_sp = tester.GetParser().ParseTypeFromDWARF(
+ sc, type_die, /*type_is_new_ptr=*/nullptr);
ASSERT_NE(type_sp, nullptr);
EXPECT_EQ(llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0),
1U);
}
+
+TEST_F(DWARFASTParserClangTests, TestBitIntParsing) {
+ // Tests that we correctly parse the DW_AT_base_type for a _BitInt.
+ // Older versions of Clang only emit the `_BitInt` string into the
+ // DW_AT_name (not including the bitsize). Make sure we understand
+ // those too.
+
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AARCH64
+DWARF:
+ debug_str:
+ - _BitInt(2)
+ - _BitInt
+ - unsigned _BitInt(2)
+ - unsigned _BitInt
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x2
+ Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_encoding
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_bit_size
+ Form: DW_FORM_data1
+ - Code: 0x3
+ Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_encoding
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+
+ debug_info:
+ - Version: 5
+ UnitType: DW_UT_compile
+ AddrSize: 8
+ Entries:
+
+# DW_TAG_compile_unit
+# DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus)
+
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x04
+
+# DW_TAG_base_type
+# DW_AT_name [DW_FORM_strp] ('_BitInt(2)')
+
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x0
+ - Value: 0x05
+ - Value: 0x01
+ - Value: 0x02
+
+# DW_TAG_base_type
+# DW_AT_name [DW_FORM_strp] ('_BitInt')
+
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x0b
+ - Value: 0x05
+ - Value: 0x08
+ - Value: 0x34
+
+# DW_TAG_base_type
+# DW_AT_name [DW_FORM_strp] ('unsigned _BitInt(2)')
+
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x13
+ - Value: 0x07
+ - Value: 0x01
+ - Value: 0x02
+
+# DW_TAG_base_type
+# DW_AT_name [DW_FORM_strp] ('unsigned _BitInt')
+
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x27
+ - Value: 0x07
+ - Value: 0x08
+ - Value: 0x34
+
+# DW_TAG_base_type
+# DW_AT_name [DW_FORM_strp] ('_BitInt')
+
+ - AbbrCode: 0x3
+ Values:
+ - Value: 0x0b
+ - Value: 0x05
+ - Value: 0x08
+...
+
+)";
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
+
+ auto type_die = cu_die.GetFirstChild();
+ ASSERT_TRUE(type_die.IsValid());
+
+ {
+ SymbolContext sc;
+ auto type_sp =
+ tester.GetParser().ParseTypeFromDWARF(sc, type_die,
+ /*type_is_new_ptr=*/nullptr);
+ ASSERT_NE(type_sp, nullptr);
+
+ EXPECT_EQ(
+ llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0),
+ 1U);
+ EXPECT_EQ(type_sp->GetEncoding(), lldb::eEncodingSint);
+ EXPECT_EQ(type_sp->GetName(), "_BitInt(2)");
+ EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), "_BitInt(2)");
+ }
+
+ {
+ type_die = type_die.GetSibling();
+ SymbolContext sc;
+ auto type_sp =
+ tester.GetParser().ParseTypeFromDWARF(sc, type_die,
+ /*type_is_new_ptr=*/nullptr);
+ ASSERT_NE(type_sp, nullptr);
+
+ EXPECT_EQ(
+ llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0),
+ 8U);
+ EXPECT_EQ(type_sp->GetEncoding(), lldb::eEncodingSint);
+ EXPECT_EQ(type_sp->GetName(), "_BitInt");
+ EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), "_BitInt(52)");
+ }
+
+ {
+ type_die = type_die.GetSibling();
+ SymbolContext sc;
+ auto type_sp =
+ tester.GetParser().ParseTypeFromDWARF(sc, type_die,
+ /*type_is_new_ptr=*/nullptr);
+ ASSERT_NE(type_sp, nullptr);
+
+ EXPECT_EQ(
+ llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0),
+ 1U);
+ EXPECT_EQ(type_sp->GetEncoding(), lldb::eEncodingUint);
+ EXPECT_EQ(type_sp->GetName(), "unsigned _BitInt(2)");
+ EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(),
+ "unsigned _BitInt(2)");
+ }
+
+ {
+ type_die = type_die.GetSibling();
+ SymbolContext sc;
+ auto type_sp =
+ tester.GetParser().ParseTypeFromDWARF(sc, type_die,
+ /*type_is_new_ptr=*/nullptr);
+ ASSERT_NE(type_sp, nullptr);
+
+ EXPECT_EQ(
+ llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0),
+ 8U);
+ EXPECT_EQ(type_sp->GetEncoding(), lldb::eEncodingUint);
+ EXPECT_EQ(type_sp->GetName(), "unsigned _BitInt");
+ EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(),
+ "unsigned _BitInt(52)");
+ }
+
+ {
+ type_die = type_die.GetSibling();
+ SymbolContext sc;
+ auto type_sp =
+ tester.GetParser().ParseTypeFromDWARF(sc, type_die,
+ /*type_is_new_ptr=*/nullptr);
+ ASSERT_NE(type_sp, nullptr);
+
+ EXPECT_EQ(
+ llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0),
+ 8U);
+ EXPECT_EQ(type_sp->GetEncoding(), lldb::eEncodingSint);
+ EXPECT_EQ(type_sp->GetName(), "_BitInt");
+
+ // Older versions of Clang didn't emit a DW_AT_bit_size for _BitInt. In
+ // those cases we would format the CompilerType name using the byte-size.
+ EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), "_BitInt(64)");
+ }
+}
+
+TEST_F(DWARFASTParserClangTests, TestTemplateAlias_NoSimpleTemplateNames) {
+ // Tests that we correctly parse the DW_TAG_template_alias generated by
+ // -gno-simple-template-names.
+
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AARCH64
+DWARF:
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x2
+ Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Code: 0x3
+ Tag: DW_TAG_template_alias
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Code: 0x4
+ Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+
+ debug_info:
+ - Version: 5
+ UnitType: DW_UT_compile
+ AddrSize: 8
+ Entries:
+
+# DW_TAG_compile_unit
+# DW_AT_language (DW_LANG_C_plus_plus)
+
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x04
+
+# DW_TAG_base_type
+# DW_AT_name ('int')
+
+ - AbbrCode: 0x2
+ Values:
+ - CStr: int
+
+# DW_TAG_template_alias
+# DW_AT_name ('Foo<int>')
+# DW_AT_type ('int')
+# DW_TAG_template_type_parameter
+# DW_AT_name ('T')
+# DW_AT_type ('int')
+
+ - AbbrCode: 0x3
+ Values:
+ - CStr: Foo<int>
+ - Value: 0xf
+
+ - AbbrCode: 0x4
+ Values:
+ - CStr: T
+ - Value: 0xf
+
+ - AbbrCode: 0x0
+ - AbbrCode: 0x0
+...
+)";
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
+
+ auto alias_die = cu_die.GetFirstChild().GetSibling();
+ ASSERT_EQ(alias_die.Tag(), DW_TAG_template_alias);
+
+ SymbolContext sc;
+ auto type_sp =
+ tester.GetParser().ParseTypeFromDWARF(sc, alias_die,
+ /*type_is_new_ptr=*/nullptr);
+ ASSERT_NE(type_sp, nullptr);
+
+ EXPECT_TRUE(type_sp->IsTypedef());
+ EXPECT_EQ(type_sp->GetName(), "Foo<int>");
+ EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), "Foo<int>");
+}
+
+TEST_F(DWARFASTParserClangTests,
+ TestTemplateAlias_InStruct_NoSimpleTemplateNames) {
+ // Tests that we correctly parse the DW_TAG_template_alias scoped inside a
+ // DW_TAG_structure_type *declaration* generated by
+ // -gno-simple-template-names. This tests the codepath the forcefully
+ // completes the context of the alias via PrepareContextToReceiveMembers.
+
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AARCH64
+DWARF:
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x2
+ Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Code: 0x3
+ Tag: DW_TAG_structure_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Code: 0x4
+ Tag: DW_TAG_template_alias
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Code: 0x5
+ Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+
+ debug_info:
+ - Version: 5
+ UnitType: DW_UT_compile
+ AddrSize: 8
+ Entries:
+
+# DW_TAG_compile_unit
+# DW_AT_language (DW_LANG_C_plus_plus)
+
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x04
+
+# DW_TAG_base_type
+# DW_AT_name ('int')
+
+ - AbbrCode: 0x2
+ Values:
+ - CStr: int
+
+# DW_TAG_structure_type
+# DW_AT_name ('Foo')
+
+ - AbbrCode: 0x3
+ Values:
+ - CStr: Foo
+
+# DW_TAG_template_alias
+# DW_AT_name ('Bar<int>')
+# DW_AT_type ('int')
+# DW_TAG_template_type_parameter
+# DW_AT_name ('T')
+# DW_AT_type ('int')
+
+ - AbbrCode: 0x4
+ Values:
+ - CStr: Bar<int>
+ - Value: 0xf
+
+ - AbbrCode: 0x5
+ Values:
+ - CStr: T
+ - Value: 0xf
+
+ - AbbrCode: 0x0
+ - AbbrCode: 0x0
+ - AbbrCode: 0x0
+...
+)";
+ DWARFASTParserClangYAMLTester tester(yamldata);
+ DWARFDIE cu_die = tester.GetCUDIE();
+
+ auto alias_die = cu_die.GetFirstChild().GetSibling().GetFirstChild();
+ ASSERT_EQ(alias_die.Tag(), DW_TAG_template_alias);
+
+ SymbolContext sc;
+ auto type_sp =
+ tester.GetParser().ParseTypeFromDWARF(sc, alias_die,
+ /*type_is_new_ptr=*/nullptr);
+ ASSERT_NE(type_sp, nullptr);
+
+ EXPECT_TRUE(type_sp->IsTypedef());
+ EXPECT_EQ(type_sp->GetName(), "Bar<int>");
+ EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), "Foo::Bar<int>");
+}
diff --git a/lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp b/lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp
index 17284b6..cd6db5f 100644
--- a/lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp
+++ b/lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp
@@ -99,7 +99,7 @@ Member *AddField(Member *member, StringRef name, uint64_t byte_offset,
std::make_unique<Member>(name, byte_offset * 8, byte_size * 8,
clang::QualType(), lldb::eAccessPublic, 0);
field->kind = kind;
- field->base_offset = base_offset;
+ field->base_offset = base_offset * 8;
member->fields.push_back(std::move(field));
return member->fields.back().get();
}
@@ -111,6 +111,9 @@ TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInStruct) {
CollectMember("m2", 0, 4);
CollectMember("m3", 0, 1);
CollectMember("m4", 0, 8);
+ CollectMember("m5", 8, 8);
+ CollectMember("m6", 16, 4);
+ CollectMember("m7", 16, 8);
ConstructRecord();
// struct {
@@ -120,6 +123,11 @@ TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInStruct) {
// m3;
// m4;
// };
+ // m5;
+ // union {
+ // m6;
+ // m7;
+ // };
// };
Record record;
record.start_offset = 0;
@@ -128,6 +136,10 @@ TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInStruct) {
AddField(u, "m2", 0, 4, Member::Field);
AddField(u, "m3", 0, 1, Member::Field);
AddField(u, "m4", 0, 8, Member::Field);
+ AddField(&record.record, "m5", 8, 8, Member::Field);
+ Member *u2 = AddField(&record.record, "", 16, 0, Member::Union);
+ AddField(u2, "m6", 16, 4, Member::Field);
+ AddField(u2, "m7", 16, 8, Member::Field);
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
}
@@ -243,3 +255,41 @@ TEST_F(UdtRecordCompleterRecordTests, TestNestedUnionStructInUnion) {
AddField(s2, "m4", 2, 4, Member::Field);
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
}
+
+TEST_F(UdtRecordCompleterRecordTests, TestNestedStructInUnionInStructInUnion) {
+ SetKind(Member::Kind::Union);
+ CollectMember("m1", 0, 4);
+ CollectMember("m2", 0, 2);
+ CollectMember("m3", 0, 2);
+ CollectMember("m4", 2, 4);
+ CollectMember("m5", 6, 2);
+ CollectMember("m6", 6, 2);
+ CollectMember("m7", 8, 2);
+ ConstructRecord();
+
+ // union {
+ // m1;
+ // m2;
+ // struct {
+ // m3;
+ // m4;
+ // union {
+ // m5;
+ // m6;
+ // };
+ // m7;
+ // };
+ // };
+ Record record;
+ record.start_offset = 0;
+ AddField(&record.record, "m1", 0, 4, Member::Field);
+ AddField(&record.record, "m2", 0, 2, Member::Field);
+ Member *s = AddField(&record.record, "", 0, 0, Member::Struct);
+ AddField(s, "m3", 0, 2, Member::Field);
+ AddField(s, "m4", 2, 4, Member::Field);
+ Member *u = AddField(s, "", 6, 0, Member::Union);
+ AddField(u, "m5", 6, 2, Member::Field);
+ AddField(u, "m6", 6, 2, Member::Field);
+ AddField(s, "m7", 8, 2, Member::Field);
+ EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
+}
diff --git a/lldb/unittests/Target/LocateModuleCallbackTest.cpp b/lldb/unittests/Target/LocateModuleCallbackTest.cpp
index 6ffa41b..d727cea 100644
--- a/lldb/unittests/Target/LocateModuleCallbackTest.cpp
+++ b/lldb/unittests/Target/LocateModuleCallbackTest.cpp
@@ -362,7 +362,7 @@ TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackFailureNoCache) {
});
m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
- ASSERT_EQ(callback_call_count, 2);
+ ASSERT_EQ(callback_call_count, 3);
ASSERT_FALSE(m_module_sp);
}
@@ -383,7 +383,7 @@ TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackFailureCached) {
});
m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
- ASSERT_EQ(callback_call_count, 2);
+ ASSERT_EQ(callback_call_count, 3);
CheckModule(m_module_sp);
ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view);
ASSERT_FALSE(m_module_sp->GetSymbolFileFileSpec());
@@ -409,7 +409,7 @@ TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackNoFiles) {
});
m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
- ASSERT_EQ(callback_call_count, 2);
+ ASSERT_EQ(callback_call_count, 3);
CheckModule(m_module_sp);
ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view);
ASSERT_FALSE(m_module_sp->GetSymbolFileFileSpec());
@@ -435,7 +435,7 @@ TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackNonExistentModule) {
});
m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
- ASSERT_EQ(callback_call_count, 2);
+ ASSERT_EQ(callback_call_count, 3);
CheckModule(m_module_sp);
ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view);
ASSERT_FALSE(m_module_sp->GetSymbolFileFileSpec());
@@ -464,7 +464,7 @@ TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackNonExistentSymbol) {
});
m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
- ASSERT_EQ(callback_call_count, 2);
+ ASSERT_EQ(callback_call_count, 3);
CheckModule(m_module_sp);
ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view);
ASSERT_TRUE(m_module_sp->GetSymbolFileFileSpec().GetPath().empty());
@@ -622,7 +622,7 @@ TEST_F(LocateModuleCallbackTest,
});
m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
- ASSERT_EQ(callback_call_count, 2);
+ ASSERT_EQ(callback_call_count, 3);
CheckModule(m_module_sp);
ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view);
ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(),
@@ -650,7 +650,7 @@ TEST_F(LocateModuleCallbackTest,
});
m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
- ASSERT_EQ(callback_call_count, 2);
+ ASSERT_EQ(callback_call_count, 3);
CheckModule(m_module_sp);
ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view);
ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(),
@@ -682,7 +682,7 @@ TEST_F(LocateModuleCallbackTest,
});
m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
- ASSERT_EQ(callback_call_count, 2);
+ ASSERT_EQ(callback_call_count, 3);
CheckModule(m_module_sp);
ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view);
ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(),
@@ -709,7 +709,7 @@ TEST_F(LocateModuleCallbackTest,
});
m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
- ASSERT_EQ(callback_call_count, 2);
+ ASSERT_EQ(callback_call_count, 3);
ASSERT_FALSE(m_module_sp);
}
@@ -731,7 +731,7 @@ TEST_F(LocateModuleCallbackTest,
});
m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
- ASSERT_EQ(callback_call_count, 2);
+ ASSERT_EQ(callback_call_count, 3);
ASSERT_FALSE(m_module_sp);
}
diff --git a/lldb/unittests/Target/MemoryTest.cpp b/lldb/unittests/Target/MemoryTest.cpp
index e444f68..131a3ca 100644
--- a/lldb/unittests/Target/MemoryTest.cpp
+++ b/lldb/unittests/Target/MemoryTest.cpp
@@ -48,6 +48,8 @@ public:
}
Status DoDestroy() override { return {}; }
void RefreshStateAfterStop() override {}
+ // Required by Target::ReadMemory() to call Process::ReadMemory()
+ bool IsAlive() override { return true; }
size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
Status &error) override {
if (m_bytes_left == 0)
@@ -61,7 +63,7 @@ public:
m_bytes_left -= size;
}
- memset(buf, 'B', num_bytes_to_write);
+ memset(buf, m_filler, num_bytes_to_write);
return num_bytes_to_write;
}
bool DoUpdateThreadList(ThreadList &old_thread_list,
@@ -72,8 +74,10 @@ public:
// Test-specific additions
size_t m_bytes_left;
+ int m_filler = 'B';
MemoryCache &GetMemoryCache() { return m_memory_cache; }
void SetMaxReadSize(size_t size) { m_bytes_left = size; }
+ void SetFiller(int filler) { m_filler = filler; }
};
} // namespace
@@ -85,6 +89,18 @@ TargetSP CreateTarget(DebuggerSP &debugger_sp, ArchSpec &arch) {
return target_sp;
}
+static ProcessSP CreateProcess(lldb::TargetSP target_sp) {
+ ListenerSP listener_sp(Listener::MakeListener("dummy"));
+ ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp);
+
+ struct TargetHack : public Target {
+ void SetProcess(ProcessSP process) { m_process_sp = process; }
+ };
+ static_cast<TargetHack *>(target_sp.get())->SetProcess(process_sp);
+
+ return process_sp;
+}
+
TEST_F(MemoryTest, TesetMemoryCacheRead) {
ArchSpec arch("x86_64-apple-macosx-");
@@ -96,8 +112,7 @@ TEST_F(MemoryTest, TesetMemoryCacheRead) {
TargetSP target_sp = CreateTarget(debugger_sp, arch);
ASSERT_TRUE(target_sp);
- ListenerSP listener_sp(Listener::MakeListener("dummy"));
- ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp);
+ ProcessSP process_sp = CreateProcess(target_sp);
ASSERT_TRUE(process_sp);
DummyProcess *process = static_cast<DummyProcess *>(process_sp.get());
@@ -227,6 +242,58 @@ TEST_F(MemoryTest, TesetMemoryCacheRead) {
// old cache
}
+TEST_F(MemoryTest, TestReadInteger) {
+ ArchSpec arch("x86_64-apple-macosx-");
+
+ Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));
+
+ DebuggerSP debugger_sp = Debugger::CreateInstance();
+ ASSERT_TRUE(debugger_sp);
+
+ TargetSP target_sp = CreateTarget(debugger_sp, arch);
+ ASSERT_TRUE(target_sp);
+
+ ProcessSP process_sp = CreateProcess(target_sp);
+ ASSERT_TRUE(process_sp);
+
+ DummyProcess *process = static_cast<DummyProcess *>(process_sp.get());
+ Status error;
+
+ process->SetFiller(0xff);
+ process->SetMaxReadSize(256);
+ // The ReadSignedIntegerFromMemory() methods return int64_t. Check that they
+ // extend the sign correctly when reading 32-bit values.
+ EXPECT_EQ(-1,
+ target_sp->ReadSignedIntegerFromMemory(Address(0), 4, 0, error));
+ EXPECT_EQ(-1, process->ReadSignedIntegerFromMemory(0, 4, 0, error));
+ // Check reading 64-bit values as well.
+ EXPECT_EQ(-1,
+ target_sp->ReadSignedIntegerFromMemory(Address(0), 8, 0, error));
+ EXPECT_EQ(-1, process->ReadSignedIntegerFromMemory(0, 8, 0, error));
+
+ // ReadUnsignedIntegerFromMemory() should not extend the sign.
+ EXPECT_EQ(0xffffffffULL,
+ target_sp->ReadUnsignedIntegerFromMemory(Address(0), 4, 0, error));
+ EXPECT_EQ(0xffffffffULL,
+ process->ReadUnsignedIntegerFromMemory(0, 4, 0, error));
+ EXPECT_EQ(0xffffffffffffffffULL,
+ target_sp->ReadUnsignedIntegerFromMemory(Address(0), 8, 0, error));
+ EXPECT_EQ(0xffffffffffffffffULL,
+ process->ReadUnsignedIntegerFromMemory(0, 8, 0, error));
+
+ // Check reading positive values.
+ process->GetMemoryCache().Clear();
+ process->SetFiller(0x7f);
+ process->SetMaxReadSize(256);
+ EXPECT_EQ(0x7f7f7f7fLL,
+ target_sp->ReadSignedIntegerFromMemory(Address(0), 4, 0, error));
+ EXPECT_EQ(0x7f7f7f7fLL, process->ReadSignedIntegerFromMemory(0, 4, 0, error));
+ EXPECT_EQ(0x7f7f7f7f7f7f7f7fLL,
+ target_sp->ReadSignedIntegerFromMemory(Address(0), 8, 0, error));
+ EXPECT_EQ(0x7f7f7f7f7f7f7f7fLL,
+ process->ReadSignedIntegerFromMemory(0, 8, 0, error));
+}
+
/// A process class that, when asked to read memory from some address X, returns
/// the least significant byte of X.
class DummyReaderProcess : public Process {
diff --git a/lldb/unittests/Target/RemoteAwarePlatformTest.cpp b/lldb/unittests/Target/RemoteAwarePlatformTest.cpp
index 3278674..cfcec69 100644
--- a/lldb/unittests/Target/RemoteAwarePlatformTest.cpp
+++ b/lldb/unittests/Target/RemoteAwarePlatformTest.cpp
@@ -32,15 +32,12 @@ public:
ProcessSP(ProcessAttachInfo &, Debugger &, Target *, Status &));
MOCK_METHOD0(CalculateTrapHandlerSymbolNames, void());
- MOCK_METHOD2(ResolveExecutable,
- std::pair<bool, ModuleSP>(const ModuleSpec &,
- const FileSpecList *));
- Status
- ResolveExecutable(const ModuleSpec &module_spec,
- lldb::ModuleSP &exe_module_sp,
- const FileSpecList *module_search_paths_ptr) /*override*/
+ MOCK_METHOD1(ResolveExecutable,
+ std::pair<bool, ModuleSP>(const ModuleSpec &));
+ Status ResolveExecutable(const ModuleSpec &module_spec,
+ lldb::ModuleSP &exe_module_sp) /*override*/
{ // NOLINT(modernize-use-override)
- auto pair = ResolveExecutable(module_spec, module_search_paths_ptr);
+ auto pair = ResolveExecutable(module_spec);
exe_module_sp = pair.second;
return pair.first ? Status() : Status::FromErrorString("error");
}
@@ -80,14 +77,14 @@ TEST_F(RemoteAwarePlatformTest, TestResolveExecutabelOnClientByPlatform) {
static const ArchSpec process_host_arch;
EXPECT_CALL(platform, GetSupportedArchitectures(process_host_arch))
.WillRepeatedly(Return(std::vector<ArchSpec>()));
- EXPECT_CALL(platform, ResolveExecutable(_, _))
+ EXPECT_CALL(platform, ResolveExecutable(_))
.WillRepeatedly(Return(std::make_pair(true, expected_executable)));
platform.SetRemotePlatform(std::make_shared<TargetPlatformTester>(false));
ModuleSP resolved_sp;
lldb_private::Status status =
- platform.ResolveExecutable(executable_spec, resolved_sp, nullptr);
+ platform.ResolveExecutable(executable_spec, resolved_sp);
ASSERT_TRUE(status.Success());
EXPECT_EQ(expected_executable.get(), resolved_sp.get());
diff --git a/lldb/unittests/TestingSupport/TestUtilities.cpp b/lldb/unittests/TestingSupport/TestUtilities.cpp
index b53822e..d164c22 100644
--- a/lldb/unittests/TestingSupport/TestUtilities.cpp
+++ b/lldb/unittests/TestingSupport/TestUtilities.cpp
@@ -20,6 +20,11 @@ using namespace lldb_private;
extern const char *TestMainArgv0;
std::once_flag TestUtilities::g_debugger_initialize_flag;
+
+std::string lldb_private::PrettyPrint(const llvm::json::Value &value) {
+ return llvm::formatv("{0:2}", value).str();
+}
+
std::string lldb_private::GetInputFilePath(const llvm::Twine &name) {
llvm::SmallString<128> result = llvm::sys::path::parent_path(TestMainArgv0);
llvm::sys::fs::make_absolute(result);
diff --git a/lldb/unittests/TestingSupport/TestUtilities.h b/lldb/unittests/TestingSupport/TestUtilities.h
index cc93a68..f05d176 100644
--- a/lldb/unittests/TestingSupport/TestUtilities.h
+++ b/lldb/unittests/TestingSupport/TestUtilities.h
@@ -30,6 +30,10 @@
}
namespace lldb_private {
+
+/// Returns a pretty printed json string of a `llvm::json::Value`.
+std::string PrettyPrint(const llvm::json::Value &E);
+
std::string GetInputFilePath(const llvm::Twine &name);
class TestUtilities {
diff --git a/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp b/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp
index eaf23fd..b72abc1 100644
--- a/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp
+++ b/lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp
@@ -856,3 +856,233 @@ TEST_F(TestArm64InstEmulation, TestCFAResetToSP) {
EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
}
+
+TEST_F(TestArm64InstEmulation, TestPrologueStartsWithStrD8) {
+ ArchSpec arch("aarch64");
+ std::unique_ptr<UnwindAssemblyInstEmulation> engine(
+ static_cast<UnwindAssemblyInstEmulation *>(
+ UnwindAssemblyInstEmulation::CreateInstance(arch)));
+ ASSERT_NE(nullptr, engine);
+
+ const UnwindPlan::Row *row;
+ AddressRange sample_range;
+ UnwindPlan unwind_plan(eRegisterKindLLDB);
+ UnwindPlan::Row::AbstractRegisterLocation regloc;
+
+ // The sample function is built with 'clang --target aarch64 -O1':
+ //
+ // int bar(float x);
+ // int foo(float x) {
+ // return bar(x) + bar(x);
+ // }
+ //
+ // The function uses one floating point register and spills it with
+ // 'str d8, [sp, #-0x20]!'.
+
+ // clang-format off
+ uint8_t data[] = {
+ // prologue
+ 0xe8, 0x0f, 0x1e, 0xfc, // 0: fc1e0fe8 str d8, [sp, #-0x20]!
+ 0xfd, 0xfb, 0x00, 0xa9, // 4: a900fbfd stp x29, x30, [sp, #0x8]
+ 0xf3, 0x0f, 0x00, 0xf9, // 8: f9000ff3 str x19, [sp, #0x18]
+ 0xfd, 0x23, 0x00, 0x91, // 12: 910023fd add x29, sp, #0x8
+
+ // epilogue
+ 0xfd, 0xfb, 0x40, 0xa9, // 16: a940fbfd ldp x29, x30, [sp, #0x8]
+ 0xf3, 0x0f, 0x40, 0xf9, // 20: f9400ff3 ldr x19, [sp, #0x18]
+ 0xe8, 0x07, 0x42, 0xfc, // 24: fc4207e8 ldr d8, [sp], #0x20
+ 0xc0, 0x03, 0x5f, 0xd6, // 28: d65f03c0 ret
+ };
+ // clang-format on
+
+ // UnwindPlan we expect:
+ // 0: CFA=sp +0 =>
+ // 4: CFA=sp+32 => d8=[CFA-32]
+ // 8: CFA=sp+32 => fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
+ // 12: CFA=sp+32 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
+ // 16: CFA=fp+24 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
+ // 20: CFA=sp+32 => x19=[CFA-8] fp=<same> lr=<same> d8=[CFA-32]
+ // 24: CFA=sp+32 => x19=<same> fp=<same> lr=<same> d8=[CFA-32]
+ // 28: CFA=sp +0 => x19=<same> fp=<same> lr=<same> d8=<same>
+
+ sample_range = AddressRange(0x1000, sizeof(data));
+
+ EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
+ sample_range, data, sizeof(data), unwind_plan));
+
+ // 4: CFA=sp+32 => d8=[CFA-32]
+ row = unwind_plan.GetRowForFunctionOffset(4);
+ EXPECT_EQ(4, row->GetOffset());
+ EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
+ EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
+ EXPECT_EQ(32, row->GetCFAValue().GetOffset());
+
+ EXPECT_TRUE(row->GetRegisterInfo(fpu_d8_arm64, regloc));
+ EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
+ EXPECT_EQ(-32, regloc.GetOffset());
+
+ // 16: CFA=fp+24 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]
+ row = unwind_plan.GetRowForFunctionOffset(16);
+ EXPECT_EQ(16, row->GetOffset());
+ EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
+ EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
+ EXPECT_EQ(24, row->GetCFAValue().GetOffset());
+
+ EXPECT_TRUE(row->GetRegisterInfo(gpr_x19_arm64, regloc));
+ EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
+ EXPECT_EQ(-8, regloc.GetOffset());
+
+ EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
+ EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
+ EXPECT_EQ(-24, regloc.GetOffset());
+
+ EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));
+ EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
+ EXPECT_EQ(-16, regloc.GetOffset());
+
+ EXPECT_TRUE(row->GetRegisterInfo(fpu_d8_arm64, regloc));
+ EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
+ EXPECT_EQ(-32, regloc.GetOffset());
+
+ // 28: CFA=sp +0 => x19=<same> fp=<same> lr=<same> d8=<same>
+ row = unwind_plan.GetRowForFunctionOffset(28);
+ EXPECT_EQ(28, row->GetOffset());
+ EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
+ EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
+ EXPECT_EQ(0, row->GetCFAValue().GetOffset());
+
+ if (row->GetRegisterInfo(gpr_x19_arm64, regloc)) {
+ EXPECT_TRUE(regloc.IsSame());
+ }
+ if (row->GetRegisterInfo(gpr_fp_arm64, regloc)) {
+ EXPECT_TRUE(regloc.IsSame());
+ }
+ if (row->GetRegisterInfo(gpr_lr_arm64, regloc)) {
+ EXPECT_TRUE(regloc.IsSame());
+ }
+ if (row->GetRegisterInfo(fpu_d8_arm64, regloc)) {
+ EXPECT_TRUE(regloc.IsSame());
+ }
+}
+
+TEST_F(TestArm64InstEmulation, TestMidFunctionEpilogueAndBackwardsJump) {
+ ArchSpec arch("arm64-apple-ios15");
+ std::unique_ptr<UnwindAssemblyInstEmulation> engine(
+ static_cast<UnwindAssemblyInstEmulation *>(
+ UnwindAssemblyInstEmulation::CreateInstance(arch)));
+ ASSERT_NE(nullptr, engine);
+
+ const UnwindPlan::Row *row;
+ AddressRange sample_range;
+ UnwindPlan unwind_plan(eRegisterKindLLDB);
+ UnwindPlan::Row::AbstractRegisterLocation regloc;
+
+ // clang-format off
+ uint8_t data[] = {
+ 0xff, 0xc3, 0x00, 0xd1, // <+0>: sub sp, sp, #0x30
+ 0xfd, 0x7b, 0x02, 0xa9, // <+4>: stp x29, x30, [sp, #0x20]
+ 0xfd, 0x83, 0x00, 0x91, // <+8>: add x29, sp, #0x20
+ 0x1f, 0x04, 0x00, 0xf1, // <+12>: cmp x0, #0x1
+ 0x21, 0x01, 0x00, 0x54, // <+16>: b.ne ; <+52> DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE
+ 0xfd, 0x7b, 0x42, 0xa9, // <+20>: ldp x29, x30, [sp, #0x20]
+ 0xff, 0xc3, 0x00, 0x91, // <+24>: add sp, sp, #0x30
+ 0xc0, 0x03, 0x5f, 0xd6, // <+28>: ret
+ // AFTER_EPILOGUE
+ 0x37, 0x00, 0x80, 0xd2, // <+32>: mov x23, #0x1
+ 0xf6, 0x5f, 0x41, 0xa9, // <+36>: ldp x22, x23, [sp, #0x10]
+ 0xfd, 0x7b, 0x42, 0xa9, // <+40>: ldp x29, x30, [sp, #0x20]
+ 0xff, 0xc3, 0x00, 0x91, // <+44>: add sp, sp, #0x30
+ 0xc0, 0x03, 0x5f, 0xd6, // <+48>: ret
+ // DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE
+ 0xf6, 0x5f, 0x01, 0xa9, // <+52>: stp x22, x23, [sp, #0x10]
+ 0x36, 0x00, 0x80, 0xd2, // <+56>: mov x22, #0x1
+ 0x37, 0x00, 0x80, 0xd2, // <+60>: mov x23, #0x1
+ 0xf8, 0xff, 0xff, 0x17, // <+64>: b ; <+32> AFTER_EPILOGUE
+ };
+
+ // UnwindPlan we expect:
+ // row[0]: 0: CFA=sp +0 =>
+ // row[1]: 4: CFA=sp+48 =>
+ // row[2]: 8: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
+ // row[3]: 12: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
+ // row[4]: 24: CFA=sp+48 => fp=<same> lr=<same>
+ //
+ // This must come from +56
+ // row[5]: 32: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] x22=[CFA-32], x23=[CFA-24]
+ // row[6]: 40: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] x22=same, x23 = same
+ // row[6]: 44: CFA=sp+48 => fp=same lr=same x22=same, x23 = same
+ // row[6]: 48: CFA=sp0 => fp=same lr=same x22=same, x23 = same
+ //
+ // row[x]: 52: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
+ // row[x]: 56: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] x22=[CFA-32], x23=[CFA-24]
+ // clang-format on
+
+ sample_range = AddressRange(0x1000, sizeof(data));
+
+ EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
+ sample_range, data, sizeof(data), unwind_plan));
+
+ // At the end of prologue (+12), CFA = fp + 16.
+ // <+0>: sub sp, sp, #0x30
+ // <+4>: stp x29, x30, [sp, #0x20]
+ // <+8>: add x29, sp, #0x20
+ row = unwind_plan.GetRowForFunctionOffset(12);
+ EXPECT_EQ(12, row->GetOffset());
+ EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());
+ EXPECT_EQ(row->GetCFAValue().GetRegisterNumber(), gpr_fp_arm64);
+ EXPECT_EQ(row->GetCFAValue().GetOffset(), 16);
+
+ // +16 and +20 are the same as +12.
+ // <+12>: cmp x0, #0x1
+ // <+16>: b.ne ; <+52> DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE
+ EXPECT_EQ(12, unwind_plan.GetRowForFunctionOffset(16)->GetOffset());
+ EXPECT_EQ(12, unwind_plan.GetRowForFunctionOffset(20)->GetOffset());
+
+ // After restoring $fp to caller's value, CFA = $sp + 48
+ // <+20>: ldp x29, x30, [sp, #0x20]
+ row = unwind_plan.GetRowForFunctionOffset(24);
+ EXPECT_EQ(24, row->GetOffset());
+ EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());
+ EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
+ EXPECT_EQ(row->GetCFAValue().GetOffset(), 48);
+
+ // $sp has been restored
+ // <+24>: add sp, sp, #0x30
+ row = unwind_plan.GetRowForFunctionOffset(28);
+ EXPECT_EQ(28, row->GetOffset());
+ EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());
+ EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
+ EXPECT_EQ(row->GetCFAValue().GetOffset(), 0);
+
+ // Row for offset +32 should not inherit the state of the `ret` instruction
+ // in +28. Instead, it should inherit the state of the branch in +64.
+ // Check for register x22, which is available in row +64.
+ // <+28>: ret
+ // <+32>: mov x23, #0x1
+ row = unwind_plan.GetRowForFunctionOffset(32);
+ EXPECT_EQ(32, row->GetOffset());
+ {
+ UnwindPlan::Row::AbstractRegisterLocation loc;
+ EXPECT_TRUE(row->GetRegisterInfo(gpr_x22_arm64, loc));
+ EXPECT_TRUE(loc.IsAtCFAPlusOffset());
+ EXPECT_EQ(loc.GetOffset(), -32);
+ }
+
+ // Check that the state of this branch
+ // <+16>: b.ne ; <+52> DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE
+ // was forwarded to the branch target:
+ // <+52>: stp x22, x23, [sp, #0x10]
+ row = unwind_plan.GetRowForFunctionOffset(52);
+ EXPECT_EQ(52, row->GetOffset());
+ EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());
+ EXPECT_EQ(row->GetCFAValue().GetRegisterNumber(), gpr_fp_arm64);
+ EXPECT_EQ(row->GetCFAValue().GetOffset(), 16);
+
+ row = unwind_plan.GetRowForFunctionOffset(64);
+ {
+ UnwindPlan::Row::AbstractRegisterLocation loc;
+ EXPECT_TRUE(row->GetRegisterInfo(gpr_x22_arm64, loc));
+ EXPECT_TRUE(loc.IsAtCFAPlusOffset());
+ EXPECT_EQ(loc.GetOffset(), -32);
+ }
+}
diff --git a/lldb/unittests/Utility/CMakeLists.txt b/lldb/unittests/Utility/CMakeLists.txt
index aed4177..4cbe15b 100644
--- a/lldb/unittests/Utility/CMakeLists.txt
+++ b/lldb/unittests/Utility/CMakeLists.txt
@@ -10,7 +10,6 @@ add_lldb_unittest(UtilityTests
DataBufferTest.cpp
DataEncoderTest.cpp
DataExtractorTest.cpp
- DiagnosticsRenderingTest.cpp
EnvironmentTest.cpp
EventTest.cpp
FileSpecListTest.cpp
@@ -48,6 +47,7 @@ add_lldb_unittest(UtilityTests
UserIDResolverTest.cpp
UUIDTest.cpp
VASprintfTest.cpp
+ VirtualDataExtractorTest.cpp
VMRangeTest.cpp
XcodeSDKTest.cpp
diff --git a/lldb/unittests/Utility/RegisterValueTest.cpp b/lldb/unittests/Utility/RegisterValueTest.cpp
index 6239dbe..7b27e84 100644
--- a/lldb/unittests/Utility/RegisterValueTest.cpp
+++ b/lldb/unittests/Utility/RegisterValueTest.cpp
@@ -57,13 +57,12 @@ TEST(RegisterValueTest, GetScalarValue) {
APInt(128, 0x7766554433221100)));
}
-static const Scalar etalon128(APInt(128, 0xffeeddccbbaa9988ull) << 64 |
- APInt(128, 0x7766554433221100ull));
-
-void TestSetValueFromData128(void *src, const lldb::ByteOrder endianness) {
- RegisterInfo ri{"uint128_register",
+void TestSetValueFromData(const Scalar &etalon, void *src, size_t src_byte_size,
+ const lldb::ByteOrder endianness,
+ const RegisterValue::Type register_value_type) {
+ RegisterInfo ri{"test",
nullptr,
- 16,
+ static_cast<uint32_t>(src_byte_size),
0,
lldb::Encoding::eEncodingUint,
lldb::Format::eFormatDefault,
@@ -71,26 +70,289 @@ void TestSetValueFromData128(void *src, const lldb::ByteOrder endianness) {
nullptr,
nullptr,
nullptr};
- DataExtractor src_extractor(src, 16, endianness, 8);
+ DataExtractor src_extractor(src, src_byte_size, endianness, 8);
RegisterValue rv;
EXPECT_TRUE(rv.SetValueFromData(ri, src_extractor, 0, false).Success());
Scalar s;
EXPECT_TRUE(rv.GetScalarValue(s));
- EXPECT_EQ(s, etalon128);
+ EXPECT_EQ(rv.GetType(), register_value_type);
+ EXPECT_EQ(s, etalon);
+}
+
+static const Scalar etalon7(APInt(32, 0x0000007F));
+
+TEST(RegisterValueTest, SetValueFromData_7_le) {
+ uint8_t src[] = {0x7F};
+ TestSetValueFromData(etalon7, src, 1, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt8);
+}
+
+TEST(RegisterValueTest, SetValueFromData_7_be) {
+ uint8_t src[] = {0x7F};
+ TestSetValueFromData(etalon7, src, 1, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt8);
+}
+
+static const Scalar etalon8(APInt(32, 0x000000FE));
+
+TEST(RegisterValueTest, SetValueFromData_8_le) {
+ uint8_t src[] = {0xFE};
+ TestSetValueFromData(etalon8, src, 1, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt8);
+}
+
+TEST(RegisterValueTest, SetValueFromData_8_be) {
+ uint8_t src[] = {0xFE};
+ TestSetValueFromData(etalon8, src, 1, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt8);
+}
+
+static const Scalar etalon9(APInt(32, 0x000001FE));
+
+TEST(RegisterValueTest, SetValueFromData_9_le) {
+ uint8_t src[] = {0xFE, 0x01};
+ TestSetValueFromData(etalon9, src, 2, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt16);
+}
+
+TEST(RegisterValueTest, SetValueFromData_9_be) {
+ uint8_t src[] = {0x01, 0xFE};
+ TestSetValueFromData(etalon9, src, 2, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt16);
+}
+
+static const Scalar etalon15(APInt(32, 0x00007FED));
+
+TEST(RegisterValueTest, SetValueFromData_15_le) {
+ uint8_t src[] = {0xED, 0x7F};
+ TestSetValueFromData(etalon15, src, 2, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt16);
+}
+
+TEST(RegisterValueTest, SetValueFromData_15_be) {
+ uint8_t src[] = {0x7F, 0xED};
+ TestSetValueFromData(etalon15, src, 2, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt16);
+}
+
+static const Scalar etalon16(APInt(32, 0x0000FEDC));
+
+TEST(RegisterValueTest, SetValueFromData_16_le) {
+ uint8_t src[] = {0xDC, 0xFE};
+ TestSetValueFromData(etalon16, src, 2, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt16);
+}
+
+TEST(RegisterValueTest, SetValueFromData_16_be) {
+ uint8_t src[] = {0xFE, 0xDC};
+ TestSetValueFromData(etalon16, src, 2, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt16);
+}
+
+static const Scalar etalon17(APInt(32, 0x0001FEDC));
+
+TEST(RegisterValueTest, SetValueFromData_17_le) {
+ uint8_t src[] = {0xDC, 0xFE, 0x01};
+ TestSetValueFromData(etalon17, src, 3, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt32);
+}
+
+TEST(RegisterValueTest, SetValueFromData_17_be) {
+ uint8_t src[] = {0x01, 0xFE, 0xDC};
+ TestSetValueFromData(etalon17, src, 3, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt32);
+}
+
+static const Scalar etalon24(APInt(32, 0x00FEDCBA));
+
+TEST(RegisterValueTest, SetValueFromData_24_le) {
+ uint8_t src[] = {0xBA, 0xDC, 0xFE};
+ TestSetValueFromData(etalon24, src, 3, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt32);
+}
+
+TEST(RegisterValueTest, SetValueFromData_24_be) {
+ uint8_t src[] = {0xFE, 0xDC, 0xBA};
+ TestSetValueFromData(etalon24, src, 3, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt32);
+}
+
+static const Scalar etalon31(APInt(32, 0x7EDCBA98));
+
+TEST(RegisterValueTest, SetValueFromData_31_le) {
+ uint8_t src[] = {0x98, 0xBA, 0xDC, 0x7E};
+ TestSetValueFromData(etalon31, src, 4, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt32);
+}
+
+TEST(RegisterValueTest, SetValueFromData_31_be) {
+ uint8_t src[] = {0x7E, 0xDC, 0xBA, 0x98};
+ TestSetValueFromData(etalon31, src, 4, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt32);
+}
+
+static const Scalar etalon32(APInt(32, 0xFEDCBA98));
+
+TEST(RegisterValueTest, SetValueFromData_32_le) {
+ uint8_t src[] = {0x98, 0xBA, 0xDC, 0xFE};
+ TestSetValueFromData(etalon32, src, 4, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt32);
}
-// Test that the "RegisterValue::SetValueFromData" method works correctly
-// with 128-bit little-endian data that represents an integer.
+TEST(RegisterValueTest, SetValueFromData_32_be) {
+ uint8_t src[] = {0xFE, 0xDC, 0xBA, 0x98};
+ TestSetValueFromData(etalon32, src, 4, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt32);
+}
+
+static const Scalar etalon33(APInt(64, 0x00000001FEDCBA98));
+
+TEST(RegisterValueTest, SetValueFromData_33_le) {
+ uint8_t src[] = {0x98, 0xBA, 0xDC, 0xFE, 0x01};
+ TestSetValueFromData(etalon33, src, 5, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt64);
+}
+
+TEST(RegisterValueTest, SetValueFromData_33_be) {
+ uint8_t src[] = {0x01, 0xFE, 0xDC, 0xBA, 0x98};
+ TestSetValueFromData(etalon33, src, 5, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt64);
+}
+
+static const Scalar etalon40(APInt(64, 0x000000FEDCBA9876));
+
+TEST(RegisterValueTest, SetValueFromData_40_le) {
+ uint8_t src[] = {0x76, 0x98, 0xBA, 0xDC, 0xFE};
+ TestSetValueFromData(etalon40, src, 5, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt64);
+}
+
+TEST(RegisterValueTest, SetValueFromData_40_be) {
+ uint8_t src[] = {0xFE, 0xDC, 0xBA, 0x98, 0x76};
+ TestSetValueFromData(etalon40, src, 5, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt64);
+}
+
+static const Scalar etalon63(APInt(64, 0x7EDCBA9876543210));
+
+TEST(RegisterValueTest, SetValueFromData_63_le) {
+ uint8_t src[] = {0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0x7E};
+ TestSetValueFromData(etalon63, src, 8, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt64);
+}
+
+TEST(RegisterValueTest, SetValueFromData_63_be) {
+ uint8_t src[] = {0x7E, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
+ TestSetValueFromData(etalon63, src, 8, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt64);
+}
+
+static const Scalar etalon64(APInt(64, 0xFEDCBA9876543210));
+
+TEST(RegisterValueTest, SetValueFromData_64_le) {
+ uint8_t src[] = {0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE};
+ TestSetValueFromData(etalon64, src, 8, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUInt64);
+}
+
+TEST(RegisterValueTest, SetValueFromData_64_be) {
+ uint8_t src[] = {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
+ TestSetValueFromData(etalon64, src, 8, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUInt64);
+}
+
+static const Scalar etalon65(APInt(72, 0x0000000000000001ull) << 1 * 64 |
+ APInt(72, 0x0706050403020100ull) << 0 * 64);
+
+TEST(RegisterValueTest, SetValueFromData_65_le) {
+ uint8_t src[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01};
+ TestSetValueFromData(etalon65, src, 9, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUIntN);
+}
+
+TEST(RegisterValueTest, SetValueFromData_65_be) {
+ uint8_t src[] = {0x01, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
+ TestSetValueFromData(etalon65, src, 9, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUIntN);
+}
+
+static const Scalar etalon127(APInt(128, 0x7f0e0d0c0b0a0908ull) << 1 * 64 |
+ APInt(128, 0x0706050403020100ull) << 0 * 64);
+
+TEST(RegisterValueTest, SetValueFromData_127_le) {
+ uint8_t src[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x7f};
+ TestSetValueFromData(etalon127, src, 16, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUIntN);
+}
+
+TEST(RegisterValueTest, SetValueFromData_127_be) {
+ uint8_t src[] = {0x7f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
+ TestSetValueFromData(etalon127, src, 16, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUIntN);
+}
+
+static const Scalar etalon128(APInt(128, 0x0f0e0d0c0b0a0908ull) << 1 * 64 |
+ APInt(128, 0x0706050403020100ull) << 0 * 64);
+
TEST(RegisterValueTest, SetValueFromData_128_le) {
- uint8_t src[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
- 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
- TestSetValueFromData128(src, lldb::ByteOrder::eByteOrderLittle);
+ uint8_t src[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+ TestSetValueFromData(etalon128, src, 16, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUIntN);
}
-// Test that the "RegisterValue::SetValueFromData" method works correctly
-// with 128-bit big-endian data that represents an integer.
TEST(RegisterValueTest, SetValueFromData_128_be) {
- uint8_t src[] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
- 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
- TestSetValueFromData128(src, lldb::ByteOrder::eByteOrderBig);
+ uint8_t src[] = {0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
+ TestSetValueFromData(etalon128, src, 16, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUIntN);
+}
+
+static const Scalar etalon256(APInt(256, 0x1f1e1d1c1b1a1918ull) << 3 * 64 |
+ APInt(256, 0x1716151413121110ull) << 2 * 64 |
+ APInt(256, 0x0f0e0d0c0b0a0908ull) << 1 * 64 |
+ APInt(256, 0x0706050403020100ull) << 0 * 64);
+
+TEST(RegisterValueTest, SetValueFromData_256_le) {
+ uint8_t src[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f};
+ TestSetValueFromData(etalon256, src, 32, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUIntN);
+}
+
+TEST(RegisterValueTest, SetValueFromData_256_be) {
+ uint8_t src[] = {0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
+ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+ 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
+ TestSetValueFromData(etalon256, src, 32, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUIntN);
+}
+
+static const Scalar etalon257(APInt(512, 0x0000000000000001ull) << 4 * 64 |
+ APInt(512, 0x1f1e1d1c1b1a1918ull) << 3 * 64 |
+ APInt(512, 0x1716151413121110ull) << 2 * 64 |
+ APInt(512, 0x0f0e0d0c0b0a0908ull) << 1 * 64 |
+ APInt(512, 0x0706050403020100ull) << 0 * 64);
+
+TEST(RegisterValueTest, SetValueFromData_257_le) {
+ uint8_t src[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
+ 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x01};
+ TestSetValueFromData(etalon257, src, 33, lldb::ByteOrder::eByteOrderLittle,
+ RegisterValue::eTypeUIntN);
+}
+
+TEST(RegisterValueTest, SetValueFromData_257_be) {
+ uint8_t src[] = {0x01, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
+ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f,
+ 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06,
+ 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
+ TestSetValueFromData(etalon257, src, 33, lldb::ByteOrder::eByteOrderBig,
+ RegisterValue::eTypeUIntN);
}
diff --git a/lldb/unittests/Utility/VirtualDataExtractorTest.cpp b/lldb/unittests/Utility/VirtualDataExtractorTest.cpp
new file mode 100644
index 0000000..09f3edb
--- /dev/null
+++ b/lldb/unittests/Utility/VirtualDataExtractorTest.cpp
@@ -0,0 +1,583 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/VirtualDataExtractor.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+using Table = VirtualDataExtractor::LookupTable;
+using Entry = Table::Entry;
+
+TEST(VirtualDataExtractorTest, BasicConstruction) {
+ uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)});
+
+ EXPECT_EQ(extractor->GetByteSize(), 8U);
+}
+
+TEST(VirtualDataExtractorTest, GetDataAtVirtualOffset) {
+ uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ const void *data = extractor->GetData(&virtual_offset, 4);
+
+ ASSERT_NE(data, nullptr);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+ EXPECT_EQ(memcmp(data, buffer, 4), 0);
+}
+
+TEST(VirtualDataExtractorTest, GetDataAtVirtualOffsetInvalid) {
+ uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ // Try to read from an invalid virtual address.
+ offset_t virtual_offset = 0x2000;
+ const void *data = extractor->GetData(&virtual_offset, 4);
+
+ EXPECT_EQ(data, nullptr);
+}
+
+TEST(VirtualDataExtractorTest, GetU8AtVirtualOffset) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x12U);
+ EXPECT_EQ(virtual_offset, 0x1001U);
+
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x34U);
+ EXPECT_EQ(virtual_offset, 0x1002U);
+}
+
+TEST(VirtualDataExtractorTest, GetU16AtVirtualOffset) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU16(&virtual_offset), 0x3412U);
+ EXPECT_EQ(virtual_offset, 0x1002U);
+
+ EXPECT_EQ(extractor->GetU16(&virtual_offset), 0x7856U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}
+
+TEST(VirtualDataExtractorTest, GetU32AtVirtualOffset) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU32(&virtual_offset), 0x78563412U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+
+ EXPECT_EQ(extractor->GetU32(&virtual_offset), 0xF0DEBC9AU);
+ EXPECT_EQ(virtual_offset, 0x1008U);
+}
+
+TEST(VirtualDataExtractorTest, GetU64AtVirtualOffset) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 8, Table{Entry(0x1000, 8, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU64(&virtual_offset), 0xF0DEBC9A78563412ULL);
+ EXPECT_EQ(virtual_offset, 0x1008U);
+}
+
+TEST(VirtualDataExtractorTest, GetAddressAtVirtualOffset) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetAddress(&virtual_offset), 0x78563412U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}
+
+TEST(VirtualDataExtractorTest, BigEndian) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderBig, 4, Table{Entry(0x1000, 4, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU16(&virtual_offset), 0x1234U);
+ EXPECT_EQ(virtual_offset, 0x1002U);
+
+ EXPECT_EQ(extractor->GetU16(&virtual_offset), 0x5678U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}
+
+TEST(VirtualDataExtractorTest, MultipleEntries) {
+ // Create a buffer with distinct patterns for each section.
+ uint8_t buffer[] = {
+ 0x01, 0x02, 0x03, 0x04, // Physical offset 0-3.
+ 0x11, 0x12, 0x13, 0x14, // Physical offset 4-7.
+ 0x21, 0x22, 0x23, 0x24 // Physical offset 8-11.
+ };
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4,
+ Table{Entry(0x1000, 4, 0), // Virt 0x1000-0x1004
+ Entry(0x2000, 4, 4), // Virt 0x2000-0x2004
+ Entry(0x3000, 4, 8)}); // Virt 0x3000-0x3004
+
+ // Test reading from first virtual range.
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x01U);
+
+ // Test reading from second virtual range.
+ virtual_offset = 0x2000;
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x11U);
+
+ // Test reading from third virtual range.
+ virtual_offset = 0x3000;
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x21U);
+}
+
+TEST(VirtualDataExtractorTest, NonContiguousVirtualAddresses) {
+ uint8_t buffer[] = {0xAA, 0xBB, 0xCC, 0xDD};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4,
+ Table{Entry(0x1000, 2, 0), // Virt 0x1000-0x1002
+ Entry(0x5000, 2, 2)}); // Virt 0x5000-0x5002
+
+ // Test reading from first virtual range.
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU16(&virtual_offset), 0xBBAAU);
+
+ // Test reading from second virtual range (non-contiguous).
+ virtual_offset = 0x5000;
+ EXPECT_EQ(extractor->GetU16(&virtual_offset), 0xDDCCU);
+
+ // Test that gap between ranges is invalid.
+ virtual_offset = 0x3000;
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0U);
+}
+
+TEST(VirtualDataExtractorTest, SharedDataBuffer) {
+ // Test with shared_ptr to DataBuffer.
+ uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04};
+ auto data_sp = std::make_shared<DataBufferHeap>(buffer, sizeof(buffer));
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ data_sp, eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU32(&virtual_offset), 0x04030201U);
+}
+
+TEST(VirtualDataExtractorTest, NullPointerHandling) {
+ uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ // Test that passing nullptr returns default values.
+ EXPECT_EQ(extractor->GetU8(nullptr), 0U);
+ EXPECT_EQ(extractor->GetU16(nullptr), 0U);
+ EXPECT_EQ(extractor->GetU32(nullptr), 0U);
+ EXPECT_EQ(extractor->GetU64(nullptr), 0U);
+ EXPECT_EQ(extractor->GetAddress(nullptr), 0U);
+ EXPECT_EQ(extractor->GetData(nullptr, 4), nullptr);
+}
+
+TEST(VirtualDataExtractorTest, OffsetMapping) {
+ // Test that virtual to physical offset mapping works correctly.
+ uint8_t buffer[] = {0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD};
+
+ // Map virtual address 0x1000 to physical offset 4 (skipping first 4 bytes).
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 4)});
+
+ offset_t virtual_offset = 0x1000;
+ // Should read from physical offset 4, not 0.
+ EXPECT_EQ(extractor->GetU32(&virtual_offset), 0xDDCCBBAAU);
+}
+
+TEST(VirtualDataExtractorTest, GetU8Unchecked) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU8_unchecked(&virtual_offset), 0x12U);
+ EXPECT_EQ(virtual_offset, 0x1001U);
+
+ EXPECT_EQ(extractor->GetU8_unchecked(&virtual_offset), 0x34U);
+ EXPECT_EQ(virtual_offset, 0x1002U);
+}
+
+TEST(VirtualDataExtractorTest, GetU16Unchecked) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU16_unchecked(&virtual_offset), 0x3412U);
+ EXPECT_EQ(virtual_offset, 0x1002U);
+
+ EXPECT_EQ(extractor->GetU16_unchecked(&virtual_offset), 0x7856U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}
+
+TEST(VirtualDataExtractorTest, GetU32Unchecked) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU32_unchecked(&virtual_offset), 0x78563412U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+
+ EXPECT_EQ(extractor->GetU32_unchecked(&virtual_offset), 0xF0DEBC9AU);
+ EXPECT_EQ(virtual_offset, 0x1008U);
+}
+
+TEST(VirtualDataExtractorTest, GetU64Unchecked) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 8, Table{Entry(0x1000, 8, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU64_unchecked(&virtual_offset),
+ 0xF0DEBC9A78563412ULL);
+ EXPECT_EQ(virtual_offset, 0x1008U);
+}
+
+TEST(VirtualDataExtractorTest, GetMaxU64Unchecked) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)});
+
+ // Test various byte sizes.
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetMaxU64_unchecked(&virtual_offset, 1), 0x12U);
+ EXPECT_EQ(virtual_offset, 0x1001U);
+
+ virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetMaxU64_unchecked(&virtual_offset, 2), 0x3412U);
+ EXPECT_EQ(virtual_offset, 0x1002U);
+
+ virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetMaxU64_unchecked(&virtual_offset, 4), 0x78563412U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+
+ virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetMaxU64_unchecked(&virtual_offset, 8),
+ 0xF0DEBC9A78563412ULL);
+ EXPECT_EQ(virtual_offset, 0x1008U);
+}
+
+TEST(VirtualDataExtractorTest, GetAddressUnchecked) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetAddress_unchecked(&virtual_offset), 0x78563412U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}
+
+TEST(VirtualDataExtractorTest, UncheckedWithBigEndian) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderBig, 4, Table{Entry(0x1000, 4, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU16_unchecked(&virtual_offset), 0x1234U);
+ EXPECT_EQ(virtual_offset, 0x1002U);
+
+ EXPECT_EQ(extractor->GetU16_unchecked(&virtual_offset), 0x5678U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}
+
+TEST(VirtualDataExtractorTest, GetCStr) {
+ // Create buffer with null-terminated strings.
+ uint8_t buffer[] = {'H', 'e', 'l', 'l', 'o', '\0', 'W', 'o',
+ 'r', 'l', 'd', '\0', 'F', 'o', 'o', '\0'};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4,
+ Table{Entry(0x1000, 6, 0), Entry(0x2000, 12, 6)});
+
+ // Test reading first string.
+ offset_t virtual_offset = 0x1000;
+ const char *str1 = extractor->GetCStr(&virtual_offset);
+ ASSERT_NE(str1, nullptr);
+ EXPECT_STREQ(str1, "Hello");
+ EXPECT_EQ(virtual_offset, 0x1006U); // After "Hello\0"
+
+ // Test reading second string.
+ virtual_offset = 0x2000;
+ const char *str2 = extractor->GetCStr(&virtual_offset);
+ ASSERT_NE(str2, nullptr);
+ EXPECT_STREQ(str2, "World");
+ EXPECT_EQ(virtual_offset, 0x2006U); // After "World\0"
+}
+
+TEST(VirtualDataExtractorTest, GetFloat) {
+ // Create buffer with float value (IEEE 754 single precision).
+ // 3.14159f in little endian: 0xDB 0x0F 0x49 0x40
+ uint8_t buffer[] = {0xDB, 0x0F, 0x49, 0x40};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ float value = extractor->GetFloat(&virtual_offset);
+ EXPECT_NEAR(value, 3.14159f, 0.00001f);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}
+
+TEST(VirtualDataExtractorTest, GetDouble) {
+ // Create buffer with double value (IEEE 754 double precision).
+ // 3.14159265358979 in little endian
+ uint8_t buffer[] = {0x18, 0x2D, 0x44, 0x54, 0xFB, 0x21, 0x09, 0x40};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 8, Table{Entry(0x1000, 8, 0)});
+
+ offset_t virtual_offset = 0x1000;
+ double value = extractor->GetDouble(&virtual_offset);
+ EXPECT_NEAR(value, 3.14159265358979, 0.00000000000001);
+ EXPECT_EQ(virtual_offset, 0x1008U);
+}
+
+TEST(VirtualDataExtractorTest, GetULEB128) {
+ // ULEB128 encoding: 0x624 (1572 decimal) = 0xA4 0x0C
+ uint8_t buffer[] = {0xA4, 0x0C, 0xFF, 0x00, 0x7F, 0x80, 0x01};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 7, 0)});
+
+ // Test reading first ULEB128 value (1572).
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetULEB128(&virtual_offset), 1572U);
+ EXPECT_EQ(virtual_offset, 0x1002U);
+
+ // Test reading second ULEB128 value (127).
+ virtual_offset = 0x1004;
+ EXPECT_EQ(extractor->GetULEB128(&virtual_offset), 127U);
+ EXPECT_EQ(virtual_offset, 0x1005U);
+
+ // Test reading third ULEB128 value (128).
+ EXPECT_EQ(extractor->GetULEB128(&virtual_offset), 128U);
+ EXPECT_EQ(virtual_offset, 0x1007U);
+}
+
+TEST(VirtualDataExtractorTest, GetSLEB128) {
+ // SLEB128 encoding: -123 = 0x85 0x7F, 123 = 0xFB 0x00
+ uint8_t buffer[] = {0x85, 0x7F, 0xFB, 0x00};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ // Test reading negative SLEB128 value (-123).
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetSLEB128(&virtual_offset), -123);
+ EXPECT_EQ(virtual_offset, 0x1002U);
+
+ // Test reading positive SLEB128 value (123).
+ EXPECT_EQ(extractor->GetSLEB128(&virtual_offset), 123);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}
+
+TEST(VirtualDataExtractorTest, GetU8Array) {
+ uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)});
+
+ // Test reading array of 4 bytes.
+ offset_t virtual_offset = 0x1000;
+ uint8_t dst[4] = {0};
+ void *result = extractor->GetU8(&virtual_offset, dst, 4);
+ ASSERT_NE(result, nullptr);
+ EXPECT_EQ(dst[0], 0x01U);
+ EXPECT_EQ(dst[1], 0x02U);
+ EXPECT_EQ(dst[2], 0x03U);
+ EXPECT_EQ(dst[3], 0x04U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}
+
+TEST(VirtualDataExtractorTest, GetU16Array) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)});
+
+ // Test reading array of 3 uint16_t values.
+ offset_t virtual_offset = 0x1000;
+ uint16_t dst[3] = {0};
+ void *result = extractor->GetU16(&virtual_offset, dst, 3);
+ ASSERT_NE(result, nullptr);
+ EXPECT_EQ(dst[0], 0x3412U);
+ EXPECT_EQ(dst[1], 0x7856U);
+ EXPECT_EQ(dst[2], 0xBC9AU);
+ EXPECT_EQ(virtual_offset, 0x1006U);
+}
+
+TEST(VirtualDataExtractorTest, GetU32Array) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)});
+
+ // Test reading array of 2 uint32_t values.
+ offset_t virtual_offset = 0x1000;
+ uint32_t dst[2] = {0};
+ void *result = extractor->GetU32(&virtual_offset, dst, 2);
+ ASSERT_NE(result, nullptr);
+ EXPECT_EQ(dst[0], 0x78563412U);
+ EXPECT_EQ(dst[1], 0xF0DEBC9AU);
+ EXPECT_EQ(virtual_offset, 0x1008U);
+}
+
+TEST(VirtualDataExtractorTest, GetU64Array) {
+ uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 8, Table{Entry(0x1000, 16, 0)});
+
+ // Test reading array of 2 uint64_t values.
+ offset_t virtual_offset = 0x1000;
+ uint64_t dst[2] = {0};
+ void *result = extractor->GetU64(&virtual_offset, dst, 2);
+ ASSERT_NE(result, nullptr);
+ EXPECT_EQ(dst[0], 0x0807060504030201ULL);
+ EXPECT_EQ(dst[1], 0x1817161514131211ULL);
+ EXPECT_EQ(virtual_offset, 0x1010U);
+}
+
+TEST(VirtualDataExtractorTest, GetMaxU64WithVariableSizes) {
+ uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)});
+
+ // Test reading 3-byte value.
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetMaxU64(&virtual_offset, 3), 0x563412U);
+ EXPECT_EQ(virtual_offset, 0x1003U);
+
+ // Test reading 5-byte value.
+ virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetMaxU64(&virtual_offset, 5), 0x9A78563412ULL);
+ EXPECT_EQ(virtual_offset, 0x1005U);
+}
+
+TEST(VirtualDataExtractorTest, GetMaxS64) {
+ // Test with negative number (sign extension).
+ uint8_t buffer[] = {0xFF, 0xFF, 0xFF, 0xFF};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ // Test reading 1-byte signed value (-1).
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetMaxS64(&virtual_offset, 1), -1);
+ EXPECT_EQ(virtual_offset, 0x1001U);
+
+ // Test reading 2-byte signed value (-1).
+ virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetMaxS64(&virtual_offset, 2), -1);
+ EXPECT_EQ(virtual_offset, 0x1002U);
+}
+
+TEST(VirtualDataExtractorTest, CannotReadAcrossEntryBoundaries) {
+ // Create buffer with two separate regions.
+ uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x11, 0x12, 0x13, 0x14};
+
+ // First entry: virtual 0x1000-0x1004 maps to physical 0-4.
+ // Second entry: virtual 0x2000-0x2004 maps to physical 4-8.
+ // Note: there's a gap in virtual addresses (0x1004-0x2000).
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4,
+ Table{Entry(0x1000, 4, 0), Entry(0x2000, 4, 4)});
+
+ // Verify we can read within the first entry.
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU32(&virtual_offset), 0x04030201U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+
+ // Verify we can read within the second entry.
+ virtual_offset = 0x2000;
+ EXPECT_EQ(extractor->GetU32(&virtual_offset), 0x14131211U);
+ EXPECT_EQ(virtual_offset, 0x2004U);
+
+ // Verify we CANNOT read in the gap between entries.
+ // This address is not in any lookup table entry.
+ virtual_offset = 0x1500;
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0U);
+ EXPECT_EQ(virtual_offset, 0x1500U);
+
+ // Verify we CANNOT read data pointer from the gap.
+ virtual_offset = 0x1800;
+ const void *data = extractor->GetData(&virtual_offset, 1);
+ EXPECT_EQ(data, nullptr);
+ EXPECT_EQ(virtual_offset, 0x1800U); // Offset unchanged.
+
+ // Verify we can read individual bytes within each entry.
+ virtual_offset = 0x1003;
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x04U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+
+ // Verify we CANNOT read past the end of an entry.
+ virtual_offset = 0x1004;
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}
+
+TEST(VirtualDataExtractorTest, ReadExactlyAtEntryEnd) {
+ uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04};
+
+ lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>(
+ buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)});
+
+ // Reading exactly to the end of an entry should work.
+ offset_t virtual_offset = 0x1000;
+ EXPECT_EQ(extractor->GetU32(&virtual_offset), 0x04030201U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+
+ // But reading one byte past the end should fail.
+ virtual_offset = 0x1004;
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+
+ // Reading from just before the end should work for smaller sizes.
+ virtual_offset = 0x1003;
+ EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x04U);
+ EXPECT_EQ(virtual_offset, 0x1004U);
+}