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