/*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#include "folly/experimental/symbolizer/Symbolizer.h"
+#include <folly/experimental/symbolizer/Symbolizer.h>
#include <limits.h>
+#include <cstdio>
+#include <iostream>
+#include <map>
-#include "folly/Conv.h"
-#include "folly/FileUtil.h"
-#include "folly/String.h"
+#ifdef __GNUC__
+#include <ext/stdio_filebuf.h>
+#include <ext/stdio_sync_filebuf.h>
+#endif
+
+#include <folly/Conv.h>
+#include <folly/FileUtil.h>
+#include <folly/ScopeGuard.h>
+#include <folly/String.h>
+
+#include <folly/experimental/symbolizer/Elf.h>
+#include <folly/experimental/symbolizer/Dwarf.h>
+#include <folly/experimental/symbolizer/LineReader.h>
-#include "folly/experimental/symbolizer/Elf.h"
-#include "folly/experimental/symbolizer/Dwarf.h"
-#include "folly/experimental/symbolizer/LineReader.h"
namespace folly {
namespace symbolizer {
return true;
}
+ElfCache* defaultElfCache() {
+ static constexpr size_t defaultCapacity = 500;
+ static ElfCache cache(defaultCapacity);
+ return &cache;
+}
+
} // namespace
+void SymbolizedFrame::set(const std::shared_ptr<ElfFile>& file,
+ uintptr_t address) {
+ clear();
+ found = true;
+
+ address += file->getBaseAddress();
+ auto sym = file->getDefinitionByAddress(address);
+ if (!sym.first) {
+ return;
+ }
+
+ file_ = file;
+ name = file->getSymbolName(sym);
+
+ Dwarf(file.get()).findAddress(address, location);
+}
+
+
+Symbolizer::Symbolizer(ElfCacheBase* cache)
+ : cache_(cache ?: defaultElfCache()) {
+}
+
void Symbolizer::symbolize(const uintptr_t* addresses,
SymbolizedFrame* frames,
size_t addressCount) {
auto& frame = frames[i];
if (!frame.found) {
++remaining;
- frame.name.clear();
- frame.location = Dwarf::LocationInfo();
+ frame.clear();
}
}
char buf[PATH_MAX + 100]; // Long enough for any line
LineReader reader(fd, buf, sizeof(buf));
- char fileNameBuf[PATH_MAX];
-
while (remaining != 0) {
StringPiece line;
if (reader.readLine(line) != LineReader::kReading) {
}
bool first = true;
- ElfFile* elfFile = nullptr;
+ std::shared_ptr<ElfFile> elfFile;
// See if any addresses are here
for (size_t i = 0; i < addressCount; ++i) {
// Open the file on first use
if (first) {
first = false;
- if (fileCount_ < kMaxFiles &&
- !fileName.empty() &&
- fileName.size() < sizeof(fileNameBuf)) {
- memcpy(fileNameBuf, fileName.data(), fileName.size());
- fileNameBuf[fileName.size()] = '\0';
- auto& f = files_[fileCount_++];
- if (f.openNoThrow(fileNameBuf) != -1) {
- elfFile = &f;
- }
- }
+ elfFile = cache_->getFile(fileName);
}
if (!elfFile) {
}
// Undo relocation
- uintptr_t fileAddress = address - from + elfFile->getBaseAddress();
- auto sym = elfFile->getDefinitionByAddress(fileAddress);
- if (!sym.first) {
- continue;
- }
- auto name = elfFile->getSymbolName(sym);
- if (name) {
- frame.name = name;
- }
-
- Dwarf(elfFile).findAddress(fileAddress, frame.location);
+ frame.set(elfFile, address - from);
}
}
namespace {
const char kHexChars[] = "0123456789abcdef";
+const SymbolizePrinter::Color kAddressColor = SymbolizePrinter::Color::BLUE;
+const SymbolizePrinter::Color kFunctionColor = SymbolizePrinter::Color::PURPLE;
+const SymbolizePrinter::Color kFileColor = SymbolizePrinter::Color::DEFAULT;
} // namespace
-void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
- if (options_ & TERSE) {
- printTerse(address, frame);
- return;
- }
+constexpr char AddressFormatter::bufTemplate[];
+
+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 buf[] = " @ 0000000000000000";
- char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
- const char padBuf[] = " ";
- folly::StringPiece pad(padBuf,
- sizeof(padBuf) - 1 - (16 - 2 * sizeof(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;
}
- doPrint(folly::StringPiece(buf, end));
- char mangledBuf[1024];
+ return folly::StringPiece(buf_, end);
+}
+
+void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
+ if (options_ & TERSE) {
+ printTerse(address, frame);
+ return;
+ }
+
+ SCOPE_EXIT { color(Color::DEFAULT); };
+
+ color(kAddressColor);
+
+ AddressFormatter formatter;
+ doPrint(formatter.format(address));
+
+ const char padBuf[] = " ";
+ folly::StringPiece pad(padBuf,
+ sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
+
+ color(kFunctionColor);
if (!frame.found) {
- doPrint(" (not found)\n");
+ doPrint(" (not found)");
return;
}
- if (frame.name.empty()) {
- doPrint(" (unknown)\n");
- } else if (frame.name.size() >= sizeof(mangledBuf)) {
- doPrint(" ");
- doPrint(frame.name);
+ if (!frame.name || frame.name[0] == '\0') {
+ doPrint(" (unknown)");
} else {
- memcpy(mangledBuf, frame.name.data(), frame.name.size());
- mangledBuf[frame.name.size()] = '\0';
-
char demangledBuf[1024];
- demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
+ demangle(frame.name, demangledBuf, sizeof(demangledBuf));
doPrint(" ");
- doPrint(demangledBuf);
+ 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) {
}
}
+namespace {
+
+const std::map<SymbolizePrinter::Color, std::string> kColorMap = {
+ { SymbolizePrinter::Color::DEFAULT, "\x1B[0m" },
+ { SymbolizePrinter::Color::RED, "\x1B[31m" },
+ { SymbolizePrinter::Color::GREEN, "\x1B[32m" },
+ { SymbolizePrinter::Color::YELLOW, "\x1B[33m" },
+ { SymbolizePrinter::Color::BLUE, "\x1B[34m" },
+ { SymbolizePrinter::Color::CYAN, "\x1B[36m" },
+ { SymbolizePrinter::Color::WHITE, "\x1B[37m" },
+ { SymbolizePrinter::Color::PURPLE, "\x1B[35m" },
+};
+
+}
+
+void SymbolizePrinter::color(SymbolizePrinter::Color color) {
+ if ((options_ & COLOR) == 0 &&
+ ((options_ & COLOR_IF_TTY) == 0 || !isTty_)) {
+ return;
+ }
+ auto it = kColorMap.find(color);
+ if (it == kColorMap.end()) {
+ return;
+ }
+ doPrint(it->second);
+}
+
void SymbolizePrinter::println(uintptr_t address,
const SymbolizedFrame& frame) {
print(address, frame);
void SymbolizePrinter::printTerse(uintptr_t address,
const SymbolizedFrame& frame) {
- if (frame.found) {
- char mangledBuf[1024];
- memcpy(mangledBuf, frame.name.data(), frame.name.size());
- mangledBuf[frame.name.size()] = '\0';
-
- char demangledBuf[1024];
- demangle(mangledBuf, demangledBuf, sizeof(demangledBuf));
- doPrint(demangledBuf);
+ if (frame.found && frame.name && frame.name[0] != '\0') {
+ char demangledBuf[1024] = {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?");
}
}
+namespace {
+
+int getFD(const std::ios& stream) {
+#ifdef __GNUC__
+ 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();
+ }
+ }
+#endif // __GNUC__
+ return -1;
+}
+
+bool isTty(int options, int fd) {
+ return ((options & SymbolizePrinter::TERSE) == 0 &&
+ (options & SymbolizePrinter::COLOR_IF_TTY) != 0 &&
+ fd >= 0 && ::isatty(fd));
+}
+
+} // anonymous namespace
+
+OStreamSymbolizePrinter::OStreamSymbolizePrinter(std::ostream& out, int options)
+ : SymbolizePrinter(options, isTty(options, getFD(out))),
+ out_(out) {
+}
+
void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
out_ << sp;
}
+FDSymbolizePrinter::FDSymbolizePrinter(int fd, int options, size_t bufferSize)
+ : SymbolizePrinter(options, isTty(options, fd)),
+ fd_(fd),
+ buffer_(bufferSize ? IOBuf::create(bufferSize) : nullptr) {
+}
+
+FDSymbolizePrinter::~FDSymbolizePrinter() {
+ flush();
+}
+
void FDSymbolizePrinter::doPrint(StringPiece sp) {
- writeFull(fd_, sp.data(), sp.size());
+ 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, isTty(options, fileno(file))),
+ file_(file) {
}
void FILESymbolizePrinter::doPrint(StringPiece sp) {