diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus')
37 files changed, 9290 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp new file mode 100644 index 000000000000..2c9b3c425397 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp @@ -0,0 +1,203 @@ +//===-- BlockPointer.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 "BlockPointer.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" +#include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.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/LLDBLog.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +class BlockPointerSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + BlockPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_block_struct_type() { + CompilerType block_pointer_type(m_backend.GetCompilerType()); + CompilerType function_pointer_type; + block_pointer_type.IsBlockPointerType(&function_pointer_type); + + TargetSP target_sp(m_backend.GetTargetSP()); + + if (!target_sp) { + return; + } + + auto type_system_or_err = target_sp->GetScratchTypeSystemForLanguage( + lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), std::move(err), + "Failed to get scratch TypeSystemClang: {0}"); + return; + } + + auto ts = block_pointer_type.GetTypeSystem(); + auto clang_ast_context = ts.dyn_cast_or_null<TypeSystemClang>(); + if (!clang_ast_context) + return; + + const char *const isa_name("__isa"); + const CompilerType isa_type = + clang_ast_context->GetBasicType(lldb::eBasicTypeObjCClass); + const char *const flags_name("__flags"); + const CompilerType flags_type = + clang_ast_context->GetBasicType(lldb::eBasicTypeInt); + const char *const reserved_name("__reserved"); + const CompilerType reserved_type = + clang_ast_context->GetBasicType(lldb::eBasicTypeInt); + const char *const FuncPtr_name("__FuncPtr"); + + m_block_struct_type = clang_ast_context->CreateStructForIdentifier( + llvm::StringRef(), {{isa_name, isa_type}, + {flags_name, flags_type}, + {reserved_name, reserved_type}, + {FuncPtr_name, function_pointer_type}}); + } + + ~BlockPointerSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override { + const bool omit_empty_base_classes = false; + return m_block_struct_type.GetNumChildren(omit_empty_base_classes, nullptr); + } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + if (!m_block_struct_type.IsValid()) { + return lldb::ValueObjectSP(); + } + + if (idx >= CalculateNumChildrenIgnoringErrors()) { + return lldb::ValueObjectSP(); + } + + const bool thread_and_frame_only_if_stopped = true; + ExecutionContext exe_ctx = m_backend.GetExecutionContextRef().Lock( + thread_and_frame_only_if_stopped); + const bool transparent_pointers = false; + const bool omit_empty_base_classes = false; + const bool ignore_array_bounds = false; + ValueObject *value_object = nullptr; + + std::string child_name; + uint32_t child_byte_size = 0; + int32_t child_byte_offset = 0; + uint32_t child_bitfield_bit_size = 0; + uint32_t child_bitfield_bit_offset = 0; + bool child_is_base_class = false; + bool child_is_deref_of_parent = false; + uint64_t language_flags = 0; + + auto child_type_or_err = m_block_struct_type.GetChildCompilerTypeAtIndex( + &exe_ctx, idx, transparent_pointers, omit_empty_base_classes, + ignore_array_bounds, child_name, child_byte_size, child_byte_offset, + child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, + child_is_deref_of_parent, value_object, language_flags); + if (!child_type_or_err) + return ValueObjectConstResult::Create( + exe_ctx.GetBestExecutionContextScope(), + Status(child_type_or_err.takeError())); + CompilerType child_type = *child_type_or_err; + + ValueObjectSP struct_pointer_sp = + m_backend.Cast(m_block_struct_type.GetPointerType()); + + if (!struct_pointer_sp) { + return lldb::ValueObjectSP(); + } + + Status err; + ValueObjectSP struct_sp = struct_pointer_sp->Dereference(err); + + if (!struct_sp || !err.Success()) { + return lldb::ValueObjectSP(); + } + + ValueObjectSP child_sp(struct_sp->GetSyntheticChildAtOffset( + child_byte_offset, child_type, true, + ConstString(child_name.c_str(), child_name.size()))); + + return child_sp; + } + + // return true if this object is now safe to use forever without ever + // updating again; the typical (and tested) answer here is 'false' + lldb::ChildCacheState Update() override { + return lldb::ChildCacheState::eRefetch; + } + + // maybe return false if the block pointer is, say, null + bool MightHaveChildren() override { return true; } + + size_t GetIndexOfChildWithName(ConstString name) override { + if (!m_block_struct_type.IsValid()) + return UINT32_MAX; + + const bool omit_empty_base_classes = false; + return m_block_struct_type.GetIndexOfChildWithName(name.AsCString(), + omit_empty_base_classes); + } + +private: + CompilerType m_block_struct_type; +}; + +} // namespace formatters +} // namespace lldb_private + +bool lldb_private::formatters::BlockPointerSummaryProvider( + ValueObject &valobj, Stream &s, const TypeSummaryOptions &) { + lldb_private::SyntheticChildrenFrontEnd *synthetic_children = + BlockPointerSyntheticFrontEndCreator(nullptr, valobj.GetSP()); + if (!synthetic_children) { + return false; + } + + synthetic_children->Update(); + + static const ConstString s_FuncPtr_name("__FuncPtr"); + + lldb::ValueObjectSP child_sp = synthetic_children->GetChildAtIndex( + synthetic_children->GetIndexOfChildWithName(s_FuncPtr_name)); + + if (!child_sp) { + return false; + } + + lldb::ValueObjectSP qualified_child_representation_sp = + child_sp->GetQualifiedRepresentationIfAvailable( + lldb::eDynamicDontRunTarget, true); + + const char *child_value = + qualified_child_representation_sp->GetValueAsCString(); + + s.Printf("%s", child_value); + + return true; +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::BlockPointerSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return new BlockPointerSyntheticFrontEnd(valobj_sp); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h new file mode 100644 index 000000000000..23f3f7b34b4f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h @@ -0,0 +1,25 @@ +//===-- BlockPointer.h ------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_BLOCKPOINTER_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_BLOCKPOINTER_H + +#include "lldb/lldb-forward.h" + +namespace lldb_private { +namespace formatters { +bool BlockPointerSummaryProvider(ValueObject &, Stream &, + const TypeSummaryOptions &); + +SyntheticChildrenFrontEnd * +BlockPointerSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_BLOCKPOINTER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp new file mode 100644 index 000000000000..06c827c2543f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -0,0 +1,1761 @@ +//===-- CPlusPlusLanguage.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 "CPlusPlusLanguage.h" + +#include <cctype> +#include <cstring> + +#include <functional> +#include <memory> +#include <mutex> +#include <set> + +#include "llvm/ADT/StringRef.h" +#include "llvm/Demangle/ItaniumDemangle.h" + +#include "lldb/Core/Mangled.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/DataFormatters/CXXFunctionPointer.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/VectorType.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" + +#include "BlockPointer.h" +#include "CPlusPlusNameParser.h" +#include "Coroutines.h" +#include "CxxStringTypes.h" +#include "Generic.h" +#include "LibCxx.h" +#include "LibCxxAtomic.h" +#include "LibCxxVariant.h" +#include "LibStdcpp.h" +#include "MSVCUndecoratedNameParser.h" +#include "lldb/lldb-enumerations.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +LLDB_PLUGIN_DEFINE(CPlusPlusLanguage) + +void CPlusPlusLanguage::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), "C++ Language", + CreateInstance); +} + +void CPlusPlusLanguage::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +bool CPlusPlusLanguage::SymbolNameFitsToLanguage(Mangled mangled) const { + const char *mangled_name = mangled.GetMangledName().GetCString(); + return mangled_name && CPlusPlusLanguage::IsCPPMangledName(mangled_name); +} + +ConstString CPlusPlusLanguage::GetDemangledFunctionNameWithoutArguments( + Mangled mangled) const { + const char *mangled_name_cstr = mangled.GetMangledName().GetCString(); + ConstString demangled_name = mangled.GetDemangledName(); + if (demangled_name && mangled_name_cstr && mangled_name_cstr[0]) { + if (mangled_name_cstr[0] == '_' && mangled_name_cstr[1] == 'Z' && + (mangled_name_cstr[2] != 'T' && // avoid virtual table, VTT structure, + // typeinfo structure, and typeinfo + // mangled_name + mangled_name_cstr[2] != 'G' && // avoid guard variables + mangled_name_cstr[2] != 'Z')) // named local entities (if we + // eventually handle eSymbolTypeData, + // we will want this back) + { + CPlusPlusLanguage::MethodName cxx_method(demangled_name); + if (!cxx_method.GetBasename().empty()) { + std::string shortname; + if (!cxx_method.GetContext().empty()) + shortname = cxx_method.GetContext().str() + "::"; + shortname += cxx_method.GetBasename().str(); + return ConstString(shortname); + } + } + } + if (demangled_name) + return demangled_name; + return mangled.GetMangledName(); +} + +// Static Functions + +Language *CPlusPlusLanguage::CreateInstance(lldb::LanguageType language) { + // Use plugin for C++ but not for Objective-C++ (which has its own plugin). + if (Language::LanguageIsCPlusPlus(language) && + language != eLanguageTypeObjC_plus_plus) + return new CPlusPlusLanguage(); + return nullptr; +} + +void CPlusPlusLanguage::MethodName::Clear() { + m_full.Clear(); + m_basename = llvm::StringRef(); + m_context = llvm::StringRef(); + m_arguments = llvm::StringRef(); + m_qualifiers = llvm::StringRef(); + m_return_type = llvm::StringRef(); + m_parsed = false; + m_parse_error = false; +} + +static bool ReverseFindMatchingChars(const llvm::StringRef &s, + const llvm::StringRef &left_right_chars, + size_t &left_pos, size_t &right_pos, + size_t pos = llvm::StringRef::npos) { + assert(left_right_chars.size() == 2); + left_pos = llvm::StringRef::npos; + const char left_char = left_right_chars[0]; + const char right_char = left_right_chars[1]; + pos = s.find_last_of(left_right_chars, pos); + if (pos == llvm::StringRef::npos || s[pos] == left_char) + return false; + right_pos = pos; + uint32_t depth = 1; + while (pos > 0 && depth > 0) { + pos = s.find_last_of(left_right_chars, pos); + if (pos == llvm::StringRef::npos) + return false; + if (s[pos] == left_char) { + if (--depth == 0) { + left_pos = pos; + return left_pos < right_pos; + } + } else if (s[pos] == right_char) { + ++depth; + } + } + return false; +} + +static bool IsTrivialBasename(const llvm::StringRef &basename) { + // Check that the basename matches with the following regular expression + // "^~?([A-Za-z_][A-Za-z_0-9]*)$" We are using a hand written implementation + // because it is significantly more efficient then using the general purpose + // regular expression library. + size_t idx = 0; + if (basename.starts_with('~')) + idx = 1; + + if (basename.size() <= idx) + return false; // Empty string or "~" + + if (!std::isalpha(basename[idx]) && basename[idx] != '_') + return false; // First character (after removing the possible '~'') isn't in + // [A-Za-z_] + + // Read all characters matching [A-Za-z_0-9] + ++idx; + while (idx < basename.size()) { + if (!std::isalnum(basename[idx]) && basename[idx] != '_') + break; + ++idx; + } + + // We processed all characters. It is a vaild basename. + return idx == basename.size(); +} + +/// Writes out the function name in 'full_name' to 'out_stream' +/// but replaces each argument type with the variable name +/// and the corresponding pretty-printed value +static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream, + char const *full_name, + ExecutionContextScope *exe_scope, + VariableList const &args) { + CPlusPlusLanguage::MethodName cpp_method{ConstString(full_name)}; + + if (!cpp_method.IsValid()) + return false; + + llvm::StringRef return_type = cpp_method.GetReturnType(); + if (!return_type.empty()) { + out_stream.PutCString(return_type); + out_stream.PutChar(' '); + } + + out_stream.PutCString(cpp_method.GetScopeQualifiedName()); + out_stream.PutChar('('); + + FormatEntity::PrettyPrintFunctionArguments(out_stream, args, exe_scope); + + out_stream.PutChar(')'); + + llvm::StringRef qualifiers = cpp_method.GetQualifiers(); + if (!qualifiers.empty()) { + out_stream.PutChar(' '); + out_stream.PutCString(qualifiers); + } + + return true; +} + +bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() { + // This method tries to parse simple method definitions which are presumably + // most comman in user programs. Definitions that can be parsed by this + // function don't have return types and templates in the name. + // A::B::C::fun(std::vector<T> &) const + size_t arg_start, arg_end; + llvm::StringRef full(m_full.GetCString()); + llvm::StringRef parens("()", 2); + if (ReverseFindMatchingChars(full, parens, arg_start, arg_end)) { + m_arguments = full.substr(arg_start, arg_end - arg_start + 1); + if (arg_end + 1 < full.size()) + m_qualifiers = full.substr(arg_end + 1).ltrim(); + + if (arg_start == 0) + return false; + size_t basename_end = arg_start; + size_t context_start = 0; + size_t context_end = full.rfind(':', basename_end); + if (context_end == llvm::StringRef::npos) + m_basename = full.substr(0, basename_end); + else { + if (context_start < context_end) + m_context = full.substr(context_start, context_end - 1 - context_start); + const size_t basename_begin = context_end + 1; + m_basename = full.substr(basename_begin, basename_end - basename_begin); + } + + if (IsTrivialBasename(m_basename)) { + return true; + } else { + // The C++ basename doesn't match our regular expressions so this can't + // be a valid C++ method, clear everything out and indicate an error + m_context = llvm::StringRef(); + m_basename = llvm::StringRef(); + m_arguments = llvm::StringRef(); + m_qualifiers = llvm::StringRef(); + m_return_type = llvm::StringRef(); + return false; + } + } + return false; +} + +void CPlusPlusLanguage::MethodName::Parse() { + if (!m_parsed && m_full) { + if (TrySimplifiedParse()) { + m_parse_error = false; + } else { + CPlusPlusNameParser parser(m_full.GetStringRef()); + if (auto function = parser.ParseAsFunctionDefinition()) { + m_basename = function->name.basename; + m_context = function->name.context; + m_arguments = function->arguments; + m_qualifiers = function->qualifiers; + m_return_type = function->return_type; + m_parse_error = false; + } else { + m_parse_error = true; + } + } + m_parsed = true; + } +} + +llvm::StringRef CPlusPlusLanguage::MethodName::GetBasename() { + if (!m_parsed) + Parse(); + return m_basename; +} + +llvm::StringRef CPlusPlusLanguage::MethodName::GetContext() { + if (!m_parsed) + Parse(); + return m_context; +} + +llvm::StringRef CPlusPlusLanguage::MethodName::GetArguments() { + if (!m_parsed) + Parse(); + return m_arguments; +} + +llvm::StringRef CPlusPlusLanguage::MethodName::GetQualifiers() { + if (!m_parsed) + Parse(); + return m_qualifiers; +} + +llvm::StringRef CPlusPlusLanguage::MethodName::GetReturnType() { + if (!m_parsed) + Parse(); + return m_return_type; +} + +std::string CPlusPlusLanguage::MethodName::GetScopeQualifiedName() { + if (!m_parsed) + Parse(); + if (m_context.empty()) + return std::string(m_basename); + + std::string res; + res += m_context; + res += "::"; + res += m_basename; + return res; +} + +llvm::StringRef +CPlusPlusLanguage::MethodName::GetBasenameNoTemplateParameters() { + llvm::StringRef basename = GetBasename(); + size_t arg_start, arg_end; + llvm::StringRef parens("<>", 2); + if (ReverseFindMatchingChars(basename, parens, arg_start, arg_end)) + return basename.substr(0, arg_start); + + return basename; +} + +bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) { + if (!m_parsed) + Parse(); + + // If we can't parse the incoming name, then just check that it contains path. + if (m_parse_error) + return m_full.GetStringRef().contains(path); + + llvm::StringRef identifier; + llvm::StringRef context; + std::string path_str = path.str(); + bool success = CPlusPlusLanguage::ExtractContextAndIdentifier( + path_str.c_str(), context, identifier); + if (!success) + return m_full.GetStringRef().contains(path); + + // Basename may include template arguments. + // E.g., + // GetBaseName(): func<int> + // identifier : func + // + // ...but we still want to account for identifiers with template parameter + // lists, e.g., when users set breakpoints on template specializations. + // + // E.g., + // GetBaseName(): func<uint32_t> + // identifier : func<int32_t*> + // + // Try to match the basename with or without template parameters. + if (GetBasename() != identifier && + GetBasenameNoTemplateParameters() != identifier) + return false; + + // Incoming path only had an identifier, so we match. + if (context.empty()) + return true; + // Incoming path has context but this method does not, no match. + if (m_context.empty()) + return false; + + llvm::StringRef haystack = m_context; + if (!haystack.consume_back(context)) + return false; + if (haystack.empty() || !isalnum(haystack.back())) + return true; + + return false; +} + +bool CPlusPlusLanguage::IsCPPMangledName(llvm::StringRef name) { + // FIXME!! we should really run through all the known C++ Language plugins + // and ask each one if this is a C++ mangled name + + Mangled::ManglingScheme scheme = Mangled::GetManglingScheme(name); + + if (scheme == Mangled::eManglingSchemeNone) + return false; + + return true; +} + +bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path, + ConstString demangled) const { + MethodName demangled_name(demangled); + return demangled_name.ContainsPath(path); +} + +bool CPlusPlusLanguage::ExtractContextAndIdentifier( + const char *name, llvm::StringRef &context, llvm::StringRef &identifier) { + if (MSVCUndecoratedNameParser::IsMSVCUndecoratedName(name)) + return MSVCUndecoratedNameParser::ExtractContextAndIdentifier(name, context, + identifier); + + CPlusPlusNameParser parser(name); + if (auto full_name = parser.ParseAsFullName()) { + identifier = full_name->basename; + context = full_name->context; + return true; + } + return false; +} + +namespace { +class NodeAllocator { + llvm::BumpPtrAllocator Alloc; + +public: + void reset() { Alloc.Reset(); } + + template <typename T, typename... Args> T *makeNode(Args &&... args) { + return new (Alloc.Allocate(sizeof(T), alignof(T))) + T(std::forward<Args>(args)...); + } + + void *allocateNodeArray(size_t sz) { + return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz, + alignof(llvm::itanium_demangle::Node *)); + } +}; + +template <typename Derived> +class ManglingSubstitutor + : public llvm::itanium_demangle::AbstractManglingParser<Derived, + NodeAllocator> { + using Base = + llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>; + +public: + ManglingSubstitutor() : Base(nullptr, nullptr) {} + + template <typename... Ts> + ConstString substitute(llvm::StringRef Mangled, Ts &&... Vals) { + this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...); + return substituteImpl(Mangled); + } + +protected: + void reset(llvm::StringRef Mangled) { + Base::reset(Mangled.begin(), Mangled.end()); + Written = Mangled.begin(); + Result.clear(); + Substituted = false; + } + + ConstString substituteImpl(llvm::StringRef Mangled) { + Log *log = GetLog(LLDBLog::Language); + if (this->parse() == nullptr) { + LLDB_LOG(log, "Failed to substitute mangling in {0}", Mangled); + return ConstString(); + } + if (!Substituted) + return ConstString(); + + // Append any trailing unmodified input. + appendUnchangedInput(); + LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result); + return ConstString(Result); + } + + void trySubstitute(llvm::StringRef From, llvm::StringRef To) { + if (!llvm::StringRef(currentParserPos(), this->numLeft()).starts_with(From)) + return; + + // We found a match. Append unmodified input up to this point. + appendUnchangedInput(); + + // And then perform the replacement. + Result += To; + Written += From.size(); + Substituted = true; + } + +private: + /// Input character until which we have constructed the respective output + /// already. + const char *Written = ""; + + llvm::SmallString<128> Result; + + /// Whether we have performed any substitutions. + bool Substituted = false; + + const char *currentParserPos() const { return this->First; } + + void appendUnchangedInput() { + Result += + llvm::StringRef(Written, std::distance(Written, currentParserPos())); + Written = currentParserPos(); + } +}; + +/// Given a mangled function `Mangled`, replace all the primitive function type +/// arguments of `Search` with type `Replace`. +class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> { + llvm::StringRef Search; + llvm::StringRef Replace; + +public: + void reset(llvm::StringRef Mangled, llvm::StringRef Search, + llvm::StringRef Replace) { + ManglingSubstitutor::reset(Mangled); + this->Search = Search; + this->Replace = Replace; + } + + llvm::itanium_demangle::Node *parseType() { + trySubstitute(Search, Replace); + return ManglingSubstitutor::parseType(); + } +}; + +class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> { +public: + llvm::itanium_demangle::Node * + parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) { + trySubstitute("C1", "C2"); + trySubstitute("D1", "D2"); + return ManglingSubstitutor::parseCtorDtorName(SoFar, State); + } +}; +} // namespace + +std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings( + const ConstString mangled_name) const { + std::vector<ConstString> alternates; + + /// Get a basic set of alternative manglings for the given symbol `name`, by + /// making a few basic possible substitutions on basic types, storage duration + /// and `const`ness for the given symbol. The output parameter `alternates` + /// is filled with a best-guess, non-exhaustive set of different manglings + /// for the given name. + + // Maybe we're looking for a const symbol but the debug info told us it was + // non-const... + if (!strncmp(mangled_name.GetCString(), "_ZN", 3) && + strncmp(mangled_name.GetCString(), "_ZNK", 4)) { + std::string fixed_scratch("_ZNK"); + fixed_scratch.append(mangled_name.GetCString() + 3); + alternates.push_back(ConstString(fixed_scratch)); + } + + // Maybe we're looking for a static symbol but we thought it was global... + if (!strncmp(mangled_name.GetCString(), "_Z", 2) && + strncmp(mangled_name.GetCString(), "_ZL", 3)) { + std::string fixed_scratch("_ZL"); + fixed_scratch.append(mangled_name.GetCString() + 2); + alternates.push_back(ConstString(fixed_scratch)); + } + + TypeSubstitutor TS; + // `char` is implementation defined as either `signed` or `unsigned`. As a + // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed + // char, 'h'-unsigned char. If we're looking for symbols with a signed char + // parameter, try finding matches which have the general case 'c'. + if (ConstString char_fixup = + TS.substitute(mangled_name.GetStringRef(), "a", "c")) + alternates.push_back(char_fixup); + + // long long parameter mangling 'x', may actually just be a long 'l' argument + if (ConstString long_fixup = + TS.substitute(mangled_name.GetStringRef(), "x", "l")) + alternates.push_back(long_fixup); + + // unsigned long long parameter mangling 'y', may actually just be unsigned + // long 'm' argument + if (ConstString ulong_fixup = + TS.substitute(mangled_name.GetStringRef(), "y", "m")) + alternates.push_back(ulong_fixup); + + if (ConstString ctor_fixup = + CtorDtorSubstitutor().substitute(mangled_name.GetStringRef())) + alternates.push_back(ctor_fixup); + + return alternates; +} + +ConstString CPlusPlusLanguage::FindBestAlternateFunctionMangledName( + const Mangled mangled, const SymbolContext &sym_ctx) const { + ConstString demangled = mangled.GetDemangledName(); + if (!demangled) + return ConstString(); + + CPlusPlusLanguage::MethodName cpp_name(demangled); + std::string scope_qualified_name = cpp_name.GetScopeQualifiedName(); + + if (!scope_qualified_name.size()) + return ConstString(); + + if (!sym_ctx.module_sp) + return ConstString(); + + lldb_private::SymbolFile *sym_file = sym_ctx.module_sp->GetSymbolFile(); + if (!sym_file) + return ConstString(); + + std::vector<ConstString> alternates; + sym_file->GetMangledNamesForFunction(scope_qualified_name, alternates); + + std::vector<ConstString> param_and_qual_matches; + std::vector<ConstString> param_matches; + for (size_t i = 0; i < alternates.size(); i++) { + ConstString alternate_mangled_name = alternates[i]; + Mangled mangled(alternate_mangled_name); + ConstString demangled = mangled.GetDemangledName(); + + CPlusPlusLanguage::MethodName alternate_cpp_name(demangled); + if (!cpp_name.IsValid()) + continue; + + if (alternate_cpp_name.GetArguments() == cpp_name.GetArguments()) { + if (alternate_cpp_name.GetQualifiers() == cpp_name.GetQualifiers()) + param_and_qual_matches.push_back(alternate_mangled_name); + else + param_matches.push_back(alternate_mangled_name); + } + } + + if (param_and_qual_matches.size()) + return param_and_qual_matches[0]; // It is assumed that there will be only + // one! + else if (param_matches.size()) + return param_matches[0]; // Return one of them as a best match + else + return ConstString(); +} + +static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { + if (!cpp_category_sp) + return; + + TypeSummaryImpl::Flags stl_summary_flags; + stl_summary_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderASCII, + "std::string summary provider", "^std::__[[:alnum:]]+::string$", + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderASCII, + "std::string summary provider", + "^std::__[[:alnum:]]+::basic_string<char, " + "std::__[[:alnum:]]+::char_traits<char>, " + "std::__[[:alnum:]]+::allocator<char> >$", + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderASCII, + "std::string summary provider", + "^std::__[[:alnum:]]+::basic_string<unsigned char, " + "std::__[[:alnum:]]+::char_traits<unsigned char>, " + "std::__[[:alnum:]]+::allocator<unsigned char> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderUTF16, + "std::u16string summary provider", + "^std::__[[:alnum:]]+::basic_string<char16_t, " + "std::__[[:alnum:]]+::char_traits<char16_t>, " + "std::__[[:alnum:]]+::allocator<char16_t> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderUTF32, + "std::u32string summary provider", + "^std::__[[:alnum:]]+::basic_string<char32_t, " + "std::__[[:alnum:]]+::char_traits<char32_t>, " + "std::__[[:alnum:]]+::allocator<char32_t> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringSummaryProvider, + "std::wstring summary provider", + "^std::__[[:alnum:]]+::wstring$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringSummaryProvider, + "std::wstring summary provider", + "^std::__[[:alnum:]]+::basic_string<wchar_t, " + "std::__[[:alnum:]]+::char_traits<wchar_t>, " + "std::__[[:alnum:]]+::allocator<wchar_t> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderASCII, + "std::string_view summary provider", + "^std::__[[:alnum:]]+::string_view$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderASCII, + "std::string_view summary provider", + "^std::__[[:alnum:]]+::basic_string_view<char, " + "std::__[[:alnum:]]+::char_traits<char> >$", + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderASCII, + "std::string_view summary provider", + "^std::__[[:alnum:]]+::basic_string_view<unsigned char, " + "std::__[[:alnum:]]+::char_traits<unsigned char> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16, + "std::u16string_view summary provider", + "^std::__[[:alnum:]]+::basic_string_view<char16_t, " + "std::__[[:alnum:]]+::char_traits<char16_t> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32, + "std::u32string_view summary provider", + "^std::__[[:alnum:]]+::basic_string_view<char32_t, " + "std::__[[:alnum:]]+::char_traits<char32_t> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringViewSummaryProvider, + "std::wstring_view summary provider", + "^std::__[[:alnum:]]+::wstring_view$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringViewSummaryProvider, + "std::wstring_view summary provider", + "^std::__[[:alnum:]]+::basic_string_view<wchar_t, " + "std::__[[:alnum:]]+::char_traits<wchar_t> >$", + stl_summary_flags, true); + + SyntheticChildren::Flags stl_synth_flags; + stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( + false); + SyntheticChildren::Flags stl_deref_flags = stl_synth_flags; + stl_deref_flags.SetFrontEndWantsDereference(); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxBitsetSyntheticFrontEndCreator, + "libc++ std::bitset synthetic children", + "^std::__[[:alnum:]]+::bitset<.+>$", stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator, + "libc++ std::vector synthetic children", + "^std::__[[:alnum:]]+::vector<.+>$", stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdValarraySyntheticFrontEndCreator, + "libc++ std::valarray synthetic children", + "^std::__[[:alnum:]]+::valarray<.+>$", stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEndCreator, + "libc++ std::slice_array synthetic children", + "^std::__[[:alnum:]]+::slice_array<.+>$", stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEndCreator, + "libc++ synthetic children for the valarray proxy arrays", + "^std::__[[:alnum:]]+::(gslice|mask|indirect)_array<.+>$", + stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdForwardListSyntheticFrontEndCreator, + "libc++ std::forward_list synthetic children", + "^std::__[[:alnum:]]+::forward_list<.+>$", stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator, + "libc++ std::list synthetic children", + // A POSIX variant of: "^std::__(?!cxx11:)[[:alnum:]]+::list<.+>$" + // so that it does not clash with: "^std::(__cxx11::)?list<.+>$" + "^std::__([A-Zabd-z0-9]|cx?[A-Za-wyz0-9]|cxx1?[A-Za-z02-9]|" + "cxx11[[:alnum:]])[[:alnum:]]*::list<.+>$", + stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::map synthetic children", "^std::__[[:alnum:]]+::map<.+> >$", + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::set synthetic children", "^std::__[[:alnum:]]+::set<.+> >$", + stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::multiset synthetic children", + "^std::__[[:alnum:]]+::multiset<.+> >$", stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::multimap synthetic children", + "^std::__[[:alnum:]]+::multimap<.+> >$", stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator, + "libc++ std::unordered containers synthetic children", + "^std::__[[:alnum:]]+::unordered_(multi)?(map|set)<.+> >$", + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator, + "libc++ std::initializer_list synthetic children", + "^std::initializer_list<.+>$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxQueueFrontEndCreator, + "libc++ std::queue synthetic children", + "^std::__[[:alnum:]]+::queue<.+>$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxTupleFrontEndCreator, + "libc++ std::tuple synthetic children", + "^std::__[[:alnum:]]+::tuple<.*>$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxOptionalSyntheticFrontEndCreator, + "libc++ std::optional synthetic children", + "^std::__[[:alnum:]]+::optional<.+>$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxVariantFrontEndCreator, + "libc++ std::variant synthetic children", + "^std::__[[:alnum:]]+::variant<.+>$", stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator, + "libc++ std::atomic synthetic children", + "^std::__[[:alnum:]]+::atomic<.+>$", stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdSpanSyntheticFrontEndCreator, + "libc++ std::span synthetic children", "^std::__[[:alnum:]]+::span<.+>$", + stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdRangesRefViewSyntheticFrontEndCreator, + "libc++ std::ranges::ref_view synthetic children", + "^std::__[[:alnum:]]+::ranges::ref_view<.+>$", stl_deref_flags, true); + + cpp_category_sp->AddTypeSynthetic( + "^std::__[[:alnum:]]+::deque<.+>$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.libcxx.stddeque_SynthProvider"))); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator, + "shared_ptr synthetic children", "^std::__[[:alnum:]]+::shared_ptr<.+>$", + stl_synth_flags, true); + + static constexpr const char *const libcxx_std_unique_ptr_regex = + "^std::__[[:alnum:]]+::unique_ptr<.+>$"; + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator, + "unique_ptr synthetic children", libcxx_std_unique_ptr_regex, + stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator, + "weak_ptr synthetic children", "^std::__[[:alnum:]]+::weak_ptr<.+>$", + stl_synth_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxFunctionSummaryProvider, + "libc++ std::function summary provider", + "^std::__[[:alnum:]]+::function<.+>$", stl_summary_flags, true); + + static constexpr const char *const libcxx_std_coroutine_handle_regex = + "^std::__[[:alnum:]]+::coroutine_handle<.+>$"; + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator, + "coroutine_handle synthetic children", libcxx_std_coroutine_handle_regex, + stl_deref_flags, true); + + stl_summary_flags.SetDontShowChildren(false); + stl_summary_flags.SetSkipPointers(false); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::bitset summary provider", + "^std::__[[:alnum:]]+::bitset<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::vector summary provider", + "^std::__[[:alnum:]]+::vector<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::valarray summary provider", + "^std::__[[:alnum:]]+::valarray<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStdSliceArraySummaryProvider, + "libc++ std::slice_array summary provider", + "^std::__[[:alnum:]]+::slice_array<.+>$", stl_summary_flags, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ summary provider for the valarray proxy arrays", + "^std::__[[:alnum:]]+::(gslice|mask|indirect)_array<.+>$", + stl_summary_flags, true); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::list summary provider", + "^std::__[[:alnum:]]+::forward_list<.+>$", stl_summary_flags, true); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::list summary provider", + // A POSIX variant of: "^std::__(?!cxx11:)[[:alnum:]]+::list<.+>$" + // so that it does not clash with: "^std::(__cxx11::)?list<.+>$" + "^std::__([A-Zabd-z0-9]|cx?[A-Za-wyz0-9]|cxx1?[A-Za-z02-9]|" + "cxx11[[:alnum:]])[[:alnum:]]*::list<.+>$", + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::map summary provider", + "^std::__[[:alnum:]]+::map<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::deque summary provider", + "^std::__[[:alnum:]]+::deque<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::queue summary provider", + "^std::__[[:alnum:]]+::queue<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::set summary provider", + "^std::__[[:alnum:]]+::set<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::multiset summary provider", + "^std::__[[:alnum:]]+::multiset<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::multimap summary provider", + "^std::__[[:alnum:]]+::multimap<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::unordered containers summary provider", + "^std::__[[:alnum:]]+::unordered_(multi)?(map|set)<.+> >$", + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, LibcxxContainerSummaryProvider, + "libc++ std::tuple summary provider", + "^std::__[[:alnum:]]+::tuple<.*>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibCxxAtomicSummaryProvider, + "libc++ std::atomic summary provider", + "^std::__[[:alnum:]]+::atomic<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::GenericOptionalSummaryProvider, + "libc++ std::optional summary provider", + "^std::__[[:alnum:]]+::optional<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxVariantSummaryProvider, + "libc++ std::variant summary provider", + "^std::__[[:alnum:]]+::variant<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::span summary provider", + "^std::__[[:alnum:]]+::span<.+>$", stl_summary_flags, true); + + stl_summary_flags.SetSkipPointers(true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxSmartPointerSummaryProvider, + "libc++ std::shared_ptr summary provider", + "^std::__[[:alnum:]]+::shared_ptr<.+>$", stl_summary_flags, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxSmartPointerSummaryProvider, + "libc++ std::weak_ptr summary provider", + "^std::__[[:alnum:]]+::weak_ptr<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxUniquePointerSummaryProvider, + "libc++ std::unique_ptr summary provider", + libcxx_std_unique_ptr_regex, stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::StdlibCoroutineHandleSummaryProvider, + "libc++ std::coroutine_handle summary provider", + libcxx_std_coroutine_handle_regex, stl_summary_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator, + "std::vector iterator synthetic children", + "^std::__[[:alnum:]]+::__wrap_iter<.+>$", stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator, + "std::map iterator synthetic children", + "^std::__[[:alnum:]]+::__map_(const_)?iterator<.+>$", stl_synth_flags, + true); + + AddCXXSynthetic(cpp_category_sp, + lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEndCreator, + "std::unordered_map iterator synthetic children", + "^std::__[[:alnum:]]+::__hash_map_(const_)?iterator<.+>$", + stl_synth_flags, true); + // Chrono duration typedefs + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::nanoseconds", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} ns"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::microseconds", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} µs"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::milliseconds", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} ms"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::seconds", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} s"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::minutes", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__rep_} min"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::hours", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} h"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::days", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__rep_} days"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::weeks", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__rep_} weeks"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::months", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__rep_} months"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::years", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__rep_} years"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::seconds", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} s"))); + + // Chrono time point types + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider, + "libc++ std::chrono::sys_seconds summary provider", + "^std::__[[:alnum:]]+::chrono::time_point<" + "std::__[[:alnum:]]+::chrono::system_clock, " + "std::__[[:alnum:]]+::chrono::duration<.*, " + "std::__[[:alnum:]]+::ratio<1, 1> " + "> >$", + eTypeOptionHideChildren | eTypeOptionHideValue | + eTypeOptionCascade, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider, + "libc++ std::chrono::sys_seconds summary provider", + "^std::__[[:alnum:]]+::chrono::time_point<" + "std::__[[:alnum:]]+::chrono::system_clock, " + "std::__[[:alnum:]]+::chrono::duration<int, " + "std::__[[:alnum:]]+::ratio<86400, 1> " + "> >$", + eTypeOptionHideChildren | eTypeOptionHideValue | + eTypeOptionCascade, + true); + + AddCXXSummary( + cpp_category_sp, + lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider, + "libc++ std::chrono::local_seconds summary provider", + "^std::__[[:alnum:]]+::chrono::time_point<" + "std::__[[:alnum:]]+::chrono::local_t, " + "std::__[[:alnum:]]+::chrono::duration<.*, " + "std::__[[:alnum:]]+::ratio<1, 1> " + "> >$", + eTypeOptionHideChildren | eTypeOptionHideValue | eTypeOptionCascade, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider, + "libc++ std::chrono::local_seconds summary provider", + "^std::__[[:alnum:]]+::chrono::time_point<" + "std::__[[:alnum:]]+::chrono::local_t, " + "std::__[[:alnum:]]+::chrono::duration<int, " + "std::__[[:alnum:]]+::ratio<86400, 1> " + "> >$", + eTypeOptionHideChildren | eTypeOptionHideValue | + eTypeOptionCascade, + true); + + // Chrono calendar types + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::day$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "day=${var.__d_%u}"))); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxChronoMonthSummaryProvider, + "libc++ std::chrono::month summary provider", + "^std::__[[:alnum:]]+::chrono::month$", + eTypeOptionHideChildren | eTypeOptionHideValue, true); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::year$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "year=${var.__y_}"))); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider, + "libc++ std::chrono::weekday summary provider", + "^std::__[[:alnum:]]+::chrono::weekday$", + eTypeOptionHideChildren | eTypeOptionHideValue, true); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::weekday_indexed$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, + "${var.__wd_} index=${var.__idx_%u}"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::weekday_last$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__wd_} index=last"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::month_day$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__m_} ${var.__d_}"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::month_day_last$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__m_} day=last"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::month_weekday$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__m_} ${var.__wdi_}"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::month_weekday_last$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__m_} ${var.__wdl_}"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::year_month$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__y_} ${var.__m_}"))); + + AddCXXSummary( + cpp_category_sp, + lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider, + "libc++ std::chrono::year_month_day summary provider", + "^std::__[[:alnum:]]+::chrono::year_month_day$", + eTypeOptionHideChildren | eTypeOptionHideValue, true); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::year_month_day_last$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__y_} ${var.__mdl_}"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::year_month_weekday$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, + "${var.__y_} ${var.__m_} ${var.__wdi_}"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::year_month_weekday_last$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, + "${var.__y_} ${var.__m_} ${var.__wdl_}"))); +} + +static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { + if (!cpp_category_sp) + return; + + TypeSummaryImpl::Flags stl_summary_flags; + stl_summary_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + lldb::TypeSummaryImplSP std_string_summary_sp( + new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p}")); + + lldb::TypeSummaryImplSP cxx11_string_summary_sp(new CXXFunctionSummaryFormat( + stl_summary_flags, LibStdcppStringSummaryProvider, + "libstdc++ c++11 std::string summary provider")); + lldb::TypeSummaryImplSP cxx11_wstring_summary_sp(new CXXFunctionSummaryFormat( + stl_summary_flags, LibStdcppWStringSummaryProvider, + "libstdc++ c++11 std::wstring summary provider")); + + cpp_category_sp->AddTypeSummary("std::string", eFormatterMatchExact, + std_string_summary_sp); + cpp_category_sp->AddTypeSummary("std::basic_string<char>", + eFormatterMatchExact, std_string_summary_sp); + cpp_category_sp->AddTypeSummary( + "std::basic_string<char,std::char_traits<char>,std::allocator<char> >", + eFormatterMatchExact, std_string_summary_sp); + cpp_category_sp->AddTypeSummary( + "std::basic_string<char, std::char_traits<char>, std::allocator<char> >", + eFormatterMatchExact, std_string_summary_sp); + + cpp_category_sp->AddTypeSummary("std::__cxx11::string", eFormatterMatchExact, + cxx11_string_summary_sp); + cpp_category_sp->AddTypeSummary( + "std::__cxx11::basic_string<char, std::char_traits<char>, " + "std::allocator<char> >", + eFormatterMatchExact, cxx11_string_summary_sp); + cpp_category_sp->AddTypeSummary("std::__cxx11::basic_string<unsigned char, " + "std::char_traits<unsigned char>, " + "std::allocator<unsigned char> >", + eFormatterMatchExact, + cxx11_string_summary_sp); + + // making sure we force-pick the summary for printing wstring (_M_p is a + // wchar_t*) + lldb::TypeSummaryImplSP std_wstring_summary_sp( + new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p%S}")); + + cpp_category_sp->AddTypeSummary("std::wstring", eFormatterMatchExact, + std_wstring_summary_sp); + cpp_category_sp->AddTypeSummary("std::basic_string<wchar_t>", + eFormatterMatchExact, std_wstring_summary_sp); + cpp_category_sp->AddTypeSummary("std::basic_string<wchar_t,std::char_traits<" + "wchar_t>,std::allocator<wchar_t> >", + eFormatterMatchExact, std_wstring_summary_sp); + cpp_category_sp->AddTypeSummary( + "std::basic_string<wchar_t, std::char_traits<wchar_t>, " + "std::allocator<wchar_t> >", + eFormatterMatchExact, std_wstring_summary_sp); + + cpp_category_sp->AddTypeSummary("std::__cxx11::wstring", eFormatterMatchExact, + cxx11_wstring_summary_sp); + cpp_category_sp->AddTypeSummary( + "std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, " + "std::allocator<wchar_t> >", + eFormatterMatchExact, cxx11_wstring_summary_sp); + + SyntheticChildren::Flags stl_synth_flags; + stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( + false); + SyntheticChildren::Flags stl_deref_flags = stl_synth_flags; + stl_deref_flags.SetFrontEndWantsDereference(); + + cpp_category_sp->AddTypeSynthetic( + "^std::vector<.+>(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::map<.+> >(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::deque<.+>(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::set<.+> >(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::multimap<.+> >(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::multiset<.+> >(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::unordered_(multi)?(map|set)<.+> >$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdUnorderedMapSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::(__cxx11::)?list<.+>(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::(__cxx11::)?forward_list<.+>(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::variant<.+>$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider"))); + + stl_summary_flags.SetDontShowChildren(false); + stl_summary_flags.SetSkipPointers(false); + cpp_category_sp->AddTypeSummary("^std::bitset<.+>(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::vector<.+>(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::map<.+> >(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::set<.+> >(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::deque<.+>(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::multimap<.+> >(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::multiset<.+> >(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::unordered_(multi)?(map|set)<.+> >$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::(__cxx11::)?list<.+>(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary( + "^std::(__cxx11::)?forward_list<.+>(( )?&)?$", eFormatterMatchRegex, + TypeSummaryImplSP(new ScriptSummaryFormat( + stl_summary_flags, + "lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider"))); + cpp_category_sp->AddTypeSummary( + "^std::variant<.+>$", eFormatterMatchRegex, + TypeSummaryImplSP(new ScriptSummaryFormat( + stl_summary_flags, + "lldb.formatters.cpp.gnu_libstdcpp.VariantSummaryProvider"))); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator, + "std::vector iterator synthetic children", + "^__gnu_cxx::__normal_iterator<.+>$", stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator, + "std::map iterator synthetic children", "^std::_Rb_tree_iterator<.+>$", + stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator, + "std::unique_ptr synthetic children", "^std::unique_ptr<.+>(( )?&)?$", + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator, + "std::shared_ptr synthetic children", "^std::shared_ptr<.+>(( )?&)?$", + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator, + "std::weak_ptr synthetic children", "^std::weak_ptr<.+>(( )?&)?$", + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator, + "std::tuple synthetic children", "^std::tuple<.+>(( )?&)?$", + stl_synth_flags, true); + + static constexpr const char *const libstdcpp_std_coroutine_handle_regex = + "^std::coroutine_handle<.+>(( )?&)?$"; + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator, + "std::coroutine_handle synthetic children", + libstdcpp_std_coroutine_handle_regex, stl_deref_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppBitsetSyntheticFrontEndCreator, + "std::bitset synthetic child", "^std::bitset<.+>(( )?&)?$", + stl_deref_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppOptionalSyntheticFrontEndCreator, + "std::optional synthetic child", "^std::optional<.+>(( )?&)?$", + stl_deref_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibStdcppUniquePointerSummaryProvider, + "libstdc++ std::unique_ptr summary provider", + "^std::unique_ptr<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibStdcppSmartPointerSummaryProvider, + "libstdc++ std::shared_ptr summary provider", + "^std::shared_ptr<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibStdcppSmartPointerSummaryProvider, + "libstdc++ std::weak_ptr summary provider", + "^std::weak_ptr<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::StdlibCoroutineHandleSummaryProvider, + "libstdc++ std::coroutine_handle summary provider", + libstdcpp_std_coroutine_handle_regex, stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::GenericOptionalSummaryProvider, + "libstd++ std::optional summary provider", + "^std::optional<.+>(( )?&)?$", stl_summary_flags, true); +} + +static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { + if (!cpp_category_sp) + return; + + 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); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char8StringSummaryProvider, + "char8_t * summary provider", "char8_t *", string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char8StringSummaryProvider, + "char8_t [] summary provider", "char8_t ?\\[[0-9]+\\]", + string_array_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16StringSummaryProvider, + "char16_t * summary provider", "char16_t *", string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16StringSummaryProvider, + "char16_t [] summary provider", "char16_t ?\\[[0-9]+\\]", + string_array_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char32StringSummaryProvider, + "char32_t * summary provider", "char32_t *", string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char32StringSummaryProvider, + "char32_t [] summary provider", "char32_t ?\\[[0-9]+\\]", + string_array_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::WCharStringSummaryProvider, + "wchar_t * summary provider", "wchar_t *", string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::WCharStringSummaryProvider, + "wchar_t * summary provider", "wchar_t ?\\[[0-9]+\\]", + string_array_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16StringSummaryProvider, + "unichar * summary provider", "unichar *", string_flags); + + TypeSummaryImpl::Flags widechar_flags; + widechar_flags.SetDontShowValue(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetCascades(true) + .SetDontShowChildren(true) + .SetHideItemNames(true) + .SetShowMembersOneLiner(false); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::Char8SummaryProvider, + "char8_t summary provider", "char8_t", widechar_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16SummaryProvider, + "char16_t summary provider", "char16_t", widechar_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char32SummaryProvider, + "char32_t summary provider", "char32_t", widechar_flags); + AddCXXSummary(cpp_category_sp, lldb_private::formatters::WCharSummaryProvider, + "wchar_t summary provider", "wchar_t", widechar_flags); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16SummaryProvider, + "unichar summary provider", "unichar", widechar_flags); +} + +std::unique_ptr<Language::TypeScavenger> CPlusPlusLanguage::GetTypeScavenger() { + class CPlusPlusTypeScavenger : public Language::ImageListTypeScavenger { + public: + CompilerType AdjustForInclusion(CompilerType &candidate) override { + LanguageType lang_type(candidate.GetMinimumLanguage()); + if (!Language::LanguageIsC(lang_type) && + !Language::LanguageIsCPlusPlus(lang_type)) + return CompilerType(); + if (candidate.IsTypedefType()) + return candidate.GetTypedefedType(); + return candidate; + } + }; + + return std::unique_ptr<TypeScavenger>(new CPlusPlusTypeScavenger()); +} + +lldb::TypeCategoryImplSP CPlusPlusLanguage::GetFormatters() { + static llvm::once_flag g_initialize; + static TypeCategoryImplSP g_category; + + llvm::call_once(g_initialize, [this]() -> void { + DataVisualization::Categories::GetCategory(ConstString(GetPluginName()), + g_category); + if (g_category) { + LoadLibStdcppFormatters(g_category); + LoadLibCxxFormatters(g_category); + LoadSystemFormatters(g_category); + } + }); + return g_category; +} + +HardcodedFormatters::HardcodedSummaryFinder +CPlusPlusLanguage::GetHardcodedSummaries() { + static llvm::once_flag g_initialize; + static ConstString g_vectortypes("VectorTypes"); + static HardcodedFormatters::HardcodedSummaryFinder g_formatters; + + llvm::call_once(g_initialize, []() -> void { + g_formatters.push_back( + [](lldb_private::ValueObject &valobj, lldb::DynamicValueType, + FormatManager &) -> TypeSummaryImpl::SharedPointer { + static CXXFunctionSummaryFormat::SharedPointer formatter_sp( + new CXXFunctionSummaryFormat( + TypeSummaryImpl::Flags(), + lldb_private::formatters::CXXFunctionPointerSummaryProvider, + "Function pointer summary provider")); + if (CompilerType CT = valobj.GetCompilerType(); + CT.IsFunctionPointerType() || CT.IsMemberFunctionPointerType() || + valobj.GetValueType() == lldb::eValueTypeVTableEntry) { + return formatter_sp; + } + return nullptr; + }); + g_formatters.push_back( + [](lldb_private::ValueObject &valobj, lldb::DynamicValueType, + FormatManager &fmt_mgr) -> TypeSummaryImpl::SharedPointer { + static CXXFunctionSummaryFormat::SharedPointer formatter_sp( + new CXXFunctionSummaryFormat( + TypeSummaryImpl::Flags() + .SetCascades(true) + .SetDontShowChildren(true) + .SetHideItemNames(true) + .SetShowMembersOneLiner(true) + .SetSkipPointers(true) + .SetSkipReferences(false), + lldb_private::formatters::VectorTypeSummaryProvider, + "vector_type pointer summary provider")); + if (valobj.GetCompilerType().IsVectorType()) { + if (fmt_mgr.GetCategory(g_vectortypes)->IsEnabled()) + return formatter_sp; + } + return nullptr; + }); + g_formatters.push_back( + [](lldb_private::ValueObject &valobj, lldb::DynamicValueType, + FormatManager &fmt_mgr) -> TypeSummaryImpl::SharedPointer { + static CXXFunctionSummaryFormat::SharedPointer formatter_sp( + new CXXFunctionSummaryFormat( + TypeSummaryImpl::Flags() + .SetCascades(true) + .SetDontShowChildren(true) + .SetHideItemNames(true) + .SetShowMembersOneLiner(true) + .SetSkipPointers(true) + .SetSkipReferences(false), + lldb_private::formatters::BlockPointerSummaryProvider, + "block pointer summary provider")); + if (valobj.GetCompilerType().IsBlockPointerType()) { + return formatter_sp; + } + return nullptr; + }); + }); + + return g_formatters; +} + +HardcodedFormatters::HardcodedSyntheticFinder +CPlusPlusLanguage::GetHardcodedSynthetics() { + static llvm::once_flag g_initialize; + static ConstString g_vectortypes("VectorTypes"); + static HardcodedFormatters::HardcodedSyntheticFinder g_formatters; + + llvm::call_once(g_initialize, []() -> void { + g_formatters.push_back([](lldb_private::ValueObject &valobj, + lldb::DynamicValueType, FormatManager &fmt_mgr) + -> SyntheticChildren::SharedPointer { + static CXXSyntheticChildren::SharedPointer formatter_sp( + new CXXSyntheticChildren( + SyntheticChildren::Flags() + .SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(true) + .SetNonCacheable(true), + "vector_type synthetic children", + lldb_private::formatters::VectorTypeSyntheticFrontEndCreator)); + if (valobj.GetCompilerType().IsVectorType()) { + if (fmt_mgr.GetCategory(g_vectortypes)->IsEnabled()) + return formatter_sp; + } + return nullptr; + }); + g_formatters.push_back([](lldb_private::ValueObject &valobj, + lldb::DynamicValueType, FormatManager &fmt_mgr) + -> SyntheticChildren::SharedPointer { + static CXXSyntheticChildren::SharedPointer formatter_sp( + new CXXSyntheticChildren( + SyntheticChildren::Flags() + .SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(true) + .SetNonCacheable(true), + "block pointer synthetic children", + lldb_private::formatters::BlockPointerSyntheticFrontEndCreator)); + if (valobj.GetCompilerType().IsBlockPointerType()) { + return formatter_sp; + } + return nullptr; + }); + }); + + return g_formatters; +} + +bool CPlusPlusLanguage::IsNilReference(ValueObject &valobj) { + if (!Language::LanguageIsCPlusPlus(valobj.GetObjectRuntimeLanguage()) || + !valobj.IsPointerType()) + return false; + bool canReadValue = true; + bool isZero = valobj.GetValueAsUnsigned(0, &canReadValue) == 0; + return canReadValue && isZero; +} + +bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const { + const auto suffixes = {".cpp", ".cxx", ".c++", ".cc", ".c", + ".h", ".hh", ".hpp", ".hxx", ".h++"}; + for (auto suffix : suffixes) { + if (file_path.ends_with_insensitive(suffix)) + return true; + } + + // Check if we're in a STL path (where the files usually have no extension + // that we could check for. + return file_path.contains("/usr/include/c++/"); +} + +bool CPlusPlusLanguage::GetFunctionDisplayName( + const SymbolContext *sc, const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, Stream &s) { + switch (representation) { + case FunctionNameRepresentation::eNameWithArgs: { + // Print the function name with arguments in it + if (sc->function) { + ExecutionContextScope *exe_scope = + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; + const char *cstr = sc->function->GetName().AsCString(nullptr); + if (cstr) { + const InlineFunctionInfo *inline_info = nullptr; + VariableListSP variable_list_sp; + bool get_function_vars = true; + if (sc->block) { + Block *inline_block = sc->block->GetContainingInlinedBlock(); + + if (inline_block) { + get_function_vars = false; + inline_info = inline_block->GetInlinedFunctionInfo(); + if (inline_info) + variable_list_sp = inline_block->GetBlockVariableList(true); + } + } + + if (get_function_vars) { + variable_list_sp = + sc->function->GetBlock(true).GetBlockVariableList(true); + } + + if (inline_info) { + s.PutCString(cstr); + s.PutCString(" [inlined] "); + cstr = inline_info->GetName().GetCString(); + } + + VariableList args; + if (variable_list_sp) + variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, + args); + if (args.GetSize() > 0) { + if (!PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args)) + return false; + } else { + s.PutCString(cstr); + } + return true; + } + } else if (sc->symbol) { + const char *cstr = sc->symbol->GetName().AsCString(nullptr); + if (cstr) { + s.PutCString(cstr); + return true; + } + } + } break; + default: + return false; + } + + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h new file mode 100644 index 000000000000..623d481bf117 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -0,0 +1,178 @@ +//===-- CPlusPlusLanguage.h -------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CPLUSPLUSLANGUAGE_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CPLUSPLUSLANGUAGE_H + +#include <set> +#include <vector> + +#include "llvm/ADT/StringRef.h" + +#include "Plugins/Language/ClangCommon/ClangHighlighter.h" +#include "lldb/Target/Language.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class CPlusPlusLanguage : public Language { + ClangHighlighter m_highlighter; + +public: + class MethodName { + public: + MethodName() + : m_full(), m_basename(), m_context(), m_arguments(), m_qualifiers() {} + + MethodName(ConstString s) + : m_full(s), m_basename(), m_context(), m_arguments(), m_qualifiers(), + m_parsed(false), m_parse_error(false) {} + + void Clear(); + + bool IsValid() { + if (!m_parsed) + Parse(); + if (m_parse_error) + return false; + return (bool)m_full; + } + + ConstString GetFullName() const { return m_full; } + + std::string GetScopeQualifiedName(); + + llvm::StringRef GetBasename(); + + llvm::StringRef GetContext(); + + llvm::StringRef GetArguments(); + + llvm::StringRef GetQualifiers(); + + /// Returns the methods return-type. + /// + /// Currently returns an empty llvm::StringRef + /// if the return-type is a function pointer. + llvm::StringRef GetReturnType(); + + bool ContainsPath(llvm::StringRef path); + + private: + /// Returns the Basename of this method without a template parameter + /// list, if any. + /// + // Examples: + // + // +--------------------------------+---------+ + // | MethodName | Returns | + // +--------------------------------+---------+ + // | void func() | func | + // | void func<int>() | func | + // | void func<std::vector<int>>() | func | + // +--------------------------------+---------+ + llvm::StringRef GetBasenameNoTemplateParameters(); + + protected: + void Parse(); + bool TrySimplifiedParse(); + + ConstString m_full; // Full name: + // "size_t lldb::SBTarget::GetBreakpointAtIndex(unsigned + // int) const" + llvm::StringRef m_basename; // Basename: "GetBreakpointAtIndex" + llvm::StringRef m_context; // Decl context: "lldb::SBTarget" + llvm::StringRef m_arguments; // Arguments: "(unsigned int)" + llvm::StringRef m_qualifiers; // Qualifiers: "const" + llvm::StringRef m_return_type; // Return type: "size_t" + bool m_parsed = false; + bool m_parse_error = false; + }; + + CPlusPlusLanguage() = default; + + ~CPlusPlusLanguage() override = default; + + lldb::LanguageType GetLanguageType() const override { + return lldb::eLanguageTypeC_plus_plus; + } + + llvm::StringRef GetUserEntryPointName() const override { return "main"; } + + std::unique_ptr<TypeScavenger> GetTypeScavenger() override; + lldb::TypeCategoryImplSP GetFormatters() override; + + HardcodedFormatters::HardcodedSummaryFinder GetHardcodedSummaries() override; + + HardcodedFormatters::HardcodedSyntheticFinder + GetHardcodedSynthetics() override; + + bool IsNilReference(ValueObject &valobj) override; + + llvm::StringRef GetNilReferenceSummaryString() override { return "nullptr"; } + + bool IsSourceFile(llvm::StringRef file_path) const override; + + const Highlighter *GetHighlighter() const override { return &m_highlighter; } + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static lldb_private::Language *CreateInstance(lldb::LanguageType language); + + static llvm::StringRef GetPluginNameStatic() { return "cplusplus"; } + + bool SymbolNameFitsToLanguage(Mangled mangled) const override; + + bool DemangledNameContainsPath(llvm::StringRef path, + ConstString demangled) const override; + + ConstString + GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override; + + bool GetFunctionDisplayName(const SymbolContext *sc, + const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, + Stream &s) override; + + static bool IsCPPMangledName(llvm::StringRef name); + + // Extract C++ context and identifier from a string using heuristic matching + // (as opposed to + // CPlusPlusLanguage::MethodName which has to have a fully qualified C++ name + // with parens and arguments. + // If the name is a lone C identifier (e.g. C) or a qualified C identifier + // (e.g. A::B::C) it will return true, + // and identifier will be the identifier (C and C respectively) and the + // context will be "" and "A::B" respectively. + // If the name fails the heuristic matching for a qualified or unqualified + // C/C++ identifier, then it will return false + // and identifier and context will be unchanged. + + static bool ExtractContextAndIdentifier(const char *name, + llvm::StringRef &context, + llvm::StringRef &identifier); + + std::vector<ConstString> + GenerateAlternateFunctionManglings(const ConstString mangled) const override; + + ConstString FindBestAlternateFunctionMangledName( + const Mangled mangled, const SymbolContext &sym_ctx) const override; + + llvm::StringRef GetInstanceVariableName() override { return "this"; } + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CPLUSPLUSLANGUAGE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp new file mode 100644 index 000000000000..d8c095d6edeb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp @@ -0,0 +1,785 @@ +//===-- CPlusPlusNameParser.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 "CPlusPlusNameParser.h" + +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/TokenKinds.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Threading.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using ParsedFunction = lldb_private::CPlusPlusNameParser::ParsedFunction; +using ParsedName = lldb_private::CPlusPlusNameParser::ParsedName; +namespace tok = clang::tok; + +std::optional<ParsedFunction> CPlusPlusNameParser::ParseAsFunctionDefinition() { + m_next_token_index = 0; + std::optional<ParsedFunction> result(std::nullopt); + + // Try to parse the name as function without a return type specified e.g. + // main(int, char*[]) + { + Bookmark start_position = SetBookmark(); + result = ParseFunctionImpl(false); + if (result && !HasMoreTokens()) + return result; + } + + // Try to parse the name as function with function pointer return type e.g. + // void (*get_func(const char*))() + result = ParseFuncPtr(true); + if (result) + return result; + + // Finally try to parse the name as a function with non-function return type + // e.g. int main(int, char*[]) + result = ParseFunctionImpl(true); + if (HasMoreTokens()) + return std::nullopt; + return result; +} + +std::optional<ParsedName> CPlusPlusNameParser::ParseAsFullName() { + m_next_token_index = 0; + std::optional<ParsedNameRanges> name_ranges = ParseFullNameImpl(); + if (!name_ranges) + return std::nullopt; + if (HasMoreTokens()) + return std::nullopt; + ParsedName result; + result.basename = GetTextForRange(name_ranges->basename_range); + result.context = GetTextForRange(name_ranges->context_range); + return result; +} + +bool CPlusPlusNameParser::HasMoreTokens() { + return m_next_token_index < m_tokens.size(); +} + +void CPlusPlusNameParser::Advance() { ++m_next_token_index; } + +void CPlusPlusNameParser::TakeBack() { --m_next_token_index; } + +bool CPlusPlusNameParser::ConsumeToken(tok::TokenKind kind) { + if (!HasMoreTokens()) + return false; + + if (!Peek().is(kind)) + return false; + + Advance(); + return true; +} + +template <typename... Ts> bool CPlusPlusNameParser::ConsumeToken(Ts... kinds) { + if (!HasMoreTokens()) + return false; + + if (!Peek().isOneOf(kinds...)) + return false; + + Advance(); + return true; +} + +CPlusPlusNameParser::Bookmark CPlusPlusNameParser::SetBookmark() { + return Bookmark(m_next_token_index); +} + +size_t CPlusPlusNameParser::GetCurrentPosition() { return m_next_token_index; } + +clang::Token &CPlusPlusNameParser::Peek() { + assert(HasMoreTokens()); + return m_tokens[m_next_token_index]; +} + +std::optional<ParsedFunction> +CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) { + Bookmark start_position = SetBookmark(); + + ParsedFunction result; + if (expect_return_type) { + size_t return_start = GetCurrentPosition(); + // Consume return type if it's expected. + if (!ConsumeToken(tok::kw_auto) && !ConsumeTypename()) + return std::nullopt; + + size_t return_end = GetCurrentPosition(); + result.return_type = GetTextForRange(Range(return_start, return_end)); + } + + auto maybe_name = ParseFullNameImpl(); + if (!maybe_name) { + return std::nullopt; + } + + size_t argument_start = GetCurrentPosition(); + if (!ConsumeArguments()) { + return std::nullopt; + } + + size_t qualifiers_start = GetCurrentPosition(); + SkipFunctionQualifiers(); + size_t end_position = GetCurrentPosition(); + + result.name.basename = GetTextForRange(maybe_name->basename_range); + result.name.context = GetTextForRange(maybe_name->context_range); + result.arguments = GetTextForRange(Range(argument_start, qualifiers_start)); + result.qualifiers = GetTextForRange(Range(qualifiers_start, end_position)); + start_position.Remove(); + return result; +} + +std::optional<ParsedFunction> +CPlusPlusNameParser::ParseFuncPtr(bool expect_return_type) { + // This function parses a function definition + // that returns a pointer type. + // E.g., double (*(*func(long))(int))(float) + + // Step 1: + // Remove the return type of the innermost + // function pointer type. + // + // Leaves us with: + // (*(*func(long))(int))(float) + Bookmark start_position = SetBookmark(); + if (expect_return_type) { + // Consume return type. + if (!ConsumeTypename()) + return std::nullopt; + } + + // Step 2: + // + // Skip a pointer and parenthesis pair. + // + // Leaves us with: + // (*func(long))(int))(float) + if (!ConsumeToken(tok::l_paren)) + return std::nullopt; + if (!ConsumePtrsAndRefs()) + return std::nullopt; + + // Step 3: + // + // Consume inner function name. This will fail unless + // we stripped all the pointers on the left hand side + // of the function name. + { + Bookmark before_inner_function_pos = SetBookmark(); + auto maybe_inner_function_name = ParseFunctionImpl(false); + if (maybe_inner_function_name) + if (ConsumeToken(tok::r_paren)) + if (ConsumeArguments()) { + SkipFunctionQualifiers(); + start_position.Remove(); + before_inner_function_pos.Remove(); + return maybe_inner_function_name; + } + } + + // Step 4: + // + // Parse the remaining string as a function pointer again. + // This time don't consume the inner-most typename since + // we're left with pointers only. This will strip another + // layer of pointers until we're left with the innermost + // function name/argument. I.e., func(long))(int))(float) + // + // Once we successfully stripped all pointers and gotten + // the innermost function name from ParseFunctionImpl above, + // we consume a single ')' and the arguments '(...)' that follows. + // + // Leaves us with: + // )(float) + // + // This is the remnant of the outer function pointers' arguments. + // Unwinding the recursive calls will remove the remaining + // arguments. + auto maybe_inner_function_ptr_name = ParseFuncPtr(false); + if (maybe_inner_function_ptr_name) + if (ConsumeToken(tok::r_paren)) + if (ConsumeArguments()) { + SkipFunctionQualifiers(); + start_position.Remove(); + return maybe_inner_function_ptr_name; + } + + return std::nullopt; +} + +bool CPlusPlusNameParser::ConsumeArguments() { + return ConsumeBrackets(tok::l_paren, tok::r_paren); +} + +bool CPlusPlusNameParser::ConsumeTemplateArgs() { + Bookmark start_position = SetBookmark(); + if (!HasMoreTokens() || Peek().getKind() != tok::less) + return false; + Advance(); + + // Consuming template arguments is a bit trickier than consuming function + // arguments, because '<' '>' brackets are not always trivially balanced. In + // some rare cases tokens '<' and '>' can appear inside template arguments as + // arithmetic or shift operators not as template brackets. Examples: + // std::enable_if<(10u)<(64), bool> + // f<A<operator<(X,Y)::Subclass>> + // Good thing that compiler makes sure that really ambiguous cases of '>' + // usage should be enclosed within '()' brackets. + int template_counter = 1; + bool can_open_template = false; + while (HasMoreTokens() && template_counter > 0) { + tok::TokenKind kind = Peek().getKind(); + switch (kind) { + case tok::greatergreater: + template_counter -= 2; + can_open_template = false; + Advance(); + break; + case tok::greater: + --template_counter; + can_open_template = false; + Advance(); + break; + case tok::less: + // '<' is an attempt to open a subteamplte + // check if parser is at the point where it's actually possible, + // otherwise it's just a part of an expression like 'sizeof(T)<(10)'. No + // need to do the same for '>' because compiler actually makes sure that + // '>' always surrounded by brackets to avoid ambiguity. + if (can_open_template) + ++template_counter; + can_open_template = false; + Advance(); + break; + case tok::kw_operator: // C++ operator overloading. + if (!ConsumeOperator()) + return false; + can_open_template = true; + break; + case tok::raw_identifier: + can_open_template = true; + Advance(); + break; + case tok::l_square: + // Handle templates tagged with an ABI tag. + // An example demangled/prettified version is: + // func[abi:tag1][abi:tag2]<type[abi:tag3]>(int) + if (ConsumeAbiTag()) + can_open_template = true; + else if (ConsumeBrackets(tok::l_square, tok::r_square)) + can_open_template = false; + else + return false; + break; + case tok::l_paren: + if (!ConsumeArguments()) + return false; + can_open_template = false; + break; + default: + can_open_template = false; + Advance(); + break; + } + } + + if (template_counter != 0) { + return false; + } + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeAbiTag() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::l_square)) + return false; + + if (HasMoreTokens() && Peek().is(tok::raw_identifier) && + Peek().getRawIdentifier() == "abi") + Advance(); + else + return false; + + if (!ConsumeToken(tok::colon)) + return false; + + // Consume the actual tag string (and allow some special characters) + while (ConsumeToken(tok::raw_identifier, tok::comma, tok::period, + tok::numeric_constant)) + ; + + if (!ConsumeToken(tok::r_square)) + return false; + + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeAnonymousNamespace() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::l_paren)) { + return false; + } + constexpr llvm::StringLiteral g_anonymous("anonymous"); + if (HasMoreTokens() && Peek().is(tok::raw_identifier) && + Peek().getRawIdentifier() == g_anonymous) { + Advance(); + } else { + return false; + } + + if (!ConsumeToken(tok::kw_namespace)) { + return false; + } + + if (!ConsumeToken(tok::r_paren)) { + return false; + } + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeLambda() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::l_brace)) { + return false; + } + constexpr llvm::StringLiteral g_lambda("lambda"); + if (HasMoreTokens() && Peek().is(tok::raw_identifier) && + Peek().getRawIdentifier() == g_lambda) { + // Put the matched brace back so we can use ConsumeBrackets + TakeBack(); + } else { + return false; + } + + if (!ConsumeBrackets(tok::l_brace, tok::r_brace)) { + return false; + } + + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeBrackets(tok::TokenKind left, + tok::TokenKind right) { + Bookmark start_position = SetBookmark(); + if (!HasMoreTokens() || Peek().getKind() != left) + return false; + Advance(); + + int counter = 1; + while (HasMoreTokens() && counter > 0) { + tok::TokenKind kind = Peek().getKind(); + if (kind == right) + --counter; + else if (kind == left) + ++counter; + Advance(); + } + + assert(counter >= 0); + if (counter > 0) { + return false; + } + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeOperator() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::kw_operator)) + return false; + + if (!HasMoreTokens()) { + return false; + } + + const auto &token = Peek(); + + // When clang generates debug info it adds template parameters to names. + // Since clang doesn't add a space between the name and the template parameter + // in some cases we are not generating valid C++ names e.g.: + // + // operator<<A::B> + // + // In some of these cases we will not parse them correctly. This fixes the + // issue by detecting this case and inserting tok::less in place of + // tok::lessless and returning successfully that we consumed the operator. + if (token.getKind() == tok::lessless) { + // Make sure we have more tokens before attempting to look ahead one more. + if (m_next_token_index + 1 < m_tokens.size()) { + // Look ahead two tokens. + clang::Token n_token = m_tokens[m_next_token_index + 1]; + // If we find ( or < then this is indeed operator<< no need for fix. + if (n_token.getKind() != tok::l_paren && n_token.getKind() != tok::less) { + clang::Token tmp_tok; + tmp_tok.startToken(); + tmp_tok.setLength(1); + tmp_tok.setLocation(token.getLocation().getLocWithOffset(1)); + tmp_tok.setKind(tok::less); + + m_tokens[m_next_token_index] = tmp_tok; + + start_position.Remove(); + return true; + } + } + } + + switch (token.getKind()) { + case tok::kw_new: + case tok::kw_delete: + // This is 'new' or 'delete' operators. + Advance(); + // Check for array new/delete. + if (HasMoreTokens() && Peek().is(tok::l_square)) { + // Consume the '[' and ']'. + if (!ConsumeBrackets(tok::l_square, tok::r_square)) + return false; + } + break; + +#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ + case tok::Token: \ + Advance(); \ + break; +#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemberOnly) +#include "clang/Basic/OperatorKinds.def" +#undef OVERLOADED_OPERATOR +#undef OVERLOADED_OPERATOR_MULTI + + case tok::l_paren: + // Call operator consume '(' ... ')'. + if (ConsumeBrackets(tok::l_paren, tok::r_paren)) + break; + return false; + + case tok::l_square: + // This is a [] operator. + // Consume the '[' and ']'. + if (ConsumeBrackets(tok::l_square, tok::r_square)) + break; + return false; + + default: + // This might be a cast operator. + if (ConsumeTypename()) + break; + return false; + } + start_position.Remove(); + return true; +} + +void CPlusPlusNameParser::SkipTypeQualifiers() { + while (ConsumeToken(tok::kw_const, tok::kw_volatile)) + ; +} + +void CPlusPlusNameParser::SkipFunctionQualifiers() { + while (ConsumeToken(tok::kw_const, tok::kw_volatile, tok::amp, tok::ampamp)) + ; +} + +bool CPlusPlusNameParser::ConsumeBuiltinType() { + bool result = false; + bool continue_parsing = true; + // Built-in types can be made of a few keywords like 'unsigned long long + // int'. This function consumes all built-in type keywords without checking + // if they make sense like 'unsigned char void'. + while (continue_parsing && HasMoreTokens()) { + switch (Peek().getKind()) { + case tok::kw_short: + case tok::kw_long: + case tok::kw___int64: + case tok::kw___int128: + case tok::kw_signed: + case tok::kw_unsigned: + case tok::kw_void: + case tok::kw_char: + case tok::kw_int: + case tok::kw_half: + case tok::kw_float: + case tok::kw_double: + case tok::kw___float128: + case tok::kw_wchar_t: + case tok::kw_bool: + case tok::kw_char16_t: + case tok::kw_char32_t: + result = true; + Advance(); + break; + default: + continue_parsing = false; + break; + } + } + return result; +} + +void CPlusPlusNameParser::SkipPtrsAndRefs() { + // Ignoring result. + ConsumePtrsAndRefs(); +} + +bool CPlusPlusNameParser::ConsumePtrsAndRefs() { + bool found = false; + SkipTypeQualifiers(); + while (ConsumeToken(tok::star, tok::amp, tok::ampamp, tok::kw_const, + tok::kw_volatile)) { + found = true; + SkipTypeQualifiers(); + } + return found; +} + +bool CPlusPlusNameParser::ConsumeDecltype() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::kw_decltype)) + return false; + + if (!ConsumeArguments()) + return false; + + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeTypename() { + Bookmark start_position = SetBookmark(); + SkipTypeQualifiers(); + if (!ConsumeBuiltinType() && !ConsumeDecltype()) { + if (!ParseFullNameImpl()) + return false; + } + SkipPtrsAndRefs(); + start_position.Remove(); + return true; +} + +std::optional<CPlusPlusNameParser::ParsedNameRanges> +CPlusPlusNameParser::ParseFullNameImpl() { + // Name parsing state machine. + enum class State { + Beginning, // start of the name + AfterTwoColons, // right after :: + AfterIdentifier, // right after alphanumerical identifier ([a-z0-9_]+) + AfterTemplate, // right after template brackets (<something>) + AfterOperator, // right after name of C++ operator + }; + + Bookmark start_position = SetBookmark(); + State state = State::Beginning; + bool continue_parsing = true; + std::optional<size_t> last_coloncolon_position; + + while (continue_parsing && HasMoreTokens()) { + const auto &token = Peek(); + switch (token.getKind()) { + case tok::raw_identifier: // Just a name. + if (state != State::Beginning && state != State::AfterTwoColons) { + continue_parsing = false; + break; + } + Advance(); + state = State::AfterIdentifier; + break; + case tok::l_square: { + // Handles types or functions that were tagged + // with, e.g., + // [[gnu::abi_tag("tag1","tag2")]] func() + // and demangled/prettified into: + // func[abi:tag1][abi:tag2]() + + // ABI tags only appear after a method or type name + const bool valid_state = + state == State::AfterIdentifier || state == State::AfterOperator; + if (!valid_state || !ConsumeAbiTag()) { + continue_parsing = false; + } + + break; + } + case tok::l_paren: { + if (state == State::Beginning || state == State::AfterTwoColons) { + // (anonymous namespace) + if (ConsumeAnonymousNamespace()) { + state = State::AfterIdentifier; + break; + } + } + + // Type declared inside a function 'func()::Type' + if (state != State::AfterIdentifier && state != State::AfterTemplate && + state != State::AfterOperator) { + continue_parsing = false; + break; + } + Bookmark l_paren_position = SetBookmark(); + // Consume the '(' ... ') [const]'. + if (!ConsumeArguments()) { + continue_parsing = false; + break; + } + SkipFunctionQualifiers(); + + // Consume '::' + size_t coloncolon_position = GetCurrentPosition(); + if (!ConsumeToken(tok::coloncolon)) { + continue_parsing = false; + break; + } + l_paren_position.Remove(); + last_coloncolon_position = coloncolon_position; + state = State::AfterTwoColons; + break; + } + case tok::l_brace: + if (state == State::Beginning || state == State::AfterTwoColons) { + if (ConsumeLambda()) { + state = State::AfterIdentifier; + break; + } + } + continue_parsing = false; + break; + case tok::coloncolon: // Type nesting delimiter. + if (state != State::Beginning && state != State::AfterIdentifier && + state != State::AfterTemplate) { + continue_parsing = false; + break; + } + last_coloncolon_position = GetCurrentPosition(); + Advance(); + state = State::AfterTwoColons; + break; + case tok::less: // Template brackets. + if (state != State::AfterIdentifier && state != State::AfterOperator) { + continue_parsing = false; + break; + } + if (!ConsumeTemplateArgs()) { + continue_parsing = false; + break; + } + state = State::AfterTemplate; + break; + case tok::kw_operator: // C++ operator overloading. + if (state != State::Beginning && state != State::AfterTwoColons) { + continue_parsing = false; + break; + } + if (!ConsumeOperator()) { + continue_parsing = false; + break; + } + state = State::AfterOperator; + break; + case tok::tilde: // Destructor. + if (state != State::Beginning && state != State::AfterTwoColons) { + continue_parsing = false; + break; + } + Advance(); + if (ConsumeToken(tok::raw_identifier)) { + state = State::AfterIdentifier; + } else { + TakeBack(); + continue_parsing = false; + } + break; + default: + continue_parsing = false; + break; + } + } + + if (state == State::AfterIdentifier || state == State::AfterOperator || + state == State::AfterTemplate) { + ParsedNameRanges result; + if (last_coloncolon_position) { + result.context_range = + Range(start_position.GetSavedPosition(), *last_coloncolon_position); + result.basename_range = + Range(*last_coloncolon_position + 1, GetCurrentPosition()); + } else { + result.basename_range = + Range(start_position.GetSavedPosition(), GetCurrentPosition()); + } + start_position.Remove(); + return result; + } else { + return std::nullopt; + } +} + +llvm::StringRef CPlusPlusNameParser::GetTextForRange(const Range &range) { + if (range.empty()) + return llvm::StringRef(); + assert(range.begin_index < range.end_index); + assert(range.begin_index < m_tokens.size()); + assert(range.end_index <= m_tokens.size()); + clang::Token &first_token = m_tokens[range.begin_index]; + clang::Token &last_token = m_tokens[range.end_index - 1]; + clang::SourceLocation start_loc = first_token.getLocation(); + clang::SourceLocation end_loc = last_token.getLocation(); + unsigned start_pos = start_loc.getRawEncoding(); + unsigned end_pos = end_loc.getRawEncoding() + last_token.getLength(); + return m_text.take_front(end_pos).drop_front(start_pos); +} + +static const clang::LangOptions &GetLangOptions() { + static clang::LangOptions g_options; + static llvm::once_flag g_once_flag; + llvm::call_once(g_once_flag, []() { + g_options.LineComment = true; + g_options.C99 = true; + g_options.C11 = true; + g_options.CPlusPlus = true; + g_options.CPlusPlus11 = true; + g_options.CPlusPlus14 = true; + g_options.CPlusPlus17 = true; + g_options.CPlusPlus20 = true; + }); + return g_options; +} + +static const llvm::StringMap<tok::TokenKind> &GetKeywordsMap() { + static llvm::StringMap<tok::TokenKind> g_map{ +#define KEYWORD(Name, Flags) {llvm::StringRef(#Name), tok::kw_##Name}, +#include "clang/Basic/TokenKinds.def" +#undef KEYWORD + }; + return g_map; +} + +void CPlusPlusNameParser::ExtractTokens() { + if (m_text.empty()) + return; + clang::Lexer lexer(clang::SourceLocation(), GetLangOptions(), m_text.data(), + m_text.data(), m_text.data() + m_text.size()); + const auto &kw_map = GetKeywordsMap(); + clang::Token token; + for (lexer.LexFromRawLexer(token); !token.is(clang::tok::eof); + lexer.LexFromRawLexer(token)) { + if (token.is(clang::tok::raw_identifier)) { + auto it = kw_map.find(token.getRawIdentifier()); + if (it != kw_map.end()) { + token.setKind(it->getValue()); + } + } + + m_tokens.push_back(token); + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h new file mode 100644 index 000000000000..326efb7a8be0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h @@ -0,0 +1,188 @@ +//===-- CPlusPlusNameParser.h -----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CPLUSPLUSNAMEPARSER_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CPLUSPLUSNAMEPARSER_H + +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +#include "lldb/Utility/ConstString.h" +#include "lldb/lldb-private.h" +#include <optional> + +namespace lldb_private { + +// Helps to validate and obtain various parts of C++ definitions. +class CPlusPlusNameParser { +public: + CPlusPlusNameParser(llvm::StringRef text) : m_text(text) { ExtractTokens(); } + + struct ParsedName { + llvm::StringRef basename; + llvm::StringRef context; + }; + + struct ParsedFunction { + ParsedName name; + llvm::StringRef arguments; + llvm::StringRef qualifiers; + llvm::StringRef return_type; + }; + + // Treats given text as a function definition and parses it. + // Function definition might or might not have a return type and this should + // change parsing result. + // Examples: + // main(int, chat const*) + // T fun(int, bool) + // std::vector<int>::push_back(int) + // int& map<int, pair<short, int>>::operator[](short) const + // int (*get_function(const chat *))() + std::optional<ParsedFunction> ParseAsFunctionDefinition(); + + // Treats given text as a potentially nested name of C++ entity (function, + // class, field) and parses it. + // Examples: + // main + // fun + // std::vector<int>::push_back + // map<int, pair<short, int>>::operator[] + // func<C>(int, C&)::nested_class::method + std::optional<ParsedName> ParseAsFullName(); + +private: + // A C++ definition to parse. + llvm::StringRef m_text; + // Tokens extracted from m_text. + llvm::SmallVector<clang::Token, 30> m_tokens; + // Index of the next token to look at from m_tokens. + size_t m_next_token_index = 0; + + // Range of tokens saved in m_next_token_index. + struct Range { + size_t begin_index = 0; + size_t end_index = 0; + + Range() = default; + Range(size_t begin, size_t end) : begin_index(begin), end_index(end) { + assert(end >= begin); + } + + size_t size() const { return end_index - begin_index; } + + bool empty() const { return size() == 0; } + }; + + struct ParsedNameRanges { + Range basename_range; + Range context_range; + }; + + // Bookmark automatically restores parsing position (m_next_token_index) + // when destructed unless it's manually removed with Remove(). + class Bookmark { + public: + Bookmark(size_t &position) + : m_position(position), m_position_value(position) {} + Bookmark(const Bookmark &) = delete; + Bookmark(Bookmark &&b) + : m_position(b.m_position), m_position_value(b.m_position_value), + m_restore(b.m_restore) { + b.Remove(); + } + Bookmark &operator=(Bookmark &&) = delete; + Bookmark &operator=(const Bookmark &) = delete; + + void Remove() { m_restore = false; } + size_t GetSavedPosition() { return m_position_value; } + ~Bookmark() { + if (m_restore) { + m_position = m_position_value; + } + } + + private: + size_t &m_position; + size_t m_position_value; + bool m_restore = true; + }; + + bool HasMoreTokens(); + void Advance(); + void TakeBack(); + bool ConsumeToken(clang::tok::TokenKind kind); + + template <typename... Ts> bool ConsumeToken(Ts... kinds); + Bookmark SetBookmark(); + size_t GetCurrentPosition(); + clang::Token &Peek(); + bool ConsumeBrackets(clang::tok::TokenKind left, clang::tok::TokenKind right); + + std::optional<ParsedFunction> ParseFunctionImpl(bool expect_return_type); + + // Parses functions returning function pointers 'string (*f(int x))(float y)' + std::optional<ParsedFunction> ParseFuncPtr(bool expect_return_type); + + // Consumes function arguments enclosed within '(' ... ')' + bool ConsumeArguments(); + + // Consumes template arguments enclosed within '<' ... '>' + bool ConsumeTemplateArgs(); + + // Consumes '(anonymous namespace)' + bool ConsumeAnonymousNamespace(); + + // Consumes '{lambda ...}' + bool ConsumeLambda(); + + // Consumes operator declaration like 'operator *' or 'operator delete []' + bool ConsumeOperator(); + + // Skips 'const' and 'volatile' + void SkipTypeQualifiers(); + + // Skips 'const', 'volatile', '&', '&&' in the end of the function. + void SkipFunctionQualifiers(); + + // Consumes built-in types like 'int' or 'unsigned long long int' + bool ConsumeBuiltinType(); + + // Consumes types defined via decltype keyword. + bool ConsumeDecltype(); + + // Skips 'const' and 'volatile' + void SkipPtrsAndRefs(); + + // Consumes things like 'const * const &' + bool ConsumePtrsAndRefs(); + + // Consumes full type name like 'Namespace::Class<int>::Method()::InnerClass' + bool ConsumeTypename(); + + /// Consumes ABI tags enclosed within '[abi:' ... ']' + /// + /// Since there is no restriction on what the ABI tag + /// string may contain, this API supports parsing a small + /// set of special characters. + /// + /// The following regex describes the set of supported characters: + /// [A-Za-z,.\s\d]+ + bool ConsumeAbiTag(); + + std::optional<ParsedNameRanges> ParseFullNameImpl(); + llvm::StringRef GetTextForRange(const Range &range); + + // Populate m_tokens by calling clang lexer on m_text. + void ExtractTokens(); +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CPLUSPLUSNAMEPARSER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp new file mode 100644 index 000000000000..5e63d1d7b214 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp @@ -0,0 +1,227 @@ +//===-- Coroutines.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 "Coroutines.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +static lldb::addr_t GetCoroFramePtrFromHandle(ValueObjectSP valobj_sp) { + if (!valobj_sp) + return LLDB_INVALID_ADDRESS; + + // We expect a single pointer in the `coroutine_handle` class. + // We don't care about its name. + if (valobj_sp->GetNumChildrenIgnoringErrors() != 1) + return LLDB_INVALID_ADDRESS; + ValueObjectSP ptr_sp(valobj_sp->GetChildAtIndex(0)); + if (!ptr_sp) + return LLDB_INVALID_ADDRESS; + if (!ptr_sp->GetCompilerType().IsPointerType()) + return LLDB_INVALID_ADDRESS; + + AddressType addr_type; + lldb::addr_t frame_ptr_addr = ptr_sp->GetPointerValue(&addr_type); + if (!frame_ptr_addr || frame_ptr_addr == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + lldbassert(addr_type == AddressType::eAddressTypeLoad); + if (addr_type != AddressType::eAddressTypeLoad) + return LLDB_INVALID_ADDRESS; + + return frame_ptr_addr; +} + +static Function *ExtractDestroyFunction(lldb::TargetSP target_sp, + lldb::addr_t frame_ptr_addr) { + lldb::ProcessSP process_sp = target_sp->GetProcessSP(); + auto ptr_size = process_sp->GetAddressByteSize(); + + Status error; + auto destroy_func_ptr_addr = frame_ptr_addr + ptr_size; + lldb::addr_t destroy_func_addr = + process_sp->ReadPointerFromMemory(destroy_func_ptr_addr, error); + if (error.Fail()) + return nullptr; + + Address destroy_func_address; + if (!target_sp->ResolveLoadAddress(destroy_func_addr, destroy_func_address)) + return nullptr; + + return destroy_func_address.CalculateSymbolContextFunction(); +} + +static CompilerType InferPromiseType(Function &destroy_func) { + Block &block = destroy_func.GetBlock(true); + auto variable_list = block.GetBlockVariableList(true); + + // clang generates an artificial `__promise` variable inside the + // `destroy` function. Look for it. + auto promise_var = variable_list->FindVariable(ConstString("__promise")); + if (!promise_var) + return {}; + if (!promise_var->IsArtificial()) + return {}; + + Type *promise_type = promise_var->GetType(); + if (!promise_type) + return {}; + return promise_type->GetForwardCompilerType(); +} + +bool lldb_private::formatters::StdlibCoroutineHandleSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + lldb::addr_t frame_ptr_addr = + GetCoroFramePtrFromHandle(valobj.GetNonSyntheticValue()); + if (frame_ptr_addr == LLDB_INVALID_ADDRESS) + return false; + + if (frame_ptr_addr == 0) { + stream << "nullptr"; + } else { + stream.Printf("coro frame = 0x%" PRIx64, frame_ptr_addr); + } + + return true; +} + +lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: + StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: + ~StdlibCoroutineHandleSyntheticFrontEnd() = default; + +llvm::Expected<uint32_t> lldb_private::formatters:: + StdlibCoroutineHandleSyntheticFrontEnd::CalculateNumChildren() { + if (!m_resume_ptr_sp || !m_destroy_ptr_sp) + return 0; + + return m_promise_ptr_sp ? 3 : 2; +} + +lldb::ValueObjectSP lldb_private::formatters:: + StdlibCoroutineHandleSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + switch (idx) { + case 0: + return m_resume_ptr_sp; + case 1: + return m_destroy_ptr_sp; + case 2: + return m_promise_ptr_sp; + } + return lldb::ValueObjectSP(); +} + +lldb::ChildCacheState +lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::Update() { + m_resume_ptr_sp.reset(); + m_destroy_ptr_sp.reset(); + m_promise_ptr_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + lldb::addr_t frame_ptr_addr = GetCoroFramePtrFromHandle(valobj_sp); + if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS) + return lldb::ChildCacheState::eRefetch; + + auto ts = valobj_sp->GetCompilerType().GetTypeSystem(); + auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>(); + if (!ast_ctx) + return lldb::ChildCacheState::eRefetch; + + // Create the `resume` and `destroy` children. + lldb::TargetSP target_sp = m_backend.GetTargetSP(); + auto &exe_ctx = m_backend.GetExecutionContextRef(); + lldb::ProcessSP process_sp = target_sp->GetProcessSP(); + auto ptr_size = process_sp->GetAddressByteSize(); + CompilerType void_type = ast_ctx->GetBasicType(lldb::eBasicTypeVoid); + CompilerType coro_func_type = ast_ctx->CreateFunctionType( + /*result_type=*/void_type, /*args=*/&void_type, /*num_args=*/1, + /*is_variadic=*/false, /*qualifiers=*/0); + CompilerType coro_func_ptr_type = coro_func_type.GetPointerType(); + m_resume_ptr_sp = CreateValueObjectFromAddress( + "resume", frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type); + lldbassert(m_resume_ptr_sp); + m_destroy_ptr_sp = CreateValueObjectFromAddress( + "destroy", frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type); + lldbassert(m_destroy_ptr_sp); + + // Get the `promise_type` from the template argument + CompilerType promise_type( + valobj_sp->GetCompilerType().GetTypeTemplateArgument(0)); + if (!promise_type) + return lldb::ChildCacheState::eRefetch; + + // Try to infer the promise_type if it was type-erased + if (promise_type.IsVoidType()) { + if (Function *destroy_func = + ExtractDestroyFunction(target_sp, frame_ptr_addr)) { + if (CompilerType inferred_type = InferPromiseType(*destroy_func)) { + promise_type = inferred_type; + } + } + } + + // If we don't know the promise type, we don't display the `promise` member. + // `CreateValueObjectFromAddress` below would fail for `void` types. + if (promise_type.IsVoidType()) { + return lldb::ChildCacheState::eRefetch; + } + + // Add the `promise` member. We intentionally add `promise` as a pointer type + // instead of a value type, and don't automatically dereference this pointer. + // We do so to avoid potential very deep recursion in case there is a cycle + // formed between `std::coroutine_handle`s and their promises. + lldb::ValueObjectSP promise = CreateValueObjectFromAddress( + "promise", frame_ptr_addr + 2 * ptr_size, exe_ctx, promise_type); + Status error; + lldb::ValueObjectSP promisePtr = promise->AddressOf(error); + if (error.Success()) + m_promise_ptr_sp = promisePtr->Clone(ConstString("promise")); + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t StdlibCoroutineHandleSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (!m_resume_ptr_sp || !m_destroy_ptr_sp) + return UINT32_MAX; + + if (name == ConstString("resume")) + return 0; + if (name == ConstString("destroy")) + return 1; + if (name == ConstString("promise_ptr") && m_promise_ptr_sp) + return 2; + + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp) + : nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.h new file mode 100644 index 000000000000..1d4bc65e2637 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.h @@ -0,0 +1,60 @@ +//===-- Coroutines.h --------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_COROUTINES_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_COROUTINES_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { + +namespace formatters { + +/// Summary provider for `std::coroutine_handle<T>` from libc++, libstdc++ and +/// MSVC STL. +bool StdlibCoroutineHandleSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +/// Synthetic children frontend for `std::coroutine_handle<promise_type>` from +/// libc++, libstdc++ and MSVC STL. Shows the compiler-generated `resume` and +/// `destroy` function pointers as well as the `promise`, if the promise type +/// is `promise_type != void`. +class StdlibCoroutineHandleSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~StdlibCoroutineHandleSyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + lldb::ValueObjectSP m_resume_ptr_sp; + lldb::ValueObjectSP m_destroy_ptr_sp; + lldb::ValueObjectSP m_promise_ptr_sp; +}; + +SyntheticChildrenFrontEnd * +StdlibCoroutineHandleSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_COROUTINES_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp new file mode 100644 index 000000000000..6781c96210a4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp @@ -0,0 +1,214 @@ +//===-- CxxStringTypes.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 "CxxStringTypes.h" + +#include "llvm/Support/ConvertUTF.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Host/Time.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +#include <algorithm> +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +using StringElementType = StringPrinter::StringElementType; + +static constexpr std::pair<const char *, Format> +getElementTraits(StringElementType ElemType) { + switch (ElemType) { + case StringElementType::UTF8: + return std::make_pair("u8", lldb::eFormatUnicode8); + case StringElementType::UTF16: + return std::make_pair("u", lldb::eFormatUnicode16); + case StringElementType::UTF32: + return std::make_pair("U", lldb::eFormatUnicode32); + default: + return std::make_pair(nullptr, lldb::eFormatInvalid); + } +} + +template <StringElementType ElemType> +static bool CharStringSummaryProvider(ValueObject &valobj, Stream &stream) { + Address valobj_addr = GetArrayAddressOrPointerValue(valobj); + if (!valobj_addr.IsValid()) + return false; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(valobj_addr); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetPrefixToken(getElementTraits(ElemType).first); + + if (!StringPrinter::ReadStringAndDumpToStream<ElemType>(options)) + stream.Printf("Summary Unavailable"); + + return true; +} + +template <StringElementType ElemType> +static bool CharSummaryProvider(ValueObject &valobj, Stream &stream) { + DataExtractor data; + Status error; + valobj.GetData(data, error); + + if (error.Fail()) + return false; + + std::string value; + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + + constexpr auto ElemTraits = getElementTraits(ElemType); + valobj.GetValueAsCString(ElemTraits.second, value); + + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + + options.SetData(std::move(data)); + options.SetStream(&stream); + options.SetPrefixToken(ElemTraits.first); + options.SetQuote('\''); + options.SetSourceSize(1); + options.SetBinaryZeroIsTerminator(false); + + return StringPrinter::ReadBufferAndDumpToStream<ElemType>(options); +} + +bool lldb_private::formatters::Char8StringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharStringSummaryProvider<StringElementType::UTF8>(valobj, stream); +} + +bool lldb_private::formatters::Char16StringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharStringSummaryProvider<StringElementType::UTF16>(valobj, stream); +} + +bool lldb_private::formatters::Char32StringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharStringSummaryProvider<StringElementType::UTF32>(valobj, stream); +} + +bool lldb_private::formatters::WCharStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + Address valobj_addr = GetArrayAddressOrPointerValue(valobj); + if (!valobj_addr.IsValid()) + return false; + + // Get a wchar_t basic type from the current type system + CompilerType wchar_compiler_type = + valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar); + + if (!wchar_compiler_type) + return false; + + // Safe to pass nullptr for exe_scope here. + std::optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr); + if (!size) + return false; + const uint32_t wchar_size = *size; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(valobj_addr); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetPrefixToken("L"); + + switch (wchar_size) { + case 8: + return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF8>( + options); + case 16: + return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF16>( + options); + case 32: + return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF32>( + options); + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; +} + +bool lldb_private::formatters::Char8SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharSummaryProvider<StringElementType::UTF8>(valobj, stream); +} + +bool lldb_private::formatters::Char16SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharSummaryProvider<StringElementType::UTF16>(valobj, stream); +} + +bool lldb_private::formatters::Char32SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharSummaryProvider<StringElementType::UTF32>(valobj, stream); +} + +bool lldb_private::formatters::WCharSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + DataExtractor data; + Status error; + valobj.GetData(data, error); + + if (error.Fail()) + return false; + + // Get a wchar_t basic type from the current type system + CompilerType wchar_compiler_type = + valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar); + + if (!wchar_compiler_type) + return false; + + // Safe to pass nullptr for exe_scope here. + std::optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr); + if (!size) + return false; + const uint32_t wchar_size = *size; + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + options.SetData(std::move(data)); + options.SetStream(&stream); + options.SetPrefixToken("L"); + options.SetQuote('\''); + options.SetSourceSize(1); + options.SetBinaryZeroIsTerminator(false); + + switch (wchar_size) { + case 8: + return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF8>( + options); + case 16: + return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF16>( + options); + case 32: + return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF32>( + options); + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h new file mode 100644 index 000000000000..2713ded45929 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h @@ -0,0 +1,49 @@ +//===-- CxxStringTypes.h ----------------------------------------------*- C++ +//-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CXXSTRINGTYPES_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CXXSTRINGTYPES_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { +bool Char8StringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char8_t* + +bool Char16StringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char16_t* and unichar* + +bool Char32StringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char32_t* + +bool WCharStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // wchar_t* + +bool Char8SummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char8_t + +bool Char16SummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char16_t and unichar + +bool Char32SummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char32_t + +bool WCharSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // wchar_t + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CXXSTRINGTYPES_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Generic.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Generic.h new file mode 100644 index 000000000000..bfb28bebf90b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Generic.h @@ -0,0 +1,25 @@ +//===-- LibCxx.h -----------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_GENERIC_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_GENERIC_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { + +bool GenericOptionalSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_GENERIC_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp new file mode 100644 index 000000000000..33955dccb6cc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp @@ -0,0 +1,154 @@ +//===-- GenericBitset.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 "LibCxx.h" +#include "LibStdcpp.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Target.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +namespace { + +/// This class can be used for handling bitsets from both libcxx and libstdcpp. +class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd { +public: + enum class StdLib { + LibCxx, + LibStdcpp, + }; + + GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib); + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + lldb::ChildCacheState Update() override; + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_elements.size(); + } + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + +private: + llvm::StringRef GetDataContainerMemberName(); + + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + // Value objects created from raw data (i.e. in a different cluster) must + // be referenced via shared pointer to keep them alive, however. + std::vector<ValueObjectSP> m_elements; + ValueObject *m_first = nullptr; + CompilerType m_bool_type; + ByteOrder m_byte_order = eByteOrderInvalid; + uint8_t m_byte_size = 0; + StdLib m_stdlib; +}; +} // namespace + +GenericBitsetFrontEnd::GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib) + : SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) { + m_bool_type = valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeBool); + if (auto target_sp = m_backend.GetTargetSP()) { + m_byte_order = target_sp->GetArchitecture().GetByteOrder(); + m_byte_size = target_sp->GetArchitecture().GetAddressByteSize(); + Update(); + } +} + +llvm::StringRef GenericBitsetFrontEnd::GetDataContainerMemberName() { + static constexpr llvm::StringLiteral s_libcxx_case("__first_"); + static constexpr llvm::StringLiteral s_libstdcpp_case("_M_w"); + switch (m_stdlib) { + case StdLib::LibCxx: + return s_libcxx_case; + case StdLib::LibStdcpp: + return s_libstdcpp_case; + } + llvm_unreachable("Unknown StdLib enum"); +} + +lldb::ChildCacheState GenericBitsetFrontEnd::Update() { + m_elements.clear(); + m_first = nullptr; + + TargetSP target_sp = m_backend.GetTargetSP(); + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + size_t size = 0; + + if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(0)) + size = arg->value.getLimitedValue(); + + m_elements.assign(size, ValueObjectSP()); + m_first = + m_backend.GetChildMemberWithName(GetDataContainerMemberName()).get(); + return lldb::ChildCacheState::eRefetch; +} + +ValueObjectSP GenericBitsetFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= m_elements.size() || !m_first) + return ValueObjectSP(); + + if (m_elements[idx]) + return m_elements[idx]; + + ExecutionContext ctx = m_backend.GetExecutionContextRef().Lock(false); + CompilerType type; + ValueObjectSP chunk; + // For small bitsets __first_ is not an array, but a plain size_t. + if (m_first->GetCompilerType().IsArrayType(&type)) { + std::optional<uint64_t> bit_size = + type.GetBitSize(ctx.GetBestExecutionContextScope()); + if (!bit_size || *bit_size == 0) + return {}; + chunk = m_first->GetChildAtIndex(idx / *bit_size); + } else { + type = m_first->GetCompilerType(); + chunk = m_first->GetSP(); + } + if (!type || !chunk) + return {}; + + std::optional<uint64_t> bit_size = + type.GetBitSize(ctx.GetBestExecutionContextScope()); + if (!bit_size || *bit_size == 0) + return {}; + size_t chunk_idx = idx % *bit_size; + uint8_t value = !!(chunk->GetValueAsUnsigned(0) & (uint64_t(1) << chunk_idx)); + DataExtractor data(&value, sizeof(value), m_byte_order, m_byte_size); + + m_elements[idx] = CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), + data, ctx, m_bool_type); + + return m_elements[idx]; +} + +SyntheticChildrenFrontEnd *formatters::LibStdcppBitsetSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new GenericBitsetFrontEnd(*valobj_sp, + GenericBitsetFrontEnd::StdLib::LibStdcpp); + return nullptr; +} + +SyntheticChildrenFrontEnd *formatters::LibcxxBitsetSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new GenericBitsetFrontEnd(*valobj_sp, + GenericBitsetFrontEnd::StdLib::LibCxx); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp new file mode 100644 index 000000000000..23756de7f1e6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp @@ -0,0 +1,138 @@ +//===-- GenericOptional.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 "Generic.h" +#include "LibCxx.h" +#include "LibStdcpp.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +bool lldb_private::formatters::GenericOptionalSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + stream.Printf(" Has Value=%s ", + valobj.GetNumChildrenIgnoringErrors() == 0 ? "false" : "true"); + + return true; +} + +// Synthetic Children Provider +namespace { + +class GenericOptionalFrontend : public SyntheticChildrenFrontEnd { +public: + enum class StdLib { + LibCxx, + LibStdcpp, + }; + + GenericOptionalFrontend(ValueObject &valobj, StdLib stdlib); + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_has_value ? 1U : 0U; + } + + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + lldb::ChildCacheState Update() override; + +private: + bool m_has_value = false; + StdLib m_stdlib; +}; + +} // namespace + +GenericOptionalFrontend::GenericOptionalFrontend(ValueObject &valobj, + StdLib stdlib) + : SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) { + if (auto target_sp = m_backend.GetTargetSP()) { + Update(); + } +} + +lldb::ChildCacheState GenericOptionalFrontend::Update() { + ValueObjectSP engaged_sp; + + if (m_stdlib == StdLib::LibCxx) + engaged_sp = m_backend.GetChildMemberWithName("__engaged_"); + else if (m_stdlib == StdLib::LibStdcpp) + engaged_sp = m_backend.GetChildMemberWithName("_M_payload") + ->GetChildMemberWithName("_M_engaged"); + + if (!engaged_sp) + return lldb::ChildCacheState::eRefetch; + + // _M_engaged/__engaged is a bool flag and is true if the optional contains a + // value. Converting it to unsigned gives us a size of 1 if it contains a + // value and 0 if not. + m_has_value = engaged_sp->GetValueAsUnsigned(0) != 0; + + return lldb::ChildCacheState::eRefetch; +} + +ValueObjectSP GenericOptionalFrontend::GetChildAtIndex(uint32_t _idx) { + if (!m_has_value) + return ValueObjectSP(); + + ValueObjectSP val_sp; + + if (m_stdlib == StdLib::LibCxx) + // __val_ contains the underlying value of an optional if it has one. + // Currently because it is part of an anonymous union + // GetChildMemberWithName() does not peer through and find it unless we are + // at the parent itself. We can obtain the parent through __engaged_. + val_sp = m_backend.GetChildMemberWithName("__engaged_") + ->GetParent() + ->GetChildAtIndex(0) + ->GetChildMemberWithName("__val_"); + else if (m_stdlib == StdLib::LibStdcpp) { + val_sp = m_backend.GetChildMemberWithName("_M_payload") + ->GetChildMemberWithName("_M_payload"); + + // In some implementations, _M_value contains the underlying value of an + // optional, and in other versions, it's in the payload member. + ValueObjectSP candidate = val_sp->GetChildMemberWithName("_M_value"); + if (candidate) + val_sp = candidate; + } + + if (!val_sp) + return ValueObjectSP(); + + CompilerType holder_type = val_sp->GetCompilerType(); + + if (!holder_type) + return ValueObjectSP(); + + return val_sp->Clone(ConstString("Value")); +} + +SyntheticChildrenFrontEnd * +formatters::LibStdcppOptionalSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new GenericOptionalFrontend( + *valobj_sp, GenericOptionalFrontend::StdLib::LibStdcpp); + return nullptr; +} + +SyntheticChildrenFrontEnd *formatters::LibcxxOptionalSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new GenericOptionalFrontend(*valobj_sp, + GenericOptionalFrontend::StdLib::LibCxx); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp new file mode 100644 index 000000000000..feaa51a96843 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -0,0 +1,939 @@ +//===-- LibCxx.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 "LibCxx.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/FormatEntity.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/VectorIterator.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/lldb-enumerations.h" +#include <optional> +#include <tuple> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName( + ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) { + for (ConstString name : alternative_names) { + lldb::ValueObjectSP child_sp = obj.GetChildMemberWithName(name); + + if (child_sp) + return child_sp; + } + return {}; +} + +lldb::ValueObjectSP +lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair( + ValueObject &pair) { + ValueObjectSP value; + ValueObjectSP first_child = pair.GetChildAtIndex(0); + if (first_child) + value = first_child->GetChildMemberWithName("__value_"); + if (!value) { + // pre-r300140 member name + value = pair.GetChildMemberWithName("__first_"); + } + return value; +} + +lldb::ValueObjectSP +lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair( + ValueObject &pair) { + ValueObjectSP value; + if (pair.GetNumChildrenIgnoringErrors() > 1) { + ValueObjectSP second_child = pair.GetChildAtIndex(1); + if (second_child) { + value = second_child->GetChildMemberWithName("__value_"); + } + } + if (!value) { + // pre-r300140 member name + value = pair.GetChildMemberWithName("__second_"); + } + return value; +} + +bool lldb_private::formatters::LibcxxFunctionSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + + if (!valobj_sp) + return false; + + ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + + if (process == nullptr) + return false; + + CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(*process); + + if (!cpp_runtime) + return false; + + CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info = + cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp); + + switch (callable_info.callable_case) { + case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid: + stream.Printf(" __f_ = %" PRIu64, callable_info.member_f_pointer_value); + return false; + break; + case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda: + stream.Printf( + " Lambda in File %s at Line %u", + callable_info.callable_line_entry.GetFile().GetFilename().GetCString(), + callable_info.callable_line_entry.line); + break; + case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject: + stream.Printf( + " Function in File %s at Line %u", + callable_info.callable_line_entry.GetFile().GetFilename().GetCString(), + callable_info.callable_line_entry.line); + break; + case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction: + stream.Printf(" Function = %s ", + callable_info.callable_symbol.GetName().GetCString()); + break; + } + + return true; +} + +bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_")); + ValueObjectSP count_sp( + valobj_sp->GetChildAtNamePath({"__cntrl_", "__shared_owners_"})); + ValueObjectSP weakcount_sp( + valobj_sp->GetChildAtNamePath({"__cntrl_", "__shared_weak_owners_"})); + + if (!ptr_sp) + return false; + + if (ptr_sp->GetValueAsUnsigned(0) == 0) { + stream.Printf("nullptr"); + return true; + } else { + bool print_pointee = false; + Status error; + ValueObjectSP pointee_sp = ptr_sp->Dereference(error); + if (pointee_sp && error.Success()) { + if (pointee_sp->DumpPrintableRepresentation( + stream, ValueObject::eValueObjectRepresentationStyleSummary, + lldb::eFormatInvalid, + ValueObject::PrintableRepresentationSpecialCases::eDisable, + false)) + print_pointee = true; + } + if (!print_pointee) + stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0)); + } + + if (count_sp) + stream.Printf(" strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(0)); + + if (weakcount_sp) + stream.Printf(" weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(0)); + + return true; +} + +bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + + ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_")); + if (!ptr_sp) + return false; + + ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp); + if (!ptr_sp) + return false; + + if (ptr_sp->GetValueAsUnsigned(0) == 0) { + stream.Printf("nullptr"); + return true; + } else { + bool print_pointee = false; + Status error; + ValueObjectSP pointee_sp = ptr_sp->Dereference(error); + if (pointee_sp && error.Success()) { + if (pointee_sp->DumpPrintableRepresentation( + stream, ValueObject::eValueObjectRepresentationStyleSummary, + lldb::eFormatInvalid, + ValueObject::PrintableRepresentationSpecialCases::eDisable, + false)) + print_pointee = true; + } + if (!print_pointee) + stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0)); + } + + return true; +} + +/* + (lldb) fr var ibeg --raw --ptr-depth 1 -T + (std::__1::__wrap_iter<int *>) ibeg = { + (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 { + (int) *__i = 1 + } + } +*/ + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new VectorIteratorSyntheticFrontEnd( + valobj_sp, {ConstString("__i_"), ConstString("__i")}) + : nullptr); +} + +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren() { + return (m_cntrl ? 1 : 0); +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_cntrl) + return lldb::ValueObjectSP(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ValueObjectSP(); + + if (idx == 0) + return valobj_sp->GetChildMemberWithName("__ptr_"); + + if (idx == 1) { + if (auto ptr_sp = valobj_sp->GetChildMemberWithName("__ptr_")) { + Status status; + auto value_type_sp = + valobj_sp->GetCompilerType() + .GetTypeTemplateArgument(0).GetPointerType(); + ValueObjectSP cast_ptr_sp = ptr_sp->Cast(value_type_sp); + ValueObjectSP value_sp = cast_ptr_sp->Dereference(status); + if (status.Success()) { + return value_sp; + } + } + } + + return lldb::ValueObjectSP(); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() { + m_cntrl = nullptr; + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName("__cntrl_")); + + m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular + // dependency + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "__ptr_") + return 0; + if (name == "$$dereference$$") + return 1; + return UINT32_MAX; +} + +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + ~LibcxxSharedPtrSyntheticFrontEnd() = default; + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: + LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: + ~LibcxxUniquePtrSyntheticFrontEnd() = default; + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxUniquePtrSyntheticFrontEnd::CalculateNumChildren() { + if (m_value_ptr_sp) + return m_deleter_sp ? 2 : 1; + return 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_value_ptr_sp) + return lldb::ValueObjectSP(); + + if (idx == 0) + return m_value_ptr_sp; + + if (idx == 1) + return m_deleter_sp; + + if (idx == 2) { + Status status; + auto value_sp = m_value_ptr_sp->Dereference(status); + if (status.Success()) { + return value_sp; + } + } + + return lldb::ValueObjectSP(); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_")); + if (!ptr_sp) + return lldb::ChildCacheState::eRefetch; + + // Retrieve the actual pointer and the deleter, and clone them to give them + // user-friendly names. + ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp); + if (value_pointer_sp) + m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer")); + + ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp); + if (deleter_sp) + m_deleter_sp = deleter_sp->Clone(ConstString("deleter")); + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "pointer") + return 0; + if (name == "deleter") + return 1; + if (name == "$$dereference$$") + return 2; + return UINT32_MAX; +} + +bool lldb_private::formatters::LibcxxContainerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + if (valobj.IsPointerType()) { + uint64_t value = valobj.GetValueAsUnsigned(0); + if (!value) + return false; + stream.Printf("0x%016" PRIx64 " ", value); + } + return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr, + nullptr, nullptr, &valobj, false, false); +} + +/// The field layout in a libc++ string (cap, side, data or data, size, cap). +namespace { +enum class StringLayout { CSD, DSC }; +} + +/// Determine the size in bytes of \p valobj (a libc++ std::string object) and +/// extract its data payload. Return the size + payload pair. +// TODO: Support big-endian architectures. +static std::optional<std::pair<uint64_t, ValueObjectSP>> +ExtractLibcxxStringInfo(ValueObject &valobj) { + ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_"); + if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) + return {}; + + // __r_ is a compressed_pair of the actual data and the allocator. The data we + // want is in the first base class. + ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(0); + if (!valobj_r_base_sp) + return {}; + + ValueObjectSP valobj_rep_sp = + valobj_r_base_sp->GetChildMemberWithName("__value_"); + if (!valobj_rep_sp) + return {}; + + ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName("__l"); + if (!l) + return {}; + + StringLayout layout = l->GetIndexOfChildWithName("__data_") == 0 + ? StringLayout::DSC + : StringLayout::CSD; + + bool short_mode = false; // this means the string is in short-mode and the + // data is stored inline + bool using_bitmasks = true; // Whether the class uses bitmasks for the mode + // flag (pre-D123580). + uint64_t size; + uint64_t size_mode_value = 0; + + ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName("__s"); + if (!short_sp) + return {}; + + ValueObjectSP is_long = short_sp->GetChildMemberWithName("__is_long_"); + ValueObjectSP size_sp = short_sp->GetChildMemberWithName("__size_"); + if (!size_sp) + return {}; + + if (is_long) { + using_bitmasks = false; + short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0); + size = size_sp->GetValueAsUnsigned(/*fail_value=*/0); + } else { + // The string mode is encoded in the size field. + size_mode_value = size_sp->GetValueAsUnsigned(0); + uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1; + short_mode = (size_mode_value & mode_mask) == 0; + } + + if (short_mode) { + ValueObjectSP location_sp = short_sp->GetChildMemberWithName("__data_"); + if (using_bitmasks) + size = (layout == StringLayout::DSC) ? size_mode_value + : ((size_mode_value >> 1) % 256); + + if (!location_sp) + return {}; + + // When the small-string optimization takes place, the data must fit in the + // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's + // likely that the string isn't initialized and we're reading garbage. + ExecutionContext exe_ctx(location_sp->GetExecutionContextRef()); + const std::optional<uint64_t> max_bytes = + location_sp->GetCompilerType().GetByteSize( + exe_ctx.GetBestExecutionContextScope()); + if (!max_bytes || size > *max_bytes) + return {}; + + return std::make_pair(size, location_sp); + } + + // we can use the layout_decider object as the data pointer + ValueObjectSP location_sp = l->GetChildMemberWithName("__data_"); + ValueObjectSP size_vo = l->GetChildMemberWithName("__size_"); + ValueObjectSP capacity_vo = l->GetChildMemberWithName("__cap_"); + if (!size_vo || !location_sp || !capacity_vo) + return {}; + size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); + uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); + if (!using_bitmasks && layout == StringLayout::CSD) + capacity *= 2; + if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || + capacity < size) + return {}; + return std::make_pair(size, location_sp); +} + +static bool +LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + ValueObjectSP location_sp, size_t size) { + if (size == 0) { + stream.Printf("L\"\""); + return true; + } + if (!location_sp) + return false; + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { + const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); + if (size > max_size) { + size = max_size; + options.SetIsTruncated(true); + } + } + + DataExtractor extractor; + const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size); + if (bytes_read < size) + return false; + + // std::wstring::size() is measured in 'characters', not bytes + TypeSystemClangSP scratch_ts_sp = + ScratchTypeSystemClang::GetForTarget(*valobj.GetTargetSP()); + if (!scratch_ts_sp) + return false; + + auto wchar_t_size = + scratch_ts_sp->GetBasicType(lldb::eBasicTypeWChar).GetByteSize(nullptr); + if (!wchar_t_size) + return false; + + options.SetData(std::move(extractor)); + options.SetStream(&stream); + options.SetPrefixToken("L"); + options.SetQuote('"'); + options.SetSourceSize(size); + options.SetBinaryZeroIsTerminator(false); + + switch (*wchar_t_size) { + case 1: + return StringPrinter::ReadBufferAndDumpToStream< + lldb_private::formatters::StringPrinter::StringElementType::UTF8>( + options); + break; + + case 2: + return StringPrinter::ReadBufferAndDumpToStream< + lldb_private::formatters::StringPrinter::StringElementType::UTF16>( + options); + break; + + case 4: + return StringPrinter::ReadBufferAndDumpToStream< + lldb_private::formatters::StringPrinter::StringElementType::UTF32>( + options); + } + return false; +} + +bool lldb_private::formatters::LibcxxWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + auto string_info = ExtractLibcxxStringInfo(valobj); + if (!string_info) + return false; + uint64_t size; + ValueObjectSP location_sp; + std::tie(size, location_sp) = *string_info; + + return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, + location_sp, size); +} + +template <StringPrinter::StringElementType element_type> +static bool +LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token, ValueObjectSP location_sp, + uint64_t size) { + + if (size == 0) { + stream.Printf("\"\""); + return true; + } + + if (!location_sp) + return false; + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + + if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { + const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); + if (size > max_size) { + size = max_size; + options.SetIsTruncated(true); + } + } + + { + DataExtractor extractor; + const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size); + if (bytes_read < size) + return false; + + options.SetData(std::move(extractor)); + } + options.SetStream(&stream); + if (prefix_token.empty()) + options.SetPrefixToken(nullptr); + else + options.SetPrefixToken(prefix_token); + options.SetQuote('"'); + options.SetSourceSize(size); + options.SetBinaryZeroIsTerminator(false); + return StringPrinter::ReadBufferAndDumpToStream<element_type>(options); +} + +template <StringPrinter::StringElementType element_type> +static bool +LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + auto string_info = ExtractLibcxxStringInfo(valobj); + if (!string_info) + return false; + uint64_t size; + ValueObjectSP location_sp; + std::tie(size, location_sp) = *string_info; + + return LibcxxStringSummaryProvider<element_type>( + valobj, stream, summary_options, prefix_token, location_sp, size); +} +template <StringPrinter::StringElementType element_type> +static bool formatStringImpl(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + StreamString scratch_stream; + const bool success = LibcxxStringSummaryProvider<element_type>( + valobj, scratch_stream, summary_options, prefix_token); + if (success) + stream << scratch_stream.GetData(); + else + stream << "Summary Unavailable"; + return true; +} + +bool lldb_private::formatters::LibcxxStringSummaryProviderASCII( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringImpl<StringPrinter::StringElementType::ASCII>( + valobj, stream, summary_options, ""); +} + +bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringImpl<StringPrinter::StringElementType::UTF16>( + valobj, stream, summary_options, "u"); +} + +bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringImpl<StringPrinter::StringElementType::UTF32>( + valobj, stream, summary_options, "U"); +} + +static std::tuple<bool, ValueObjectSP, size_t> +LibcxxExtractStringViewData(ValueObject& valobj) { + auto dataobj = GetChildMemberWithName( + valobj, {ConstString("__data_"), ConstString("__data")}); + auto sizeobj = GetChildMemberWithName( + valobj, {ConstString("__size_"), ConstString("__size")}); + if (!dataobj || !sizeobj) + return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {}); + + if (!dataobj->GetError().Success() || !sizeobj->GetError().Success()) + return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {}); + + bool success{false}; + uint64_t size = sizeobj->GetValueAsUnsigned(0, &success); + if (!success) + return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {}); + + return std::make_tuple(true,dataobj,size); +} + +template <StringPrinter::StringElementType element_type> +static bool formatStringViewImpl(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + + bool success; + ValueObjectSP dataobj; + size_t size; + std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj); + + if (!success) { + stream << "Summary Unavailable"; + return true; + } + + return LibcxxStringSummaryProvider<element_type>( + valobj, stream, summary_options, prefix_token, dataobj, size); +} + +bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringViewImpl<StringPrinter::StringElementType::ASCII>( + valobj, stream, summary_options, ""); +} + +bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringViewImpl<StringPrinter::StringElementType::UTF16>( + valobj, stream, summary_options, "u"); +} + +bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringViewImpl<StringPrinter::StringElementType::UTF32>( + valobj, stream, summary_options, "U"); +} + +bool lldb_private::formatters::LibcxxWStringViewSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + + bool success; + ValueObjectSP dataobj; + size_t size; + std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj); + + if (!success) { + stream << "Summary Unavailable"; + return true; + } + + return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, + dataobj, size); +} + +static bool +LibcxxChronoTimePointSecondsSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options, + const char *fmt) { + ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_"); + if (!ptr_sp) + return false; + ptr_sp = ptr_sp->GetChildMemberWithName("__rep_"); + if (!ptr_sp) + return false; + +#ifndef _WIN32 + // The date time in the chrono library is valid in the range + // [-32767-01-01T00:00:00Z, 32767-12-31T23:59:59Z]. A 64-bit time_t has a + // larger range, the function strftime is not able to format the entire range + // of time_t. The exact point has not been investigated; it's limited to + // chrono's range. + const std::time_t chrono_timestamp_min = + -1'096'193'779'200; // -32767-01-01T00:00:00Z + const std::time_t chrono_timestamp_max = + 971'890'963'199; // 32767-12-31T23:59:59Z +#else + const std::time_t chrono_timestamp_min = -43'200; // 1969-12-31T12:00:00Z + const std::time_t chrono_timestamp_max = + 32'536'850'399; // 3001-01-19T21:59:59 +#endif + + const std::time_t seconds = ptr_sp->GetValueAsSigned(0); + if (seconds < chrono_timestamp_min || seconds > chrono_timestamp_max) + stream.Printf("timestamp=%" PRId64 " s", static_cast<int64_t>(seconds)); + else { + std::array<char, 128> str; + std::size_t size = + std::strftime(str.data(), str.size(), fmt, gmtime(&seconds)); + if (size == 0) + return false; + + stream.Printf("date/time=%s timestamp=%" PRId64 " s", str.data(), + static_cast<int64_t>(seconds)); + } + + return true; +} + +bool lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options, + "%FT%H:%M:%SZ"); +} + +bool lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options, + "%FT%H:%M:%S"); +} + +static bool +LibcxxChronoTimepointDaysSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options, + const char *fmt) { + ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_"); + if (!ptr_sp) + return false; + ptr_sp = ptr_sp->GetChildMemberWithName("__rep_"); + if (!ptr_sp) + return false; + +#ifndef _WIN32 + // The date time in the chrono library is valid in the range + // [-32767-01-01Z, 32767-12-31Z]. A 32-bit time_t has a larger range, the + // function strftime is not able to format the entire range of time_t. The + // exact point has not been investigated; it's limited to chrono's range. + const int chrono_timestamp_min = -12'687'428; // -32767-01-01Z + const int chrono_timestamp_max = 11'248'737; // 32767-12-31Z +#else + const int chrono_timestamp_min = 0; // 1970-01-01Z + const int chrono_timestamp_max = 376'583; // 3001-01-19Z +#endif + + const int days = ptr_sp->GetValueAsSigned(0); + if (days < chrono_timestamp_min || days > chrono_timestamp_max) + stream.Printf("timestamp=%d days", days); + + else { + const std::time_t seconds = std::time_t(86400) * days; + + std::array<char, 128> str; + std::size_t size = + std::strftime(str.data(), str.size(), fmt, gmtime(&seconds)); + if (size == 0) + return false; + + stream.Printf("date=%s timestamp=%d days", str.data(), days); + } + + return true; +} + +bool lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options, + "%FZ"); +} + +bool lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options, + "%F"); +} + +bool lldb_private::formatters::LibcxxChronoMonthSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + // FIXME: These are the names used in the C++20 ostream operator. Since LLVM + // uses C++17 it's not possible to use the ostream operator directly. + static const std::array<std::string_view, 12> months = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"}; + + ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__m_"); + if (!ptr_sp) + return false; + + const unsigned month = ptr_sp->GetValueAsUnsigned(0); + if (month >= 1 && month <= 12) + stream << "month=" << months[month - 1]; + else + stream.Printf("month=%u", month); + + return true; +} + +bool lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + // FIXME: These are the names used in the C++20 ostream operator. Since LLVM + // uses C++17 it's not possible to use the ostream operator directly. + static const std::array<std::string_view, 7> weekdays = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}; + + ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__wd_"); + if (!ptr_sp) + return false; + + const unsigned weekday = ptr_sp->GetValueAsUnsigned(0); + if (weekday < 7) + stream << "weekday=" << weekdays[weekday]; + else + stream.Printf("weekday=%u", weekday); + + return true; +} + +bool lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__y_"); + if (!ptr_sp) + return false; + ptr_sp = ptr_sp->GetChildMemberWithName("__y_"); + if (!ptr_sp) + return false; + int year = ptr_sp->GetValueAsSigned(0); + + ptr_sp = valobj.GetChildMemberWithName("__m_"); + if (!ptr_sp) + return false; + ptr_sp = ptr_sp->GetChildMemberWithName("__m_"); + if (!ptr_sp) + return false; + const unsigned month = ptr_sp->GetValueAsUnsigned(0); + + ptr_sp = valobj.GetChildMemberWithName("__d_"); + if (!ptr_sp) + return false; + ptr_sp = ptr_sp->GetChildMemberWithName("__d_"); + if (!ptr_sp) + return false; + const unsigned day = ptr_sp->GetValueAsUnsigned(0); + + stream << "date="; + if (year < 0) { + stream << '-'; + year = -year; + } + stream.Printf("%04d-%02u-%02u", year, month, day); + + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h new file mode 100644 index 000000000000..5307b5251ca8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -0,0 +1,244 @@ +//===-- LibCxx.h ---------------------------------------------------*- C++ +//-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXX_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXX_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { + +/// Find a child member of \c obj_sp, trying all alternative names in order. +lldb::ValueObjectSP +GetChildMemberWithName(ValueObject &obj, + llvm::ArrayRef<ConstString> alternative_names); + +lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair); +lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair); + + +bool LibcxxStringSummaryProviderASCII( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::string + +bool LibcxxStringSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::u16string + +bool LibcxxStringSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::u32string + +bool LibcxxWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::wstring + +bool LibcxxStringViewSummaryProviderASCII( + ValueObject &valueObj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::string_view + +bool LibcxxStringViewSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::u16string_view + +bool LibcxxStringViewSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::u32string_view + +bool LibcxxWStringViewSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::wstring_view + +bool LibcxxStdSliceArraySummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::slice_array + +bool LibcxxSmartPointerSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions + &options); // libc++ std::shared_ptr<> and std::weak_ptr<> + +// libc++ std::unique_ptr<> +bool LibcxxUniquePointerSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool LibcxxFunctionSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::function<> + +SyntheticChildrenFrontEnd * +LibcxxVectorBoolSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +bool LibcxxContainerSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +/// Formatter for libc++ std::span<>. +bool LibcxxSpanSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +SyntheticChildrenFrontEnd * +LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +class LibcxxSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + + ~LibcxxSharedPtrSyntheticFrontEnd() override; + +private: + ValueObject *m_cntrl; +}; + +class LibcxxUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + + ~LibcxxUniquePtrSyntheticFrontEnd() override; + +private: + lldb::ValueObjectSP m_value_ptr_sp; + lldb::ValueObjectSP m_deleter_sp; +}; + +SyntheticChildrenFrontEnd * +LibcxxBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdVectorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdValarraySyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdSliceArraySyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdProxyArraySyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdMapSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd *LibcxxQueueFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd *LibcxxTupleFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxOptionalSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + +SyntheticChildrenFrontEnd * +LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + +SyntheticChildrenFrontEnd * +LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdRangesRefViewSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +bool LibcxxChronoSysSecondsSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::sys_seconds + +bool LibcxxChronoSysDaysSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::sys_days + +bool LibcxxChronoLocalSecondsSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::local_seconds + +bool LibcxxChronoLocalDaysSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::local_days + +bool LibcxxChronoMonthSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::month + +bool LibcxxChronoWeekdaySummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::weekday + +bool LibcxxChronoYearMonthDaySummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::year_month_day + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXX_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp new file mode 100644 index 000000000000..7f30dc186291 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp @@ -0,0 +1,151 @@ +//===-- LibCxxAtomic.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 "LibCxxAtomic.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +// +// We are supporting two versions of libc++ std::atomic +// +// Given std::atomic<int> i; +// +// The previous version of std::atomic was laid out like this +// +// (lldb) frame var -L -R i +// 0x00007ffeefbff9a0: (std::__1::atomic<int>) i = { +// 0x00007ffeefbff9a0: std::__1::__atomic_base<int, true> = { +// 0x00007ffeefbff9a0: std::__1::__atomic_base<int, false> = { +// 0x00007ffeefbff9a0: __a_ = 5 +// } +// } +// } +// +// In this case we need to obtain __a_ and the current version is laid out as so +// +// (lldb) frame var -L -R i +// 0x00007ffeefbff9b0: (std::__1::atomic<int>) i = { +// 0x00007ffeefbff9b0: std::__1::__atomic_base<int, true> = { +// 0x00007ffeefbff9b0: std::__1::__atomic_base<int, false> = { +// 0x00007ffeefbff9b0: __a_ = { +// 0x00007ffeefbff9b0: std::__1::__cxx_atomic_base_impl<int> = { +// 0x00007ffeefbff9b0: __a_value = 5 +// } +// } +// } +// } +//} +// +// In this case we need to obtain __a_value +// +// The below method covers both cases and returns the relevant member as a +// ValueObjectSP +// +ValueObjectSP +lldb_private::formatters::GetLibCxxAtomicValue(ValueObject &valobj) { + ValueObjectSP non_sythetic = valobj.GetNonSyntheticValue(); + if (!non_sythetic) + return {}; + + ValueObjectSP member__a_ = non_sythetic->GetChildMemberWithName("__a_"); + if (!member__a_) + return {}; + + ValueObjectSP member__a_value = + member__a_->GetChildMemberWithName("__a_value"); + if (!member__a_value) + return member__a_; + + return member__a_value; +} + +bool lldb_private::formatters::LibCxxAtomicSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + + if (ValueObjectSP atomic_value = GetLibCxxAtomicValue(valobj)) { + std::string summary; + if (atomic_value->GetSummaryAsCString(summary, options) && + summary.size() > 0) { + stream.Printf("%s", summary.c_str()); + return true; + } + } + + return false; +} + +namespace lldb_private { +namespace formatters { +class LibcxxStdAtomicSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdAtomicSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_real_child = nullptr; +}; +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + LibcxxStdAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) {} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::Update() { + ValueObjectSP atomic_value = GetLibCxxAtomicValue(m_backend); + if (atomic_value) + m_real_child = GetLibCxxAtomicValue(m_backend).get(); + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdAtomicSyntheticFrontEnd::CalculateNumChildren() { + return m_real_child ? 1 : 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (idx == 0) + return m_real_child->GetSP()->Clone(ConstString("Value")); + return nullptr; +} + +size_t lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + return name == "Value" ? 0 : UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new LibcxxStdAtomicSyntheticFrontEnd(valobj_sp); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h new file mode 100644 index 000000000000..6fcceb645c7b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h @@ -0,0 +1,33 @@ +//===-- LibCxxAtomic.h -------------------------------------------*- C++ +//-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXATOMIC_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXATOMIC_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { + +lldb::ValueObjectSP GetLibCxxAtomicValue(ValueObject &valobj); + +bool LibCxxAtomicSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +SyntheticChildrenFrontEnd * +LibcxxAtomicSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXATOMIC_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp new file mode 100644 index 000000000000..bd9c72497664 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp @@ -0,0 +1,119 @@ +//===-- LibCxxInitializerList.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { +class LibcxxInitializerListSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibcxxInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxInitializerListSyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_start = nullptr; + CompilerType m_element_type; + uint32_t m_element_size = 0; + size_t m_num_elements = 0; +}; +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + LibcxxInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + ~LibcxxInitializerListSyntheticFrontEnd() { + // this needs to stay around because it's a child object who will follow its + // parent's life cycle + // delete m_start; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxInitializerListSyntheticFrontEnd::CalculateNumChildren() { + m_num_elements = 0; + ValueObjectSP size_sp(m_backend.GetChildMemberWithName("__size_")); + if (size_sp) + m_num_elements = size_sp->GetValueAsUnsigned(0); + return m_num_elements; +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibcxxInitializerListSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (!m_start) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::Update() { + m_start = nullptr; + m_num_elements = 0; + m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0); + if (!m_element_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) { + m_element_size = *size; + // Store raw pointers or end up with a circular dependency. + m_start = m_backend.GetChildMemberWithName("__begin_").get(); + } + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxInitializerListSyntheticFrontEnd(valobj_sp) + : nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp new file mode 100644 index 000000000000..d7cfeb30557c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp @@ -0,0 +1,426 @@ +//===-- LibCxxList.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 "LibCxx.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class ListEntry { +public: + ListEntry() = default; + ListEntry(ValueObjectSP entry_sp) : m_entry_sp(std::move(entry_sp)) {} + ListEntry(ValueObject *entry) + : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + + ListEntry next() { + if (!m_entry_sp) + return ListEntry(); + return ListEntry(m_entry_sp->GetChildMemberWithName("__next_")); + } + + ListEntry prev() { + if (!m_entry_sp) + return ListEntry(); + return ListEntry(m_entry_sp->GetChildMemberWithName("__prev_")); + } + + uint64_t value() const { + if (!m_entry_sp) + return 0; + return m_entry_sp->GetValueAsUnsigned(0); + } + + bool null() { return (value() == 0); } + + explicit operator bool() { return GetEntry() && !null(); } + + ValueObjectSP GetEntry() { return m_entry_sp; } + + void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; } + + bool operator==(const ListEntry &rhs) const { return value() == rhs.value(); } + + bool operator!=(const ListEntry &rhs) const { return !(*this == rhs); } + +private: + ValueObjectSP m_entry_sp; +}; + +class ListIterator { +public: + ListIterator() = default; + ListIterator(ListEntry entry) : m_entry(std::move(entry)) {} + ListIterator(ValueObjectSP entry) : m_entry(std::move(entry)) {} + ListIterator(ValueObject *entry) : m_entry(entry) {} + + ValueObjectSP value() { return m_entry.GetEntry(); } + + ValueObjectSP advance(size_t count) { + if (count == 0) + return m_entry.GetEntry(); + if (count == 1) { + next(); + return m_entry.GetEntry(); + } + while (count > 0) { + next(); + count--; + if (m_entry.null()) + return lldb::ValueObjectSP(); + } + return m_entry.GetEntry(); + } + + bool operator==(const ListIterator &rhs) const { + return (rhs.m_entry == m_entry); + } + +protected: + void next() { m_entry = m_entry.next(); } + + void prev() { m_entry = m_entry.prev(); } + +private: + ListEntry m_entry; +}; + +class AbstractListFrontEnd : public SyntheticChildrenFrontEnd { +public: + size_t GetIndexOfChildWithName(ConstString name) override { + return ExtractIndexFromString(name.GetCString()); + } + bool MightHaveChildren() override { return true; } + lldb::ChildCacheState Update() override; + +protected: + AbstractListFrontEnd(ValueObject &valobj) + : SyntheticChildrenFrontEnd(valobj) {} + + size_t m_count = 0; + ValueObject *m_head = nullptr; + + static constexpr bool g_use_loop_detect = true; + size_t m_loop_detected = 0; // The number of elements that have had loop + // detection run over them. + ListEntry m_slow_runner; // Used for loop detection + ListEntry m_fast_runner; // Used for loop detection + + size_t m_list_capping_size = 0; + CompilerType m_element_type; + std::map<size_t, ListIterator> m_iterators; + + bool HasLoop(size_t count); + ValueObjectSP GetItem(size_t idx); +}; + +class ForwardListFrontEnd : public AbstractListFrontEnd { +public: + ForwardListFrontEnd(ValueObject &valobj); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + lldb::ChildCacheState Update() override; +}; + +class ListFrontEnd : public AbstractListFrontEnd { +public: + ListFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~ListFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + +private: + lldb::addr_t m_node_address = 0; + ValueObject *m_tail = nullptr; +}; + +} // end anonymous namespace + +lldb::ChildCacheState AbstractListFrontEnd::Update() { + m_loop_detected = 0; + m_count = UINT32_MAX; + m_head = nullptr; + m_list_capping_size = 0; + m_slow_runner.SetEntry(nullptr); + m_fast_runner.SetEntry(nullptr); + m_iterators.clear(); + + if (m_backend.GetTargetSP()) + m_list_capping_size = + m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + if (m_list_capping_size == 0) + m_list_capping_size = 255; + + CompilerType list_type = m_backend.GetCompilerType(); + if (list_type.IsReferenceType()) + list_type = list_type.GetNonReferenceType(); + + if (list_type.GetNumTemplateArguments() == 0) + return lldb::ChildCacheState::eRefetch; + m_element_type = list_type.GetTypeTemplateArgument(0); + + return lldb::ChildCacheState::eRefetch; +} + +bool AbstractListFrontEnd::HasLoop(size_t count) { + if (!g_use_loop_detect) + return false; + // don't bother checking for a loop if we won't actually need to jump nodes + if (m_count < 2) + return false; + + if (m_loop_detected == 0) { + // This is the first time we are being run (after the last update). Set up + // the loop invariant for the first element. + m_slow_runner = ListEntry(m_head).next(); + m_fast_runner = m_slow_runner.next(); + m_loop_detected = 1; + } + + // Loop invariant: + // Loop detection has been run over the first m_loop_detected elements. If + // m_slow_runner == m_fast_runner then the loop has been detected after + // m_loop_detected elements. + const size_t steps_to_run = std::min(count, m_count); + while (m_loop_detected < steps_to_run && m_slow_runner && m_fast_runner && + m_slow_runner != m_fast_runner) { + + m_slow_runner = m_slow_runner.next(); + m_fast_runner = m_fast_runner.next().next(); + m_loop_detected++; + } + if (count <= m_loop_detected) + return false; // No loop in the first m_loop_detected elements. + if (!m_slow_runner || !m_fast_runner) + return false; // Reached the end of the list. Definitely no loops. + return m_slow_runner == m_fast_runner; +} + +ValueObjectSP AbstractListFrontEnd::GetItem(size_t idx) { + size_t advance = idx; + ListIterator current(m_head); + if (idx > 0) { + auto cached_iterator = m_iterators.find(idx - 1); + if (cached_iterator != m_iterators.end()) { + current = cached_iterator->second; + advance = 1; + } + } + ValueObjectSP value_sp = current.advance(advance); + m_iterators[idx] = current; + return value_sp; +} + +ForwardListFrontEnd::ForwardListFrontEnd(ValueObject &valobj) + : AbstractListFrontEnd(valobj) { + Update(); +} + +llvm::Expected<uint32_t> ForwardListFrontEnd::CalculateNumChildren() { + if (m_count != UINT32_MAX) + return m_count; + + ListEntry current(m_head); + m_count = 0; + while (current && m_count < m_list_capping_size) { + ++m_count; + current = current.next(); + } + return m_count; +} + +ValueObjectSP ForwardListFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= CalculateNumChildrenIgnoringErrors()) + return nullptr; + + if (!m_head) + return nullptr; + + if (HasLoop(idx + 1)) + return nullptr; + + ValueObjectSP current_sp = GetItem(idx); + if (!current_sp) + return nullptr; + + current_sp = current_sp->GetChildAtIndex(1); // get the __value_ child + if (!current_sp) + return nullptr; + + // we need to copy current_sp into a new object otherwise we will end up with + // all items named __value_ + DataExtractor data; + Status error; + current_sp->GetData(data, error); + if (error.Fail()) + return nullptr; + + return CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), data, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState ForwardListFrontEnd::Update() { + AbstractListFrontEnd::Update(); + + Status err; + ValueObjectSP backend_addr(m_backend.AddressOf(err)); + if (err.Fail() || !backend_addr) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_")); + if (!impl_sp) + return lldb::ChildCacheState::eRefetch; + impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp); + if (!impl_sp) + return lldb::ChildCacheState::eRefetch; + m_head = impl_sp->GetChildMemberWithName("__next_").get(); + return lldb::ChildCacheState::eRefetch; +} + +ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp) + : AbstractListFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> ListFrontEnd::CalculateNumChildren() { + if (m_count != UINT32_MAX) + return m_count; + if (!m_head || !m_tail || m_node_address == 0) + return 0; + ValueObjectSP size_alloc(m_backend.GetChildMemberWithName("__size_alloc_")); + if (size_alloc) { + ValueObjectSP value = GetFirstValueOfLibCXXCompressedPair(*size_alloc); + if (value) { + m_count = value->GetValueAsUnsigned(UINT32_MAX); + } + } + if (m_count != UINT32_MAX) { + return m_count; + } else { + uint64_t next_val = m_head->GetValueAsUnsigned(0); + uint64_t prev_val = m_tail->GetValueAsUnsigned(0); + if (next_val == 0 || prev_val == 0) + return 0; + if (next_val == m_node_address) + return 0; + if (next_val == prev_val) + return 1; + uint64_t size = 2; + ListEntry current(m_head); + while (current.next() && current.next().value() != m_node_address) { + size++; + current = current.next(); + if (size > m_list_capping_size) + break; + } + return m_count = (size - 1); + } +} + +lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(uint32_t idx) { + static ConstString g_value("__value_"); + static ConstString g_next("__next_"); + + if (idx >= CalculateNumChildrenIgnoringErrors()) + return lldb::ValueObjectSP(); + + if (!m_head || !m_tail || m_node_address == 0) + return lldb::ValueObjectSP(); + + if (HasLoop(idx + 1)) + return lldb::ValueObjectSP(); + + ValueObjectSP current_sp = GetItem(idx); + if (!current_sp) + return lldb::ValueObjectSP(); + + current_sp = current_sp->GetChildAtIndex(1); // get the __value_ child + if (!current_sp) + return lldb::ValueObjectSP(); + + if (current_sp->GetName() == g_next) { + ProcessSP process_sp(current_sp->GetProcessSP()); + if (!process_sp) + return lldb::ValueObjectSP(); + + // if we grabbed the __next_ pointer, then the child is one pointer deep-er + lldb::addr_t addr = current_sp->GetParent()->GetPointerValue(); + addr = addr + 2 * process_sp->GetAddressByteSize(); + ExecutionContext exe_ctx(process_sp); + current_sp = + CreateValueObjectFromAddress("__value_", addr, exe_ctx, m_element_type); + if (!current_sp) + return lldb::ValueObjectSP(); + } + + // we need to copy current_sp into a new object otherwise we will end up with + // all items named __value_ + DataExtractor data; + Status error; + current_sp->GetData(data, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromData(name.GetString(), data, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState ListFrontEnd::Update() { + AbstractListFrontEnd::Update(); + m_tail = nullptr; + m_node_address = 0; + + Status err; + ValueObjectSP backend_addr(m_backend.AddressOf(err)); + if (err.Fail() || !backend_addr) + return lldb::ChildCacheState::eRefetch; + m_node_address = backend_addr->GetValueAsUnsigned(0); + if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS) + return lldb::ChildCacheState::eRefetch; + ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__end_")); + if (!impl_sp) + return lldb::ChildCacheState::eRefetch; + m_head = impl_sp->GetChildMemberWithName("__next_").get(); + m_tail = impl_sp->GetChildMemberWithName("__prev_").get(); + return lldb::ChildCacheState::eRefetch; +} + +SyntheticChildrenFrontEnd *formatters::LibcxxStdListSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new ListFrontEnd(valobj_sp) : nullptr); +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxStdForwardListSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return valobj_sp ? new ForwardListFrontEnd(*valobj_sp) : nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp new file mode 100644 index 000000000000..5106a63d531f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp @@ -0,0 +1,499 @@ +//===-- LibCxxMap.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 "LibCxx.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" +#include <cstdint> +#include <locale> +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +// The flattened layout of the std::__tree_iterator::__ptr_ looks +// as follows: +// +// The following shows the contiguous block of memory: +// +// +-----------------------------+ class __tree_end_node +// __ptr_ | pointer __left_; | +// +-----------------------------+ class __tree_node_base +// | pointer __right_; | +// | __parent_pointer __parent_; | +// | bool __is_black_; | +// +-----------------------------+ class __tree_node +// | __node_value_type __value_; | <<< our key/value pair +// +-----------------------------+ +// +// where __ptr_ has type __iter_pointer. + +class MapEntry { +public: + MapEntry() = default; + explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} + explicit MapEntry(ValueObject *entry) + : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + + ValueObjectSP left() const { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetSyntheticChildAtOffset( + 0, m_entry_sp->GetCompilerType(), true); + } + + ValueObjectSP right() const { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetSyntheticChildAtOffset( + m_entry_sp->GetProcessSP()->GetAddressByteSize(), + m_entry_sp->GetCompilerType(), true); + } + + ValueObjectSP parent() const { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetSyntheticChildAtOffset( + 2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(), + m_entry_sp->GetCompilerType(), true); + } + + uint64_t value() const { + if (!m_entry_sp) + return 0; + return m_entry_sp->GetValueAsUnsigned(0); + } + + bool error() const { + if (!m_entry_sp) + return true; + return m_entry_sp->GetError().Fail(); + } + + bool null() const { return (value() == 0); } + + ValueObjectSP GetEntry() const { return m_entry_sp; } + + void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; } + + bool operator==(const MapEntry &rhs) const { + return (rhs.m_entry_sp.get() == m_entry_sp.get()); + } + +private: + ValueObjectSP m_entry_sp; +}; + +class MapIterator { +public: + MapIterator(ValueObject *entry, size_t depth = 0) + : m_entry(entry), m_max_depth(depth), m_error(false) {} + + MapIterator() = default; + + ValueObjectSP value() { return m_entry.GetEntry(); } + + ValueObjectSP advance(size_t count) { + ValueObjectSP fail; + if (m_error) + return fail; + size_t steps = 0; + while (count > 0) { + next(); + count--, steps++; + if (m_error || m_entry.null() || (steps > m_max_depth)) + return fail; + } + return m_entry.GetEntry(); + } + +private: + /// Mimicks libc++'s __tree_next algorithm, which libc++ uses + /// in its __tree_iteartor::operator++. + void next() { + if (m_entry.null()) + return; + MapEntry right(m_entry.right()); + if (!right.null()) { + m_entry = tree_min(std::move(right)); + return; + } + size_t steps = 0; + while (!is_left_child(m_entry)) { + if (m_entry.error()) { + m_error = true; + return; + } + m_entry.SetEntry(m_entry.parent()); + steps++; + if (steps > m_max_depth) { + m_entry = MapEntry(); + return; + } + } + m_entry = MapEntry(m_entry.parent()); + } + + /// Mimicks libc++'s __tree_min algorithm. + MapEntry tree_min(MapEntry x) { + if (x.null()) + return MapEntry(); + MapEntry left(x.left()); + size_t steps = 0; + while (!left.null()) { + if (left.error()) { + m_error = true; + return MapEntry(); + } + x = left; + left.SetEntry(x.left()); + steps++; + if (steps > m_max_depth) + return MapEntry(); + } + return x; + } + + bool is_left_child(const MapEntry &x) { + if (x.null()) + return false; + MapEntry rhs(x.parent()); + rhs.SetEntry(rhs.left()); + return x.value() == rhs.value(); + } + + MapEntry m_entry; + size_t m_max_depth = 0; + bool m_error = false; +}; + +namespace lldb_private { +namespace formatters { +class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdMapSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + /// Returns the ValueObject for the __tree_node type that + /// holds the key/value pair of the node at index \ref idx. + /// + /// \param[in] idx The child index that we're looking to get + /// the key/value pair for. + /// + /// \param[in] max_depth The maximum search depth after which + /// we stop trying to find the key/value + /// pair for. + /// + /// \returns On success, returns the ValueObjectSP corresponding + /// to the __tree_node's __value_ member (which holds + /// the key/value pair the formatter wants to display). + /// On failure, will return nullptr. + ValueObjectSP GetKeyValuePair(size_t idx, size_t max_depth); + + ValueObject *m_tree = nullptr; + ValueObject *m_root_node = nullptr; + CompilerType m_node_ptr_type; + size_t m_count = UINT32_MAX; + std::map<size_t, MapIterator> m_iterators; +}; + +class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + + ~LibCxxMapIteratorSyntheticFrontEnd() override = default; + +private: + ValueObjectSP m_pair_sp = nullptr; +}; +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: + LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren() { + if (m_count != UINT32_MAX) + return m_count; + + if (m_tree == nullptr) + return 0; + + ValueObjectSP size_node(m_tree->GetChildMemberWithName("__pair3_")); + if (!size_node) + return 0; + + size_node = GetFirstValueOfLibCXXCompressedPair(*size_node); + + if (!size_node) + return 0; + + m_count = size_node->GetValueAsUnsigned(0); + return m_count; +} + +ValueObjectSP +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetKeyValuePair( + size_t idx, size_t max_depth) { + MapIterator iterator(m_root_node, max_depth); + + size_t advance_by = idx; + if (idx > 0) { + // If we have already created the iterator for the previous + // index, we can start from there and advance by 1. + auto cached_iterator = m_iterators.find(idx - 1); + if (cached_iterator != m_iterators.end()) { + iterator = cached_iterator->second; + advance_by = 1; + } + } + + ValueObjectSP iterated_sp(iterator.advance(advance_by)); + if (!iterated_sp) + // this tree is garbage - stop + return nullptr; + + if (!m_node_ptr_type.IsValid()) + return nullptr; + + // iterated_sp is a __iter_pointer at this point. + // We can cast it to a __node_pointer (which is what libc++ does). + auto value_type_sp = iterated_sp->Cast(m_node_ptr_type); + if (!value_type_sp) + return nullptr; + + // Finally, get the key/value pair. + value_type_sp = value_type_sp->GetChildMemberWithName("__value_"); + if (!value_type_sp) + return nullptr; + + m_iterators[idx] = iterator; + + return value_type_sp; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + static ConstString g_cc_("__cc_"), g_cc("__cc"); + static ConstString g_nc("__nc"); + uint32_t num_children = CalculateNumChildrenIgnoringErrors(); + if (idx >= num_children) + return nullptr; + + if (m_tree == nullptr || m_root_node == nullptr) + return nullptr; + + ValueObjectSP key_val_sp = GetKeyValuePair(idx, /*max_depth=*/num_children); + if (!key_val_sp) { + // this will stop all future searches until an Update() happens + m_tree = nullptr; + return nullptr; + } + + // at this point we have a valid + // we need to copy current_sp into a new object otherwise we will end up with + // all items named __value_ + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + auto potential_child_sp = key_val_sp->Clone(ConstString(name.GetString())); + if (potential_child_sp) { + switch (potential_child_sp->GetNumChildrenIgnoringErrors()) { + case 1: { + auto child0_sp = potential_child_sp->GetChildAtIndex(0); + if (child0_sp && + (child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc)) + potential_child_sp = child0_sp->Clone(ConstString(name.GetString())); + break; + } + case 2: { + auto child0_sp = potential_child_sp->GetChildAtIndex(0); + auto child1_sp = potential_child_sp->GetChildAtIndex(1); + if (child0_sp && + (child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc) && + child1_sp && child1_sp->GetName() == g_nc) + potential_child_sp = child0_sp->Clone(ConstString(name.GetString())); + break; + } + } + } + return potential_child_sp; +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { + m_count = UINT32_MAX; + m_tree = m_root_node = nullptr; + m_iterators.clear(); + m_tree = m_backend.GetChildMemberWithName("__tree_").get(); + if (!m_tree) + return lldb::ChildCacheState::eRefetch; + m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get(); + m_node_ptr_type = + m_tree->GetCompilerType().GetDirectNestedTypeWithName("__node_pointer"); + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + return ExtractIndexFromString(name.GetCString()); +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxStdMapSyntheticFrontEnd(valobj_sp) : nullptr); +} + +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() { + m_pair_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + // m_backend is a std::map::iterator + // ...which is a __map_iterator<__tree_iterator<..., __node_pointer, ...>> + // + // Then, __map_iterator::__i_ is a __tree_iterator + auto tree_iter_sp = valobj_sp->GetChildMemberWithName("__i_"); + if (!tree_iter_sp) + return lldb::ChildCacheState::eRefetch; + + // Type is __tree_iterator::__node_pointer + // (We could alternatively also get this from the template argument) + auto node_pointer_type = + tree_iter_sp->GetCompilerType().GetDirectNestedTypeWithName( + "__node_pointer"); + if (!node_pointer_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + // __ptr_ is a __tree_iterator::__iter_pointer + auto iter_pointer_sp = tree_iter_sp->GetChildMemberWithName("__ptr_"); + if (!iter_pointer_sp) + return lldb::ChildCacheState::eRefetch; + + // Cast the __iter_pointer to a __node_pointer (which stores our key/value + // pair) + auto node_pointer_sp = iter_pointer_sp->Cast(node_pointer_type); + if (!node_pointer_sp) + return lldb::ChildCacheState::eRefetch; + + auto key_value_sp = node_pointer_sp->GetChildMemberWithName("__value_"); + if (!key_value_sp) + return lldb::ChildCacheState::eRefetch; + + // Create the synthetic child, which is a pair where the key and value can be + // retrieved by querying the synthetic frontend for + // GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second") + // respectively. + // + // std::map stores the actual key/value pair in value_type::__cc_ (or + // previously __cc). + key_value_sp = key_value_sp->Clone(ConstString("pair")); + if (key_value_sp->GetNumChildrenIgnoringErrors() == 1) { + auto child0_sp = key_value_sp->GetChildAtIndex(0); + if (child0_sp && + (child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc")) + key_value_sp = child0_sp->Clone(ConstString("pair")); + } + + m_pair_sp = key_value_sp; + + return lldb::ChildCacheState::eRefetch; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibCxxMapIteratorSyntheticFrontEnd::CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_pair_sp) + return nullptr; + + return m_pair_sp->GetChildAtIndex(idx); +} + +bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_pair_sp) + return UINT32_MAX; + + return m_pair_sp->GetIndexOfChildWithName(name); +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp) + : nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp new file mode 100644 index 000000000000..726f06523b97 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp @@ -0,0 +1,194 @@ +//===-- LibCxxProxyArray.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +/// Data formatter for libc++'s std::"proxy_array". +/// +/// A proxy_array's are created by using: +/// std::gslice_array operator[](const std::gslice& gslicearr); +/// std::mask_array operator[](const std::valarray<bool>& boolarr); +/// std::indirect_array operator[](const std::valarray<std::size_t>& indarr); +/// +/// These arrays have the following members: +/// - __vp_ points to std::valarray::__begin_ +/// - __1d_ an array of offsets of the elements from @a __vp_ +class LibcxxStdProxyArraySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdProxyArraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdProxyArraySyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + /// A non-owning pointer to the array's __vp_. + ValueObject *m_base = nullptr; + /// The type of the array's template argument T. + CompilerType m_element_type; + /// The sizeof the array's template argument T. + uint32_t m_element_size = 0; + + /// A non-owning pointer to the array's __1d_.__begin_. + ValueObject *m_start = nullptr; + /// A non-owning pointer to the array's __1d_.__end_. + ValueObject *m_finish = nullptr; + /// The type of the __1d_ array's template argument T (size_t). + CompilerType m_element_type_size_t; + /// The sizeof the __1d_ array's template argument T (size_t) + uint32_t m_element_size_size_t = 0; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: + LibcxxStdProxyArraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: + ~LibcxxStdProxyArraySyntheticFrontEnd() { + // these need to stay around because they are child objects who will follow + // their parent's life cycle + // delete m_base; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdProxyArraySyntheticFrontEnd::CalculateNumChildren() { + + if (!m_start || !m_finish) + return 0; + uint64_t start_val = m_start->GetValueAsUnsigned(0); + uint64_t finish_val = m_finish->GetValueAsUnsigned(0); + + if (start_val == 0 || finish_val == 0) + return 0; + + if (start_val >= finish_val) + return 0; + + size_t num_children = (finish_val - start_val); + if (num_children % m_element_size_size_t) + return 0; + return num_children / m_element_size_size_t; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_base) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_element_size_size_t; + offset = offset + m_start->GetValueAsUnsigned(0); + + lldb::ValueObjectSP indirect = CreateValueObjectFromAddress( + "", offset, m_backend.GetExecutionContextRef(), m_element_type_size_t); + if (!indirect) + return lldb::ValueObjectSP(); + + const size_t value = indirect->GetValueAsUnsigned(0); + if (!value) + return lldb::ValueObjectSP(); + + offset = value * m_element_size; + offset = offset + m_base->GetValueAsUnsigned(0); + + StreamString name; + name.Printf("[%" PRIu64 "] -> [%zu]", (uint64_t)idx, value); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd::Update() { + m_base = nullptr; + m_start = nullptr; + m_finish = nullptr; + + CompilerType type = m_backend.GetCompilerType(); + if (type.GetNumTemplateArguments() == 0) + return ChildCacheState::eRefetch; + + m_element_type = type.GetTypeTemplateArgument(0); + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) + m_element_size = *size; + + if (m_element_size == 0) + return ChildCacheState::eRefetch; + + ValueObjectSP vector = m_backend.GetChildMemberWithName("__1d_"); + if (!vector) + return ChildCacheState::eRefetch; + + type = vector->GetCompilerType(); + if (type.GetNumTemplateArguments() == 0) + return ChildCacheState::eRefetch; + + m_element_type_size_t = type.GetTypeTemplateArgument(0); + if (std::optional<uint64_t> size = m_element_type_size_t.GetByteSize(nullptr)) + m_element_size_size_t = *size; + + if (m_element_size_size_t == 0) + return ChildCacheState::eRefetch; + + ValueObjectSP base = m_backend.GetChildMemberWithName("__vp_"); + ValueObjectSP start = vector->GetChildMemberWithName("__begin_"); + ValueObjectSP finish = vector->GetChildMemberWithName("__end_"); + if (!base || !start || !finish) + return ChildCacheState::eRefetch; + + m_base = base.get(); + m_start = start.get(); + m_finish = finish.get(); + + return ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_base) + return std::numeric_limits<size_t>::max(); + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return new LibcxxStdProxyArraySyntheticFrontEnd(valobj_sp); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp new file mode 100644 index 000000000000..5b459a17fe29 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp @@ -0,0 +1,65 @@ +//===-- LibCxxQueue.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 "LibCxx.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + +class QueueFrontEnd : public SyntheticChildrenFrontEnd { +public: + QueueFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { + Update(); + } + + size_t GetIndexOfChildWithName(ConstString name) override { + return m_container_sp ? m_container_sp->GetIndexOfChildWithName(name) + : UINT32_MAX; + } + + bool MightHaveChildren() override { return true; } + lldb::ChildCacheState Update() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_container_sp ? m_container_sp->GetNumChildren() : 0; + } + + ValueObjectSP GetChildAtIndex(uint32_t idx) override { + return m_container_sp ? m_container_sp->GetChildAtIndex(idx) + : nullptr; + } + +private: + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + ValueObject* m_container_sp = nullptr; +}; +} // namespace + +lldb::ChildCacheState QueueFrontEnd::Update() { + m_container_sp = nullptr; + ValueObjectSP c_sp = m_backend.GetChildMemberWithName("c"); + if (!c_sp) + return lldb::ChildCacheState::eRefetch; + m_container_sp = c_sp->GetSyntheticValue().get(); + return lldb::ChildCacheState::eRefetch; +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxQueueFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new QueueFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxRangesRefView.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxRangesRefView.cpp new file mode 100644 index 000000000000..01a7b8f142ec --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxRangesRefView.cpp @@ -0,0 +1,88 @@ +//===-- LibCxxRangesRefView.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include "llvm/ADT/APSInt.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +class LibcxxStdRangesRefViewSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibcxxStdRangesRefViewSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdRangesRefViewSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override { + // __range_ will be the sole child of this type + return 1; + } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + // Since we only have a single child, return it + assert(idx == 0); + return m_range_sp; + } + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override { return true; } + + size_t GetIndexOfChildWithName(ConstString name) override { + // We only have a single child + return 0; + } + +private: + /// Pointer to the dereferenced __range_ member + lldb::ValueObjectSP m_range_sp = nullptr; +}; + +lldb_private::formatters::LibcxxStdRangesRefViewSyntheticFrontEnd:: + LibcxxStdRangesRefViewSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdRangesRefViewSyntheticFrontEnd::Update() { + ValueObjectSP range_ptr = + GetChildMemberWithName(m_backend, {ConstString("__range_")}); + if (!range_ptr) + return lldb::ChildCacheState::eRefetch; + + lldb_private::Status error; + m_range_sp = range_ptr->Dereference(error); + + return error.Success() ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +lldb_private::SyntheticChildrenFrontEnd * +LibcxxStdRangesRefViewSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + CompilerType type = valobj_sp->GetCompilerType(); + if (!type.IsValid()) + return nullptr; + return new LibcxxStdRangesRefViewSyntheticFrontEnd(valobj_sp); +} + +} // namespace formatters +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp new file mode 100644 index 000000000000..c33d8e91af70 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp @@ -0,0 +1,166 @@ +//===-- LibCxxSliceArray.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +bool LibcxxStdSliceArraySummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + ValueObjectSP obj = valobj.GetNonSyntheticValue(); + if (!obj) + return false; + + ValueObjectSP ptr_sp = obj->GetChildMemberWithName("__size_"); + if (!ptr_sp) + return false; + const size_t size = ptr_sp->GetValueAsUnsigned(0); + + ptr_sp = obj->GetChildMemberWithName("__stride_"); + if (!ptr_sp) + return false; + const size_t stride = ptr_sp->GetValueAsUnsigned(0); + + stream.Printf("stride=%zu size=%zu", stride, size); + + return true; +} + +/// Data formatter for libc++'s std::slice_array. +/// +/// A slice_array is created by using: +/// operator[](std::slice slicearr); +/// and std::slice is created by: +/// slice(std::size_t start, std::size_t size, std::size_t stride); +/// The std::slice_array has the following members: +/// - __vp_ points to std::valarray::__begin_ + @a start +/// - __size_ is @a size +/// - __stride_is @a stride +class LibcxxStdSliceArraySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdSliceArraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdSliceArraySyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + /// A non-owning pointer to slice_array.__vp_. + ValueObject *m_start = nullptr; + /// slice_array.__size_. + size_t m_size = 0; + /// slice_array.__stride_. + size_t m_stride = 0; + /// The type of slice_array's template argument T. + CompilerType m_element_type; + /// The sizeof slice_array's template argument T. + uint32_t m_element_size = 0; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: + LibcxxStdSliceArraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: + ~LibcxxStdSliceArraySyntheticFrontEnd() { + // these need to stay around because they are child objects who will follow + // their parent's life cycle + // delete m_start; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdSliceArraySyntheticFrontEnd::CalculateNumChildren() { + return m_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_start) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_stride * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd::Update() { + m_start = nullptr; + + CompilerType type = m_backend.GetCompilerType(); + if (type.GetNumTemplateArguments() == 0) + return ChildCacheState::eRefetch; + + m_element_type = type.GetTypeTemplateArgument(0); + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) + m_element_size = *size; + + if (m_element_size == 0) + return ChildCacheState::eRefetch; + + ValueObjectSP start = m_backend.GetChildMemberWithName("__vp_"); + ValueObjectSP size = m_backend.GetChildMemberWithName("__size_"); + ValueObjectSP stride = m_backend.GetChildMemberWithName("__stride_"); + + if (!start || !size || !stride) + return ChildCacheState::eRefetch; + + m_start = start.get(); + m_size = size->GetValueAsUnsigned(0); + m_stride = stride->GetValueAsUnsigned(0); + + return ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start) + return std::numeric_limits<size_t>::max(); + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return new LibcxxStdSliceArraySyntheticFrontEnd(valobj_sp); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp new file mode 100644 index 000000000000..9895f336bfd0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp @@ -0,0 +1,153 @@ +//===-- LibCxxSpan.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include "llvm/ADT/APSInt.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +class LibcxxStdSpanSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdSpanSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdSpanSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + /// Determines properties of the std::span<> associated with this object + // + // std::span can either be instantiated with a compile-time known + // extent or a std::dynamic_extent (this is the default if only the + // type template argument is provided). The layout of std::span + // depends on whether the extent is dynamic or not. For static + // extents (e.g., std::span<int, 9>): + // + // (std::__1::span<const int, 9>) s = { + // __data = 0x000000016fdff494 + // } + // + // For dynamic extents, e.g., std::span<int>, the layout is: + // + // (std::__1::span<const int, 18446744073709551615>) s = { + // __data = 0x000000016fdff494 + // __size = 6 + // } + // + // This function checks for a '__size' member to determine the number + // of elements in the span. If no such member exists, we get the size + // from the only other place it can be: the template argument. + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_start = nullptr; ///< First element of span. Held, not owned. + CompilerType m_element_type{}; ///< Type of span elements. + size_t m_num_elements = 0; ///< Number of elements in span. + uint32_t m_element_size = 0; ///< Size in bytes of each span element. +}; + +lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + LibcxxStdSpanSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdSpanSyntheticFrontEnd::CalculateNumChildren() { + return m_num_elements; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_start) + return {}; + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::Update() { + // Get element type. + ValueObjectSP data_type_finder_sp = GetChildMemberWithName( + m_backend, {ConstString("__data_"), ConstString("__data")}); + if (!data_type_finder_sp) + return lldb::ChildCacheState::eRefetch; + + m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType(); + + // Get element size. + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) { + m_element_size = *size; + + // Get data. + if (m_element_size > 0) { + m_start = data_type_finder_sp.get(); + } + + // Get number of elements. + if (auto size_sp = GetChildMemberWithName( + m_backend, {ConstString("__size_"), ConstString("__size")})) { + m_num_elements = size_sp->GetValueAsUnsigned(0); + } else if (auto arg = + m_backend.GetCompilerType().GetIntegralTemplateArgument(1)) { + + m_num_elements = arg->value.getLimitedValue(); + } + } + + return lldb::ChildCacheState::eReuse; +} + +bool lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + CompilerType type = valobj_sp->GetCompilerType(); + if (!type.IsValid() || type.GetNumTemplateArguments() != 2) + return nullptr; + return new LibcxxStdSpanSyntheticFrontEnd(valobj_sp); +} + +} // namespace formatters +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp new file mode 100644 index 000000000000..3e3259ab428d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp @@ -0,0 +1,95 @@ +//===-- LibCxxTuple.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 "LibCxx.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + +class TupleFrontEnd: public SyntheticChildrenFrontEnd { +public: + TupleFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { + Update(); + } + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + lldb::ChildCacheState Update() override; + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_elements.size(); + } + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + +private: + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + std::vector<ValueObject*> m_elements; + ValueObject* m_base = nullptr; +}; +} + +lldb::ChildCacheState TupleFrontEnd::Update() { + m_elements.clear(); + m_base = nullptr; + + ValueObjectSP base_sp; + base_sp = m_backend.GetChildMemberWithName("__base_"); + if (!base_sp) { + // Pre r304382 name of the base element. + base_sp = m_backend.GetChildMemberWithName("base_"); + } + if (!base_sp) + return lldb::ChildCacheState::eRefetch; + m_base = base_sp.get(); + m_elements.assign(base_sp->GetCompilerType().GetNumDirectBaseClasses(), + nullptr); + return lldb::ChildCacheState::eRefetch; +} + +ValueObjectSP TupleFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= m_elements.size()) + return ValueObjectSP(); + if (!m_base) + return ValueObjectSP(); + if (m_elements[idx]) + return m_elements[idx]->GetSP(); + + CompilerType holder_type = + m_base->GetCompilerType().GetDirectBaseClassAtIndex(idx, nullptr); + if (!holder_type) + return ValueObjectSP(); + ValueObjectSP holder_sp = m_base->GetChildAtIndex(idx); + if (!holder_sp) + return ValueObjectSP(); + + ValueObjectSP elem_sp = holder_sp->GetChildAtIndex(0); + if (elem_sp) + m_elements[idx] = + elem_sp->Clone(ConstString(llvm::formatv("[{0}]", idx).str())).get(); + + if (m_elements[idx]) + return m_elements[idx]->GetSP(); + return ValueObjectSP(); +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxTupleFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new TupleFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp new file mode 100644 index 000000000000..93e7f4f4fd86 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp @@ -0,0 +1,388 @@ +//===-- LibCxxUnorderedMap.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 "LibCxx.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { +class LibcxxStdUnorderedMapSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + CompilerType m_element_type; + CompilerType m_node_type; + ValueObject *m_tree = nullptr; + size_t m_num_elements = 0; + ValueObject *m_next_element = nullptr; + std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache; +}; + +class LibCxxUnorderedMapIteratorSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibCxxUnorderedMapIteratorSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + lldb::ValueObjectSP m_pair_sp; ///< ValueObject for the key/value pair + ///< that the iterator currently points + ///< to. +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), + m_elements_cache() { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdUnorderedMapSyntheticFrontEnd::CalculateNumChildren() { + return m_num_elements; +} + +static void consumeInlineNamespace(llvm::StringRef &name) { + // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+:: + auto scratch = name; + if (scratch.consume_front("__") && std::isalnum(scratch[0])) { + scratch = scratch.drop_while([](char c) { return std::isalnum(c); }); + if (scratch.consume_front("::")) { + // Successfully consumed a namespace. + name = scratch; + } + } +} + +static bool isStdTemplate(ConstString type_name, llvm::StringRef type) { + llvm::StringRef name = type_name.GetStringRef(); + // The type name may be prefixed with `std::__<inline-namespace>::`. + if (name.consume_front("std::")) + consumeInlineNamespace(name); + return name.consume_front(type) && name.starts_with("<"); +} + +static bool isUnorderedMap(ConstString type_name) { + return isStdTemplate(type_name, "unordered_map") || + isStdTemplate(type_name, "unordered_multimap"); +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= CalculateNumChildrenIgnoringErrors()) + return lldb::ValueObjectSP(); + if (m_tree == nullptr) + return lldb::ValueObjectSP(); + + while (idx >= m_elements_cache.size()) { + if (m_next_element == nullptr) + return lldb::ValueObjectSP(); + + Status error; + ValueObjectSP node_sp = m_next_element->Dereference(error); + if (!node_sp || error.Fail()) + return lldb::ValueObjectSP(); + + ValueObjectSP value_sp = node_sp->GetChildMemberWithName("__value_"); + ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_"); + if (!hash_sp || !value_sp) { + if (!m_element_type) { + auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"}); + if (!p1_sp) + return nullptr; + + ValueObjectSP first_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); + if (!first_sp) + return nullptr; + + m_element_type = first_sp->GetCompilerType(); + m_element_type = m_element_type.GetTypeTemplateArgument(0); + m_element_type = m_element_type.GetPointeeType(); + m_node_type = m_element_type; + m_element_type = m_element_type.GetTypeTemplateArgument(0); + // This synthetic provider is used for both unordered_(multi)map and + // unordered_(multi)set. For unordered_map, the element type has an + // additional type layer, an internal struct (`__hash_value_type`) + // that wraps a std::pair. Peel away the internal wrapper type - whose + // structure is of no value to users, to expose the std::pair. This + // matches the structure returned by the std::map synthetic provider. + if (isUnorderedMap(m_backend.GetTypeName())) { + std::string name; + CompilerType field_type = m_element_type.GetFieldAtIndex( + 0, name, nullptr, nullptr, nullptr); + CompilerType actual_type = field_type.GetTypedefedType(); + if (isStdTemplate(actual_type.GetTypeName(), "pair")) + m_element_type = actual_type; + } + } + if (!m_node_type) + return nullptr; + node_sp = m_next_element->Cast(m_node_type.GetPointerType()) + ->Dereference(error); + if (!node_sp || error.Fail()) + return nullptr; + + hash_sp = node_sp->GetChildMemberWithName("__hash_"); + if (!hash_sp) + return nullptr; + + value_sp = node_sp->GetChildMemberWithName("__value_"); + if (!value_sp) { + // clang-format off + // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an + // anonymous union. + // Child 0: __hash_node_base base class + // Child 1: __hash_ + // Child 2: anonymous union + // clang-format on + auto anon_union_sp = node_sp->GetChildAtIndex(2); + if (!anon_union_sp) + return nullptr; + + value_sp = anon_union_sp->GetChildMemberWithName("__value_"); + if (!value_sp) + return nullptr; + } + } + m_elements_cache.push_back( + {value_sp.get(), hash_sp->GetValueAsUnsigned(0)}); + m_next_element = node_sp->GetChildMemberWithName("__next_").get(); + if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0) + m_next_element = nullptr; + } + + std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx]; + if (!val_hash.first) + return lldb::ValueObjectSP(); + StreamString stream; + stream.Printf("[%" PRIu64 "]", (uint64_t)idx); + DataExtractor data; + Status error; + val_hash.first->GetData(data, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + const bool thread_and_frame_only_if_stopped = true; + ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock( + thread_and_frame_only_if_stopped); + return CreateValueObjectFromData(stream.GetString(), data, exe_ctx, + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() { + m_num_elements = 0; + m_next_element = nullptr; + m_elements_cache.clear(); + ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_"); + if (!table_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_"); + if (!p2_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp); + if (!num_elements_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP p1_sp = table_sp->GetChildMemberWithName("__p1_"); + if (!p1_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP value_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); + if (!value_sp) + return lldb::ChildCacheState::eRefetch; + + m_tree = value_sp->GetChildMemberWithName("__next_").get(); + if (m_tree == nullptr) + return lldb::ChildCacheState::eRefetch; + + m_num_elements = num_elements_sp->GetValueAsUnsigned(0); + + if (m_num_elements > 0) + m_next_element = m_tree; + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + return ExtractIndexFromString(name.GetCString()); +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::Update() { + m_pair_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + // Get the unordered_map::iterator + // m_backend is an 'unordered_map::iterator', aka a + // '__hash_map_iterator<__hash_table::iterator>' + // + // __hash_map_iterator::__i_ is a __hash_table::iterator (aka + // __hash_iterator<__node_pointer>) + auto hash_iter_sp = valobj_sp->GetChildMemberWithName("__i_"); + if (!hash_iter_sp) + return lldb::ChildCacheState::eRefetch; + + // Type is '__hash_iterator<__node_pointer>' + auto hash_iter_type = hash_iter_sp->GetCompilerType(); + if (!hash_iter_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + // Type is '__node_pointer' + auto node_pointer_type = hash_iter_type.GetTypeTemplateArgument(0); + if (!node_pointer_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + // Cast the __hash_iterator to a __node_pointer (which stores our key/value + // pair) + auto hash_node_sp = hash_iter_sp->Cast(node_pointer_type); + if (!hash_node_sp) + return lldb::ChildCacheState::eRefetch; + + auto key_value_sp = hash_node_sp->GetChildMemberWithName("__value_"); + if (!key_value_sp) { + // clang-format off + // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an + // anonymous union. + // Child 0: __hash_node_base base class + // Child 1: __hash_ + // Child 2: anonymous union + // clang-format on + auto anon_union_sp = hash_node_sp->GetChildAtIndex(2); + if (!anon_union_sp) + return lldb::ChildCacheState::eRefetch; + + key_value_sp = anon_union_sp->GetChildMemberWithName("__value_"); + if (!key_value_sp) + return lldb::ChildCacheState::eRefetch; + } + + // Create the synthetic child, which is a pair where the key and value can be + // retrieved by querying the synthetic frontend for + // GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second") + // respectively. + // + // std::unordered_map stores the actual key/value pair in + // __hash_value_type::__cc_ (or previously __cc). + auto potential_child_sp = key_value_sp->Clone(ConstString("pair")); + if (potential_child_sp) + if (potential_child_sp->GetNumChildrenIgnoringErrors() == 1) + if (auto child0_sp = potential_child_sp->GetChildAtIndex(0); + child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc") + potential_child_sp = child0_sp->Clone(ConstString("pair")); + + m_pair_sp = potential_child_sp; + + return lldb::ChildCacheState::eRefetch; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (m_pair_sp) + return m_pair_sp->GetChildAtIndex(idx); + return lldb::ValueObjectSP(); +} + +bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "first") + return 0; + if (name == "second") + return 1; + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp) + : nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp new file mode 100644 index 000000000000..99f94406e99a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp @@ -0,0 +1,145 @@ +//===-- LibCxxValarray.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { +class LibcxxStdValarraySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdValarraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdValarraySyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + /// A non-owning pointer to valarray's __begin_ member. + ValueObject *m_start = nullptr; + /// A non-owning pointer to valarray's __end_ member. + ValueObject *m_finish = nullptr; + /// The type of valarray's template argument T. + CompilerType m_element_type; + /// The sizeof valarray's template argument T. + uint32_t m_element_size = 0; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: + LibcxxStdValarraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: + ~LibcxxStdValarraySyntheticFrontEnd() { + // these need to stay around because they are child objects who will follow + // their parent's life cycle + // delete m_start; + // delete m_finish; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdValarraySyntheticFrontEnd::CalculateNumChildren() { + if (!m_start || !m_finish) + return 0; + uint64_t start_val = m_start->GetValueAsUnsigned(0); + uint64_t finish_val = m_finish->GetValueAsUnsigned(0); + + if (start_val == 0 || finish_val == 0) + return 0; + + if (start_val >= finish_val) + return 0; + + size_t num_children = (finish_val - start_val); + if (num_children % m_element_size) + return 0; + return num_children / m_element_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_start || !m_finish) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd::Update() { + m_start = m_finish = nullptr; + + CompilerType type = m_backend.GetCompilerType(); + if (type.GetNumTemplateArguments() == 0) + return ChildCacheState::eRefetch; + + m_element_type = type.GetTypeTemplateArgument(0); + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) + m_element_size = *size; + + if (m_element_size == 0) + return ChildCacheState::eRefetch; + + ValueObjectSP start = m_backend.GetChildMemberWithName("__begin_"); + ValueObjectSP finish = m_backend.GetChildMemberWithName("__end_"); + + if (!start || !finish) + return ChildCacheState::eRefetch; + + m_start = start.get(); + m_finish = finish.get(); + + return ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start || !m_finish) + return std::numeric_limits<size_t>::max(); + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdValarraySyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return new LibcxxStdValarraySyntheticFrontEnd(valobj_sp); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp new file mode 100644 index 000000000000..62794318e077 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp @@ -0,0 +1,281 @@ +//===-- LibCxxVariant.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 "LibCxxVariant.h" +#include "LibCxx.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Utility/LLDBAssert.h" + +#include "llvm/ADT/ScopeExit.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +// libc++ variant implementation contains two members that we care about both +// are contained in the __impl member. +// - __index which tells us which of the variadic template types is the active +// type for the variant +// - __data is a variadic union which recursively contains itself as member +// which refers to the tailing variadic types. +// - __head which refers to the leading non pack type +// - __value refers to the actual value contained +// - __tail which refers to the remaining pack types +// +// e.g. given std::variant<int,double,char> v1 +// +// (lldb) frame var -R v1.__impl.__data +//(... __union<... 0, int, double, char>) v1.__impl.__data = { +// ... +// __head = { +// __value = ... +// } +// __tail = { +// ... +// __head = { +// __value = ... +// } +// __tail = { +// ... +// __head = { +// __value = ... +// ... +// +// So given +// - __index equal to 0 the active value is contained in +// +// __data.__head.__value +// +// - __index equal to 1 the active value is contained in +// +// __data.__tail.__head.__value +// +// - __index equal to 2 the active value is contained in +// +// __data.__tail.__tail.__head.__value +// + +namespace { +// libc++ std::variant index could have one of three states +// 1) Valid, we can obtain it and its not variant_npos +// 2) Invalid, we can't obtain it or it is not a type we expect +// 3) NPos, its value is variant_npos which means the variant has no value +enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos }; + +uint64_t VariantNposValue(uint64_t index_byte_size) { + switch (index_byte_size) { + case 1: + return static_cast<uint8_t>(-1); + case 2: + return static_cast<uint16_t>(-1); + case 4: + return static_cast<uint32_t>(-1); + } + lldbassert(false && "Unknown index type size"); + return static_cast<uint32_t>(-1); // Fallback to stable ABI type. +} + +LibcxxVariantIndexValidity +LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) { + ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index")); + + if (!index_sp) + return LibcxxVariantIndexValidity::Invalid; + + // In the stable ABI, the type of __index is just int. + // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is + // enabled, the type can either be unsigned char/short/int depending on + // how many variant types there are. + // We only need to do this here when comparing against npos, because npos is + // just `-1`, but that translates to different unsigned values depending on + // the byte size. + CompilerType index_type = index_sp->GetCompilerType(); + + std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr); + if (!index_type_bytes) + return LibcxxVariantIndexValidity::Invalid; + + uint64_t npos_value = VariantNposValue(*index_type_bytes); + uint64_t index_value = index_sp->GetValueAsUnsigned(0); + + if (index_value == npos_value) + return LibcxxVariantIndexValidity::NPos; + + return LibcxxVariantIndexValidity::Valid; +} + +std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) { + ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index")); + + if (!index_sp) + return {}; + + return {index_sp->GetValueAsUnsigned(0)}; +} + +ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) { + ValueObjectSP data_sp(impl_sp->GetChildMemberWithName("__data")); + + if (!data_sp) + return ValueObjectSP{}; + + ValueObjectSP current_level = data_sp; + for (uint64_t n = index; n != 0; --n) { + ValueObjectSP tail_sp(current_level->GetChildMemberWithName("__tail")); + + if (!tail_sp) + return ValueObjectSP{}; + + current_level = tail_sp; + } + + return current_level->GetChildMemberWithName("__head"); +} +} // namespace + +namespace lldb_private { +namespace formatters { +bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + + ValueObjectSP impl_sp = GetChildMemberWithName( + *valobj_sp, {ConstString("__impl_"), ConstString("__impl")}); + + if (!impl_sp) + return false; + + LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); + + if (validity == LibcxxVariantIndexValidity::Invalid) + return false; + + if (validity == LibcxxVariantIndexValidity::NPos) { + stream.Printf(" No Value"); + return true; + } + + auto optional_index_value = LibcxxVariantIndexValue(impl_sp); + + if (!optional_index_value) + return false; + + uint64_t index_value = *optional_index_value; + + ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); + + if (!nth_head) + return false; + + CompilerType head_type = nth_head->GetCompilerType(); + + if (!head_type) + return false; + + CompilerType template_type = head_type.GetTypeTemplateArgument(1); + + if (!template_type) + return false; + + stream << " Active Type = " << template_type.GetDisplayTypeName() << " "; + + return true; +} +} // namespace formatters +} // namespace lldb_private + +namespace { +class VariantFrontEnd : public SyntheticChildrenFrontEnd { +public: + VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { + Update(); + } + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + lldb::ChildCacheState Update() override; + llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; } + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + +private: + size_t m_size = 0; +}; +} // namespace + +lldb::ChildCacheState VariantFrontEnd::Update() { + m_size = 0; + ValueObjectSP impl_sp = formatters::GetChildMemberWithName( + m_backend, {ConstString("__impl_"), ConstString("__impl")}); + if (!impl_sp) + return lldb::ChildCacheState::eRefetch; + + LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); + + if (validity == LibcxxVariantIndexValidity::Invalid) + return lldb::ChildCacheState::eRefetch; + + if (validity == LibcxxVariantIndexValidity::NPos) + return lldb::ChildCacheState::eReuse; + + m_size = 1; + + return lldb::ChildCacheState::eRefetch; +} + +ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= m_size) + return {}; + + ValueObjectSP impl_sp = formatters::GetChildMemberWithName( + m_backend, {ConstString("__impl_"), ConstString("__impl")}); + if (!impl_sp) + return {}; + + auto optional_index_value = LibcxxVariantIndexValue(impl_sp); + + if (!optional_index_value) + return {}; + + uint64_t index_value = *optional_index_value; + + ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); + + if (!nth_head) + return {}; + + CompilerType head_type = nth_head->GetCompilerType(); + + if (!head_type) + return {}; + + CompilerType template_type = head_type.GetTypeTemplateArgument(1); + + if (!template_type) + return {}; + + ValueObjectSP head_value(nth_head->GetChildMemberWithName("__value")); + + if (!head_value) + return {}; + + return head_value->Clone(ConstString("Value")); +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new VariantFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h new file mode 100644 index 000000000000..9488d94efda7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h @@ -0,0 +1,29 @@ +//===-- LibCxxVariant.h ----------------------------------------*- C++ -*--===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXVARIANT_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXVARIANT_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { +bool LibcxxVariantSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::variant<> + +SyntheticChildrenFrontEnd *LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXVARIANT_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp new file mode 100644 index 000000000000..461fed35164b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -0,0 +1,279 @@ +//===-- LibCxxVector.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { +class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdVectorSyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_start = nullptr; + ValueObject *m_finish = nullptr; + CompilerType m_element_type; + uint32_t m_element_size = 0; +}; + +class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override { return true; } + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + CompilerType m_bool_type; + ExecutionContextRef m_exe_ctx_ref; + uint64_t m_count = 0; + lldb::addr_t m_base_data_address = 0; + std::map<size_t, lldb::ValueObjectSP> m_children; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: + LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: + ~LibcxxStdVectorSyntheticFrontEnd() { + // these need to stay around because they are child objects who will follow + // their parent's life cycle + // delete m_start; + // delete m_finish; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren() { + if (!m_start || !m_finish) + return 0; + uint64_t start_val = m_start->GetValueAsUnsigned(0); + uint64_t finish_val = m_finish->GetValueAsUnsigned(0); + + if (start_val == 0 || finish_val == 0) + return 0; + + if (start_val >= finish_val) + return 0; + + size_t num_children = (finish_val - start_val); + if (num_children % m_element_size) + return 0; + return num_children / m_element_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_start || !m_finish) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() { + m_start = m_finish = nullptr; + ValueObjectSP data_type_finder_sp( + m_backend.GetChildMemberWithName("__end_cap_")); + if (!data_type_finder_sp) + return lldb::ChildCacheState::eRefetch; + + data_type_finder_sp = + GetFirstValueOfLibCXXCompressedPair(*data_type_finder_sp); + if (!data_type_finder_sp) + return lldb::ChildCacheState::eRefetch; + + m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType(); + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) { + m_element_size = *size; + + if (m_element_size > 0) { + // store raw pointers or end up with a circular dependency + m_start = m_backend.GetChildMemberWithName("__begin_").get(); + m_finish = m_backend.GetChildMemberWithName("__end_").get(); + } + } + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start || !m_finish) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: + LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_bool_type(), m_exe_ctx_ref(), + m_children() { + if (valobj_sp) { + Update(); + m_bool_type = + valobj_sp->GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeBool); + } +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxVectorBoolSyntheticFrontEnd::CalculateNumChildren() { + return m_count; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + auto iter = m_children.find(idx), end = m_children.end(); + if (iter != end) + return iter->second; + if (idx >= m_count) + return {}; + if (m_base_data_address == 0 || m_count == 0) + return {}; + if (!m_bool_type) + return {}; + size_t byte_idx = (idx >> 3); // divide by 8 to get byte index + size_t bit_index = (idx & 7); // efficient idx % 8 for bit index + lldb::addr_t byte_location = m_base_data_address + byte_idx; + ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); + if (!process_sp) + return {}; + uint8_t byte = 0; + uint8_t mask = 0; + Status err; + size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err); + if (err.Fail() || bytes_read == 0) + return {}; + mask = 1 << bit_index; + bool bit_set = ((byte & mask) != 0); + std::optional<uint64_t> size = m_bool_type.GetByteSize(nullptr); + if (!size) + return {}; + WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0)); + if (bit_set && buffer_sp && buffer_sp->GetBytes()) { + // regardless of endianness, anything non-zero is true + *(buffer_sp->GetBytes()) = 1; + } + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + ValueObjectSP retval_sp(CreateValueObjectFromData( + name.GetString(), + DataExtractor(buffer_sp, process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()), + m_exe_ctx_ref, m_bool_type)); + if (retval_sp) + m_children[idx] = retval_sp; + return retval_sp; +} + +/*(std::__1::vector<std::__1::allocator<bool> >) vBool = { + __begin_ = 0x00000001001000e0 + __size_ = 56 + __cap_alloc_ = { + std::__1::__libcpp_compressed_pair_imp<unsigned long, + std::__1::allocator<unsigned long> > = { + __first_ = 1 + } + } + }*/ + +lldb::ChildCacheState +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + ValueObjectSP size_sp(valobj_sp->GetChildMemberWithName("__size_")); + if (!size_sp) + return lldb::ChildCacheState::eRefetch; + m_count = size_sp->GetValueAsUnsigned(0); + if (!m_count) + return lldb::ChildCacheState::eReuse; + ValueObjectSP begin_sp(valobj_sp->GetChildMemberWithName("__begin_")); + if (!begin_sp) { + m_count = 0; + return lldb::ChildCacheState::eRefetch; + } + m_base_data_address = begin_sp->GetValueAsUnsigned(0); + if (!m_base_data_address) { + m_count = 0; + return lldb::ChildCacheState::eRefetch; + } + return lldb::ChildCacheState::eRefetch; +} + +size_t lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_count || !m_base_data_address) + return UINT32_MAX; + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + CompilerType type = valobj_sp->GetCompilerType(); + if (!type.IsValid() || type.GetNumTemplateArguments() == 0) + return nullptr; + CompilerType arg_type = type.GetTypeTemplateArgument(0); + if (arg_type.GetTypeName() == "bool") + return new LibcxxVectorBoolSyntheticFrontEnd(valobj_sp); + return new LibcxxStdVectorSyntheticFrontEnd(valobj_sp); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp new file mode 100644 index 000000000000..86bb575af5ca --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp @@ -0,0 +1,472 @@ +//===-- LibStdcpp.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 "LibStdcpp.h" +#include "LibCxx.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/VectorIterator.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { + /* + (std::_Rb_tree_iterator<std::pair<const int, std::basic_string<char, + std::char_traits<char>, std::allocator<char> > > >) ibeg = { + (_Base_ptr) _M_node = 0x0000000100103910 { + (std::_Rb_tree_color) _M_color = _S_black + (std::_Rb_tree_node_base::_Base_ptr) _M_parent = 0x00000001001038c0 + (std::_Rb_tree_node_base::_Base_ptr) _M_left = 0x0000000000000000 + (std::_Rb_tree_node_base::_Base_ptr) _M_right = 0x0000000000000000 + } + } + */ + +public: + explicit LibstdcppMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ExecutionContextRef m_exe_ctx_ref; + lldb::addr_t m_pair_address = 0; + CompilerType m_pair_type; + lldb::ValueObjectSP m_pair_sp; +}; + +class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit LibStdcppSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; +private: + + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + ValueObject* m_ptr_obj = nullptr; // Underlying pointer (held, not owned) + ValueObject* m_obj_obj = nullptr; // Underlying object (held, not owned) +}; + +} // end of anonymous namespace + +LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type(), + m_pair_sp() { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState LibstdcppMapIteratorSyntheticFrontEnd::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8); + + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + ValueObjectSP _M_node_sp(valobj_sp->GetChildMemberWithName("_M_node")); + if (!_M_node_sp) + return lldb::ChildCacheState::eRefetch; + + m_pair_address = _M_node_sp->GetValueAsUnsigned(0); + if (m_pair_address == 0) + return lldb::ChildCacheState::eRefetch; + + m_pair_address += (is_64bit ? 32 : 16); + + CompilerType my_type(valobj_sp->GetCompilerType()); + if (my_type.GetNumTemplateArguments() >= 1) { + CompilerType pair_type = my_type.GetTypeTemplateArgument(0); + if (!pair_type) + return lldb::ChildCacheState::eRefetch; + m_pair_type = pair_type; + } else + return lldb::ChildCacheState::eRefetch; + + return lldb::ChildCacheState::eReuse; +} + +llvm::Expected<uint32_t> +LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP +LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (m_pair_address != 0 && m_pair_type) { + if (!m_pair_sp) + m_pair_sp = CreateValueObjectFromAddress("pair", m_pair_address, + m_exe_ctx_ref, m_pair_type); + if (m_pair_sp) + return m_pair_sp->GetChildAtIndex(idx); + } + return lldb::ValueObjectSP(); +} + +bool LibstdcppMapIteratorSyntheticFrontEnd::MightHaveChildren() { return true; } + +size_t LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (name == "first") + return 0; + if (name == "second") + return 1; + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +/* + (lldb) fr var ibeg --ptr-depth 1 + (__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >) + ibeg = { + _M_current = 0x00000001001037a0 { + *_M_current = 1 + } + } + */ + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new VectorIteratorSyntheticFrontEnd( + valobj_sp, {ConstString("_M_current")}) + : nullptr); +} + +lldb_private::formatters::VectorIteratorSyntheticFrontEnd:: + VectorIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp, + llvm::ArrayRef<ConstString> item_names) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), + m_item_names(item_names), m_item_sp() { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState VectorIteratorSyntheticFrontEnd::Update() { + m_item_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP item_ptr = + formatters::GetChildMemberWithName(*valobj_sp, m_item_names); + if (!item_ptr) + return lldb::ChildCacheState::eRefetch; + if (item_ptr->GetValueAsUnsigned(0) == 0) + return lldb::ChildCacheState::eRefetch; + Status err; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + m_item_sp = CreateValueObjectFromAddress( + "item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref, + item_ptr->GetCompilerType().GetPointeeType()); + if (err.Fail()) + m_item_sp.reset(); + return lldb::ChildCacheState::eRefetch; +} + +llvm::Expected<uint32_t> +VectorIteratorSyntheticFrontEnd::CalculateNumChildren() { + return 1; +} + +lldb::ValueObjectSP +VectorIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx == 0) + return m_item_sp; + return lldb::ValueObjectSP(); +} + +bool VectorIteratorSyntheticFrontEnd::MightHaveChildren() { return true; } + +size_t VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (name == "item") + return 0; + return UINT32_MAX; +} + +bool lldb_private::formatters::LibStdcppStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + const bool scalar_is_load_addr = true; + AddressType addr_type; + lldb::addr_t addr_of_string = LLDB_INVALID_ADDRESS; + if (valobj.IsPointerOrReferenceType()) { + Status error; + ValueObjectSP pointee_sp = valobj.Dereference(error); + if (pointee_sp && error.Success()) + addr_of_string = pointee_sp->GetAddressOf(scalar_is_load_addr, &addr_type); + } else + addr_of_string = + valobj.GetAddressOf(scalar_is_load_addr, &addr_type); + if (addr_of_string != LLDB_INVALID_ADDRESS) { + switch (addr_type) { + case eAddressTypeLoad: { + ProcessSP process_sp(valobj.GetProcessSP()); + if (!process_sp) + return false; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + Status error; + lldb::addr_t addr_of_data = + process_sp->ReadPointerFromMemory(addr_of_string, error); + if (error.Fail() || addr_of_data == 0 || + addr_of_data == LLDB_INVALID_ADDRESS) + return false; + options.SetLocation(addr_of_data); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetNeedsZeroTermination(false); + options.SetBinaryZeroIsTerminator(true); + lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory( + addr_of_string + process_sp->GetAddressByteSize(), error); + if (error.Fail()) + return false; + options.SetSourceSize(size_of_data); + options.SetHasSourceSize(true); + + if (!StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF8>(options)) { + stream.Printf("Summary Unavailable"); + return true; + } else + return true; + } break; + case eAddressTypeHost: + break; + case eAddressTypeInvalid: + case eAddressTypeFile: + break; + } + } + return false; +} + +bool lldb_private::formatters::LibStdcppWStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + const bool scalar_is_load_addr = true; + AddressType addr_type; + lldb::addr_t addr_of_string = + valobj.GetAddressOf(scalar_is_load_addr, &addr_type); + if (addr_of_string != LLDB_INVALID_ADDRESS) { + switch (addr_type) { + case eAddressTypeLoad: { + ProcessSP process_sp(valobj.GetProcessSP()); + if (!process_sp) + return false; + + CompilerType wchar_compiler_type = + valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar); + + if (!wchar_compiler_type) + return false; + + // Safe to pass nullptr for exe_scope here. + std::optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr); + if (!size) + return false; + const uint32_t wchar_size = *size; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + Status error; + lldb::addr_t addr_of_data = + process_sp->ReadPointerFromMemory(addr_of_string, error); + if (error.Fail() || addr_of_data == 0 || + addr_of_data == LLDB_INVALID_ADDRESS) + return false; + options.SetLocation(addr_of_data); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetNeedsZeroTermination(false); + options.SetBinaryZeroIsTerminator(false); + lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory( + addr_of_string + process_sp->GetAddressByteSize(), error); + if (error.Fail()) + return false; + options.SetSourceSize(size_of_data); + options.SetHasSourceSize(true); + options.SetPrefixToken("L"); + + switch (wchar_size) { + case 8: + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF8>(options); + case 16: + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF16>(options); + case 32: + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF32>(options); + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; + } break; + case eAddressTypeHost: + break; + case eAddressTypeInvalid: + case eAddressTypeFile: + break; + } + } + return false; +} + +LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> +LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() { + return 1; +} + +lldb::ValueObjectSP +LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx == 0) + return m_ptr_obj->GetSP(); + if (idx == 1) { + if (m_ptr_obj && !m_obj_obj) { + Status error; + ValueObjectSP obj_obj = m_ptr_obj->Dereference(error); + if (error.Success()) + m_obj_obj = obj_obj->Clone(ConstString("object")).get(); + } + if (m_obj_obj) + return m_obj_obj->GetSP(); + } + return lldb::ValueObjectSP(); +} + +lldb::ChildCacheState LibStdcppSharedPtrSyntheticFrontEnd::Update() { + auto backend = m_backend.GetSP(); + if (!backend) + return lldb::ChildCacheState::eRefetch; + + auto valobj_sp = backend->GetNonSyntheticValue(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + auto ptr_obj_sp = valobj_sp->GetChildMemberWithName("_M_ptr"); + if (!ptr_obj_sp) + return lldb::ChildCacheState::eRefetch; + + m_ptr_obj = ptr_obj_sp->Clone(ConstString("pointer")).get(); + m_obj_obj = nullptr; + + return lldb::ChildCacheState::eRefetch; +} + +bool LibStdcppSharedPtrSyntheticFrontEnd::MightHaveChildren() { return true; } + +size_t LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (name == "pointer") + return 0; + if (name == "object" || name == "$$dereference$$") + return 1; + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + + ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("_M_ptr")); + if (!ptr_sp) + return false; + + ValueObjectSP usecount_sp( + valobj_sp->GetChildAtNamePath({"_M_refcount", "_M_pi", "_M_use_count"})); + if (!usecount_sp) + return false; + + if (ptr_sp->GetValueAsUnsigned(0) == 0 || + usecount_sp->GetValueAsUnsigned(0) == 0) { + stream.Printf("nullptr"); + return true; + } + + Status error; + ValueObjectSP pointee_sp = ptr_sp->Dereference(error); + if (pointee_sp && error.Success()) { + if (pointee_sp->DumpPrintableRepresentation( + stream, ValueObject::eValueObjectRepresentationStyleSummary, + lldb::eFormatInvalid, + ValueObject::PrintableRepresentationSpecialCases::eDisable, + false)) { + return true; + } + } + + stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0)); + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h new file mode 100644 index 000000000000..1c1c8fdb9ea4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -0,0 +1,67 @@ +//===-- LibStdcpp.h ---------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBSTDCPP_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBSTDCPP_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { +bool LibStdcppStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libcstdc++ c++11 std::string + +bool LibStdcppWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libcstdc++ c++11 std::wstring + +bool LibStdcppSmartPointerSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions + &options); // libstdc++ std::shared_ptr<> and std::weak_ptr<> + +bool LibStdcppUniquePointerSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libstdc++ std::unique_ptr<> + +SyntheticChildrenFrontEnd * +LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppTupleSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppOptionalSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBSTDCPP_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp new file mode 100644 index 000000000000..05199ba35b9a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp @@ -0,0 +1,112 @@ +//===-- LibStdcppTuple.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 "LibStdcpp.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/ConstString.h" + +#include <memory> +#include <vector> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class LibStdcppTupleSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit LibStdcppTupleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + std::vector<ValueObject*> m_members; +}; + +} // end of anonymous namespace + +LibStdcppTupleSyntheticFrontEnd::LibStdcppTupleSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + Update(); +} + +lldb::ChildCacheState LibStdcppTupleSyntheticFrontEnd::Update() { + m_members.clear(); + + ValueObjectSP valobj_backend_sp = m_backend.GetSP(); + if (!valobj_backend_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP next_child_sp = valobj_backend_sp->GetNonSyntheticValue(); + while (next_child_sp != nullptr) { + ValueObjectSP current_child = next_child_sp; + next_child_sp = nullptr; + + size_t child_count = current_child->GetNumChildrenIgnoringErrors(); + for (size_t i = 0; i < child_count; ++i) { + ValueObjectSP child_sp = current_child->GetChildAtIndex(i); + llvm::StringRef name_str = child_sp->GetName().GetStringRef(); + if (name_str.starts_with("std::_Tuple_impl<")) { + next_child_sp = child_sp; + } else if (name_str.starts_with("std::_Head_base<")) { + ValueObjectSP value_sp = + child_sp->GetChildMemberWithName("_M_head_impl"); + if (value_sp) { + StreamString name; + name.Printf("[%zd]", m_members.size()); + m_members.push_back(value_sp->Clone(ConstString(name.GetString())).get()); + } + } + } + } + + return lldb::ChildCacheState::eRefetch; +} + +bool LibStdcppTupleSyntheticFrontEnd::MightHaveChildren() { return true; } + +lldb::ValueObjectSP +LibStdcppTupleSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx < m_members.size() && m_members[idx]) + return m_members[idx]->GetSP(); + return lldb::ValueObjectSP(); +} + +llvm::Expected<uint32_t> +LibStdcppTupleSyntheticFrontEnd::CalculateNumChildren() { + return m_members.size(); +} + +size_t LibStdcppTupleSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + return ExtractIndexFromString(name.GetCString()); +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibStdcppTupleSyntheticFrontEnd(valobj_sp) : nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp new file mode 100644 index 000000000000..92f540d9ca52 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp @@ -0,0 +1,183 @@ +//===-- LibStdcppUniquePointer.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 "LibStdcpp.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/ConstString.h" + +#include <memory> +#include <vector> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class LibStdcppUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit LibStdcppUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + + bool GetSummary(Stream &stream, const TypeSummaryOptions &options); + +private: + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + ValueObject* m_ptr_obj = nullptr; + ValueObject* m_obj_obj = nullptr; + ValueObject* m_del_obj = nullptr; + + ValueObjectSP GetTuple(); +}; + +} // end of anonymous namespace + +LibStdcppUniquePtrSyntheticFrontEnd::LibStdcppUniquePtrSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + Update(); +} + +ValueObjectSP LibStdcppUniquePtrSyntheticFrontEnd::GetTuple() { + ValueObjectSP valobj_backend_sp = m_backend.GetSP(); + + if (!valobj_backend_sp) + return nullptr; + + ValueObjectSP valobj_sp = valobj_backend_sp->GetNonSyntheticValue(); + if (!valobj_sp) + return nullptr; + + ValueObjectSP obj_child_sp = valobj_sp->GetChildMemberWithName("_M_t"); + if (!obj_child_sp) + return nullptr; + + ValueObjectSP obj_subchild_sp = obj_child_sp->GetChildMemberWithName("_M_t"); + + // if there is a _M_t subchild, the tuple is found in the obj_subchild_sp + // (for libstdc++ 6.0.23). + if (obj_subchild_sp) { + return obj_subchild_sp; + } + + return obj_child_sp; +} + +lldb::ChildCacheState LibStdcppUniquePtrSyntheticFrontEnd::Update() { + ValueObjectSP tuple_sp = GetTuple(); + + if (!tuple_sp) + return lldb::ChildCacheState::eRefetch; + + std::unique_ptr<SyntheticChildrenFrontEnd> tuple_frontend( + LibStdcppTupleSyntheticFrontEndCreator(nullptr, tuple_sp)); + + ValueObjectSP ptr_obj = tuple_frontend->GetChildAtIndex(0); + if (ptr_obj) + m_ptr_obj = ptr_obj->Clone(ConstString("pointer")).get(); + + // Add a 'deleter' child if there was a non-empty deleter type specified. + // + // The object might have size=1 in the TypeSystem but occupies no dedicated + // storage due to no_unique_address, so infer the actual size from the total + // size of the unique_ptr class. If sizeof(unique_ptr) == sizeof(void*) then + // the deleter is empty and should be hidden. + if (tuple_sp->GetByteSize() > ptr_obj->GetByteSize()) { + ValueObjectSP del_obj = tuple_frontend->GetChildAtIndex(1); + if (del_obj) + m_del_obj = del_obj->Clone(ConstString("deleter")).get(); + } + m_obj_obj = nullptr; + + return lldb::ChildCacheState::eRefetch; +} + +bool LibStdcppUniquePtrSyntheticFrontEnd::MightHaveChildren() { return true; } + +lldb::ValueObjectSP +LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx == 0 && m_ptr_obj) + return m_ptr_obj->GetSP(); + if (idx == 1 && m_del_obj) + return m_del_obj->GetSP(); + if (idx == 2) { + if (m_ptr_obj && !m_obj_obj) { + Status error; + ValueObjectSP obj_obj = m_ptr_obj->Dereference(error); + if (error.Success()) { + m_obj_obj = obj_obj->Clone(ConstString("object")).get(); + } + } + if (m_obj_obj) + return m_obj_obj->GetSP(); + } + return lldb::ValueObjectSP(); +} + +llvm::Expected<uint32_t> +LibStdcppUniquePtrSyntheticFrontEnd::CalculateNumChildren() { + if (m_del_obj) + return 2; + return 1; +} + +size_t LibStdcppUniquePtrSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (name == "ptr" || name == "pointer") + return 0; + if (name == "del" || name == "deleter") + return 1; + if (name == "obj" || name == "object" || name == "$$dereference$$") + return 2; + return UINT32_MAX; +} + +bool LibStdcppUniquePtrSyntheticFrontEnd::GetSummary( + Stream &stream, const TypeSummaryOptions &options) { + if (!m_ptr_obj) + return false; + + bool success; + uint64_t ptr_value = m_ptr_obj->GetValueAsUnsigned(0, &success); + if (!success) + return false; + if (ptr_value == 0) + stream.Printf("nullptr"); + else + stream.Printf("0x%" PRIx64, ptr_value); + return true; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibStdcppUniquePtrSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +bool lldb_private::formatters::LibStdcppUniquePointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + LibStdcppUniquePtrSyntheticFrontEnd formatter(valobj.GetSP()); + return formatter.GetSummary(stream, options); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp new file mode 100644 index 000000000000..72afc6fe75ac --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp @@ -0,0 +1,105 @@ +//===-- MSVCUndecoratedNameParser.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 "MSVCUndecoratedNameParser.h" + +#include <stack> + +MSVCUndecoratedNameParser::MSVCUndecoratedNameParser(llvm::StringRef name) { + // Global ctor and dtor are global functions. + if (name.contains("dynamic initializer for") || + name.contains("dynamic atexit destructor for")) { + m_specifiers.emplace_back(name, name); + return; + } + + std::size_t last_base_start = 0; + + std::stack<std::size_t> stack; + unsigned int open_angle_brackets = 0; + for (size_t i = 0; i < name.size(); i++) { + switch (name[i]) { + case '<': + // Do not treat `operator<' and `operator<<' as templates + // (sometimes they represented as `<' and `<<' in the name). + if (i == last_base_start || + (i == last_base_start + 1 && name[last_base_start] == '<')) + break; + + stack.push(i); + open_angle_brackets++; + + break; + case '>': + if (!stack.empty() && name[stack.top()] == '<') { + open_angle_brackets--; + stack.pop(); + } + + break; + case '`': + stack.push(i); + + break; + case '\'': + while (!stack.empty()) { + std::size_t top = stack.top(); + if (name[top] == '<') + open_angle_brackets--; + + stack.pop(); + + if (name[top] == '`') + break; + } + + break; + case ':': + if (open_angle_brackets) + break; + if (i == 0 || name[i - 1] != ':') + break; + + m_specifiers.emplace_back(name.take_front(i - 1), + name.slice(last_base_start, i - 1)); + + last_base_start = i + 1; + break; + default: + break; + } + } + + m_specifiers.emplace_back(name, name.drop_front(last_base_start)); +} + +bool MSVCUndecoratedNameParser::IsMSVCUndecoratedName(llvm::StringRef name) { + return name.contains('`'); +} + +bool MSVCUndecoratedNameParser::ExtractContextAndIdentifier( + llvm::StringRef name, llvm::StringRef &context, + llvm::StringRef &identifier) { + MSVCUndecoratedNameParser parser(name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + + std::size_t count = specs.size(); + identifier = count > 0 ? specs[count - 1].GetBaseName() : ""; + context = count > 1 ? specs[count - 2].GetFullName() : ""; + + return count; +} + +llvm::StringRef MSVCUndecoratedNameParser::DropScope(llvm::StringRef name) { + MSVCUndecoratedNameParser parser(name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + if (specs.empty()) + return ""; + + return specs[specs.size() - 1].GetBaseName(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h new file mode 100644 index 000000000000..e5b60a0a1d5b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h @@ -0,0 +1,50 @@ +//===-- MSVCUndecoratedNameParser.h -----------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_MSVCUNDECORATEDNAMEPARSER_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_MSVCUNDECORATEDNAMEPARSER_H + +#include <vector> + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +class MSVCUndecoratedNameSpecifier { +public: + MSVCUndecoratedNameSpecifier(llvm::StringRef full_name, + llvm::StringRef base_name) + : m_full_name(full_name), m_base_name(base_name) {} + + llvm::StringRef GetFullName() const { return m_full_name; } + llvm::StringRef GetBaseName() const { return m_base_name; } + +private: + llvm::StringRef m_full_name; + llvm::StringRef m_base_name; +}; + +class MSVCUndecoratedNameParser { +public: + explicit MSVCUndecoratedNameParser(llvm::StringRef name); + + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> GetSpecifiers() const { + return m_specifiers; + } + + static bool IsMSVCUndecoratedName(llvm::StringRef name); + static bool ExtractContextAndIdentifier(llvm::StringRef name, + llvm::StringRef &context, + llvm::StringRef &identifier); + + static llvm::StringRef DropScope(llvm::StringRef name); + +private: + std::vector<MSVCUndecoratedNameSpecifier> m_specifiers; +}; + +#endif |