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