[Lldb-commits] [lldb] Add the ability to get a C++ vtable ValueObject from another ValueObj… (PR #67599)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Sep 27 14:46:11 PDT 2023
================
@@ -0,0 +1,325 @@
+//===-- ValueObjectVTable.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 "lldb/Core/ValueObjectVTable.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObjectChild.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private-enumerations.h"
+#include "clang/Tooling/Transformer/RangeSelector.h"
+#include "llvm/Support/MathExtras.h"
+#include <cstdint>
+
+using namespace lldb;
+using namespace lldb_private;
+
+class ValueObjectVTableChild : public ValueObject {
+public:
+ ValueObjectVTableChild(ValueObject &parent, uint32_t func_idx,
+ uint64_t addr_size)
+ : ValueObject(parent), m_func_idx(func_idx), m_addr_size(addr_size) {
+ SetFormat(eFormatPointer);
+ SetName(ConstString(llvm::formatv("[{0}]", func_idx).str()));
+ }
+
+ ~ValueObjectVTableChild() override = default;
+
+ std::optional<uint64_t> GetByteSize() override { return m_addr_size; };
+
+ size_t CalculateNumChildren(uint32_t max) override { return 0; };
+
+ ValueType GetValueType() const override { return eValueTypeVTableEntry; };
+
+ bool IsInScope() override {
+ ValueObject *parent = GetParent();
+ if (parent)
+ return parent->IsInScope();
+ return false;
+ };
+
+protected:
+ bool UpdateValue() override {
+ SetValueIsValid(false);
+ m_value.Clear();
+ ValueObject *parent = GetParent();
+ if (!parent) {
+ m_error.SetErrorString("no parent object");
+ return false;
+ }
+
+ addr_t parent_addr = parent->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ if (parent_addr == LLDB_INVALID_ADDRESS) {
+ m_error.SetErrorString("parent has invalid address");
+ return false;
+ }
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp) {
+ m_error.SetErrorString("no process");
+ return false;
+ }
+
+ TargetSP target_sp = GetTargetSP();
+ if (!target_sp) {
+ m_error.SetErrorString("no target");
+ return false;
+ }
+
+ // Each `vtable_entry_addr` points to the function pointer.
+ addr_t vtable_entry_addr = parent_addr + m_func_idx * m_addr_size;
+ addr_t vfunc_ptr =
+ process_sp->ReadPointerFromMemory(vtable_entry_addr, m_error);
+ if (m_error.Fail()) {
+ m_error.SetErrorStringWithFormat(
+ "failed to read virtual function entry 0x%16.16" PRIx64,
+ vtable_entry_addr);
+ return false;
+ }
+
+ Address resolved_vfunc_ptr_address;
+ target_sp->ResolveLoadAddress(vfunc_ptr, resolved_vfunc_ptr_address);
+ if (!resolved_vfunc_ptr_address.IsValid()) {
+ m_error.SetErrorStringWithFormat(
+ "unable to resolve func ptr address: 0x%16.16" PRIx64, vfunc_ptr);
+ return false;
+ }
+
+ // Set our value to be the load address of the function pointer in memory
+ // and our type to be the function pointer type.
+ m_value.SetValueType(Value::ValueType::LoadAddress);
+ m_value.GetScalar() = vtable_entry_addr;
+
+ // See if our resolved address points to a function in the debug info. If
+ // it does, then we can report the type as a function prototype for this
+ // function.
+ Function *function =
+ resolved_vfunc_ptr_address.CalculateSymbolContextFunction();
+ if (function) {
+ m_value.SetCompilerType(function->GetCompilerType());
+ } else {
+ // Set our value's compiler type to a generic function protoype so that
+ // it displays as a hex function pointer for the value and the summary
+ // will display the address description.
+ auto type_system_or_err =
+ target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC_plus_plus);
+ if (type_system_or_err) {
+ CompilerType proto = (*type_system_or_err)->CreateGenericFunctionPrototype();
+ if (proto.IsFunctionType())
+ m_value.SetCompilerType(proto);
+ } else {
+ consumeError(type_system_or_err.takeError());
+ }
+ }
+
+ // Now read our value into m_data so that our we can use the default
+ // summary provider for C++ for function pointers which will get the
+ // address description for our function pointer.
+ if (m_error.Success()) {
+ const bool thread_and_frame_only_if_stopped = true;
+ ExecutionContext exe_ctx(
+ GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped));
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+ }
+ SetValueDidChange(true);
+ SetValueIsValid(true);
+ return true;
+ };
+
+ CompilerType GetCompilerTypeImpl() override {
+ return m_value.GetCompilerType();
+ };
+
+ const uint32_t m_func_idx;
+ const uint64_t m_addr_size;
+
+private:
+ // For ValueObject only
+ ValueObjectVTableChild(const ValueObjectVTableChild &) = delete;
+ const ValueObjectVTableChild &
+ operator=(const ValueObjectVTableChild &) = delete;
+};
+
+ValueObjectSP ValueObjectVTable::Create(ValueObject &parent) {
+ return (new ValueObjectVTable(parent))->GetSP();
+}
+
+ValueObjectVTable::ValueObjectVTable(ValueObject &parent)
+ : ValueObject(parent) {
+ SetFormat(eFormatPointer);
+}
+
+std::optional<uint64_t> ValueObjectVTable::GetByteSize() {
+ if (m_vtable_symbol)
+ return m_vtable_symbol->GetByteSize();
+ else
+ return std::nullopt;
+}
+
+size_t ValueObjectVTable::CalculateNumChildren(uint32_t max) {
+ if (UpdateValueIfNeeded(false))
+ return m_num_vtable_entries <= max ? m_num_vtable_entries : max;
+ return 0;
+}
+
+ValueType ValueObjectVTable::GetValueType() const { return eValueTypeVTable; }
+
+ConstString ValueObjectVTable::GetTypeName() {
+ if (m_vtable_symbol)
+ return m_vtable_symbol->GetName();
+ return ConstString();
+}
+
+ConstString ValueObjectVTable::GetQualifiedTypeName() { return GetTypeName(); }
+
+ConstString ValueObjectVTable::GetDisplayTypeName() {
+ if (m_vtable_symbol)
+ return m_vtable_symbol->GetDisplayName();
+ return ConstString();
+}
+
+bool ValueObjectVTable::IsInScope() { return GetParent()->IsInScope(); }
+
+ValueObject *ValueObjectVTable::CreateChildAtIndex(size_t idx,
+ bool synthetic_array_member,
+ int32_t synthetic_index) {
+ if (synthetic_array_member)
+ return nullptr;
+ return new ValueObjectVTableChild(*this, idx, m_addr_size);
+}
+
+bool ValueObjectVTable::UpdateValue() {
+ m_error.Clear();
+ m_flags.m_children_count_valid = false;
+ SetValueIsValid(false);
+ m_num_vtable_entries = 0;
+ ValueObject *parent = GetParent();
+ if (!parent) {
+ m_error.SetErrorString("no parent object");
+ return false;
+ }
+
+ // Check to make sure the class has a vtable.
+
+ // If we have a pointer or reference type, get the pointee type from this
+ // so we can ask if it has virtual functions below.
+ CompilerType value_type = parent->GetCompilerType();
+ if (value_type.IsPointerOrReferenceType()) {
+ CompilerType pointee_type = value_type.GetPointeeType();
+ if (pointee_type)
+ value_type = pointee_type;
+ }
+
+ // Make sure this is a class or a struct first by checking the type class
+ // bitfield that gets returned.
+ if ((value_type.GetTypeClass() & (eTypeClassStruct | eTypeClassClass)) == 0) {
+ m_error.SetErrorStringWithFormat("type \"%s\" is not a class or struct",
+ value_type.GetTypeName().AsCString("<invalid>"));
+ return false;
+ }
+
+ // Check if the type has virtual functions by asking it if it is polymorphic.
+ if (!value_type.IsPolymorphicClass()) {
+ m_error.SetErrorStringWithFormat("type \"%s\" doesn't have a vtable",
+ value_type.GetTypeName().AsCString("<invalid>"));
+ return false;
+ }
+
+ TargetSP target_sp = GetTargetSP();
+ if (!target_sp) {
+ m_error.SetErrorString("no target");
+ return false;
+ }
+
+ // Get the address of our parent. This will update the parent value object
+ // if needed and fetch the address of the parent.
+ AddressType addr_type;
+ addr_t parent_load_addr =
+ parent->GetAddressOf(/*scalar_is_load_address=*/true, &addr_type);
+ if (addr_type == eAddressTypeFile) {
+ ModuleSP module_sp(parent->GetModule());
+ if (!module_sp) {
+ parent_load_addr = LLDB_INVALID_ADDRESS;
+ } else {
+ Address addr;
+ module_sp->ResolveFileAddress(parent_load_addr, addr);
+ parent_load_addr = addr.GetLoadAddress(target_sp.get());
+ }
+ } else if (addr_type == eAddressTypeHost ||
+ addr_type == eAddressTypeInvalid) {
+ parent_load_addr = LLDB_INVALID_ADDRESS;
+ }
+
+ if (parent_load_addr == LLDB_INVALID_ADDRESS) {
+ m_error.SetErrorString("parent is not in memory");
+ return false;
+ }
+
+ m_value.Clear();
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp) {
+ m_error.SetErrorString("no process");
+ return false;
+ }
+
+ // We expect to find the vtable at the first block of memory.
----------------
jimingham wrote:
The C++ Language Runtime has to find the VTable pointer so it can determine the dynamic type of the ValueObject, can we reuse that code here?
https://github.com/llvm/llvm-project/pull/67599
More information about the lldb-commits
mailing list