1 //===-- StringPrinter.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 "lldb/DataFormatters/StringPrinter.h" 10 11 #include "lldb/Core/Debugger.h" 12 #include "lldb/Core/ValueObject.h" 13 #include "lldb/Target/Language.h" 14 #include "lldb/Target/Process.h" 15 #include "lldb/Target/Target.h" 16 #include "lldb/Utility/Status.h" 17 18 #include "llvm/Support/ConvertUTF.h" 19 20 #include <ctype.h> 21 #include <locale> 22 #include <memory> 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() != nullptr) 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 = 310 std::make_shared<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() != nullptr) 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 != nullptr) 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 != nullptr) 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