[Lldb-commits] [lldb] Add the ability to get a C++ vtable ValueObject from another ValueObj… (PR #67599)
Greg Clayton via lldb-commits
lldb-commits at lists.llvm.org
Thu Sep 28 12:00:54 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();
----------------
clayborg wrote:
This is some magic to have the lifetime managed by the root object.
https://github.com/llvm/llvm-project/pull/67599
More information about the lldb-commits
mailing list