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), m_count_sp(), 383 m_weak_count_sp(), m_ptr_size(0), m_byte_order(lldb::eByteOrderInvalid) { 384 if (valobj_sp) 385 Update(); 386 } 387 388 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 389 CalculateNumChildren() { 390 return (m_cntrl ? 1 : 0); 391 } 392 393 lldb::ValueObjectSP 394 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex( 395 size_t idx) { 396 if (!m_cntrl) 397 return lldb::ValueObjectSP(); 398 399 ValueObjectSP valobj_sp = m_backend.GetSP(); 400 if (!valobj_sp) 401 return lldb::ValueObjectSP(); 402 403 if (idx == 0) 404 return valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true); 405 406 if (idx > 2) 407 return lldb::ValueObjectSP(); 408 409 if (idx == 1) { 410 if (!m_count_sp) { 411 ValueObjectSP shared_owners_sp(m_cntrl->GetChildMemberWithName( 412 ConstString("__shared_owners_"), true)); 413 if (!shared_owners_sp) 414 return lldb::ValueObjectSP(); 415 uint64_t count = 1 + shared_owners_sp->GetValueAsUnsigned(0); 416 DataExtractor data(&count, 8, m_byte_order, m_ptr_size); 417 m_count_sp = CreateValueObjectFromData( 418 "count", data, valobj_sp->GetExecutionContextRef(), 419 shared_owners_sp->GetCompilerType()); 420 } 421 return m_count_sp; 422 } else /* if (idx == 2) */ 423 { 424 if (!m_weak_count_sp) { 425 ValueObjectSP shared_weak_owners_sp(m_cntrl->GetChildMemberWithName( 426 ConstString("__shared_weak_owners_"), true)); 427 if (!shared_weak_owners_sp) 428 return lldb::ValueObjectSP(); 429 uint64_t count = 1 + shared_weak_owners_sp->GetValueAsUnsigned(0); 430 DataExtractor data(&count, 8, m_byte_order, m_ptr_size); 431 m_weak_count_sp = CreateValueObjectFromData( 432 "count", data, valobj_sp->GetExecutionContextRef(), 433 shared_weak_owners_sp->GetCompilerType()); 434 } 435 return m_weak_count_sp; 436 } 437 } 438 439 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() { 440 m_count_sp.reset(); 441 m_weak_count_sp.reset(); 442 m_cntrl = nullptr; 443 444 ValueObjectSP valobj_sp = m_backend.GetSP(); 445 if (!valobj_sp) 446 return false; 447 448 TargetSP target_sp(valobj_sp->GetTargetSP()); 449 if (!target_sp) 450 return false; 451 452 m_byte_order = target_sp->GetArchitecture().GetByteOrder(); 453 m_ptr_size = target_sp->GetArchitecture().GetAddressByteSize(); 454 455 lldb::ValueObjectSP cntrl_sp( 456 valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"), true)); 457 458 m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular 459 // dependency 460 return false; 461 } 462 463 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 464 MightHaveChildren() { 465 return true; 466 } 467 468 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 469 GetIndexOfChildWithName(ConstString name) { 470 if (name == "__ptr_") 471 return 0; 472 if (name == "count") 473 return 1; 474 if (name == "weak_count") 475 return 2; 476 return UINT32_MAX; 477 } 478 479 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 480 ~LibcxxSharedPtrSyntheticFrontEnd() = default; 481 482 SyntheticChildrenFrontEnd * 483 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator( 484 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 485 return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp) 486 : nullptr); 487 } 488 489 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: 490 LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 491 : SyntheticChildrenFrontEnd(*valobj_sp), m_compressed_pair_sp() { 492 if (valobj_sp) 493 Update(); 494 } 495 496 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: 497 ~LibcxxUniquePtrSyntheticFrontEnd() = default; 498 499 SyntheticChildrenFrontEnd * 500 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator( 501 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 502 return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp) 503 : nullptr); 504 } 505 506 size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: 507 CalculateNumChildren() { 508 return (m_compressed_pair_sp ? 1 : 0); 509 } 510 511 lldb::ValueObjectSP 512 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex( 513 size_t idx) { 514 if (!m_compressed_pair_sp) 515 return lldb::ValueObjectSP(); 516 517 if (idx != 0) 518 return lldb::ValueObjectSP(); 519 520 return m_compressed_pair_sp; 521 } 522 523 bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() { 524 ValueObjectSP valobj_sp = m_backend.GetSP(); 525 if (!valobj_sp) 526 return false; 527 528 ValueObjectSP ptr_sp( 529 valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true)); 530 if (!ptr_sp) 531 return false; 532 533 m_compressed_pair_sp = GetValueOfLibCXXCompressedPair(*ptr_sp); 534 535 return false; 536 } 537 538 bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: 539 MightHaveChildren() { 540 return true; 541 } 542 543 size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: 544 GetIndexOfChildWithName(ConstString name) { 545 if (name == "__value_") 546 return 0; 547 return UINT32_MAX; 548 } 549 550 bool lldb_private::formatters::LibcxxContainerSummaryProvider( 551 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 552 if (valobj.IsPointerType()) { 553 uint64_t value = valobj.GetValueAsUnsigned(0); 554 if (!value) 555 return false; 556 stream.Printf("0x%016" PRIx64 " ", value); 557 } 558 return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr, 559 nullptr, nullptr, &valobj, false, false); 560 } 561 562 // the field layout in a libc++ string (cap, side, data or data, size, cap) 563 enum LibcxxStringLayoutMode { 564 eLibcxxStringLayoutModeCSD = 0, 565 eLibcxxStringLayoutModeDSC = 1, 566 eLibcxxStringLayoutModeInvalid = 0xffff 567 }; 568 569 /// Determine the size in bytes of \p valobj (a libc++ std::string object) and 570 /// extract its data payload. Return the size + payload pair. 571 static llvm::Optional<std::pair<uint64_t, ValueObjectSP>> 572 ExtractLibcxxStringInfo(ValueObject &valobj) { 573 ValueObjectSP D(valobj.GetChildAtIndexPath({0, 0, 0, 0})); 574 if (!D) 575 return {}; 576 577 ValueObjectSP layout_decider( 578 D->GetChildAtIndexPath(llvm::ArrayRef<size_t>({0, 0}))); 579 580 // this child should exist 581 if (!layout_decider) 582 return {}; 583 584 ConstString g_data_name("__data_"); 585 ConstString g_size_name("__size_"); 586 bool short_mode = false; // this means the string is in short-mode and the 587 // data is stored inline 588 LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name) 589 ? eLibcxxStringLayoutModeDSC 590 : eLibcxxStringLayoutModeCSD; 591 uint64_t size_mode_value = 0; 592 593 if (layout == eLibcxxStringLayoutModeDSC) { 594 ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 1, 0})); 595 if (!size_mode) 596 return {}; 597 598 if (size_mode->GetName() != g_size_name) { 599 // we are hitting the padding structure, move along 600 size_mode = D->GetChildAtIndexPath({1, 1, 1}); 601 if (!size_mode) 602 return {}; 603 } 604 605 size_mode_value = (size_mode->GetValueAsUnsigned(0)); 606 short_mode = ((size_mode_value & 0x80) == 0); 607 } else { 608 ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 0, 0})); 609 if (!size_mode) 610 return {}; 611 612 size_mode_value = (size_mode->GetValueAsUnsigned(0)); 613 short_mode = ((size_mode_value & 1) == 0); 614 } 615 616 if (short_mode) { 617 ValueObjectSP s(D->GetChildAtIndex(1, true)); 618 if (!s) 619 return {}; 620 ValueObjectSP location_sp = s->GetChildAtIndex( 621 (layout == eLibcxxStringLayoutModeDSC) ? 0 : 1, true); 622 const uint64_t size = (layout == eLibcxxStringLayoutModeDSC) 623 ? size_mode_value 624 : ((size_mode_value >> 1) % 256); 625 626 // When the small-string optimization takes place, the data must fit in the 627 // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's 628 // likely that the string isn't initialized and we're reading garbage. 629 ExecutionContext exe_ctx(location_sp->GetExecutionContextRef()); 630 const llvm::Optional<uint64_t> max_bytes = 631 location_sp->GetCompilerType().GetByteSize( 632 exe_ctx.GetBestExecutionContextScope()); 633 if (!max_bytes || size > *max_bytes || !location_sp) 634 return {}; 635 636 return std::make_pair(size, location_sp); 637 } 638 639 ValueObjectSP l(D->GetChildAtIndex(0, true)); 640 if (!l) 641 return {}; 642 // we can use the layout_decider object as the data pointer 643 ValueObjectSP location_sp = (layout == eLibcxxStringLayoutModeDSC) 644 ? layout_decider 645 : l->GetChildAtIndex(2, true); 646 ValueObjectSP size_vo(l->GetChildAtIndex(1, true)); 647 const unsigned capacity_index = 648 (layout == eLibcxxStringLayoutModeDSC) ? 2 : 0; 649 ValueObjectSP capacity_vo(l->GetChildAtIndex(capacity_index, true)); 650 if (!size_vo || !location_sp || !capacity_vo) 651 return {}; 652 const uint64_t size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); 653 const uint64_t capacity = 654 capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); 655 if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || 656 capacity < size) 657 return {}; 658 return std::make_pair(size, location_sp); 659 } 660 661 bool lldb_private::formatters::LibcxxWStringSummaryProvider( 662 ValueObject &valobj, Stream &stream, 663 const TypeSummaryOptions &summary_options) { 664 auto string_info = ExtractLibcxxStringInfo(valobj); 665 if (!string_info) 666 return false; 667 uint64_t size; 668 ValueObjectSP location_sp; 669 std::tie(size, location_sp) = *string_info; 670 671 if (size == 0) { 672 stream.Printf("L\"\""); 673 return true; 674 } 675 if (!location_sp) 676 return false; 677 678 679 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); 680 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { 681 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); 682 if (size > max_size) { 683 size = max_size; 684 options.SetIsTruncated(true); 685 } 686 } 687 688 DataExtractor extractor; 689 const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size); 690 if (bytes_read < size) 691 return false; 692 693 // std::wstring::size() is measured in 'characters', not bytes 694 TypeSystemClang *ast_context = 695 ScratchTypeSystemClang::GetForTarget(*valobj.GetTargetSP()); 696 if (!ast_context) 697 return false; 698 699 auto wchar_t_size = 700 ast_context->GetBasicType(lldb::eBasicTypeWChar).GetByteSize(nullptr); 701 if (!wchar_t_size) 702 return false; 703 704 options.SetData(extractor); 705 options.SetStream(&stream); 706 options.SetPrefixToken("L"); 707 options.SetQuote('"'); 708 options.SetSourceSize(size); 709 options.SetBinaryZeroIsTerminator(false); 710 711 switch (*wchar_t_size) { 712 case 1: 713 return StringPrinter::ReadBufferAndDumpToStream< 714 lldb_private::formatters::StringPrinter::StringElementType::UTF8>( 715 options); 716 break; 717 718 case 2: 719 return StringPrinter::ReadBufferAndDumpToStream< 720 lldb_private::formatters::StringPrinter::StringElementType::UTF16>( 721 options); 722 break; 723 724 case 4: 725 return StringPrinter::ReadBufferAndDumpToStream< 726 lldb_private::formatters::StringPrinter::StringElementType::UTF32>( 727 options); 728 } 729 return false; 730 } 731 732 template <StringPrinter::StringElementType element_type> 733 bool LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, 734 const TypeSummaryOptions &summary_options, 735 std::string prefix_token) { 736 auto string_info = ExtractLibcxxStringInfo(valobj); 737 if (!string_info) 738 return false; 739 uint64_t size; 740 ValueObjectSP location_sp; 741 std::tie(size, location_sp) = *string_info; 742 743 if (size == 0) { 744 stream.Printf("\"\""); 745 return true; 746 } 747 748 if (!location_sp) 749 return false; 750 751 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); 752 753 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { 754 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); 755 if (size > max_size) { 756 size = max_size; 757 options.SetIsTruncated(true); 758 } 759 } 760 761 DataExtractor extractor; 762 const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size); 763 if (bytes_read < size) 764 return false; 765 766 options.SetData(extractor); 767 options.SetStream(&stream); 768 if (prefix_token.empty()) 769 options.SetPrefixToken(nullptr); 770 else 771 options.SetPrefixToken(prefix_token); 772 options.SetQuote('"'); 773 options.SetSourceSize(size); 774 options.SetBinaryZeroIsTerminator(false); 775 return StringPrinter::ReadBufferAndDumpToStream<element_type>(options); 776 } 777 778 template <StringPrinter::StringElementType element_type> 779 static bool formatStringImpl(ValueObject &valobj, Stream &stream, 780 const TypeSummaryOptions &summary_options, 781 std::string prefix_token) { 782 StreamString scratch_stream; 783 const bool success = LibcxxStringSummaryProvider<element_type>( 784 valobj, scratch_stream, summary_options, prefix_token); 785 if (success) 786 stream << scratch_stream.GetData(); 787 else 788 stream << "Summary Unavailable"; 789 return true; 790 } 791 792 bool lldb_private::formatters::LibcxxStringSummaryProviderASCII( 793 ValueObject &valobj, Stream &stream, 794 const TypeSummaryOptions &summary_options) { 795 return formatStringImpl<StringPrinter::StringElementType::ASCII>( 796 valobj, stream, summary_options, ""); 797 } 798 799 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16( 800 ValueObject &valobj, Stream &stream, 801 const TypeSummaryOptions &summary_options) { 802 return formatStringImpl<StringPrinter::StringElementType::UTF16>( 803 valobj, stream, summary_options, "u"); 804 } 805 806 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32( 807 ValueObject &valobj, Stream &stream, 808 const TypeSummaryOptions &summary_options) { 809 return formatStringImpl<StringPrinter::StringElementType::UTF32>( 810 valobj, stream, summary_options, "U"); 811 } 812