================ @@ -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<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_start = nullptr; + ValueObject *m_finish = nullptr; + CompilerType m_element_type; + uint32_t m_element_size = 0; +}; + +class MsvcStlVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + MsvcStlVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + llvm::Expected<size_t> 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<size_t, lldb::ValueObjectSP> 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<uint32_t> 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<uint64_t> 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<size_t> 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<uint32_t> 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<bool> 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<uint64_t> 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<uint64_t> 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<size_t> +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<uint32_t> indexes; + // vector<T> + if (type.GetIndexOfChildMemberWithName("_Mypair", true, indexes) > 0) + return new MsvcStlVectorSyntheticFrontEnd(valobj_sp); + // vector<bool> + if (type.GetIndexOfChildMemberWithName("_Myvec", true, indexes) > 0) ---------------- Nerixyz wrote:
> Is it because PDB strips template info? This. I don't know how this is done in DWARF exactly, but my thought was that PDB could use the demangling to add template parameters(?). https://github.com/llvm/llvm-project/pull/147538 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits