diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index bbfc31a722f27..6869e4b56ebee 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -33,6 +33,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN LibStdcppTuple.cpp LibStdcppUniquePointer.cpp MsvcStl.cpp + MsvcStlVector.cpp MSVCUndecoratedNameParser.cpp LINK_COMPONENTS diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 17963c0273ba8..5a9fcba05ddbc 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1404,7 +1404,7 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { stl_deref_flags.SetFrontEndWantsDereference(); cpp_category_sp->AddTypeSynthetic( - "^std::(__debug::)?vector<.+>(( )?&)?$", eFormatterMatchRegex, + "^std::__debug::vector<.+>(( )?&)?$", eFormatterMatchRegex, SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider"))); @@ -1465,10 +1465,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "libstdc++ std::bitset summary provider", "^std::(__debug::)?bitset<.+>(( )?&)?$", stl_summary_flags, true); - AddCXXSummary( - cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, - "libstdc++ std::vector summary provider", - "^std::(__debug::)?vector<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::ContainerSizeSummaryProvider, + "libstdc++ std::__debug::vector summary provider", + "^std::__debug::vector<.+>(( )?&)?$", stl_summary_flags, true); AddCXXSummary( cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, @@ -1611,6 +1611,10 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { .SetDontShowValue(false) .SetShowMembersOneLiner(false) .SetHideItemNames(false); + SyntheticChildren::Flags stl_synth_flags; + stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( + false); + using StringElementType = StringPrinter::StringElementType; RegisterStdStringSummaryProvider( @@ -1636,6 +1640,28 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { return LibStdcppStringSummaryProvider(valobj, stream, options); }, "MSVC STL/libstdc++ std::wstring summary provider")); + + stl_summary_flags.SetDontShowChildren(false); + stl_summary_flags.SetSkipPointers(false); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::ContainerSizeSummaryProvider, + "MSVC/libstdc++ std::vector summary provider", + "^std::vector<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSynthetic( + cpp_category_sp, + [](CXXSyntheticChildren *, + ValueObjectSP valobj_sp) -> SyntheticChildrenFrontEnd * { + if (!valobj_sp) + return nullptr; + if (auto *msvc = MsvcStlVectorSyntheticFrontEndCreator(valobj_sp)) + return msvc; + return new ScriptedSyntheticChildren::FrontEnd( + "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider", + *valobj_sp); + }, + "MSVC/libstdc++ std::vector synthetic provider", + "^std::vector<.+>(( )?&)?$", stl_synth_flags, true); } static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h index e4ed923033aa7..78b9e56d65e28 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -29,6 +29,9 @@ bool MsvcStlWStringSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); // VC 2015+ std::wstring +lldb_private::SyntheticChildrenFrontEnd * +MsvcStlVectorSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp); + } // namespace formatters } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp new file mode 100644 index 0000000000000..f845aa30ebe1a --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp @@ -0,0 +1,295 @@ +//===-- MsvcStlVector.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MsvcStl.h" + +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/TypeSynthetic.h" + +using namespace lldb; + +namespace lldb_private { +namespace formatters { + +class MsvcStlVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + MsvcStlVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + llvm::Expected GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_start = nullptr; + ValueObject *m_finish = nullptr; + CompilerType m_element_type; + uint32_t m_element_size = 0; +}; + +class MsvcStlVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + MsvcStlVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + llvm::Expected GetIndexOfChildWithName(ConstString name) override; + +private: + CompilerType m_bool_type; + ExecutionContextRef m_exe_ctx_ref; + uint64_t m_count = 0; + uint64_t m_element_bit_size = 0; + lldb::addr_t m_base_data_address = 0; + std::map m_children; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::MsvcStlVectorSyntheticFrontEnd:: + MsvcStlVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +llvm::Expected lldb_private::formatters:: + MsvcStlVectorSyntheticFrontEnd::CalculateNumChildren() { + if (!m_start || !m_finish) + return llvm::createStringError( + "Failed to determine start/end of vector data."); + + uint64_t start_val = m_start->GetValueAsUnsigned(0); + uint64_t finish_val = m_finish->GetValueAsUnsigned(0); + + // A default-initialized empty vector. + if (start_val == 0 && finish_val == 0) + return 0; + + if (start_val == 0) + return llvm::createStringError("Invalid value for start of vector."); + + if (finish_val == 0) + return llvm::createStringError("Invalid value for end of vector."); + + if (start_val > finish_val) + return llvm::createStringError( + "Start of vector data begins after end pointer."); + + size_t num_children = (finish_val - start_val); + if (num_children % m_element_size) + return llvm::createStringError("Size not multiple of element size."); + + return num_children / m_element_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::MsvcStlVectorSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_start || !m_finish) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::MsvcStlVectorSyntheticFrontEnd::Update() { + m_start = m_finish = nullptr; + ValueObjectSP data_sp(m_backend.GetChildAtNamePath({"_Mypair", "_Myval2"})); + + if (!data_sp) + return lldb::ChildCacheState::eRefetch; + + m_start = data_sp->GetChildMemberWithName("_Myfirst").get(); + m_finish = data_sp->GetChildMemberWithName("_Mylast").get(); + if (!m_start || !m_finish) + return lldb::ChildCacheState::eRefetch; + + m_element_type = m_start->GetCompilerType().GetPointeeType(); + llvm::Expected size_or_err = m_element_type.GetByteSize(nullptr); + if (size_or_err) + m_element_size = *size_or_err; + else + LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(), + "{0}"); + + return lldb::ChildCacheState::eRefetch; +} + +llvm::Expected lldb_private::formatters:: + MsvcStlVectorSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { + if (!m_start || !m_finish) + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + auto optional_idx = ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + return *optional_idx; +} + +lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd:: + MsvcStlVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_bool_type(), m_exe_ctx_ref(), + m_children() { + if (valobj_sp) { + Update(); + m_bool_type = + valobj_sp->GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeBool); + } +} + +llvm::Expected lldb_private::formatters:: + MsvcStlVectorBoolSyntheticFrontEnd::CalculateNumChildren() { + return m_count; +} + +lldb::ValueObjectSP +lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + auto iter = m_children.find(idx), end = m_children.end(); + if (iter != end) + return iter->second; + if (idx >= m_count) + return {}; + if (m_base_data_address == 0 || m_count == 0) + return {}; + if (!m_bool_type) + return {}; + + // The vector is represented as a sequence of `int`s. + // The size of an `int` is in `m_element_bit_size` (most often 32b). + // To access the element at index `i`: + // (bool)((data_address[i / bit_size] >> (i % bit_size)) & 1) + + // int *byte_location = &data_address[i / bit_size] + size_t byte_idx = (idx / m_element_bit_size) * (m_element_bit_size / 8); + lldb::addr_t byte_location = m_base_data_address + byte_idx; + + ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); + if (!process_sp) + return {}; + Status err; + Scalar scalar; + size_t bytes_read = process_sp->ReadScalarIntegerFromMemory( + byte_location, m_element_bit_size / 8, false, scalar, err); + if (err.Fail() || bytes_read == 0 || !scalar.IsValid()) + return {}; + + size_t bit_index = idx % m_element_bit_size; + bool bit_set = scalar.GetAPSInt()[bit_index]; + std::optional size = + llvm::expectedToOptional(m_bool_type.GetByteSize(nullptr)); + if (!size) + return {}; + WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0)); + if (bit_set && buffer_sp && buffer_sp->GetBytes()) { + // regardless of endianness, anything non-zero is true + *(buffer_sp->GetBytes()) = 1; + } + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + ValueObjectSP retval_sp(CreateValueObjectFromData( + name.GetString(), + DataExtractor(buffer_sp, process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()), + m_exe_ctx_ref, m_bool_type)); + if (retval_sp) + m_children[idx] = retval_sp; + return retval_sp; +} + +lldb::ChildCacheState +lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + ValueObjectSP size_sp = valobj_sp->GetChildMemberWithName("_Mysize"); + if (!size_sp) + return lldb::ChildCacheState::eRefetch; + m_count = size_sp->GetValueAsUnsigned(0); + if (!m_count) + return lldb::ChildCacheState::eReuse; + + ValueObjectSP begin_sp(valobj_sp->GetChildAtNamePath( + {"_Myvec", "_Mypair", "_Myval2", "_Myfirst"})); + if (!begin_sp) { + m_count = 0; + return lldb::ChildCacheState::eRefetch; + } + + // FIXME: the STL exposes _EEN_VBITS as a constant - it should be used instead + CompilerType begin_ty = begin_sp->GetCompilerType().GetPointeeType(); + if (!begin_ty.IsValid()) + return lldb::ChildCacheState::eRefetch; + llvm::Expected bit_size = begin_ty.GetBitSize(nullptr); + if (!bit_size) + return lldb::ChildCacheState::eRefetch; + m_element_bit_size = *bit_size; + + m_base_data_address = begin_sp->GetValueAsUnsigned(0); + if (!m_base_data_address) { + m_count = 0; + return lldb::ChildCacheState::eRefetch; + } + return lldb::ChildCacheState::eRefetch; +} + +llvm::Expected +lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_count || !m_base_data_address) + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + auto optional_idx = ExtractIndexFromString(name.AsCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + uint32_t idx = *optional_idx; + if (idx >= CalculateNumChildrenIgnoringErrors()) + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + return idx; +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::MsvcStlVectorSyntheticFrontEndCreator( + lldb::ValueObjectSP valobj_sp) { + CompilerType type = valobj_sp->GetCompilerType(); + if (!type.IsValid()) + return nullptr; + + std::vector indexes; + // vector + if (type.GetIndexOfChildMemberWithName("_Mypair", true, indexes) > 0) + return new MsvcStlVectorSyntheticFrontEnd(valobj_sp); + // vector + if (type.GetIndexOfChildMemberWithName("_Myvec", true, indexes) > 0) + return new MsvcStlVectorBoolSyntheticFrontEnd(valobj_sp); + + return nullptr; +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py index 56c86d1edde25..b4d6ce15b1af0 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py @@ -47,7 +47,7 @@ def cleanup(): self.expect( "frame variable -A vBool", substrs=[ - "size=49", + "size=73", "[0] = false", "[1] = true", "[18] = false", @@ -55,13 +55,20 @@ def cleanup(): "[36] = false", "[47] = true", "[48] = true", + "[49] = true", + "[50] = false", + "[56] = false", + "[65] = true", + "[70] = false", + "[71] = true", + "[72] = true", ], ) self.expect( "expr -A -- vBool", substrs=[ - "size=49", + "size=73", "[0] = false", "[1] = true", "[18] = false", @@ -69,6 +76,13 @@ def cleanup(): "[36] = false", "[47] = true", "[48] = true", + "[49] = true", + "[50] = false", + "[56] = false", + "[65] = true", + "[70] = false", + "[71] = true", + "[72] = true", ], ) @@ -88,3 +102,9 @@ def test_libstdcxx_debug(self): dictionary={"USE_LIBSTDCPP": 1, "CXXFLAGS_EXTRAS": "-D_GLIBCXX_DEBUG"} ) self.do_test() + + @add_test_categories(["msvcstl"]) + def test_libstdcxx(self): + # No flags, because the "msvcstl" category checks that the MSVC STL is used by default. + self.build(dictionary={}) + self.do_test() diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/main.cpp index 22fc6c89ca8a2..2c54166ace7cc 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/main.cpp @@ -1,10 +1,10 @@ #include -#include #include int main() { std::vector vBool; + // 0..=7 vBool.push_back(false); vBool.push_back(true); vBool.push_back(false); @@ -14,6 +14,7 @@ int main() { vBool.push_back(false); vBool.push_back(true); + // 8..=15 vBool.push_back(false); vBool.push_back(true); vBool.push_back(false); @@ -23,6 +24,7 @@ int main() { vBool.push_back(false); vBool.push_back(true); + // 16..=23 vBool.push_back(false); vBool.push_back(true); vBool.push_back(false); @@ -32,6 +34,7 @@ int main() { vBool.push_back(false); vBool.push_back(true); + // 24..=31 vBool.push_back(false); vBool.push_back(true); vBool.push_back(false); @@ -41,6 +44,7 @@ int main() { vBool.push_back(false); vBool.push_back(true); + // 32..=39 vBool.push_back(false); vBool.push_back(true); vBool.push_back(false); @@ -50,6 +54,7 @@ int main() { vBool.push_back(false); vBool.push_back(true); + // 40..=47 vBool.push_back(false); vBool.push_back(true); vBool.push_back(false); @@ -58,6 +63,38 @@ int main() { vBool.push_back(true); vBool.push_back(false); vBool.push_back(true); + + // 48..=55 + vBool.push_back(true); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + // 56..=63 + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + // 64..=71 + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + vBool.push_back(true); + vBool.push_back(true); + vBool.push_back(false); + vBool.push_back(true); + + // 72 vBool.push_back(true); std::puts("// Set break point at this line."); diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vector/TestDataFormatterStdVector.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vector/TestDataFormatterStdVector.py index ba8b10450f4fc..353f5fe4dcbad 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vector/TestDataFormatterStdVector.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vector/TestDataFormatterStdVector.py @@ -184,6 +184,12 @@ def test_libcxx(self): self.build(dictionary={"USE_LIBCPP": 1}) self.do_test() + @add_test_categories(["msvcstl"]) + def test_msvcstl(self): + # No flags, because the "msvcstl" category checks that the MSVC STL is used by default. + self.build(dictionary={}) + self.do_test() + def do_test_ref_and_ptr(self): """Test that that file and class static variables display correctly.""" (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( @@ -215,3 +221,8 @@ def test_ref_and_ptr_libstdcxx_debug(self): def test_ref_and_ptr_libcxx(self): self.build(dictionary={"USE_LIBCPP": 1}) self.do_test_ref_and_ptr() + + @add_test_categories(["msvcstl"]) + def test_ref_and_ptr_msvcstl(self): + self.build(dictionary={}) + self.do_test_ref_and_ptr()