diff options
Diffstat (limited to 'lldb/source/Plugins/Language/CPlusPlus')
29 files changed, 6750 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp b/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp new file mode 100644 index 0000000000000..5cfd978774fd0 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp @@ -0,0 +1,207 @@ +//===-- BlockPointer.cpp ----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "BlockPointer.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Target/Target.h" + +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" + +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( + lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS), + std::move(err), "Failed to get scratch ClangASTContext"); + return; + } + + ClangASTContext *clang_ast_context = + llvm::dyn_cast<ClangASTContext>(&type_system_or_err.get()); + + if (!clang_ast_context) { + return; + } + + ClangASTImporterSP clang_ast_importer = target_sp->GetClangASTImporter(); + + if (!clang_ast_importer) { + 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"); + const CompilerType FuncPtr_type = + clang_ast_importer->CopyType(*clang_ast_context, function_pointer_type); + + m_block_struct_type = clang_ast_context->CreateStructForIdentifier( + ConstString(), {{isa_name, isa_type}, + {flags_name, flags_type}, + {reserved_name, reserved_type}, + {FuncPtr_name, FuncPtr_type}}); + } + + ~BlockPointerSyntheticFrontEnd() override = default; + + size_t CalculateNumChildren() override { + const bool omit_empty_base_classes = false; + return m_block_struct_type.GetNumChildren(omit_empty_base_classes, nullptr); + } + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { + if (!m_block_struct_type.IsValid()) { + return lldb::ValueObjectSP(); + } + + if (idx >= CalculateNumChildren()) { + 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; + + const CompilerType child_type = + 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); + + 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' + bool Update() override { return false; } + + // 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/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h b/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h new file mode 100644 index 0000000000000..624c17a6a6af8 --- /dev/null +++ b/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 liblldb_BlockPointer_h_ +#define liblldb_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 // liblldb_BlockPointer_h_ diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp new file mode 100644 index 0000000000000..489fa7d0ad918 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -0,0 +1,1076 @@ +//===-- CPlusPlusLanguage.cpp -----------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#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/PluginManager.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/DataFormatters/CXXFunctionPointer.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/VectorType.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" + +#include "BlockPointer.h" +#include "CPlusPlusNameParser.h" +#include "CxxStringTypes.h" +#include "LibCxx.h" +#include "LibCxxAtomic.h" +#include "LibCxxVariant.h" +#include "LibStdcpp.h" +#include "MSVCUndecoratedNameParser.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +void CPlusPlusLanguage::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), "C++ Language", + CreateInstance); +} + +void CPlusPlusLanguage::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString CPlusPlusLanguage::GetPluginNameStatic() { + static ConstString g_name("cplusplus"); + return g_name; +} + +// PluginInterface protocol + +lldb_private::ConstString CPlusPlusLanguage::GetPluginName() { + return GetPluginNameStatic(); +} + +uint32_t CPlusPlusLanguage::GetPluginVersion() { return 1; } + +// Static Functions + +Language *CPlusPlusLanguage::CreateInstance(lldb::LanguageType language) { + if (Language::LanguageIsCPlusPlus(language)) + 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_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.size() > 0 && basename[0] == '~') + idx = 1; + + if (basename.size() <= idx) + return false; // Empty string or "~" + + if (!std::isalpha(basename[idx]) && basename[idx] != '_') + return false; // First charater (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(); +} + +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(); + 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.getValue().name.basename; + m_context = function.getValue().name.context; + m_arguments = function.getValue().arguments; + m_qualifiers = function.getValue().qualifiers; + 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; +} + +std::string CPlusPlusLanguage::MethodName::GetScopeQualifiedName() { + if (!m_parsed) + Parse(); + if (m_context.empty()) + return m_basename; + + std::string res; + res += m_context; + res += "::"; + res += m_basename; + return res; +} + +bool CPlusPlusLanguage::IsCPPMangledName(const char *name) { + // FIXME!! we should really run through all the known C++ Language plugins + // and ask each one if this is a C++ mangled name + + if (name == nullptr) + return false; + + // MSVC style mangling + if (name[0] == '?') + return true; + + return (name[0] != '\0' && name[0] == '_' && name[1] == 'Z'); +} + +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.getValue().basename; + context = full_name.getValue().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 *)); + } +}; + +/// Given a mangled function `Mangled`, replace all the primitive function type +/// arguments of `Search` with type `Replace`. +class TypeSubstitutor + : public llvm::itanium_demangle::AbstractManglingParser<TypeSubstitutor, + NodeAllocator> { + /// Input character until which we have constructed the respective output + /// already + const char *Written; + + llvm::StringRef Search; + llvm::StringRef Replace; + llvm::SmallString<128> Result; + + /// Whether we have performed any substitutions. + bool Substituted; + + void reset(llvm::StringRef Mangled, llvm::StringRef Search, + llvm::StringRef Replace) { + AbstractManglingParser::reset(Mangled.begin(), Mangled.end()); + Written = Mangled.begin(); + this->Search = Search; + this->Replace = Replace; + Result.clear(); + Substituted = false; + } + + void appendUnchangedInput() { + Result += llvm::StringRef(Written, First - Written); + Written = First; + } + +public: + TypeSubstitutor() : AbstractManglingParser(nullptr, nullptr) {} + + ConstString substitute(llvm::StringRef Mangled, llvm::StringRef From, + llvm::StringRef To) { + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + + reset(Mangled, From, To); + if (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); + } + + llvm::itanium_demangle::Node *parseType() { + if (llvm::StringRef(First, numLeft()).startswith(Search)) { + // We found a match. Append unmodified input up to this point. + appendUnchangedInput(); + + // And then perform the replacement. + Result += Replace; + Written += Search.size(); + Substituted = true; + } + return AbstractManglingParser::parseType(); + } +}; +} + +uint32_t CPlusPlusLanguage::FindAlternateFunctionManglings( + const ConstString mangled_name, std::set<ConstString> &alternates) { + const auto start_size = alternates.size(); + /// 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.insert(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.insert(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.insert(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.insert(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.insert(ulong_fixup); + + return alternates.size() - start_size; +} + +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", + ConstString("^std::__[[:alnum:]]+::string$"), stl_summary_flags, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderASCII, + "std::string summary provider", + ConstString("^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::LibcxxStringSummaryProviderUTF16, + "std::u16string summary provider", + ConstString( + "^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", + ConstString( + "^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", + ConstString("^std::__[[:alnum:]]+::wstring$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringSummaryProvider, + "std::wstring summary provider", + ConstString("^std::__[[:alnum:]]+::basic_string<wchar_t, " + "std::__[[:alnum:]]+::char_traits<wchar_t>, " + "std::__[[:alnum:]]+::allocator<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", + ConstString("^std::__[[:alnum:]]+::bitset<.+>(( )?&)?$"), stl_deref_flags, + true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator, + "libc++ std::vector synthetic children", + ConstString("^std::__[[:alnum:]]+::vector<.+>(( )?&)?$"), stl_deref_flags, + true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdForwardListSyntheticFrontEndCreator, + "libc++ std::forward_list synthetic children", + ConstString("^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<.+>(( )?&)?$" + ConstString("^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", + ConstString("^std::__[[:alnum:]]+::map<.+> >(( )?&)?$"), stl_synth_flags, + true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::set synthetic children", + ConstString("^std::__[[:alnum:]]+::set<.+> >(( )?&)?$"), stl_deref_flags, + true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::multiset synthetic children", + ConstString("^std::__[[:alnum:]]+::multiset<.+> >(( )?&)?$"), + stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::multimap synthetic children", + ConstString("^std::__[[:alnum:]]+::multimap<.+> >(( )?&)?$"), + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator, + "libc++ std::unordered containers synthetic children", + ConstString("^(std::__[[:alnum:]]+::)unordered_(multi)?(map|set)<.+> >$"), + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator, + "libc++ std::initializer_list synthetic children", + ConstString("^std::initializer_list<.+>(( )?&)?$"), stl_synth_flags, + true); + AddCXXSynthetic(cpp_category_sp, LibcxxQueueFrontEndCreator, + "libc++ std::queue synthetic children", + ConstString("^std::__[[:alnum:]]+::queue<.+>(( )?&)?$"), + stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxTupleFrontEndCreator, + "libc++ std::tuple synthetic children", + ConstString("^std::__[[:alnum:]]+::tuple<.*>(( )?&)?$"), + stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxOptionalFrontEndCreator, + "libc++ std::optional synthetic children", + ConstString("^std::__[[:alnum:]]+::optional<.+>(( )?&)?$"), + stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxVariantFrontEndCreator, + "libc++ std::variant synthetic children", + ConstString("^std::__[[:alnum:]]+::variant<.+>(( )?&)?$"), + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator, + "libc++ std::atomic synthetic children", + ConstString("^std::__[[:alnum:]]+::atomic<.+>$"), stl_synth_flags, true); + + cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( + RegularExpression( + llvm::StringRef("^(std::__[[:alnum:]]+::)deque<.+>(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.libcxx.stddeque_SynthProvider"))); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator, + "shared_ptr synthetic children", + ConstString("^(std::__[[:alnum:]]+::)shared_ptr<.+>(( )?&)?$"), + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator, + "weak_ptr synthetic children", + ConstString("^(std::__[[:alnum:]]+::)weak_ptr<.+>(( )?&)?$"), + stl_synth_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", + ConstString("^std::__[[:alnum:]]+::bitset<.+>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::vector summary provider", + ConstString("^std::__[[:alnum:]]+::vector<.+>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::list summary provider", + ConstString("^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<.+>(( )?&)?$" + ConstString("^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", + ConstString("^std::__[[:alnum:]]+::map<.+>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::deque summary provider", + ConstString("^std::__[[:alnum:]]+::deque<.+>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::queue summary provider", + ConstString("^std::__[[:alnum:]]+::queue<.+>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::set summary provider", + ConstString("^std::__[[:alnum:]]+::set<.+>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::multiset summary provider", + ConstString("^std::__[[:alnum:]]+::multiset<.+>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::multimap summary provider", + ConstString("^std::__[[:alnum:]]+::multimap<.+>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::unordered containers summary provider", + ConstString("^(std::__[[:alnum:]]+::)unordered_(multi)?(map|set)<.+> >$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, LibcxxContainerSummaryProvider, + "libc++ std::tuple summary provider", + ConstString("^std::__[[:alnum:]]+::tuple<.*>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::LibCxxAtomicSummaryProvider, + "libc++ std::atomic summary provider", + ConstString("^std::__[[:alnum:]]+::atomic<.+>$"), stl_summary_flags, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxOptionalSummaryProvider, + "libc++ std::optional summary provider", + ConstString("^std::__[[:alnum:]]+::optional<.+>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxVariantSummaryProvider, + "libc++ std::variant summary provider", + ConstString("^std::__[[:alnum:]]+::variant<.+>(( )?&)?$"), + stl_summary_flags, true); + + stl_summary_flags.SetSkipPointers(true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxSmartPointerSummaryProvider, + "libc++ std::shared_ptr summary provider", + ConstString("^std::__[[:alnum:]]+::shared_ptr<.+>(( )?&)?$"), + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxSmartPointerSummaryProvider, + "libc++ std::weak_ptr summary provider", + ConstString("^std::__[[:alnum:]]+::weak_ptr<.+>(( )?&)?$"), + stl_summary_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator, + "std::vector iterator synthetic children", + ConstString("^std::__[[:alnum:]]+::__wrap_iter<.+>$"), stl_synth_flags, + true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator, + "std::map iterator synthetic children", + ConstString("^std::__[[:alnum:]]+::__map_iterator<.+>$"), stl_synth_flags, + true); +} + +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->GetTypeSummariesContainer()->Add(ConstString("std::string"), + std_string_summary_sp); + cpp_category_sp->GetTypeSummariesContainer()->Add( + ConstString("std::basic_string<char>"), std_string_summary_sp); + cpp_category_sp->GetTypeSummariesContainer()->Add( + ConstString("std::basic_string<char,std::char_traits<char>,std::" + "allocator<char> >"), + std_string_summary_sp); + cpp_category_sp->GetTypeSummariesContainer()->Add( + ConstString("std::basic_string<char, std::char_traits<char>, " + "std::allocator<char> >"), + std_string_summary_sp); + + cpp_category_sp->GetTypeSummariesContainer()->Add( + ConstString("std::__cxx11::string"), cxx11_string_summary_sp); + cpp_category_sp->GetTypeSummariesContainer()->Add( + ConstString("std::__cxx11::basic_string<char, std::char_traits<char>, " + "std::allocator<char> >"), + 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->GetTypeSummariesContainer()->Add(ConstString("std::wstring"), + std_wstring_summary_sp); + cpp_category_sp->GetTypeSummariesContainer()->Add( + ConstString("std::basic_string<wchar_t>"), std_wstring_summary_sp); + cpp_category_sp->GetTypeSummariesContainer()->Add( + ConstString("std::basic_string<wchar_t,std::char_traits<wchar_t>,std::" + "allocator<wchar_t> >"), + std_wstring_summary_sp); + cpp_category_sp->GetTypeSummariesContainer()->Add( + ConstString("std::basic_string<wchar_t, std::char_traits<wchar_t>, " + "std::allocator<wchar_t> >"), + std_wstring_summary_sp); + + cpp_category_sp->GetTypeSummariesContainer()->Add( + ConstString("std::__cxx11::wstring"), cxx11_wstring_summary_sp); + cpp_category_sp->GetTypeSummariesContainer()->Add( + ConstString("std::__cxx11::basic_string<wchar_t, " + "std::char_traits<wchar_t>, std::allocator<wchar_t> >"), + cxx11_wstring_summary_sp); + + SyntheticChildren::Flags stl_synth_flags; + stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( + false); + + cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( + RegularExpression(llvm::StringRef("^std::vector<.+>(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider"))); + cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( + RegularExpression(llvm::StringRef("^std::map<.+> >(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapSynthProvider"))); + cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( + RegularExpression(llvm::StringRef("^std::(__cxx11::)?list<.+>(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); + stl_summary_flags.SetDontShowChildren(false); + stl_summary_flags.SetSkipPointers(true); + cpp_category_sp->GetRegexTypeSummariesContainer()->Add( + RegularExpression(llvm::StringRef("^std::vector<.+>(( )?&)?$")), + TypeSummaryImplSP( + new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->GetRegexTypeSummariesContainer()->Add( + RegularExpression(llvm::StringRef("^std::map<.+> >(( )?&)?$")), + TypeSummaryImplSP( + new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->GetRegexTypeSummariesContainer()->Add( + RegularExpression(llvm::StringRef("^std::(__cxx11::)?list<.+>(( )?&)?$")), + TypeSummaryImplSP( + new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator, + "std::vector iterator synthetic children", + ConstString("^__gnu_cxx::__normal_iterator<.+>$"), stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator, + "std::map iterator synthetic children", + ConstString("^std::_Rb_tree_iterator<.+>$"), stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator, + "std::unique_ptr synthetic children", + ConstString("^std::unique_ptr<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator, + "std::shared_ptr synthetic children", + ConstString("^std::shared_ptr<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator, + "std::weak_ptr synthetic children", + ConstString("^std::weak_ptr<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator, + "std::tuple synthetic children", ConstString("^std::tuple<.+>(( )?&)?$"), + stl_synth_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibStdcppUniquePointerSummaryProvider, + "libstdc++ std::unique_ptr summary provider", + ConstString("^std::unique_ptr<.+>(( )?&)?$"), stl_summary_flags, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibStdcppSmartPointerSummaryProvider, + "libstdc++ std::shared_ptr summary provider", + ConstString("^std::shared_ptr<.+>(( )?&)?$"), stl_summary_flags, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibStdcppSmartPointerSummaryProvider, + "libstdc++ std::weak_ptr summary provider", + ConstString("^std::weak_ptr<.+>(( )?&)?$"), 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); + + // FIXME because of a bug in the FormattersContainer we need to add a summary + // for both X* and const X* (<rdar://problem/12717717>) + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::Char8StringSummaryProvider, + "char8_t * summary provider", ConstString("char8_t *"), string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char8StringSummaryProvider, + "char8_t [] summary provider", + ConstString("char8_t \\[[0-9]+\\]"), string_array_flags, true); + + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::Char16StringSummaryProvider, + "char16_t * summary provider", ConstString("char16_t *"), string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16StringSummaryProvider, + "char16_t [] summary provider", + ConstString("char16_t \\[[0-9]+\\]"), string_array_flags, true); + + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::Char32StringSummaryProvider, + "char32_t * summary provider", ConstString("char32_t *"), string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char32StringSummaryProvider, + "char32_t [] summary provider", + ConstString("char32_t \\[[0-9]+\\]"), string_array_flags, true); + + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::WCharStringSummaryProvider, + "wchar_t * summary provider", ConstString("wchar_t *"), string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::WCharStringSummaryProvider, + "wchar_t * summary provider", + ConstString("wchar_t \\[[0-9]+\\]"), string_array_flags, true); + + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::Char16StringSummaryProvider, + "unichar * summary provider", ConstString("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", ConstString("char8_t"), + widechar_flags); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::Char16SummaryProvider, + "char16_t summary provider", ConstString("char16_t"), widechar_flags); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::Char32SummaryProvider, + "char32_t summary provider", ConstString("char32_t"), widechar_flags); + AddCXXSummary(cpp_category_sp, lldb_private::formatters::WCharSummaryProvider, + "wchar_t summary provider", ConstString("wchar_t"), + widechar_flags); + + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::Char16SummaryProvider, + "unichar summary provider", ConstString("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(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 (valobj.GetCompilerType().IsFunctionPointerType()) { + 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(nullptr, nullptr)) { + 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(nullptr)) { + 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(nullptr, nullptr)) { + 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(nullptr)) { + return formatter_sp; + } + return nullptr; + }); + + }); + + return g_formatters; +} + +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.endswith_lower(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++/"); +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h new file mode 100644 index 0000000000000..d30e560807323 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -0,0 +1,136 @@ +//===-- 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 liblldb_CPlusPlusLanguage_h_ +#define liblldb_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(), + m_parsed(false), m_parse_error(false) {} + + 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(); + + protected: + void Parse(); + bool TrySimplifiedParse(); + + ConstString m_full; // Full name: + // "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" + bool m_parsed; + bool m_parse_error; + }; + + CPlusPlusLanguage() = default; + + ~CPlusPlusLanguage() override = default; + + lldb::LanguageType GetLanguageType() const override { + return lldb::eLanguageTypeC_plus_plus; + } + + std::unique_ptr<TypeScavenger> GetTypeScavenger() override; + lldb::TypeCategoryImplSP GetFormatters() override; + + HardcodedFormatters::HardcodedSummaryFinder GetHardcodedSummaries() override; + + HardcodedFormatters::HardcodedSyntheticFinder + GetHardcodedSynthetics() override; + + 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 lldb_private::ConstString GetPluginNameStatic(); + + static bool IsCPPMangledName(const char *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); + + // Given a mangled function name, calculates some alternative manglings since + // the compiler mangling may not line up with the symbol we are expecting + static uint32_t + FindAlternateFunctionManglings(const ConstString mangled, + std::set<ConstString> &candidates); + + // PluginInterface protocol + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; +}; + +} // namespace lldb_private + +#endif // liblldb_CPlusPlusLanguage_h_ diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp new file mode 100644 index 0000000000000..932db17b964a5 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp @@ -0,0 +1,660 @@ +//===-- CPlusPlusNameParser.cpp ---------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "CPlusPlusNameParser.h" + +#include "clang/Basic/IdentifierTable.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Threading.h" + +using namespace lldb; +using namespace lldb_private; +using llvm::Optional; +using llvm::None; +using ParsedFunction = lldb_private::CPlusPlusNameParser::ParsedFunction; +using ParsedName = lldb_private::CPlusPlusNameParser::ParsedName; +namespace tok = clang::tok; + +Optional<ParsedFunction> CPlusPlusNameParser::ParseAsFunctionDefinition() { + m_next_token_index = 0; + Optional<ParsedFunction> result(None); + + // 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 None; + return result; +} + +Optional<ParsedName> CPlusPlusNameParser::ParseAsFullName() { + m_next_token_index = 0; + Optional<ParsedNameRanges> name_ranges = ParseFullNameImpl(); + if (!name_ranges) + return None; + if (HasMoreTokens()) + return None; + ParsedName result; + result.basename = GetTextForRange(name_ranges.getValue().basename_range); + result.context = GetTextForRange(name_ranges.getValue().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]; +} + +Optional<ParsedFunction> +CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) { + Bookmark start_position = SetBookmark(); + if (expect_return_type) { + // Consume return type if it's expected. + if (!ConsumeTypename()) + return None; + } + + auto maybe_name = ParseFullNameImpl(); + if (!maybe_name) { + return None; + } + + size_t argument_start = GetCurrentPosition(); + if (!ConsumeArguments()) { + return None; + } + + size_t qualifiers_start = GetCurrentPosition(); + SkipFunctionQualifiers(); + size_t end_position = GetCurrentPosition(); + + ParsedFunction result; + result.name.basename = GetTextForRange(maybe_name.getValue().basename_range); + result.name.context = GetTextForRange(maybe_name.getValue().context_range); + result.arguments = GetTextForRange(Range(argument_start, qualifiers_start)); + result.qualifiers = GetTextForRange(Range(qualifiers_start, end_position)); + start_position.Remove(); + return result; +} + +Optional<ParsedFunction> +CPlusPlusNameParser::ParseFuncPtr(bool expect_return_type) { + Bookmark start_position = SetBookmark(); + if (expect_return_type) { + // Consume return type. + if (!ConsumeTypename()) + return None; + } + + if (!ConsumeToken(tok::l_paren)) + return None; + if (!ConsumePtrsAndRefs()) + return None; + + { + 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; + } + } + + 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 None; +} + +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: + if (!ConsumeBrackets(tok::l_square, tok::r_square)) + return false; + can_open_template = 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::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(); + 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; +} + +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; + Optional<size_t> last_coloncolon_position = None; + + 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_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.getValue()); + result.basename_range = + Range(last_coloncolon_position.getValue() + 1, GetCurrentPosition()); + } else { + result.basename_range = + Range(start_position.GetSavedPosition(), GetCurrentPosition()); + } + start_position.Remove(); + return result; + } else { + return None; + } +} + +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; + }); + 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/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h new file mode 100644 index 0000000000000..414c3a0091577 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h @@ -0,0 +1,177 @@ +//===-- 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 liblldb_CPlusPlusNameParser_h_ +#define liblldb_CPlusPlusNameParser_h_ + + +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +#include "lldb/Utility/ConstString.h" +#include "lldb/lldb-private.h" + +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; + }; + + // 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 *))() + llvm::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 + llvm::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() {} + 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); + + llvm::Optional<ParsedFunction> ParseFunctionImpl(bool expect_return_type); + + // Parses functions returning function pointers 'string (*f(int x))(float y)' + llvm::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(); + + llvm::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 // liblldb_CPlusPlusNameParser_h_ diff --git a/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp b/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp new file mode 100644 index 0000000000000..3ea7589d8e4a8 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp @@ -0,0 +1,279 @@ +//===-- CxxStringTypes.cpp --------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "CxxStringTypes.h" + +#include "llvm/Support/ConvertUTF.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/Symbol/ClangASTContext.h" +#include "lldb/Target/ProcessStructReader.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> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool lldb_private::formatters::Char8StringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t valobj_addr = GetArrayAddressOrPointerValue(valobj); + if (valobj_addr == 0 || valobj_addr == LLDB_INVALID_ADDRESS) + return false; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(valobj_addr); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken("u8"); + + if (!StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF8>(options)) { + stream.Printf("Summary Unavailable"); + return true; + } + + return true; +} + +bool lldb_private::formatters::Char16StringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t valobj_addr = GetArrayAddressOrPointerValue(valobj); + if (valobj_addr == 0 || valobj_addr == LLDB_INVALID_ADDRESS) + return false; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(valobj_addr); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken("u"); + + if (!StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF16>(options)) { + stream.Printf("Summary Unavailable"); + return true; + } + + return true; +} + +bool lldb_private::formatters::Char32StringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t valobj_addr = GetArrayAddressOrPointerValue(valobj); + if (valobj_addr == 0 || valobj_addr == LLDB_INVALID_ADDRESS) + return false; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(valobj_addr); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken("U"); + + if (!StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF32>(options)) { + stream.Printf("Summary Unavailable"); + return true; + } + + return true; +} + +bool lldb_private::formatters::WCharStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t valobj_addr = GetArrayAddressOrPointerValue(valobj); + if (valobj_addr == 0 || valobj_addr == LLDB_INVALID_ADDRESS) + 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. + llvm::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.SetProcessSP(process_sp); + options.SetStream(&stream); + 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; +} + +bool lldb_private::formatters::Char8SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + DataExtractor data; + Status error; + valobj.GetData(data, error); + + if (error.Fail()) + return false; + + std::string value; + valobj.GetValueAsCString(lldb::eFormatUnicode8, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + options.SetData(data); + options.SetStream(&stream); + options.SetPrefixToken("u8"); + options.SetQuote('\''); + options.SetSourceSize(1); + options.SetBinaryZeroIsTerminator(false); + + return StringPrinter::ReadBufferAndDumpToStream< + StringPrinter::StringElementType::UTF8>(options); +} + +bool lldb_private::formatters::Char16SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + DataExtractor data; + Status error; + valobj.GetData(data, error); + + if (error.Fail()) + return false; + + std::string value; + valobj.GetValueAsCString(lldb::eFormatUnicode16, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + options.SetData(data); + options.SetStream(&stream); + options.SetPrefixToken("u"); + options.SetQuote('\''); + options.SetSourceSize(1); + options.SetBinaryZeroIsTerminator(false); + + return StringPrinter::ReadBufferAndDumpToStream< + StringPrinter::StringElementType::UTF16>(options); +} + +bool lldb_private::formatters::Char32SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + DataExtractor data; + Status error; + valobj.GetData(data, error); + + if (error.Fail()) + return false; + + std::string value; + valobj.GetValueAsCString(lldb::eFormatUnicode32, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + options.SetData(data); + options.SetStream(&stream); + options.SetPrefixToken("U"); + options.SetQuote('\''); + options.SetSourceSize(1); + options.SetBinaryZeroIsTerminator(false); + + return StringPrinter::ReadBufferAndDumpToStream< + StringPrinter::StringElementType::UTF32>(options); +} + +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. + llvm::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(data); + options.SetStream(&stream); + options.SetPrefixToken("L"); + options.SetQuote('\''); + options.SetSourceSize(1); + options.SetBinaryZeroIsTerminator(false); + + switch (wchar_size) { + case 8: + return StringPrinter::ReadBufferAndDumpToStream< + StringPrinter::StringElementType::UTF8>(options); + case 16: + return StringPrinter::ReadBufferAndDumpToStream< + StringPrinter::StringElementType::UTF16>(options); + case 32: + return StringPrinter::ReadBufferAndDumpToStream< + StringPrinter::StringElementType::UTF32>(options); + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h b/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h new file mode 100644 index 0000000000000..35498b3b568f7 --- /dev/null +++ b/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 liblldb_CxxStringTypes_h_ +#define liblldb_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 // liblldb_CxxStringTypes_h_ diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp new file mode 100644 index 0000000000000..abe89035c5323 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -0,0 +1,681 @@ +//===-- LibCxx.cpp ----------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibCxx.h" + +#include "llvm/ADT/ScopeExit.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/Symbol/ClangASTContext.h" +#include "lldb/Target/ProcessStructReader.h" +#include "lldb/Target/SectionLoadList.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 "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool lldb_private::formatters::LibcxxOptionalSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + + // An optional either contains a value or not, the member __engaged_ is + // a bool flag, it is true if the optional has a value and false otherwise. + ValueObjectSP engaged_sp( + valobj_sp->GetChildMemberWithName(ConstString("__engaged_"), true)); + + if (!engaged_sp) + return false; + + llvm::StringRef engaged_as_cstring( + engaged_sp->GetValueAsUnsigned(0) == 1 ? "true" : "false"); + + stream.Printf(" Has Value=%s ", engaged_as_cstring.data()); + + return true; +} + +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.file.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.file.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(ConstString("__ptr_"), true)); + ValueObjectSP count_sp(valobj_sp->GetChildAtNamePath( + {ConstString("__cntrl_"), ConstString("__shared_owners_")})); + ValueObjectSP weakcount_sp(valobj_sp->GetChildAtNamePath( + {ConstString("__cntrl_"), ConstString("__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; +} + +/* + (lldb) fr var ibeg --raw --ptr-depth 1 + (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int, + std::__1::basic_string<char, std::__1::char_traits<char>, + std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int, + std::__1::basic_string<char, std::__1::char_traits<char>, + std::__1::allocator<char> > >, void *> *, long> >) ibeg = { + __i_ = { + __ptr_ = 0x0000000100103870 { + std::__1::__tree_node_base<void *> = { + std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = { + __left_ = 0x0000000000000000 + } + __right_ = 0x0000000000000000 + __parent_ = 0x00000001001038b0 + __is_black_ = true + } + __value_ = { + first = 0 + second = { std::string } + */ + +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() { + if (valobj_sp) + Update(); +} + +bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() { + m_pair_sp.reset(); + m_pair_ptr = nullptr; + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return false; + + if (!valobj_sp) + return false; + + static ConstString g___i_("__i_"); + + // this must be a ValueObject* because it is a child of the ValueObject we + // are producing children for it if were a ValueObjectSP, we would end up + // with a loop (iterator -> synthetic -> child -> parent == iterator) and + // that would in turn leak memory by never allowing the ValueObjects to die + // and free their memory + m_pair_ptr = valobj_sp + ->GetValueForExpressionPath( + ".__i_.__ptr_->__value_", nullptr, nullptr, + ValueObject::GetValueForExpressionPathOptions() + .DontCheckDotVsArrowSyntax() + .SetSyntheticChildrenTraversal( + ValueObject::GetValueForExpressionPathOptions:: + SyntheticChildrenTraversal::None), + nullptr) + .get(); + + if (!m_pair_ptr) { + m_pair_ptr = valobj_sp + ->GetValueForExpressionPath( + ".__i_.__ptr_", nullptr, nullptr, + ValueObject::GetValueForExpressionPathOptions() + .DontCheckDotVsArrowSyntax() + .SetSyntheticChildrenTraversal( + ValueObject::GetValueForExpressionPathOptions:: + SyntheticChildrenTraversal::None), + nullptr) + .get(); + if (m_pair_ptr) { + auto __i_(valobj_sp->GetChildMemberWithName(g___i_, true)); + if (!__i_) { + m_pair_ptr = nullptr; + return false; + } + CompilerType pair_type( + __i_->GetCompilerType().GetTypeTemplateArgument(0)); + std::string name; + uint64_t bit_offset_ptr; + uint32_t bitfield_bit_size_ptr; + bool is_bitfield_ptr; + pair_type = pair_type.GetFieldAtIndex( + 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr); + if (!pair_type) { + m_pair_ptr = nullptr; + return false; + } + + auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS)); + m_pair_ptr = nullptr; + if (addr && addr != LLDB_INVALID_ADDRESS) { + ClangASTContext *ast_ctx = + llvm::dyn_cast_or_null<ClangASTContext>(pair_type.GetTypeSystem()); + if (!ast_ctx) + return false; + CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( + ConstString(), + {{"ptr0", + ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, + {"ptr1", + ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, + {"ptr2", + ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, + {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)}, + {"payload", pair_type}}); + llvm::Optional<uint64_t> size = tree_node_type.GetByteSize(nullptr); + if (!size) + return false; + DataBufferSP buffer_sp(new DataBufferHeap(*size, 0)); + ProcessSP process_sp(target_sp->GetProcessSP()); + Status error; + process_sp->ReadMemory(addr, buffer_sp->GetBytes(), + buffer_sp->GetByteSize(), error); + if (error.Fail()) + return false; + DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()); + auto pair_sp = CreateValueObjectFromData( + "pair", extractor, valobj_sp->GetExecutionContextRef(), + tree_node_type); + if (pair_sp) + m_pair_sp = pair_sp->GetChildAtIndex(4, true); + } + } + } + + return false; +} + +size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex( + size_t idx) { + if (m_pair_ptr) + return m_pair_ptr->GetChildAtIndex(idx, true); + if (m_pair_sp) + return m_pair_sp->GetChildAtIndex(idx, true); + return lldb::ValueObjectSP(); +} + +bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "first") + return 0; + if (name == "second") + return 1; + return UINT32_MAX; +} + +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + ~LibCxxMapIteratorSyntheticFrontEnd() { + // this will be deleted when its parent dies (since it's a child object) + // delete m_pair_ptr; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +/* + (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) { + static ConstString g_item_name; + if (!g_item_name) + g_item_name.SetCString("__i"); + return (valobj_sp + ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name) + : nullptr); +} + +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr), m_count_sp(), + m_weak_count_sp(), m_ptr_size(0), m_byte_order(lldb::eByteOrderInvalid) { + if (valobj_sp) + Update(); +} + +size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + CalculateNumChildren() { + return (m_cntrl ? 1 : 0); +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex( + size_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(ConstString("__ptr_"), true); + + if (idx > 2) + return lldb::ValueObjectSP(); + + if (idx == 1) { + if (!m_count_sp) { + ValueObjectSP shared_owners_sp(m_cntrl->GetChildMemberWithName( + ConstString("__shared_owners_"), true)); + if (!shared_owners_sp) + return lldb::ValueObjectSP(); + uint64_t count = 1 + shared_owners_sp->GetValueAsUnsigned(0); + DataExtractor data(&count, 8, m_byte_order, m_ptr_size); + m_count_sp = CreateValueObjectFromData( + "count", data, valobj_sp->GetExecutionContextRef(), + shared_owners_sp->GetCompilerType()); + } + return m_count_sp; + } else /* if (idx == 2) */ + { + if (!m_weak_count_sp) { + ValueObjectSP shared_weak_owners_sp(m_cntrl->GetChildMemberWithName( + ConstString("__shared_weak_owners_"), true)); + if (!shared_weak_owners_sp) + return lldb::ValueObjectSP(); + uint64_t count = 1 + shared_weak_owners_sp->GetValueAsUnsigned(0); + DataExtractor data(&count, 8, m_byte_order, m_ptr_size); + m_weak_count_sp = CreateValueObjectFromData( + "count", data, valobj_sp->GetExecutionContextRef(), + shared_weak_owners_sp->GetCompilerType()); + } + return m_weak_count_sp; + } +} + +bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() { + m_count_sp.reset(); + m_weak_count_sp.reset(); + m_cntrl = nullptr; + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + if (!target_sp) + return false; + + m_byte_order = target_sp->GetArchitecture().GetByteOrder(); + m_ptr_size = target_sp->GetArchitecture().GetAddressByteSize(); + + lldb::ValueObjectSP cntrl_sp( + valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"), true)); + + m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular + // dependency + return false; +} + +bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "__ptr_") + return 0; + if (name == "count") + return 1; + if (name == "weak_count") + return 2; + 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); +} + +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) +enum LibcxxStringLayoutMode { + eLibcxxStringLayoutModeCSD = 0, + eLibcxxStringLayoutModeDSC = 1, + eLibcxxStringLayoutModeInvalid = 0xffff +}; + +// this function abstracts away the layout and mode details of a libc++ string +// and returns the address of the data and the size ready for callers to +// consume +static bool ExtractLibcxxStringInfo(ValueObject &valobj, + ValueObjectSP &location_sp, + uint64_t &size) { + ValueObjectSP D(valobj.GetChildAtIndexPath({0, 0, 0, 0})); + if (!D) + return false; + + ValueObjectSP layout_decider( + D->GetChildAtIndexPath(llvm::ArrayRef<size_t>({0, 0}))); + + // this child should exist + if (!layout_decider) + return false; + + ConstString g_data_name("__data_"); + ConstString g_size_name("__size_"); + bool short_mode = false; // this means the string is in short-mode and the + // data is stored inline + LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name) + ? eLibcxxStringLayoutModeDSC + : eLibcxxStringLayoutModeCSD; + uint64_t size_mode_value = 0; + + if (layout == eLibcxxStringLayoutModeDSC) { + ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 1, 0})); + if (!size_mode) + return false; + + if (size_mode->GetName() != g_size_name) { + // we are hitting the padding structure, move along + size_mode = D->GetChildAtIndexPath({1, 1, 1}); + if (!size_mode) + return false; + } + + size_mode_value = (size_mode->GetValueAsUnsigned(0)); + short_mode = ((size_mode_value & 0x80) == 0); + } else { + ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 0, 0})); + if (!size_mode) + return false; + + size_mode_value = (size_mode->GetValueAsUnsigned(0)); + short_mode = ((size_mode_value & 1) == 0); + } + + if (short_mode) { + ValueObjectSP s(D->GetChildAtIndex(1, true)); + if (!s) + return false; + location_sp = s->GetChildAtIndex( + (layout == eLibcxxStringLayoutModeDSC) ? 0 : 1, true); + size = (layout == eLibcxxStringLayoutModeDSC) + ? size_mode_value + : ((size_mode_value >> 1) % 256); + return (location_sp.get() != nullptr); + } else { + ValueObjectSP l(D->GetChildAtIndex(0, true)); + if (!l) + return false; + // we can use the layout_decider object as the data pointer + location_sp = (layout == eLibcxxStringLayoutModeDSC) + ? layout_decider + : l->GetChildAtIndex(2, true); + ValueObjectSP size_vo(l->GetChildAtIndex(1, true)); + if (!size_vo || !location_sp) + return false; + size = size_vo->GetValueAsUnsigned(0); + return true; + } +} + +bool lldb_private::formatters::LibcxxWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + uint64_t size = 0; + ValueObjectSP location_sp; + if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) + return false; + if (size == 0) { + stream.Printf("L\"\""); + return true; + } + if (!location_sp) + return false; + + DataExtractor extractor; + + 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); + } + } + location_sp->GetPointeeData(extractor, 0, size); + + // std::wstring::size() is measured in 'characters', not bytes + auto wchar_t_size = valobj.GetTargetSP() + ->GetScratchClangASTContext() + ->GetBasicType(lldb::eBasicTypeWChar) + .GetByteSize(nullptr); + if (!wchar_t_size) + return false; + + options.SetData(extractor); + options.SetStream(&stream); + options.SetPrefixToken("L"); + options.SetQuote('"'); + options.SetSourceSize(size); + options.SetBinaryZeroIsTerminator(false); + + switch (*wchar_t_size) { + case 1: + StringPrinter::ReadBufferAndDumpToStream< + lldb_private::formatters::StringPrinter::StringElementType::UTF8>( + options); + break; + + case 2: + lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream< + lldb_private::formatters::StringPrinter::StringElementType::UTF16>( + options); + break; + + case 4: + lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream< + lldb_private::formatters::StringPrinter::StringElementType::UTF32>( + options); + break; + + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + + return true; +} + +template <StringPrinter::StringElementType element_type> +bool LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token = "") { + uint64_t size = 0; + ValueObjectSP location_sp; + + if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) + return false; + + if (size == 0) { + stream.Printf("\"\""); + return true; + } + + if (!location_sp) + return false; + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + + DataExtractor extractor; + if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { + const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); + if (size > max_size) { + size = max_size; + options.SetIsTruncated(true); + } + } + location_sp->GetPointeeData(extractor, 0, size); + + options.SetData(extractor); + options.SetStream(&stream); + + if (prefix_token.empty()) + options.SetPrefixToken(nullptr); + else + options.SetPrefixToken(prefix_token); + + options.SetQuote('"'); + options.SetSourceSize(size); + options.SetBinaryZeroIsTerminator(false); + StringPrinter::ReadBufferAndDumpToStream<element_type>(options); + + return true; +} + +bool lldb_private::formatters::LibcxxStringSummaryProviderASCII( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return LibcxxStringSummaryProvider<StringPrinter::StringElementType::ASCII>( + valobj, stream, summary_options); +} + +bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return LibcxxStringSummaryProvider<StringPrinter::StringElementType::UTF16>( + valobj, stream, summary_options, "u"); +} + +bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return LibcxxStringSummaryProvider<StringPrinter::StringElementType::UTF32>( + valobj, stream, summary_options, "U"); +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h new file mode 100644 index 0000000000000..214f5512e4a50 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -0,0 +1,159 @@ +//===-- 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 liblldb_LibCxx_h_ +#define liblldb_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 { + +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 LibcxxOptionalSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::optional<> + +bool LibcxxSmartPointerSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions + &options); // libc++ std::shared_ptr<> and std::weak_ptr<> + +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); + +class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + + ~LibCxxMapIteratorSyntheticFrontEnd() override; + +private: + ValueObject *m_pair_ptr; + lldb::ValueObjectSP m_pair_sp; +}; + +SyntheticChildrenFrontEnd * +LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +class LibcxxSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + + ~LibcxxSharedPtrSyntheticFrontEnd() override; + +private: + ValueObject *m_cntrl; + lldb::ValueObjectSP m_count_sp; + lldb::ValueObjectSP m_weak_count_sp; + uint8_t m_ptr_size; + lldb::ByteOrder m_byte_order; +}; + +SyntheticChildrenFrontEnd * +LibcxxBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdVectorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdMapSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd *LibcxxQueueFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd *LibcxxTupleFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxOptionalFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + +SyntheticChildrenFrontEnd * +LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + +} // namespace formatters +} // namespace lldb_private + +#endif // liblldb_LibCxx_h_ diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp new file mode 100644 index 0000000000000..b4e7a1703e46a --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp @@ -0,0 +1,159 @@ +//===-- LibCxxAtomic.cpp ------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibCxxAtomic.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(ConstString("__a_"), true); + if (!member__a_) + return {}; + + ValueObjectSP member__a_value = + member__a_->GetChildMemberWithName(ConstString("__a_value"), true); + 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; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + + lldb::ValueObjectSP GetSyntheticValue() override; + +private: + ValueObject *m_real_child; +}; +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + LibcxxStdAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_real_child(nullptr) {} + +bool lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::Update() { + ValueObjectSP atomic_value = GetLibCxxAtomicValue(m_backend); + if (atomic_value) + m_real_child = GetLibCxxAtomicValue(m_backend).get(); + + return false; +} + +bool lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + CalculateNumChildren() { + return m_real_child ? m_real_child->GetNumChildren() : 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::GetChildAtIndex( + size_t idx) { + return m_real_child ? m_real_child->GetChildAtIndex(idx, true) : nullptr; +} + +size_t lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + return m_real_child ? m_real_child->GetIndexOfChildWithName(name) + : UINT32_MAX; +} + +lldb::ValueObjectSP lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + GetSyntheticValue() { + if (m_real_child && m_real_child->CanProvideValue()) + return m_real_child->GetSP(); + return nullptr; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new LibcxxStdAtomicSyntheticFrontEnd(valobj_sp); + return nullptr; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h new file mode 100644 index 0000000000000..8be833dd82f6e --- /dev/null +++ b/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 liblldb_LibCxxAtomic_h_ +#define liblldb_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 // liblldb_LibCxxAtomic_h_ diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp new file mode 100644 index 0000000000000..78c453cd1b3c7 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp @@ -0,0 +1,120 @@ +//===-- LibCxxBitset.cpp ----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibCxx.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + +class BitsetFrontEnd : public SyntheticChildrenFrontEnd { +public: + BitsetFrontEnd(ValueObject &valobj); + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + bool Update() override; + size_t CalculateNumChildren() override { return m_elements.size(); } + ValueObjectSP GetChildAtIndex(size_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). + // 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; +}; +} // namespace + +BitsetFrontEnd::BitsetFrontEnd(ValueObject &valobj) + : SyntheticChildrenFrontEnd(valobj) { + 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(); + } +} + +bool BitsetFrontEnd::Update() { + m_elements.clear(); + m_first = nullptr; + + TargetSP target_sp = m_backend.GetTargetSP(); + if (!target_sp) + return false; + size_t capping_size = target_sp->GetMaximumNumberOfChildrenToDisplay(); + + size_t size = 0; + if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(0)) + size = arg->value.getLimitedValue(capping_size); + + m_elements.assign(size, ValueObjectSP()); + + m_first = m_backend.GetChildMemberWithName(ConstString("__first_"), true).get(); + return false; +} + +ValueObjectSP BitsetFrontEnd::GetChildAtIndex(size_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, nullptr, nullptr)) { + llvm::Optional<uint64_t> bit_size = + type.GetBitSize(ctx.GetBestExecutionContextScope()); + if (!bit_size || *bit_size == 0) + return {}; + chunk = m_first->GetChildAtIndex(idx / *bit_size, true); + } else { + type = m_first->GetCompilerType(); + chunk = m_first->GetSP(); + } + if (!type || !chunk) + return {}; + + llvm::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::LibcxxBitsetSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new BitsetFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp new file mode 100644 index 0000000000000..79c7434f617f8 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp @@ -0,0 +1,122 @@ +//===-- LibCxxInitializerList.cpp -------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" + +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; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_start; + CompilerType m_element_type; + uint32_t m_element_size; + size_t m_num_elements; +}; +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + LibcxxInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_start(nullptr), m_element_type(), + m_element_size(0), m_num_elements(0) { + 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; +} + +size_t lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + CalculateNumChildren() { + static ConstString g___size_("__size_"); + m_num_elements = 0; + ValueObjectSP size_sp(m_backend.GetChildMemberWithName(g___size_, true)); + if (size_sp) + m_num_elements = size_sp->GetValueAsUnsigned(0); + return m_num_elements; +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibcxxInitializerListSyntheticFrontEnd::GetChildAtIndex(size_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); +} + +bool lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + Update() { + static ConstString g___begin_("__begin_"); + + m_start = nullptr; + m_num_elements = 0; + m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0); + if (!m_element_type.IsValid()) + return false; + + if (llvm::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(g___begin_, true).get(); + } + + return false; +} + +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/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp new file mode 100644 index 0000000000000..f5281f2ce532b --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp @@ -0,0 +1,444 @@ +//===-- LibCxxList.cpp ------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/ClangASTContext.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(entry_sp) {} + ListEntry(const ListEntry &rhs) = default; + ListEntry(ValueObject *entry) + : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + + ListEntry next() { + static ConstString g_next("__next_"); + + if (!m_entry_sp) + return ListEntry(); + return ListEntry(m_entry_sp->GetChildMemberWithName(g_next, true)); + } + + ListEntry prev() { + static ConstString g_prev("__prev_"); + + if (!m_entry_sp) + return ListEntry(); + return ListEntry(m_entry_sp->GetChildMemberWithName(g_prev, true)); + } + + 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(entry) {} + ListIterator(ValueObjectSP entry) : m_entry(entry) {} + ListIterator(const ListIterator &rhs) = default; + 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; } + bool Update() override; + +protected: + AbstractListFrontEnd(ValueObject &valobj) + : SyntheticChildrenFrontEnd(valobj) {} + + size_t m_count; + ValueObject *m_head; + + static constexpr bool g_use_loop_detect = true; + size_t m_loop_detected; // 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; + 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); + + size_t CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override; +}; + +class ListFrontEnd : public AbstractListFrontEnd { +public: + ListFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~ListFrontEnd() override = default; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + +private: + lldb::addr_t m_node_address; + ValueObject *m_tail; +}; + +} // end anonymous namespace + +bool 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 false; + m_element_type = list_type.GetTypeTemplateArgument(0); + + return false; +} + +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(); +} + +size_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(size_t idx) { + if (idx >= CalculateNumChildren()) + 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, true); // 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); +} + +static ValueObjectSP GetValueOfCompressedPair(ValueObject &pair) { + ValueObjectSP value = pair.GetChildMemberWithName(ConstString("__value_"), true); + if (! value) { + // pre-r300140 member name + value = pair.GetChildMemberWithName(ConstString("__first_"), true); + } + return value; +} + +bool ForwardListFrontEnd::Update() { + AbstractListFrontEnd::Update(); + + Status err; + ValueObjectSP backend_addr(m_backend.AddressOf(err)); + if (err.Fail() || !backend_addr) + return false; + + ValueObjectSP impl_sp( + m_backend.GetChildMemberWithName(ConstString("__before_begin_"), true)); + if (!impl_sp) + return false; + impl_sp = GetValueOfCompressedPair(*impl_sp); + if (!impl_sp) + return false; + m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get(); + return false; +} + +ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp) + : AbstractListFrontEnd(*valobj_sp), m_node_address(), m_tail(nullptr) { + if (valobj_sp) + Update(); +} + +size_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(ConstString("__size_alloc_"), true)); + if (size_alloc) { + ValueObjectSP value = GetValueOfCompressedPair(*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(size_t idx) { + static ConstString g_value("__value_"); + static ConstString g_next("__next_"); + + if (idx >= CalculateNumChildren()) + 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, true); // 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); +} + +bool 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 false; + m_node_address = backend_addr->GetValueAsUnsigned(0); + if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS) + return false; + ValueObjectSP impl_sp( + m_backend.GetChildMemberWithName(ConstString("__end_"), true)); + if (!impl_sp) + return false; + m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get(); + m_tail = impl_sp->GetChildMemberWithName(ConstString("__prev_"), true).get(); + return false; +} + +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/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp new file mode 100644 index 0000000000000..619c718a1c1b9 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp @@ -0,0 +1,463 @@ +//===-- LibCxxMap.cpp -------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/ClangASTContext.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; + +class MapEntry { +public: + MapEntry() = default; + explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} + MapEntry(const MapEntry &rhs) = default; + explicit MapEntry(ValueObject *entry) + : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + + ValueObjectSP left() const { + static ConstString g_left("__left_"); + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetSyntheticChildAtOffset( + 0, m_entry_sp->GetCompilerType(), true); + } + + ValueObjectSP right() const { + static ConstString g_right("__right_"); + 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 { + static ConstString g_parent("__parent_"); + 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() = default; + MapIterator(MapEntry entry, size_t depth = 0) + : m_entry(entry), m_max_depth(depth), m_error(false) {} + MapIterator(ValueObjectSP entry, size_t depth = 0) + : m_entry(entry), m_max_depth(depth), m_error(false) {} + MapIterator(const MapIterator &rhs) + : m_entry(rhs.m_entry), m_max_depth(rhs.m_max_depth), m_error(false) {} + MapIterator(ValueObject *entry, size_t depth = 0) + : m_entry(entry), m_max_depth(depth), m_error(false) {} + + 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(); + } + +protected: + 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()); + } + +private: + 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; + bool m_error; +}; + +namespace lldb_private { +namespace formatters { +class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdMapSyntheticFrontEnd() override = default; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + bool GetDataType(); + + void GetValueOffset(const lldb::ValueObjectSP &node); + + ValueObject *m_tree; + ValueObject *m_root_node; + CompilerType m_element_type; + uint32_t m_skip_size; + size_t m_count; + std::map<size_t, MapIterator> m_iterators; +}; +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: + LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_tree(nullptr), + m_root_node(nullptr), m_element_type(), m_skip_size(UINT32_MAX), + m_count(UINT32_MAX), m_iterators() { + if (valobj_sp) + Update(); +} + +size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: + CalculateNumChildren() { + static ConstString g___pair3_("__pair3_"); + static ConstString g___first_("__first_"); + static ConstString g___value_("__value_"); + + if (m_count != UINT32_MAX) + return m_count; + if (m_tree == nullptr) + return 0; + ValueObjectSP m_item(m_tree->GetChildMemberWithName(g___pair3_, true)); + if (!m_item) + return 0; + + switch (m_item->GetCompilerType().GetNumDirectBaseClasses()) { + case 1: + // Assume a pre llvm r300140 __compressed_pair implementation: + m_item = m_item->GetChildMemberWithName(g___first_, true); + break; + case 2: { + // Assume a post llvm r300140 __compressed_pair implementation: + ValueObjectSP first_elem_parent = m_item->GetChildAtIndex(0, true); + m_item = first_elem_parent->GetChildMemberWithName(g___value_, true); + break; + } + default: + return false; + } + + if (!m_item) + return 0; + m_count = m_item->GetValueAsUnsigned(0); + return m_count; +} + +bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() { + static ConstString g___value_("__value_"); + static ConstString g_tree_("__tree_"); + static ConstString g_pair3("__pair3_"); + + if (m_element_type.GetOpaqueQualType() && m_element_type.GetTypeSystem()) + return true; + m_element_type.Clear(); + ValueObjectSP deref; + Status error; + deref = m_root_node->Dereference(error); + if (!deref || error.Fail()) + return false; + deref = deref->GetChildMemberWithName(g___value_, true); + if (deref) { + m_element_type = deref->GetCompilerType(); + return true; + } + deref = m_backend.GetChildAtNamePath({g_tree_, g_pair3}); + if (!deref) + return false; + m_element_type = deref->GetCompilerType() + .GetTypeTemplateArgument(1) + .GetTypeTemplateArgument(1); + if (m_element_type) { + std::string name; + uint64_t bit_offset_ptr; + uint32_t bitfield_bit_size_ptr; + bool is_bitfield_ptr; + m_element_type = m_element_type.GetFieldAtIndex( + 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr); + m_element_type = m_element_type.GetTypedefedType(); + return m_element_type.IsValid(); + } else { + m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0); + return m_element_type.IsValid(); + } +} + +void lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset( + const lldb::ValueObjectSP &node) { + if (m_skip_size != UINT32_MAX) + return; + if (!node) + return; + CompilerType node_type(node->GetCompilerType()); + uint64_t bit_offset; + if (node_type.GetIndexOfFieldWithName("__value_", nullptr, &bit_offset) != + UINT32_MAX) { + m_skip_size = bit_offset / 8u; + } else { + ClangASTContext *ast_ctx = + llvm::dyn_cast_or_null<ClangASTContext>(node_type.GetTypeSystem()); + if (!ast_ctx) + return; + CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( + ConstString(), + {{"ptr0", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, + {"ptr1", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, + {"ptr2", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, + {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)}, + {"payload", (m_element_type.GetCompleteType(), m_element_type)}}); + std::string child_name; + uint32_t child_byte_size; + int32_t child_byte_offset = 0; + uint32_t child_bitfield_bit_size; + uint32_t child_bitfield_bit_offset; + bool child_is_base_class; + bool child_is_deref_of_parent; + uint64_t language_flags; + if (tree_node_type + .GetChildCompilerTypeAtIndex( + nullptr, 4, true, true, true, 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, nullptr, language_flags) + .IsValid()) + m_skip_size = (uint32_t)child_byte_offset; + } +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex( + size_t idx) { + static ConstString g___cc("__cc"); + static ConstString g___nc("__nc"); + static ConstString g___value_("__value_"); + + if (idx >= CalculateNumChildren()) + return lldb::ValueObjectSP(); + if (m_tree == nullptr || m_root_node == nullptr) + return lldb::ValueObjectSP(); + + MapIterator iterator(m_root_node, CalculateNumChildren()); + + const bool need_to_skip = (idx > 0); + size_t actual_advancde = idx; + if (need_to_skip) { + auto cached_iterator = m_iterators.find(idx - 1); + if (cached_iterator != m_iterators.end()) { + iterator = cached_iterator->second; + actual_advancde = 1; + } + } + + ValueObjectSP iterated_sp(iterator.advance(actual_advancde)); + if (!iterated_sp) { + // this tree is garbage - stop + m_tree = + nullptr; // this will stop all future searches until an Update() happens + return iterated_sp; + } + if (GetDataType()) { + if (!need_to_skip) { + Status error; + iterated_sp = iterated_sp->Dereference(error); + if (!iterated_sp || error.Fail()) { + m_tree = nullptr; + return lldb::ValueObjectSP(); + } + GetValueOffset(iterated_sp); + auto child_sp = iterated_sp->GetChildMemberWithName(g___value_, true); + if (child_sp) + iterated_sp = child_sp; + else + iterated_sp = iterated_sp->GetSyntheticChildAtOffset( + m_skip_size, m_element_type, true); + if (!iterated_sp) { + m_tree = nullptr; + return lldb::ValueObjectSP(); + } + } else { + // because of the way our debug info is made, we need to read item 0 + // first so that we can cache information used to generate other elements + if (m_skip_size == UINT32_MAX) + GetChildAtIndex(0); + if (m_skip_size == UINT32_MAX) { + m_tree = nullptr; + return lldb::ValueObjectSP(); + } + iterated_sp = iterated_sp->GetSyntheticChildAtOffset( + m_skip_size, m_element_type, true); + if (!iterated_sp) { + m_tree = nullptr; + return lldb::ValueObjectSP(); + } + } + } else { + m_tree = nullptr; + return lldb::ValueObjectSP(); + } + // 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_ + DataExtractor data; + Status error; + iterated_sp->GetData(data, error); + if (error.Fail()) { + m_tree = nullptr; + return lldb::ValueObjectSP(); + } + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + auto potential_child_sp = CreateValueObjectFromData( + name.GetString(), data, m_backend.GetExecutionContextRef(), + m_element_type); + if (potential_child_sp) { + switch (potential_child_sp->GetNumChildren()) { + case 1: { + auto child0_sp = potential_child_sp->GetChildAtIndex(0, true); + if (child0_sp && 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, true); + auto child1_sp = potential_child_sp->GetChildAtIndex(1, true); + if (child0_sp && child0_sp->GetName() == g___cc && child1_sp && + child1_sp->GetName() == g___nc) + potential_child_sp = child0_sp->Clone(ConstString(name.GetString())); + break; + } + } + } + m_iterators[idx] = iterator; + return potential_child_sp; +} + +bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { + static ConstString g___tree_("__tree_"); + static ConstString g___begin_node_("__begin_node_"); + m_count = UINT32_MAX; + m_tree = m_root_node = nullptr; + m_iterators.clear(); + m_tree = m_backend.GetChildMemberWithName(g___tree_, true).get(); + if (!m_tree) + return false; + m_root_node = m_tree->GetChildMemberWithName(g___begin_node_, true).get(); + return false; +} + +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); +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp new file mode 100644 index 0000000000000..b1ad171d0b0c6 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp @@ -0,0 +1,83 @@ +//===-- LibCxxOptional.cpp --------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibCxx.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + +class OptionalFrontEnd : public SyntheticChildrenFrontEnd { +public: + OptionalFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { + Update(); + } + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + bool Update() override; + size_t CalculateNumChildren() override { return m_size; } + ValueObjectSP GetChildAtIndex(size_t idx) override; + +private: + size_t m_size = 0; +}; +} // namespace + +bool OptionalFrontEnd::Update() { + ValueObjectSP engaged_sp( + m_backend.GetChildMemberWithName(ConstString("__engaged_"), true)); + + if (!engaged_sp) + return false; + + // __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_size = engaged_sp->GetValueAsUnsigned(0); + + return false; +} + +ValueObjectSP OptionalFrontEnd::GetChildAtIndex(size_t idx) { + if (idx >= m_size) + return ValueObjectSP(); + + // __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_. + ValueObjectSP val_sp( + m_backend.GetChildMemberWithName(ConstString("__engaged_"), true) + ->GetParent() + ->GetChildAtIndex(0, true) + ->GetChildMemberWithName(ConstString("__val_"), true)); + + if (!val_sp) + return ValueObjectSP(); + + CompilerType holder_type = val_sp->GetCompilerType(); + + if (!holder_type) + return ValueObjectSP(); + + return val_sp->Clone(ConstString(llvm::formatv("Value").str())); +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxOptionalFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new OptionalFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp new file mode 100644 index 0000000000000..2f06d684f953c --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp @@ -0,0 +1,65 @@ +//===-- LibCxxQueue.cpp -----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#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; } + bool Update() override; + + size_t CalculateNumChildren() override { + return m_container_sp ? m_container_sp->GetNumChildren() : 0; + } + + ValueObjectSP GetChildAtIndex(size_t idx) override { + return m_container_sp ? m_container_sp->GetChildAtIndex(idx, true) + : 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 + +bool QueueFrontEnd::Update() { + m_container_sp = nullptr; + ValueObjectSP c_sp = m_backend.GetChildMemberWithName(ConstString("c"), true); + if (!c_sp) + return false; + m_container_sp = c_sp->GetSyntheticValue().get(); + return false; +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxQueueFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new QueueFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp new file mode 100644 index 0000000000000..45294e25f0f5d --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp @@ -0,0 +1,93 @@ +//===-- LibCxxTuple.cpp -----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#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; } + bool Update() override; + size_t CalculateNumChildren() override { return m_elements.size(); } + ValueObjectSP GetChildAtIndex(size_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; +}; +} + +bool TupleFrontEnd::Update() { + m_elements.clear(); + m_base = nullptr; + + ValueObjectSP base_sp; + base_sp = m_backend.GetChildMemberWithName(ConstString("__base_"), true); + if (!base_sp) { + // Pre r304382 name of the base element. + base_sp = m_backend.GetChildMemberWithName(ConstString("base_"), true); + } + if (!base_sp) + return false; + m_base = base_sp.get(); + m_elements.assign(base_sp->GetCompilerType().GetNumDirectBaseClasses(), + nullptr); + return false; +} + +ValueObjectSP TupleFrontEnd::GetChildAtIndex(size_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, true); + if (!holder_sp) + return ValueObjectSP(); + + ValueObjectSP elem_sp = holder_sp->GetChildAtIndex(0, true); + 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/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp new file mode 100644 index 0000000000000..b2c38c915c815 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp @@ -0,0 +1,221 @@ +//===-- LibCxxUnorderedMap.cpp ----------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/ClangASTContext.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 lldb_private { +namespace formatters { +class LibcxxStdUnorderedMapSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + CompilerType m_element_type; + CompilerType m_node_type; + ValueObject *m_tree; + size_t m_num_elements; + ValueObject *m_next_element; + std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache; +}; +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), m_tree(nullptr), + m_num_elements(0), m_next_element(nullptr), m_elements_cache() { + if (valobj_sp) + Update(); +} + +size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + CalculateNumChildren() { + if (m_num_elements != UINT32_MAX) + return m_num_elements; + return 0; +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (idx >= CalculateNumChildren()) + 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(ConstString("__value_"), true); + ValueObjectSP hash_sp = + node_sp->GetChildMemberWithName(ConstString("__hash_"), true); + if (!hash_sp || !value_sp) { + if (!m_element_type) { + auto p1_sp = m_backend.GetChildAtNamePath({ConstString("__table_"), + ConstString("__p1_")}); + if (!p1_sp) + return nullptr; + + ValueObjectSP first_sp = nullptr; + switch (p1_sp->GetCompilerType().GetNumDirectBaseClasses()) { + case 1: + // Assume a pre llvm r300140 __compressed_pair implementation: + first_sp = p1_sp->GetChildMemberWithName(ConstString("__first_"), + true); + break; + case 2: { + // Assume a post llvm r300140 __compressed_pair implementation: + ValueObjectSP first_elem_parent_sp = + p1_sp->GetChildAtIndex(0, true); + first_sp = p1_sp->GetChildMemberWithName(ConstString("__value_"), + true); + break; + } + default: + return nullptr; + } + + 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); + std::string name; + m_element_type = + m_element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr); + m_element_type = m_element_type.GetTypedefedType(); + } + if (!m_node_type) + return nullptr; + node_sp = node_sp->Cast(m_node_type); + value_sp = node_sp->GetChildMemberWithName(ConstString("__value_"), true); + hash_sp = node_sp->GetChildMemberWithName(ConstString("__hash_"), true); + if (!value_sp || !hash_sp) + return nullptr; + } + m_elements_cache.push_back( + {value_sp.get(), hash_sp->GetValueAsUnsigned(0)}); + m_next_element = + node_sp->GetChildMemberWithName(ConstString("__next_"), true).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, + val_hash.first->GetCompilerType()); +} + +bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + Update() { + m_num_elements = UINT32_MAX; + m_next_element = nullptr; + m_elements_cache.clear(); + ValueObjectSP table_sp = + m_backend.GetChildMemberWithName(ConstString("__table_"), true); + if (!table_sp) + return false; + + ValueObjectSP p2_sp = table_sp->GetChildMemberWithName( + ConstString("__p2_"), true); + ValueObjectSP num_elements_sp = nullptr; + llvm::SmallVector<ConstString, 3> next_path; + switch (p2_sp->GetCompilerType().GetNumDirectBaseClasses()) { + case 1: + // Assume a pre llvm r300140 __compressed_pair implementation: + num_elements_sp = p2_sp->GetChildMemberWithName( + ConstString("__first_"), true); + next_path.append({ConstString("__p1_"), ConstString("__first_"), + ConstString("__next_")}); + break; + case 2: { + // Assume a post llvm r300140 __compressed_pair implementation: + ValueObjectSP first_elem_parent = p2_sp->GetChildAtIndex(0, true); + num_elements_sp = first_elem_parent->GetChildMemberWithName( + ConstString("__value_"), true); + next_path.append({ConstString("__p1_"), ConstString("__value_"), + ConstString("__next_")}); + break; + } + default: + return false; + } + + if (!num_elements_sp) + return false; + m_num_elements = num_elements_sp->GetValueAsUnsigned(0); + m_tree = table_sp->GetChildAtNamePath(next_path).get(); + if (m_num_elements > 0) + m_next_element = + table_sp->GetChildAtNamePath(next_path).get(); + return false; +} + +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); +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp new file mode 100644 index 0000000000000..62945bd3ce80b --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp @@ -0,0 +1,254 @@ +//===-- LibCxxVariant.cpp --------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibCxxVariant.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/ScopeExit.h" + +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 }; + +LibcxxVariantIndexValidity +LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) { + ValueObjectSP index_sp( + impl_sp->GetChildMemberWithName(ConstString("__index"), true)); + + if (!index_sp) + return LibcxxVariantIndexValidity::INVALID; + + int64_t index_value = index_sp->GetValueAsSigned(0); + + if (index_value == -1) + return LibcxxVariantIndexValidity::NPOS; + + return LibcxxVariantIndexValidity::VALID; +} + +llvm::Optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) { + ValueObjectSP index_sp( + impl_sp->GetChildMemberWithName(ConstString("__index"), true)); + + if (!index_sp) + return {}; + + return {index_sp->GetValueAsUnsigned(0)}; +} + +ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) { + ValueObjectSP data_sp( + impl_sp->GetChildMemberWithName(ConstString("__data"), true)); + + if (!data_sp) + return ValueObjectSP{}; + + ValueObjectSP current_level = data_sp; + for (uint64_t n = index; n != 0; --n) { + ValueObjectSP tail_sp( + current_level->GetChildMemberWithName(ConstString("__tail"), true)); + + if (!tail_sp) + return ValueObjectSP{}; + + current_level = tail_sp; + } + + return current_level->GetChildMemberWithName(ConstString("__head"), true); +} +} // 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( + valobj_sp->GetChildMemberWithName(ConstString("__impl"), true)); + + 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.Printf(" Active Type = %s ", template_type.GetTypeName().GetCString()); + + 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; } + bool Update() override; + size_t CalculateNumChildren() override { return m_size; } + ValueObjectSP GetChildAtIndex(size_t idx) override; + +private: + size_t m_size = 0; +}; +} // namespace + +bool VariantFrontEnd::Update() { + m_size = 0; + ValueObjectSP impl_sp( + m_backend.GetChildMemberWithName(ConstString("__impl"), true)); + if (!impl_sp) + return false; + + LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); + + if (validity == LibcxxVariantIndexValidity::INVALID) + return false; + + if (validity == LibcxxVariantIndexValidity::NPOS) + return true; + + m_size = 1; + + return false; +} + +ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) { + if (idx >= m_size) + return ValueObjectSP(); + + ValueObjectSP impl_sp( + m_backend.GetChildMemberWithName(ConstString("__impl"), true)); + + auto optional_index_value = LibcxxVariantIndexValue(impl_sp); + + if (!optional_index_value) + return ValueObjectSP(); + + uint64_t index_value = *optional_index_value; + + ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); + + if (!nth_head) + return ValueObjectSP(); + + CompilerType head_type = nth_head->GetCompilerType(); + + if (!head_type) + return ValueObjectSP(); + + CompilerType template_type = head_type.GetTypeTemplateArgument(1); + + if (!template_type) + return ValueObjectSP(); + + ValueObjectSP head_value( + nth_head->GetChildMemberWithName(ConstString("__value"), true)); + + if (!head_value) + return ValueObjectSP(); + + return head_value->Clone(ConstString(ConstString("Value").AsCString())); +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new VariantFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h new file mode 100644 index 0000000000000..65db5aeaa99de --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h @@ -0,0 +1,30 @@ +//===-- 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 liblldb_LibCxxVariant_h_ +#define liblldb_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 // liblldb_LibCxxVariant_h_ diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp new file mode 100644 index 0000000000000..bcd7442bc6693 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -0,0 +1,296 @@ +//===-- LibCxxVector.cpp ----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" + +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; + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_start; + ValueObject *m_finish; + CompilerType m_element_type; + uint32_t m_element_size; +}; + +class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool 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; + lldb::addr_t m_base_data_address; + 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_start(nullptr), + m_finish(nullptr), m_element_type(), m_element_size(0) { + 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; +} + +size_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( + size_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); +} + +bool lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() { + m_start = m_finish = nullptr; + ValueObjectSP data_type_finder_sp( + m_backend.GetChildMemberWithName(ConstString("__end_cap_"), true)); + if (!data_type_finder_sp) + return false; + + switch (data_type_finder_sp->GetCompilerType().GetNumDirectBaseClasses()) { + case 1: + // Assume a pre llvm r300140 __compressed_pair implementation: + data_type_finder_sp = data_type_finder_sp->GetChildMemberWithName( + ConstString("__first_"), true); + break; + case 2: { + // Assume a post llvm r300140 __compressed_pair implementation: + ValueObjectSP first_elem_parent_sp = + data_type_finder_sp->GetChildAtIndex(0, true); + data_type_finder_sp = first_elem_parent_sp->GetChildMemberWithName( + ConstString("__value_"), true); + break; + } + default: + return false; + } + + if (!data_type_finder_sp) + return false; + m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType(); + if (llvm::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(ConstString("__begin_"), true).get(); + m_finish = + m_backend.GetChildMemberWithName(ConstString("__end_"), true).get(); + } + } + return false; +} + +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_count(0), m_base_data_address(0), m_children() { + if (valobj_sp) { + Update(); + m_bool_type = + valobj_sp->GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeBool); + } +} + +size_t lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: + CalculateNumChildren() { + return m_count; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex( + size_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); + llvm::Optional<uint64_t> size = m_bool_type.GetByteSize(nullptr); + if (!size) + return {}; + DataBufferSP 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 + } + } + }*/ + +bool lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + ValueObjectSP size_sp( + valobj_sp->GetChildMemberWithName(ConstString("__size_"), true)); + if (!size_sp) + return false; + m_count = size_sp->GetValueAsUnsigned(0); + if (!m_count) + return true; + ValueObjectSP begin_sp( + valobj_sp->GetChildMemberWithName(ConstString("__begin_"), true)); + if (!begin_sp) { + m_count = 0; + return false; + } + m_base_data_address = begin_sp->GetValueAsUnsigned(0); + if (!m_base_data_address) { + m_count = 0; + return false; + } + return false; +} + +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 >= CalculateNumChildren()) + 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/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp new file mode 100644 index 0000000000000..0e0f6663c924b --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp @@ -0,0 +1,426 @@ +//===-- LibStdcpp.cpp -------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LibStdcpp.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/VectorIterator.h" +#include "lldb/Symbol/ClangASTContext.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 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); + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ExecutionContextRef m_exe_ctx_ref; + lldb::addr_t m_pair_address; + CompilerType m_pair_type; + lldb::ValueObjectSP m_pair_sp; +}; + +class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit LibStdcppSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; +}; + +} // end of anonymous namespace + +LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_address(0), + m_pair_type(), m_pair_sp() { + if (valobj_sp) + Update(); +} + +bool LibstdcppMapIteratorSyntheticFrontEnd::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return false; + + bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8); + + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + ValueObjectSP _M_node_sp( + valobj_sp->GetChildMemberWithName(ConstString("_M_node"), true)); + if (!_M_node_sp) + return false; + + m_pair_address = _M_node_sp->GetValueAsUnsigned(0); + if (m_pair_address == 0) + return false; + + 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 false; + m_pair_type = pair_type; + } else + return false; + + return true; +} + +size_t LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP +LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex(size_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, true); + } + 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) { + static ConstString g_item_name; + if (!g_item_name) + g_item_name.SetCString("_M_current"); + return (valobj_sp + ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name) + : nullptr); +} + +lldb_private::formatters::VectorIteratorSyntheticFrontEnd:: + VectorIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp, + ConstString item_name) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), + m_item_name(item_name), m_item_sp() { + if (valobj_sp) + Update(); +} + +bool VectorIteratorSyntheticFrontEnd::Update() { + m_item_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + + if (!valobj_sp) + return false; + + ValueObjectSP item_ptr(valobj_sp->GetChildMemberWithName(m_item_name, true)); + if (!item_ptr) + return false; + if (item_ptr->GetValueAsUnsigned(0) == 0) + return false; + 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 false; +} + +size_t VectorIteratorSyntheticFrontEnd::CalculateNumChildren() { return 1; } + +lldb::ValueObjectSP +VectorIteratorSyntheticFrontEnd::GetChildAtIndex(size_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 = + 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.SetProcessSP(process_sp); + 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); + + 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. + llvm::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.SetProcessSP(process_sp); + 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.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(); +} + +size_t LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() { return 1; } + +lldb::ValueObjectSP +LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ValueObjectSP(); + + if (idx == 0) + return valobj_sp->GetChildMemberWithName(ConstString("_M_ptr"), true); + else + return lldb::ValueObjectSP(); +} + +bool LibStdcppSharedPtrSyntheticFrontEnd::Update() { return false; } + +bool LibStdcppSharedPtrSyntheticFrontEnd::MightHaveChildren() { return true; } + +size_t LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (name == "_M_ptr") + return 0; + 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(ConstString("_M_ptr"), true)); + if (!ptr_sp) + return false; + + ValueObjectSP usecount_sp(valobj_sp->GetChildAtNamePath( + {ConstString("_M_refcount"), ConstString("_M_pi"), + ConstString("_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/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h new file mode 100644 index 0000000000000..e7f88d667c141 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -0,0 +1,59 @@ +//===-- 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 liblldb_LibStdCpp_h_ +#define liblldb_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 * +LibStdcppVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +} // namespace formatters +} // namespace lldb_private + +#endif // liblldb_LibStdCpp_h_ diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp new file mode 100644 index 0000000000000..0ac7b8f8e02b0 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp @@ -0,0 +1,111 @@ +//===-- LibStdcppTuple.cpp --------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#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); + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool 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(); +} + +bool LibStdcppTupleSyntheticFrontEnd::Update() { + m_members.clear(); + + ValueObjectSP valobj_backend_sp = m_backend.GetSP(); + if (!valobj_backend_sp) + return false; + + 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->GetNumChildren(); + for (size_t i = 0; i < child_count; ++i) { + ValueObjectSP child_sp = current_child->GetChildAtIndex(i, true); + llvm::StringRef name_str = child_sp->GetName().GetStringRef(); + if (name_str.startswith("std::_Tuple_impl<")) { + next_child_sp = child_sp; + } else if (name_str.startswith("std::_Head_base<")) { + ValueObjectSP value_sp = + child_sp->GetChildMemberWithName(ConstString("_M_head_impl"), true); + if (value_sp) { + StreamString name; + name.Printf("[%zd]", m_members.size()); + m_members.push_back(value_sp->Clone(ConstString(name.GetString())).get()); + } + } + } + } + + return false; +} + +bool LibStdcppTupleSyntheticFrontEnd::MightHaveChildren() { return true; } + +lldb::ValueObjectSP +LibStdcppTupleSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (idx < m_members.size() && m_members[idx]) + return m_members[idx]->GetSP(); + return lldb::ValueObjectSP(); +} + +size_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/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp new file mode 100644 index 0000000000000..cceb511cdc465 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp @@ -0,0 +1,174 @@ +//===-- LibStdcppUniquePointer.cpp ------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#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); + + size_t CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; + + bool 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(ConstString("_M_t"), true); + if (!obj_child_sp) + return nullptr; + + ValueObjectSP obj_subchild_sp = + obj_child_sp->GetChildMemberWithName(ConstString("_M_t"), true); + + // 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; +} + +bool LibStdcppUniquePtrSyntheticFrontEnd::Update() { + ValueObjectSP tuple_sp = GetTuple(); + + if (!tuple_sp) + return false; + + 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(); + + ValueObjectSP del_obj = tuple_frontend->GetChildAtIndex(1); + if (del_obj) + m_del_obj = del_obj->Clone(ConstString("deleter")).get(); + + if (m_ptr_obj) { + Status error; + ValueObjectSP obj_obj = m_ptr_obj->Dereference(error); + if (error.Success()) { + m_obj_obj = obj_obj->Clone(ConstString("object")).get(); + } + } + + return false; +} + +bool LibStdcppUniquePtrSyntheticFrontEnd::MightHaveChildren() { return true; } + +lldb::ValueObjectSP +LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(size_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 && m_obj_obj) + return m_obj_obj->GetSP(); + return lldb::ValueObjectSP(); +} + +size_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/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp b/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp new file mode 100644 index 0000000000000..248c51acea420 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp @@ -0,0 +1,98 @@ +//===-- MSVCUndecoratedNameParser.cpp ---------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "MSVCUndecoratedNameParser.h" + +#include <stack> + +MSVCUndecoratedNameParser::MSVCUndecoratedNameParser(llvm::StringRef name) { + 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.find('`') != llvm::StringRef::npos; +} + +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/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h b/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h new file mode 100644 index 0000000000000..6e20877cae1b5 --- /dev/null +++ b/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 liblldb_MSVCUndecoratedNameParser_h_ +#define liblldb_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 |
