1 //===-- StringPrinter.cpp ----------------------------------------*- C++ 2 //-*-===// 3 // 4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 // See https://llvm.org/LICENSE.txt for license information. 6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/DataFormatters/StringPrinter.h" 11 12 #include "lldb/Core/Debugger.h" 13 #include "lldb/Core/ValueObject.h" 14 #include "lldb/Target/Language.h" 15 #include "lldb/Target/Process.h" 16 #include "lldb/Target/Target.h" 17 #include "lldb/Utility/Status.h" 18 19 #include "llvm/Support/ConvertUTF.h" 20 21 #include <ctype.h> 22 #include <locale> 23 #include <memory> 24 25 using namespace lldb; 26 using namespace lldb_private; 27 using namespace lldb_private::formatters; 28 29 // we define this for all values of type but only implement it for those we 30 // care about that's good because we get linker errors for any unsupported type 31 template <lldb_private::formatters::StringPrinter::StringElementType type> 32 static StringPrinter::StringPrinterBufferPointer<> 33 GetPrintableImpl(uint8_t *buffer, uint8_t *buffer_end, uint8_t *&next); 34 35 // mimic isprint() for Unicode codepoints 36 static bool isprint(char32_t codepoint) { 37 if (codepoint <= 0x1F || codepoint == 0x7F) // C0 38 { 39 return false; 40 } 41 if (codepoint >= 0x80 && codepoint <= 0x9F) // C1 42 { 43 return false; 44 } 45 if (codepoint == 0x2028 || codepoint == 0x2029) // line/paragraph separators 46 { 47 return false; 48 } 49 if (codepoint == 0x200E || codepoint == 0x200F || 50 (codepoint >= 0x202A && 51 codepoint <= 0x202E)) // bidirectional text control 52 { 53 return false; 54 } 55 if (codepoint >= 0xFFF9 && 56 codepoint <= 0xFFFF) // interlinears and generally specials 57 { 58 return false; 59 } 60 return true; 61 } 62 63 template <> 64 StringPrinter::StringPrinterBufferPointer<> 65 GetPrintableImpl<StringPrinter::StringElementType::ASCII>(uint8_t *buffer, 66 uint8_t *buffer_end, 67 uint8_t *&next) { 68 StringPrinter::StringPrinterBufferPointer<> retval = {nullptr}; 69 70 switch (*buffer) { 71 case 0: 72 retval = {"\\0", 2}; 73 break; 74 case '\a': 75 retval = {"\\a", 2}; 76 break; 77 case '\b': 78 retval = {"\\b", 2}; 79 break; 80 case '\f': 81 retval = {"\\f", 2}; 82 break; 83 case '\n': 84 retval = {"\\n", 2}; 85 break; 86 case '\r': 87 retval = {"\\r", 2}; 88 break; 89 case '\t': 90 retval = {"\\t", 2}; 91 break; 92 case '\v': 93 retval = {"\\v", 2}; 94 break; 95 case '\"': 96 retval = {"\\\"", 2}; 97 break; 98 case '\\': 99 retval = {"\\\\", 2}; 100 break; 101 default: 102 if (isprint(*buffer)) 103 retval = {buffer, 1}; 104 else { 105 uint8_t *data = new uint8_t[5]; 106 sprintf((char *)data, "\\x%02x", *buffer); 107 retval = {data, 4, [](const uint8_t *c) { delete[] c; }}; 108 break; 109 } 110 } 111 112 next = buffer + 1; 113 return retval; 114 } 115 116 static char32_t ConvertUTF8ToCodePoint(unsigned char c0, unsigned char c1) { 117 return (c0 - 192) * 64 + (c1 - 128); 118 } 119 static char32_t ConvertUTF8ToCodePoint(unsigned char c0, unsigned char c1, 120 unsigned char c2) { 121 return (c0 - 224) * 4096 + (c1 - 128) * 64 + (c2 - 128); 122 } 123 static char32_t ConvertUTF8ToCodePoint(unsigned char c0, unsigned char c1, 124 unsigned char c2, unsigned char c3) { 125 return (c0 - 240) * 262144 + (c2 - 128) * 4096 + (c2 - 128) * 64 + (c3 - 128); 126 } 127 128 template <> 129 StringPrinter::StringPrinterBufferPointer<> 130 GetPrintableImpl<StringPrinter::StringElementType::UTF8>(uint8_t *buffer, 131 uint8_t *buffer_end, 132 uint8_t *&next) { 133 StringPrinter::StringPrinterBufferPointer<> retval{nullptr}; 134 135 unsigned utf8_encoded_len = llvm::getNumBytesForUTF8(*buffer); 136 137 if (1u + std::distance(buffer, buffer_end) < utf8_encoded_len) { 138 // I don't have enough bytes - print whatever I have left 139 retval = {buffer, static_cast<size_t>(1 + buffer_end - buffer)}; 140 next = buffer_end + 1; 141 return retval; 142 } 143 144 char32_t codepoint = 0; 145 switch (utf8_encoded_len) { 146 case 1: 147 // this is just an ASCII byte - ask ASCII 148 return GetPrintableImpl<StringPrinter::StringElementType::ASCII>( 149 buffer, buffer_end, next); 150 case 2: 151 codepoint = ConvertUTF8ToCodePoint((unsigned char)*buffer, 152 (unsigned char)*(buffer + 1)); 153 break; 154 case 3: 155 codepoint = ConvertUTF8ToCodePoint((unsigned char)*buffer, 156 (unsigned char)*(buffer + 1), 157 (unsigned char)*(buffer + 2)); 158 break; 159 case 4: 160 codepoint = ConvertUTF8ToCodePoint( 161 (unsigned char)*buffer, (unsigned char)*(buffer + 1), 162 (unsigned char)*(buffer + 2), (unsigned char)*(buffer + 3)); 163 break; 164 default: 165 // this is probably some bogus non-character thing just print it as-is and 166 // hope to sync up again soon 167 retval = {buffer, 1}; 168 next = buffer + 1; 169 return retval; 170 } 171 172 if (codepoint) { 173 switch (codepoint) { 174 case 0: 175 retval = {"\\0", 2}; 176 break; 177 case '\a': 178 retval = {"\\a", 2}; 179 break; 180 case '\b': 181 retval = {"\\b", 2}; 182 break; 183 case '\f': 184 retval = {"\\f", 2}; 185 break; 186 case '\n': 187 retval = {"\\n", 2}; 188 break; 189 case '\r': 190 retval = {"\\r", 2}; 191 break; 192 case '\t': 193 retval = {"\\t", 2}; 194 break; 195 case '\v': 196 retval = {"\\v", 2}; 197 break; 198 case '\"': 199 retval = {"\\\"", 2}; 200 break; 201 case '\\': 202 retval = {"\\\\", 2}; 203 break; 204 default: 205 if (isprint(codepoint)) 206 retval = {buffer, utf8_encoded_len}; 207 else { 208 uint8_t *data = new uint8_t[11]; 209 sprintf((char *)data, "\\U%08x", (unsigned)codepoint); 210 retval = {data, 10, [](const uint8_t *c) { delete[] c; }}; 211 break; 212 } 213 } 214 215 next = buffer + utf8_encoded_len; 216 return retval; 217 } 218 219 // this should not happen - but just in case.. try to resync at some point 220 retval = {buffer, 1}; 221 next = buffer + 1; 222 return retval; 223 } 224 225 // Given a sequence of bytes, this function returns: a sequence of bytes to 226 // actually print out + a length the following unscanned position of the buffer 227 // is in next 228 static StringPrinter::StringPrinterBufferPointer<> 229 GetPrintable(StringPrinter::StringElementType type, uint8_t *buffer, 230 uint8_t *buffer_end, uint8_t *&next) { 231 if (!buffer) 232 return {nullptr}; 233 234 switch (type) { 235 case StringPrinter::StringElementType::ASCII: 236 return GetPrintableImpl<StringPrinter::StringElementType::ASCII>( 237 buffer, buffer_end, next); 238 case StringPrinter::StringElementType::UTF8: 239 return GetPrintableImpl<StringPrinter::StringElementType::UTF8>( 240 buffer, buffer_end, next); 241 default: 242 return {nullptr}; 243 } 244 } 245 246 StringPrinter::EscapingHelper 247 StringPrinter::GetDefaultEscapingHelper(GetPrintableElementType elem_type) { 248 switch (elem_type) { 249 case GetPrintableElementType::UTF8: 250 return [](uint8_t *buffer, uint8_t *buffer_end, 251 uint8_t *&next) -> StringPrinter::StringPrinterBufferPointer<> { 252 return GetPrintable(StringPrinter::StringElementType::UTF8, buffer, 253 buffer_end, next); 254 }; 255 case GetPrintableElementType::ASCII: 256 return [](uint8_t *buffer, uint8_t *buffer_end, 257 uint8_t *&next) -> StringPrinter::StringPrinterBufferPointer<> { 258 return GetPrintable(StringPrinter::StringElementType::ASCII, buffer, 259 buffer_end, next); 260 }; 261 } 262 llvm_unreachable("bad element type"); 263 } 264 265 // use this call if you already have an LLDB-side buffer for the data 266 template <typename SourceDataType> 267 static bool DumpUTFBufferToStream( 268 llvm::ConversionResult (*ConvertFunction)(const SourceDataType **, 269 const SourceDataType *, 270 llvm::UTF8 **, llvm::UTF8 *, 271 llvm::ConversionFlags), 272 const StringPrinter::ReadBufferAndDumpToStreamOptions &dump_options) { 273 Stream &stream(*dump_options.GetStream()); 274 if (dump_options.GetPrefixToken() != nullptr) 275 stream.Printf("%s", dump_options.GetPrefixToken()); 276 if (dump_options.GetQuote() != 0) 277 stream.Printf("%c", dump_options.GetQuote()); 278 auto data(dump_options.GetData()); 279 auto source_size(dump_options.GetSourceSize()); 280 if (data.GetByteSize() && data.GetDataStart() && data.GetDataEnd()) { 281 const int bufferSPSize = data.GetByteSize(); 282 if (dump_options.GetSourceSize() == 0) { 283 const int origin_encoding = 8 * sizeof(SourceDataType); 284 source_size = bufferSPSize / (origin_encoding / 4); 285 } 286 287 const SourceDataType *data_ptr = 288 (const SourceDataType *)data.GetDataStart(); 289 const SourceDataType *data_end_ptr = data_ptr + source_size; 290 291 const bool zero_is_terminator = dump_options.GetBinaryZeroIsTerminator(); 292 293 if (zero_is_terminator) { 294 while (data_ptr < data_end_ptr) { 295 if (!*data_ptr) { 296 data_end_ptr = data_ptr; 297 break; 298 } 299 data_ptr++; 300 } 301 302 data_ptr = (const SourceDataType *)data.GetDataStart(); 303 } 304 305 lldb::DataBufferSP utf8_data_buffer_sp; 306 llvm::UTF8 *utf8_data_ptr = nullptr; 307 llvm::UTF8 *utf8_data_end_ptr = nullptr; 308 309 if (ConvertFunction) { 310 utf8_data_buffer_sp = 311 std::make_shared<DataBufferHeap>(4 * bufferSPSize, 0); 312 utf8_data_ptr = (llvm::UTF8 *)utf8_data_buffer_sp->GetBytes(); 313 utf8_data_end_ptr = utf8_data_ptr + utf8_data_buffer_sp->GetByteSize(); 314 ConvertFunction(&data_ptr, data_end_ptr, &utf8_data_ptr, 315 utf8_data_end_ptr, llvm::lenientConversion); 316 if (!zero_is_terminator) 317 utf8_data_end_ptr = utf8_data_ptr; 318 // needed because the ConvertFunction will change the value of the 319 // data_ptr. 320 utf8_data_ptr = 321 (llvm::UTF8 *)utf8_data_buffer_sp->GetBytes(); 322 } else { 323 // just copy the pointers - the cast is necessary to make the compiler 324 // happy but this should only happen if we are reading UTF8 data 325 utf8_data_ptr = const_cast<llvm::UTF8 *>( 326 reinterpret_cast<const llvm::UTF8 *>(data_ptr)); 327 utf8_data_end_ptr = const_cast<llvm::UTF8 *>( 328 reinterpret_cast<const llvm::UTF8 *>(data_end_ptr)); 329 } 330 331 const bool escape_non_printables = dump_options.GetEscapeNonPrintables(); 332 lldb_private::formatters::StringPrinter::EscapingHelper escaping_callback; 333 if (escape_non_printables) { 334 if (Language *language = Language::FindPlugin(dump_options.GetLanguage())) 335 escaping_callback = language->GetStringPrinterEscapingHelper( 336 lldb_private::formatters::StringPrinter::GetPrintableElementType:: 337 UTF8); 338 else 339 escaping_callback = 340 lldb_private::formatters::StringPrinter::GetDefaultEscapingHelper( 341 lldb_private::formatters::StringPrinter:: 342 GetPrintableElementType::UTF8); 343 } 344 345 // since we tend to accept partial data (and even partially malformed data) 346 // we might end up with no NULL terminator before the end_ptr hence we need 347 // to take a slower route and ensure we stay within boundaries 348 for (; utf8_data_ptr < utf8_data_end_ptr;) { 349 if (zero_is_terminator && !*utf8_data_ptr) 350 break; 351 352 if (escape_non_printables) { 353 uint8_t *next_data = nullptr; 354 auto printable = 355 escaping_callback(utf8_data_ptr, utf8_data_end_ptr, next_data); 356 auto printable_bytes = printable.GetBytes(); 357 auto printable_size = printable.GetSize(); 358 if (!printable_bytes || !next_data) { 359 // GetPrintable() failed on us - print one byte in a desperate resync 360 // attempt 361 printable_bytes = utf8_data_ptr; 362 printable_size = 1; 363 next_data = utf8_data_ptr + 1; 364 } 365 for (unsigned c = 0; c < printable_size; c++) 366 stream.Printf("%c", *(printable_bytes + c)); 367 utf8_data_ptr = (uint8_t *)next_data; 368 } else { 369 stream.Printf("%c", *utf8_data_ptr); 370 utf8_data_ptr++; 371 } 372 } 373 } 374 if (dump_options.GetQuote() != 0) 375 stream.Printf("%c", dump_options.GetQuote()); 376 if (dump_options.GetSuffixToken() != nullptr) 377 stream.Printf("%s", dump_options.GetSuffixToken()); 378 if (dump_options.GetIsTruncated()) 379 stream.Printf("..."); 380 return true; 381 } 382 383 lldb_private::formatters::StringPrinter::ReadStringAndDumpToStreamOptions:: 384 ReadStringAndDumpToStreamOptions(ValueObject &valobj) 385 : ReadStringAndDumpToStreamOptions() { 386 SetEscapeNonPrintables( 387 valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables()); 388 } 389 390 lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions:: 391 ReadBufferAndDumpToStreamOptions(ValueObject &valobj) 392 : ReadBufferAndDumpToStreamOptions() { 393 SetEscapeNonPrintables( 394 valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables()); 395 } 396 397 lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions:: 398 ReadBufferAndDumpToStreamOptions( 399 const ReadStringAndDumpToStreamOptions &options) 400 : ReadBufferAndDumpToStreamOptions() { 401 SetStream(options.GetStream()); 402 SetPrefixToken(options.GetPrefixToken()); 403 SetSuffixToken(options.GetSuffixToken()); 404 SetQuote(options.GetQuote()); 405 SetEscapeNonPrintables(options.GetEscapeNonPrintables()); 406 SetBinaryZeroIsTerminator(options.GetBinaryZeroIsTerminator()); 407 SetLanguage(options.GetLanguage()); 408 } 409 410 namespace lldb_private { 411 412 namespace formatters { 413 414 template <> 415 bool StringPrinter::ReadStringAndDumpToStream< 416 StringPrinter::StringElementType::ASCII>( 417 const ReadStringAndDumpToStreamOptions &options) { 418 assert(options.GetStream() && "need a Stream to print the string to"); 419 Status my_error; 420 421 ProcessSP process_sp(options.GetProcessSP()); 422 423 if (process_sp.get() == nullptr || options.GetLocation() == 0) 424 return false; 425 426 size_t size; 427 const auto max_size = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); 428 bool is_truncated = false; 429 430 if (options.GetSourceSize() == 0) 431 size = max_size; 432 else if (!options.GetIgnoreMaxLength()) { 433 size = options.GetSourceSize(); 434 if (size > max_size) { 435 size = max_size; 436 is_truncated = true; 437 } 438 } else 439 size = options.GetSourceSize(); 440 441 lldb::DataBufferSP buffer_sp(new DataBufferHeap(size, 0)); 442 443 process_sp->ReadCStringFromMemory( 444 options.GetLocation(), (char *)buffer_sp->GetBytes(), size, my_error); 445 446 if (my_error.Fail()) 447 return false; 448 449 const char *prefix_token = options.GetPrefixToken(); 450 char quote = options.GetQuote(); 451 452 if (prefix_token != nullptr) 453 options.GetStream()->Printf("%s%c", prefix_token, quote); 454 else if (quote != 0) 455 options.GetStream()->Printf("%c", quote); 456 457 uint8_t *data_end = buffer_sp->GetBytes() + buffer_sp->GetByteSize(); 458 459 const bool escape_non_printables = options.GetEscapeNonPrintables(); 460 lldb_private::formatters::StringPrinter::EscapingHelper escaping_callback; 461 if (escape_non_printables) { 462 if (Language *language = Language::FindPlugin(options.GetLanguage())) 463 escaping_callback = language->GetStringPrinterEscapingHelper( 464 lldb_private::formatters::StringPrinter::GetPrintableElementType:: 465 ASCII); 466 else 467 escaping_callback = 468 lldb_private::formatters::StringPrinter::GetDefaultEscapingHelper( 469 lldb_private::formatters::StringPrinter::GetPrintableElementType:: 470 ASCII); 471 } 472 473 // since we tend to accept partial data (and even partially malformed data) 474 // we might end up with no NULL terminator before the end_ptr hence we need 475 // to take a slower route and ensure we stay within boundaries 476 for (uint8_t *data = buffer_sp->GetBytes(); *data && (data < data_end);) { 477 if (escape_non_printables) { 478 uint8_t *next_data = nullptr; 479 auto printable = escaping_callback(data, data_end, next_data); 480 auto printable_bytes = printable.GetBytes(); 481 auto printable_size = printable.GetSize(); 482 if (!printable_bytes || !next_data) { 483 // GetPrintable() failed on us - print one byte in a desperate resync 484 // attempt 485 printable_bytes = data; 486 printable_size = 1; 487 next_data = data + 1; 488 } 489 for (unsigned c = 0; c < printable_size; c++) 490 options.GetStream()->Printf("%c", *(printable_bytes + c)); 491 data = (uint8_t *)next_data; 492 } else { 493 options.GetStream()->Printf("%c", *data); 494 data++; 495 } 496 } 497 498 const char *suffix_token = options.GetSuffixToken(); 499 500 if (suffix_token != nullptr) 501 options.GetStream()->Printf("%c%s", quote, suffix_token); 502 else if (quote != 0) 503 options.GetStream()->Printf("%c", quote); 504 505 if (is_truncated) 506 options.GetStream()->Printf("..."); 507 508 return true; 509 } 510 511 template <typename SourceDataType> 512 static bool ReadUTFBufferAndDumpToStream( 513 const StringPrinter::ReadStringAndDumpToStreamOptions &options, 514 llvm::ConversionResult (*ConvertFunction)(const SourceDataType **, 515 const SourceDataType *, 516 llvm::UTF8 **, llvm::UTF8 *, 517 llvm::ConversionFlags)) { 518 assert(options.GetStream() && "need a Stream to print the string to"); 519 520 if (options.GetLocation() == 0 || 521 options.GetLocation() == LLDB_INVALID_ADDRESS) 522 return false; 523 524 lldb::ProcessSP process_sp(options.GetProcessSP()); 525 526 if (!process_sp) 527 return false; 528 529 const int type_width = sizeof(SourceDataType); 530 const int origin_encoding = 8 * type_width; 531 if (origin_encoding != 8 && origin_encoding != 16 && origin_encoding != 32) 532 return false; 533 // if not UTF8, I need a conversion function to return proper UTF8 534 if (origin_encoding != 8 && !ConvertFunction) 535 return false; 536 537 if (!options.GetStream()) 538 return false; 539 540 uint32_t sourceSize = options.GetSourceSize(); 541 bool needs_zero_terminator = options.GetNeedsZeroTermination(); 542 543 bool is_truncated = false; 544 const auto max_size = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); 545 546 if (!sourceSize) { 547 sourceSize = max_size; 548 needs_zero_terminator = true; 549 } else if (!options.GetIgnoreMaxLength()) { 550 if (sourceSize > max_size) { 551 sourceSize = max_size; 552 is_truncated = true; 553 } 554 } 555 556 const int bufferSPSize = sourceSize * type_width; 557 558 lldb::DataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize, 0)); 559 560 if (!buffer_sp->GetBytes()) 561 return false; 562 563 Status error; 564 char *buffer = reinterpret_cast<char *>(buffer_sp->GetBytes()); 565 566 if (needs_zero_terminator) 567 process_sp->ReadStringFromMemory(options.GetLocation(), buffer, 568 bufferSPSize, error, type_width); 569 else 570 process_sp->ReadMemoryFromInferior(options.GetLocation(), 571 (char *)buffer_sp->GetBytes(), 572 bufferSPSize, error); 573 574 if (error.Fail()) { 575 options.GetStream()->Printf("unable to read data"); 576 return true; 577 } 578 579 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), 580 process_sp->GetAddressByteSize()); 581 582 StringPrinter::ReadBufferAndDumpToStreamOptions dump_options(options); 583 dump_options.SetData(data); 584 dump_options.SetSourceSize(sourceSize); 585 dump_options.SetIsTruncated(is_truncated); 586 587 return DumpUTFBufferToStream(ConvertFunction, dump_options); 588 } 589 590 template <> 591 bool StringPrinter::ReadStringAndDumpToStream< 592 StringPrinter::StringElementType::UTF8>( 593 const ReadStringAndDumpToStreamOptions &options) { 594 return ReadUTFBufferAndDumpToStream<llvm::UTF8>(options, nullptr); 595 } 596 597 template <> 598 bool StringPrinter::ReadStringAndDumpToStream< 599 StringPrinter::StringElementType::UTF16>( 600 const ReadStringAndDumpToStreamOptions &options) { 601 return ReadUTFBufferAndDumpToStream<llvm::UTF16>(options, 602 llvm::ConvertUTF16toUTF8); 603 } 604 605 template <> 606 bool StringPrinter::ReadStringAndDumpToStream< 607 StringPrinter::StringElementType::UTF32>( 608 const ReadStringAndDumpToStreamOptions &options) { 609 return ReadUTFBufferAndDumpToStream<llvm::UTF32>(options, 610 llvm::ConvertUTF32toUTF8); 611 } 612 613 template <> 614 bool StringPrinter::ReadBufferAndDumpToStream< 615 StringPrinter::StringElementType::UTF8>( 616 const ReadBufferAndDumpToStreamOptions &options) { 617 assert(options.GetStream() && "need a Stream to print the string to"); 618 619 return DumpUTFBufferToStream<llvm::UTF8>(nullptr, options); 620 } 621 622 template <> 623 bool StringPrinter::ReadBufferAndDumpToStream< 624 StringPrinter::StringElementType::ASCII>( 625 const ReadBufferAndDumpToStreamOptions &options) { 626 // treat ASCII the same as UTF8 627 // FIXME: can we optimize ASCII some more? 628 return ReadBufferAndDumpToStream<StringElementType::UTF8>(options); 629 } 630 631 template <> 632 bool StringPrinter::ReadBufferAndDumpToStream< 633 StringPrinter::StringElementType::UTF16>( 634 const ReadBufferAndDumpToStreamOptions &options) { 635 assert(options.GetStream() && "need a Stream to print the string to"); 636 637 return DumpUTFBufferToStream(llvm::ConvertUTF16toUTF8, options); 638 } 639 640 template <> 641 bool StringPrinter::ReadBufferAndDumpToStream< 642 StringPrinter::StringElementType::UTF32>( 643 const ReadBufferAndDumpToStreamOptions &options) { 644 assert(options.GetStream() && "need a Stream to print the string to"); 645 646 return DumpUTFBufferToStream(llvm::ConvertUTF32toUTF8, options); 647 } 648 649 } // namespace formatters 650 651 } // namespace lldb_private 652