2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/experimental/symbolizer/Symbolizer.h>
27 #include <ext/stdio_filebuf.h>
28 #include <ext/stdio_sync_filebuf.h>
31 #include <folly/Conv.h>
32 #include <folly/FileUtil.h>
33 #include <folly/Memory.h>
34 #include <folly/ScopeGuard.h>
35 #include <folly/String.h>
37 #include <folly/experimental/symbolizer/Elf.h>
38 #include <folly/experimental/symbolizer/Dwarf.h>
39 #include <folly/experimental/symbolizer/LineReader.h>
43 * This is declared in `link.h' on Linux platforms, but apparently not on the
44 * Mac version of the file. It's harmless to declare again, in any case.
46 * Note that declaring it with `extern "C"` results in linkage conflicts.
48 extern struct r_debug _r_debug;
51 namespace symbolizer {
55 ElfCache* defaultElfCache() {
56 static constexpr size_t defaultCapacity = 500;
57 static auto cache = new ElfCache(defaultCapacity);
63 void SymbolizedFrame::set(const std::shared_ptr<ElfFile>& file,
65 Dwarf::LocationInfoMode mode) {
69 address += file->getBaseAddress();
70 auto sym = file->getDefinitionByAddress(address);
76 name = file->getSymbolName(sym);
78 Dwarf(file.get()).findAddress(address, location, mode);
81 Symbolizer::Symbolizer(ElfCacheBase* cache, Dwarf::LocationInfoMode mode)
82 : cache_(cache ? cache : defaultElfCache()), mode_(mode) {
85 void Symbolizer::symbolize(const uintptr_t* addresses,
86 SymbolizedFrame* frames,
89 for (size_t i = 0; i < addrCount; ++i) {
90 auto& frame = frames[i];
97 if (remaining == 0) { // we're done
101 if (_r_debug.r_version != 1) {
105 char selfPath[PATH_MAX + 8];
107 if ((selfSize = readlink("/proc/self/exe", selfPath, PATH_MAX + 1)) == -1) {
108 // Something has gone terribly wrong.
111 selfPath[selfSize] = '\0';
113 for (auto lmap = _r_debug.r_map;
114 lmap != nullptr && remaining != 0;
115 lmap = lmap->l_next) {
116 // The empty string is used in place of the filename for the link_map
117 // corresponding to the running executable. Additionally, the `l_addr' is
118 // 0 and the link_map appears to be first in the list---but none of this
119 // behavior appears to be documented, so checking for the empty string is
120 // as good as anything.
121 auto const objPath = lmap->l_name[0] != '\0' ? lmap->l_name : selfPath;
123 auto const elfFile = cache_->getFile(objPath);
128 // Get the address at which the object is loaded. We have to use the ELF
129 // header for the running executable, since its `l_addr' is zero, but we
130 // should use `l_addr' for everything else---in particular, if the object
131 // is position-independent, getBaseAddress() (which is p_vaddr) will be 0.
132 auto const base = lmap->l_addr != 0
134 : elfFile->getBaseAddress();
136 for (size_t i = 0; i < addrCount && remaining != 0; ++i) {
137 auto& frame = frames[i];
142 auto const addr = addresses[i];
143 // Get the unrelocated, ELF-relative address.
144 auto const adjusted = addr - base;
146 if (elfFile->getSectionContainingAddress(adjusted)) {
147 frame.set(elfFile, adjusted, mode_);
155 constexpr char kHexChars[] = "0123456789abcdef";
156 constexpr auto kAddressColor = SymbolizePrinter::Color::BLUE;
157 constexpr auto kFunctionColor = SymbolizePrinter::Color::PURPLE;
158 constexpr auto kFileColor = SymbolizePrinter::Color::DEFAULT;
161 constexpr char AddressFormatter::bufTemplate[];
162 constexpr std::array<const char*, SymbolizePrinter::Color::NUM>
163 SymbolizePrinter::kColorMap;
165 AddressFormatter::AddressFormatter() {
166 memcpy(buf_, bufTemplate, sizeof(buf_));
169 folly::StringPiece AddressFormatter::format(uintptr_t address) {
170 // Can't use sprintf, not async-signal-safe
171 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
172 char* end = buf_ + sizeof(buf_) - 1 - (16 - 2 * sizeof(uintptr_t));
175 while (address != 0) {
176 *p-- = kHexChars[address & 0xf];
180 return folly::StringPiece(buf_, end);
183 void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
184 if (options_ & TERSE) {
185 printTerse(address, frame);
189 SCOPE_EXIT { color(Color::DEFAULT); };
191 if (!(options_ & NO_FRAME_ADDRESS)) {
192 color(kAddressColor);
194 AddressFormatter formatter;
195 doPrint(formatter.format(address));
198 const char padBuf[] = " ";
199 folly::StringPiece pad(padBuf,
200 sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
202 color(kFunctionColor);
204 doPrint(" (not found)");
208 if (!frame.name || frame.name[0] == '\0') {
209 doPrint(" (unknown)");
211 char demangledBuf[2048];
212 demangle(frame.name, demangledBuf, sizeof(demangledBuf));
214 doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
217 if (!(options_ & NO_FILE_AND_LINE)) {
219 char fileBuf[PATH_MAX];
221 if (frame.location.hasFileAndLine) {
222 frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
228 uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
230 doPrint(StringPiece(buf, n));
233 if (frame.location.hasMainFile) {
234 char mainFileBuf[PATH_MAX];
235 mainFileBuf[0] = '\0';
236 frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
237 if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
241 doPrint(mainFileBuf);
247 void SymbolizePrinter::color(SymbolizePrinter::Color color) {
248 if ((options_ & COLOR) == 0 &&
249 ((options_ & COLOR_IF_TTY) == 0 || !isTty_)) {
252 if (color < 0 || color >= kColorMap.size()) {
255 doPrint(kColorMap[color]);
258 void SymbolizePrinter::println(uintptr_t address,
259 const SymbolizedFrame& frame) {
260 print(address, frame);
264 void SymbolizePrinter::printTerse(uintptr_t address,
265 const SymbolizedFrame& frame) {
266 if (frame.found && frame.name && frame.name[0] != '\0') {
267 char demangledBuf[2048] = {0};
268 demangle(frame.name, demangledBuf, sizeof(demangledBuf));
269 doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
271 // Can't use sprintf, not async-signal-safe
272 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
273 char buf[] = "0x0000000000000000";
274 char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
277 while (address != 0) {
278 *p-- = kHexChars[address & 0xf];
281 doPrint(StringPiece(buf, end));
285 void SymbolizePrinter::println(const uintptr_t* addresses,
286 const SymbolizedFrame* frames,
288 for (size_t i = 0; i < frameCount; ++i) {
289 println(addresses[i], frames[i]);
295 int getFD(const std::ios& stream) {
297 std::streambuf* buf = stream.rdbuf();
298 using namespace __gnu_cxx;
301 auto sbuf = dynamic_cast<stdio_sync_filebuf<char>*>(buf);
303 return fileno(sbuf->file());
307 auto sbuf = dynamic_cast<stdio_filebuf<char>*>(buf);
316 bool isColorfulTty(int options, int fd) {
317 if ((options & SymbolizePrinter::TERSE) != 0 ||
318 (options & SymbolizePrinter::COLOR_IF_TTY) == 0 ||
319 fd < 0 || !::isatty(fd)) {
322 auto term = ::getenv("TERM");
323 return !(term == nullptr || term[0] == '\0' || strcmp(term, "dumb") == 0);
326 } // anonymous namespace
328 OStreamSymbolizePrinter::OStreamSymbolizePrinter(std::ostream& out, int options)
329 : SymbolizePrinter(options, isColorfulTty(options, getFD(out))),
333 void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
337 FDSymbolizePrinter::FDSymbolizePrinter(int fd, int options, size_t bufferSize)
338 : SymbolizePrinter(options, isColorfulTty(options, fd)),
340 buffer_(bufferSize ? IOBuf::create(bufferSize) : nullptr) {
343 FDSymbolizePrinter::~FDSymbolizePrinter() {
347 void FDSymbolizePrinter::doPrint(StringPiece sp) {
349 if (sp.size() > buffer_->tailroom()) {
351 writeFull(fd_, sp.data(), sp.size());
353 memcpy(buffer_->writableTail(), sp.data(), sp.size());
354 buffer_->append(sp.size());
357 writeFull(fd_, sp.data(), sp.size());
361 void FDSymbolizePrinter::flush() {
362 if (buffer_ && !buffer_->empty()) {
363 writeFull(fd_, buffer_->data(), buffer_->length());
368 FILESymbolizePrinter::FILESymbolizePrinter(FILE* file, int options)
369 : SymbolizePrinter(options, isColorfulTty(options, fileno(file))),
373 void FILESymbolizePrinter::doPrint(StringPiece sp) {
374 fwrite(sp.data(), 1, sp.size(), file_);
377 void StringSymbolizePrinter::doPrint(StringPiece sp) {
378 buf_.append(sp.data(), sp.size());
381 StackTracePrinter::StackTracePrinter(size_t minSignalSafeElfCacheSize, int fd)
383 elfCache_(std::max(countLoadedElfFiles(), minSignalSafeElfCacheSize)),
386 SymbolizePrinter::COLOR_IF_TTY,
387 size_t(64) << 10), // 64KiB
388 addresses_(std::make_unique<FrameArray<kMaxStackTraceDepth>>()) {}
390 void StackTracePrinter::flush() {
395 void StackTracePrinter::printStackTrace(bool symbolize) {
400 // Skip the getStackTrace frame
401 if (!getStackTraceSafe(*addresses_)) {
402 print("(error retrieving stack trace)\n");
403 } else if (symbolize) {
404 // Do our best to populate location info, process is going to terminate,
405 // so performance isn't critical.
406 Symbolizer symbolizer(&elfCache_, Dwarf::LocationInfoMode::FULL);
407 symbolizer.symbolize(*addresses_);
409 // Skip the top 2 frames:
411 // StackTracePrinter::printStackTrace (here)
413 // Leaving signalHandler on the stack for clarity, I think.
414 printer_.println(*addresses_, 2);
416 print("(safe mode, symbolizer not available)\n");
417 AddressFormatter formatter;
418 for (size_t i = 0; i < addresses_->frameCount; ++i) {
419 print(formatter.format(addresses_->addresses[i]));
425 } // namespace symbolizer