diff options
| author | Augusto Noronha <anoronha@apple.com> | 2025-02-06 19:04:01 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-06 19:04:01 -0800 |
| commit | 0cbc4983adcdbbd85ccb38b2dfbfe5985367b1b2 (patch) | |
| tree | 2e38570729c780142c6f6811f9d7ebbc7e36df54 /lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp | |
| parent | 6f508492d13944edd0e7e70a3cc34eb29caeb8e9 (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.cpp | 243 |
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); +} |
