1 //===-- LibCxx.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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "LibCxx.h" 10 11 #include "lldb/Core/Debugger.h" 12 #include "lldb/Core/FormatEntity.h" 13 #include "lldb/Core/ValueObject.h" 14 #include "lldb/Core/ValueObjectConstResult.h" 15 #include "lldb/DataFormatters/FormattersHelpers.h" 16 #include "lldb/DataFormatters/StringPrinter.h" 17 #include "lldb/DataFormatters/TypeSummary.h" 18 #include "lldb/DataFormatters/VectorIterator.h" 19 #include "lldb/Target/ProcessStructReader.h" 20 #include "lldb/Target/SectionLoadList.h" 21 #include "lldb/Target/Target.h" 22 #include "lldb/Utility/DataBufferHeap.h" 23 #include "lldb/Utility/Endian.h" 24 #include "lldb/Utility/Status.h" 25 #include "lldb/Utility/Stream.h" 26 27 #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" 28 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 29 30 using namespace lldb; 31 using namespace lldb_private; 32 using namespace lldb_private::formatters; 33 34 bool lldb_private::formatters::LibcxxOptionalSummaryProvider( 35 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 36 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); 37 if (!valobj_sp) 38 return false; 39 40 // An optional either contains a value or not, the member __engaged_ is 41 // a bool flag, it is true if the optional has a value and false otherwise. 42 ValueObjectSP engaged_sp( 43 valobj_sp->GetChildMemberWithName(ConstString("__engaged_"), true)); 44 45 if (!engaged_sp) 46 return false; 47 48 llvm::StringRef engaged_as_cstring( 49 engaged_sp->GetValueAsUnsigned(0) == 1 ? "true" : "false"); 50 51 stream.Printf(" Has Value=%s ", engaged_as_cstring.data()); 52 53 return true; 54 } 55 56 bool lldb_private::formatters::LibcxxFunctionSummaryProvider( 57 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 58 59 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); 60 61 if (!valobj_sp) 62 return false; 63 64 ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); 65 Process *process = exe_ctx.GetProcessPtr(); 66 67 if (process == nullptr) 68 return false; 69 70 CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(*process); 71 72 if (!cpp_runtime) 73 return false; 74 75 CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info = 76 cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp); 77 78 switch (callable_info.callable_case) { 79 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid: 80 stream.Printf(" __f_ = %" PRIu64, callable_info.member__f_pointer_value); 81 return false; 82 break; 83 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda: 84 stream.Printf( 85 " Lambda in File %s at Line %u", 86 callable_info.callable_line_entry.file.GetFilename().GetCString(), 87 callable_info.callable_line_entry.line); 88 break; 89 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject: 90 stream.Printf( 91 " Function in File %s at Line %u", 92 callable_info.callable_line_entry.file.GetFilename().GetCString(), 93 callable_info.callable_line_entry.line); 94 break; 95 case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction: 96 stream.Printf(" Function = %s ", 97 callable_info.callable_symbol.GetName().GetCString()); 98 break; 99 } 100 101 return true; 102 } 103 104 bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider( 105 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 106 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); 107 if (!valobj_sp) 108 return false; 109 ValueObjectSP ptr_sp( 110 valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true)); 111 ValueObjectSP count_sp(valobj_sp->GetChildAtNamePath( 112 {ConstString("__cntrl_"), ConstString("__shared_owners_")})); 113 ValueObjectSP weakcount_sp(valobj_sp->GetChildAtNamePath( 114 {ConstString("__cntrl_"), ConstString("__shared_weak_owners_")})); 115 116 if (!ptr_sp) 117 return false; 118 119 if (ptr_sp->GetValueAsUnsigned(0) == 0) { 120 stream.Printf("nullptr"); 121 return true; 122 } else { 123 bool print_pointee = false; 124 Status error; 125 ValueObjectSP pointee_sp = ptr_sp->Dereference(error); 126 if (pointee_sp && error.Success()) { 127 if (pointee_sp->DumpPrintableRepresentation( 128 stream, ValueObject::eValueObjectRepresentationStyleSummary, 129 lldb::eFormatInvalid, 130 ValueObject::PrintableRepresentationSpecialCases::eDisable, 131 false)) 132 print_pointee = true; 133 } 134 if (!print_pointee) 135 stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0)); 136 } 137 138 if (count_sp) 139 stream.Printf(" strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(0)); 140 141 if (weakcount_sp) 142 stream.Printf(" weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(0)); 143 144 return true; 145 } 146 147 bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider( 148 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 149 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); 150 if (!valobj_sp) 151 return false; 152 153 ValueObjectSP ptr_sp( 154 valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true)); 155 if (!ptr_sp) 156 return false; 157 158 ptr_sp = GetValueOfLibCXXCompressedPair(*ptr_sp); 159 if (!ptr_sp) 160 return false; 161 162 if (ptr_sp->GetValueAsUnsigned(0) == 0) { 163 stream.Printf("nullptr"); 164 return true; 165 } else { 166 bool print_pointee = false; 167 Status error; 168 ValueObjectSP pointee_sp = ptr_sp->Dereference(error); 169 if (pointee_sp && error.Success()) { 170 if (pointee_sp->DumpPrintableRepresentation( 171 stream, ValueObject::eValueObjectRepresentationStyleSummary, 172 lldb::eFormatInvalid, 173 ValueObject::PrintableRepresentationSpecialCases::eDisable, 174 false)) 175 print_pointee = true; 176 } 177 if (!print_pointee) 178 stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0)); 179 } 180 181 return true; 182 } 183 184 /* 185 (lldb) fr var ibeg --raw --ptr-depth 1 186 (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int, 187 std::__1::basic_string<char, std::__1::char_traits<char>, 188 std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int, 189 std::__1::basic_string<char, std::__1::char_traits<char>, 190 std::__1::allocator<char> > >, void *> *, long> >) ibeg = { 191 __i_ = { 192 __ptr_ = 0x0000000100103870 { 193 std::__1::__tree_node_base<void *> = { 194 std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = { 195 __left_ = 0x0000000000000000 196 } 197 __right_ = 0x0000000000000000 198 __parent_ = 0x00000001001038b0 199 __is_black_ = true 200 } 201 __value_ = { 202 first = 0 203 second = { std::string } 204 */ 205 206 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: 207 LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 208 : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() { 209 if (valobj_sp) 210 Update(); 211 } 212 213 bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() { 214 m_pair_sp.reset(); 215 m_pair_ptr = nullptr; 216 217 ValueObjectSP valobj_sp = m_backend.GetSP(); 218 if (!valobj_sp) 219 return false; 220 221 TargetSP target_sp(valobj_sp->GetTargetSP()); 222 223 if (!target_sp) 224 return false; 225 226 if (!valobj_sp) 227 return false; 228 229 static ConstString g___i_("__i_"); 230 231 // this must be a ValueObject* because it is a child of the ValueObject we 232 // are producing children for it if were a ValueObjectSP, we would end up 233 // with a loop (iterator -> synthetic -> child -> parent == iterator) and 234 // that would in turn leak memory by never allowing the ValueObjects to die 235 // and free their memory 236 m_pair_ptr = valobj_sp 237 ->GetValueForExpressionPath( 238 ".__i_.__ptr_->__value_", nullptr, nullptr, 239 ValueObject::GetValueForExpressionPathOptions() 240 .DontCheckDotVsArrowSyntax() 241 .SetSyntheticChildrenTraversal( 242 ValueObject::GetValueForExpressionPathOptions:: 243 SyntheticChildrenTraversal::None), 244 nullptr) 245 .get(); 246 247 if (!m_pair_ptr) { 248 m_pair_ptr = valobj_sp 249 ->GetValueForExpressionPath( 250 ".__i_.__ptr_", nullptr, nullptr, 251 ValueObject::GetValueForExpressionPathOptions() 252 .DontCheckDotVsArrowSyntax() 253 .SetSyntheticChildrenTraversal( 254 ValueObject::GetValueForExpressionPathOptions:: 255 SyntheticChildrenTraversal::None), 256 nullptr) 257 .get(); 258 if (m_pair_ptr) { 259 auto __i_(valobj_sp->GetChildMemberWithName(g___i_, true)); 260 if (!__i_) { 261 m_pair_ptr = nullptr; 262 return false; 263 } 264 CompilerType pair_type( 265 __i_->GetCompilerType().GetTypeTemplateArgument(0)); 266 std::string name; 267 uint64_t bit_offset_ptr; 268 uint32_t bitfield_bit_size_ptr; 269 bool is_bitfield_ptr; 270 pair_type = pair_type.GetFieldAtIndex( 271 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr); 272 if (!pair_type) { 273 m_pair_ptr = nullptr; 274 return false; 275 } 276 277 auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS)); 278 m_pair_ptr = nullptr; 279 if (addr && addr != LLDB_INVALID_ADDRESS) { 280 TypeSystemClang *ast_ctx = 281 llvm::dyn_cast_or_null<TypeSystemClang>(pair_type.GetTypeSystem()); 282 if (!ast_ctx) 283 return false; 284 CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( 285 ConstString(), 286 {{"ptr0", 287 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, 288 {"ptr1", 289 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, 290 {"ptr2", 291 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, 292 {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)}, 293 {"payload", pair_type}}); 294 llvm::Optional<uint64_t> size = tree_node_type.GetByteSize(nullptr); 295 if (!size) 296 return false; 297 DataBufferSP buffer_sp(new DataBufferHeap(*size, 0)); 298 ProcessSP process_sp(target_sp->GetProcessSP()); 299 Status error; 300 process_sp->ReadMemory(addr, buffer_sp->GetBytes(), 301 buffer_sp->GetByteSize(), error); 302 if (error.Fail()) 303 return false; 304 DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(), 305 process_sp->GetAddressByteSize()); 306 auto pair_sp = CreateValueObjectFromData( 307 "pair", extractor, valobj_sp->GetExecutionContextRef(), 308 tree_node_type); 309 if (pair_sp) 310 m_pair_sp = pair_sp->GetChildAtIndex(4, true); 311 } 312 } 313 } 314 315 return false; 316 } 317 318 size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: 319 CalculateNumChildren() { 320 return 2; 321 } 322 323 lldb::ValueObjectSP 324 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex( 325 size_t idx) { 326 if (m_pair_ptr) 327 return m_pair_ptr->GetChildAtIndex(idx, true); 328 if (m_pair_sp) 329 return m_pair_sp->GetChildAtIndex(idx, true); 330 return lldb::ValueObjectSP(); 331 } 332 333 bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: 334 MightHaveChildren() { 335 return true; 336 } 337 338 size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: 339 GetIndexOfChildWithName(ConstString name) { 340 if (name == "first") 341 return 0; 342 if (name == "second") 343 return 1; 344 return UINT32_MAX; 345 } 346 347 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: 348 ~LibCxxMapIteratorSyntheticFrontEnd() { 349 // this will be deleted when its parent dies (since it's a child object) 350 // delete m_pair_ptr; 351 } 352 353 SyntheticChildrenFrontEnd * 354 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator( 355 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 356 return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp) 357 : nullptr); 358 } 359 360 /* 361 (lldb) fr var ibeg --raw --ptr-depth 1 -T 362 (std::__1::__wrap_iter<int *>) ibeg = { 363 (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 { 364 (int) *__i = 1 365 } 366 } 367 */ 368 369 SyntheticChildrenFrontEnd * 370 lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator( 371 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 372 static ConstString g_item_name; 373 if (!g_item_name) 374 g_item_name.SetCString("__i"); 375 return (valobj_sp 376 ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name) 377 : nullptr); 378 } 379 380 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 381 LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 382 : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr) { 383 if (valobj_sp) 384 Update(); 385 } 386 387 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 388 CalculateNumChildren() { 389 return (m_cntrl ? 1 : 0); 390 } 391 392 lldb::ValueObjectSP 393 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex( 394 size_t idx) { 395 if (!m_cntrl) 396 return lldb::ValueObjectSP(); 397 398 ValueObjectSP valobj_sp = m_backend.GetSP(); 399 if (!valobj_sp) 400 return lldb::ValueObjectSP(); 401 402 if (idx == 0) 403 return valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true); 404 405 if (idx == 1) { 406 if (auto ptr_sp = 407 valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true)) { 408 Status status; 409 auto value_sp = ptr_sp->Dereference(status); 410 if (status.Success()) { 411 auto value_type_sp = 412 valobj_sp->GetCompilerType().GetTypeTemplateArgument(0); 413 return value_sp->Cast(value_type_sp); 414 } 415 } 416 } 417 418 return lldb::ValueObjectSP(); 419 } 420 421 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() { 422 m_cntrl = nullptr; 423 424 ValueObjectSP valobj_sp = m_backend.GetSP(); 425 if (!valobj_sp) 426 return false; 427 428 TargetSP target_sp(valobj_sp->GetTargetSP()); 429 if (!target_sp) 430 return false; 431 432 lldb::ValueObjectSP cntrl_sp( 433 valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"), true)); 434 435 m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular 436 // dependency 437 return false; 438 } 439 440 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 441 MightHaveChildren() { 442 return true; 443 } 444 445 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 446 GetIndexOfChildWithName(ConstString name) { 447 if (name == "__ptr_") 448 return 0; 449 if (name == "$$dereference$$") 450 return 1; 451 return UINT32_MAX; 452 } 453 454 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 455 ~LibcxxSharedPtrSyntheticFrontEnd() = default; 456 457 SyntheticChildrenFrontEnd * 458 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator( 459 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 460 return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp) 461 : nullptr); 462 } 463 464 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: 465 LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 466 : SyntheticChildrenFrontEnd(*valobj_sp), m_compressed_pair_sp() { 467 if (valobj_sp) 468 Update(); 469 } 470 471 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: 472 ~LibcxxUniquePtrSyntheticFrontEnd() = default; 473 474 SyntheticChildrenFrontEnd * 475 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator( 476 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 477 return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp) 478 : nullptr); 479 } 480 481 size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: 482 CalculateNumChildren() { 483 return (m_compressed_pair_sp ? 1 : 0); 484 } 485 486 lldb::ValueObjectSP 487 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex( 488 size_t idx) { 489 if (!m_compressed_pair_sp) 490 return lldb::ValueObjectSP(); 491 492 if (idx != 0) 493 return lldb::ValueObjectSP(); 494 495 return m_compressed_pair_sp; 496 } 497 498 bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() { 499 ValueObjectSP valobj_sp = m_backend.GetSP(); 500 if (!valobj_sp) 501 return false; 502 503 ValueObjectSP ptr_sp( 504 valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true)); 505 if (!ptr_sp) 506 return false; 507 508 m_compressed_pair_sp = GetValueOfLibCXXCompressedPair(*ptr_sp); 509 510 return false; 511 } 512 513 bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: 514 MightHaveChildren() { 515 return true; 516 } 517 518 size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: 519 GetIndexOfChildWithName(ConstString name) { 520 if (name == "__value_") 521 return 0; 522 return UINT32_MAX; 523 } 524 525 bool lldb_private::formatters::LibcxxContainerSummaryProvider( 526 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 527 if (valobj.IsPointerType()) { 528 uint64_t value = valobj.GetValueAsUnsigned(0); 529 if (!value) 530 return false; 531 stream.Printf("0x%016" PRIx64 " ", value); 532 } 533 return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr, 534 nullptr, nullptr, &valobj, false, false); 535 } 536 537 // the field layout in a libc++ string (cap, side, data or data, size, cap) 538 enum LibcxxStringLayoutMode { 539 eLibcxxStringLayoutModeCSD = 0, 540 eLibcxxStringLayoutModeDSC = 1, 541 eLibcxxStringLayoutModeInvalid = 0xffff 542 }; 543 544 /// Determine the size in bytes of \p valobj (a libc++ std::string object) and 545 /// extract its data payload. Return the size + payload pair. 546 static llvm::Optional<std::pair<uint64_t, ValueObjectSP>> 547 ExtractLibcxxStringInfo(ValueObject &valobj) { 548 ValueObjectSP D(valobj.GetChildAtIndexPath({0, 0, 0, 0})); 549 if (!D) 550 return {}; 551 552 ValueObjectSP layout_decider( 553 D->GetChildAtIndexPath(llvm::ArrayRef<size_t>({0, 0}))); 554 555 // this child should exist 556 if (!layout_decider) 557 return {}; 558 559 ConstString g_data_name("__data_"); 560 ConstString g_size_name("__size_"); 561 bool short_mode = false; // this means the string is in short-mode and the 562 // data is stored inline 563 LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name) 564 ? eLibcxxStringLayoutModeDSC 565 : eLibcxxStringLayoutModeCSD; 566 uint64_t size_mode_value = 0; 567 568 if (layout == eLibcxxStringLayoutModeDSC) { 569 ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 1, 0})); 570 if (!size_mode) 571 return {}; 572 573 if (size_mode->GetName() != g_size_name) { 574 // we are hitting the padding structure, move along 575 size_mode = D->GetChildAtIndexPath({1, 1, 1}); 576 if (!size_mode) 577 return {}; 578 } 579 580 size_mode_value = (size_mode->GetValueAsUnsigned(0)); 581 short_mode = ((size_mode_value & 0x80) == 0); 582 } else { 583 ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 0, 0})); 584 if (!size_mode) 585 return {}; 586 587 size_mode_value = (size_mode->GetValueAsUnsigned(0)); 588 short_mode = ((size_mode_value & 1) == 0); 589 } 590 591 if (short_mode) { 592 ValueObjectSP s(D->GetChildAtIndex(1, true)); 593 if (!s) 594 return {}; 595 ValueObjectSP location_sp = s->GetChildAtIndex( 596 (layout == eLibcxxStringLayoutModeDSC) ? 0 : 1, true); 597 const uint64_t size = (layout == eLibcxxStringLayoutModeDSC) 598 ? size_mode_value 599 : ((size_mode_value >> 1) % 256); 600 601 // When the small-string optimization takes place, the data must fit in the 602 // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's 603 // likely that the string isn't initialized and we're reading garbage. 604 ExecutionContext exe_ctx(location_sp->GetExecutionContextRef()); 605 const llvm::Optional<uint64_t> max_bytes = 606 location_sp->GetCompilerType().GetByteSize( 607 exe_ctx.GetBestExecutionContextScope()); 608 if (!max_bytes || size > *max_bytes || !location_sp) 609 return {}; 610 611 return std::make_pair(size, location_sp); 612 } 613 614 ValueObjectSP l(D->GetChildAtIndex(0, true)); 615 if (!l) 616 return {}; 617 // we can use the layout_decider object as the data pointer 618 ValueObjectSP location_sp = (layout == eLibcxxStringLayoutModeDSC) 619 ? layout_decider 620 : l->GetChildAtIndex(2, true); 621 ValueObjectSP size_vo(l->GetChildAtIndex(1, true)); 622 const unsigned capacity_index = 623 (layout == eLibcxxStringLayoutModeDSC) ? 2 : 0; 624 ValueObjectSP capacity_vo(l->GetChildAtIndex(capacity_index, true)); 625 if (!size_vo || !location_sp || !capacity_vo) 626 return {}; 627 const uint64_t size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); 628 const uint64_t capacity = 629 capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); 630 if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || 631 capacity < size) 632 return {}; 633 return std::make_pair(size, location_sp); 634 } 635 636 bool lldb_private::formatters::LibcxxWStringSummaryProvider( 637 ValueObject &valobj, Stream &stream, 638 const TypeSummaryOptions &summary_options) { 639 auto string_info = ExtractLibcxxStringInfo(valobj); 640 if (!string_info) 641 return false; 642 uint64_t size; 643 ValueObjectSP location_sp; 644 std::tie(size, location_sp) = *string_info; 645 646 if (size == 0) { 647 stream.Printf("L\"\""); 648 return true; 649 } 650 if (!location_sp) 651 return false; 652 653 654 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); 655 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { 656 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); 657 if (size > max_size) { 658 size = max_size; 659 options.SetIsTruncated(true); 660 } 661 } 662 663 DataExtractor extractor; 664 const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size); 665 if (bytes_read < size) 666 return false; 667 668 // std::wstring::size() is measured in 'characters', not bytes 669 TypeSystemClang *ast_context = 670 ScratchTypeSystemClang::GetForTarget(*valobj.GetTargetSP()); 671 if (!ast_context) 672 return false; 673 674 auto wchar_t_size = 675 ast_context->GetBasicType(lldb::eBasicTypeWChar).GetByteSize(nullptr); 676 if (!wchar_t_size) 677 return false; 678 679 options.SetData(extractor); 680 options.SetStream(&stream); 681 options.SetPrefixToken("L"); 682 options.SetQuote('"'); 683 options.SetSourceSize(size); 684 options.SetBinaryZeroIsTerminator(false); 685 686 switch (*wchar_t_size) { 687 case 1: 688 return StringPrinter::ReadBufferAndDumpToStream< 689 lldb_private::formatters::StringPrinter::StringElementType::UTF8>( 690 options); 691 break; 692 693 case 2: 694 return StringPrinter::ReadBufferAndDumpToStream< 695 lldb_private::formatters::StringPrinter::StringElementType::UTF16>( 696 options); 697 break; 698 699 case 4: 700 return StringPrinter::ReadBufferAndDumpToStream< 701 lldb_private::formatters::StringPrinter::StringElementType::UTF32>( 702 options); 703 } 704 return false; 705 } 706 707 template <StringPrinter::StringElementType element_type> 708 bool LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, 709 const TypeSummaryOptions &summary_options, 710 std::string prefix_token) { 711 auto string_info = ExtractLibcxxStringInfo(valobj); 712 if (!string_info) 713 return false; 714 uint64_t size; 715 ValueObjectSP location_sp; 716 std::tie(size, location_sp) = *string_info; 717 718 if (size == 0) { 719 stream.Printf("\"\""); 720 return true; 721 } 722 723 if (!location_sp) 724 return false; 725 726 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); 727 728 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { 729 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); 730 if (size > max_size) { 731 size = max_size; 732 options.SetIsTruncated(true); 733 } 734 } 735 736 DataExtractor extractor; 737 const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size); 738 if (bytes_read < size) 739 return false; 740 741 options.SetData(extractor); 742 options.SetStream(&stream); 743 if (prefix_token.empty()) 744 options.SetPrefixToken(nullptr); 745 else 746 options.SetPrefixToken(prefix_token); 747 options.SetQuote('"'); 748 options.SetSourceSize(size); 749 options.SetBinaryZeroIsTerminator(false); 750 return StringPrinter::ReadBufferAndDumpToStream<element_type>(options); 751 } 752 753 template <StringPrinter::StringElementType element_type> 754 static bool formatStringImpl(ValueObject &valobj, Stream &stream, 755 const TypeSummaryOptions &summary_options, 756 std::string prefix_token) { 757 StreamString scratch_stream; 758 const bool success = LibcxxStringSummaryProvider<element_type>( 759 valobj, scratch_stream, summary_options, prefix_token); 760 if (success) 761 stream << scratch_stream.GetData(); 762 else 763 stream << "Summary Unavailable"; 764 return true; 765 } 766 767 bool lldb_private::formatters::LibcxxStringSummaryProviderASCII( 768 ValueObject &valobj, Stream &stream, 769 const TypeSummaryOptions &summary_options) { 770 return formatStringImpl<StringPrinter::StringElementType::ASCII>( 771 valobj, stream, summary_options, ""); 772 } 773 774 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16( 775 ValueObject &valobj, Stream &stream, 776 const TypeSummaryOptions &summary_options) { 777 return formatStringImpl<StringPrinter::StringElementType::UTF16>( 778 valobj, stream, summary_options, "u"); 779 } 780 781 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32( 782 ValueObject &valobj, Stream &stream, 783 const TypeSummaryOptions &summary_options) { 784 return formatStringImpl<StringPrinter::StringElementType::UTF32>( 785 valobj, stream, summary_options, "U"); 786 } 787