blob: 79fe98a6e9d15b8a2d1b2b9be9828fdcad64c5d0 [file]
//===-- LibCxxVector.cpp --------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "LibCxx.h"
#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorExtras.h"
#include <optional>
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::formatters;
namespace lldb_private {
namespace formatters {
class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
~LibcxxStdVectorSyntheticFrontEnd() override;
llvm::Expected<uint32_t> CalculateNumChildren() override;
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
lldb::ChildCacheState Update() override;
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
private:
lldb::ChildCacheState UpdateVectorWithLayoutSubobject(ValueObject *layout);
ValueObject *m_start = nullptr;
// m_finish may point to a pointer (`__end_`) or an integer (`__size_`)
// depending on how libc++'s vector is implemented. Interpreting what is
// pointed to is done using `m_layout`.
ValueObject *m_finish = nullptr;
enum class VectorLayout : bool { Pointer, Size };
VectorLayout m_layout;
CompilerType m_element_type;
uint32_t m_element_size = 0;
};
class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
llvm::Expected<uint32_t> CalculateNumChildren() override;
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
lldb::ChildCacheState Update() override;
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
private:
CompilerType m_bool_type;
ExecutionContextRef m_exe_ctx_ref;
uint64_t m_count = 0;
lldb::addr_t m_base_data_address = 0;
std::map<size_t, lldb::ValueObjectSP> m_children;
};
} // namespace formatters
} // namespace lldb_private
lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() {
if (valobj_sp)
Update();
}
lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
~LibcxxStdVectorSyntheticFrontEnd() {
// these need to stay around because they are child objects who will follow
// their parent's life cycle
// delete m_start;
// delete m_finish;
}
static llvm::Expected<uint32_t>
CalculateNumChildrenUsingPointerArithmetic(ValueObject *begin, ValueObject *end,
uint64_t value_type_size) {
uint64_t start_val = begin->GetValueAsUnsigned(0);
uint64_t finish_val = end->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 % value_type_size)
return llvm::createStringError("size not multiple of element size");
return num_children / value_type_size;
}
static llvm::Expected<uint32_t> GetNumChildren(ValueObject *size) {
if (!size->GetCompilerType().IsInteger())
return llvm::createStringError(
"size data member must be a built-in integer type");
return size->GetValueAsUnsigned(0);
}
llvm::Expected<uint32_t> lldb_private::formatters::
LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren() {
if (!m_start || !m_finish)
return llvm::createStringError(
"failed to determine start/end of vector data");
switch (m_layout) {
case VectorLayout::Pointer:
return CalculateNumChildrenUsingPointerArithmetic(m_start, m_finish,
m_element_size);
case VectorLayout::Size:
return GetNumChildren(m_finish);
}
assert(false && "invalid vector layout");
return llvm::createStringError("invalid vector layout");
}
lldb::ValueObjectSP
lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex(
uint32_t idx) {
if (!m_start || !m_finish)
return lldb::ValueObjectSP();
uint64_t offset = idx * m_element_size;
offset = offset + m_start->GetValueAsUnsigned(0);
StreamString name;
name.Printf("[%" PRIu64 "]", (uint64_t)idx);
return CreateChildValueObjectFromAddress(name.GetString(), offset,
m_backend.GetExecutionContextRef(),
m_element_type);
}
lldb::ChildCacheState
lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() {
m_start = m_finish = nullptr;
// Determine if this version of libc++'s `std::vector` uses `__vector_layout`.
ValueObjectSP layout_sp = m_backend.GetChildMemberWithName("__layout_");
ValueObject *target = layout_sp ? layout_sp.get() : &m_backend;
ValueObjectSP begin_sp = target->GetChildMemberWithName("__begin_");
if (!begin_sp)
return lldb::ChildCacheState::eRefetch;
m_element_type = begin_sp->GetCompilerType().GetPointeeType();
llvm::Expected<uint64_t> size_or_err = m_element_type.GetByteSize(nullptr);
if (!size_or_err) {
LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(),
"{0}");
return lldb::ChildCacheState::eRefetch;
}
m_element_size = *size_or_err;
if (m_element_size == 0) {
return lldb::ChildCacheState::eRefetch;
}
// store raw pointers or end up with a circular dependency
m_start = begin_sp.get();
if (ValueObjectSP end_sp = target->GetChildMemberWithName("__end_")) {
m_finish = end_sp.get();
m_layout = VectorLayout::Pointer;
return lldb::ChildCacheState::eRefetch;
}
ValueObjectSP size_sp = target->GetChildMemberWithName("__size_");
if (!size_sp)
return lldb::ChildCacheState::eRefetch;
m_finish = size_sp.get();
m_layout = VectorLayout::Size;
return lldb::ChildCacheState::eRefetch;
}
llvm::Expected<size_t>
lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name) {
if (!m_start || !m_finish)
return llvm::createStringErrorV("type has no child named '{0}'", name);
auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
if (!optional_idx) {
return llvm::createStringErrorV("type has no child named '{0}'", name);
}
return *optional_idx;
}
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::
LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp), m_bool_type(), m_exe_ctx_ref(),
m_children() {
if (valobj_sp) {
Update();
m_bool_type =
valobj_sp->GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeBool);
}
}
llvm::Expected<uint32_t> lldb_private::formatters::
LibcxxVectorBoolSyntheticFrontEnd::CalculateNumChildren() {
return m_count;
}
lldb::ValueObjectSP
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex(
uint32_t idx) {
auto iter = m_children.find(idx), end = m_children.end();
if (iter != end)
return iter->second;
if (idx >= m_count)
return {};
if (m_base_data_address == 0 || m_count == 0)
return {};
if (!m_bool_type)
return {};
size_t byte_idx = (idx >> 3); // divide by 8 to get byte index
size_t bit_index = (idx & 7); // efficient idx % 8 for bit index
lldb::addr_t byte_location = m_base_data_address + byte_idx;
ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
if (!process_sp)
return {};
uint8_t byte = 0;
uint8_t mask = 0;
Status err;
size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err);
if (err.Fail() || bytes_read == 0)
return {};
mask = 1 << bit_index;
bool bit_set = ((byte & mask) != 0);
std::optional<uint64_t> size =
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 = CreateChildValueObjectFromData(
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::LibcxxVectorBoolSyntheticFrontEnd::Update() {
m_children.clear();
ValueObjectSP valobj_sp = m_backend.GetSP();
if (!valobj_sp)
return lldb::ChildCacheState::eRefetch;
m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
ValueObjectSP size_sp(valobj_sp->GetChildMemberWithName("__size_"));
if (!size_sp)
return lldb::ChildCacheState::eRefetch;
m_count = size_sp->GetValueAsUnsigned(0);
if (!m_count)
return lldb::ChildCacheState::eReuse;
ValueObjectSP begin_sp(valobj_sp->GetChildMemberWithName("__begin_"));
if (!begin_sp) {
m_count = 0;
return lldb::ChildCacheState::eRefetch;
}
m_base_data_address = begin_sp->GetValueAsUnsigned(0);
if (!m_base_data_address) {
m_count = 0;
return lldb::ChildCacheState::eRefetch;
}
return lldb::ChildCacheState::eRefetch;
}
llvm::Expected<size_t>
lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name) {
if (!m_count || !m_base_data_address)
return llvm::createStringErrorV("type has no child named '{0}'", name);
auto optional_idx = ExtractIndexFromString(name.AsCString(nullptr));
if (!optional_idx) {
return llvm::createStringErrorV("type has no child named '{0}'", name);
}
uint32_t idx = *optional_idx;
if (idx >= CalculateNumChildrenIgnoringErrors())
return llvm::createStringErrorV("type has no child named '{0}'", name);
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);
}