text: /*
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include <folly/experimental/symbolizer/SymbolizePrinter.h>
  #include <folly/Conv.h>
  #include <folly/Demangle.h>
  #include <folly/FileUtil.h>
  #include <folly/ScopeGuard.h>
  #include <folly/io/IOBuf.h>
  
  #ifdef __GLIBCXX__
  #include <ext/stdio_filebuf.h>
  #include <ext/stdio_sync_filebuf.h>
  #endif
  
  namespace folly {
  namespace symbolizer {
  
  namespace {
  constexpr char kHexChars[] = "0123456789abcdef";
  constexpr auto kAddressColor = SymbolizePrinter::Color::BLUE;
  constexpr auto kFunctionColor = SymbolizePrinter::Color::PURPLE;
  constexpr auto kFileColor = SymbolizePrinter::Color::DEFAULT;
  } // namespace
  
  constexpr char AddressFormatter::bufTemplate[];
  constexpr std::array<const char*, SymbolizePrinter::Color::NUM>
      SymbolizePrinter::kColorMap;
  
  AddressFormatter::AddressFormatter() {
    memcpy(buf_, bufTemplate, sizeof(buf_));
  }
  
  folly::StringPiece AddressFormatter::format(uintptr_t address) {
    // Can't use sprintf, not async-signal-safe
    static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
    char* end = buf_ + sizeof(buf_) - 1 - (16 - 2 * sizeof(uintptr_t));
    char* p = end;
    *p-- = '\0';
    while (address != 0) {
      *p-- = kHexChars[address & 0xf];
      address >>= 4;
    }
  
    return folly::StringPiece(buf_, end);
  }
  
  void SymbolizePrinter::print(const SymbolizedFrame& frame) {
    if (options_ & TERSE) {
      printTerse(frame);
      return;
    }
  
    SCOPE_EXIT {
      color(Color::DEFAULT);
    };
  
    if (!(options_ & NO_FRAME_ADDRESS) && !(options_ & TERSE_FILE_AND_LINE)) {
      color(kAddressColor);
  
      AddressFormatter formatter;
      doPrint(formatter.format(frame.addr));
    }
  
    const char padBuf[] = "                       ";
    folly::StringPiece pad(
        padBuf, sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
  
    color(kFunctionColor);
    if (!frame.found) {
      doPrint(" (not found)");
      return;
    }
  
    if (!(options_ & TERSE_FILE_AND_LINE)) {
      if (!frame.name || frame.name[0] == '\0') {
        doPrint(" (unknown)");
      } else {
        char demangledBuf[2048];
        folly::demangle(frame.name, demangledBuf, sizeof(demangledBuf));
        doPrint(" ");
        doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
      }
    }
  
    if (!(options_ & NO_FILE_AND_LINE)) {
      color(kFileColor);
      char fileBuf[PATH_MAX];
      fileBuf[0] = '\0';
      if (frame.location.hasFileAndLine) {
        frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
        if (!(options_ & TERSE_FILE_AND_LINE)) {
          doPrint("\n");
          doPrint(pad);
        }
        doPrint(fileBuf);
  
        char buf[22];
        uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
        doPrint(":");
        doPrint(StringPiece(buf, n));
      } else {
        if ((options_ & TERSE_FILE_AND_LINE)) {
          doPrint("(unknown)");
        }
      }
  
      if (frame.location.hasMainFile && !(options_ & TERSE_FILE_AND_LINE)) {
        char mainFileBuf[PATH_MAX];
        mainFileBuf[0] = '\0';
        frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
        if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
          doPrint("\n");
          doPrint(pad);
          doPrint("-> ");
          doPrint(mainFileBuf);
        }
      }
    }
  }
  
  void SymbolizePrinter::color(SymbolizePrinter::Color color) {
    if ((options_ & COLOR) == 0 && ((options_ & COLOR_IF_TTY) == 0 || !isTty_)) {
      return;
    }
    if (static_cast<size_t>(color) >= kColorMap.size()) { // catches underflow too
      return;
    }
    doPrint(kColorMap[color]);
  }
  
  void SymbolizePrinter::println(const SymbolizedFrame& frame) {
    print(frame);
    doPrint("\n");
  }
  
  void SymbolizePrinter::printTerse(const SymbolizedFrame& frame) {
    if (frame.found && frame.name && frame.name[0] != '\0') {
      char demangledBuf[2048] = {0};
      demangle(frame.name, demangledBuf, sizeof(demangledBuf));
      doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
    } else {
      // Can't use sprintf, not async-signal-safe
      static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
      char buf[] = "0x0000000000000000";
      char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
      char* p = end;
      *p-- = '\0';
      auto address = frame.addr;
      while (address != 0) {
        *p-- = kHexChars[address & 0xf];
        address >>= 4;
      }
      doPrint(StringPiece(buf, end));
    }
  }
  
  void SymbolizePrinter::println(
      const SymbolizedFrame* frames,
      size_t frameCount) {
    for (size_t i = 0; i < frameCount; ++i) {
      println(frames[i]);
    }
  }
  
  namespace {
  
  int getFD(const std::ios& stream) {
  #if FOLLY_USE_LIBSTDCPP && FOLLY_HAS_RTTI
    std::streambuf* buf = stream.rdbuf();
    using namespace __gnu_cxx;
  
    {
      auto sbuf = dynamic_cast<stdio_sync_filebuf<char>*>(buf);
      if (sbuf) {
        return fileno(sbuf->file());
      }
    }
    {
      auto sbuf = dynamic_cast<stdio_filebuf<char>*>(buf);
      if (sbuf) {
        return sbuf->fd();
      }
    }
  #else
    (void)stream;
  #endif // __GNUC__
    return -1;
  }
  
  bool isColorfulTty(int options, int fd) {
    if ((options & SymbolizePrinter::TERSE) != 0 ||
        (options & SymbolizePrinter::COLOR_IF_TTY) == 0 || fd < 0 ||
        !::isatty(fd)) {
      return false;
    }
    auto term = ::getenv("TERM");
    return !(term == nullptr || term[0] == '\0' || strcmp(term, "dumb") == 0);
  }
  
  } // namespace
  
  OStreamSymbolizePrinter::OStreamSymbolizePrinter(std::ostream& out, int options)
      : SymbolizePrinter(options, isColorfulTty(options, getFD(out))),
        out_(out) {}
  
  void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
    out_ << sp;
  }
  
  FDSymbolizePrinter::FDSymbolizePrinter(int fd, int options, size_t bufferSize)
      : SymbolizePrinter(options, isColorfulTty(options, fd)),
        fd_(fd),
        buffer_(bufferSize ? IOBuf::create(bufferSize) : nullptr) {}
  
  FDSymbolizePrinter::~FDSymbolizePrinter() {
    flush();
  }
  
  void FDSymbolizePrinter::doPrint(StringPiece sp) {
    if (buffer_) {
      if (sp.size() > buffer_->tailroom()) {
        flush();
        writeFull(fd_, sp.data(), sp.size());
      } else {
        memcpy(buffer_->writableTail(), sp.data(), sp.size());
        buffer_->append(sp.size());
      }
    } else {
      writeFull(fd_, sp.data(), sp.size());
    }
  }
  
  void FDSymbolizePrinter::flush() {
    if (buffer_ && !buffer_->empty()) {
      writeFull(fd_, buffer_->data(), buffer_->length());
      buffer_->clear();
    }
  }
  
  FILESymbolizePrinter::FILESymbolizePrinter(FILE* file, int options)
      : SymbolizePrinter(options, isColorfulTty(options, fileno(file))),
        file_(file) {}
  
  void FILESymbolizePrinter::doPrint(StringPiece sp) {
    fwrite(sp.data(), 1, sp.size(), file_);
  }
  
  void StringSymbolizePrinter::doPrint(StringPiece sp) {
    buf_.append(sp.data(), sp.size());
  }
  
  } // namespace symbolizer
  } // namespace folly
  