413 lines · cpp
1//===-- LibStdcpp.cpp -----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "LibStdcpp.h"10#include "LibCxx.h"11 12#include "Plugins/Language/CPlusPlus/Generic.h"13#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"14#include "lldb/DataFormatters/FormattersHelpers.h"15#include "lldb/DataFormatters/StringPrinter.h"16#include "lldb/DataFormatters/VectorIterator.h"17#include "lldb/Target/Target.h"18#include "lldb/Utility/DataBufferHeap.h"19#include "lldb/Utility/Endian.h"20#include "lldb/Utility/Status.h"21#include "lldb/Utility/Stream.h"22#include "lldb/ValueObject/ValueObject.h"23#include "lldb/ValueObject/ValueObjectConstResult.h"24#include <optional>25 26using namespace lldb;27using namespace lldb_private;28using namespace lldb_private::formatters;29 30namespace {31 32class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {33 /*34 (std::_Rb_tree_iterator<std::pair<const int, std::basic_string<char,35 std::char_traits<char>, std::allocator<char> > > >) ibeg = {36 (_Base_ptr) _M_node = 0x0000000100103910 {37 (std::_Rb_tree_color) _M_color = _S_black38 (std::_Rb_tree_node_base::_Base_ptr) _M_parent = 0x00000001001038c039 (std::_Rb_tree_node_base::_Base_ptr) _M_left = 0x000000000000000040 (std::_Rb_tree_node_base::_Base_ptr) _M_right = 0x000000000000000041 }42 }43 */44 45public:46 explicit LibstdcppMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);47 48 llvm::Expected<uint32_t> CalculateNumChildren() override;49 50 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;51 52 lldb::ChildCacheState Update() override;53 54 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;55 56private:57 ExecutionContextRef m_exe_ctx_ref;58 lldb::addr_t m_pair_address = 0;59 CompilerType m_pair_type;60 lldb::ValueObjectSP m_pair_sp;61};62 63class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {64public:65 explicit LibStdcppSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);66 67 llvm::Expected<uint32_t> CalculateNumChildren() override;68 69 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;70 71 lldb::ChildCacheState Update() override;72 73 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;74 75private:76 77 // The lifetime of a ValueObject and all its derivative ValueObjects78 // (children, clones, etc.) is managed by a ClusterManager. These79 // objects are only destroyed when every shared pointer to any of them80 // is destroyed, so we must not store a shared pointer to any ValueObject81 // derived from our backend ValueObject (since we're in the same cluster).82 ValueObject *m_ptr_obj = nullptr; // Underlying pointer (held, not owned)83};84 85} // end of anonymous namespace86 87LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd(88 lldb::ValueObjectSP valobj_sp)89 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type(),90 m_pair_sp() {91 if (valobj_sp)92 Update();93}94 95lldb::ChildCacheState LibstdcppMapIteratorSyntheticFrontEnd::Update() {96 ValueObjectSP valobj_sp = m_backend.GetSP();97 if (!valobj_sp)98 return lldb::ChildCacheState::eRefetch;99 100 TargetSP target_sp(valobj_sp->GetTargetSP());101 102 if (!target_sp)103 return lldb::ChildCacheState::eRefetch;104 105 bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8);106 107 if (!valobj_sp)108 return lldb::ChildCacheState::eRefetch;109 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();110 111 ValueObjectSP _M_node_sp(valobj_sp->GetChildMemberWithName("_M_node"));112 if (!_M_node_sp)113 return lldb::ChildCacheState::eRefetch;114 115 m_pair_address = _M_node_sp->GetValueAsUnsigned(0);116 if (m_pair_address == 0)117 return lldb::ChildCacheState::eRefetch;118 119 m_pair_address += (is_64bit ? 32 : 16);120 121 CompilerType my_type(valobj_sp->GetCompilerType());122 if (my_type.GetNumTemplateArguments() >= 1) {123 CompilerType pair_type = my_type.GetTypeTemplateArgument(0);124 if (!pair_type)125 return lldb::ChildCacheState::eRefetch;126 m_pair_type = pair_type;127 } else128 return lldb::ChildCacheState::eRefetch;129 130 return lldb::ChildCacheState::eReuse;131}132 133llvm::Expected<uint32_t>134LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() {135 return 2;136}137 138lldb::ValueObjectSP139LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {140 if (m_pair_address != 0 && m_pair_type) {141 if (!m_pair_sp)142 m_pair_sp = CreateValueObjectFromAddress("pair", m_pair_address,143 m_exe_ctx_ref, m_pair_type);144 if (m_pair_sp)145 return m_pair_sp->GetChildAtIndex(idx);146 }147 return lldb::ValueObjectSP();148}149 150llvm::Expected<size_t>151LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName(152 ConstString name) {153 if (name == "first")154 return 0;155 if (name == "second")156 return 1;157 return llvm::createStringError("Type has no child named '%s'",158 name.AsCString());159}160 161SyntheticChildrenFrontEnd *162lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator(163 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {164 return (valobj_sp ? new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp)165 : nullptr);166}167 168/*169 (lldb) fr var ibeg --ptr-depth 1170 (__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >)171 ibeg = {172 _M_current = 0x00000001001037a0 {173 *_M_current = 1174 }175 }176 */177 178SyntheticChildrenFrontEnd *179lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator(180 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {181 return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(182 valobj_sp, {ConstString("_M_current")})183 : nullptr);184}185 186lldb_private::formatters::VectorIteratorSyntheticFrontEnd::187 VectorIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp,188 llvm::ArrayRef<ConstString> item_names)189 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(),190 m_item_names(item_names), m_item_sp() {191 if (valobj_sp)192 Update();193}194 195lldb::ChildCacheState VectorIteratorSyntheticFrontEnd::Update() {196 m_item_sp.reset();197 198 ValueObjectSP valobj_sp = m_backend.GetSP();199 if (!valobj_sp)200 return lldb::ChildCacheState::eRefetch;201 202 ValueObjectSP item_ptr =203 formatters::GetChildMemberWithName(*valobj_sp, m_item_names);204 if (!item_ptr)205 return lldb::ChildCacheState::eRefetch;206 if (item_ptr->GetValueAsUnsigned(0) == 0)207 return lldb::ChildCacheState::eRefetch;208 Status err;209 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();210 m_item_sp = CreateValueObjectFromAddress(211 "item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref,212 item_ptr->GetCompilerType().GetPointeeType());213 if (err.Fail())214 m_item_sp.reset();215 return lldb::ChildCacheState::eRefetch;216}217 218llvm::Expected<uint32_t>219VectorIteratorSyntheticFrontEnd::CalculateNumChildren() {220 return 1;221}222 223lldb::ValueObjectSP224VectorIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {225 if (idx == 0)226 return m_item_sp;227 return lldb::ValueObjectSP();228}229 230llvm::Expected<size_t>231VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {232 if (name == "item")233 return 0;234 return llvm::createStringError("Type has no child named '%s'",235 name.AsCString());236}237 238bool lldb_private::formatters::LibStdcppStringSummaryProvider(239 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {240 ValueObjectSP ptr = valobj.GetChildAtNamePath({"_M_dataplus", "_M_p"});241 if (!ptr || !ptr->GetError().Success())242 stream << "Summary Unavailable";243 else244 stream << ptr->GetSummaryAsCString();245 246 return true;247}248 249LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(250 lldb::ValueObjectSP valobj_sp)251 : SyntheticChildrenFrontEnd(*valobj_sp) {252 if (valobj_sp)253 Update();254}255 256llvm::Expected<uint32_t>257LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() {258 return 1;259}260 261lldb::ValueObjectSP262LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {263 if (!m_ptr_obj)264 return nullptr;265 266 if (idx == 0)267 return m_ptr_obj->GetSP();268 269 if (idx == 1) {270 ValueObjectSP valobj_sp = m_backend.GetSP();271 if (!valobj_sp)272 return nullptr;273 274 Status status;275 ValueObjectSP value_sp = m_ptr_obj->Dereference(status);276 if (status.Success())277 return value_sp;278 }279 return lldb::ValueObjectSP();280}281 282lldb::ChildCacheState LibStdcppSharedPtrSyntheticFrontEnd::Update() {283 auto backend = m_backend.GetSP();284 if (!backend)285 return lldb::ChildCacheState::eRefetch;286 287 auto valobj_sp = backend->GetNonSyntheticValue();288 if (!valobj_sp)289 return lldb::ChildCacheState::eRefetch;290 291 auto ptr_obj_sp = valobj_sp->GetChildMemberWithName("_M_ptr");292 if (!ptr_obj_sp)293 return lldb::ChildCacheState::eRefetch;294 295 auto cast_ptr_sp = GetDesugaredSmartPointerValue(*ptr_obj_sp, *valobj_sp);296 if (!cast_ptr_sp)297 return lldb::ChildCacheState::eRefetch;298 299 m_ptr_obj = cast_ptr_sp->Clone(ConstString("pointer")).get();300 301 return lldb::ChildCacheState::eRefetch;302}303 304llvm::Expected<size_t>305LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {306 if (name == "pointer")307 return 0;308 309 if (name == "object" || name == "$$dereference$$")310 return 1;311 312 return llvm::createStringError("Type has no child named '%s'",313 name.AsCString());314}315 316SyntheticChildrenFrontEnd *317lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(318 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {319 return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp)320 : nullptr);321}322 323bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider(324 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {325 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());326 if (!valobj_sp)327 return false;328 329 ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("_M_ptr"));330 if (!ptr_sp)331 return false;332 333 DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);334 335 ValueObjectSP pi_sp = valobj_sp->GetChildAtNamePath({"_M_refcount", "_M_pi"});336 if (!pi_sp)337 return false;338 339 bool success;340 uint64_t pi_addr = pi_sp->GetValueAsUnsigned(0, &success);341 // Empty control field. We're done.342 if (!success || pi_addr == 0)343 return true;344 345 int64_t shared_count = 0;346 if (auto count_sp = pi_sp->GetChildMemberWithName("_M_use_count")) {347 bool success;348 shared_count = count_sp->GetValueAsSigned(0, &success);349 if (!success)350 return false;351 352 stream.Printf(" strong=%" PRId64, shared_count);353 }354 355 // _M_weak_count is the number of weak references + (_M_use_count != 0).356 if (auto weak_count_sp = pi_sp->GetChildMemberWithName("_M_weak_count")) {357 bool success;358 int64_t count = weak_count_sp->GetValueAsUnsigned(0, &success);359 if (!success)360 return false;361 362 stream.Printf(" weak=%" PRId64, count - (shared_count != 0));363 }364 365 return true;366}367 368static uint64_t LibStdcppVariantNposValue(size_t index_byte_size) {369 switch (index_byte_size) {370 case 1:371 return 0xff;372 case 2:373 return 0xffff;374 default:375 return 0xffff'ffff;376 }377}378 379bool formatters::LibStdcppVariantSummaryProvider(380 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {381 ValueObjectSP valobj_sp = valobj.GetNonSyntheticValue();382 if (!valobj_sp)383 return false;384 385 ValueObjectSP index_obj = valobj_sp->GetChildMemberWithName("_M_index");386 ValueObjectSP data_obj = valobj_sp->GetChildMemberWithName("_M_u");387 if (!index_obj || !data_obj)388 return false;389 390 auto index_bytes = index_obj->GetByteSize();391 if (!index_bytes)392 return false;393 auto npos_value = LibStdcppVariantNposValue(*index_bytes);394 auto index = index_obj->GetValueAsUnsigned(0);395 if (index == npos_value) {396 stream.Printf(" No Value");397 return true;398 }399 400 auto variant_type =401 valobj_sp->GetCompilerType().GetCanonicalType().GetNonReferenceType();402 if (!variant_type)403 return false;404 if (index >= variant_type.GetNumTemplateArguments(true)) {405 stream.Printf(" <Invalid>");406 return true;407 }408 409 auto active_type = variant_type.GetTypeTemplateArgument(index, true);410 stream << " Active Type = " << active_type.GetDisplayTypeName() << " ";411 return true;412}413