diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/DataFormatters')
16 files changed, 4757 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/CXXFunctionPointer.cpp b/contrib/llvm-project/lldb/source/DataFormatters/CXXFunctionPointer.cpp new file mode 100644 index 000000000000..6543433d17ff --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/CXXFunctionPointer.cpp @@ -0,0 +1,87 @@ +//===-- CXXFunctionPointer.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/DataFormatters/CXXFunctionPointer.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Stream.h" +#include "lldb/lldb-enumerations.h" + +#include <string> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool lldb_private::formatters::CXXFunctionPointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + std::string destination; + StreamString sstr; + AddressType func_ptr_address_type = eAddressTypeInvalid; + addr_t func_ptr_address = valobj.GetPointerValue(&func_ptr_address_type); + if (func_ptr_address != 0 && func_ptr_address != LLDB_INVALID_ADDRESS) { + switch (func_ptr_address_type) { + case eAddressTypeInvalid: + case eAddressTypeFile: + case eAddressTypeHost: + break; + + case eAddressTypeLoad: { + ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); + + Address so_addr; + Target *target = exe_ctx.GetTargetPtr(); + if (target && !target->GetSectionLoadList().IsEmpty()) { + target->GetSectionLoadList().ResolveLoadAddress(func_ptr_address, + so_addr); + if (so_addr.GetSection() == nullptr) { + // If we have an address that doesn't correspond to any symbol, + // it might have authentication bits. Strip them & see if it + // now points to a symbol -- if so, do the SymbolContext lookup + // based on the stripped address. + // If we find a symbol with the ptrauth bits stripped, print the + // raw value into the stream, and replace the Address with the + // one that points to a symbol for a fuller description. + if (Process *process = exe_ctx.GetProcessPtr()) { + if (ABISP abi_sp = process->GetABI()) { + addr_t fixed_addr = abi_sp->FixCodeAddress(func_ptr_address); + if (fixed_addr != func_ptr_address) { + Address test_address; + test_address.SetLoadAddress(fixed_addr, target); + if (test_address.GetSection() != nullptr) { + int addrsize = target->GetArchitecture().GetAddressByteSize(); + sstr.Printf("actual=0x%*.*" PRIx64 " ", addrsize * 2, + addrsize * 2, fixed_addr); + so_addr = test_address; + } + } + } + } + } + + if (so_addr.IsValid()) { + so_addr.Dump(&sstr, exe_ctx.GetBestExecutionContextScope(), + Address::DumpStyleResolvedDescription, + Address::DumpStyleSectionNameOffset); + } + } + } break; + } + } + if (sstr.GetSize() > 0) { + if (valobj.GetValueType() == lldb::eValueTypeVTableEntry) + stream.PutCString(sstr.GetData()); + else + stream.Printf("(%s)", sstr.GetData()); + return true; + } else + return false; +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/DataVisualization.cpp b/contrib/llvm-project/lldb/source/DataFormatters/DataVisualization.cpp new file mode 100644 index 000000000000..036c9372baf8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/DataVisualization.cpp @@ -0,0 +1,196 @@ +//===-- DataVisualization.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/DataFormatters/DataVisualization.h" + + +using namespace lldb; +using namespace lldb_private; + +static FormatManager &GetFormatManager() { + static FormatManager g_format_manager; + return g_format_manager; +} + +void DataVisualization::ForceUpdate() { GetFormatManager().Changed(); } + +uint32_t DataVisualization::GetCurrentRevision() { + return GetFormatManager().GetCurrentRevision(); +} + +bool DataVisualization::ShouldPrintAsOneLiner(ValueObject &valobj) { + return GetFormatManager().ShouldPrintAsOneLiner(valobj); +} + +lldb::TypeFormatImplSP +DataVisualization::GetFormat(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) { + return GetFormatManager().GetFormat(valobj, use_dynamic); +} + +lldb::TypeFormatImplSP +DataVisualization::GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp) { + return GetFormatManager().GetFormatForType(type_sp); +} + +lldb::TypeSummaryImplSP +DataVisualization::GetSummaryFormat(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) { + return GetFormatManager().GetSummaryFormat(valobj, use_dynamic); +} + +lldb::TypeSummaryImplSP +DataVisualization::GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp) { + return GetFormatManager().GetSummaryForType(type_sp); +} + +lldb::SyntheticChildrenSP +DataVisualization::GetSyntheticChildren(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) { + return GetFormatManager().GetSyntheticChildren(valobj, use_dynamic); +} + +lldb::TypeFilterImplSP +DataVisualization::GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp) { + return GetFormatManager().GetFilterForType(type_sp); +} + +lldb::ScriptedSyntheticChildrenSP +DataVisualization::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) { + return GetFormatManager().GetSyntheticForType(type_sp); +} + +bool DataVisualization::AnyMatches( + const FormattersMatchCandidate &candidate_type, + TypeCategoryImpl::FormatCategoryItems items, bool only_enabled, + const char **matching_category, + TypeCategoryImpl::FormatCategoryItems *matching_type) { + return GetFormatManager().AnyMatches(candidate_type, items, only_enabled, + matching_category, matching_type); +} + +bool DataVisualization::Categories::GetCategory(ConstString category, + lldb::TypeCategoryImplSP &entry, + bool allow_create) { + entry = GetFormatManager().GetCategory(category, allow_create); + return (entry.get() != nullptr); +} + +bool DataVisualization::Categories::GetCategory( + lldb::LanguageType language, lldb::TypeCategoryImplSP &entry) { + if (LanguageCategory *lang_category = + GetFormatManager().GetCategoryForLanguage(language)) + entry = lang_category->GetCategory(); + return (entry.get() != nullptr); +} + +void DataVisualization::Categories::Add(ConstString category) { + GetFormatManager().GetCategory(category); +} + +bool DataVisualization::Categories::Delete(ConstString category) { + GetFormatManager().DisableCategory(category); + return GetFormatManager().DeleteCategory(category); +} + +void DataVisualization::Categories::Clear() { + GetFormatManager().ClearCategories(); +} + +void DataVisualization::Categories::Clear(ConstString category) { + GetFormatManager().GetCategory(category)->Clear(eFormatCategoryItemSummary); +} + +void DataVisualization::Categories::Enable(ConstString category, + TypeCategoryMap::Position pos) { + if (GetFormatManager().GetCategory(category)->IsEnabled()) + GetFormatManager().DisableCategory(category); + GetFormatManager().EnableCategory(category, pos, {}); +} + +void DataVisualization::Categories::Enable(lldb::LanguageType lang_type) { + if (LanguageCategory *lang_category = + GetFormatManager().GetCategoryForLanguage(lang_type)) + lang_category->Enable(); +} + +void DataVisualization::Categories::Disable(ConstString category) { + if (GetFormatManager().GetCategory(category)->IsEnabled()) + GetFormatManager().DisableCategory(category); +} + +void DataVisualization::Categories::Disable(lldb::LanguageType lang_type) { + if (LanguageCategory *lang_category = + GetFormatManager().GetCategoryForLanguage(lang_type)) + lang_category->Disable(); +} + +void DataVisualization::Categories::Enable( + const lldb::TypeCategoryImplSP &category, TypeCategoryMap::Position pos) { + if (category.get()) { + if (category->IsEnabled()) + GetFormatManager().DisableCategory(category); + GetFormatManager().EnableCategory(category, pos); + } +} + +void DataVisualization::Categories::Disable( + const lldb::TypeCategoryImplSP &category) { + if (category.get() && category->IsEnabled()) + GetFormatManager().DisableCategory(category); +} + +void DataVisualization::Categories::EnableStar() { + GetFormatManager().EnableAllCategories(); +} + +void DataVisualization::Categories::DisableStar() { + GetFormatManager().DisableAllCategories(); +} + +void DataVisualization::Categories::ForEach( + TypeCategoryMap::ForEachCallback callback) { + GetFormatManager().ForEachCategory(callback); +} + +uint32_t DataVisualization::Categories::GetCount() { + return GetFormatManager().GetCategoriesCount(); +} + +lldb::TypeCategoryImplSP +DataVisualization::Categories::GetCategoryAtIndex(size_t index) { + return GetFormatManager().GetCategoryAtIndex(index); +} + +bool DataVisualization::NamedSummaryFormats::GetSummaryFormat( + ConstString type, lldb::TypeSummaryImplSP &entry) { + return GetFormatManager().GetNamedSummaryContainer().GetExact(type, entry); +} + +void DataVisualization::NamedSummaryFormats::Add( + ConstString type, const lldb::TypeSummaryImplSP &entry) { + GetFormatManager().GetNamedSummaryContainer().Add(type, entry); +} + +bool DataVisualization::NamedSummaryFormats::Delete(ConstString type) { + return GetFormatManager().GetNamedSummaryContainer().Delete(type); +} + +void DataVisualization::NamedSummaryFormats::Clear() { + GetFormatManager().GetNamedSummaryContainer().Clear(); +} + +void DataVisualization::NamedSummaryFormats::ForEach( + std::function<bool(const TypeMatcher &, const lldb::TypeSummaryImplSP &)> + callback) { + GetFormatManager().GetNamedSummaryContainer().ForEach(callback); +} + +uint32_t DataVisualization::NamedSummaryFormats::GetCount() { + return GetFormatManager().GetNamedSummaryContainer().GetCount(); +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/DumpValueObjectOptions.cpp b/contrib/llvm-project/lldb/source/DataFormatters/DumpValueObjectOptions.cpp new file mode 100644 index 000000000000..c5e84810cdc8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/DumpValueObjectOptions.cpp @@ -0,0 +1,214 @@ +//===-- DumpValueObjectOptions.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/DataFormatters/DumpValueObjectOptions.h" + +#include "lldb/Core/ValueObject.h" + +using namespace lldb; +using namespace lldb_private; + +DumpValueObjectOptions::DumpValueObjectOptions() + : m_summary_sp(), m_root_valobj_name(), + m_max_ptr_depth(PointerDepth{PointerDepth::Mode::Default, 0}), + m_decl_printing_helper(), m_child_printing_decider(), + m_pointer_as_array(), m_use_synthetic(true), + m_scope_already_checked(false), m_flat_output(false), m_ignore_cap(false), + m_show_types(false), m_show_location(false), m_use_objc(false), + m_hide_root_type(false), m_hide_root_name(false), m_hide_name(false), + m_hide_value(false), m_run_validator(false), + m_use_type_display_name(true), m_allow_oneliner_mode(true), + m_hide_pointer_value(false), m_reveal_empty_aggregates(true) {} + +DumpValueObjectOptions::DumpValueObjectOptions(ValueObject &valobj) + : DumpValueObjectOptions() { + m_use_dynamic = valobj.GetDynamicValueType(); + m_use_synthetic = valobj.IsSynthetic(); + m_varformat_language = valobj.GetPreferredDisplayLanguage(); +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetMaximumPointerDepth(PointerDepth depth) { + m_max_ptr_depth = depth; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetMaximumDepth(uint32_t depth, bool is_default) { + m_max_depth = depth; + m_max_depth_is_default = is_default; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetDeclPrintingHelper(DeclPrintingHelper helper) { + m_decl_printing_helper = helper; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetChildPrintingDecider(ChildPrintingDecider decider) { + m_child_printing_decider = decider; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetShowTypes(bool show) { + m_show_types = show; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetShowLocation(bool show) { + m_show_location = show; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetUseObjectiveC(bool use) { + m_use_objc = use; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetShowSummary(bool show) { + if (!show) + SetOmitSummaryDepth(UINT32_MAX); + else + SetOmitSummaryDepth(0); + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetUseDynamicType(lldb::DynamicValueType dyn) { + m_use_dynamic = dyn; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetUseSyntheticValue(bool use_synthetic) { + m_use_synthetic = use_synthetic; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetScopeChecked(bool check) { + m_scope_already_checked = check; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetFlatOutput(bool flat) { + m_flat_output = flat; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetOmitSummaryDepth(uint32_t depth) { + m_omit_summary_depth = depth; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetIgnoreCap(bool ignore) { + m_ignore_cap = ignore; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetRawDisplay() { + SetUseSyntheticValue(false); + SetOmitSummaryDepth(UINT32_MAX); + SetIgnoreCap(true); + SetHideName(false); + SetHideValue(false); + SetUseTypeDisplayName(false); + SetAllowOnelinerMode(false); + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetFormat(lldb::Format format) { + m_format = format; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetSummary(lldb::TypeSummaryImplSP summary) { + m_summary_sp = summary; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetRootValueObjectName(const char *name) { + if (name) + m_root_valobj_name.assign(name); + else + m_root_valobj_name.clear(); + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetHideRootType(bool hide_root_type) { + m_hide_root_type = hide_root_type; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetHideRootName(bool hide_root_name) { + m_hide_root_name = hide_root_name; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetHideName(bool hide_name) { + m_hide_name = hide_name; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetHideValue(bool hide_value) { + m_hide_value = hide_value; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetHidePointerValue(bool hide) { + m_hide_pointer_value = hide; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetVariableFormatDisplayLanguage( + lldb::LanguageType lang) { + m_varformat_language = lang; + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetRunValidator(bool run) { + m_run_validator = run; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetUseTypeDisplayName(bool dis) { + m_use_type_display_name = dis; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetAllowOnelinerMode(bool oneliner) { + m_allow_oneliner_mode = oneliner; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetRevealEmptyAggregates(bool reveal) { + m_reveal_empty_aggregates = reveal; + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetElementCount(uint32_t element_count) { + m_pointer_as_array = PointerAsArraySettings(element_count); + return *this; +} + +DumpValueObjectOptions &DumpValueObjectOptions::SetPointerAsArray( + const PointerAsArraySettings &ptr_array) { + m_pointer_as_array = ptr_array; + return *this; +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/FormatCache.cpp b/contrib/llvm-project/lldb/source/DataFormatters/FormatCache.cpp new file mode 100644 index 000000000000..6c83b36e79de --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/FormatCache.cpp @@ -0,0 +1,113 @@ +//===-- FormatCache.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/DataFormatters/FormatCache.h" + +using namespace lldb; +using namespace lldb_private; + +FormatCache::Entry::Entry() + : m_format_cached(false), m_summary_cached(false), + m_synthetic_cached(false) {} + +bool FormatCache::Entry::IsFormatCached() { return m_format_cached; } + +bool FormatCache::Entry::IsSummaryCached() { return m_summary_cached; } + +bool FormatCache::Entry::IsSyntheticCached() { return m_synthetic_cached; } + +void FormatCache::Entry::Get(lldb::TypeFormatImplSP &retval) { + retval = m_format_sp; +} + +void FormatCache::Entry::Get(lldb::TypeSummaryImplSP &retval) { + retval = m_summary_sp; +} + +void FormatCache::Entry::Get(lldb::SyntheticChildrenSP &retval) { + retval = m_synthetic_sp; +} + +void FormatCache::Entry::Set(lldb::TypeFormatImplSP format_sp) { + m_format_cached = true; + m_format_sp = format_sp; +} + +void FormatCache::Entry::Set(lldb::TypeSummaryImplSP summary_sp) { + m_summary_cached = true; + m_summary_sp = summary_sp; +} + +void FormatCache::Entry::Set(lldb::SyntheticChildrenSP synthetic_sp) { + m_synthetic_cached = true; + m_synthetic_sp = synthetic_sp; +} + +namespace lldb_private { + +template<> bool FormatCache::Entry::IsCached<lldb::TypeFormatImplSP>() { + return IsFormatCached(); +} +template<> bool FormatCache::Entry::IsCached<lldb::TypeSummaryImplSP> () { + return IsSummaryCached(); +} +template<> bool FormatCache::Entry::IsCached<lldb::SyntheticChildrenSP>() { + return IsSyntheticCached(); +} + +} // namespace lldb_private + +template <typename ImplSP> +bool FormatCache::Get(ConstString type, ImplSP &format_impl_sp) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + auto entry = m_entries[type]; + if (entry.IsCached<ImplSP>()) { + m_cache_hits++; + entry.Get(format_impl_sp); + return true; + } + m_cache_misses++; + format_impl_sp.reset(); + return false; +} + +/// Explicit instantiations for the three types. +/// \{ +template bool +FormatCache::Get<lldb::TypeFormatImplSP>(ConstString, lldb::TypeFormatImplSP &); +template bool +FormatCache::Get<lldb::TypeSummaryImplSP>(ConstString, + lldb::TypeSummaryImplSP &); +template bool +FormatCache::Get<lldb::SyntheticChildrenSP>(ConstString, + lldb::SyntheticChildrenSP &); +/// \} + +void FormatCache::Set(ConstString type, lldb::TypeFormatImplSP &format_sp) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_entries[type].Set(format_sp); +} + +void FormatCache::Set(ConstString type, lldb::TypeSummaryImplSP &summary_sp) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_entries[type].Set(summary_sp); +} + +void FormatCache::Set(ConstString type, + lldb::SyntheticChildrenSP &synthetic_sp) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_entries[type].Set(synthetic_sp); +} + +void FormatCache::Clear() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_entries.clear(); +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/FormatClasses.cpp b/contrib/llvm-project/lldb/source/DataFormatters/FormatClasses.cpp new file mode 100644 index 000000000000..44fa96033bc1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/FormatClasses.cpp @@ -0,0 +1,49 @@ +//===-- FormatClasses.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/DataFormatters/FormatClasses.h" + +#include "lldb/DataFormatters/FormatManager.h" + + + + + +using namespace lldb; +using namespace lldb_private; + +FormattersMatchData::FormattersMatchData(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) + : m_valobj(valobj), m_dynamic_value_type(use_dynamic), + m_formatters_match_vector({}, false), m_type_for_cache(), + m_candidate_languages() { + m_type_for_cache = FormatManager::GetTypeForCache(valobj, use_dynamic); + m_candidate_languages = + FormatManager::GetCandidateLanguages(valobj.GetObjectRuntimeLanguage()); +} + +FormattersMatchVector FormattersMatchData::GetMatchesVector() { + if (!m_formatters_match_vector.second) { + m_formatters_match_vector.second = true; + m_formatters_match_vector.first = + FormatManager::GetPossibleMatches(m_valobj, m_dynamic_value_type); + } + return m_formatters_match_vector.first; +} + +ConstString FormattersMatchData::GetTypeForCache() { return m_type_for_cache; } + +CandidateLanguagesVector FormattersMatchData::GetCandidateLanguages() { + return m_candidate_languages; +} + +ValueObject &FormattersMatchData::GetValueObject() { return m_valobj; } + +lldb::DynamicValueType FormattersMatchData::GetDynamicValueType() { + return m_dynamic_value_type; +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/FormatManager.cpp b/contrib/llvm-project/lldb/source/DataFormatters/FormatManager.cpp new file mode 100644 index 000000000000..7e19989a8264 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/FormatManager.cpp @@ -0,0 +1,795 @@ +//===-- FormatManager.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/DataFormatters/FormatManager.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/LanguageCategory.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Language.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +struct FormatInfo { + Format format; + const char format_char; // One or more format characters that can be used for + // this format. + const char *format_name; // Long format name that can be used to specify the + // current format +}; + +static constexpr FormatInfo g_format_infos[] = { + {eFormatDefault, '\0', "default"}, + {eFormatBoolean, 'B', "boolean"}, + {eFormatBinary, 'b', "binary"}, + {eFormatBytes, 'y', "bytes"}, + {eFormatBytesWithASCII, 'Y', "bytes with ASCII"}, + {eFormatChar, 'c', "character"}, + {eFormatCharPrintable, 'C', "printable character"}, + {eFormatComplexFloat, 'F', "complex float"}, + {eFormatCString, 's', "c-string"}, + {eFormatDecimal, 'd', "decimal"}, + {eFormatEnum, 'E', "enumeration"}, + {eFormatHex, 'x', "hex"}, + {eFormatHexUppercase, 'X', "uppercase hex"}, + {eFormatFloat, 'f', "float"}, + {eFormatOctal, 'o', "octal"}, + {eFormatOSType, 'O', "OSType"}, + {eFormatUnicode16, 'U', "unicode16"}, + {eFormatUnicode32, '\0', "unicode32"}, + {eFormatUnsigned, 'u', "unsigned decimal"}, + {eFormatPointer, 'p', "pointer"}, + {eFormatVectorOfChar, '\0', "char[]"}, + {eFormatVectorOfSInt8, '\0', "int8_t[]"}, + {eFormatVectorOfUInt8, '\0', "uint8_t[]"}, + {eFormatVectorOfSInt16, '\0', "int16_t[]"}, + {eFormatVectorOfUInt16, '\0', "uint16_t[]"}, + {eFormatVectorOfSInt32, '\0', "int32_t[]"}, + {eFormatVectorOfUInt32, '\0', "uint32_t[]"}, + {eFormatVectorOfSInt64, '\0', "int64_t[]"}, + {eFormatVectorOfUInt64, '\0', "uint64_t[]"}, + {eFormatVectorOfFloat16, '\0', "float16[]"}, + {eFormatVectorOfFloat32, '\0', "float32[]"}, + {eFormatVectorOfFloat64, '\0', "float64[]"}, + {eFormatVectorOfUInt128, '\0', "uint128_t[]"}, + {eFormatComplexInteger, 'I', "complex integer"}, + {eFormatCharArray, 'a', "character array"}, + {eFormatAddressInfo, 'A', "address"}, + {eFormatHexFloat, '\0', "hex float"}, + {eFormatInstruction, 'i', "instruction"}, + {eFormatVoid, 'v', "void"}, + {eFormatUnicode8, 'u', "unicode8"}, +}; + +static_assert((sizeof(g_format_infos) / sizeof(g_format_infos[0])) == + kNumFormats, + "All formats must have a corresponding info entry."); + +static uint32_t g_num_format_infos = std::size(g_format_infos); + +static bool GetFormatFromFormatChar(char format_char, Format &format) { + for (uint32_t i = 0; i < g_num_format_infos; ++i) { + if (g_format_infos[i].format_char == format_char) { + format = g_format_infos[i].format; + return true; + } + } + format = eFormatInvalid; + return false; +} + +static bool GetFormatFromFormatName(llvm::StringRef format_name, + Format &format) { + uint32_t i; + for (i = 0; i < g_num_format_infos; ++i) { + if (format_name.equals_insensitive(g_format_infos[i].format_name)) { + format = g_format_infos[i].format; + return true; + } + } + + for (i = 0; i < g_num_format_infos; ++i) { + if (llvm::StringRef(g_format_infos[i].format_name) + .starts_with_insensitive(format_name)) { + format = g_format_infos[i].format; + return true; + } + } + format = eFormatInvalid; + return false; +} + +void FormatManager::Changed() { + ++m_last_revision; + m_format_cache.Clear(); + std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex); + for (auto &iter : m_language_categories_map) { + if (iter.second) + iter.second->GetFormatCache().Clear(); + } +} + +bool FormatManager::GetFormatFromCString(const char *format_cstr, + lldb::Format &format) { + bool success = false; + if (format_cstr && format_cstr[0]) { + if (format_cstr[1] == '\0') { + success = GetFormatFromFormatChar(format_cstr[0], format); + if (success) + return true; + } + + success = GetFormatFromFormatName(format_cstr, format); + } + if (!success) + format = eFormatInvalid; + return success; +} + +char FormatManager::GetFormatAsFormatChar(lldb::Format format) { + for (uint32_t i = 0; i < g_num_format_infos; ++i) { + if (g_format_infos[i].format == format) + return g_format_infos[i].format_char; + } + return '\0'; +} + +const char *FormatManager::GetFormatAsCString(Format format) { + if (format >= eFormatDefault && format < kNumFormats) + return g_format_infos[format].format_name; + return nullptr; +} + +void FormatManager::EnableAllCategories() { + m_categories_map.EnableAllCategories(); + std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex); + for (auto &iter : m_language_categories_map) { + if (iter.second) + iter.second->Enable(); + } +} + +void FormatManager::DisableAllCategories() { + m_categories_map.DisableAllCategories(); + std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex); + for (auto &iter : m_language_categories_map) { + if (iter.second) + iter.second->Disable(); + } +} + +void FormatManager::GetPossibleMatches( + ValueObject &valobj, CompilerType compiler_type, + lldb::DynamicValueType use_dynamic, FormattersMatchVector &entries, + FormattersMatchCandidate::Flags current_flags, bool root_level) { + compiler_type = compiler_type.GetTypeForFormatters(); + ConstString type_name(compiler_type.GetTypeName()); + // A ValueObject that couldn't be made correctly won't necessarily have a + // target. We aren't going to find a formatter in this case anyway, so we + // should just exit. + TargetSP target_sp = valobj.GetTargetSP(); + if (!target_sp) + return; + ScriptInterpreter *script_interpreter = + target_sp->GetDebugger().GetScriptInterpreter(); + if (valobj.GetBitfieldBitSize() > 0) { + StreamString sstring; + sstring.Printf("%s:%d", type_name.AsCString(), valobj.GetBitfieldBitSize()); + ConstString bitfieldname(sstring.GetString()); + entries.push_back({bitfieldname, script_interpreter, + TypeImpl(compiler_type), current_flags}); + } + + if (!compiler_type.IsMeaninglessWithoutDynamicResolution()) { + entries.push_back({type_name, script_interpreter, TypeImpl(compiler_type), + current_flags}); + + ConstString display_type_name(compiler_type.GetTypeName()); + if (display_type_name != type_name) + entries.push_back({display_type_name, script_interpreter, + TypeImpl(compiler_type), current_flags}); + } + + for (bool is_rvalue_ref = true, j = true; + j && compiler_type.IsReferenceType(nullptr, &is_rvalue_ref); j = false) { + CompilerType non_ref_type = compiler_type.GetNonReferenceType(); + GetPossibleMatches(valobj, non_ref_type, use_dynamic, entries, + current_flags.WithStrippedReference()); + if (non_ref_type.IsTypedefType()) { + CompilerType deffed_referenced_type = non_ref_type.GetTypedefedType(); + deffed_referenced_type = + is_rvalue_ref ? deffed_referenced_type.GetRValueReferenceType() + : deffed_referenced_type.GetLValueReferenceType(); + // this is not exactly the usual meaning of stripping typedefs + GetPossibleMatches( + valobj, deffed_referenced_type, + use_dynamic, entries, current_flags.WithStrippedTypedef()); + } + } + + if (compiler_type.IsPointerType()) { + CompilerType non_ptr_type = compiler_type.GetPointeeType(); + GetPossibleMatches(valobj, non_ptr_type, use_dynamic, entries, + current_flags.WithStrippedPointer()); + if (non_ptr_type.IsTypedefType()) { + CompilerType deffed_pointed_type = + non_ptr_type.GetTypedefedType().GetPointerType(); + // this is not exactly the usual meaning of stripping typedefs + GetPossibleMatches(valobj, deffed_pointed_type, use_dynamic, entries, + current_flags.WithStrippedTypedef()); + } + } + + // For arrays with typedef-ed elements, we add a candidate with the typedef + // stripped. + uint64_t array_size; + if (compiler_type.IsArrayType(nullptr, &array_size, nullptr)) { + ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); + CompilerType element_type = compiler_type.GetArrayElementType( + exe_ctx.GetBestExecutionContextScope()); + if (element_type.IsTypedefType()) { + // Get the stripped element type and compute the stripped array type + // from it. + CompilerType deffed_array_type = + element_type.GetTypedefedType().GetArrayType(array_size); + // this is not exactly the usual meaning of stripping typedefs + GetPossibleMatches( + valobj, deffed_array_type, + use_dynamic, entries, current_flags.WithStrippedTypedef()); + } + } + + for (lldb::LanguageType language_type : + GetCandidateLanguages(valobj.GetObjectRuntimeLanguage())) { + if (Language *language = Language::FindPlugin(language_type)) { + for (const FormattersMatchCandidate& candidate : + language->GetPossibleFormattersMatches(valobj, use_dynamic)) { + entries.push_back(candidate); + } + } + } + + // try to strip typedef chains + if (compiler_type.IsTypedefType()) { + CompilerType deffed_type = compiler_type.GetTypedefedType(); + GetPossibleMatches(valobj, deffed_type, use_dynamic, entries, + current_flags.WithStrippedTypedef()); + } + + if (root_level) { + do { + if (!compiler_type.IsValid()) + break; + + CompilerType unqual_compiler_ast_type = + compiler_type.GetFullyUnqualifiedType(); + if (!unqual_compiler_ast_type.IsValid()) + break; + if (unqual_compiler_ast_type.GetOpaqueQualType() != + compiler_type.GetOpaqueQualType()) + GetPossibleMatches(valobj, unqual_compiler_ast_type, use_dynamic, + entries, current_flags); + } while (false); + + // if all else fails, go to static type + if (valobj.IsDynamic()) { + lldb::ValueObjectSP static_value_sp(valobj.GetStaticValue()); + if (static_value_sp) + GetPossibleMatches(*static_value_sp.get(), + static_value_sp->GetCompilerType(), use_dynamic, + entries, current_flags, true); + } + } +} + +lldb::TypeFormatImplSP +FormatManager::GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp) { + if (!type_sp) + return lldb::TypeFormatImplSP(); + lldb::TypeFormatImplSP format_chosen_sp; + uint32_t num_categories = m_categories_map.GetCount(); + lldb::TypeCategoryImplSP category_sp; + uint32_t prio_category = UINT32_MAX; + for (uint32_t category_id = 0; category_id < num_categories; category_id++) { + category_sp = GetCategoryAtIndex(category_id); + if (!category_sp->IsEnabled()) + continue; + lldb::TypeFormatImplSP format_current_sp = + category_sp->GetFormatForType(type_sp); + if (format_current_sp && + (format_chosen_sp.get() == nullptr || + (prio_category > category_sp->GetEnabledPosition()))) { + prio_category = category_sp->GetEnabledPosition(); + format_chosen_sp = format_current_sp; + } + } + return format_chosen_sp; +} + +lldb::TypeSummaryImplSP +FormatManager::GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp) { + if (!type_sp) + return lldb::TypeSummaryImplSP(); + lldb::TypeSummaryImplSP summary_chosen_sp; + uint32_t num_categories = m_categories_map.GetCount(); + lldb::TypeCategoryImplSP category_sp; + uint32_t prio_category = UINT32_MAX; + for (uint32_t category_id = 0; category_id < num_categories; category_id++) { + category_sp = GetCategoryAtIndex(category_id); + if (!category_sp->IsEnabled()) + continue; + lldb::TypeSummaryImplSP summary_current_sp = + category_sp->GetSummaryForType(type_sp); + if (summary_current_sp && + (summary_chosen_sp.get() == nullptr || + (prio_category > category_sp->GetEnabledPosition()))) { + prio_category = category_sp->GetEnabledPosition(); + summary_chosen_sp = summary_current_sp; + } + } + return summary_chosen_sp; +} + +lldb::TypeFilterImplSP +FormatManager::GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp) { + if (!type_sp) + return lldb::TypeFilterImplSP(); + lldb::TypeFilterImplSP filter_chosen_sp; + uint32_t num_categories = m_categories_map.GetCount(); + lldb::TypeCategoryImplSP category_sp; + uint32_t prio_category = UINT32_MAX; + for (uint32_t category_id = 0; category_id < num_categories; category_id++) { + category_sp = GetCategoryAtIndex(category_id); + if (!category_sp->IsEnabled()) + continue; + lldb::TypeFilterImplSP filter_current_sp( + (TypeFilterImpl *)category_sp->GetFilterForType(type_sp).get()); + if (filter_current_sp && + (filter_chosen_sp.get() == nullptr || + (prio_category > category_sp->GetEnabledPosition()))) { + prio_category = category_sp->GetEnabledPosition(); + filter_chosen_sp = filter_current_sp; + } + } + return filter_chosen_sp; +} + +lldb::ScriptedSyntheticChildrenSP +FormatManager::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) { + if (!type_sp) + return lldb::ScriptedSyntheticChildrenSP(); + lldb::ScriptedSyntheticChildrenSP synth_chosen_sp; + uint32_t num_categories = m_categories_map.GetCount(); + lldb::TypeCategoryImplSP category_sp; + uint32_t prio_category = UINT32_MAX; + for (uint32_t category_id = 0; category_id < num_categories; category_id++) { + category_sp = GetCategoryAtIndex(category_id); + if (!category_sp->IsEnabled()) + continue; + lldb::ScriptedSyntheticChildrenSP synth_current_sp( + (ScriptedSyntheticChildren *)category_sp->GetSyntheticForType(type_sp) + .get()); + if (synth_current_sp && + (synth_chosen_sp.get() == nullptr || + (prio_category > category_sp->GetEnabledPosition()))) { + prio_category = category_sp->GetEnabledPosition(); + synth_chosen_sp = synth_current_sp; + } + } + return synth_chosen_sp; +} + +void FormatManager::ForEachCategory(TypeCategoryMap::ForEachCallback callback) { + m_categories_map.ForEach(callback); + std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex); + for (const auto &entry : m_language_categories_map) { + if (auto category_sp = entry.second->GetCategory()) { + if (!callback(category_sp)) + break; + } + } +} + +lldb::TypeCategoryImplSP +FormatManager::GetCategory(ConstString category_name, bool can_create) { + if (!category_name) + return GetCategory(m_default_category_name); + lldb::TypeCategoryImplSP category; + if (m_categories_map.Get(category_name, category)) + return category; + + if (!can_create) + return lldb::TypeCategoryImplSP(); + + m_categories_map.Add( + category_name, + lldb::TypeCategoryImplSP(new TypeCategoryImpl(this, category_name))); + return GetCategory(category_name); +} + +lldb::Format FormatManager::GetSingleItemFormat(lldb::Format vector_format) { + switch (vector_format) { + case eFormatVectorOfChar: + return eFormatCharArray; + + case eFormatVectorOfSInt8: + case eFormatVectorOfSInt16: + case eFormatVectorOfSInt32: + case eFormatVectorOfSInt64: + return eFormatDecimal; + + case eFormatVectorOfUInt8: + case eFormatVectorOfUInt16: + case eFormatVectorOfUInt32: + case eFormatVectorOfUInt64: + case eFormatVectorOfUInt128: + return eFormatHex; + + case eFormatVectorOfFloat16: + case eFormatVectorOfFloat32: + case eFormatVectorOfFloat64: + return eFormatFloat; + + default: + return lldb::eFormatInvalid; + } +} + +bool FormatManager::ShouldPrintAsOneLiner(ValueObject &valobj) { + TargetSP target_sp = valobj.GetTargetSP(); + // if settings say no oneline whatsoever + if (target_sp && !target_sp->GetDebugger().GetAutoOneLineSummaries()) + return false; // then don't oneline + + // if this object has a summary, then ask the summary + if (valobj.GetSummaryFormat().get() != nullptr) + return valobj.GetSummaryFormat()->IsOneLiner(); + + const size_t max_num_children = + (target_sp ? *target_sp : Target::GetGlobalProperties()) + .GetMaximumNumberOfChildrenToDisplay(); + auto num_children = valobj.GetNumChildren(max_num_children); + if (!num_children) { + llvm::consumeError(num_children.takeError()); + return true; + } + // no children, no party + if (*num_children == 0) + return false; + + // ask the type if it has any opinion about this eLazyBoolCalculate == no + // opinion; other values should be self explanatory + CompilerType compiler_type(valobj.GetCompilerType()); + if (compiler_type.IsValid()) { + switch (compiler_type.ShouldPrintAsOneLiner(&valobj)) { + case eLazyBoolNo: + return false; + case eLazyBoolYes: + return true; + case eLazyBoolCalculate: + break; + } + } + + size_t total_children_name_len = 0; + + for (size_t idx = 0; idx < *num_children; idx++) { + bool is_synth_val = false; + ValueObjectSP child_sp(valobj.GetChildAtIndex(idx)); + // something is wrong here - bail out + if (!child_sp) + return false; + + // also ask the child's type if it has any opinion + CompilerType child_compiler_type(child_sp->GetCompilerType()); + if (child_compiler_type.IsValid()) { + switch (child_compiler_type.ShouldPrintAsOneLiner(child_sp.get())) { + case eLazyBoolYes: + // an opinion of yes is only binding for the child, so keep going + case eLazyBoolCalculate: + break; + case eLazyBoolNo: + // but if the child says no, then it's a veto on the whole thing + return false; + } + } + + // if we decided to define synthetic children for a type, we probably care + // enough to show them, but avoid nesting children in children + if (child_sp->GetSyntheticChildren().get() != nullptr) { + ValueObjectSP synth_sp(child_sp->GetSyntheticValue()); + // wait.. wat? just get out of here.. + if (!synth_sp) + return false; + // but if we only have them to provide a value, keep going + if (!synth_sp->MightHaveChildren() && + synth_sp->DoesProvideSyntheticValue()) + is_synth_val = true; + else + return false; + } + + total_children_name_len += child_sp->GetName().GetLength(); + + // 50 itself is a "randomly" chosen number - the idea is that + // overly long structs should not get this treatment + // FIXME: maybe make this a user-tweakable setting? + if (total_children_name_len > 50) + return false; + + // if a summary is there.. + if (child_sp->GetSummaryFormat()) { + // and it wants children, then bail out + if (child_sp->GetSummaryFormat()->DoesPrintChildren(child_sp.get())) + return false; + } + + // if this child has children.. + if (child_sp->HasChildren()) { + // ...and no summary... + // (if it had a summary and the summary wanted children, we would have + // bailed out anyway + // so this only makes us bail out if this has no summary and we would + // then print children) + if (!child_sp->GetSummaryFormat() && !is_synth_val) // but again only do + // that if not a + // synthetic valued + // child + return false; // then bail out + } + } + return true; +} + +ConstString FormatManager::GetTypeForCache(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) { + ValueObjectSP valobj_sp = valobj.GetQualifiedRepresentationIfAvailable( + use_dynamic, valobj.IsSynthetic()); + if (valobj_sp && valobj_sp->GetCompilerType().IsValid()) { + if (!valobj_sp->GetCompilerType().IsMeaninglessWithoutDynamicResolution()) + return valobj_sp->GetQualifiedTypeName(); + } + return ConstString(); +} + +std::vector<lldb::LanguageType> +FormatManager::GetCandidateLanguages(lldb::LanguageType lang_type) { + switch (lang_type) { + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC89: + case lldb::eLanguageTypeC99: + case lldb::eLanguageTypeC11: + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeC_plus_plus_03: + case lldb::eLanguageTypeC_plus_plus_11: + case lldb::eLanguageTypeC_plus_plus_14: + return {lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeObjC}; + default: + return {lang_type}; + } + llvm_unreachable("Fully covered switch"); +} + +LanguageCategory * +FormatManager::GetCategoryForLanguage(lldb::LanguageType lang_type) { + std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex); + auto iter = m_language_categories_map.find(lang_type), + end = m_language_categories_map.end(); + if (iter != end) + return iter->second.get(); + LanguageCategory *lang_category = new LanguageCategory(lang_type); + m_language_categories_map[lang_type] = + LanguageCategory::UniquePointer(lang_category); + return lang_category; +} + +template <typename ImplSP> +ImplSP FormatManager::GetHardcoded(FormattersMatchData &match_data) { + ImplSP retval_sp; + for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) { + if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) { + if (lang_category->GetHardcoded(*this, match_data, retval_sp)) + return retval_sp; + } + } + return retval_sp; +} + +namespace { +template <typename ImplSP> const char *FormatterKind; +template <> const char *FormatterKind<lldb::TypeFormatImplSP> = "format"; +template <> const char *FormatterKind<lldb::TypeSummaryImplSP> = "summary"; +template <> const char *FormatterKind<lldb::SyntheticChildrenSP> = "synthetic"; +} // namespace + +#define FORMAT_LOG(Message) "[%s] " Message, FormatterKind<ImplSP> + +template <typename ImplSP> +ImplSP FormatManager::Get(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) { + FormattersMatchData match_data(valobj, use_dynamic); + if (ImplSP retval_sp = GetCached<ImplSP>(match_data)) + return retval_sp; + + Log *log = GetLog(LLDBLog::DataFormatters); + + LLDB_LOGF(log, FORMAT_LOG("Search failed. Giving language a chance.")); + for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) { + if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) { + ImplSP retval_sp; + if (lang_category->Get(match_data, retval_sp)) + if (retval_sp) { + LLDB_LOGF(log, FORMAT_LOG("Language search success. Returning.")); + return retval_sp; + } + } + } + + LLDB_LOGF(log, FORMAT_LOG("Search failed. Giving hardcoded a chance.")); + return GetHardcoded<ImplSP>(match_data); +} + +template <typename ImplSP> +ImplSP FormatManager::GetCached(FormattersMatchData &match_data) { + ImplSP retval_sp; + Log *log = GetLog(LLDBLog::DataFormatters); + if (match_data.GetTypeForCache()) { + LLDB_LOGF(log, "\n\n" FORMAT_LOG("Looking into cache for type %s"), + match_data.GetTypeForCache().AsCString("<invalid>")); + if (m_format_cache.Get(match_data.GetTypeForCache(), retval_sp)) { + if (log) { + LLDB_LOGF(log, FORMAT_LOG("Cache search success. Returning.")); + LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}", + m_format_cache.GetCacheHits(), + m_format_cache.GetCacheMisses()); + } + return retval_sp; + } + LLDB_LOGF(log, FORMAT_LOG("Cache search failed. Going normal route")); + } + + m_categories_map.Get(match_data, retval_sp); + if (match_data.GetTypeForCache() && (!retval_sp || !retval_sp->NonCacheable())) { + LLDB_LOGF(log, FORMAT_LOG("Caching %p for type %s"), + static_cast<void *>(retval_sp.get()), + match_data.GetTypeForCache().AsCString("<invalid>")); + m_format_cache.Set(match_data.GetTypeForCache(), retval_sp); + } + LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}", + m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses()); + return retval_sp; +} + +#undef FORMAT_LOG + +lldb::TypeFormatImplSP +FormatManager::GetFormat(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) { + return Get<lldb::TypeFormatImplSP>(valobj, use_dynamic); +} + +lldb::TypeSummaryImplSP +FormatManager::GetSummaryFormat(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) { + return Get<lldb::TypeSummaryImplSP>(valobj, use_dynamic); +} + +lldb::SyntheticChildrenSP +FormatManager::GetSyntheticChildren(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) { + return Get<lldb::SyntheticChildrenSP>(valobj, use_dynamic); +} + +FormatManager::FormatManager() + : m_last_revision(0), m_format_cache(), m_language_categories_mutex(), + m_language_categories_map(), m_named_summaries_map(this), + m_categories_map(this), m_default_category_name(ConstString("default")), + m_system_category_name(ConstString("system")), + m_vectortypes_category_name(ConstString("VectorTypes")) { + LoadSystemFormatters(); + LoadVectorFormatters(); + + EnableCategory(m_vectortypes_category_name, TypeCategoryMap::Last, + lldb::eLanguageTypeObjC_plus_plus); + EnableCategory(m_system_category_name, TypeCategoryMap::Last, + lldb::eLanguageTypeObjC_plus_plus); +} + +void FormatManager::LoadSystemFormatters() { + TypeSummaryImpl::Flags string_flags; + string_flags.SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + TypeSummaryImpl::Flags string_array_flags; + string_array_flags.SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + lldb::TypeSummaryImplSP string_format( + new StringSummaryFormat(string_flags, "${var%s}")); + + lldb::TypeSummaryImplSP string_array_format( + new StringSummaryFormat(string_array_flags, "${var%char[]}")); + + TypeCategoryImpl::SharedPointer sys_category_sp = + GetCategory(m_system_category_name); + + sys_category_sp->AddTypeSummary(R"(^(unsigned )?char ?(\*|\[\])$)", + eFormatterMatchRegex, string_format); + + sys_category_sp->AddTypeSummary(R"(^((un)?signed )?char ?\[[0-9]+\]$)", + eFormatterMatchRegex, string_array_format); + + lldb::TypeSummaryImplSP ostype_summary( + new StringSummaryFormat(TypeSummaryImpl::Flags() + .SetCascades(false) + .SetSkipPointers(true) + .SetSkipReferences(true) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false), + "${var%O}")); + + sys_category_sp->AddTypeSummary("OSType", eFormatterMatchExact, + ostype_summary); + + TypeFormatImpl::Flags fourchar_flags; + fourchar_flags.SetCascades(true).SetSkipPointers(true).SetSkipReferences( + true); + + AddFormat(sys_category_sp, lldb::eFormatOSType, "FourCharCode", + fourchar_flags); +} + +void FormatManager::LoadVectorFormatters() { + TypeCategoryImpl::SharedPointer vectors_category_sp = + GetCategory(m_vectortypes_category_name); + + TypeSummaryImpl::Flags vector_flags; + vector_flags.SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(true) + .SetHideItemNames(true); + + AddStringSummary(vectors_category_sp, "${var.uint128}", "builtin_type_vec128", + vector_flags); + AddStringSummary(vectors_category_sp, "", "float[4]", vector_flags); + AddStringSummary(vectors_category_sp, "", "int32_t[4]", vector_flags); + AddStringSummary(vectors_category_sp, "", "int16_t[8]", vector_flags); + AddStringSummary(vectors_category_sp, "", "vDouble", vector_flags); + AddStringSummary(vectors_category_sp, "", "vFloat", vector_flags); + AddStringSummary(vectors_category_sp, "", "vSInt8", vector_flags); + AddStringSummary(vectors_category_sp, "", "vSInt16", vector_flags); + AddStringSummary(vectors_category_sp, "", "vSInt32", vector_flags); + AddStringSummary(vectors_category_sp, "", "vUInt16", vector_flags); + AddStringSummary(vectors_category_sp, "", "vUInt8", vector_flags); + AddStringSummary(vectors_category_sp, "", "vUInt16", vector_flags); + AddStringSummary(vectors_category_sp, "", "vUInt32", vector_flags); + AddStringSummary(vectors_category_sp, "", "vBool32", vector_flags); +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/FormattersHelpers.cpp b/contrib/llvm-project/lldb/source/DataFormatters/FormattersHelpers.cpp new file mode 100644 index 000000000000..085ed3d0a2f2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/FormattersHelpers.cpp @@ -0,0 +1,128 @@ +//===-- FormattersHelpers.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/DataFormatters/FormattersHelpers.h" +#include "lldb/Core/Module.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/RegularExpression.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +void lldb_private::formatters::AddFormat( + TypeCategoryImpl::SharedPointer category_sp, lldb::Format format, + llvm::StringRef type_name, TypeFormatImpl::Flags flags, bool regex) { + lldb::TypeFormatImplSP format_sp(new TypeFormatImpl_Format(format, flags)); + + FormatterMatchType match_type = + regex ? eFormatterMatchRegex : eFormatterMatchExact; + category_sp->AddTypeFormat(type_name, match_type, format_sp); +} + +void lldb_private::formatters::AddSummary( + TypeCategoryImpl::SharedPointer category_sp, TypeSummaryImplSP summary_sp, + llvm::StringRef type_name, bool regex) { + FormatterMatchType match_type = + regex ? eFormatterMatchRegex : eFormatterMatchExact; + category_sp->AddTypeSummary(type_name, match_type, summary_sp); +} + +void lldb_private::formatters::AddStringSummary( + TypeCategoryImpl::SharedPointer category_sp, const char *string, + llvm::StringRef type_name, TypeSummaryImpl::Flags flags, bool regex) { + lldb::TypeSummaryImplSP summary_sp(new StringSummaryFormat(flags, string)); + + FormatterMatchType match_type = + regex ? eFormatterMatchRegex : eFormatterMatchExact; + category_sp->AddTypeSummary(type_name, match_type, summary_sp); +} + +void lldb_private::formatters::AddOneLineSummary( + TypeCategoryImpl::SharedPointer category_sp, llvm::StringRef type_name, + TypeSummaryImpl::Flags flags, bool regex) { + flags.SetShowMembersOneLiner(true); + lldb::TypeSummaryImplSP summary_sp(new StringSummaryFormat(flags, "")); + + FormatterMatchType match_type = + regex ? eFormatterMatchRegex : eFormatterMatchExact; + category_sp->AddTypeSummary(type_name, match_type, summary_sp); +} + +void lldb_private::formatters::AddCXXSummary( + TypeCategoryImpl::SharedPointer category_sp, + CXXFunctionSummaryFormat::Callback funct, const char *description, + llvm::StringRef type_name, TypeSummaryImpl::Flags flags, bool regex) { + lldb::TypeSummaryImplSP summary_sp( + new CXXFunctionSummaryFormat(flags, funct, description)); + + FormatterMatchType match_type = + regex ? eFormatterMatchRegex : eFormatterMatchExact; + category_sp->AddTypeSummary(type_name, match_type, summary_sp); +} + +void lldb_private::formatters::AddCXXSynthetic( + TypeCategoryImpl::SharedPointer category_sp, + CXXSyntheticChildren::CreateFrontEndCallback generator, + const char *description, llvm::StringRef type_name, + ScriptedSyntheticChildren::Flags flags, bool regex) { + lldb::SyntheticChildrenSP synth_sp( + new CXXSyntheticChildren(flags, description, generator)); + FormatterMatchType match_type = + regex ? eFormatterMatchRegex : eFormatterMatchExact; + category_sp->AddTypeSynthetic(type_name, match_type, synth_sp); +} + +void lldb_private::formatters::AddFilter( + TypeCategoryImpl::SharedPointer category_sp, + std::vector<std::string> children, const char *description, + llvm::StringRef type_name, ScriptedSyntheticChildren::Flags flags, + bool regex) { + TypeFilterImplSP filter_sp(new TypeFilterImpl(flags)); + for (auto child : children) + filter_sp->AddExpressionPath(child); + FormatterMatchType match_type = + regex ? eFormatterMatchRegex : eFormatterMatchExact; + category_sp->AddTypeFilter(type_name, match_type, filter_sp); +} + +size_t lldb_private::formatters::ExtractIndexFromString(const char *item_name) { + if (!item_name || !*item_name) + return UINT32_MAX; + if (*item_name != '[') + return UINT32_MAX; + item_name++; + char *endptr = nullptr; + unsigned long int idx = ::strtoul(item_name, &endptr, 0); + if (idx == 0 && endptr == item_name) + return UINT32_MAX; + if (idx == ULONG_MAX) + return UINT32_MAX; + return idx; +} + +Address +lldb_private::formatters::GetArrayAddressOrPointerValue(ValueObject &valobj) { + lldb::addr_t data_addr = LLDB_INVALID_ADDRESS; + AddressType type; + + if (valobj.IsPointerType()) + data_addr = valobj.GetPointerValue(&type); + else if (valobj.IsArrayType()) + data_addr = valobj.GetAddressOf(/*scalar_is_load_address=*/true, &type); + if (data_addr != LLDB_INVALID_ADDRESS && type == eAddressTypeFile) + return Address(data_addr, valobj.GetModule()->GetSectionList()); + + return data_addr; +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/LanguageCategory.cpp b/contrib/llvm-project/lldb/source/DataFormatters/LanguageCategory.cpp new file mode 100644 index 000000000000..4794186ce9ae --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/LanguageCategory.cpp @@ -0,0 +1,136 @@ +//===-- LanguageCategory.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/DataFormatters/LanguageCategory.h" + +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/DataFormatters/TypeCategory.h" +#include "lldb/DataFormatters/TypeFormat.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Target/Language.h" + +using namespace lldb; +using namespace lldb_private; + +LanguageCategory::LanguageCategory(lldb::LanguageType lang_type) + : m_category_sp(), m_hardcoded_formats(), m_hardcoded_summaries(), + m_hardcoded_synthetics(), m_format_cache(), m_enabled(false) { + if (Language *language_plugin = Language::FindPlugin(lang_type)) { + m_category_sp = language_plugin->GetFormatters(); + m_hardcoded_formats = language_plugin->GetHardcodedFormats(); + m_hardcoded_summaries = language_plugin->GetHardcodedSummaries(); + m_hardcoded_synthetics = language_plugin->GetHardcodedSynthetics(); + } + Enable(); +} + +template<typename ImplSP> +bool LanguageCategory::Get(FormattersMatchData &match_data, + ImplSP &retval_sp) { + if (!m_category_sp) + return false; + + if (!IsEnabled()) + return false; + + if (match_data.GetTypeForCache()) { + if (m_format_cache.Get(match_data.GetTypeForCache(), retval_sp)) + return (bool)retval_sp; + } + + ValueObject &valobj(match_data.GetValueObject()); + bool result = m_category_sp->Get(valobj.GetObjectRuntimeLanguage(), + match_data.GetMatchesVector(), retval_sp); + if (match_data.GetTypeForCache() && + (!retval_sp || !retval_sp->NonCacheable())) { + m_format_cache.Set(match_data.GetTypeForCache(), retval_sp); + } + return result; +} + +namespace lldb_private { + +/// Explicit instantiations for the three types. +/// \{ +template bool +LanguageCategory::Get<lldb::TypeFormatImplSP>(FormattersMatchData &, + lldb::TypeFormatImplSP &); +template bool +LanguageCategory::Get<lldb::TypeSummaryImplSP>(FormattersMatchData &, + lldb::TypeSummaryImplSP &); +template bool +LanguageCategory::Get<lldb::SyntheticChildrenSP>(FormattersMatchData &, + lldb::SyntheticChildrenSP &); +/// \} + +template <> +auto &LanguageCategory::GetHardcodedFinder<lldb::TypeFormatImplSP>() { + return m_hardcoded_formats; +} + +template <> +auto &LanguageCategory::GetHardcodedFinder<lldb::TypeSummaryImplSP>() { + return m_hardcoded_summaries; +} + +template <> +auto &LanguageCategory::GetHardcodedFinder<lldb::SyntheticChildrenSP>() { + return m_hardcoded_synthetics; +} + +} // namespace lldb_private + +template <typename ImplSP> +bool LanguageCategory::GetHardcoded(FormatManager &fmt_mgr, + FormattersMatchData &match_data, + ImplSP &retval_sp) { + if (!IsEnabled()) + return false; + + ValueObject &valobj(match_data.GetValueObject()); + lldb::DynamicValueType use_dynamic(match_data.GetDynamicValueType()); + + for (auto &candidate : GetHardcodedFinder<ImplSP>()) { + if (auto result = candidate(valobj, use_dynamic, fmt_mgr)) { + retval_sp = result; + break; + } + } + return (bool)retval_sp; +} + +/// Explicit instantiations for the three types. +/// \{ +template bool LanguageCategory::GetHardcoded<lldb::TypeFormatImplSP>( + FormatManager &, FormattersMatchData &, lldb::TypeFormatImplSP &); +template bool LanguageCategory::GetHardcoded<lldb::TypeSummaryImplSP>( + FormatManager &, FormattersMatchData &, lldb::TypeSummaryImplSP &); +template bool LanguageCategory::GetHardcoded<lldb::SyntheticChildrenSP>( + FormatManager &, FormattersMatchData &, lldb::SyntheticChildrenSP &); +/// \} + +lldb::TypeCategoryImplSP LanguageCategory::GetCategory() const { + return m_category_sp; +} + +FormatCache &LanguageCategory::GetFormatCache() { return m_format_cache; } + +void LanguageCategory::Enable() { + if (m_category_sp) + m_category_sp->Enable(true, TypeCategoryMap::Default); + m_enabled = true; +} + +void LanguageCategory::Disable() { + if (m_category_sp) + m_category_sp->Disable(); + m_enabled = false; +} + +bool LanguageCategory::IsEnabled() { return m_enabled; } diff --git a/contrib/llvm-project/lldb/source/DataFormatters/StringPrinter.cpp b/contrib/llvm-project/lldb/source/DataFormatters/StringPrinter.cpp new file mode 100644 index 000000000000..ab07c74fd185 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/StringPrinter.cpp @@ -0,0 +1,560 @@ +//===-- StringPrinter.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/DataFormatters/StringPrinter.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Status.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ConvertUTF.h" + +#include <cctype> +#include <locale> +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; +using GetPrintableElementType = StringPrinter::GetPrintableElementType; +using StringElementType = StringPrinter::StringElementType; + +/// DecodedCharBuffer stores the decoded contents of a single character. It +/// avoids managing memory on the heap by copying decoded bytes into an in-line +/// buffer. +class DecodedCharBuffer { +public: + DecodedCharBuffer(std::nullptr_t) {} + + DecodedCharBuffer(const uint8_t *bytes, size_t size) : m_size(size) { + if (size > MaxLength) + llvm_unreachable("unsupported length"); + memcpy(m_data, bytes, size); + } + + DecodedCharBuffer(const char *bytes, size_t size) + : DecodedCharBuffer(reinterpret_cast<const uint8_t *>(bytes), size) {} + + const uint8_t *GetBytes() const { return m_data; } + + size_t GetSize() const { return m_size; } + +private: + static constexpr unsigned MaxLength = 16; + + size_t m_size = 0; + uint8_t m_data[MaxLength] = {0}; +}; + +using EscapingHelper = + std::function<DecodedCharBuffer(uint8_t *, uint8_t *, uint8_t *&)>; + +// we define this for all values of type but only implement it for those we +// care about that's good because we get linker errors for any unsupported type +template <StringElementType type> +static DecodedCharBuffer +GetPrintableImpl(uint8_t *buffer, uint8_t *buffer_end, uint8_t *&next, + StringPrinter::EscapeStyle escape_style); + +// Mimic isprint() for Unicode codepoints. +static bool isprint32(char32_t codepoint) { + if (codepoint <= 0x1F || codepoint == 0x7F) // C0 + { + return false; + } + if (codepoint >= 0x80 && codepoint <= 0x9F) // C1 + { + return false; + } + if (codepoint == 0x2028 || codepoint == 0x2029) // line/paragraph separators + { + return false; + } + if (codepoint == 0x200E || codepoint == 0x200F || + (codepoint >= 0x202A && + codepoint <= 0x202E)) // bidirectional text control + { + return false; + } + if (codepoint >= 0xFFF9 && + codepoint <= 0xFFFF) // interlinears and generally specials + { + return false; + } + return true; +} + +DecodedCharBuffer attemptASCIIEscape(llvm::UTF32 c, + StringPrinter::EscapeStyle escape_style) { + const bool is_swift_escape_style = + escape_style == StringPrinter::EscapeStyle::Swift; + switch (c) { + case 0: + return {"\\0", 2}; + case '\a': + return {"\\a", 2}; + case '\b': + if (is_swift_escape_style) + return nullptr; + return {"\\b", 2}; + case '\f': + if (is_swift_escape_style) + return nullptr; + return {"\\f", 2}; + case '\n': + return {"\\n", 2}; + case '\r': + return {"\\r", 2}; + case '\t': + return {"\\t", 2}; + case '\v': + if (is_swift_escape_style) + return nullptr; + return {"\\v", 2}; + case '\"': + return {"\\\"", 2}; + case '\'': + if (is_swift_escape_style) + return {"\\'", 2}; + return nullptr; + case '\\': + return {"\\\\", 2}; + } + return nullptr; +} + +template <> +DecodedCharBuffer GetPrintableImpl<StringElementType::ASCII>( + uint8_t *buffer, uint8_t *buffer_end, uint8_t *&next, + StringPrinter::EscapeStyle escape_style) { + // The ASCII helper always advances 1 byte at a time. + next = buffer + 1; + + DecodedCharBuffer retval = attemptASCIIEscape(*buffer, escape_style); + if (retval.GetSize()) + return retval; + + // Use llvm's locale-independent isPrint(char), instead of the libc + // implementation which may give different results on different platforms. + if (llvm::isPrint(*buffer)) + return {buffer, 1}; + + unsigned escaped_len; + constexpr unsigned max_buffer_size = 7; + uint8_t data[max_buffer_size]; + switch (escape_style) { + case StringPrinter::EscapeStyle::CXX: + // Prints 4 characters, then a \0 terminator. + escaped_len = snprintf((char *)data, max_buffer_size, "\\x%02x", *buffer); + break; + case StringPrinter::EscapeStyle::Swift: + // Prints up to 6 characters, then a \0 terminator. + escaped_len = snprintf((char *)data, max_buffer_size, "\\u{%x}", *buffer); + break; + } + lldbassert(escaped_len > 0 && "unknown string escape style"); + return {data, escaped_len}; +} + +template <> +DecodedCharBuffer GetPrintableImpl<StringElementType::UTF8>( + uint8_t *buffer, uint8_t *buffer_end, uint8_t *&next, + StringPrinter::EscapeStyle escape_style) { + // If the utf8 encoded length is invalid (i.e., not in the closed interval + // [1;4]), or if there aren't enough bytes to print, or if the subsequence + // isn't valid utf8, fall back to printing an ASCII-escaped subsequence. + if (!llvm::isLegalUTF8Sequence(buffer, buffer_end)) + return GetPrintableImpl<StringElementType::ASCII>(buffer, buffer_end, next, + escape_style); + + // Convert the valid utf8 sequence to a utf32 codepoint. This cannot fail. + llvm::UTF32 codepoint = 0; + const llvm::UTF8 *buffer_for_conversion = buffer; + llvm::ConversionResult result = llvm::convertUTF8Sequence( + &buffer_for_conversion, buffer_end, &codepoint, llvm::strictConversion); + assert(result == llvm::conversionOK && + "Failed to convert legal utf8 sequence"); + UNUSED_IF_ASSERT_DISABLED(result); + + // The UTF8 helper always advances by the utf8 encoded length. + const unsigned utf8_encoded_len = buffer_for_conversion - buffer; + next = buffer + utf8_encoded_len; + + DecodedCharBuffer retval = attemptASCIIEscape(codepoint, escape_style); + if (retval.GetSize()) + return retval; + if (isprint32(codepoint)) + return {buffer, utf8_encoded_len}; + + unsigned escaped_len; + constexpr unsigned max_buffer_size = 13; + uint8_t data[max_buffer_size]; + switch (escape_style) { + case StringPrinter::EscapeStyle::CXX: + // Prints 10 characters, then a \0 terminator. + escaped_len = snprintf((char *)data, max_buffer_size, "\\U%08x", codepoint); + break; + case StringPrinter::EscapeStyle::Swift: + // Prints up to 12 characters, then a \0 terminator. + escaped_len = snprintf((char *)data, max_buffer_size, "\\u{%x}", codepoint); + break; + } + lldbassert(escaped_len > 0 && "unknown string escape style"); + return {data, escaped_len}; +} + +// Given a sequence of bytes, this function returns: a sequence of bytes to +// actually print out + a length the following unscanned position of the buffer +// is in next +static DecodedCharBuffer GetPrintable(StringElementType type, uint8_t *buffer, + uint8_t *buffer_end, uint8_t *&next, + StringPrinter::EscapeStyle escape_style) { + if (!buffer || buffer >= buffer_end) + return {nullptr}; + + switch (type) { + case StringElementType::ASCII: + return GetPrintableImpl<StringElementType::ASCII>(buffer, buffer_end, next, + escape_style); + case StringElementType::UTF8: + return GetPrintableImpl<StringElementType::UTF8>(buffer, buffer_end, next, + escape_style); + default: + return {nullptr}; + } +} + +static EscapingHelper +GetDefaultEscapingHelper(GetPrintableElementType elem_type, + StringPrinter::EscapeStyle escape_style) { + switch (elem_type) { + case GetPrintableElementType::UTF8: + case GetPrintableElementType::ASCII: + return [escape_style, elem_type](uint8_t *buffer, uint8_t *buffer_end, + uint8_t *&next) -> DecodedCharBuffer { + return GetPrintable(elem_type == GetPrintableElementType::UTF8 + ? StringElementType::UTF8 + : StringElementType::ASCII, + buffer, buffer_end, next, escape_style); + }; + } + llvm_unreachable("bad element type"); +} + +/// Read a string encoded in accordance with \tparam SourceDataType from a +/// host-side LLDB buffer, then pretty-print it to a stream using \p style. +template <typename SourceDataType> +static bool DumpEncodedBufferToStream( + GetPrintableElementType style, + llvm::ConversionResult (*ConvertFunction)(const SourceDataType **, + const SourceDataType *, + llvm::UTF8 **, llvm::UTF8 *, + llvm::ConversionFlags), + const StringPrinter::ReadBufferAndDumpToStreamOptions &dump_options) { + assert(dump_options.GetStream() && "need a Stream to print the string to"); + Stream &stream(*dump_options.GetStream()); + if (dump_options.GetPrefixToken() != nullptr) + stream.Printf("%s", dump_options.GetPrefixToken()); + if (dump_options.GetQuote() != 0) + stream.Printf("%c", dump_options.GetQuote()); + auto data(dump_options.GetData()); + auto source_size(dump_options.GetSourceSize()); + if (data.GetByteSize() && data.GetDataStart() && data.GetDataEnd()) { + const int bufferSPSize = data.GetByteSize(); + if (dump_options.GetSourceSize() == 0) { + const int origin_encoding = 8 * sizeof(SourceDataType); + source_size = bufferSPSize / (origin_encoding / 4); + } + + const SourceDataType *data_ptr = + (const SourceDataType *)data.GetDataStart(); + const SourceDataType *data_end_ptr = data_ptr + source_size; + + const bool zero_is_terminator = dump_options.GetBinaryZeroIsTerminator(); + + if (zero_is_terminator) { + while (data_ptr < data_end_ptr) { + if (!*data_ptr) { + data_end_ptr = data_ptr; + break; + } + data_ptr++; + } + + data_ptr = (const SourceDataType *)data.GetDataStart(); + } + + lldb::WritableDataBufferSP utf8_data_buffer_sp; + llvm::UTF8 *utf8_data_ptr = nullptr; + llvm::UTF8 *utf8_data_end_ptr = nullptr; + + if (ConvertFunction) { + utf8_data_buffer_sp = + std::make_shared<DataBufferHeap>(4 * bufferSPSize, 0); + utf8_data_ptr = (llvm::UTF8 *)utf8_data_buffer_sp->GetBytes(); + utf8_data_end_ptr = utf8_data_ptr + utf8_data_buffer_sp->GetByteSize(); + ConvertFunction(&data_ptr, data_end_ptr, &utf8_data_ptr, + utf8_data_end_ptr, llvm::lenientConversion); + if (!zero_is_terminator) + utf8_data_end_ptr = utf8_data_ptr; + // needed because the ConvertFunction will change the value of the + // data_ptr. + utf8_data_ptr = + (llvm::UTF8 *)utf8_data_buffer_sp->GetBytes(); + } else { + // just copy the pointers - the cast is necessary to make the compiler + // happy but this should only happen if we are reading UTF8 data + utf8_data_ptr = const_cast<llvm::UTF8 *>( + reinterpret_cast<const llvm::UTF8 *>(data_ptr)); + utf8_data_end_ptr = const_cast<llvm::UTF8 *>( + reinterpret_cast<const llvm::UTF8 *>(data_end_ptr)); + } + + const bool escape_non_printables = dump_options.GetEscapeNonPrintables(); + EscapingHelper escaping_callback; + if (escape_non_printables) + escaping_callback = + GetDefaultEscapingHelper(style, dump_options.GetEscapeStyle()); + + // since we tend to accept partial data (and even partially malformed data) + // we might end up with no NULL terminator before the end_ptr hence we need + // to take a slower route and ensure we stay within boundaries + for (; utf8_data_ptr < utf8_data_end_ptr;) { + if (zero_is_terminator && !*utf8_data_ptr) + break; + + if (escape_non_printables) { + uint8_t *next_data = nullptr; + auto printable = + escaping_callback(utf8_data_ptr, utf8_data_end_ptr, next_data); + auto printable_bytes = printable.GetBytes(); + auto printable_size = printable.GetSize(); + + // We failed to figure out how to print this string. + if (!printable_bytes || !next_data) + return false; + + for (unsigned c = 0; c < printable_size; c++) + stream.Printf("%c", *(printable_bytes + c)); + utf8_data_ptr = (uint8_t *)next_data; + } else { + stream.Printf("%c", *utf8_data_ptr); + utf8_data_ptr++; + } + } + } + if (dump_options.GetQuote() != 0) + stream.Printf("%c", dump_options.GetQuote()); + if (dump_options.GetSuffixToken() != nullptr) + stream.Printf("%s", dump_options.GetSuffixToken()); + if (dump_options.GetIsTruncated()) + stream.Printf("..."); + return true; +} + +lldb_private::formatters::StringPrinter::ReadStringAndDumpToStreamOptions:: + ReadStringAndDumpToStreamOptions(ValueObject &valobj) + : ReadStringAndDumpToStreamOptions() { + SetEscapeNonPrintables( + valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables()); +} + +lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions:: + ReadBufferAndDumpToStreamOptions(ValueObject &valobj) + : ReadBufferAndDumpToStreamOptions() { + SetEscapeNonPrintables( + valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables()); +} + +lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions:: + ReadBufferAndDumpToStreamOptions( + const ReadStringAndDumpToStreamOptions &options) + : ReadBufferAndDumpToStreamOptions() { + SetStream(options.GetStream()); + SetPrefixToken(options.GetPrefixToken()); + SetSuffixToken(options.GetSuffixToken()); + SetQuote(options.GetQuote()); + SetEscapeNonPrintables(options.GetEscapeNonPrintables()); + SetBinaryZeroIsTerminator(options.GetBinaryZeroIsTerminator()); + SetEscapeStyle(options.GetEscapeStyle()); +} + +namespace lldb_private { + +namespace formatters { + +template <typename SourceDataType> +static bool ReadEncodedBufferAndDumpToStream( + StringElementType elem_type, + const StringPrinter::ReadStringAndDumpToStreamOptions &options, + llvm::ConversionResult (*ConvertFunction)(const SourceDataType **, + const SourceDataType *, + llvm::UTF8 **, llvm::UTF8 *, + llvm::ConversionFlags)) { + assert(options.GetStream() && "need a Stream to print the string to"); + if (!options.GetStream()) + return false; + + if (options.GetLocation() == 0 || + options.GetLocation() == LLDB_INVALID_ADDRESS) + return false; + + lldb::TargetSP target_sp = options.GetTargetSP(); + if (!target_sp) + return false; + + constexpr int type_width = sizeof(SourceDataType); + constexpr int origin_encoding = 8 * type_width; + if (origin_encoding != 8 && origin_encoding != 16 && origin_encoding != 32) + return false; + // If not UTF8 or ASCII, conversion to UTF8 is necessary. + if (origin_encoding != 8 && !ConvertFunction) + return false; + + bool needs_zero_terminator = options.GetNeedsZeroTermination(); + + bool is_truncated = false; + const auto max_size = target_sp->GetMaximumSizeOfStringSummary(); + + uint32_t sourceSize; + if (elem_type == StringElementType::ASCII && !options.GetSourceSize()) { + // FIXME: The NSString formatter sets HasSourceSize(true) when the size is + // actually unknown, as well as SetBinaryZeroIsTerminator(false). IIUC the + // C++ formatter also sets SetBinaryZeroIsTerminator(false) when it doesn't + // mean to. I don't see how this makes sense: we should fix the formatters. + // + // Until then, the behavior that's expected for ASCII strings with unknown + // lengths is to read up to the max size and then null-terminate. Do that. + sourceSize = max_size; + needs_zero_terminator = true; + } else if (options.HasSourceSize()) { + sourceSize = options.GetSourceSize(); + if (!options.GetIgnoreMaxLength()) { + if (sourceSize > max_size) { + sourceSize = max_size; + is_truncated = true; + } + } + } else { + sourceSize = max_size; + needs_zero_terminator = true; + } + + const int bufferSPSize = sourceSize * type_width; + lldb::WritableDataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize, 0)); + + // Check if we got bytes. We never get any bytes if we have an empty + // string, but we still continue so that we end up actually printing + // an empty string (""). + if (sourceSize != 0 && !buffer_sp->GetBytes()) + return false; + + Status error; + char *buffer = reinterpret_cast<char *>(buffer_sp->GetBytes()); + + if (elem_type == StringElementType::ASCII) + target_sp->ReadCStringFromMemory(options.GetLocation(), buffer, + bufferSPSize, error); + else if (needs_zero_terminator) + target_sp->ReadStringFromMemory(options.GetLocation(), buffer, + bufferSPSize, error, type_width); + else + target_sp->ReadMemory(options.GetLocation(), buffer, bufferSPSize, error); + if (error.Fail()) { + options.GetStream()->Printf("unable to read data"); + return true; + } + + StringPrinter::ReadBufferAndDumpToStreamOptions dump_options(options); + dump_options.SetData( + DataExtractor(buffer_sp, target_sp->GetArchitecture().GetByteOrder(), + target_sp->GetArchitecture().GetAddressByteSize())); + dump_options.SetSourceSize(sourceSize); + dump_options.SetIsTruncated(is_truncated); + dump_options.SetNeedsZeroTermination(needs_zero_terminator); + if (needs_zero_terminator) + dump_options.SetBinaryZeroIsTerminator(true); + + GetPrintableElementType print_style = (elem_type == StringElementType::ASCII) + ? GetPrintableElementType::ASCII + : GetPrintableElementType::UTF8; + return DumpEncodedBufferToStream(print_style, ConvertFunction, dump_options); +} + +template <> +bool StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF8>( + const ReadStringAndDumpToStreamOptions &options) { + return ReadEncodedBufferAndDumpToStream<llvm::UTF8>(StringElementType::UTF8, + options, nullptr); +} + +template <> +bool StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF16>( + const ReadStringAndDumpToStreamOptions &options) { + return ReadEncodedBufferAndDumpToStream<llvm::UTF16>( + StringElementType::UTF16, options, llvm::ConvertUTF16toUTF8); +} + +template <> +bool StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF32>( + const ReadStringAndDumpToStreamOptions &options) { + return ReadEncodedBufferAndDumpToStream<llvm::UTF32>( + StringElementType::UTF32, options, llvm::ConvertUTF32toUTF8); +} + +template <> +bool StringPrinter::ReadStringAndDumpToStream<StringElementType::ASCII>( + const ReadStringAndDumpToStreamOptions &options) { + return ReadEncodedBufferAndDumpToStream<char>(StringElementType::ASCII, + options, nullptr); +} + +template <> +bool StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF8>( + const ReadBufferAndDumpToStreamOptions &options) { + return DumpEncodedBufferToStream<llvm::UTF8>(GetPrintableElementType::UTF8, + nullptr, options); +} + +template <> +bool StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF16>( + const ReadBufferAndDumpToStreamOptions &options) { + return DumpEncodedBufferToStream(GetPrintableElementType::UTF8, + llvm::ConvertUTF16toUTF8, options); +} + +template <> +bool StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF32>( + const ReadBufferAndDumpToStreamOptions &options) { + return DumpEncodedBufferToStream(GetPrintableElementType::UTF8, + llvm::ConvertUTF32toUTF8, options); +} + +template <> +bool StringPrinter::ReadBufferAndDumpToStream<StringElementType::ASCII>( + const ReadBufferAndDumpToStreamOptions &options) { + // Treat ASCII the same as UTF8. + // + // FIXME: This is probably not the right thing to do (well, it's debatable). + // If an ASCII-encoded string happens to contain a sequence of invalid bytes + // that forms a valid UTF8 character, we'll print out that character. This is + // good if you're playing fast and loose with encodings (probably good for + // std::string users), but maybe not so good if you care about your string + // formatter respecting the semantics of your selected string encoding. In + // the latter case you'd want to see the character byte sequence ('\x..'), not + // the UTF8 character itself. + return ReadBufferAndDumpToStream<StringElementType::UTF8>(options); +} + +} // namespace formatters + +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeCategory.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeCategory.cpp new file mode 100644 index 000000000000..4d8663ea9c03 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeCategory.cpp @@ -0,0 +1,334 @@ +//===-- TypeCategory.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/DataFormatters/TypeCategory.h" +#include "lldb/Target/Language.h" + + +using namespace lldb; +using namespace lldb_private; + +TypeCategoryImpl::TypeCategoryImpl(IFormatChangeListener *clist, + ConstString name) + : m_format_cont(clist), m_summary_cont(clist), m_filter_cont(clist), + m_synth_cont(clist), m_enabled(false), m_change_listener(clist), + m_mutex(), m_name(name), m_languages() {} + +static bool IsApplicable(lldb::LanguageType category_lang, + lldb::LanguageType valobj_lang) { + switch (category_lang) { + // Unless we know better, allow only exact equality. + default: + return category_lang == valobj_lang; + + // the C family, we consider it as one + case eLanguageTypeC89: + case eLanguageTypeC: + case eLanguageTypeC99: + return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC || + valobj_lang == eLanguageTypeC99; + + // ObjC knows about C and itself + case eLanguageTypeObjC: + return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC || + valobj_lang == eLanguageTypeC99 || valobj_lang == eLanguageTypeObjC; + + // C++ knows about C and C++ + case eLanguageTypeC_plus_plus: + return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC || + valobj_lang == eLanguageTypeC99 || + valobj_lang == eLanguageTypeC_plus_plus; + + // ObjC++ knows about C,C++,ObjC and ObjC++ + case eLanguageTypeObjC_plus_plus: + return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC || + valobj_lang == eLanguageTypeC99 || + valobj_lang == eLanguageTypeC_plus_plus || + valobj_lang == eLanguageTypeObjC; + + // Categories with unspecified language match everything. + case eLanguageTypeUnknown: + return true; + } +} + +bool TypeCategoryImpl::IsApplicable(lldb::LanguageType lang) { + for (size_t idx = 0; idx < GetNumLanguages(); idx++) { + const lldb::LanguageType category_lang = GetLanguageAtIndex(idx); + if (::IsApplicable(category_lang, lang)) + return true; + } + return false; +} + +size_t TypeCategoryImpl::GetNumLanguages() { + if (m_languages.empty()) + return 1; + return m_languages.size(); +} + +lldb::LanguageType TypeCategoryImpl::GetLanguageAtIndex(size_t idx) { + if (m_languages.empty()) + return lldb::eLanguageTypeUnknown; + return m_languages[idx]; +} + +void TypeCategoryImpl::AddLanguage(lldb::LanguageType lang) { + m_languages.push_back(lang); +} + +bool TypeCategoryImpl::Get(lldb::LanguageType lang, + const FormattersMatchVector &candidates, + lldb::TypeFormatImplSP &entry) { + if (!IsEnabled() || !IsApplicable(lang)) + return false; + return m_format_cont.Get(candidates, entry); +} + +bool TypeCategoryImpl::Get(lldb::LanguageType lang, + const FormattersMatchVector &candidates, + lldb::TypeSummaryImplSP &entry) { + if (!IsEnabled() || !IsApplicable(lang)) + return false; + return m_summary_cont.Get(candidates, entry); +} + +bool TypeCategoryImpl::Get(lldb::LanguageType lang, + const FormattersMatchVector &candidates, + lldb::SyntheticChildrenSP &entry) { + if (!IsEnabled() || !IsApplicable(lang)) + return false; + + // first find both Filter and Synth, and then check which is most recent + bool pick_synth = false; + + TypeFilterImpl::SharedPointer filter_sp; + m_filter_cont.Get(candidates, filter_sp); + + ScriptedSyntheticChildren::SharedPointer synth_sp; + m_synth_cont.Get(candidates, synth_sp); + + if (!filter_sp.get() && !synth_sp.get()) + return false; + else if (!filter_sp.get() && synth_sp.get()) + pick_synth = true; + else if (filter_sp.get() && !synth_sp.get()) + pick_synth = false; + else /*if (filter_sp.get() && synth_sp.get())*/ + { + pick_synth = filter_sp->GetRevision() <= synth_sp->GetRevision(); + } + + if (pick_synth) { + entry = synth_sp; + return true; + } else { + entry = filter_sp; + return true; + } + return false; +} + +void TypeCategoryImpl::Clear(FormatCategoryItems items) { + if (items & eFormatCategoryItemFormat) + m_format_cont.Clear(); + + if (items & eFormatCategoryItemSummary) + m_summary_cont.Clear(); + + if (items & eFormatCategoryItemFilter) + m_filter_cont.Clear(); + + if (items & eFormatCategoryItemSynth) + m_synth_cont.Clear(); +} + +bool TypeCategoryImpl::Delete(ConstString name, FormatCategoryItems items) { + bool success = false; + + if (items & eFormatCategoryItemFormat) + success = m_format_cont.Delete(name) || success; + + if (items & eFormatCategoryItemSummary) + success = m_summary_cont.Delete(name) || success; + + if (items & eFormatCategoryItemFilter) + success = m_filter_cont.Delete(name) || success; + + if (items & eFormatCategoryItemSynth) + success = m_synth_cont.Delete(name) || success; + + return success; +} + +uint32_t TypeCategoryImpl::GetCount(FormatCategoryItems items) { + uint32_t count = 0; + + if (items & eFormatCategoryItemFormat) + count += m_format_cont.GetCount(); + + if (items & eFormatCategoryItemSummary) + count += m_summary_cont.GetCount(); + + if (items & eFormatCategoryItemFilter) + count += m_filter_cont.GetCount(); + + if (items & eFormatCategoryItemSynth) + count += m_synth_cont.GetCount(); + + return count; +} + +bool TypeCategoryImpl::AnyMatches( + const FormattersMatchCandidate &candidate_type, FormatCategoryItems items, + bool only_enabled, const char **matching_category, + FormatCategoryItems *matching_type) { + if (!IsEnabled() && only_enabled) + return false; + + if (items & eFormatCategoryItemFormat) { + if (m_format_cont.AnyMatches(candidate_type)) { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemFormat; + return true; + } + } + + if (items & eFormatCategoryItemSummary) { + if (m_summary_cont.AnyMatches(candidate_type)) { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemSummary; + return true; + } + } + + if (items & eFormatCategoryItemFilter) { + if (m_filter_cont.AnyMatches(candidate_type)) { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemFilter; + return true; + } + } + + if (items & eFormatCategoryItemSynth) { + if (m_synth_cont.AnyMatches(candidate_type)) { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemSynth; + return true; + } + } + + return false; +} + +void TypeCategoryImpl::AutoComplete(CompletionRequest &request, + FormatCategoryItems items) { + if (items & eFormatCategoryItemFormat) + m_format_cont.AutoComplete(request); + if (items & eFormatCategoryItemSummary) + m_summary_cont.AutoComplete(request); + if (items & eFormatCategoryItemFilter) + m_filter_cont.AutoComplete(request); + if (items & eFormatCategoryItemSynth) + m_synth_cont.AutoComplete(request); +} + +TypeCategoryImpl::FormatContainer::MapValueType +TypeCategoryImpl::GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp) { + return m_format_cont.GetForTypeNameSpecifier(type_sp); +} + +TypeCategoryImpl::SummaryContainer::MapValueType +TypeCategoryImpl::GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp) { + return m_summary_cont.GetForTypeNameSpecifier(type_sp); +} + +TypeCategoryImpl::FilterContainer::MapValueType +TypeCategoryImpl::GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp) { + return m_filter_cont.GetForTypeNameSpecifier(type_sp); +} + +TypeCategoryImpl::SynthContainer::MapValueType +TypeCategoryImpl::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) { + return m_synth_cont.GetForTypeNameSpecifier(type_sp); +} + +TypeCategoryImpl::FormatContainer::MapValueType +TypeCategoryImpl::GetFormatAtIndex(size_t index) { + return m_format_cont.GetAtIndex(index); +} + +TypeCategoryImpl::SummaryContainer::MapValueType +TypeCategoryImpl::GetSummaryAtIndex(size_t index) { + return m_summary_cont.GetAtIndex(index); +} + +TypeCategoryImpl::FilterContainer::MapValueType +TypeCategoryImpl::GetFilterAtIndex(size_t index) { + return m_filter_cont.GetAtIndex(index); +} + +TypeCategoryImpl::SynthContainer::MapValueType +TypeCategoryImpl::GetSyntheticAtIndex(size_t index) { + return m_synth_cont.GetAtIndex(index); +} + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForFormatAtIndex(size_t index) { + return m_format_cont.GetTypeNameSpecifierAtIndex(index); +} + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForSummaryAtIndex(size_t index) { + return m_summary_cont.GetTypeNameSpecifierAtIndex(index); +} + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForFilterAtIndex(size_t index) { + return m_filter_cont.GetTypeNameSpecifierAtIndex(index); +} + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForSyntheticAtIndex(size_t index) { + return m_synth_cont.GetTypeNameSpecifierAtIndex(index); +} + +void TypeCategoryImpl::Enable(bool value, uint32_t position) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if ((m_enabled = value)) + m_enabled_position = position; + if (m_change_listener) + m_change_listener->Changed(); +} + +std::string TypeCategoryImpl::GetDescription() { + StreamString stream; + stream.Printf("%s (%s", GetName(), (IsEnabled() ? "enabled" : "disabled")); + StreamString lang_stream; + lang_stream.Printf(", applicable for language(s): "); + bool print_lang = false; + for (size_t idx = 0; idx < GetNumLanguages(); idx++) { + const lldb::LanguageType lang = GetLanguageAtIndex(idx); + if (lang != lldb::eLanguageTypeUnknown) + print_lang = true; + lang_stream.Printf("%s%s", Language::GetNameForLanguageType(lang), + idx + 1 < GetNumLanguages() ? ", " : ""); + } + if (print_lang) + stream.PutCString(lang_stream.GetString()); + stream.PutChar(')'); + return std::string(stream.GetString()); +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeCategoryMap.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeCategoryMap.cpp new file mode 100644 index 000000000000..ce2cf369b5be --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeCategoryMap.cpp @@ -0,0 +1,265 @@ +//===-- TypeCategoryMap.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/DataFormatters/TypeCategoryMap.h" + +#include "lldb/DataFormatters/FormatClasses.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; + +TypeCategoryMap::TypeCategoryMap(IFormatChangeListener *lst) + : m_map_mutex(), listener(lst), m_map(), m_active_categories() { + ConstString default_cs("default"); + lldb::TypeCategoryImplSP default_sp = + lldb::TypeCategoryImplSP(new TypeCategoryImpl(listener, default_cs)); + Add(default_cs, default_sp); + Enable(default_cs, First); +} + +void TypeCategoryMap::Add(KeyType name, const TypeCategoryImplSP &entry) { + { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + m_map[name] = entry; + } + // Release the mutex to avoid a potential deadlock between + // TypeCategoryMap::m_map_mutex and + // FormatManager::m_language_categories_mutex which can be acquired in + // reverse order when calling FormatManager::Changed. + if (listener) + listener->Changed(); +} + +bool TypeCategoryMap::Delete(KeyType name) { + { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + MapIterator iter = m_map.find(name); + if (iter == m_map.end()) + return false; + m_map.erase(name); + Disable(name); + } + // Release the mutex to avoid a potential deadlock between + // TypeCategoryMap::m_map_mutex and + // FormatManager::m_language_categories_mutex which can be acquired in + // reverse order when calling FormatManager::Changed. + if (listener) + listener->Changed(); + return true; +} + +bool TypeCategoryMap::Enable(KeyType category_name, Position pos) { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + TypeCategoryImplSP category; + if (!Get(category_name, category)) + return false; + return Enable(category, pos); +} + +bool TypeCategoryMap::Disable(KeyType category_name) { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + TypeCategoryImplSP category; + if (!Get(category_name, category)) + return false; + return Disable(category); +} + +bool TypeCategoryMap::Enable(TypeCategoryImplSP category, Position pos) { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + if (category.get()) { + Position pos_w = pos; + if (pos == First || m_active_categories.size() == 0) + m_active_categories.push_front(category); + else if (pos == Last || pos == m_active_categories.size()) + m_active_categories.push_back(category); + else if (pos < m_active_categories.size()) { + ActiveCategoriesList::iterator iter = m_active_categories.begin(); + while (pos_w) { + pos_w--, iter++; + } + m_active_categories.insert(iter, category); + } else + return false; + category->Enable(true, pos); + return true; + } + return false; +} + +bool TypeCategoryMap::Disable(TypeCategoryImplSP category) { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + if (category.get()) { + m_active_categories.remove_if(delete_matching_categories(category)); + category->Disable(); + return true; + } + return false; +} + +void TypeCategoryMap::EnableAllCategories() { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + std::vector<TypeCategoryImplSP> sorted_categories(m_map.size(), TypeCategoryImplSP()); + MapType::iterator iter = m_map.begin(), end = m_map.end(); + for (; iter != end; ++iter) { + if (iter->second->IsEnabled()) + continue; + auto pos = iter->second->GetLastEnabledPosition(); + if (pos >= sorted_categories.size()) { + auto iter = std::find_if( + sorted_categories.begin(), sorted_categories.end(), + [](const TypeCategoryImplSP &sp) -> bool { return sp.get() == nullptr; }); + pos = std::distance(sorted_categories.begin(), iter); + } + sorted_categories.at(pos) = iter->second; + } + decltype(sorted_categories)::iterator viter = sorted_categories.begin(), + vend = sorted_categories.end(); + for (; viter != vend; viter++) + if (*viter) + Enable(*viter, Last); +} + +void TypeCategoryMap::DisableAllCategories() { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + for (Position p = First; !m_active_categories.empty(); p++) { + m_active_categories.front()->SetEnabledPosition(p); + Disable(m_active_categories.front()); + } +} + +void TypeCategoryMap::Clear() { + { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + m_map.clear(); + m_active_categories.clear(); + } + // Release the mutex to avoid a potential deadlock between + // TypeCategoryMap::m_map_mutex and + // FormatManager::m_language_categories_mutex which can be acquired in + // reverse order when calling FormatManager::Changed. + if (listener) + listener->Changed(); +} + +bool TypeCategoryMap::Get(KeyType name, TypeCategoryImplSP &entry) { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + MapIterator iter = m_map.find(name); + if (iter == m_map.end()) + return false; + entry = iter->second; + return true; +} + +bool TypeCategoryMap::AnyMatches( + const FormattersMatchCandidate &candidate_type, + TypeCategoryImpl::FormatCategoryItems items, bool only_enabled, + const char **matching_category, + TypeCategoryImpl::FormatCategoryItems *matching_type) { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + + MapIterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; pos++) { + if (pos->second->AnyMatches(candidate_type, items, only_enabled, + matching_category, matching_type)) + return true; + } + return false; +} + +template <typename ImplSP> +void TypeCategoryMap::Get(FormattersMatchData &match_data, ImplSP &retval) { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + + ActiveCategoriesIterator begin, end = m_active_categories.end(); + + Log *log = GetLog(LLDBLog::DataFormatters); + + if (log) { + for (auto match : match_data.GetMatchesVector()) { + LLDB_LOGF( + log, + "[%s] candidate match = %s %s %s %s", + __FUNCTION__, + match.GetTypeName().GetCString(), + match.DidStripPointer() ? "strip-pointers" : "no-strip-pointers", + match.DidStripReference() ? "strip-reference" : "no-strip-reference", + match.DidStripTypedef() ? "strip-typedef" : "no-strip-typedef"); + } + } + + for (begin = m_active_categories.begin(); begin != end; begin++) { + lldb::TypeCategoryImplSP category_sp = *begin; + ImplSP current_format; + LLDB_LOGF(log, "[%s] Trying to use category %s", __FUNCTION__, + category_sp->GetName()); + if (!category_sp->Get( + match_data.GetValueObject().GetObjectRuntimeLanguage(), + match_data.GetMatchesVector(), current_format)) + continue; + + retval = std::move(current_format); + return; + } + LLDB_LOGF(log, "[%s] nothing found - returning empty SP", __FUNCTION__); +} + +/// Explicit instantiations for the three types. +/// \{ +template void +TypeCategoryMap::Get<lldb::TypeFormatImplSP>(FormattersMatchData &match_data, + lldb::TypeFormatImplSP &retval); +template void +TypeCategoryMap::Get<lldb::TypeSummaryImplSP>(FormattersMatchData &match_data, + lldb::TypeSummaryImplSP &retval); +template void TypeCategoryMap::Get<lldb::SyntheticChildrenSP>( + FormattersMatchData &match_data, lldb::SyntheticChildrenSP &retval); +/// \} + +void TypeCategoryMap::ForEach(ForEachCallback callback) { + if (callback) { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + + // loop through enabled categories in respective order + { + ActiveCategoriesIterator begin, end = m_active_categories.end(); + for (begin = m_active_categories.begin(); begin != end; begin++) { + lldb::TypeCategoryImplSP category = *begin; + if (!callback(category)) + break; + } + } + + // loop through disabled categories in just any order + { + MapIterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; pos++) { + if (pos->second->IsEnabled()) + continue; + if (!callback(pos->second)) + break; + } + } + } +} + +TypeCategoryImplSP TypeCategoryMap::GetAtIndex(uint32_t index) { + std::lock_guard<std::recursive_mutex> guard(m_map_mutex); + + if (index < m_map.size()) { + MapIterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; pos++) { + if (index == 0) + return pos->second; + index--; + } + } + + return TypeCategoryImplSP(); +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeFormat.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeFormat.cpp new file mode 100644 index 000000000000..409c452110bd --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeFormat.cpp @@ -0,0 +1,205 @@ +//===-- TypeFormat.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/DataFormatters/TypeFormat.h" + + + + +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-public.h" + +#include "lldb/Core/DumpDataExtractor.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/StreamString.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +TypeFormatImpl::TypeFormatImpl(const Flags &flags) : m_flags(flags) {} + +TypeFormatImpl::~TypeFormatImpl() = default; + +TypeFormatImpl_Format::TypeFormatImpl_Format(lldb::Format f, + const TypeFormatImpl::Flags &flags) + : TypeFormatImpl(flags), m_format(f) {} + +TypeFormatImpl_Format::~TypeFormatImpl_Format() = default; + +bool TypeFormatImpl_Format::FormatObject(ValueObject *valobj, + std::string &dest) const { + if (!valobj) + return false; + if (valobj->CanProvideValue()) { + Value &value(valobj->GetValue()); + const Value::ContextType context_type = value.GetContextType(); + ExecutionContext exe_ctx(valobj->GetExecutionContextRef()); + DataExtractor data; + + if (context_type == Value::ContextType::RegisterInfo) { + const RegisterInfo *reg_info = value.GetRegisterInfo(); + if (reg_info) { + Status error; + valobj->GetData(data, error); + if (error.Fail()) + return false; + + StreamString reg_sstr; + DumpDataExtractor(data, ®_sstr, 0, GetFormat(), reg_info->byte_size, + 1, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0, + exe_ctx.GetBestExecutionContextScope()); + dest = std::string(reg_sstr.GetString()); + } + } else { + CompilerType compiler_type = value.GetCompilerType(); + if (compiler_type) { + // put custom bytes to display in the DataExtractor to override the + // default value logic + if (GetFormat() == eFormatCString) { + lldb_private::Flags type_flags(compiler_type.GetTypeInfo( + nullptr)); // disambiguate w.r.t. TypeFormatImpl::Flags + if (type_flags.Test(eTypeIsPointer) && + !type_flags.Test(eTypeIsObjC)) { + // if we are dumping a pointer as a c-string, get the pointee data + // as a string + TargetSP target_sp(valobj->GetTargetSP()); + if (target_sp) { + size_t max_len = target_sp->GetMaximumSizeOfStringSummary(); + Status error; + WritableDataBufferSP buffer_sp( + new DataBufferHeap(max_len + 1, 0)); + Address address(valobj->GetPointerValue()); + target_sp->ReadCStringFromMemory( + address, (char *)buffer_sp->GetBytes(), max_len, error); + if (error.Success()) + data.SetData(buffer_sp); + } + } + } else { + Status error; + valobj->GetData(data, error); + if (error.Fail()) + return false; + } + + ExecutionContextScope *exe_scope = + exe_ctx.GetBestExecutionContextScope(); + std::optional<uint64_t> size = compiler_type.GetByteSize(exe_scope); + if (!size) + return false; + StreamString sstr; + compiler_type.DumpTypeValue( + &sstr, // The stream to use for display + GetFormat(), // Format to display this type with + data, // Data to extract from + 0, // Byte offset into "m_data" + *size, // Byte size of item in "m_data" + valobj->GetBitfieldBitSize(), // Bitfield bit size + valobj->GetBitfieldBitOffset(), // Bitfield bit offset + exe_scope); + // Given that we do not want to set the ValueObject's m_error for a + // formatting error (or else we wouldn't be able to reformat until a + // next update), an empty string is treated as a "false" return from + // here, but that's about as severe as we get + // CompilerType::DumpTypeValue() should always return something, even + // if that something is an error message + dest = std::string(sstr.GetString()); + } + } + return !dest.empty(); + } else + return false; +} + +std::string TypeFormatImpl_Format::GetDescription() { + StreamString sstr; + sstr.Printf("%s%s%s%s", FormatManager::GetFormatAsCString(GetFormat()), + Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : ""); + return std::string(sstr.GetString()); +} + +TypeFormatImpl_EnumType::TypeFormatImpl_EnumType( + ConstString type_name, const TypeFormatImpl::Flags &flags) + : TypeFormatImpl(flags), m_enum_type(type_name), m_types() {} + +TypeFormatImpl_EnumType::~TypeFormatImpl_EnumType() = default; + +bool TypeFormatImpl_EnumType::FormatObject(ValueObject *valobj, + std::string &dest) const { + dest.clear(); + if (!valobj) + return false; + if (!valobj->CanProvideValue()) + return false; + ProcessSP process_sp; + TargetSP target_sp; + void *valobj_key = (process_sp = valobj->GetProcessSP()).get(); + if (!valobj_key) + valobj_key = (target_sp = valobj->GetTargetSP()).get(); + else + target_sp = process_sp->GetTarget().shared_from_this(); + if (!valobj_key) + return false; + auto iter = m_types.find(valobj_key), end = m_types.end(); + CompilerType valobj_enum_type; + if (iter == end) { + // probably a redundant check + if (!target_sp) + return false; + const ModuleList &images(target_sp->GetImages()); + TypeQuery query(m_enum_type.GetStringRef()); + TypeResults results; + images.FindTypes(nullptr, query, results); + if (results.GetTypeMap().Empty()) + return false; + for (lldb::TypeSP type_sp : results.GetTypeMap().Types()) { + if (!type_sp) + continue; + if ((type_sp->GetForwardCompilerType().GetTypeInfo() & + eTypeIsEnumeration) == eTypeIsEnumeration) { + valobj_enum_type = type_sp->GetFullCompilerType(); + m_types.emplace(valobj_key, valobj_enum_type); + break; + } + } + } else + valobj_enum_type = iter->second; + if (!valobj_enum_type.IsValid()) + return false; + DataExtractor data; + Status error; + valobj->GetData(data, error); + if (error.Fail()) + return false; + ExecutionContext exe_ctx(valobj->GetExecutionContextRef()); + StreamString sstr; + valobj_enum_type.DumpTypeValue(&sstr, lldb::eFormatEnum, data, 0, + data.GetByteSize(), 0, 0, + exe_ctx.GetBestExecutionContextScope()); + if (!sstr.GetString().empty()) + dest = std::string(sstr.GetString()); + return !dest.empty(); +} + +std::string TypeFormatImpl_EnumType::GetDescription() { + StreamString sstr; + sstr.Printf("as type %s%s%s%s", m_enum_type.AsCString("<invalid type>"), + Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : ""); + return std::string(sstr.GetString()); +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeSummary.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeSummary.cpp new file mode 100644 index 000000000000..3707d2d879d3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeSummary.cpp @@ -0,0 +1,203 @@ +//===-- TypeSummary.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/DataFormatters/TypeSummary.h" + + + + +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-public.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/ValueObjectPrinter.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +TypeSummaryOptions::TypeSummaryOptions() = default; + +lldb::LanguageType TypeSummaryOptions::GetLanguage() const { return m_lang; } + +lldb::TypeSummaryCapping TypeSummaryOptions::GetCapping() const { + return m_capping; +} + +TypeSummaryOptions &TypeSummaryOptions::SetLanguage(lldb::LanguageType lang) { + m_lang = lang; + return *this; +} + +TypeSummaryOptions & +TypeSummaryOptions::SetCapping(lldb::TypeSummaryCapping cap) { + m_capping = cap; + return *this; +} + +TypeSummaryImpl::TypeSummaryImpl(Kind kind, const TypeSummaryImpl::Flags &flags) + : m_flags(flags), m_kind(kind) {} + +StringSummaryFormat::StringSummaryFormat(const TypeSummaryImpl::Flags &flags, + const char *format_cstr) + : TypeSummaryImpl(Kind::eSummaryString, flags), m_format_str() { + SetSummaryString(format_cstr); +} + +void StringSummaryFormat::SetSummaryString(const char *format_cstr) { + m_format.Clear(); + if (format_cstr && format_cstr[0]) { + m_format_str = format_cstr; + m_error = FormatEntity::Parse(format_cstr, m_format); + } else { + m_format_str.clear(); + m_error.Clear(); + } +} + +bool StringSummaryFormat::FormatObject(ValueObject *valobj, std::string &retval, + const TypeSummaryOptions &options) { + if (!valobj) { + retval.assign("NULL ValueObject"); + return false; + } + + StreamString s; + ExecutionContext exe_ctx(valobj->GetExecutionContextRef()); + SymbolContext sc; + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame) + sc = frame->GetSymbolContext(lldb::eSymbolContextEverything); + + if (IsOneLiner()) { + // We've already checked the case of a NULL valobj above. Let's put in an + // assert here to make sure someone doesn't take that out: + assert(valobj && "Must have a valid ValueObject to summarize"); + ValueObjectPrinter printer(*valobj, &s, DumpValueObjectOptions()); + printer.PrintChildrenOneLiner(HideNames(valobj)); + retval = std::string(s.GetString()); + return true; + } else { + if (FormatEntity::Format(m_format, s, &sc, &exe_ctx, + &sc.line_entry.range.GetBaseAddress(), valobj, + false, false)) { + retval.assign(std::string(s.GetString())); + return true; + } else { + retval.assign("error: summary string parsing error"); + return false; + } + } +} + +std::string StringSummaryFormat::GetDescription() { + StreamString sstr; + + sstr.Printf("`%s`%s%s%s%s%s%s%s%s%s", m_format_str.c_str(), + m_error.Fail() ? " error: " : "", + m_error.Fail() ? m_error.AsCString() : "", + Cascades() ? "" : " (not cascading)", + !DoesPrintChildren(nullptr) ? "" : " (show children)", + !DoesPrintValue(nullptr) ? " (hide value)" : "", + IsOneLiner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames(nullptr) ? " (hide member names)" : ""); + return std::string(sstr.GetString()); +} + +CXXFunctionSummaryFormat::CXXFunctionSummaryFormat( + const TypeSummaryImpl::Flags &flags, Callback impl, const char *description) + : TypeSummaryImpl(Kind::eCallback, flags), m_impl(impl), + m_description(description ? description : "") {} + +bool CXXFunctionSummaryFormat::FormatObject(ValueObject *valobj, + std::string &dest, + const TypeSummaryOptions &options) { + dest.clear(); + StreamString stream; + if (!m_impl || !m_impl(*valobj, stream, options)) + return false; + dest = std::string(stream.GetString()); + return true; +} + +std::string CXXFunctionSummaryFormat::GetDescription() { + StreamString sstr; + sstr.Printf("%s%s%s%s%s%s%s %s", Cascades() ? "" : " (not cascading)", + !DoesPrintChildren(nullptr) ? "" : " (show children)", + !DoesPrintValue(nullptr) ? " (hide value)" : "", + IsOneLiner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames(nullptr) ? " (hide member names)" : "", + m_description.c_str()); + return std::string(sstr.GetString()); +} + +ScriptSummaryFormat::ScriptSummaryFormat(const TypeSummaryImpl::Flags &flags, + const char *function_name, + const char *python_script) + : TypeSummaryImpl(Kind::eScript, flags), m_function_name(), + m_python_script(), m_script_function_sp() { + if (function_name) + m_function_name.assign(function_name); + if (python_script) + m_python_script.assign(python_script); +} + +bool ScriptSummaryFormat::FormatObject(ValueObject *valobj, std::string &retval, + const TypeSummaryOptions &options) { + if (!valobj) + return false; + + TargetSP target_sp(valobj->GetTargetSP()); + + if (!target_sp) { + retval.assign("error: no target"); + return false; + } + + ScriptInterpreter *script_interpreter = + target_sp->GetDebugger().GetScriptInterpreter(); + + if (!script_interpreter) { + retval.assign("error: no ScriptInterpreter"); + return false; + } + + return script_interpreter->GetScriptedSummary( + m_function_name.c_str(), valobj->GetSP(), m_script_function_sp, options, + retval); +} + +std::string ScriptSummaryFormat::GetDescription() { + StreamString sstr; + sstr.Printf("%s%s%s%s%s%s%s\n ", Cascades() ? "" : " (not cascading)", + !DoesPrintChildren(nullptr) ? "" : " (show children)", + !DoesPrintValue(nullptr) ? " (hide value)" : "", + IsOneLiner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames(nullptr) ? " (hide member names)" : ""); + if (m_python_script.empty()) { + if (m_function_name.empty()) { + sstr.PutCString("no backing script"); + } else { + sstr.PutCString(m_function_name); + } + } else { + sstr.PutCString(m_python_script); + } + return std::string(sstr.GetString()); +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeSynthetic.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeSynthetic.cpp new file mode 100644 index 000000000000..7aa0670190b2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeSynthetic.cpp @@ -0,0 +1,251 @@ +//===-- TypeSynthetic.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/lldb-enumerations.h" +#include "lldb/lldb-public.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +void TypeFilterImpl::AddExpressionPath(const std::string &path) { + bool need_add_dot = true; + if (path[0] == '.' || (path[0] == '-' && path[1] == '>') || path[0] == '[') + need_add_dot = false; + // add a '.' symbol to help forgetful users + if (!need_add_dot) + m_expression_paths.push_back(path); + else + m_expression_paths.push_back(std::string(".") + path); +} + +bool TypeFilterImpl::SetExpressionPathAtIndex(size_t i, + const std::string &path) { + if (i >= GetCount()) + return false; + bool need_add_dot = true; + if (path[0] == '.' || (path[0] == '-' && path[1] == '>') || path[0] == '[') + need_add_dot = false; + // add a '.' symbol to help forgetful users + if (!need_add_dot) + m_expression_paths[i] = path; + else + m_expression_paths[i] = std::string(".") + path; + return true; +} + +size_t +TypeFilterImpl::FrontEnd::GetIndexOfChildWithName(ConstString name) { + const char *name_cstr = name.GetCString(); + if (name_cstr) { + for (size_t i = 0; i < filter->GetCount(); i++) { + const char *expr_cstr = filter->GetExpressionPathAtIndex(i); + if (expr_cstr) { + if (*expr_cstr == '.') + expr_cstr++; + else if (*expr_cstr == '-' && *(expr_cstr + 1) == '>') + expr_cstr += 2; + } + if (expr_cstr) { + if (!::strcmp(name_cstr, expr_cstr)) + return i; + } + } + } + return UINT32_MAX; +} + +std::string TypeFilterImpl::GetDescription() { + StreamString sstr; + sstr.Printf("%s%s%s {\n", Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : ""); + + for (size_t i = 0; i < GetCount(); i++) { + sstr.Printf(" %s\n", GetExpressionPathAtIndex(i)); + } + + sstr.Printf("}"); + return std::string(sstr.GetString()); +} + +SyntheticChildren::SyntheticChildren(const Flags &flags) : m_flags(flags) {} + +SyntheticChildren::~SyntheticChildren() = default; + +CXXSyntheticChildren::CXXSyntheticChildren( + const SyntheticChildren::Flags &flags, const char *description, + CreateFrontEndCallback callback) + : SyntheticChildren(flags), m_create_callback(std::move(callback)), + m_description(description ? description : "") {} + +CXXSyntheticChildren::~CXXSyntheticChildren() = default; + +bool SyntheticChildren::IsScripted() { return false; } + +std::string SyntheticChildren::GetDescription() { return ""; } + +SyntheticChildrenFrontEnd::AutoPointer +SyntheticChildren::GetFrontEnd(ValueObject &backend) { + return nullptr; +} + +std::string CXXSyntheticChildren::GetDescription() { + StreamString sstr; + sstr.Printf("%s%s%s %s", Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + m_description.c_str()); + + return std::string(sstr.GetString()); +} + +uint32_t +SyntheticChildrenFrontEnd::CalculateNumChildrenIgnoringErrors(uint32_t max) { + auto value_or_err = CalculateNumChildren(max); + if (value_or_err) + return *value_or_err; + LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), value_or_err.takeError(), + "{0}"); + return 0; +} + +lldb::ValueObjectSP SyntheticChildrenFrontEnd::CreateValueObjectFromExpression( + llvm::StringRef name, llvm::StringRef expression, + const ExecutionContext &exe_ctx) { + ValueObjectSP valobj_sp( + ValueObject::CreateValueObjectFromExpression(name, expression, exe_ctx)); + if (valobj_sp) + valobj_sp->SetSyntheticChildrenGenerated(true); + return valobj_sp; +} + +lldb::ValueObjectSP SyntheticChildrenFrontEnd::CreateValueObjectFromAddress( + llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, + CompilerType type) { + ValueObjectSP valobj_sp( + ValueObject::CreateValueObjectFromAddress(name, address, exe_ctx, type)); + if (valobj_sp) + valobj_sp->SetSyntheticChildrenGenerated(true); + return valobj_sp; +} + +lldb::ValueObjectSP SyntheticChildrenFrontEnd::CreateValueObjectFromData( + llvm::StringRef name, const DataExtractor &data, + const ExecutionContext &exe_ctx, CompilerType type) { + ValueObjectSP valobj_sp( + ValueObject::CreateValueObjectFromData(name, data, exe_ctx, type)); + if (valobj_sp) + valobj_sp->SetSyntheticChildrenGenerated(true); + return valobj_sp; +} + +ScriptedSyntheticChildren::FrontEnd::FrontEnd(std::string pclass, + ValueObject &backend) + : SyntheticChildrenFrontEnd(backend), m_python_class(pclass), + m_wrapper_sp(), m_interpreter(nullptr) { + if (backend.GetID() == LLDB_INVALID_UID) + return; + + TargetSP target_sp = backend.GetTargetSP(); + + if (!target_sp) + return; + + m_interpreter = target_sp->GetDebugger().GetScriptInterpreter(); + + if (m_interpreter != nullptr) + m_wrapper_sp = m_interpreter->CreateSyntheticScriptedProvider( + m_python_class.c_str(), backend.GetSP()); +} + +ScriptedSyntheticChildren::FrontEnd::~FrontEnd() = default; + +lldb::ValueObjectSP +ScriptedSyntheticChildren::FrontEnd::GetChildAtIndex(uint32_t idx) { + if (!m_wrapper_sp || !m_interpreter) + return lldb::ValueObjectSP(); + + return m_interpreter->GetChildAtIndex(m_wrapper_sp, idx); +} + +bool ScriptedSyntheticChildren::FrontEnd::IsValid() { + return (m_wrapper_sp && m_wrapper_sp->IsValid() && m_interpreter); +} + +llvm::Expected<uint32_t> +ScriptedSyntheticChildren::FrontEnd::CalculateNumChildren() { + if (!m_wrapper_sp || m_interpreter == nullptr) + return 0; + return m_interpreter->CalculateNumChildren(m_wrapper_sp, UINT32_MAX); +} + +llvm::Expected<uint32_t> +ScriptedSyntheticChildren::FrontEnd::CalculateNumChildren(uint32_t max) { + if (!m_wrapper_sp || m_interpreter == nullptr) + return 0; + return m_interpreter->CalculateNumChildren(m_wrapper_sp, max); +} + +lldb::ChildCacheState ScriptedSyntheticChildren::FrontEnd::Update() { + if (!m_wrapper_sp || m_interpreter == nullptr) + return lldb::ChildCacheState::eRefetch; + + return m_interpreter->UpdateSynthProviderInstance(m_wrapper_sp) + ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +bool ScriptedSyntheticChildren::FrontEnd::MightHaveChildren() { + if (!m_wrapper_sp || m_interpreter == nullptr) + return false; + + return m_interpreter->MightHaveChildrenSynthProviderInstance(m_wrapper_sp); +} + +size_t ScriptedSyntheticChildren::FrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (!m_wrapper_sp || m_interpreter == nullptr) + return UINT32_MAX; + return m_interpreter->GetIndexOfChildWithName(m_wrapper_sp, + name.GetCString()); +} + +lldb::ValueObjectSP ScriptedSyntheticChildren::FrontEnd::GetSyntheticValue() { + if (!m_wrapper_sp || m_interpreter == nullptr) + return nullptr; + + return m_interpreter->GetSyntheticValue(m_wrapper_sp); +} + +ConstString ScriptedSyntheticChildren::FrontEnd::GetSyntheticTypeName() { + if (!m_wrapper_sp || m_interpreter == nullptr) + return ConstString(); + + return m_interpreter->GetSyntheticTypeName(m_wrapper_sp); +} + +std::string ScriptedSyntheticChildren::GetDescription() { + StreamString sstr; + sstr.Printf("%s%s%s Python class %s", Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + m_python_class.c_str()); + + return std::string(sstr.GetString()); +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/contrib/llvm-project/lldb/source/DataFormatters/ValueObjectPrinter.cpp new file mode 100644 index 000000000000..ce24a7866e53 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -0,0 +1,886 @@ +//===-- ValueObjectPrinter.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/DataFormatters/ValueObjectPrinter.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Stream.h" +#include "llvm/Support/MathExtras.h" +#include <cstdint> + +using namespace lldb; +using namespace lldb_private; + +ValueObjectPrinter::ValueObjectPrinter(ValueObject &valobj, Stream *s) + : m_orig_valobj(valobj) { + DumpValueObjectOptions options(valobj); + Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr); +} + +ValueObjectPrinter::ValueObjectPrinter(ValueObject &valobj, Stream *s, + const DumpValueObjectOptions &options) + : m_orig_valobj(valobj) { + Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr); +} + +ValueObjectPrinter::ValueObjectPrinter( + ValueObject &valobj, Stream *s, const DumpValueObjectOptions &options, + const DumpValueObjectOptions::PointerDepth &ptr_depth, uint32_t curr_depth, + InstancePointersSetSP printed_instance_pointers) + : m_orig_valobj(valobj) { + Init(valobj, s, options, ptr_depth, curr_depth, printed_instance_pointers); +} + +void ValueObjectPrinter::Init( + ValueObject &valobj, Stream *s, const DumpValueObjectOptions &options, + const DumpValueObjectOptions::PointerDepth &ptr_depth, uint32_t curr_depth, + InstancePointersSetSP printed_instance_pointers) { + m_cached_valobj = nullptr; + m_stream = s; + m_options = options; + m_ptr_depth = ptr_depth; + m_curr_depth = curr_depth; + assert(m_stream && "cannot print to a NULL Stream"); + m_should_print = eLazyBoolCalculate; + m_is_nil = eLazyBoolCalculate; + m_is_uninit = eLazyBoolCalculate; + m_is_ptr = eLazyBoolCalculate; + m_is_ref = eLazyBoolCalculate; + m_is_aggregate = eLazyBoolCalculate; + m_is_instance_ptr = eLazyBoolCalculate; + m_summary_formatter = {nullptr, false}; + m_value.assign(""); + m_summary.assign(""); + m_error.assign(""); + m_val_summary_ok = false; + m_printed_instance_pointers = + printed_instance_pointers + ? printed_instance_pointers + : InstancePointersSetSP(new InstancePointersSet()); + SetupMostSpecializedValue(); +} + +llvm::Error ValueObjectPrinter::PrintValueObject() { + // If the incoming ValueObject is in an error state, the best we're going to + // get out of it is its type. But if we don't even have that, just print + // the error and exit early. + if (m_orig_valobj.GetError().Fail() && + !m_orig_valobj.GetCompilerType().IsValid()) + return m_orig_valobj.GetError().ToError(); + + if (ShouldPrintValueObject()) { + PrintLocationIfNeeded(); + m_stream->Indent(); + + PrintDecl(); + } + + bool value_printed = false; + bool summary_printed = false; + + m_val_summary_ok = + PrintValueAndSummaryIfNeeded(value_printed, summary_printed); + + if (m_val_summary_ok) + return PrintChildrenIfNeeded(value_printed, summary_printed); + m_stream->EOL(); + + return llvm::Error::success(); +} + +ValueObject &ValueObjectPrinter::GetMostSpecializedValue() { + assert(m_cached_valobj && "ValueObjectPrinter must have a valid ValueObject"); + return *m_cached_valobj; +} + +void ValueObjectPrinter::SetupMostSpecializedValue() { + bool update_success = m_orig_valobj.UpdateValueIfNeeded(true); + // If we can't find anything better, we'll fall back on the original + // ValueObject. + m_cached_valobj = &m_orig_valobj; + if (update_success) { + if (m_orig_valobj.IsDynamic()) { + if (m_options.m_use_dynamic == eNoDynamicValues) { + ValueObject *static_value = m_orig_valobj.GetStaticValue().get(); + if (static_value) + m_cached_valobj = static_value; + } + } else { + if (m_options.m_use_dynamic != eNoDynamicValues) { + ValueObject *dynamic_value = + m_orig_valobj.GetDynamicValue(m_options.m_use_dynamic).get(); + if (dynamic_value) + m_cached_valobj = dynamic_value; + } + } + + if (m_cached_valobj->IsSynthetic()) { + if (!m_options.m_use_synthetic) { + ValueObject *non_synthetic = + m_cached_valobj->GetNonSyntheticValue().get(); + if (non_synthetic) + m_cached_valobj = non_synthetic; + } + } else { + if (m_options.m_use_synthetic) { + ValueObject *synthetic = m_cached_valobj->GetSyntheticValue().get(); + if (synthetic) + m_cached_valobj = synthetic; + } + } + } + m_compiler_type = m_cached_valobj->GetCompilerType(); + m_type_flags = m_compiler_type.GetTypeInfo(); + assert(m_cached_valobj && + "SetupMostSpecialized value must compute a valid ValueObject"); +} + +llvm::Expected<std::string> ValueObjectPrinter::GetDescriptionForDisplay() { + ValueObject &valobj = GetMostSpecializedValue(); + llvm::Expected<std::string> maybe_str = valobj.GetObjectDescription(); + if (maybe_str) + return maybe_str; + + const char *str = nullptr; + if (!str) + str = valobj.GetSummaryAsCString(); + if (!str) + str = valobj.GetValueAsCString(); + + if (!str) + return maybe_str; + llvm::consumeError(maybe_str.takeError()); + return str; +} + +const char *ValueObjectPrinter::GetRootNameForDisplay() { + const char *root_valobj_name = + m_options.m_root_valobj_name.empty() + ? GetMostSpecializedValue().GetName().AsCString() + : m_options.m_root_valobj_name.c_str(); + return root_valobj_name ? root_valobj_name : ""; +} + +bool ValueObjectPrinter::ShouldPrintValueObject() { + if (m_should_print == eLazyBoolCalculate) + m_should_print = + (!m_options.m_flat_output || m_type_flags.Test(eTypeHasValue)) + ? eLazyBoolYes + : eLazyBoolNo; + return m_should_print == eLazyBoolYes; +} + +bool ValueObjectPrinter::IsNil() { + if (m_is_nil == eLazyBoolCalculate) + m_is_nil = + GetMostSpecializedValue().IsNilReference() ? eLazyBoolYes : eLazyBoolNo; + return m_is_nil == eLazyBoolYes; +} + +bool ValueObjectPrinter::IsUninitialized() { + if (m_is_uninit == eLazyBoolCalculate) + m_is_uninit = GetMostSpecializedValue().IsUninitializedReference() + ? eLazyBoolYes + : eLazyBoolNo; + return m_is_uninit == eLazyBoolYes; +} + +bool ValueObjectPrinter::IsPtr() { + if (m_is_ptr == eLazyBoolCalculate) + m_is_ptr = m_type_flags.Test(eTypeIsPointer) ? eLazyBoolYes : eLazyBoolNo; + return m_is_ptr == eLazyBoolYes; +} + +bool ValueObjectPrinter::IsRef() { + if (m_is_ref == eLazyBoolCalculate) + m_is_ref = m_type_flags.Test(eTypeIsReference) ? eLazyBoolYes : eLazyBoolNo; + return m_is_ref == eLazyBoolYes; +} + +bool ValueObjectPrinter::IsAggregate() { + if (m_is_aggregate == eLazyBoolCalculate) + m_is_aggregate = + m_type_flags.Test(eTypeHasChildren) ? eLazyBoolYes : eLazyBoolNo; + return m_is_aggregate == eLazyBoolYes; +} + +bool ValueObjectPrinter::IsInstancePointer() { + // you need to do this check on the value's clang type + ValueObject &valobj = GetMostSpecializedValue(); + if (m_is_instance_ptr == eLazyBoolCalculate) + m_is_instance_ptr = (valobj.GetValue().GetCompilerType().GetTypeInfo() & + eTypeInstanceIsPointer) != 0 + ? eLazyBoolYes + : eLazyBoolNo; + if ((eLazyBoolYes == m_is_instance_ptr) && valobj.IsBaseClass()) + m_is_instance_ptr = eLazyBoolNo; + return m_is_instance_ptr == eLazyBoolYes; +} + +bool ValueObjectPrinter::PrintLocationIfNeeded() { + if (m_options.m_show_location) { + m_stream->Printf("%s: ", GetMostSpecializedValue().GetLocationAsCString()); + return true; + } + return false; +} + +void ValueObjectPrinter::PrintDecl() { + bool show_type = true; + // if we are at the root-level and been asked to hide the root's type, then + // hide it + if (m_curr_depth == 0 && m_options.m_hide_root_type) + show_type = false; + else + // otherwise decide according to the usual rules (asked to show types - + // always at the root level) + show_type = m_options.m_show_types || + (m_curr_depth == 0 && !m_options.m_flat_output); + + StreamString typeName; + // Figure out which ValueObject we're acting on + ValueObject &valobj = GetMostSpecializedValue(); + + // always show the type at the root level if it is invalid + if (show_type) { + // Some ValueObjects don't have types (like registers sets). Only print the + // type if there is one to print + ConstString type_name; + if (m_compiler_type.IsValid()) { + type_name = m_options.m_use_type_display_name + ? valobj.GetDisplayTypeName() + : valobj.GetQualifiedTypeName(); + } else { + // only show an invalid type name if the user explicitly triggered + // show_type + if (m_options.m_show_types) + type_name = ConstString("<invalid type>"); + } + + if (type_name) { + std::string type_name_str(type_name.GetCString()); + if (m_options.m_hide_pointer_value) { + for (auto iter = type_name_str.find(" *"); iter != std::string::npos; + iter = type_name_str.find(" *")) { + type_name_str.erase(iter, 2); + } + } + typeName << type_name_str.c_str(); + } + } + + StreamString varName; + + if (ShouldShowName()) { + if (m_options.m_flat_output) + valobj.GetExpressionPath(varName); + else + varName << GetRootNameForDisplay(); + } + + bool decl_printed = false; + if (!m_options.m_decl_printing_helper) { + // if the user didn't give us a custom helper, pick one based upon the + // language, either the one that this printer is bound to, or the preferred + // one for the ValueObject + lldb::LanguageType lang_type = + (m_options.m_varformat_language == lldb::eLanguageTypeUnknown) + ? valobj.GetPreferredDisplayLanguage() + : m_options.m_varformat_language; + if (Language *lang_plugin = Language::FindPlugin(lang_type)) { + m_options.m_decl_printing_helper = lang_plugin->GetDeclPrintingHelper(); + } + } + + if (m_options.m_decl_printing_helper) { + ConstString type_name_cstr(typeName.GetString()); + ConstString var_name_cstr(varName.GetString()); + + DumpValueObjectOptions decl_print_options = m_options; + // Pass printing helpers an option object that indicates whether the name + // should be shown or hidden. + decl_print_options.SetHideName(!ShouldShowName()); + + StreamString dest_stream; + if (m_options.m_decl_printing_helper(type_name_cstr, var_name_cstr, + decl_print_options, dest_stream)) { + decl_printed = true; + m_stream->PutCString(dest_stream.GetString()); + } + } + + // if the helper failed, or there is none, do a default thing + if (!decl_printed) { + if (!typeName.Empty()) + m_stream->Printf("(%s) ", typeName.GetData()); + if (!varName.Empty()) + m_stream->Printf("%s =", varName.GetData()); + else if (ShouldShowName()) + m_stream->Printf(" ="); + } +} + +bool ValueObjectPrinter::CheckScopeIfNeeded() { + if (m_options.m_scope_already_checked) + return true; + return GetMostSpecializedValue().IsInScope(); +} + +TypeSummaryImpl *ValueObjectPrinter::GetSummaryFormatter(bool null_if_omitted) { + if (!m_summary_formatter.second) { + TypeSummaryImpl *entry = + m_options.m_summary_sp + ? m_options.m_summary_sp.get() + : GetMostSpecializedValue().GetSummaryFormat().get(); + + if (m_options.m_omit_summary_depth > 0) + entry = nullptr; + m_summary_formatter.first = entry; + m_summary_formatter.second = true; + } + if (m_options.m_omit_summary_depth > 0 && null_if_omitted) + return nullptr; + return m_summary_formatter.first; +} + +static bool IsPointerValue(const CompilerType &type) { + Flags type_flags(type.GetTypeInfo()); + if (type_flags.AnySet(eTypeInstanceIsPointer | eTypeIsPointer)) + return type_flags.AllClear(eTypeIsBuiltIn); + return false; +} + +void ValueObjectPrinter::GetValueSummaryError(std::string &value, + std::string &summary, + std::string &error) { + lldb::Format format = m_options.m_format; + ValueObject &valobj = GetMostSpecializedValue(); + // if I am printing synthetized elements, apply the format to those elements + // only + if (m_options.m_pointer_as_array) + valobj.GetValueAsCString(lldb::eFormatDefault, value); + else if (format != eFormatDefault && format != valobj.GetFormat()) + valobj.GetValueAsCString(format, value); + else { + const char *val_cstr = valobj.GetValueAsCString(); + if (val_cstr) + value.assign(val_cstr); + } + const char *err_cstr = valobj.GetError().AsCString(); + if (err_cstr) + error.assign(err_cstr); + + if (!ShouldPrintValueObject()) + return; + + if (IsNil()) { + lldb::LanguageType lang_type = + (m_options.m_varformat_language == lldb::eLanguageTypeUnknown) + ? valobj.GetPreferredDisplayLanguage() + : m_options.m_varformat_language; + if (Language *lang_plugin = Language::FindPlugin(lang_type)) { + summary.assign(lang_plugin->GetNilReferenceSummaryString().str()); + } else { + // We treat C as the fallback language rather than as a separate Language + // plugin. + summary.assign("NULL"); + } + } else if (IsUninitialized()) { + summary.assign("<uninitialized>"); + } else if (m_options.m_omit_summary_depth == 0) { + TypeSummaryImpl *entry = GetSummaryFormatter(); + if (entry) { + valobj.GetSummaryAsCString(entry, summary, + m_options.m_varformat_language); + } else { + const char *sum_cstr = + valobj.GetSummaryAsCString(m_options.m_varformat_language); + if (sum_cstr) + summary.assign(sum_cstr); + } + } +} + +bool ValueObjectPrinter::PrintValueAndSummaryIfNeeded(bool &value_printed, + bool &summary_printed) { + bool error_printed = false; + if (ShouldPrintValueObject()) { + if (!CheckScopeIfNeeded()) + m_error.assign("out of scope"); + if (m_error.empty()) { + GetValueSummaryError(m_value, m_summary, m_error); + } + if (m_error.size()) { + // we need to support scenarios in which it is actually fine for a value + // to have no type but - on the other hand - if we get an error *AND* + // have no type, we try to get out gracefully, since most often that + // combination means "could not resolve a type" and the default failure + // mode is quite ugly + if (!m_compiler_type.IsValid()) { + m_stream->Printf(" <could not resolve type>"); + return false; + } + + error_printed = true; + m_stream->Printf(" <%s>\n", m_error.c_str()); + } else { + // Make sure we have a value and make sure the summary didn't specify + // that the value should not be printed - and do not print the value if + // this thing is nil (but show the value if the user passes a format + // explicitly) + TypeSummaryImpl *entry = GetSummaryFormatter(); + ValueObject &valobj = GetMostSpecializedValue(); + const bool has_nil_or_uninitialized_summary = + (IsNil() || IsUninitialized()) && !m_summary.empty(); + if (!has_nil_or_uninitialized_summary && !m_value.empty() && + (entry == nullptr || + (entry->DoesPrintValue(&valobj) || + m_options.m_format != eFormatDefault) || + m_summary.empty()) && + !m_options.m_hide_value) { + if (m_options.m_hide_pointer_value && + IsPointerValue(valobj.GetCompilerType())) { + } else { + if (ShouldShowName()) + m_stream->PutChar(' '); + m_stream->PutCString(m_value); + value_printed = true; + } + } + + if (m_summary.size()) { + if (ShouldShowName() || value_printed) + m_stream->PutChar(' '); + m_stream->PutCString(m_summary); + summary_printed = true; + } + } + } + return !error_printed; +} + +llvm::Error +ValueObjectPrinter::PrintObjectDescriptionIfNeeded(bool value_printed, + bool summary_printed) { + if (ShouldPrintValueObject()) { + // let's avoid the overly verbose no description error for a nil thing + if (m_options.m_use_objc && !IsNil() && !IsUninitialized() && + (!m_options.m_pointer_as_array)) { + if (!m_options.m_hide_value || ShouldShowName()) + *m_stream << ' '; + llvm::Expected<std::string> object_desc = + (value_printed || summary_printed) + ? GetMostSpecializedValue().GetObjectDescription() + : GetDescriptionForDisplay(); + if (!object_desc) { + // If no value or summary was printed, surface the error. + if (!value_printed && !summary_printed) + return object_desc.takeError(); + // Otherwise gently nudge the user that they should have used + // `p` instead of `po`. Unfortunately we cannot be more direct + // about this, because we don't actually know what the user did. + *m_stream << "warning: no object description available\n"; + llvm::consumeError(object_desc.takeError()); + } else { + *m_stream << *object_desc; + // If the description already ends with a \n don't add another one. + if (object_desc->empty() || object_desc->back() != '\n') + *m_stream << '\n'; + } + return llvm::Error::success(); + } + } + return llvm::Error::success(); +} + +bool DumpValueObjectOptions::PointerDepth::CanAllowExpansion() const { + switch (m_mode) { + case Mode::Always: + case Mode::Default: + return m_count > 0; + case Mode::Never: + return false; + } + return false; +} + +bool ValueObjectPrinter::ShouldPrintChildren( + DumpValueObjectOptions::PointerDepth &curr_ptr_depth) { + const bool is_ref = IsRef(); + const bool is_ptr = IsPtr(); + const bool is_uninit = IsUninitialized(); + + if (is_uninit) + return false; + + // If we have reached the maximum depth we shouldn't print any more children. + if (HasReachedMaximumDepth()) + return false; + + // if the user has specified an element count, always print children as it is + // explicit user demand being honored + if (m_options.m_pointer_as_array) + return true; + + if (m_options.m_use_objc) + return false; + + bool print_children = true; + ValueObject &valobj = GetMostSpecializedValue(); + if (TypeSummaryImpl *type_summary = GetSummaryFormatter()) + print_children = type_summary->DoesPrintChildren(&valobj); + + // We will show children for all concrete types. We won't show pointer + // contents unless a pointer depth has been specified. We won't reference + // contents unless the reference is the root object (depth of zero). + + // Use a new temporary pointer depth in case we override the current + // pointer depth below... + + if (is_ptr || is_ref) { + // We have a pointer or reference whose value is an address. Make sure + // that address is not NULL + AddressType ptr_address_type; + if (valobj.GetPointerValue(&ptr_address_type) == 0) + return false; + + const bool is_root_level = m_curr_depth == 0; + + if (is_ref && is_root_level && print_children) { + // If this is the root object (depth is zero) that we are showing and + // it is a reference, and no pointer depth has been supplied print out + // what it references. Don't do this at deeper depths otherwise we can + // end up with infinite recursion... + return true; + } + + return curr_ptr_depth.CanAllowExpansion(); + } + + return print_children || m_summary.empty(); +} + +bool ValueObjectPrinter::ShouldExpandEmptyAggregates() { + TypeSummaryImpl *entry = GetSummaryFormatter(); + + if (!entry) + return true; + + return entry->DoesPrintEmptyAggregates(); +} + +ValueObject &ValueObjectPrinter::GetValueObjectForChildrenGeneration() { + return GetMostSpecializedValue(); +} + +void ValueObjectPrinter::PrintChildrenPreamble(bool value_printed, + bool summary_printed) { + if (m_options.m_flat_output) { + if (ShouldPrintValueObject()) + m_stream->EOL(); + } else { + if (ShouldPrintValueObject()) { + if (IsRef()) { + m_stream->PutCString(": "); + } else if (value_printed || summary_printed || ShouldShowName()) { + m_stream->PutChar(' '); + } + m_stream->PutCString("{\n"); + } + m_stream->IndentMore(); + } +} + +void ValueObjectPrinter::PrintChild( + ValueObjectSP child_sp, + const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) { + const uint32_t consumed_summary_depth = m_options.m_pointer_as_array ? 0 : 1; + const bool does_consume_ptr_depth = + ((IsPtr() && !m_options.m_pointer_as_array) || IsRef()); + + DumpValueObjectOptions child_options(m_options); + child_options.SetFormat(m_options.m_format) + .SetSummary() + .SetRootValueObjectName(); + child_options.SetScopeChecked(true) + .SetHideName(m_options.m_hide_name) + .SetHideValue(m_options.m_hide_value) + .SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1 + ? child_options.m_omit_summary_depth - + consumed_summary_depth + : 0) + .SetElementCount(0); + + if (child_sp.get()) { + auto ptr_depth = curr_ptr_depth; + if (does_consume_ptr_depth) + ptr_depth = curr_ptr_depth.Decremented(); + + ValueObjectPrinter child_printer(*(child_sp.get()), m_stream, child_options, + ptr_depth, m_curr_depth + 1, + m_printed_instance_pointers); + llvm::Error error = child_printer.PrintValueObject(); + if (error) { + if (m_stream) + *m_stream << "error: " << toString(std::move(error)); + else + llvm::consumeError(std::move(error)); + } + } +} + +llvm::Expected<uint32_t> +ValueObjectPrinter::GetMaxNumChildrenToPrint(bool &print_dotdotdot) { + ValueObject &synth_valobj = GetValueObjectForChildrenGeneration(); + + if (m_options.m_pointer_as_array) + return m_options.m_pointer_as_array.m_element_count; + + const uint32_t max_num_children = + m_options.m_ignore_cap ? UINT32_MAX + : GetMostSpecializedValue() + .GetTargetSP() + ->GetMaximumNumberOfChildrenToDisplay(); + // Ask for one more child than the maximum to see if we should print "...". + auto num_children_or_err = synth_valobj.GetNumChildren( + llvm::SaturatingAdd(max_num_children, uint32_t(1))); + if (!num_children_or_err) + return num_children_or_err; + if (*num_children_or_err > max_num_children) { + print_dotdotdot = true; + return max_num_children; + } + return num_children_or_err; +} + +void ValueObjectPrinter::PrintChildrenPostamble(bool print_dotdotdot) { + if (!m_options.m_flat_output) { + if (print_dotdotdot) { + GetMostSpecializedValue() + .GetTargetSP() + ->GetDebugger() + .GetCommandInterpreter() + .ChildrenTruncated(); + m_stream->Indent("...\n"); + } + m_stream->IndentLess(); + m_stream->Indent("}\n"); + } +} + +bool ValueObjectPrinter::ShouldPrintEmptyBrackets(bool value_printed, + bool summary_printed) { + ValueObject &synth_valobj = GetValueObjectForChildrenGeneration(); + + if (!IsAggregate()) + return false; + + if (!m_options.m_reveal_empty_aggregates) { + if (value_printed || summary_printed) + return false; + } + + if (synth_valobj.MightHaveChildren()) + return true; + + if (m_val_summary_ok) + return false; + + return true; +} + +static constexpr size_t PhysicalIndexForLogicalIndex(size_t base, size_t stride, + size_t logical) { + return base + logical * stride; +} + +ValueObjectSP ValueObjectPrinter::GenerateChild(ValueObject &synth_valobj, + size_t idx) { + if (m_options.m_pointer_as_array) { + // if generating pointer-as-array children, use GetSyntheticArrayMember + return synth_valobj.GetSyntheticArrayMember( + PhysicalIndexForLogicalIndex( + m_options.m_pointer_as_array.m_base_element, + m_options.m_pointer_as_array.m_stride, idx), + true); + } else { + // otherwise, do the usual thing + return synth_valobj.GetChildAtIndex(idx); + } +} + +void ValueObjectPrinter::PrintChildren( + bool value_printed, bool summary_printed, + const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) { + ValueObject &synth_valobj = GetValueObjectForChildrenGeneration(); + + bool print_dotdotdot = false; + auto num_children_or_err = GetMaxNumChildrenToPrint(print_dotdotdot); + if (!num_children_or_err) { + *m_stream << " <" << llvm::toString(num_children_or_err.takeError()) << '>'; + return; + } + uint32_t num_children = *num_children_or_err; + if (num_children) { + bool any_children_printed = false; + + for (size_t idx = 0; idx < num_children; ++idx) { + if (ValueObjectSP child_sp = GenerateChild(synth_valobj, idx)) { + if (m_options.m_child_printing_decider && + !m_options.m_child_printing_decider(child_sp->GetName())) + continue; + if (!any_children_printed) { + PrintChildrenPreamble(value_printed, summary_printed); + any_children_printed = true; + } + PrintChild(child_sp, curr_ptr_depth); + } + } + + if (any_children_printed) + PrintChildrenPostamble(print_dotdotdot); + else { + if (ShouldPrintEmptyBrackets(value_printed, summary_printed)) { + if (ShouldPrintValueObject()) + m_stream->PutCString(" {}\n"); + else + m_stream->EOL(); + } else + m_stream->EOL(); + } + } else if (ShouldPrintEmptyBrackets(value_printed, summary_printed)) { + // Aggregate, no children... + if (ShouldPrintValueObject()) { + // if it has a synthetic value, then don't print {}, the synthetic + // children are probably only being used to vend a value + if (GetMostSpecializedValue().DoesProvideSyntheticValue() || + !ShouldExpandEmptyAggregates()) + m_stream->PutCString("\n"); + else + m_stream->PutCString(" {}\n"); + } + } else { + if (ShouldPrintValueObject()) + m_stream->EOL(); + } +} + +bool ValueObjectPrinter::PrintChildrenOneLiner(bool hide_names) { + ValueObject &synth_valobj = GetValueObjectForChildrenGeneration(); + + bool print_dotdotdot = false; + auto num_children_or_err = GetMaxNumChildrenToPrint(print_dotdotdot); + if (!num_children_or_err) { + *m_stream << '<' << llvm::toString(num_children_or_err.takeError()) << '>'; + return true; + } + uint32_t num_children = *num_children_or_err; + + if (num_children) { + m_stream->PutChar('('); + + bool did_print_children = false; + for (uint32_t idx = 0; idx < num_children; ++idx) { + lldb::ValueObjectSP child_sp(synth_valobj.GetChildAtIndex(idx)); + if (child_sp) + child_sp = child_sp->GetQualifiedRepresentationIfAvailable( + m_options.m_use_dynamic, m_options.m_use_synthetic); + if (child_sp) { + if (m_options.m_child_printing_decider && + !m_options.m_child_printing_decider(child_sp->GetName())) + continue; + if (idx && did_print_children) + m_stream->PutCString(", "); + did_print_children = true; + if (!hide_names) { + const char *name = child_sp.get()->GetName().AsCString(); + if (name && *name) { + m_stream->PutCString(name); + m_stream->PutCString(" = "); + } + } + child_sp->DumpPrintableRepresentation( + *m_stream, ValueObject::eValueObjectRepresentationStyleSummary, + m_options.m_format, + ValueObject::PrintableRepresentationSpecialCases::eDisable); + } + } + + if (print_dotdotdot) + m_stream->PutCString(", ...)"); + else + m_stream->PutChar(')'); + } + return true; +} + +llvm::Error ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed, + bool summary_printed) { + auto error = PrintObjectDescriptionIfNeeded(value_printed, summary_printed); + if (error) + return error; + + ValueObject &valobj = GetMostSpecializedValue(); + + DumpValueObjectOptions::PointerDepth curr_ptr_depth = m_ptr_depth; + const bool print_children = ShouldPrintChildren(curr_ptr_depth); + const bool print_oneline = + (curr_ptr_depth.CanAllowExpansion() || m_options.m_show_types || + !m_options.m_allow_oneliner_mode || m_options.m_flat_output || + (m_options.m_pointer_as_array) || m_options.m_show_location) + ? false + : DataVisualization::ShouldPrintAsOneLiner(valobj); + if (print_children && IsInstancePointer()) { + uint64_t instance_ptr_value = valobj.GetValueAsUnsigned(0); + if (m_printed_instance_pointers->count(instance_ptr_value)) { + // We already printed this instance-is-pointer thing, so don't expand it. + m_stream->PutCString(" {...}\n"); + return llvm::Error::success(); + } else { + // Remember this guy for future reference. + m_printed_instance_pointers->emplace(instance_ptr_value); + } + } + + if (print_children) { + if (print_oneline) { + m_stream->PutChar(' '); + PrintChildrenOneLiner(false); + m_stream->EOL(); + } else + PrintChildren(value_printed, summary_printed, curr_ptr_depth); + } else if (HasReachedMaximumDepth() && IsAggregate() && + ShouldPrintValueObject()) { + m_stream->PutCString("{...}\n"); + // The maximum child depth has been reached. If `m_max_depth` is the default + // (i.e. the user has _not_ customized it), then lldb presents a warning to + // the user. The warning tells the user that the limit has been reached, but + // more importantly tells them how to expand the limit if desired. + if (m_options.m_max_depth_is_default) + valobj.GetTargetSP() + ->GetDebugger() + .GetCommandInterpreter() + .SetReachedMaximumDepth(); + } else + m_stream->EOL(); + return llvm::Error::success(); +} + +bool ValueObjectPrinter::HasReachedMaximumDepth() { + return m_curr_depth >= m_options.m_max_depth; +} + +bool ValueObjectPrinter::ShouldShowName() const { + if (m_curr_depth == 0) + return !m_options.m_hide_root_name && !m_options.m_hide_name; + return !m_options.m_hide_name; +} diff --git a/contrib/llvm-project/lldb/source/DataFormatters/VectorType.cpp b/contrib/llvm-project/lldb/source/DataFormatters/VectorType.cpp new file mode 100644 index 000000000000..19de204c2435 --- /dev/null +++ b/contrib/llvm-project/lldb/source/DataFormatters/VectorType.cpp @@ -0,0 +1,335 @@ +//===-- VectorType.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/DataFormatters/VectorType.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Target/Target.h" + +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +static CompilerType GetCompilerTypeForFormat(lldb::Format format, + CompilerType element_type, + TypeSystemSP type_system) { + lldbassert(type_system && "type_system needs to be not NULL"); + if (!type_system) + return {}; + + switch (format) { + case lldb::eFormatAddressInfo: + case lldb::eFormatPointer: + return type_system->GetBuiltinTypeForEncodingAndBitSize( + eEncodingUint, 8 * type_system->GetPointerByteSize()); + + case lldb::eFormatBoolean: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeBool); + + case lldb::eFormatBytes: + case lldb::eFormatBytesWithASCII: + case lldb::eFormatChar: + case lldb::eFormatCharArray: + case lldb::eFormatCharPrintable: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeChar); + + case lldb::eFormatComplex /* lldb::eFormatComplexFloat */: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloatComplex); + + case lldb::eFormatCString: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeChar) + .GetPointerType(); + + case lldb::eFormatFloat: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloat); + + case lldb::eFormatHex: + case lldb::eFormatHexUppercase: + case lldb::eFormatOctal: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeInt); + + case lldb::eFormatHexFloat: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloat); + + case lldb::eFormatUnicode16: + case lldb::eFormatUnicode32: + + case lldb::eFormatUnsigned: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeUnsignedInt); + + case lldb::eFormatVectorOfChar: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeChar); + + case lldb::eFormatVectorOfFloat32: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingIEEE754, + 32); + + case lldb::eFormatVectorOfFloat64: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingIEEE754, + 64); + + case lldb::eFormatVectorOfSInt16: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 16); + + case lldb::eFormatVectorOfSInt32: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 32); + + case lldb::eFormatVectorOfSInt64: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 64); + + case lldb::eFormatVectorOfSInt8: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 8); + + case lldb::eFormatVectorOfUInt128: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 128); + + case lldb::eFormatVectorOfUInt16: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 16); + + case lldb::eFormatVectorOfUInt32: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32); + + case lldb::eFormatVectorOfUInt64: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 64); + + case lldb::eFormatVectorOfUInt8: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8); + + case lldb::eFormatDefault: + return element_type; + + case lldb::eFormatBinary: + case lldb::eFormatComplexInteger: + case lldb::eFormatDecimal: + case lldb::eFormatEnum: + case lldb::eFormatInstruction: + case lldb::eFormatOSType: + case lldb::eFormatVoid: + default: + return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8); + } +} + +static lldb::Format GetItemFormatForFormat(lldb::Format format, + CompilerType element_type) { + switch (format) { + case lldb::eFormatVectorOfChar: + return lldb::eFormatChar; + + case lldb::eFormatVectorOfFloat32: + case lldb::eFormatVectorOfFloat64: + return lldb::eFormatFloat; + + case lldb::eFormatVectorOfSInt16: + case lldb::eFormatVectorOfSInt32: + case lldb::eFormatVectorOfSInt64: + case lldb::eFormatVectorOfSInt8: + return lldb::eFormatDecimal; + + case lldb::eFormatVectorOfUInt128: + case lldb::eFormatVectorOfUInt16: + case lldb::eFormatVectorOfUInt32: + case lldb::eFormatVectorOfUInt64: + case lldb::eFormatVectorOfUInt8: + return lldb::eFormatUnsigned; + + case lldb::eFormatBinary: + case lldb::eFormatComplexInteger: + case lldb::eFormatDecimal: + case lldb::eFormatEnum: + case lldb::eFormatInstruction: + case lldb::eFormatOSType: + case lldb::eFormatVoid: + return eFormatHex; + + case lldb::eFormatDefault: { + // special case the (default, char) combination to actually display as an + // integer value most often, you won't want to see the ASCII characters... + // (and if you do, eFormatChar is a keystroke away) + bool is_char = element_type.IsCharType(); + bool is_signed = false; + element_type.IsIntegerType(is_signed); + return is_char ? (is_signed ? lldb::eFormatDecimal : eFormatHex) : format; + } break; + + default: + return format; + } +} + +/// Calculates the number of elements stored in a container (with +/// element type 'container_elem_type') as if it had elements of type +/// 'element_type'. +/// +/// For example, a container of type +/// `uint8_t __attribute__((vector_size(16)))` has 16 elements. +/// But calling `CalculateNumChildren` with an 'element_type' +/// of `float` (4-bytes) will return `4` because we are interpreting +/// the byte-array as a `float32[]`. +/// +/// \param[in] container_elem_type The type of the elements stored +/// in the container we are calculating the children of. +/// +/// \param[in] num_elements Number of 'container_elem_type's our +/// container stores. +/// +/// \param[in] element_type The type of elements we interpret +/// container_type to contain for the purposes of calculating +/// the number of children. +/// +/// \returns The number of elements stored in a container of +/// type 'element_type'. Returns a std::nullopt if the +/// size of the container is not a multiple of 'element_type' +/// or if an error occurs. +static std::optional<size_t> +CalculateNumChildren(CompilerType container_elem_type, uint64_t num_elements, + CompilerType element_type) { + std::optional<uint64_t> container_elem_size = + container_elem_type.GetByteSize(/* exe_scope */ nullptr); + if (!container_elem_size) + return {}; + + auto container_size = *container_elem_size * num_elements; + + std::optional<uint64_t> element_size = + element_type.GetByteSize(/* exe_scope */ nullptr); + if (!element_size || !*element_size) + return {}; + + if (container_size % *element_size) + return {}; + + return container_size / *element_size; +} + +namespace lldb_private { +namespace formatters { + +class VectorTypeSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + VectorTypeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_child_type() {} + + ~VectorTypeSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_num_children; + } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + auto num_children_or_err = CalculateNumChildren(); + if (!num_children_or_err) + return ValueObjectConstResult::Create( + nullptr, Status(num_children_or_err.takeError())); + if (idx >= *num_children_or_err) + return {}; + std::optional<uint64_t> size = m_child_type.GetByteSize(nullptr); + if (!size) + return {}; + auto offset = idx * *size; + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + ValueObjectSP child_sp(m_backend.GetSyntheticChildAtOffset( + offset, m_child_type, true, ConstString(idx_name.GetString()))); + if (!child_sp) + return child_sp; + + child_sp->SetFormat(m_item_format); + + return child_sp; + } + + lldb::ChildCacheState Update() override { + m_parent_format = m_backend.GetFormat(); + CompilerType parent_type(m_backend.GetCompilerType()); + CompilerType element_type; + uint64_t num_elements; + parent_type.IsVectorType(&element_type, &num_elements); + m_child_type = ::GetCompilerTypeForFormat( + m_parent_format, element_type, + parent_type.GetTypeSystem().GetSharedPointer()); + m_num_children = + ::CalculateNumChildren(element_type, num_elements, m_child_type) + .value_or(0); + m_item_format = GetItemFormatForFormat(m_parent_format, m_child_type); + return lldb::ChildCacheState::eRefetch; + } + + bool MightHaveChildren() override { return true; } + + size_t GetIndexOfChildWithName(ConstString name) override { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; + } + +private: + lldb::Format m_parent_format = eFormatInvalid; + lldb::Format m_item_format = eFormatInvalid; + CompilerType m_child_type; + size_t m_num_children = 0; +}; + +} // namespace formatters +} // namespace lldb_private + +bool lldb_private::formatters::VectorTypeSummaryProvider( + ValueObject &valobj, Stream &s, const TypeSummaryOptions &) { + auto synthetic_children = + VectorTypeSyntheticFrontEndCreator(nullptr, valobj.GetSP()); + if (!synthetic_children) + return false; + + synthetic_children->Update(); + + s.PutChar('('); + bool first = true; + + size_t idx = 0, + len = synthetic_children->CalculateNumChildrenIgnoringErrors(); + + for (; idx < len; idx++) { + auto child_sp = synthetic_children->GetChildAtIndex(idx); + if (!child_sp) + continue; + child_sp = child_sp->GetQualifiedRepresentationIfAvailable( + lldb::eDynamicDontRunTarget, true); + + const char *child_value = child_sp->GetValueAsCString(); + if (child_value && *child_value) { + if (first) { + s.Printf("%s", child_value); + first = false; + } else { + s.Printf(", %s", child_value); + } + } + } + + s.PutChar(')'); + + return true; +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::VectorTypeSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return new VectorTypeSyntheticFrontEnd(valobj_sp); +} |