blob: ebaf60a16b069bbbeeeddf0d973ace06a51a5bb3 [file] [log] [blame]
//===-- LibCxxMap.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 "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "lldb/DataFormatters/FormattersHelpers.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 "lldb/ValueObject/ValueObject.h"
#include "lldb/ValueObject/ValueObjectConstResult.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include <cstdint>
#include <locale>
#include <optional>
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::formatters;
// The flattened layout of the std::__tree_iterator::__ptr_ looks
// as follows:
//
// The following shows the contiguous block of memory:
//
// +-----------------------------+ class __tree_end_node
// __ptr_ | pointer __left_; |
// +-----------------------------+ class __tree_node_base
// | pointer __right_; |
// | __parent_pointer __parent_; |
// | bool __is_black_; |
// +-----------------------------+ class __tree_node
// | __node_value_type __value_; | <<< our key/value pair
// +-----------------------------+
//
// where __ptr_ has type __iter_pointer.
class MapEntry {
public:
MapEntry() = default;
explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
explicit MapEntry(ValueObject *entry)
: m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
ValueObjectSP left() const {
if (!m_entry_sp)
return m_entry_sp;
return m_entry_sp->GetSyntheticChildAtOffset(
0, m_entry_sp->GetCompilerType(), true);
}
ValueObjectSP right() const {
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 {
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(ValueObject *entry, size_t depth = 0)
: m_entry(entry), m_max_depth(depth), m_error(false) {}
MapIterator() = default;
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();
}
private:
/// Mimicks libc++'s __tree_next algorithm, which libc++ uses
/// in its __tree_iteartor::operator++.
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());
}
/// Mimicks libc++'s __tree_min algorithm.
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 = 0;
bool m_error = false;
};
namespace lldb_private {
namespace formatters {
class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
~LibcxxStdMapSyntheticFrontEnd() override = default;
llvm::Expected<uint32_t> CalculateNumChildren() override;
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
lldb::ChildCacheState Update() override;
bool MightHaveChildren() override;
size_t GetIndexOfChildWithName(ConstString name) override;
private:
llvm::Expected<uint32_t> CalculateNumChildrenForOldCompressedPairLayout();
/// Returns the ValueObject for the __tree_node type that
/// holds the key/value pair of the node at index \ref idx.
///
/// \param[in] idx The child index that we're looking to get
/// the key/value pair for.
///
/// \param[in] max_depth The maximum search depth after which
/// we stop trying to find the key/value
/// pair for.
///
/// \returns On success, returns the ValueObjectSP corresponding
/// to the __tree_node's __value_ member (which holds
/// the key/value pair the formatter wants to display).
/// On failure, will return nullptr.
ValueObjectSP GetKeyValuePair(size_t idx, size_t max_depth);
ValueObject *m_tree = nullptr;
ValueObject *m_root_node = nullptr;
CompilerType m_node_ptr_type;
size_t m_count = UINT32_MAX;
std::map<size_t, MapIterator> m_iterators;
};
class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
llvm::Expected<uint32_t> CalculateNumChildren() override;
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
lldb::ChildCacheState Update() override;
bool MightHaveChildren() override;
size_t GetIndexOfChildWithName(ConstString name) override;
~LibCxxMapIteratorSyntheticFrontEnd() override = default;
private:
ValueObjectSP m_pair_sp = nullptr;
};
} // namespace formatters
} // namespace lldb_private
lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp) {
if (valobj_sp)
Update();
}
llvm::Expected<uint32_t>
lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
CalculateNumChildrenForOldCompressedPairLayout() {
ValueObjectSP node_sp(m_tree->GetChildMemberWithName("__pair3_"));
if (!node_sp)
return 0;
if (!isOldCompressedPairLayout(*node_sp))
return llvm::createStringError("Unexpected std::map layout: expected "
"old __compressed_pair layout.");
node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp);
if (!node_sp)
return 0;
m_count = node_sp->GetValueAsUnsigned(0);
return m_count;
}
llvm::Expected<uint32_t> lldb_private::formatters::
LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren() {
if (m_count != UINT32_MAX)
return m_count;
if (m_tree == nullptr)
return 0;
if (auto node_sp = m_tree->GetChildMemberWithName("__size_")) {
m_count = node_sp->GetValueAsUnsigned(0);
return m_count;
}
return CalculateNumChildrenForOldCompressedPairLayout();
}
ValueObjectSP
lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetKeyValuePair(
size_t idx, size_t max_depth) {
MapIterator iterator(m_root_node, max_depth);
size_t advance_by = idx;
if (idx > 0) {
// If we have already created the iterator for the previous
// index, we can start from there and advance by 1.
auto cached_iterator = m_iterators.find(idx - 1);
if (cached_iterator != m_iterators.end()) {
iterator = cached_iterator->second;
advance_by = 1;
}
}
ValueObjectSP iterated_sp(iterator.advance(advance_by));
if (!iterated_sp)
// this tree is garbage - stop
return nullptr;
if (!m_node_ptr_type.IsValid())
return nullptr;
// iterated_sp is a __iter_pointer at this point.
// We can cast it to a __node_pointer (which is what libc++ does).
auto value_type_sp = iterated_sp->Cast(m_node_ptr_type);
if (!value_type_sp)
return nullptr;
// Finally, get the key/value pair.
value_type_sp = value_type_sp->GetChildMemberWithName("__value_");
if (!value_type_sp)
return nullptr;
m_iterators[idx] = iterator;
return value_type_sp;
}
lldb::ValueObjectSP
lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex(
uint32_t idx) {
static ConstString g_cc_("__cc_"), g_cc("__cc");
static ConstString g_nc("__nc");
uint32_t num_children = CalculateNumChildrenIgnoringErrors();
if (idx >= num_children)
return nullptr;
if (m_tree == nullptr || m_root_node == nullptr)
return nullptr;
ValueObjectSP key_val_sp = GetKeyValuePair(idx, /*max_depth=*/num_children);
if (!key_val_sp) {
// this will stop all future searches until an Update() happens
m_tree = nullptr;
return nullptr;
}
// 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_
StreamString name;
name.Printf("[%" PRIu64 "]", (uint64_t)idx);
auto potential_child_sp = key_val_sp->Clone(ConstString(name.GetString()));
if (potential_child_sp) {
switch (potential_child_sp->GetNumChildrenIgnoringErrors()) {
case 1: {
auto child0_sp = potential_child_sp->GetChildAtIndex(0);
if (child0_sp &&
(child0_sp->GetName() == g_cc_ || 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);
auto child1_sp = potential_child_sp->GetChildAtIndex(1);
if (child0_sp &&
(child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc) &&
child1_sp && child1_sp->GetName() == g_nc)
potential_child_sp = child0_sp->Clone(ConstString(name.GetString()));
break;
}
}
}
return potential_child_sp;
}
lldb::ChildCacheState
lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {
m_count = UINT32_MAX;
m_tree = m_root_node = nullptr;
m_iterators.clear();
m_tree = m_backend.GetChildMemberWithName("__tree_").get();
if (!m_tree)
return lldb::ChildCacheState::eRefetch;
m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get();
m_node_ptr_type =
m_tree->GetCompilerType().GetDirectNestedTypeWithName("__node_pointer");
return lldb::ChildCacheState::eRefetch;
}
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);
}
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp) {
if (valobj_sp)
Update();
}
lldb::ChildCacheState
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
m_pair_sp.reset();
ValueObjectSP valobj_sp = m_backend.GetSP();
if (!valobj_sp)
return lldb::ChildCacheState::eRefetch;
TargetSP target_sp(valobj_sp->GetTargetSP());
if (!target_sp)
return lldb::ChildCacheState::eRefetch;
// m_backend is a std::map::iterator
// ...which is a __map_iterator<__tree_iterator<..., __node_pointer, ...>>
//
// Then, __map_iterator::__i_ is a __tree_iterator
auto tree_iter_sp = valobj_sp->GetChildMemberWithName("__i_");
if (!tree_iter_sp)
return lldb::ChildCacheState::eRefetch;
// Type is __tree_iterator::__node_pointer
// (We could alternatively also get this from the template argument)
auto node_pointer_type =
tree_iter_sp->GetCompilerType().GetDirectNestedTypeWithName(
"__node_pointer");
if (!node_pointer_type.IsValid())
return lldb::ChildCacheState::eRefetch;
// __ptr_ is a __tree_iterator::__iter_pointer
auto iter_pointer_sp = tree_iter_sp->GetChildMemberWithName("__ptr_");
if (!iter_pointer_sp)
return lldb::ChildCacheState::eRefetch;
// Cast the __iter_pointer to a __node_pointer (which stores our key/value
// pair)
auto node_pointer_sp = iter_pointer_sp->Cast(node_pointer_type);
if (!node_pointer_sp)
return lldb::ChildCacheState::eRefetch;
auto key_value_sp = node_pointer_sp->GetChildMemberWithName("__value_");
if (!key_value_sp)
return lldb::ChildCacheState::eRefetch;
// Create the synthetic child, which is a pair where the key and value can be
// retrieved by querying the synthetic frontend for
// GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second")
// respectively.
//
// std::map stores the actual key/value pair in value_type::__cc_ (or
// previously __cc).
key_value_sp = key_value_sp->Clone(ConstString("pair"));
if (key_value_sp->GetNumChildrenIgnoringErrors() == 1) {
auto child0_sp = key_value_sp->GetChildAtIndex(0);
if (child0_sp &&
(child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc"))
key_value_sp = child0_sp->Clone(ConstString("pair"));
}
m_pair_sp = key_value_sp;
return lldb::ChildCacheState::eRefetch;
}
llvm::Expected<uint32_t> lldb_private::formatters::
LibCxxMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
return 2;
}
lldb::ValueObjectSP
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
uint32_t idx) {
if (!m_pair_sp)
return nullptr;
return m_pair_sp->GetChildAtIndex(idx);
}
bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
MightHaveChildren() {
return true;
}
size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name) {
if (!m_pair_sp)
return UINT32_MAX;
return m_pair_sp->GetIndexOfChildWithName(name);
}
SyntheticChildrenFrontEnd *
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
: nullptr);
}