brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.5 KiB · 2571784 Raw
561 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/DataFormatters/StringPrinter.h"10 11#include "lldb/Core/Debugger.h"12#include "lldb/Target/Language.h"13#include "lldb/Target/Process.h"14#include "lldb/Target/Target.h"15#include "lldb/Utility/Status.h"16#include "lldb/ValueObject/ValueObject.h"17 18#include "llvm/ADT/StringExtras.h"19#include "llvm/Support/ConvertUTF.h"20 21#include <cctype>22#include <locale>23#include <memory>24 25using namespace lldb;26using namespace lldb_private;27using namespace lldb_private::formatters;28using GetPrintableElementType = StringPrinter::GetPrintableElementType;29using StringElementType = StringPrinter::StringElementType;30 31/// DecodedCharBuffer stores the decoded contents of a single character. It32/// avoids managing memory on the heap by copying decoded bytes into an in-line33/// buffer.34class DecodedCharBuffer {35public:36  DecodedCharBuffer(std::nullptr_t) {}37 38  DecodedCharBuffer(const uint8_t *bytes, size_t size) : m_size(size) {39    if (size > MaxLength)40      llvm_unreachable("unsupported length");41    memcpy(m_data, bytes, size);42  }43 44  DecodedCharBuffer(const char *bytes, size_t size)45      : DecodedCharBuffer(reinterpret_cast<const uint8_t *>(bytes), size) {}46 47  const uint8_t *GetBytes() const { return m_data; }48 49  size_t GetSize() const { return m_size; }50 51private:52  static constexpr unsigned MaxLength = 16;53 54  size_t m_size = 0;55  uint8_t m_data[MaxLength] = {0};56};57 58using EscapingHelper =59    std::function<DecodedCharBuffer(uint8_t *, uint8_t *, uint8_t *&)>;60 61// we define this for all values of type but only implement it for those we62// care about that's good because we get linker errors for any unsupported type63template <StringElementType type>64static DecodedCharBuffer65GetPrintableImpl(uint8_t *buffer, uint8_t *buffer_end, uint8_t *&next,66                 StringPrinter::EscapeStyle escape_style);67 68// Mimic isprint() for Unicode codepoints.69static bool isprint32(char32_t codepoint) {70  if (codepoint <= 0x1F || codepoint == 0x7F) // C071  {72    return false;73  }74  if (codepoint >= 0x80 && codepoint <= 0x9F) // C175  {76    return false;77  }78  if (codepoint == 0x2028 || codepoint == 0x2029) // line/paragraph separators79  {80    return false;81  }82  if (codepoint == 0x200E || codepoint == 0x200F ||83      (codepoint >= 0x202A &&84       codepoint <= 0x202E)) // bidirectional text control85  {86    return false;87  }88  if (codepoint >= 0xFFF9 &&89      codepoint <= 0xFFFF) // interlinears and generally specials90  {91    return false;92  }93  return true;94}95 96DecodedCharBuffer attemptASCIIEscape(llvm::UTF32 c,97                                     StringPrinter::EscapeStyle escape_style) {98  const bool is_swift_escape_style =99      escape_style == StringPrinter::EscapeStyle::Swift;100  switch (c) {101  case 0:102    return {"\\0", 2};103  case '\a':104    return {"\\a", 2};105  case '\b':106    if (is_swift_escape_style)107      return nullptr;108    return {"\\b", 2};109  case '\f':110    if (is_swift_escape_style)111      return nullptr;112    return {"\\f", 2};113  case '\n':114    return {"\\n", 2};115  case '\r':116    return {"\\r", 2};117  case '\t':118    return {"\\t", 2};119  case '\v':120    if (is_swift_escape_style)121      return nullptr;122    return {"\\v", 2};123  case '\"':124    return {"\\\"", 2};125  case '\'':126    if (is_swift_escape_style)127      return {"\\'", 2};128    return nullptr;129  case '\\':130    return {"\\\\", 2};131  }132  return nullptr;133}134 135template <>136DecodedCharBuffer GetPrintableImpl<StringElementType::ASCII>(137    uint8_t *buffer, uint8_t *buffer_end, uint8_t *&next,138    StringPrinter::EscapeStyle escape_style) {139  // The ASCII helper always advances 1 byte at a time.140  next = buffer + 1;141 142  DecodedCharBuffer retval = attemptASCIIEscape(*buffer, escape_style);143  if (retval.GetSize())144    return retval;145 146  // Use llvm's locale-independent isPrint(char), instead of the libc147  // implementation which may give different results on different platforms.148  if (llvm::isPrint(*buffer))149    return {buffer, 1};150 151  unsigned escaped_len;152  constexpr unsigned max_buffer_size = 7;153  uint8_t data[max_buffer_size];154  switch (escape_style) {155  case StringPrinter::EscapeStyle::CXX:156    // Prints 4 characters, then a \0 terminator.157    escaped_len = snprintf((char *)data, max_buffer_size, "\\x%02x", *buffer);158    break;159  case StringPrinter::EscapeStyle::Swift:160    // Prints up to 6 characters, then a \0 terminator.161    escaped_len = snprintf((char *)data, max_buffer_size, "\\u{%x}", *buffer);162    break;163  }164  lldbassert(escaped_len > 0 && "unknown string escape style");165  return {data, escaped_len};166}167 168template <>169DecodedCharBuffer GetPrintableImpl<StringElementType::UTF8>(170    uint8_t *buffer, uint8_t *buffer_end, uint8_t *&next,171    StringPrinter::EscapeStyle escape_style) {172  // If the utf8 encoded length is invalid (i.e., not in the closed interval173  // [1;4]), or if there aren't enough bytes to print, or if the subsequence174  // isn't valid utf8, fall back to printing an ASCII-escaped subsequence.175  if (!llvm::isLegalUTF8Sequence(buffer, buffer_end))176    return GetPrintableImpl<StringElementType::ASCII>(buffer, buffer_end, next,177                                                      escape_style);178 179  // Convert the valid utf8 sequence to a utf32 codepoint. This cannot fail.180  llvm::UTF32 codepoint = 0;181  const llvm::UTF8 *buffer_for_conversion = buffer;182  llvm::ConversionResult result = llvm::convertUTF8Sequence(183      &buffer_for_conversion, buffer_end, &codepoint, llvm::strictConversion);184  assert(result == llvm::conversionOK &&185         "Failed to convert legal utf8 sequence");186  UNUSED_IF_ASSERT_DISABLED(result);187 188  // The UTF8 helper always advances by the utf8 encoded length.189  const unsigned utf8_encoded_len = buffer_for_conversion - buffer;190  next = buffer + utf8_encoded_len;191 192  DecodedCharBuffer retval = attemptASCIIEscape(codepoint, escape_style);193  if (retval.GetSize())194    return retval;195  if (isprint32(codepoint))196    return {buffer, utf8_encoded_len};197 198  unsigned escaped_len;199  constexpr unsigned max_buffer_size = 13;200  uint8_t data[max_buffer_size];201  switch (escape_style) {202  case StringPrinter::EscapeStyle::CXX:203    // Prints 10 characters, then a \0 terminator.204    escaped_len = snprintf((char *)data, max_buffer_size, "\\U%08x", codepoint);205    break;206  case StringPrinter::EscapeStyle::Swift:207    // Prints up to 12 characters, then a \0 terminator.208    escaped_len = snprintf((char *)data, max_buffer_size, "\\u{%x}", codepoint);209    break;210  }211  lldbassert(escaped_len > 0 && "unknown string escape style");212  return {data, escaped_len};213}214 215// Given a sequence of bytes, this function returns: a sequence of bytes to216// actually print out + a length the following unscanned position of the buffer217// is in next218static DecodedCharBuffer GetPrintable(StringElementType type, uint8_t *buffer,219                                      uint8_t *buffer_end, uint8_t *&next,220                                      StringPrinter::EscapeStyle escape_style) {221  if (!buffer || buffer >= buffer_end)222    return {nullptr};223 224  switch (type) {225  case StringElementType::ASCII:226    return GetPrintableImpl<StringElementType::ASCII>(buffer, buffer_end, next,227                                                      escape_style);228  case StringElementType::UTF8:229    return GetPrintableImpl<StringElementType::UTF8>(buffer, buffer_end, next,230                                                     escape_style);231  default:232    return {nullptr};233  }234}235 236static EscapingHelper237GetDefaultEscapingHelper(GetPrintableElementType elem_type,238                         StringPrinter::EscapeStyle escape_style) {239  switch (elem_type) {240  case GetPrintableElementType::UTF8:241  case GetPrintableElementType::ASCII:242    return [escape_style, elem_type](uint8_t *buffer, uint8_t *buffer_end,243                                     uint8_t *&next) -> DecodedCharBuffer {244      return GetPrintable(elem_type == GetPrintableElementType::UTF8245                              ? StringElementType::UTF8246                              : StringElementType::ASCII,247                          buffer, buffer_end, next, escape_style);248    };249  }250  llvm_unreachable("bad element type");251}252 253/// Read a string encoded in accordance with \tparam SourceDataType from a254/// host-side LLDB buffer, then pretty-print it to a stream using \p style.255template <typename SourceDataType>256static bool DumpEncodedBufferToStream(257    GetPrintableElementType style,258    llvm::ConversionResult (*ConvertFunction)(const SourceDataType **,259                                              const SourceDataType *,260                                              llvm::UTF8 **, llvm::UTF8 *,261                                              llvm::ConversionFlags),262    const StringPrinter::ReadBufferAndDumpToStreamOptions &dump_options) {263  assert(dump_options.GetStream() && "need a Stream to print the string to");264  Stream &stream(*dump_options.GetStream());265  if (dump_options.GetPrefixToken() != nullptr)266    stream.Printf("%s", dump_options.GetPrefixToken());267  if (dump_options.GetQuote() != 0)268    stream.Printf("%c", dump_options.GetQuote());269  auto data(dump_options.GetData());270  auto source_size(dump_options.GetSourceSize());271  if (data.GetByteSize() && data.GetDataStart() && data.GetDataEnd()) {272    const int bufferSPSize = data.GetByteSize();273    if (dump_options.GetSourceSize() == 0) {274      const int origin_encoding = 8 * sizeof(SourceDataType);275      source_size = bufferSPSize / (origin_encoding / 4);276    }277 278    const SourceDataType *data_ptr =279        (const SourceDataType *)data.GetDataStart();280    const SourceDataType *data_end_ptr = data_ptr + source_size;281 282    const bool zero_is_terminator = dump_options.GetBinaryZeroIsTerminator();283 284    if (zero_is_terminator) {285      while (data_ptr < data_end_ptr) {286        if (!*data_ptr) {287          data_end_ptr = data_ptr;288          break;289        }290        data_ptr++;291      }292 293      data_ptr = (const SourceDataType *)data.GetDataStart();294    }295 296    lldb::WritableDataBufferSP utf8_data_buffer_sp;297    llvm::UTF8 *utf8_data_ptr = nullptr;298    llvm::UTF8 *utf8_data_end_ptr = nullptr;299 300    if (ConvertFunction) {301      utf8_data_buffer_sp =302          std::make_shared<DataBufferHeap>(4 * bufferSPSize, 0);303      utf8_data_ptr = (llvm::UTF8 *)utf8_data_buffer_sp->GetBytes();304      utf8_data_end_ptr = utf8_data_ptr + utf8_data_buffer_sp->GetByteSize();305      ConvertFunction(&data_ptr, data_end_ptr, &utf8_data_ptr,306                      utf8_data_end_ptr, llvm::lenientConversion);307      if (!zero_is_terminator)308        utf8_data_end_ptr = utf8_data_ptr;309      // needed because the ConvertFunction will change the value of the310      // data_ptr.311      utf8_data_ptr =312          (llvm::UTF8 *)utf8_data_buffer_sp->GetBytes();313    } else {314      // just copy the pointers - the cast is necessary to make the compiler315      // happy but this should only happen if we are reading UTF8 data316      utf8_data_ptr = const_cast<llvm::UTF8 *>(317          reinterpret_cast<const llvm::UTF8 *>(data_ptr));318      utf8_data_end_ptr = const_cast<llvm::UTF8 *>(319          reinterpret_cast<const llvm::UTF8 *>(data_end_ptr));320    }321 322    const bool escape_non_printables = dump_options.GetEscapeNonPrintables();323    EscapingHelper escaping_callback;324    if (escape_non_printables)325      escaping_callback =326          GetDefaultEscapingHelper(style, dump_options.GetEscapeStyle());327 328    // since we tend to accept partial data (and even partially malformed data)329    // we might end up with no NULL terminator before the end_ptr hence we need330    // to take a slower route and ensure we stay within boundaries331    for (; utf8_data_ptr < utf8_data_end_ptr;) {332      if (zero_is_terminator && !*utf8_data_ptr)333        break;334 335      if (escape_non_printables) {336        uint8_t *next_data = nullptr;337        auto printable =338            escaping_callback(utf8_data_ptr, utf8_data_end_ptr, next_data);339        auto printable_bytes = printable.GetBytes();340        auto printable_size = printable.GetSize();341 342        // We failed to figure out how to print this string.343        if (!printable_bytes || !next_data)344          return false;345 346        for (unsigned c = 0; c < printable_size; c++)347          stream.Printf("%c", *(printable_bytes + c));348        utf8_data_ptr = (uint8_t *)next_data;349      } else {350        stream.Printf("%c", *utf8_data_ptr);351        utf8_data_ptr++;352      }353    }354  }355  if (dump_options.GetQuote() != 0)356    stream.Printf("%c", dump_options.GetQuote());357  if (dump_options.GetSuffixToken() != nullptr)358    stream.Printf("%s", dump_options.GetSuffixToken());359  if (dump_options.GetIsTruncated())360    stream.Printf("...");361  return true;362}363 364lldb_private::formatters::StringPrinter::ReadStringAndDumpToStreamOptions::365    ReadStringAndDumpToStreamOptions(ValueObject &valobj)366    : ReadStringAndDumpToStreamOptions() {367  SetEscapeNonPrintables(368      valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables());369}370 371lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions::372    ReadBufferAndDumpToStreamOptions(ValueObject &valobj)373    : ReadBufferAndDumpToStreamOptions() {374  SetEscapeNonPrintables(375      valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables());376}377 378lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions::379    ReadBufferAndDumpToStreamOptions(380        const ReadStringAndDumpToStreamOptions &options)381    : ReadBufferAndDumpToStreamOptions() {382  SetStream(options.GetStream());383  SetPrefixToken(options.GetPrefixToken());384  SetSuffixToken(options.GetSuffixToken());385  SetQuote(options.GetQuote());386  SetEscapeNonPrintables(options.GetEscapeNonPrintables());387  SetBinaryZeroIsTerminator(options.GetBinaryZeroIsTerminator());388  SetEscapeStyle(options.GetEscapeStyle());389}390 391namespace lldb_private {392 393namespace formatters {394 395template <typename SourceDataType>396static bool ReadEncodedBufferAndDumpToStream(397    StringElementType elem_type,398    const StringPrinter::ReadStringAndDumpToStreamOptions &options,399    llvm::ConversionResult (*ConvertFunction)(const SourceDataType **,400                                              const SourceDataType *,401                                              llvm::UTF8 **, llvm::UTF8 *,402                                              llvm::ConversionFlags)) {403  assert(options.GetStream() && "need a Stream to print the string to");404  if (!options.GetStream())405    return false;406 407  if (options.GetLocation() == 0 ||408      options.GetLocation() == LLDB_INVALID_ADDRESS)409    return false;410 411  lldb::TargetSP target_sp = options.GetTargetSP();412  if (!target_sp)413    return false;414 415  constexpr int type_width = sizeof(SourceDataType);416  constexpr int origin_encoding = 8 * type_width;417  if (origin_encoding != 8 && origin_encoding != 16 && origin_encoding != 32)418    return false;419  // If not UTF8 or ASCII, conversion to UTF8 is necessary.420  if (origin_encoding != 8 && !ConvertFunction)421    return false;422 423  bool needs_zero_terminator = options.GetNeedsZeroTermination();424 425  bool is_truncated = false;426  const auto max_size = target_sp->GetMaximumSizeOfStringSummary();427 428  uint32_t sourceSize;429  if (elem_type == StringElementType::ASCII && !options.GetSourceSize()) {430    // FIXME: The NSString formatter sets HasSourceSize(true) when the size is431    // actually unknown, as well as SetBinaryZeroIsTerminator(false). IIUC the432    // C++ formatter also sets SetBinaryZeroIsTerminator(false) when it doesn't433    // mean to. I don't see how this makes sense: we should fix the formatters.434    //435    // Until then, the behavior that's expected for ASCII strings with unknown436    // lengths is to read up to the max size and then null-terminate. Do that.437    sourceSize = max_size;438    needs_zero_terminator = true;439  } else if (options.HasSourceSize()) {440    sourceSize = options.GetSourceSize();441    if (!options.GetIgnoreMaxLength()) {442      if (sourceSize > max_size) {443        sourceSize = max_size;444        is_truncated = true;445      }446    }447  } else {448    sourceSize = max_size;449    needs_zero_terminator = true;450  }451 452  const int bufferSPSize = sourceSize * type_width;453  lldb::WritableDataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize, 0));454 455  // Check if we got bytes. We never get any bytes if we have an empty456  // string, but we still continue so that we end up actually printing457  // an empty string ("").458  if (sourceSize != 0 && !buffer_sp->GetBytes())459    return false;460 461  Status error;462  char *buffer = reinterpret_cast<char *>(buffer_sp->GetBytes());463 464  if (elem_type == StringElementType::ASCII)465    target_sp->ReadCStringFromMemory(options.GetLocation(), buffer,466                                      bufferSPSize, error);467  else if (needs_zero_terminator)468    target_sp->ReadStringFromMemory(options.GetLocation(), buffer,469                                     bufferSPSize, error, type_width);470  else471    target_sp->ReadMemory(options.GetLocation(), buffer, bufferSPSize, error);472  if (error.Fail()) {473    options.GetStream()->Printf("unable to read data");474    return true;475  }476 477  StringPrinter::ReadBufferAndDumpToStreamOptions dump_options(options);478  dump_options.SetData(479      DataExtractor(buffer_sp, target_sp->GetArchitecture().GetByteOrder(),480                    target_sp->GetArchitecture().GetAddressByteSize()));481  dump_options.SetSourceSize(sourceSize);482  dump_options.SetIsTruncated(is_truncated);483  dump_options.SetNeedsZeroTermination(needs_zero_terminator);484  if (needs_zero_terminator)485    dump_options.SetBinaryZeroIsTerminator(true);486 487  GetPrintableElementType print_style = (elem_type == StringElementType::ASCII)488                                            ? GetPrintableElementType::ASCII489                                            : GetPrintableElementType::UTF8;490  return DumpEncodedBufferToStream(print_style, ConvertFunction, dump_options);491}492 493template <>494bool StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF8>(495    const ReadStringAndDumpToStreamOptions &options) {496  return ReadEncodedBufferAndDumpToStream<llvm::UTF8>(StringElementType::UTF8,497                                                      options, nullptr);498}499 500template <>501bool StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF16>(502    const ReadStringAndDumpToStreamOptions &options) {503  return ReadEncodedBufferAndDumpToStream<llvm::UTF16>(504      StringElementType::UTF16, options, llvm::ConvertUTF16toUTF8);505}506 507template <>508bool StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF32>(509    const ReadStringAndDumpToStreamOptions &options) {510  return ReadEncodedBufferAndDumpToStream<llvm::UTF32>(511      StringElementType::UTF32, options, llvm::ConvertUTF32toUTF8);512}513 514template <>515bool StringPrinter::ReadStringAndDumpToStream<StringElementType::ASCII>(516    const ReadStringAndDumpToStreamOptions &options) {517  return ReadEncodedBufferAndDumpToStream<char>(StringElementType::ASCII,518                                                options, nullptr);519}520 521template <>522bool StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF8>(523    const ReadBufferAndDumpToStreamOptions &options) {524  return DumpEncodedBufferToStream<llvm::UTF8>(GetPrintableElementType::UTF8,525                                               nullptr, options);526}527 528template <>529bool StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF16>(530    const ReadBufferAndDumpToStreamOptions &options) {531  return DumpEncodedBufferToStream(GetPrintableElementType::UTF8,532                                   llvm::ConvertUTF16toUTF8, options);533}534 535template <>536bool StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF32>(537    const ReadBufferAndDumpToStreamOptions &options) {538  return DumpEncodedBufferToStream(GetPrintableElementType::UTF8,539                                   llvm::ConvertUTF32toUTF8, options);540}541 542template <>543bool StringPrinter::ReadBufferAndDumpToStream<StringElementType::ASCII>(544    const ReadBufferAndDumpToStreamOptions &options) {545  // Treat ASCII the same as UTF8.546  //547  // FIXME: This is probably not the right thing to do (well, it's debatable).548  // If an ASCII-encoded string happens to contain a sequence of invalid bytes549  // that forms a valid UTF8 character, we'll print out that character. This is550  // good if you're playing fast and loose with encodings (probably good for551  // std::string users), but maybe not so good if you care about your string552  // formatter respecting the semantics of your selected string encoding. In553  // the latter case you'd want to see the character byte sequence ('\x..'), not554  // the UTF8 character itself.555  return ReadBufferAndDumpToStream<StringElementType::UTF8>(options);556}557 558} // namespace formatters559 560} // namespace lldb_private561