1 //===-- StringPrinter.cpp ----------------------------------------*- C++ 2 //-*-===// 3 // 4 // The LLVM Compiler Infrastructure 5 // 6 // This file is distributed under the University of Illinois Open Source 7 // License. See LICENSE.TXT for details. 8 // 9 //===----------------------------------------------------------------------===// 10 11 #include "lldb/DataFormatters/StringPrinter.h" 12 13 #include "lldb/Core/Debugger.h" 14 #include "lldb/Core/ValueObject.h" 15 #include "lldb/Target/Language.h" 16 #include "lldb/Target/Process.h" 17 #include "lldb/Target/Target.h" 18 #include "lldb/Utility/Status.h" 19 20 #include "llvm/Support/ConvertUTF.h" 21 22 #include <ctype.h> 23 #include <locale> 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() != 0) 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.reset(new DataBufferHeap(4 * bufferSPSize, 0)); 311 utf8_data_ptr = (llvm::UTF8 *)utf8_data_buffer_sp->GetBytes(); 312 utf8_data_end_ptr = utf8_data_ptr + utf8_data_buffer_sp->GetByteSize(); 313 ConvertFunction(&data_ptr, data_end_ptr, &utf8_data_ptr, 314 utf8_data_end_ptr, llvm::lenientConversion); 315 if (!zero_is_terminator) 316 utf8_data_end_ptr = utf8_data_ptr; 317 // needed because the ConvertFunction will change the value of the 318 // data_ptr. 319 utf8_data_ptr = 320 (llvm::UTF8 *)utf8_data_buffer_sp->GetBytes(); 321 } else { 322 // just copy the pointers - the cast is necessary to make the compiler 323 // happy but this should only happen if we are reading UTF8 data 324 utf8_data_ptr = const_cast<llvm::UTF8 *>( 325 reinterpret_cast<const llvm::UTF8 *>(data_ptr)); 326 utf8_data_end_ptr = const_cast<llvm::UTF8 *>( 327 reinterpret_cast<const llvm::UTF8 *>(data_end_ptr)); 328 } 329 330 const bool escape_non_printables = dump_options.GetEscapeNonPrintables(); 331 lldb_private::formatters::StringPrinter::EscapingHelper escaping_callback; 332 if (escape_non_printables) { 333 if (Language *language = Language::FindPlugin(dump_options.GetLanguage())) 334 escaping_callback = language->GetStringPrinterEscapingHelper( 335 lldb_private::formatters::StringPrinter::GetPrintableElementType:: 336 UTF8); 337 else 338 escaping_callback = 339 lldb_private::formatters::StringPrinter::GetDefaultEscapingHelper( 340 lldb_private::formatters::StringPrinter:: 341 GetPrintableElementType::UTF8); 342 } 343 344 // since we tend to accept partial data (and even partially malformed data) 345 // we might end up with no NULL terminator before the end_ptr hence we need 346 // to take a slower route and ensure we stay within boundaries 347 for (; utf8_data_ptr < utf8_data_end_ptr;) { 348 if (zero_is_terminator && !*utf8_data_ptr) 349 break; 350 351 if (escape_non_printables) { 352 uint8_t *next_data = nullptr; 353 auto printable = 354 escaping_callback(utf8_data_ptr, utf8_data_end_ptr, next_data); 355 auto printable_bytes = printable.GetBytes(); 356 auto printable_size = printable.GetSize(); 357 if (!printable_bytes || !next_data) { 358 // GetPrintable() failed on us - print one byte in a desperate resync 359 // attempt 360 printable_bytes = utf8_data_ptr; 361 printable_size = 1; 362 next_data = utf8_data_ptr + 1; 363 } 364 for (unsigned c = 0; c < printable_size; c++) 365 stream.Printf("%c", *(printable_bytes + c)); 366 utf8_data_ptr = (uint8_t *)next_data; 367 } else { 368 stream.Printf("%c", *utf8_data_ptr); 369 utf8_data_ptr++; 370 } 371 } 372 } 373 if (dump_options.GetQuote() != 0) 374 stream.Printf("%c", dump_options.GetQuote()); 375 if (dump_options.GetSuffixToken() != 0) 376 stream.Printf("%s", dump_options.GetSuffixToken()); 377 if (dump_options.GetIsTruncated()) 378 stream.Printf("..."); 379 return true; 380 } 381 382 lldb_private::formatters::StringPrinter::ReadStringAndDumpToStreamOptions:: 383 ReadStringAndDumpToStreamOptions(ValueObject &valobj) 384 : ReadStringAndDumpToStreamOptions() { 385 SetEscapeNonPrintables( 386 valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables()); 387 } 388 389 lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions:: 390 ReadBufferAndDumpToStreamOptions(ValueObject &valobj) 391 : ReadBufferAndDumpToStreamOptions() { 392 SetEscapeNonPrintables( 393 valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables()); 394 } 395 396 lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions:: 397 ReadBufferAndDumpToStreamOptions( 398 const ReadStringAndDumpToStreamOptions &options) 399 : ReadBufferAndDumpToStreamOptions() { 400 SetStream(options.GetStream()); 401 SetPrefixToken(options.GetPrefixToken()); 402 SetSuffixToken(options.GetSuffixToken()); 403 SetQuote(options.GetQuote()); 404 SetEscapeNonPrintables(options.GetEscapeNonPrintables()); 405 SetBinaryZeroIsTerminator(options.GetBinaryZeroIsTerminator()); 406 SetLanguage(options.GetLanguage()); 407 } 408 409 namespace lldb_private { 410 411 namespace formatters { 412 413 template <> 414 bool StringPrinter::ReadStringAndDumpToStream< 415 StringPrinter::StringElementType::ASCII>( 416 const ReadStringAndDumpToStreamOptions &options) { 417 assert(options.GetStream() && "need a Stream to print the string to"); 418 Status my_error; 419 420 ProcessSP process_sp(options.GetProcessSP()); 421 422 if (process_sp.get() == nullptr || options.GetLocation() == 0) 423 return false; 424 425 size_t size; 426 const auto max_size = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); 427 bool is_truncated = false; 428 429 if (options.GetSourceSize() == 0) 430 size = max_size; 431 else if (!options.GetIgnoreMaxLength()) { 432 size = options.GetSourceSize(); 433 if (size > max_size) { 434 size = max_size; 435 is_truncated = true; 436 } 437 } else 438 size = options.GetSourceSize(); 439 440 lldb::DataBufferSP buffer_sp(new DataBufferHeap(size, 0)); 441 442 process_sp->ReadCStringFromMemory( 443 options.GetLocation(), (char *)buffer_sp->GetBytes(), size, my_error); 444 445 if (my_error.Fail()) 446 return false; 447 448 const char *prefix_token = options.GetPrefixToken(); 449 char quote = options.GetQuote(); 450 451 if (prefix_token != 0) 452 options.GetStream()->Printf("%s%c", prefix_token, quote); 453 else if (quote != 0) 454 options.GetStream()->Printf("%c", quote); 455 456 uint8_t *data_end = buffer_sp->GetBytes() + buffer_sp->GetByteSize(); 457 458 const bool escape_non_printables = options.GetEscapeNonPrintables(); 459 lldb_private::formatters::StringPrinter::EscapingHelper escaping_callback; 460 if (escape_non_printables) { 461 if (Language *language = Language::FindPlugin(options.GetLanguage())) 462 escaping_callback = language->GetStringPrinterEscapingHelper( 463 lldb_private::formatters::StringPrinter::GetPrintableElementType:: 464 ASCII); 465 else 466 escaping_callback = 467 lldb_private::formatters::StringPrinter::GetDefaultEscapingHelper( 468 lldb_private::formatters::StringPrinter::GetPrintableElementType:: 469 ASCII); 470 } 471 472 // since we tend to accept partial data (and even partially malformed data) 473 // we might end up with no NULL terminator before the end_ptr hence we need 474 // to take a slower route and ensure we stay within boundaries 475 for (uint8_t *data = buffer_sp->GetBytes(); *data && (data < data_end);) { 476 if (escape_non_printables) { 477 uint8_t *next_data = nullptr; 478 auto printable = escaping_callback(data, data_end, next_data); 479 auto printable_bytes = printable.GetBytes(); 480 auto printable_size = printable.GetSize(); 481 if (!printable_bytes || !next_data) { 482 // GetPrintable() failed on us - print one byte in a desperate resync 483 // attempt 484 printable_bytes = data; 485 printable_size = 1; 486 next_data = data + 1; 487 } 488 for (unsigned c = 0; c < printable_size; c++) 489 options.GetStream()->Printf("%c", *(printable_bytes + c)); 490 data = (uint8_t *)next_data; 491 } else { 492 options.GetStream()->Printf("%c", *data); 493 data++; 494 } 495 } 496 497 const char *suffix_token = options.GetSuffixToken(); 498 499 if (suffix_token != 0) 500 options.GetStream()->Printf("%c%s", quote, suffix_token); 501 else if (quote != 0) 502 options.GetStream()->Printf("%c", quote); 503 504 if (is_truncated) 505 options.GetStream()->Printf("..."); 506 507 return true; 508 } 509 510 template <typename SourceDataType> 511 static bool ReadUTFBufferAndDumpToStream( 512 const StringPrinter::ReadStringAndDumpToStreamOptions &options, 513 llvm::ConversionResult (*ConvertFunction)(const SourceDataType **, 514 const SourceDataType *, 515 llvm::UTF8 **, llvm::UTF8 *, 516 llvm::ConversionFlags)) { 517 assert(options.GetStream() && "need a Stream to print the string to"); 518 519 if (options.GetLocation() == 0 || 520 options.GetLocation() == LLDB_INVALID_ADDRESS) 521 return false; 522 523 lldb::ProcessSP process_sp(options.GetProcessSP()); 524 525 if (!process_sp) 526 return false; 527 528 const int type_width = sizeof(SourceDataType); 529 const int origin_encoding = 8 * type_width; 530 if (origin_encoding != 8 && origin_encoding != 16 && origin_encoding != 32) 531 return false; 532 // if not UTF8, I need a conversion function to return proper UTF8 533 if (origin_encoding != 8 && !ConvertFunction) 534 return false; 535 536 if (!options.GetStream()) 537 return false; 538 539 uint32_t sourceSize = options.GetSourceSize(); 540 bool needs_zero_terminator = options.GetNeedsZeroTermination(); 541 542 bool is_truncated = false; 543 const auto max_size = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); 544 545 if (!sourceSize) { 546 sourceSize = max_size; 547 needs_zero_terminator = true; 548 } else if (!options.GetIgnoreMaxLength()) { 549 if (sourceSize > max_size) { 550 sourceSize = max_size; 551 is_truncated = true; 552 } 553 } 554 555 const int bufferSPSize = sourceSize * type_width; 556 557 lldb::DataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize, 0)); 558 559 if (!buffer_sp->GetBytes()) 560 return false; 561 562 Status error; 563 char *buffer = reinterpret_cast<char *>(buffer_sp->GetBytes()); 564 565 if (needs_zero_terminator) 566 process_sp->ReadStringFromMemory(options.GetLocation(), buffer, 567 bufferSPSize, error, type_width); 568 else 569 process_sp->ReadMemoryFromInferior(options.GetLocation(), 570 (char *)buffer_sp->GetBytes(), 571 bufferSPSize, error); 572 573 if (error.Fail()) { 574 options.GetStream()->Printf("unable to read data"); 575 return true; 576 } 577 578 DataExtractor data(buffer_sp, process_sp->GetByteOrder(), 579 process_sp->GetAddressByteSize()); 580 581 StringPrinter::ReadBufferAndDumpToStreamOptions dump_options(options); 582 dump_options.SetData(data); 583 dump_options.SetSourceSize(sourceSize); 584 dump_options.SetIsTruncated(is_truncated); 585 586 return DumpUTFBufferToStream(ConvertFunction, dump_options); 587 } 588 589 template <> 590 bool StringPrinter::ReadStringAndDumpToStream< 591 StringPrinter::StringElementType::UTF8>( 592 const ReadStringAndDumpToStreamOptions &options) { 593 return ReadUTFBufferAndDumpToStream<llvm::UTF8>(options, nullptr); 594 } 595 596 template <> 597 bool StringPrinter::ReadStringAndDumpToStream< 598 StringPrinter::StringElementType::UTF16>( 599 const ReadStringAndDumpToStreamOptions &options) { 600 return ReadUTFBufferAndDumpToStream<llvm::UTF16>(options, 601 llvm::ConvertUTF16toUTF8); 602 } 603 604 template <> 605 bool StringPrinter::ReadStringAndDumpToStream< 606 StringPrinter::StringElementType::UTF32>( 607 const ReadStringAndDumpToStreamOptions &options) { 608 return ReadUTFBufferAndDumpToStream<llvm::UTF32>(options, 609 llvm::ConvertUTF32toUTF8); 610 } 611 612 template <> 613 bool StringPrinter::ReadBufferAndDumpToStream< 614 StringPrinter::StringElementType::UTF8>( 615 const ReadBufferAndDumpToStreamOptions &options) { 616 assert(options.GetStream() && "need a Stream to print the string to"); 617 618 return DumpUTFBufferToStream<llvm::UTF8>(nullptr, options); 619 } 620 621 template <> 622 bool StringPrinter::ReadBufferAndDumpToStream< 623 StringPrinter::StringElementType::ASCII>( 624 const ReadBufferAndDumpToStreamOptions &options) { 625 // treat ASCII the same as UTF8 626 // FIXME: can we optimize ASCII some more? 627 return ReadBufferAndDumpToStream<StringElementType::UTF8>(options); 628 } 629 630 template <> 631 bool StringPrinter::ReadBufferAndDumpToStream< 632 StringPrinter::StringElementType::UTF16>( 633 const ReadBufferAndDumpToStreamOptions &options) { 634 assert(options.GetStream() && "need a Stream to print the string to"); 635 636 return DumpUTFBufferToStream(llvm::ConvertUTF16toUTF8, options); 637 } 638 639 template <> 640 bool StringPrinter::ReadBufferAndDumpToStream< 641 StringPrinter::StringElementType::UTF32>( 642 const ReadBufferAndDumpToStreamOptions &options) { 643 assert(options.GetStream() && "need a Stream to print the string to"); 644 645 return DumpUTFBufferToStream(llvm::ConvertUTF32toUTF8, options); 646 } 647 648 } // namespace formatters 649 650 } // namespace lldb_private 651