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