/* * 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 #include #include #include #include #include #ifdef __GLIBCXX__ #include #include #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; #ifdef _WIN32 constexpr size_t kPathMax = 4096; #else constexpr size_t kPathMax = PATH_MAX; #endif } // namespace constexpr char AddressFormatter::bufTemplate[]; constexpr std::array 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[kPathMax]; 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[to_ascii_size_max_decimal]; uint32_t n = to_ascii_decimal(buf, frame.location.line); 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[kPathMax]; 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(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*>(buf); if (sbuf) { return fileno(sbuf->file()); } } { auto sbuf = dynamic_cast*>(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