
This adds synthetic child providers for `std::vector<T>` and `std::vector<bool>` for MSVC's STL. The structure of a `std::vector<T>` is relatively similar to libc++'s implementation that uses `__begin` and `__end`. `std::vector<bool>` is different. It's a `std::vector<unsigned int>` wrapper instead of `std::vector<uint8_t>`. This makes the calculation slightly less simple. I put a comment in the `GetChildAtIndex` to make this clear. - [NatVis for `std::vector<T>`](313964b78a/stl/debugger/STL.natvis (L1193-L1205)
) - [NatVis for `std::vector<bool>`](313964b78a/stl/debugger/STL.natvis (L1167-L1179)
) Towards #24834.
306 lines
9.9 KiB
C++
306 lines
9.9 KiB
C++
//===-- 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_exe_ctx_ref.Clear();
|
|
m_count = 0;
|
|
m_element_bit_size = 0;
|
|
m_base_data_address = 0;
|
|
m_children.clear();
|
|
|
|
ValueObjectSP valobj_sp = m_backend.GetSP();
|
|
if (!valobj_sp)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
auto exe_ctx_ref = valobj_sp->GetExecutionContextRef();
|
|
|
|
ValueObjectSP size_sp = valobj_sp->GetChildMemberWithName("_Mysize");
|
|
if (!size_sp)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
uint64_t count = size_sp->GetValueAsUnsigned(0);
|
|
if (count == 0)
|
|
return lldb::ChildCacheState::eReuse;
|
|
|
|
ValueObjectSP begin_sp(valobj_sp->GetChildAtNamePath(
|
|
{"_Myvec", "_Mypair", "_Myval2", "_Myfirst"}));
|
|
if (!begin_sp)
|
|
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> element_bit_size = begin_ty.GetBitSize(nullptr);
|
|
if (!element_bit_size)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
uint64_t base_data_address = begin_sp->GetValueAsUnsigned(0);
|
|
if (!base_data_address)
|
|
return lldb::ChildCacheState::eRefetch;
|
|
|
|
m_exe_ctx_ref = exe_ctx_ref;
|
|
m_count = count;
|
|
m_element_bit_size = *element_bit_size;
|
|
m_base_data_address = base_data_address;
|
|
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) {
|
|
if (!valobj_sp)
|
|
return nullptr;
|
|
|
|
valobj_sp = valobj_sp->GetNonSyntheticValue();
|
|
if (!valobj_sp)
|
|
return nullptr;
|
|
|
|
// We can't check the template parameter here, because PDB doesn't include
|
|
// this information.
|
|
|
|
// vector<T>
|
|
if (valobj_sp->GetChildMemberWithName("_Mypair") != nullptr)
|
|
return new MsvcStlVectorSyntheticFrontEnd(valobj_sp);
|
|
// vector<bool>
|
|
if (valobj_sp->GetChildMemberWithName("_Myvec") != nullptr)
|
|
return new MsvcStlVectorBoolSyntheticFrontEnd(valobj_sp);
|
|
|
|
return nullptr;
|
|
}
|