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 /* 148 (lldb) fr var ibeg --raw --ptr-depth 1 149 (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int, 150 std::__1::basic_string<char, std::__1::char_traits<char>, 151 std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int, 152 std::__1::basic_string<char, std::__1::char_traits<char>, 153 std::__1::allocator<char> > >, void *> *, long> >) ibeg = { 154 __i_ = { 155 __ptr_ = 0x0000000100103870 { 156 std::__1::__tree_node_base<void *> = { 157 std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = { 158 __left_ = 0x0000000000000000 159 } 160 __right_ = 0x0000000000000000 161 __parent_ = 0x00000001001038b0 162 __is_black_ = true 163 } 164 __value_ = { 165 first = 0 166 second = { std::string } 167 */ 168 169 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: 170 LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 171 : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() { 172 if (valobj_sp) 173 Update(); 174 } 175 176 bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() { 177 m_pair_sp.reset(); 178 m_pair_ptr = nullptr; 179 180 ValueObjectSP valobj_sp = m_backend.GetSP(); 181 if (!valobj_sp) 182 return false; 183 184 TargetSP target_sp(valobj_sp->GetTargetSP()); 185 186 if (!target_sp) 187 return false; 188 189 if (!valobj_sp) 190 return false; 191 192 static ConstString g___i_("__i_"); 193 194 // this must be a ValueObject* because it is a child of the ValueObject we 195 // are producing children for it if were a ValueObjectSP, we would end up 196 // with a loop (iterator -> synthetic -> child -> parent == iterator) and 197 // that would in turn leak memory by never allowing the ValueObjects to die 198 // and free their memory 199 m_pair_ptr = valobj_sp 200 ->GetValueForExpressionPath( 201 ".__i_.__ptr_->__value_", nullptr, nullptr, 202 ValueObject::GetValueForExpressionPathOptions() 203 .DontCheckDotVsArrowSyntax() 204 .SetSyntheticChildrenTraversal( 205 ValueObject::GetValueForExpressionPathOptions:: 206 SyntheticChildrenTraversal::None), 207 nullptr) 208 .get(); 209 210 if (!m_pair_ptr) { 211 m_pair_ptr = valobj_sp 212 ->GetValueForExpressionPath( 213 ".__i_.__ptr_", nullptr, nullptr, 214 ValueObject::GetValueForExpressionPathOptions() 215 .DontCheckDotVsArrowSyntax() 216 .SetSyntheticChildrenTraversal( 217 ValueObject::GetValueForExpressionPathOptions:: 218 SyntheticChildrenTraversal::None), 219 nullptr) 220 .get(); 221 if (m_pair_ptr) { 222 auto __i_(valobj_sp->GetChildMemberWithName(g___i_, true)); 223 if (!__i_) { 224 m_pair_ptr = nullptr; 225 return false; 226 } 227 CompilerType pair_type( 228 __i_->GetCompilerType().GetTypeTemplateArgument(0)); 229 std::string name; 230 uint64_t bit_offset_ptr; 231 uint32_t bitfield_bit_size_ptr; 232 bool is_bitfield_ptr; 233 pair_type = pair_type.GetFieldAtIndex( 234 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr); 235 if (!pair_type) { 236 m_pair_ptr = nullptr; 237 return false; 238 } 239 240 auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS)); 241 m_pair_ptr = nullptr; 242 if (addr && addr != LLDB_INVALID_ADDRESS) { 243 TypeSystemClang *ast_ctx = 244 llvm::dyn_cast_or_null<TypeSystemClang>(pair_type.GetTypeSystem()); 245 if (!ast_ctx) 246 return false; 247 CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( 248 ConstString(), 249 {{"ptr0", 250 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, 251 {"ptr1", 252 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, 253 {"ptr2", 254 ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, 255 {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)}, 256 {"payload", pair_type}}); 257 llvm::Optional<uint64_t> size = tree_node_type.GetByteSize(nullptr); 258 if (!size) 259 return false; 260 DataBufferSP buffer_sp(new DataBufferHeap(*size, 0)); 261 ProcessSP process_sp(target_sp->GetProcessSP()); 262 Status error; 263 process_sp->ReadMemory(addr, buffer_sp->GetBytes(), 264 buffer_sp->GetByteSize(), error); 265 if (error.Fail()) 266 return false; 267 DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(), 268 process_sp->GetAddressByteSize()); 269 auto pair_sp = CreateValueObjectFromData( 270 "pair", extractor, valobj_sp->GetExecutionContextRef(), 271 tree_node_type); 272 if (pair_sp) 273 m_pair_sp = pair_sp->GetChildAtIndex(4, true); 274 } 275 } 276 } 277 278 return false; 279 } 280 281 size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: 282 CalculateNumChildren() { 283 return 2; 284 } 285 286 lldb::ValueObjectSP 287 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex( 288 size_t idx) { 289 if (m_pair_ptr) 290 return m_pair_ptr->GetChildAtIndex(idx, true); 291 if (m_pair_sp) 292 return m_pair_sp->GetChildAtIndex(idx, true); 293 return lldb::ValueObjectSP(); 294 } 295 296 bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: 297 MightHaveChildren() { 298 return true; 299 } 300 301 size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: 302 GetIndexOfChildWithName(ConstString name) { 303 if (name == "first") 304 return 0; 305 if (name == "second") 306 return 1; 307 return UINT32_MAX; 308 } 309 310 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: 311 ~LibCxxMapIteratorSyntheticFrontEnd() { 312 // this will be deleted when its parent dies (since it's a child object) 313 // delete m_pair_ptr; 314 } 315 316 SyntheticChildrenFrontEnd * 317 lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator( 318 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 319 return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp) 320 : nullptr); 321 } 322 323 /* 324 (lldb) fr var ibeg --raw --ptr-depth 1 -T 325 (std::__1::__wrap_iter<int *>) ibeg = { 326 (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 { 327 (int) *__i = 1 328 } 329 } 330 */ 331 332 SyntheticChildrenFrontEnd * 333 lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator( 334 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 335 static ConstString g_item_name; 336 if (!g_item_name) 337 g_item_name.SetCString("__i"); 338 return (valobj_sp 339 ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name) 340 : nullptr); 341 } 342 343 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 344 LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 345 : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr), m_count_sp(), 346 m_weak_count_sp(), m_ptr_size(0), m_byte_order(lldb::eByteOrderInvalid) { 347 if (valobj_sp) 348 Update(); 349 } 350 351 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 352 CalculateNumChildren() { 353 return (m_cntrl ? 1 : 0); 354 } 355 356 lldb::ValueObjectSP 357 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex( 358 size_t idx) { 359 if (!m_cntrl) 360 return lldb::ValueObjectSP(); 361 362 ValueObjectSP valobj_sp = m_backend.GetSP(); 363 if (!valobj_sp) 364 return lldb::ValueObjectSP(); 365 366 if (idx == 0) 367 return valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true); 368 369 if (idx > 2) 370 return lldb::ValueObjectSP(); 371 372 if (idx == 1) { 373 if (!m_count_sp) { 374 ValueObjectSP shared_owners_sp(m_cntrl->GetChildMemberWithName( 375 ConstString("__shared_owners_"), true)); 376 if (!shared_owners_sp) 377 return lldb::ValueObjectSP(); 378 uint64_t count = 1 + shared_owners_sp->GetValueAsUnsigned(0); 379 DataExtractor data(&count, 8, m_byte_order, m_ptr_size); 380 m_count_sp = CreateValueObjectFromData( 381 "count", data, valobj_sp->GetExecutionContextRef(), 382 shared_owners_sp->GetCompilerType()); 383 } 384 return m_count_sp; 385 } else /* if (idx == 2) */ 386 { 387 if (!m_weak_count_sp) { 388 ValueObjectSP shared_weak_owners_sp(m_cntrl->GetChildMemberWithName( 389 ConstString("__shared_weak_owners_"), true)); 390 if (!shared_weak_owners_sp) 391 return lldb::ValueObjectSP(); 392 uint64_t count = 1 + shared_weak_owners_sp->GetValueAsUnsigned(0); 393 DataExtractor data(&count, 8, m_byte_order, m_ptr_size); 394 m_weak_count_sp = CreateValueObjectFromData( 395 "count", data, valobj_sp->GetExecutionContextRef(), 396 shared_weak_owners_sp->GetCompilerType()); 397 } 398 return m_weak_count_sp; 399 } 400 } 401 402 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() { 403 m_count_sp.reset(); 404 m_weak_count_sp.reset(); 405 m_cntrl = nullptr; 406 407 ValueObjectSP valobj_sp = m_backend.GetSP(); 408 if (!valobj_sp) 409 return false; 410 411 TargetSP target_sp(valobj_sp->GetTargetSP()); 412 if (!target_sp) 413 return false; 414 415 m_byte_order = target_sp->GetArchitecture().GetByteOrder(); 416 m_ptr_size = target_sp->GetArchitecture().GetAddressByteSize(); 417 418 lldb::ValueObjectSP cntrl_sp( 419 valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"), true)); 420 421 m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular 422 // dependency 423 return false; 424 } 425 426 bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 427 MightHaveChildren() { 428 return true; 429 } 430 431 size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 432 GetIndexOfChildWithName(ConstString name) { 433 if (name == "__ptr_") 434 return 0; 435 if (name == "count") 436 return 1; 437 if (name == "weak_count") 438 return 2; 439 return UINT32_MAX; 440 } 441 442 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: 443 ~LibcxxSharedPtrSyntheticFrontEnd() = default; 444 445 SyntheticChildrenFrontEnd * 446 lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator( 447 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 448 return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp) 449 : nullptr); 450 } 451 452 bool lldb_private::formatters::LibcxxContainerSummaryProvider( 453 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 454 if (valobj.IsPointerType()) { 455 uint64_t value = valobj.GetValueAsUnsigned(0); 456 if (!value) 457 return false; 458 stream.Printf("0x%016" PRIx64 " ", value); 459 } 460 return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr, 461 nullptr, nullptr, &valobj, false, false); 462 } 463 464 // the field layout in a libc++ string (cap, side, data or data, size, cap) 465 enum LibcxxStringLayoutMode { 466 eLibcxxStringLayoutModeCSD = 0, 467 eLibcxxStringLayoutModeDSC = 1, 468 eLibcxxStringLayoutModeInvalid = 0xffff 469 }; 470 471 /// Determine the size in bytes of \p valobj (a libc++ std::string object) and 472 /// extract its data payload. Return the size + payload pair. 473 static llvm::Optional<std::pair<uint64_t, ValueObjectSP>> 474 ExtractLibcxxStringInfo(ValueObject &valobj) { 475 ValueObjectSP D(valobj.GetChildAtIndexPath({0, 0, 0, 0})); 476 if (!D) 477 return {}; 478 479 ValueObjectSP layout_decider( 480 D->GetChildAtIndexPath(llvm::ArrayRef<size_t>({0, 0}))); 481 482 // this child should exist 483 if (!layout_decider) 484 return {}; 485 486 ConstString g_data_name("__data_"); 487 ConstString g_size_name("__size_"); 488 bool short_mode = false; // this means the string is in short-mode and the 489 // data is stored inline 490 LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name) 491 ? eLibcxxStringLayoutModeDSC 492 : eLibcxxStringLayoutModeCSD; 493 uint64_t size_mode_value = 0; 494 495 if (layout == eLibcxxStringLayoutModeDSC) { 496 ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 1, 0})); 497 if (!size_mode) 498 return {}; 499 500 if (size_mode->GetName() != g_size_name) { 501 // we are hitting the padding structure, move along 502 size_mode = D->GetChildAtIndexPath({1, 1, 1}); 503 if (!size_mode) 504 return {}; 505 } 506 507 size_mode_value = (size_mode->GetValueAsUnsigned(0)); 508 short_mode = ((size_mode_value & 0x80) == 0); 509 } else { 510 ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 0, 0})); 511 if (!size_mode) 512 return {}; 513 514 size_mode_value = (size_mode->GetValueAsUnsigned(0)); 515 short_mode = ((size_mode_value & 1) == 0); 516 } 517 518 if (short_mode) { 519 ValueObjectSP s(D->GetChildAtIndex(1, true)); 520 if (!s) 521 return {}; 522 ValueObjectSP location_sp = s->GetChildAtIndex( 523 (layout == eLibcxxStringLayoutModeDSC) ? 0 : 1, true); 524 const uint64_t size = (layout == eLibcxxStringLayoutModeDSC) 525 ? size_mode_value 526 : ((size_mode_value >> 1) % 256); 527 528 // When the small-string optimization takes place, the data must fit in the 529 // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's 530 // likely that the string isn't initialized and we're reading garbage. 531 ExecutionContext exe_ctx(location_sp->GetExecutionContextRef()); 532 const llvm::Optional<uint64_t> max_bytes = 533 location_sp->GetCompilerType().GetByteSize( 534 exe_ctx.GetBestExecutionContextScope()); 535 if (!max_bytes || size > *max_bytes || !location_sp) 536 return {}; 537 538 return std::make_pair(size, location_sp); 539 } 540 541 ValueObjectSP l(D->GetChildAtIndex(0, true)); 542 if (!l) 543 return {}; 544 // we can use the layout_decider object as the data pointer 545 ValueObjectSP location_sp = (layout == eLibcxxStringLayoutModeDSC) 546 ? layout_decider 547 : l->GetChildAtIndex(2, true); 548 ValueObjectSP size_vo(l->GetChildAtIndex(1, true)); 549 const unsigned capacity_index = 550 (layout == eLibcxxStringLayoutModeDSC) ? 2 : 0; 551 ValueObjectSP capacity_vo(l->GetChildAtIndex(capacity_index, true)); 552 if (!size_vo || !location_sp || !capacity_vo) 553 return {}; 554 const uint64_t size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); 555 const uint64_t capacity = 556 capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); 557 if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || 558 capacity < size) 559 return {}; 560 return std::make_pair(size, location_sp); 561 } 562 563 bool lldb_private::formatters::LibcxxWStringSummaryProvider( 564 ValueObject &valobj, Stream &stream, 565 const TypeSummaryOptions &summary_options) { 566 auto string_info = ExtractLibcxxStringInfo(valobj); 567 if (!string_info) 568 return false; 569 uint64_t size; 570 ValueObjectSP location_sp; 571 std::tie(size, location_sp) = *string_info; 572 573 if (size == 0) { 574 stream.Printf("L\"\""); 575 return true; 576 } 577 if (!location_sp) 578 return false; 579 580 581 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); 582 if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { 583 const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); 584 if (size > max_size) { 585 size = max_size; 586 options.SetIsTruncated(true); 587 } 588 } 589 590 DataExtractor extractor; 591 const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size); 592 if (bytes_read < size) 593 return false; 594 595 // std::wstring::size() is measured in 'characters', not bytes 596 TypeSystemClang *ast_context = 597 TypeSystemClang::GetScratch(*valobj.GetTargetSP()); 598 if (!ast_context) 599 return false; 600 601 auto wchar_t_size = 602 ast_context->GetBasicType(lldb::eBasicTypeWChar).GetByteSize(nullptr); 603 if (!wchar_t_size) 604 return false; 605 606 options.SetData(extractor); 607 options.SetStream(&stream); 608 options.SetPrefixToken("L"); 609 options.SetQuote('"'); 610 options.SetSourceSize(size); 611 options.SetBinaryZeroIsTerminator(false); 612 613 switch (*wchar_t_size) { 614 case 1: 615 return StringPrinter::ReadBufferAndDumpToStream< 616 lldb_private::formatters::StringPrinter::StringElementType::UTF8>( 617 options); 618 break; 619 620 case 2: 621 return StringPrinter::ReadBufferAndDumpToStream< 622 lldb_private::formatters::StringPrinter::StringElementType::UTF16>( 623 options); 624 break; 625 626 case 4: 627 return StringPrinter::ReadBufferAndDumpToStream< 628 lldb_private::formatters::StringPrinter::StringElementType::UTF32>( 629 options); 630 } 631 return false; 632 } 633 634 template <StringPrinter::StringElementType element_type> 635 bool LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, 636 const TypeSummaryOptions &summary_options, 637 std::string prefix_token) { 638 auto string_info = ExtractLibcxxStringInfo(valobj); 639 if (!string_info) 640 return false; 641 uint64_t size; 642 ValueObjectSP location_sp; 643 std::tie(size, location_sp) = *string_info; 644 645 if (size == 0) { 646 stream.Printf("\"\""); 647 return true; 648 } 649 650 if (!location_sp) 651 return false; 652 653 StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); 654 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 options.SetData(extractor); 669 options.SetStream(&stream); 670 if (prefix_token.empty()) 671 options.SetPrefixToken(nullptr); 672 else 673 options.SetPrefixToken(prefix_token); 674 options.SetQuote('"'); 675 options.SetSourceSize(size); 676 options.SetBinaryZeroIsTerminator(false); 677 return StringPrinter::ReadBufferAndDumpToStream<element_type>(options); 678 } 679 680 template <StringPrinter::StringElementType element_type> 681 static bool formatStringImpl(ValueObject &valobj, Stream &stream, 682 const TypeSummaryOptions &summary_options, 683 std::string prefix_token) { 684 StreamString scratch_stream; 685 const bool success = LibcxxStringSummaryProvider<element_type>( 686 valobj, scratch_stream, summary_options, prefix_token); 687 if (success) 688 stream << scratch_stream.GetData(); 689 else 690 stream << "Summary Unavailable"; 691 return true; 692 } 693 694 bool lldb_private::formatters::LibcxxStringSummaryProviderASCII( 695 ValueObject &valobj, Stream &stream, 696 const TypeSummaryOptions &summary_options) { 697 return formatStringImpl<StringPrinter::StringElementType::ASCII>( 698 valobj, stream, summary_options, ""); 699 } 700 701 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16( 702 ValueObject &valobj, Stream &stream, 703 const TypeSummaryOptions &summary_options) { 704 return formatStringImpl<StringPrinter::StringElementType::UTF16>( 705 valobj, stream, summary_options, "u"); 706 } 707 708 bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32( 709 ValueObject &valobj, Stream &stream, 710 const TypeSummaryOptions &summary_options) { 711 return formatStringImpl<StringPrinter::StringElementType::UTF32>( 712 valobj, stream, summary_options, "U"); 713 } 714