[Lldb-commits] [lldb] [lldb] Make ValueObjectDynamicValue::UpdateValue() point to a host b… (PR #125143)

Augusto Noronha via lldb-commits lldb-commits at lists.llvm.org
Thu Feb 6 19:03:53 PST 2025


https://github.com/augusto2112 updated https://github.com/llvm/llvm-project/pull/125143

>From 937fdeab0b8526bd4b0e951f939a74ae01b1020c Mon Sep 17 00:00:00 2001
From: Augusto Noronha <anoronha at apple.com>
Date: Thu, 30 Jan 2025 16:33:09 -0800
Subject: [PATCH] [lldb] Make ValueObjectDynamicValue::UpdateValue() point to 
 a host buffer

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
---
 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   |  43 +++-
 .../TestingSupport/Symbol/ClangTestUtils.h    |  22 +-
 lldb/unittests/ValueObject/CMakeLists.txt     |   1 +
 .../DynamicValueObjectLocalBuffer.cpp         | 243 ++++++++++++++++++
 17 files changed, 342 insertions(+), 41 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 4a0214b04e235e6..f9ae2dc5896329c 100644
--- a/lldb/include/lldb/Target/LanguageRuntime.h
+++ b/lldb/include/lldb/Target/LanguageRuntime.h
@@ -105,12 +105,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<uint8_t> &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 4f77384bb8f1361..c8d5c2723106d6d 100644
--- a/lldb/include/lldb/ValueObject/ValueObject.h
+++ b/lldb/include/lldb/ValueObject/ValueObject.h
@@ -865,6 +865,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<ValueObject> ValueObjectManager;
 
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
index 66cdab1307ce9b9..8faf7135217acfa 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
@@ -289,7 +289,7 @@ llvm::Expected<LanguageRuntime::VTableInfo>
 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<uint8_t> &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 0f7e73cfee07546..7abf2f8547cd508 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<uint8_t> &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 ceee19c136d253d..ad60290382c02dd 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<uint8_t> &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 da58d44db19a890..425a608d65c2cf3 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<uint8_t> &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 93168c23f3547b6..db1317d70d060c9 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<uint8_t> &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 46d8e89c906e321..c51ac24e690b808 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<uint8_t> &local_buffer) override;
 
   llvm::Expected<std::unique_ptr<UtilityFunction>>
   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 c43871b08191db2..a57099f3df4543e 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -770,7 +770,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<uint8_t> &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 2422539b13f13dd..79840f9be79b3a2 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<uint8_t> &local_buffer) override;
 
   llvm::Expected<std::unique_ptr<UtilityFunction>>
   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 d6ffb03ab55e2c3..a4b3e26474a550e 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<uint8_t> &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 de24466ebb003cd..94a5c9e1261a823 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<uint8_t> &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 2864af107b925fc..551d882a48d40f6 100644
--- a/lldb/source/ValueObject/ValueObject.cpp
+++ b/lldb/source/ValueObject/ValueObject.cpp
@@ -849,6 +849,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 588c644bbfd07b6..dddb0f0700b38a4 100644
--- a/lldb/source/ValueObject/ValueObjectDynamicValue.cpp
+++ b/lldb/source/ValueObject/ValueObjectDynamicValue.cpp
@@ -145,6 +145,7 @@ bool ValueObjectDynamicValue::UpdateValue() {
   Address dynamic_address;
   bool found_dynamic_type = false;
   Value::ValueType value_type;
+  llvm::ArrayRef<uint8_t> local_buffer;
 
   LanguageRuntime *runtime = nullptr;
 
@@ -157,7 +158,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;
@@ -166,20 +167,20 @@ bool ValueObjectDynamicValue::UpdateValue() {
       // Fallback to the runtime for `known_type`.
       found_dynamic_type = runtime->GetDynamicTypeAndAddress(
           *m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
-          value_type);
+          value_type, local_buffer);
   } else {
     runtime = process->GetLanguageRuntime(lldb::eLanguageTypeC_plus_plus);
     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);
     }
   }
 
@@ -239,11 +240,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)
@@ -258,7 +277,11 @@ bool ValueObjectDynamicValue::UpdateValue() {
     LLDB_LOGF(log, "[%s %p] has a new dynamic type %s", GetName().GetCString(),
               static_cast<void *>(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 21525266119b404..63b2ba8c8688a4e 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<TypeSystemClangHolder>("test ASTContext");
     ast = holder->GetAST();
     record_type = createRecordWithField(
         *ast, "Source", ast->GetBasicType(lldb::BasicType::eBasicTypeChar),
-        "a_field");
+        "a_field", lang);
     record_decl =
         llvm::cast<clang::RecordDecl>(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 14808aa2f213a55..6ef0091647a59d8 100644
--- a/lldb/unittests/ValueObject/CMakeLists.txt
+++ b/lldb/unittests/ValueObject/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_lldb_unittest(LLDBValueObjectTests
   DumpValueObjectOptionsTests.cpp
   DILLexerTests.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 000000000000000..0ae3963f0c8320a
--- /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);
+}



More information about the lldb-commits mailing list