A very common pattern in our synthetic child providers was to make the child ValueObject using ValueObjectConstResult::Create or some form of the static ValueObject::CreateValueObjectFrom*** methods, and store and hand that out as the child. Doing that creates a "root" ValueObject whose lifecycle is not linked to the lifecycle of the ValueObject it is a child of. And that means it is possible that either the child or the parent could have gotten destroyed when the other ValueObject gets asked a question about it. For the most part this doesn't happen because there are usually enough other shared pointer references binding the two to keep both sides alive. But we have gotten a small but steady stream of reports for years now of crashes where a ValueObject accesses its ClusterManager but that has already been deleted. I've never been able to find a reproducible case of this, but one plausible cause is that we are violating the contract that "all the children of a ValueObject have coterminous lifespans, enforced by the ClusterManager". So it is unsurprising that they might sometimes not stay alive as long as they should. This patch addresses that by providing a way to use these static create methods but passing in the ClusterManager to be used, and adds or modifies where extant the CreateChildValueObjectFrom*** methods to pass in the parent manager. This patch is not complete. It turns out that routing the ClusterManager from CreateValueObjectFromExpression all the way to where the expression result VO is made is quite intrusive, and would have made this already quite large (though largly mechanical) patch become unweildy. Since I am mostly concerned about synthetic child providers, and we discourage using expressions for them, I think that's an acceptable separation. I also added a test where the ValueObjectSynthetic hands out its children, checking whether the child being handed out is in fact owned by the parent handing it out. It is controlled by a setting (target.check-vo-ownership) which is currently off by default - since I can't provide a way to do this correctly for expressions yet. But I turn it on in the testsuite. That's the real testing strategy for this patch, since its goal is to ensure that all the children we hand out are managed by their parents. I didn't put an equivalent check in ValueObjectVariable because that system really doesn't allow a way to do this incorrectly. I also fixed some of the tests that were making children using CreateValueObjectFromExpression to make their children with CreateValueObjectFromData instead. I could have opted the tests out of the check, but using the expression parser to do this work is not best practice, so I didn't want to leave bad examples around. I left one case that does do it with expressions, however, so we still have a test for that not-recommended but people do it anyway path.
484 lines
16 KiB
C++
484 lines
16 KiB
C++
//===-- ValueObjectSynthetic.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/ValueObject/ValueObjectSynthetic.h"
|
|
|
|
#include "lldb/Core/Value.h"
|
|
#include "lldb/DataFormatters/FormattersHelpers.h"
|
|
#include "lldb/DataFormatters/TypeSynthetic.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Utility/ConstString.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/Status.h"
|
|
#include "lldb/ValueObject/ValueObject.h"
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/ErrorExtras.h"
|
|
#include <optional>
|
|
|
|
namespace lldb_private {
|
|
class Declaration;
|
|
}
|
|
|
|
using namespace lldb_private;
|
|
|
|
class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
|
|
public:
|
|
DummySyntheticFrontEnd(ValueObject &backend)
|
|
: SyntheticChildrenFrontEnd(backend) {}
|
|
|
|
llvm::Expected<uint32_t> CalculateNumChildren() override {
|
|
return m_backend.GetNumChildren();
|
|
}
|
|
|
|
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
|
|
return m_backend.GetChildAtIndex(idx);
|
|
}
|
|
|
|
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
|
|
return m_backend.GetIndexOfChildWithName(name);
|
|
}
|
|
|
|
bool MightHaveChildren() override { return m_backend.MightHaveChildren(); }
|
|
|
|
lldb::ChildCacheState Update() override {
|
|
return lldb::ChildCacheState::eRefetch;
|
|
}
|
|
};
|
|
|
|
ValueObjectSynthetic::ValueObjectSynthetic(ValueObject &parent,
|
|
lldb::SyntheticChildrenSP filter)
|
|
: ValueObject(parent), m_synth_sp(std::move(filter)), m_children_byindex(),
|
|
m_name_toindex(), m_synthetic_children_cache(),
|
|
m_synthetic_children_count(UINT32_MAX),
|
|
m_parent_type_name(parent.GetTypeName()),
|
|
m_might_have_children(eLazyBoolCalculate),
|
|
m_provides_value(eLazyBoolCalculate) {
|
|
SetName(parent.GetName());
|
|
// Copying the data of an incomplete type won't work as it has no byte size.
|
|
if (m_parent->GetCompilerType().IsCompleteType())
|
|
CopyValueData(m_parent);
|
|
CreateSynthFilter();
|
|
}
|
|
|
|
ValueObjectSynthetic::~ValueObjectSynthetic() = default;
|
|
|
|
CompilerType ValueObjectSynthetic::GetCompilerTypeImpl() {
|
|
return m_parent->GetCompilerType();
|
|
}
|
|
|
|
ConstString ValueObjectSynthetic::GetTypeName() {
|
|
return m_parent->GetTypeName();
|
|
}
|
|
|
|
ConstString ValueObjectSynthetic::GetQualifiedTypeName() {
|
|
return m_parent->GetQualifiedTypeName();
|
|
}
|
|
|
|
ConstString ValueObjectSynthetic::GetDisplayTypeName() {
|
|
if (ConstString synth_name = m_synth_filter_up->GetSyntheticTypeName())
|
|
return synth_name;
|
|
|
|
return m_parent->GetDisplayTypeName();
|
|
}
|
|
|
|
llvm::Expected<uint32_t>
|
|
ValueObjectSynthetic::CalculateNumChildren(uint32_t max) {
|
|
Log *log = GetLog(LLDBLog::DataFormatters);
|
|
|
|
UpdateValueIfNeeded();
|
|
if (m_synthetic_children_count < UINT32_MAX)
|
|
return m_synthetic_children_count <= max ? m_synthetic_children_count : max;
|
|
|
|
if (max < UINT32_MAX) {
|
|
auto num_children = m_synth_filter_up->CalculateNumChildren(max);
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::CalculateNumChildren] for VO of name "
|
|
"{0} and type {1}, the filter returned {2} child values",
|
|
GetName(), GetTypeName(), num_children ? *num_children : 0);
|
|
return num_children;
|
|
} else {
|
|
auto num_children_or_err = m_synth_filter_up->CalculateNumChildren(max);
|
|
if (!num_children_or_err) {
|
|
m_synthetic_children_count = 0;
|
|
return num_children_or_err;
|
|
}
|
|
auto num_children = (m_synthetic_children_count = *num_children_or_err);
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::CalculateNumChildren] for VO of name "
|
|
"{0} and type {1}, the filter returned {2} child values",
|
|
GetName(), GetTypeName(), num_children);
|
|
return num_children;
|
|
}
|
|
}
|
|
|
|
lldb::ValueObjectSP
|
|
ValueObjectSynthetic::GetDynamicValue(lldb::DynamicValueType valueType) {
|
|
if (!m_parent)
|
|
return lldb::ValueObjectSP();
|
|
if (IsDynamic() && GetDynamicValueType() == valueType)
|
|
return GetSP();
|
|
return m_parent->GetDynamicValue(valueType);
|
|
}
|
|
|
|
bool ValueObjectSynthetic::MightHaveChildren() {
|
|
if (m_might_have_children == eLazyBoolCalculate)
|
|
m_might_have_children =
|
|
(m_synth_filter_up->MightHaveChildren() ? eLazyBoolYes : eLazyBoolNo);
|
|
return (m_might_have_children != eLazyBoolNo);
|
|
}
|
|
|
|
llvm::Expected<uint64_t> ValueObjectSynthetic::GetByteSize() {
|
|
return m_parent->GetByteSize();
|
|
}
|
|
|
|
lldb::ValueType ValueObjectSynthetic::GetValueType() const {
|
|
return m_parent->GetValueType();
|
|
}
|
|
|
|
void ValueObjectSynthetic::CreateSynthFilter() {
|
|
ValueObject *valobj_for_frontend = m_parent;
|
|
if (m_synth_sp->WantsDereference()) {
|
|
CompilerType type = m_parent->GetCompilerType();
|
|
if (type.IsValid() && type.IsPointerOrReferenceType()) {
|
|
Status error;
|
|
lldb::ValueObjectSP deref_sp = m_parent->Dereference(error);
|
|
if (error.Success())
|
|
valobj_for_frontend = deref_sp.get();
|
|
}
|
|
}
|
|
m_synth_filter_up = (m_synth_sp->GetFrontEnd(*valobj_for_frontend));
|
|
if (!m_synth_filter_up)
|
|
m_synth_filter_up = std::make_unique<DummySyntheticFrontEnd>(*m_parent);
|
|
}
|
|
|
|
bool ValueObjectSynthetic::UpdateValue() {
|
|
Log *log = GetLog(LLDBLog::DataFormatters);
|
|
|
|
SetValueIsValid(false);
|
|
m_error.Clear();
|
|
|
|
if (!m_parent->UpdateValueIfNeeded(false)) {
|
|
// our parent could not update.. as we are meaningless without a parent,
|
|
// just stop
|
|
if (m_parent->GetError().Fail())
|
|
m_error = m_parent->GetError().Clone();
|
|
return false;
|
|
}
|
|
|
|
// Regenerate the synthetic filter if our typename changes. When the (dynamic)
|
|
// type of an object changes, so does their synthetic filter of choice.
|
|
ConstString new_parent_type_name = m_parent->GetTypeName();
|
|
if (new_parent_type_name != m_parent_type_name) {
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::UpdateValue] name={0}, type changed "
|
|
"from {1} to {2}, recomputing synthetic filter",
|
|
GetName(), m_parent_type_name, new_parent_type_name);
|
|
m_parent_type_name = new_parent_type_name;
|
|
CreateSynthFilter();
|
|
}
|
|
|
|
// let our backend do its update
|
|
if (m_synth_filter_up->Update() == lldb::ChildCacheState::eRefetch) {
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::UpdateValue] name={0}, synthetic "
|
|
"filter said caches are stale - clearing",
|
|
GetName());
|
|
// filter said that cached values are stale
|
|
{
|
|
std::lock_guard<std::mutex> guard(m_child_mutex);
|
|
m_children_byindex.clear();
|
|
m_name_toindex.clear();
|
|
}
|
|
// usually, an object's value can change but this does not alter its
|
|
// children count for a synthetic VO that might indeed happen, so we need
|
|
// to tell the upper echelons that they need to come back to us asking for
|
|
// children
|
|
m_flags.m_children_count_valid = false;
|
|
{
|
|
std::lock_guard<std::mutex> guard(m_child_mutex);
|
|
m_synthetic_children_cache.clear();
|
|
}
|
|
m_synthetic_children_count = UINT32_MAX;
|
|
m_might_have_children = eLazyBoolCalculate;
|
|
} else {
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::UpdateValue] name={0}, synthetic "
|
|
"filter said caches are still valid",
|
|
GetName());
|
|
}
|
|
|
|
m_provides_value = eLazyBoolCalculate;
|
|
|
|
lldb::ValueObjectSP synth_val(m_synth_filter_up->GetSyntheticValue());
|
|
|
|
if (synth_val && synth_val->CanProvideValue()) {
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::UpdateValue] name={0}, synthetic "
|
|
"filter said it can provide a value",
|
|
GetName());
|
|
|
|
m_provides_value = eLazyBoolYes;
|
|
CopyValueData(synth_val.get());
|
|
} else {
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::UpdateValue] name={0}, synthetic "
|
|
"filter said it will not provide a value",
|
|
GetName());
|
|
|
|
m_provides_value = eLazyBoolNo;
|
|
// Copying the data of an incomplete type won't work as it has no byte size.
|
|
if (m_parent->GetCompilerType().IsCompleteType())
|
|
CopyValueData(m_parent);
|
|
}
|
|
|
|
SetValueIsValid(true);
|
|
return true;
|
|
}
|
|
|
|
lldb::ValueObjectSP ValueObjectSynthetic::GetChildAtIndex(uint32_t idx,
|
|
bool can_create) {
|
|
Log *log = GetLog(LLDBLog::DataFormatters);
|
|
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::GetChildAtIndex] name={0}, retrieving "
|
|
"child at index {1}",
|
|
GetName(), idx);
|
|
|
|
UpdateValueIfNeeded();
|
|
|
|
ValueObject *valobj;
|
|
bool child_is_cached;
|
|
{
|
|
std::lock_guard<std::mutex> guard(m_child_mutex);
|
|
auto cached_child_it = m_children_byindex.find(idx);
|
|
child_is_cached = cached_child_it != m_children_byindex.end();
|
|
if (child_is_cached)
|
|
valobj = cached_child_it->second;
|
|
}
|
|
|
|
if (!child_is_cached) {
|
|
if (can_create && m_synth_filter_up != nullptr) {
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::GetChildAtIndex] name={0}, child at "
|
|
"index {1} not cached and will be created",
|
|
GetName(), idx);
|
|
|
|
lldb::ValueObjectSP synth_guy = m_synth_filter_up->GetChildAtIndex(idx);
|
|
|
|
LLDB_LOG(
|
|
log,
|
|
"[ValueObjectSynthetic::GetChildAtIndex] name={0}, child at index "
|
|
"{1} created as {2} (is "
|
|
"synthetic: {3})",
|
|
GetName(), idx, static_cast<void *>(synth_guy.get()),
|
|
synth_guy.get()
|
|
? (synth_guy->IsSyntheticChildrenGenerated() ? "yes" : "no")
|
|
: "no");
|
|
|
|
if (!synth_guy)
|
|
return synth_guy;
|
|
|
|
{
|
|
std::lock_guard<std::mutex> guard(m_child_mutex);
|
|
if (synth_guy->IsSyntheticChildrenGenerated())
|
|
m_synthetic_children_cache.push_back(synth_guy);
|
|
m_children_byindex[idx] = synth_guy.get();
|
|
}
|
|
synth_guy->SetPreferredDisplayLanguageIfNeeded(
|
|
GetPreferredDisplayLanguage());
|
|
|
|
if (lldb::ValueObjectSP check_sp =
|
|
CheckValueObjectOwnership(synth_guy.get()))
|
|
return check_sp;
|
|
return synth_guy;
|
|
} else {
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::GetChildAtIndex] name={0}, child at "
|
|
"index {1} not cached and cannot "
|
|
"be created (can_create = {2}, synth_filter = {3})",
|
|
GetName(), idx, can_create ? "yes" : "no",
|
|
static_cast<void *>(m_synth_filter_up.get()));
|
|
|
|
return lldb::ValueObjectSP();
|
|
}
|
|
} else {
|
|
LLDB_LOG(log,
|
|
"[ValueObjectSynthetic::GetChildAtIndex] name={0}, child at "
|
|
"index {1} cached as {2}",
|
|
GetName(), idx, static_cast<void *>(valobj));
|
|
lldb::ValueObjectSP check_sp = CheckValueObjectOwnership(valobj);
|
|
if (check_sp)
|
|
return check_sp;
|
|
return valobj->GetSP();
|
|
}
|
|
}
|
|
|
|
lldb::ValueObjectSP
|
|
ValueObjectSynthetic::GetChildMemberWithName(llvm::StringRef name,
|
|
bool can_create) {
|
|
UpdateValueIfNeeded();
|
|
|
|
auto index_or_err = GetIndexOfChildWithName(name);
|
|
|
|
if (!index_or_err) {
|
|
llvm::consumeError(index_or_err.takeError());
|
|
return lldb::ValueObjectSP();
|
|
}
|
|
|
|
return GetChildAtIndex(*index_or_err, can_create);
|
|
}
|
|
|
|
llvm::Expected<size_t>
|
|
ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) {
|
|
UpdateValueIfNeeded();
|
|
|
|
ConstString name(name_ref);
|
|
|
|
std::optional<uint32_t> found_index = std::nullopt;
|
|
{
|
|
std::lock_guard<std::mutex> guard(m_child_mutex);
|
|
auto name_to_index = m_name_toindex.find(name.GetCString());
|
|
if (name_to_index != m_name_toindex.end())
|
|
found_index = name_to_index->second;
|
|
}
|
|
|
|
if (!found_index && m_synth_filter_up != nullptr) {
|
|
size_t index = SIZE_MAX;
|
|
if (auto index_or_err = m_synth_filter_up->GetIndexOfChildWithName(name)) {
|
|
index = *index_or_err;
|
|
} else if (!m_synth_sp->CustomSubscripting()) {
|
|
// Provide automatic support for subscript child names ("[N]").
|
|
auto maybe_index = formatters::ExtractIndexFromString(name.GetCString());
|
|
if (!maybe_index)
|
|
// The child name was not of the form "[N]", return the original error.
|
|
return index_or_err.takeError();
|
|
|
|
// Subscripting succeeded, ignore the original error.
|
|
llvm::consumeError(index_or_err.takeError());
|
|
index = *maybe_index;
|
|
|
|
// Prevent unnecessary work by limiting max to one past the index.
|
|
uint32_t max = index + 1;
|
|
auto num_children = GetNumChildrenIgnoringErrors(max);
|
|
if (index >= num_children)
|
|
return llvm::createStringErrorV("subscript index out of range: {0}",
|
|
index);
|
|
}
|
|
std::lock_guard<std::mutex> guard(m_child_mutex);
|
|
m_name_toindex[name.GetCString()] = index;
|
|
return index;
|
|
} else if (!found_index && m_synth_filter_up == nullptr) {
|
|
return llvm::createStringErrorV("type has no child named '{0}'", name);
|
|
} else if (found_index)
|
|
return *found_index;
|
|
|
|
return llvm::createStringErrorV("type has no child named '{0}'", name);
|
|
}
|
|
|
|
bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); }
|
|
|
|
lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() {
|
|
return m_parent->GetSP();
|
|
}
|
|
|
|
void ValueObjectSynthetic::CopyValueData(ValueObject *source) {
|
|
if (!source->UpdateValueIfNeeded())
|
|
return;
|
|
m_value = source->GetValue();
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
|
|
}
|
|
|
|
bool ValueObjectSynthetic::CanProvideValue() {
|
|
if (!UpdateValueIfNeeded())
|
|
return false;
|
|
if (m_provides_value == eLazyBoolYes)
|
|
return true;
|
|
return m_parent->CanProvideValue();
|
|
}
|
|
|
|
bool ValueObjectSynthetic::SetValueFromCString(const char *value_str,
|
|
Status &error) {
|
|
return m_parent->SetValueFromCString(value_str, error);
|
|
}
|
|
|
|
void ValueObjectSynthetic::SetFormat(lldb::Format format) {
|
|
if (m_parent) {
|
|
m_parent->ClearUserVisibleData(eClearUserVisibleDataItemsAll);
|
|
m_parent->SetFormat(format);
|
|
}
|
|
this->ValueObject::SetFormat(format);
|
|
this->ClearUserVisibleData(eClearUserVisibleDataItemsAll);
|
|
}
|
|
|
|
void ValueObjectSynthetic::SetPreferredDisplayLanguage(
|
|
lldb::LanguageType lang) {
|
|
this->ValueObject::SetPreferredDisplayLanguage(lang);
|
|
if (m_parent)
|
|
m_parent->SetPreferredDisplayLanguage(lang);
|
|
}
|
|
|
|
lldb::LanguageType ValueObjectSynthetic::GetPreferredDisplayLanguage() {
|
|
if (m_preferred_display_language == lldb::eLanguageTypeUnknown) {
|
|
if (m_parent)
|
|
return m_parent->GetPreferredDisplayLanguage();
|
|
return lldb::eLanguageTypeUnknown;
|
|
} else
|
|
return m_preferred_display_language;
|
|
}
|
|
|
|
bool ValueObjectSynthetic::IsSyntheticChildrenGenerated() {
|
|
if (m_parent)
|
|
return m_parent->IsSyntheticChildrenGenerated();
|
|
return false;
|
|
}
|
|
|
|
void ValueObjectSynthetic::SetSyntheticChildrenGenerated(bool b) {
|
|
if (m_parent)
|
|
m_parent->SetSyntheticChildrenGenerated(b);
|
|
this->ValueObject::SetSyntheticChildrenGenerated(b);
|
|
}
|
|
|
|
bool ValueObjectSynthetic::GetDeclaration(Declaration &decl) {
|
|
if (m_parent)
|
|
return m_parent->GetDeclaration(decl);
|
|
|
|
return ValueObject::GetDeclaration(decl);
|
|
}
|
|
|
|
uint64_t ValueObjectSynthetic::GetLanguageFlags() {
|
|
if (m_parent)
|
|
return m_parent->GetLanguageFlags();
|
|
return this->ValueObject::GetLanguageFlags();
|
|
}
|
|
|
|
void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) {
|
|
if (m_parent)
|
|
m_parent->SetLanguageFlags(flags);
|
|
else
|
|
this->ValueObject::SetLanguageFlags(flags);
|
|
}
|
|
|
|
void ValueObjectSynthetic::GetExpressionPath(Stream &stream,
|
|
GetExpressionPathFormat epformat) {
|
|
// A synthetic ValueObject may wrap an underlying Register or RegisterSet
|
|
// ValueObject, which requires a different approach to generating the
|
|
// expression path. In such cases, delegate to the non-synthetic value object.
|
|
if (const lldb::ValueType obj_value_type = GetValueType();
|
|
IsSynthetic() && (obj_value_type == lldb::eValueTypeRegister ||
|
|
obj_value_type == lldb::eValueTypeRegisterSet)) {
|
|
|
|
if (const lldb::ValueObjectSP raw_value = GetNonSyntheticValue())
|
|
return raw_value->GetExpressionPath(stream, epformat);
|
|
}
|
|
return ValueObject::GetExpressionPath(stream, epformat);
|
|
}
|