From 674bc6f4c782b7d1dd54c52196816272d0f23001 Mon Sep 17 00:00:00 2001 From: Augusto Noronha Date: Thu, 6 Feb 2025 19:04:01 -0800 Subject: [PATCH] =?UTF-8?q?[lldb]=20Make=20ValueObjectDynamicValue::Update?= =?UTF-8?q?Value()=20point=20to=20=20a=20host=20b=E2=80=A6=20(#125143)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …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 (cherry picked from commit 0cbc4983adcdbbd85ccb38b2dfbfe5985367b1b2) --- lldb/include/lldb/Target/LanguageRuntime.h | 16 +- lldb/include/lldb/ValueObject/ValueObject.h | 12 + .../ItaniumABI/ItaniumABILanguageRuntime.cpp | 2 +- .../ItaniumABI/ItaniumABILanguageRuntime.h | 4 +- .../AppleObjCRuntime/AppleObjCRuntime.cpp | 2 +- .../ObjC/AppleObjCRuntime/AppleObjCRuntime.h | 4 +- .../AppleObjCRuntime/AppleObjCRuntimeV1.cpp | 2 +- .../AppleObjCRuntime/AppleObjCRuntimeV1.h | 4 +- .../AppleObjCRuntime/AppleObjCRuntimeV2.cpp | 2 +- .../AppleObjCRuntime/AppleObjCRuntimeV2.h | 4 +- .../GNUstepObjCRuntime/GNUstepObjCRuntime.cpp | 2 +- .../GNUstepObjCRuntime/GNUstepObjCRuntime.h | 4 +- lldb/source/ValueObject/ValueObject.cpp | 16 ++ .../ValueObject/ValueObjectDynamicValue.cpp | 41 ++- .../TestingSupport/Symbol/ClangTestUtils.h | 22 +- lldb/unittests/ValueObject/CMakeLists.txt | 1 + .../DynamicValueObjectLocalBuffer.cpp | 243 ++++++++++++++++++ 17 files changed, 341 insertions(+), 40 deletions(-) create mode 100644 lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp diff --git a/lldb/include/lldb/Target/LanguageRuntime.h b/lldb/include/lldb/Target/LanguageRuntime.h index 8903d8dfd5c93..0b816139b2cf2 100644 --- a/lldb/include/lldb/Target/LanguageRuntime.h +++ b/lldb/include/lldb/Target/LanguageRuntime.h @@ -106,12 +106,16 @@ class LanguageRuntime : public Runtime, public PluginInterface { "language doesn't support getting vtable information"); } - // this call should return true if it could set the name and/or the type - virtual bool GetDynamicTypeAndAddress(ValueObject &in_value, - lldb::DynamicValueType use_dynamic, - TypeAndOrName &class_type_or_name, - Address &address, - Value::ValueType &value_type) = 0; + /// This call should return true if it could set the name and/or the type + /// Sets address to the address of the dynamic type if value_type is set to + /// a file or load address. Sets local_buffer to a buffer containing the data + /// of the dynamic type if value_type is set to a host address. Callers should + /// copy local_buffer over into their own buffer if they want to keep the data + /// alive. + virtual bool GetDynamicTypeAndAddress( + ValueObject &in_value, lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, Address &address, + Value::ValueType &value_type, llvm::ArrayRef &local_buffer) = 0; // This call should return a CompilerType given a generic type name and an // ExecutionContextScope in which one can actually fetch any specialization diff --git a/lldb/include/lldb/ValueObject/ValueObject.h b/lldb/include/lldb/ValueObject/ValueObject.h index a3d288a6637d4..ef5b0f900ffbd 100644 --- a/lldb/include/lldb/ValueObject/ValueObject.h +++ b/lldb/include/lldb/ValueObject/ValueObject.h @@ -869,6 +869,18 @@ class ValueObject { virtual void SetLanguageFlags(uint64_t flags) { m_language_flags = flags; } + /// Returns the size of the local buffer if it's available. + /// \return + /// The size of the local buffer if this value object's value points to a + /// host address, and if that size can be determined. Otherwise, returns + /// LLDB_INVALID_ADDRESS. + /// + /// TODO: Because a ValueObject's Value can point to any arbitrary memory + /// location, it is possible that the size of the local buffer can't be + /// determined at all. See the comment in Value::m_value for a more thorough + /// explanation of why that is. + uint64_t GetLocalBufferSize(); + protected: typedef ClusterManager ValueObjectManager; diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp index afaf2ebb70e4b..2820cec66addd 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp @@ -288,7 +288,7 @@ llvm::Expected bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress( ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &dynamic_address, - Value::ValueType &value_type) { + Value::ValueType &value_type, llvm::ArrayRef &local_buffer) { // For Itanium, if the type has a vtable pointer in the object, it will be at // offset 0 in the object. That will point to the "address point" within the // vtable (not the beginning of the vtable.) We can then look up the symbol diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h index 0f7e73cfee075..7abf2f8547cd5 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h @@ -54,8 +54,8 @@ class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime { bool GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, - Address &address, - Value::ValueType &value_type) override; + Address &address, Value::ValueType &value_type, + llvm::ArrayRef &local_buffer) override; TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name, ValueObject &static_value) override; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp index ceee19c136d25..ad60290382c02 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp @@ -276,7 +276,7 @@ bool AppleObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) { bool AppleObjCRuntime::GetDynamicTypeAndAddress( ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &address, - Value::ValueType &value_type) { + Value::ValueType &value_type, llvm::ArrayRef &local_buffer) { return false; } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h index da58d44db19a8..425a608d65c2c 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h @@ -54,8 +54,8 @@ class AppleObjCRuntime : public lldb_private::ObjCLanguageRuntime { bool GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, - Address &address, - Value::ValueType &value_type) override; + Address &address, Value::ValueType &value_type, + llvm::ArrayRef &local_buffer) override; TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name, ValueObject &static_value) override; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp index 93168c23f3547..db1317d70d060 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp @@ -48,7 +48,7 @@ AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process) bool AppleObjCRuntimeV1::GetDynamicTypeAndAddress( ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &address, - Value::ValueType &value_type) { + Value::ValueType &value_type, llvm::ArrayRef &local_buffer) { class_type_or_name.Clear(); value_type = Value::ValueType::Scalar; if (CouldHaveDynamicValue(in_value)) { diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h index 46d8e89c906e3..c51ac24e690b8 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h @@ -100,8 +100,8 @@ class AppleObjCRuntimeV1 : public AppleObjCRuntime { bool GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, - Address &address, - Value::ValueType &value_type) override; + Address &address, Value::ValueType &value_type, + llvm::ArrayRef &local_buffer) override; llvm::Expected> CreateObjectChecker(std::string, ExecutionContext &exe_ctx) override; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index 234a13a881c76..1ffc0ea537fa6 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -775,7 +775,7 @@ AppleObjCRuntimeV2::GetPreferredLanguageRuntime(ValueObject &in_value) { bool AppleObjCRuntimeV2::GetDynamicTypeAndAddress( ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &address, - Value::ValueType &value_type) { + Value::ValueType &value_type, llvm::ArrayRef &local_buffer) { // We should never get here with a null process... assert(m_process != nullptr); diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h index 0bd433c9051cd..d04a936c8b5f6 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h @@ -53,8 +53,8 @@ class AppleObjCRuntimeV2 : public AppleObjCRuntime { bool GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, - Address &address, - Value::ValueType &value_type) override; + Address &address, Value::ValueType &value_type, + llvm::ArrayRef &local_buffer) override; llvm::Expected> CreateObjectChecker(std::string name, ExecutionContext &exe_ctx) override; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp index d6ffb03ab55e2..a4b3e26474a55 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp @@ -127,7 +127,7 @@ bool GNUstepObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) { bool GNUstepObjCRuntime::GetDynamicTypeAndAddress( ValueObject &in_value, DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &address, - Value::ValueType &value_type) { + Value::ValueType &value_type, llvm::ArrayRef &local_buffer) { return false; } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h index de24466ebb003..94a5c9e1261a8 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h @@ -67,8 +67,8 @@ class GNUstepObjCRuntime : public lldb_private::ObjCLanguageRuntime { bool GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, - Address &address, - Value::ValueType &value_type) override; + Address &address, Value::ValueType &value_type, + llvm::ArrayRef &local_buffer) override; TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name, ValueObject &static_value) override; diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index 7f5eb5b5c4075..388ca02e5112b 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -917,6 +917,22 @@ bool ValueObject::SetData(DataExtractor &data, Status &error) { return true; } +uint64_t ValueObject::GetLocalBufferSize() { + if (m_value.GetValueType() != Value::ValueType::HostAddress) + return LLDB_INVALID_ADDRESS; + auto start = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + if (start == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + // Does our pointer point to this value object's m_data buffer? + if ((uint64_t)m_data.GetDataStart() == start) + return m_data.GetByteSize(); + // Does our pointer point to the value's buffer? + if ((uint64_t)m_value.GetBuffer().GetBytes() == start) + return m_value.GetBuffer().GetByteSize(); + // Our pointer points to something else. We can't know what the size is. + return LLDB_INVALID_ADDRESS; +} + static bool CopyStringDataToBufferSP(const StreamString &source, lldb::WritableDataBufferSP &destination) { llvm::StringRef src = source.GetString(); diff --git a/lldb/source/ValueObject/ValueObjectDynamicValue.cpp b/lldb/source/ValueObject/ValueObjectDynamicValue.cpp index 6ecf378638350..be7f3df5e9872 100644 --- a/lldb/source/ValueObject/ValueObjectDynamicValue.cpp +++ b/lldb/source/ValueObject/ValueObjectDynamicValue.cpp @@ -157,6 +157,7 @@ bool ValueObjectDynamicValue::UpdateValue() { Address dynamic_address; bool found_dynamic_type = false; Value::ValueType value_type; + llvm::ArrayRef local_buffer; LanguageRuntime *runtime = nullptr; lldb::LanguageType known_type = m_parent->GetObjectRuntimeLanguage(); @@ -183,7 +184,7 @@ bool ValueObjectDynamicValue::UpdateValue() { // Try the preferred runtime first. found_dynamic_type = preferred_runtime->GetDynamicTypeAndAddress( *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, - value_type); + value_type, local_buffer); if (found_dynamic_type) // Set the operative `runtime` for later use in this function. runtime = preferred_runtime; @@ -198,14 +199,14 @@ bool ValueObjectDynamicValue::UpdateValue() { if (runtime) found_dynamic_type = runtime->GetDynamicTypeAndAddress( *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, - value_type); + value_type, local_buffer); if (!found_dynamic_type) { runtime = process->GetLanguageRuntime(lldb::eLanguageTypeObjC); if (runtime) found_dynamic_type = runtime->GetDynamicTypeAndAddress( *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, - value_type); + value_type, local_buffer); } } @@ -277,11 +278,29 @@ bool ValueObjectDynamicValue::UpdateValue() { if (m_address.IsValid()) SetValueDidChange(true); - // We've moved, so we should be fine... - m_address = dynamic_address; - lldb::TargetSP target_sp(GetTargetSP()); - lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get()); - m_value.GetScalar() = load_address; + // If we found a host address, and the dynamic type fits in the local buffer + // that was found, point to thar buffer. Later on this function will copy + // the buffer over. + if (value_type == Value::ValueType::HostAddress && !local_buffer.empty()) { + auto *exe_scope = exe_ctx.GetBestExecutionContextScope(); + // If we found a host address but it doesn't fit in the buffer, there's + // nothing we can do. + if (local_buffer.size() < + m_dynamic_type_info.GetCompilerType().GetByteSize(exe_scope)) { + SetValueIsValid(false); + return false; + } + + m_value.GetScalar() = (uint64_t)local_buffer.data(); + m_address = LLDB_INVALID_ADDRESS; + } else { + // Otherwise we have a legitimate address on the target. Point to the load + // address. + m_address = dynamic_address; + lldb::TargetSP target_sp(GetTargetSP()); + lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get()); + m_value.GetScalar() = load_address; + } } if (runtime) @@ -296,7 +315,11 @@ bool ValueObjectDynamicValue::UpdateValue() { LLDB_LOGF(log, "[%s %p] has a new dynamic type %s", GetName().GetCString(), static_cast(this), GetTypeName().GetCString()); - if (m_address.IsValid() && m_dynamic_type_info) { + // m_address could be invalid but we could still have a local buffer + // containing the dynamic value. + if ((m_address.IsValid() || + m_value.GetValueType() == Value::ValueType::HostAddress) && + m_dynamic_type_info) { // The variable value is in the Scalar value inside the m_value. We can // point our m_data right to it. m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get()); diff --git a/lldb/unittests/TestingSupport/Symbol/ClangTestUtils.h b/lldb/unittests/TestingSupport/Symbol/ClangTestUtils.h index 21525266119b4..63b2ba8c8688a 100644 --- a/lldb/unittests/TestingSupport/Symbol/ClangTestUtils.h +++ b/lldb/unittests/TestingSupport/Symbol/ClangTestUtils.h @@ -21,20 +21,21 @@ inline clang::DeclarationName getDeclarationName(TypeSystemClang &ast, return ast.getASTContext().DeclarationNames.getIdentifier(&II); } -inline CompilerType createRecord(TypeSystemClang &ast, llvm::StringRef name) { +inline CompilerType +createRecord(TypeSystemClang &ast, llvm::StringRef name, + lldb::LanguageType lang = lldb::LanguageType::eLanguageTypeC) { return ast.CreateRecordType(ast.getASTContext().getTranslationUnitDecl(), OptionalClangModuleID(), - lldb::AccessType::eAccessPublic, name, 0, - lldb::LanguageType::eLanguageTypeC); + lldb::AccessType::eAccessPublic, name, 0, lang); } /// Create a record with the given name and a field with the given type /// and name. -inline CompilerType createRecordWithField(TypeSystemClang &ast, - llvm::StringRef record_name, - CompilerType field_type, - llvm::StringRef field_name) { - CompilerType t = createRecord(ast, record_name); +inline CompilerType createRecordWithField( + TypeSystemClang &ast, llvm::StringRef record_name, CompilerType field_type, + llvm::StringRef field_name, + lldb::LanguageType lang = lldb::LanguageType::eLanguageTypeC) { + CompilerType t = createRecord(ast, record_name, lang); TypeSystemClang::StartTagDeclarationDefinition(t); ast.AddFieldToRecordType(t, field_name, field_type, @@ -63,12 +64,13 @@ struct SourceASTWithRecord { CompilerType record_type; clang::RecordDecl *record_decl = nullptr; clang::FieldDecl *field_decl = nullptr; - SourceASTWithRecord() { + SourceASTWithRecord( + lldb::LanguageType lang = lldb::LanguageType::eLanguageTypeC) { holder = std::make_unique("test ASTContext"); ast = holder->GetAST(); record_type = createRecordWithField( *ast, "Source", ast->GetBasicType(lldb::BasicType::eBasicTypeChar), - "a_field"); + "a_field", lang); record_decl = llvm::cast(ClangUtil::GetAsTagDecl(record_type)); field_decl = *record_decl->fields().begin(); diff --git a/lldb/unittests/ValueObject/CMakeLists.txt b/lldb/unittests/ValueObject/CMakeLists.txt index 8fcc8d62a7997..d55d861a36536 100644 --- a/lldb/unittests/ValueObject/CMakeLists.txt +++ b/lldb/unittests/ValueObject/CMakeLists.txt @@ -1,5 +1,6 @@ add_lldb_unittest(LLDBValueObjectTests DumpValueObjectOptionsTests.cpp + DynamicValueObjectLocalBuffer.cpp LINK_LIBS lldbValueObject diff --git a/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp b/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp new file mode 100644 index 0000000000000..0ae3963f0c832 --- /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 &local_buffer) override { + auto ast = in_value.GetCompilerType() + .GetTypeSystem() + .dyn_cast_or_null(); + + 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(m_target_sp, m_listener_sp); + ASSERT_TRUE(m_process_sp); + m_exe_ctx = ExecutionContext(m_process_sp); + + m_holder = std::make_unique("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 holder = + std::make_unique("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 + m_subsystems; + std::unique_ptr 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); +}