239 lines · cpp
1//===---DynamicValueObjectLocalBuffer.cpp-----------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Plugins/Platform/Linux/PlatformLinux.h"10#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h"11#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"12#include "TestingSupport/SubsystemRAII.h"13#include "TestingSupport/Symbol/ClangTestUtils.h"14#include "lldb/Core/Debugger.h"15#include "lldb/Core/PluginManager.h"16#include "lldb/Target/Language.h"17#include "lldb/Target/LanguageRuntime.h"18#include "lldb/ValueObject/ValueObject.h"19#include "lldb/ValueObject/ValueObjectConstResult.h"20 21#include "gtest/gtest.h"22 23using namespace lldb;24using namespace lldb_private;25using namespace lldb_private::clang_utils;26 27// This entire class is boilerplate.28struct MockLanguage : public Language {29 30 llvm::StringRef GetPluginName() override { return "MockLanguage"; }31 lldb::LanguageType GetLanguageType() const override {32 return lldb::eLanguageTypeC_plus_plus;33 };34 35 static Language *CreateInstance(lldb::LanguageType language) {36 return new MockLanguage();37 }38 static void Initialize() {39 PluginManager::RegisterPlugin("MockLanguage", "Mock Language",40 CreateInstance);41 };42 43 static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }44 bool IsSourceFile(llvm::StringRef file_path) const override { return true; }45};46LLDB_PLUGIN_DEFINE(MockLanguage)47 48struct MockLanguageRuntime : public LanguageRuntime {49 // This is the only method in this class that matters for this test.50 // This will unconditionally succeed and return a type with size 4,51 // a value_type of HostAddress, and a local buffer that points to the parent's52 // local buffer.53 // The tests will set that buffer to be either be larger or smaller than the54 // type we're returning.55 bool56 GetDynamicTypeAndAddress(ValueObject &in_value,57 lldb::DynamicValueType use_dynamic,58 TypeAndOrName &class_type_or_name, Address &address,59 Value::ValueType &value_type,60 llvm::ArrayRef<uint8_t> &local_buffer) override {61 auto ast = in_value.GetCompilerType().GetTypeSystem<TypeSystemClang>();62 63 auto int_type = createRecordWithField(64 *ast, "TypeWitInt", ast->GetBasicType(lldb::BasicType::eBasicTypeInt),65 "theIntField", LanguageType::eLanguageTypeC_plus_plus);66 class_type_or_name.SetCompilerType(int_type);67 local_buffer = in_value.GetLocalBuffer();68 value_type = Value::ValueType::HostAddress;69 return true;70 }71 72 // All of this is boilerplate.73 MockLanguageRuntime(Process *process) : LanguageRuntime(process) {}74 llvm::StringRef GetPluginName() override { return "MockLanguageRuntime"; }75 lldb::LanguageType GetLanguageType() const override {76 return lldb::eLanguageTypeC_plus_plus;77 }78 79 llvm::Error GetObjectDescription(Stream &str, ValueObject &object) override {80 return llvm::Error::success();81 }82 83 llvm::Error GetObjectDescription(Stream &str, Value &value,84 ExecutionContextScope *exe_scope) override {85 return llvm::Error::success();86 }87 88 bool CouldHaveDynamicValue(ValueObject &in_value) override { return true; }89 90 TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,91 ValueObject &static_value) override {92 return type_and_or_name;93 }94 95 lldb::BreakpointResolverSP96 CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,97 bool throw_bp) override {98 return lldb::BreakpointResolverSP();99 }100 101 lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,102 bool stop_others) override {103 return {};104 }105 106 static LanguageRuntime *CreateInstance(Process *process,107 LanguageType language) {108 return new MockLanguageRuntime(process);109 }110 111 static void Initialize() {112 PluginManager::RegisterPlugin(113 "MockLanguageRuntime", "MockLanguageRuntime", CreateInstance,114 [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP {115 return {};116 },117 [](lldb::LanguageType language,118 bool throw_bp) -> BreakpointPreconditionSP { return {}; });119 }120 121 static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }122};123LLDB_PLUGIN_DEFINE(MockLanguageRuntime)124 125// This entire class is boilerplate.126struct MockProcess : Process {127 MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)128 : Process(target_sp, listener_sp) {}129 130 llvm::StringRef GetPluginName() override { return "mock process"; }131 132 bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {133 return false;134 };135 136 Status DoDestroy() override { return {}; }137 138 void RefreshStateAfterStop() override {}139 140 bool DoUpdateThreadList(ThreadList &old_thread_list,141 ThreadList &new_thread_list) override {142 return false;143 };144 145 size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,146 Status &error) override {147 // No need to read memory in these tests.148 return size;149 }150};151 152class DynamicValueObjectLocalBufferTest : public ::testing::Test {153public:154 void SetUp() override {155 ArchSpec arch("i386-pc-linux");156 Platform::SetHostPlatform(157 platform_linux::PlatformLinux::CreateInstance(true, &arch));158 // std::call_once(TestUtilities::g_debugger_initialize_flag,159 // []() { Debugger::Initialize(nullptr); });160 m_debugger_sp = Debugger::CreateInstance();161 ASSERT_TRUE(m_debugger_sp);162 m_debugger_sp->GetTargetList().CreateTarget(*m_debugger_sp, "", arch,163 eLoadDependentsNo,164 m_platform_sp, m_target_sp);165 ASSERT_TRUE(m_target_sp);166 ASSERT_TRUE(m_target_sp->GetArchitecture().IsValid());167 ASSERT_TRUE(m_platform_sp);168 m_listener_sp = Listener::MakeListener("dummy");169 m_process_sp = std::make_shared<MockProcess>(m_target_sp, m_listener_sp);170 ASSERT_TRUE(m_process_sp);171 m_exe_ctx = ExecutionContext(m_process_sp);172 173 m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>("test");174 m_type_system = m_holder->GetAST();175 LLDB_PLUGIN_INITIALIZE(MockLanguage);176 LLDB_PLUGIN_INITIALIZE(MockLanguageRuntime);177 }178 void TearDown() override {179 LLDB_PLUGIN_TERMINATE(MockLanguage);180 LLDB_PLUGIN_TERMINATE(MockLanguageRuntime);181 }182 183 void TestValueObjectWithLocalBuffer(DataExtractor &data_extractor,184 bool should_succeed) {185 std::unique_ptr<TypeSystemClangHolder> holder =186 std::make_unique<TypeSystemClangHolder>("test ASTContext");187 TypeSystemClang *ast = holder->GetAST();188 auto char_type = createRecordWithField(189 *ast, "TypeWithChar",190 ast->GetBasicType(lldb::BasicType::eBasicTypeChar), "theField");191 192 ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();193 ConstString var_name("test_var");194 auto valobj_sp = ValueObjectConstResult::Create(exe_scope, char_type,195 var_name, data_extractor);196 auto dyn_valobj = valobj_sp->GetDynamicValue(lldb::eDynamicCanRunTarget);197 ASSERT_TRUE(dyn_valobj->GetValueIsValid() == should_succeed);198 }199 200 SubsystemRAII<FileSystem, HostInfo, platform_linux::PlatformLinux,201 ScriptInterpreterNone>202 m_subsystems;203 std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder;204 lldb::DebuggerSP m_debugger_sp;205 lldb::TargetSP m_target_sp;206 lldb::PlatformSP m_platform_sp;207 lldb::ListenerSP m_listener_sp;208 lldb::ProcessSP m_process_sp;209 ExecutionContext m_exe_ctx;210 TypeSystemClang *m_type_system;211};212 213TEST_F(DynamicValueObjectLocalBufferTest, BufferTooSmall) {214 /// Test that a value object with a buffer to small to fit the215 /// "dynamic" type will return an invalid dynamic value object.216 uint8_t value = 1;217 ByteOrder endian = endian::InlHostByteOrder();218 DataExtractor data_extractor{&value, sizeof(value), endian, 4};219 TestValueObjectWithLocalBuffer(data_extractor, false);220}221 222TEST_F(DynamicValueObjectLocalBufferTest, BufferTooBig) {223 /// Test that a value object with a buffer big enough fit the224 /// "dynamic" type will return a valid dynamic value object.225 uint64_t value = 1;226 ByteOrder endian = endian::InlHostByteOrder();227 DataExtractor data_extractor{&value, sizeof(value), endian, 4};228 TestValueObjectWithLocalBuffer(data_extractor, true);229}230 231TEST_F(DynamicValueObjectLocalBufferTest, BufferExactlyRight) {232 /// Test that a value object with a buffer exactly the size of the233 /// "dynamic" type will return a valid dynamic value object.234 uint32_t value = 1;235 ByteOrder endian = endian::InlHostByteOrder();236 DataExtractor data_extractor{&value, sizeof(value), endian, 4};237 TestValueObjectWithLocalBuffer(data_extractor, true);238}239