summaryrefslogtreecommitdiff
path: root/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp
diff options
context:
space:
mode:
authorAugusto Noronha <anoronha@apple.com>2025-02-06 19:04:01 -0800
committerGitHub <noreply@github.com>2025-02-06 19:04:01 -0800
commit0cbc4983adcdbbd85ccb38b2dfbfe5985367b1b2 (patch)
tree2e38570729c780142c6f6811f9d7ebbc7e36df54 /lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp
parent6f508492d13944edd0e7e70a3cc34eb29caeb8e9 (diff)
[lldb] Make ValueObjectDynamicValue::UpdateValue() point to a host b… (#125143)
…uffer ValueObjectDynamicValue::UpdateValue() assumes that the dynamic type found by GetDynamicTypeAndAddress() would return an address in the inferior. This commit makes it so it can deal with being passed a host address instead. This is needed downstream by the Swift fork. rdar://143357274
Diffstat (limited to 'lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp')
-rw-r--r--lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp243
1 files changed, 243 insertions, 0 deletions
diff --git a/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp b/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp
new file mode 100644
index 000000000000..0ae3963f0c83
--- /dev/null
+++ b/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp
@@ -0,0 +1,243 @@
+//===---DynamicValueObjectLocalBuffer.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 "Plugins/Platform/Linux/PlatformLinux.h"
+#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h"
+#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/Symbol/ClangTestUtils.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/ValueObject/ValueObject.h"
+#include "lldb/ValueObject/ValueObjectConstResult.h"
+
+#include "gtest/gtest.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::clang_utils;
+
+// This entire class is boilerplate.
+struct MockLanguage : public Language {
+
+ llvm::StringRef GetPluginName() override { return "MockLanguage"; }
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeC_plus_plus;
+ };
+
+ static Language *CreateInstance(lldb::LanguageType language) {
+ return new MockLanguage();
+ }
+ static void Initialize() {
+ PluginManager::RegisterPlugin("MockLanguage", "Mock Language",
+ CreateInstance);
+ };
+
+ static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }
+ bool IsSourceFile(llvm::StringRef file_path) const override { return true; }
+};
+LLDB_PLUGIN_DEFINE(MockLanguage)
+
+struct MockLanguageRuntime : public LanguageRuntime {
+ // This is the only method in this class that matters for this test.
+ // This will unconditionally succeed and return a type with size 4,
+ // a value_type of HostAddress, and a local buffer that points to the parent's
+ // local buffer.
+ // The tests will set that buffer to be either be larger or smaller than the
+ // type we're returning.
+ bool
+ GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &address,
+ Value::ValueType &value_type,
+ llvm::ArrayRef<uint8_t> &local_buffer) override {
+ auto ast = in_value.GetCompilerType()
+ .GetTypeSystem()
+ .dyn_cast_or_null<TypeSystemClang>();
+
+ auto int_type = createRecordWithField(
+ *ast, "TypeWitInt", ast->GetBasicType(lldb::BasicType::eBasicTypeInt),
+ "theIntField", LanguageType::eLanguageTypeC_plus_plus);
+ class_type_or_name.SetCompilerType(int_type);
+ local_buffer = {(uint8_t *)in_value.GetValue().GetScalar().ULongLong(
+ LLDB_INVALID_ADDRESS),
+ in_value.GetLocalBufferSize()};
+ value_type = Value::ValueType::HostAddress;
+
+ return true;
+ }
+
+ // All of this is boilerplate.
+ MockLanguageRuntime(Process *process) : LanguageRuntime(process) {}
+ llvm::StringRef GetPluginName() override { return "MockLanguageRuntime"; }
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeC_plus_plus;
+ }
+
+ llvm::Error GetObjectDescription(Stream &str, ValueObject &object) override {
+ return llvm::Error::success();
+ }
+
+ llvm::Error GetObjectDescription(Stream &str, Value &value,
+ ExecutionContextScope *exe_scope) override {
+ return llvm::Error::success();
+ }
+
+ bool CouldHaveDynamicValue(ValueObject &in_value) override { return true; }
+
+ TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) override {
+ return type_and_or_name;
+ }
+
+ lldb::BreakpointResolverSP
+ CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
+ bool throw_bp) override {
+ return lldb::BreakpointResolverSP();
+ }
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) override {
+ return {};
+ }
+
+ static LanguageRuntime *CreateInstance(Process *process,
+ LanguageType language) {
+ return new MockLanguageRuntime(process);
+ }
+
+ static void Initialize() {
+ PluginManager::RegisterPlugin(
+ "MockLanguageRuntime", "MockLanguageRuntime", CreateInstance,
+ [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP {
+ return {};
+ },
+ [](lldb::LanguageType language,
+ bool throw_bp) -> BreakpointPreconditionSP { return {}; });
+ }
+
+ static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }
+};
+LLDB_PLUGIN_DEFINE(MockLanguageRuntime)
+
+// This entire class is boilerplate.
+struct MockProcess : Process {
+ MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
+ : Process(target_sp, listener_sp) {}
+
+ llvm::StringRef GetPluginName() override { return "mock process"; }
+
+ bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
+ return false;
+ };
+
+ Status DoDestroy() override { return {}; }
+
+ void RefreshStateAfterStop() override {}
+
+ bool DoUpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) override {
+ return false;
+ };
+
+ size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ Status &error) override {
+ // No need to read memory in these tests.
+ return size;
+ }
+};
+
+class DynamicValueObjectLocalBufferTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ ArchSpec arch("i386-pc-linux");
+ Platform::SetHostPlatform(
+ platform_linux::PlatformLinux::CreateInstance(true, &arch));
+ // std::call_once(TestUtilities::g_debugger_initialize_flag,
+ // []() { Debugger::Initialize(nullptr); });
+ m_debugger_sp = Debugger::CreateInstance();
+ ASSERT_TRUE(m_debugger_sp);
+ m_debugger_sp->GetTargetList().CreateTarget(*m_debugger_sp, "", arch,
+ eLoadDependentsNo,
+ m_platform_sp, m_target_sp);
+ ASSERT_TRUE(m_target_sp);
+ ASSERT_TRUE(m_target_sp->GetArchitecture().IsValid());
+ ASSERT_TRUE(m_platform_sp);
+ m_listener_sp = Listener::MakeListener("dummy");
+ m_process_sp = std::make_shared<MockProcess>(m_target_sp, m_listener_sp);
+ ASSERT_TRUE(m_process_sp);
+ m_exe_ctx = ExecutionContext(m_process_sp);
+
+ m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>("test");
+ m_type_system = m_holder->GetAST();
+ LLDB_PLUGIN_INITIALIZE(MockLanguage);
+ LLDB_PLUGIN_INITIALIZE(MockLanguageRuntime);
+ }
+ void TearDown() override {
+ LLDB_PLUGIN_TERMINATE(MockLanguage);
+ LLDB_PLUGIN_TERMINATE(MockLanguageRuntime);
+ }
+
+ void TestValueObjectWithLocalBuffer(DataExtractor &data_extractor,
+ bool should_succeed) {
+ std::unique_ptr<TypeSystemClangHolder> holder =
+ std::make_unique<TypeSystemClangHolder>("test ASTContext");
+ TypeSystemClang *ast = holder->GetAST();
+ auto char_type = createRecordWithField(
+ *ast, "TypeWithChar",
+ ast->GetBasicType(lldb::BasicType::eBasicTypeChar), "theField");
+
+ ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
+ ConstString var_name("test_var");
+ auto valobj_sp = ValueObjectConstResult::Create(exe_scope, char_type,
+ var_name, data_extractor);
+ auto dyn_valobj = valobj_sp->GetDynamicValue(lldb::eDynamicCanRunTarget);
+ ASSERT_TRUE(dyn_valobj->GetValueIsValid() == should_succeed);
+ }
+
+ SubsystemRAII<FileSystem, HostInfo, platform_linux::PlatformLinux,
+ ScriptInterpreterNone>
+ m_subsystems;
+ std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder;
+ lldb::DebuggerSP m_debugger_sp;
+ lldb::TargetSP m_target_sp;
+ lldb::PlatformSP m_platform_sp;
+ lldb::ListenerSP m_listener_sp;
+ lldb::ProcessSP m_process_sp;
+ ExecutionContext m_exe_ctx;
+ TypeSystemClang *m_type_system;
+};
+
+TEST_F(DynamicValueObjectLocalBufferTest, BufferTooSmall) {
+ /// Test that a value object with a buffer to small to fit the
+ /// "dynamic" type will return an invalid dynamic value object.
+ u_int8_t value = 1;
+ ByteOrder endian = endian::InlHostByteOrder();
+ DataExtractor data_extractor{&value, sizeof(value), endian, 4};
+ TestValueObjectWithLocalBuffer(data_extractor, false);
+}
+
+TEST_F(DynamicValueObjectLocalBufferTest, BufferTooBig) {
+ /// Test that a value object with a buffer big enough fit the
+ /// "dynamic" type will return a valid dynamic value object.
+ uint64_t value = 1;
+ ByteOrder endian = endian::InlHostByteOrder();
+ DataExtractor data_extractor{&value, sizeof(value), endian, 4};
+ TestValueObjectWithLocalBuffer(data_extractor, true);
+}
+
+TEST_F(DynamicValueObjectLocalBufferTest, BufferExactlyRight) {
+ /// Test that a value object with a buffer exactly the size of the
+ /// "dynamic" type will return a valid dynamic value object.
+ uint32_t value = 1;
+ ByteOrder endian = endian::InlHostByteOrder();
+ DataExtractor data_extractor{&value, sizeof(value), endian, 4};
+ TestValueObjectWithLocalBuffer(data_extractor, true);
+}