1/*
2 * Copyright 2012-present Facebook, Inc.
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include <folly/experimental/symbolizer/Symbolizer.h>
18
19#include <link.h>
20#include <ucontext.h>
21
22#include <climits>
23#include <cstdio>
24#include <cstdlib>
25#include <iostream>
26
27#ifdef __GLIBCXX__
28#include <ext/stdio_filebuf.h>
29#include <ext/stdio_sync_filebuf.h>
30#endif
31
32#include <folly/Conv.h>
33#include <folly/FileUtil.h>
34#include <folly/Memory.h>
35#include <folly/ScopeGuard.h>
36#include <folly/String.h>
37
38#include <folly/experimental/symbolizer/Dwarf.h>
39#include <folly/experimental/symbolizer/Elf.h>
40#include <folly/experimental/symbolizer/LineReader.h>
41#include <folly/portability/SysMman.h>
42#include <folly/portability/Unistd.h>
43
44/*
45 * This is declared in `link.h' on Linux platforms, but apparently not on the
46 * Mac version of the file. It's harmless to declare again, in any case.
47 *
48 * Note that declaring it with `extern "C"` results in linkage conflicts.
49 */
50extern struct r_debug _r_debug;
51
52namespace folly {
53namespace symbolizer {
54
55namespace {
56
57ElfCache* defaultElfCache() {
58 static constexpr size_t defaultCapacity = 500;
59 static auto cache = new ElfCache(defaultCapacity);
60 return cache;
61}
62
63} // namespace
64
65void SymbolizedFrame::set(
66 const std::shared_ptr<ElfFile>& file,
67 uintptr_t address,
68 Dwarf::LocationInfoMode mode) {
69 clear();
70 found = true;
71
72 address += file->getBaseAddress();
73 auto sym = file->getDefinitionByAddress(address);
74 if (!sym.first) {
75 return;
76 }
77
78 file_ = file;
79 name = file->getSymbolName(sym);
80
81 Dwarf(file.get()).findAddress(address, location, mode);
82}
83
84Symbolizer::Symbolizer(
85 ElfCacheBase* cache,
86 Dwarf::LocationInfoMode mode,
87 size_t symbolCacheSize)
88 : cache_(cache ? cache : defaultElfCache()), mode_(mode) {
89 if (symbolCacheSize > 0) {
90 symbolCache_.emplace(folly::in_place, symbolCacheSize);
91 }
92}
93
94void Symbolizer::symbolize(
95 const uintptr_t* addresses,
96 SymbolizedFrame* frames,
97 size_t addrCount) {
98 size_t remaining = 0;
99 for (size_t i = 0; i < addrCount; ++i) {
100 auto& frame = frames[i];
101 if (!frame.found) {
102 ++remaining;
103 frame.clear();
104 }
105 }
106
107 if (remaining == 0) { // we're done
108 return;
109 }
110
111 if (_r_debug.r_version != 1) {
112 return;
113 }
114
115 char selfPath[PATH_MAX + 8];
116 ssize_t selfSize;
117 if ((selfSize = readlink("/proc/self/exe", selfPath, PATH_MAX + 1)) == -1) {
118 // Something has gone terribly wrong.
119 return;
120 }
121 selfPath[selfSize] = '\0';
122
123 for (auto lmap = _r_debug.r_map; lmap != nullptr && remaining != 0;
124 lmap = lmap->l_next) {
125 // The empty string is used in place of the filename for the link_map
126 // corresponding to the running executable. Additionally, the `l_addr' is
127 // 0 and the link_map appears to be first in the list---but none of this
128 // behavior appears to be documented, so checking for the empty string is
129 // as good as anything.
130 auto const objPath = lmap->l_name[0] != '\0' ? lmap->l_name : selfPath;
131
132 auto const elfFile = cache_->getFile(objPath);
133 if (!elfFile) {
134 continue;
135 }
136
137 // Get the address at which the object is loaded. We have to use the ELF
138 // header for the running executable, since its `l_addr' is zero, but we
139 // should use `l_addr' for everything else---in particular, if the object
140 // is position-independent, getBaseAddress() (which is p_vaddr) will be 0.
141 auto const base =
142 lmap->l_addr != 0 ? lmap->l_addr : elfFile->getBaseAddress();
143
144 for (size_t i = 0; i < addrCount && remaining != 0; ++i) {
145 auto& frame = frames[i];
146 if (frame.found) {
147 continue;
148 }
149
150 auto const addr = addresses[i];
151 if (symbolCache_) {
152 // Need a write lock, because EvictingCacheMap brings found item to
153 // front of eviction list.
154 auto lockedSymbolCache = symbolCache_->wlock();
155
156 auto const iter = lockedSymbolCache->find(addr);
157 if (iter != lockedSymbolCache->end()) {
158 frame = iter->second;
159 continue;
160 }
161 }
162
163 // Get the unrelocated, ELF-relative address.
164 auto const adjusted = addr - base;
165
166 if (elfFile->getSectionContainingAddress(adjusted)) {
167 frame.set(elfFile, adjusted, mode_);
168 --remaining;
169 if (symbolCache_) {
170 // frame may already have been set here. That's ok, we'll just
171 // overwrite, which doesn't cause a correctness problem.
172 symbolCache_->wlock()->set(addr, frame);
173 }
174 }
175 }
176 }
177}
178
179namespace {
180constexpr char kHexChars[] = "0123456789abcdef";
181constexpr auto kAddressColor = SymbolizePrinter::Color::BLUE;
182constexpr auto kFunctionColor = SymbolizePrinter::Color::PURPLE;
183constexpr auto kFileColor = SymbolizePrinter::Color::DEFAULT;
184} // namespace
185
186constexpr char AddressFormatter::bufTemplate[];
187constexpr std::array<const char*, SymbolizePrinter::Color::NUM>
188 SymbolizePrinter::kColorMap;
189
190AddressFormatter::AddressFormatter() {
191 memcpy(buf_, bufTemplate, sizeof(buf_));
192}
193
194folly::StringPiece AddressFormatter::format(uintptr_t address) {
195 // Can't use sprintf, not async-signal-safe
196 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
197 char* end = buf_ + sizeof(buf_) - 1 - (16 - 2 * sizeof(uintptr_t));
198 char* p = end;
199 *p-- = '\0';
200 while (address != 0) {
201 *p-- = kHexChars[address & 0xf];
202 address >>= 4;
203 }
204
205 return folly::StringPiece(buf_, end);
206}
207
208void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
209 if (options_ & TERSE) {
210 printTerse(address, frame);
211 return;
212 }
213
214 SCOPE_EXIT {
215 color(Color::DEFAULT);
216 };
217
218 if (!(options_ & NO_FRAME_ADDRESS)) {
219 color(kAddressColor);
220
221 AddressFormatter formatter;
222 doPrint(formatter.format(address));
223 }
224
225 const char padBuf[] = " ";
226 folly::StringPiece pad(
227 padBuf, sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
228
229 color(kFunctionColor);
230 if (!frame.found) {
231 doPrint(" (not found)");
232 return;
233 }
234
235 if (!frame.name || frame.name[0] == '\0') {
236 doPrint(" (unknown)");
237 } else {
238 char demangledBuf[2048];
239 demangle(frame.name, demangledBuf, sizeof(demangledBuf));
240 doPrint(" ");
241 doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
242 }
243
244 if (!(options_ & NO_FILE_AND_LINE)) {
245 color(kFileColor);
246 char fileBuf[PATH_MAX];
247 fileBuf[0] = '\0';
248 if (frame.location.hasFileAndLine) {
249 frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
250 doPrint("\n");
251 doPrint(pad);
252 doPrint(fileBuf);
253
254 char buf[22];
255 uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
256 doPrint(":");
257 doPrint(StringPiece(buf, n));
258 }
259
260 if (frame.location.hasMainFile) {
261 char mainFileBuf[PATH_MAX];
262 mainFileBuf[0] = '\0';
263 frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
264 if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
265 doPrint("\n");
266 doPrint(pad);
267 doPrint("-> ");
268 doPrint(mainFileBuf);
269 }
270 }
271 }
272}
273
274void SymbolizePrinter::color(SymbolizePrinter::Color color) {
275 if ((options_ & COLOR) == 0 && ((options_ & COLOR_IF_TTY) == 0 || !isTty_)) {
276 return;
277 }
278 if (static_cast<size_t>(color) >= kColorMap.size()) { // catches underflow too
279 return;
280 }
281 doPrint(kColorMap[color]);
282}
283
284void SymbolizePrinter::println(
285 uintptr_t address,
286 const SymbolizedFrame& frame) {
287 print(address, frame);
288 doPrint("\n");
289}
290
291void SymbolizePrinter::printTerse(
292 uintptr_t address,
293 const SymbolizedFrame& frame) {
294 if (frame.found && frame.name && frame.name[0] != '\0') {
295 char demangledBuf[2048] = {0};
296 demangle(frame.name, demangledBuf, sizeof(demangledBuf));
297 doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
298 } else {
299 // Can't use sprintf, not async-signal-safe
300 static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?");
301 char buf[] = "0x0000000000000000";
302 char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));
303 char* p = end;
304 *p-- = '\0';
305 while (address != 0) {
306 *p-- = kHexChars[address & 0xf];
307 address >>= 4;
308 }
309 doPrint(StringPiece(buf, end));
310 }
311}
312
313void SymbolizePrinter::println(
314 const uintptr_t* addresses,
315 const SymbolizedFrame* frames,
316 size_t frameCount) {
317 for (size_t i = 0; i < frameCount; ++i) {
318 println(addresses[i], frames[i]);
319 }
320}
321
322namespace {
323
324int getFD(const std::ios& stream) {
325#if defined(__GNUC__) && FOLLY_HAS_RTTI
326 std::streambuf* buf = stream.rdbuf();
327 using namespace __gnu_cxx;
328
329 {
330 auto sbuf = dynamic_cast<stdio_sync_filebuf<char>*>(buf);
331 if (sbuf) {
332 return fileno(sbuf->file());
333 }
334 }
335 {
336 auto sbuf = dynamic_cast<stdio_filebuf<char>*>(buf);
337 if (sbuf) {
338 return sbuf->fd();
339 }
340 }
341#else
342 (void)stream;
343#endif // __GNUC__
344 return -1;
345}
346
347bool isColorfulTty(int options, int fd) {
348 if ((options & SymbolizePrinter::TERSE) != 0 ||
349 (options & SymbolizePrinter::COLOR_IF_TTY) == 0 || fd < 0 ||
350 !::isatty(fd)) {
351 return false;
352 }
353 auto term = ::getenv("TERM");
354 return !(term == nullptr || term[0] == '\0' || strcmp(term, "dumb") == 0);
355}
356
357} // namespace
358
359OStreamSymbolizePrinter::OStreamSymbolizePrinter(std::ostream& out, int options)
360 : SymbolizePrinter(options, isColorfulTty(options, getFD(out))),
361 out_(out) {}
362
363void OStreamSymbolizePrinter::doPrint(StringPiece sp) {
364 out_ << sp;
365}
366
367FDSymbolizePrinter::FDSymbolizePrinter(int fd, int options, size_t bufferSize)
368 : SymbolizePrinter(options, isColorfulTty(options, fd)),
369 fd_(fd),
370 buffer_(bufferSize ? IOBuf::create(bufferSize) : nullptr) {}
371
372FDSymbolizePrinter::~FDSymbolizePrinter() {
373 flush();
374}
375
376void FDSymbolizePrinter::doPrint(StringPiece sp) {
377 if (buffer_) {
378 if (sp.size() > buffer_->tailroom()) {
379 flush();
380 writeFull(fd_, sp.data(), sp.size());
381 } else {
382 memcpy(buffer_->writableTail(), sp.data(), sp.size());
383 buffer_->append(sp.size());
384 }
385 } else {
386 writeFull(fd_, sp.data(), sp.size());
387 }
388}
389
390void FDSymbolizePrinter::flush() {
391 if (buffer_ && !buffer_->empty()) {
392 writeFull(fd_, buffer_->data(), buffer_->length());
393 buffer_->clear();
394 }
395}
396
397FILESymbolizePrinter::FILESymbolizePrinter(FILE* file, int options)
398 : SymbolizePrinter(options, isColorfulTty(options, fileno(file))),
399 file_(file) {}
400
401void FILESymbolizePrinter::doPrint(StringPiece sp) {
402 fwrite(sp.data(), 1, sp.size(), file_);
403}
404
405void StringSymbolizePrinter::doPrint(StringPiece sp) {
406 buf_.append(sp.data(), sp.size());
407}
408
409SafeStackTracePrinter::SafeStackTracePrinter(
410 size_t minSignalSafeElfCacheSize,
411 int fd)
412 : fd_(fd),
413 elfCache_(std::max(countLoadedElfFiles(), minSignalSafeElfCacheSize)),
414 printer_(
415 fd,
416 SymbolizePrinter::COLOR_IF_TTY,
417 size_t(64) << 10), // 64KiB
418 addresses_(std::make_unique<FrameArray<kMaxStackTraceDepth>>()) {}
419
420void SafeStackTracePrinter::flush() {
421 printer_.flush();
422 fsyncNoInt(fd_);
423}
424
425void SafeStackTracePrinter::printSymbolizedStackTrace() {
426 // This function might run on an alternative stack allocated by
427 // UnsafeSelfAllocateStackTracePrinter. Capturing a stack from
428 // here is probably wrong.
429
430 // Do our best to populate location info, process is going to terminate,
431 // so performance isn't critical.
432 Symbolizer symbolizer(&elfCache_, Dwarf::LocationInfoMode::FULL);
433 symbolizer.symbolize(*addresses_);
434
435 // Skip the top 2 frames captured by printStackTrace:
436 // getStackTraceSafe
437 // SafeStackTracePrinter::printStackTrace (captured stack)
438 //
439 // Leaving signalHandler on the stack for clarity, I think.
440 printer_.println(*addresses_, 2);
441}
442
443void SafeStackTracePrinter::printStackTrace(bool symbolize) {
444 SCOPE_EXIT {
445 flush();
446 };
447
448 // Skip the getStackTrace frame
449 if (!getStackTraceSafe(*addresses_)) {
450 print("(error retrieving stack trace)\n");
451 } else if (symbolize) {
452 printSymbolizedStackTrace();
453 } else {
454 print("(safe mode, symbolizer not available)\n");
455 AddressFormatter formatter;
456 for (size_t i = 0; i < addresses_->frameCount; ++i) {
457 print(formatter.format(addresses_->addresses[i]));
458 print("\n");
459 }
460 }
461}
462
463FastStackTracePrinter::FastStackTracePrinter(
464 std::unique_ptr<SymbolizePrinter> printer,
465 size_t elfCacheSize,
466 size_t symbolCacheSize)
467 : elfCache_(
468 elfCacheSize == 0
469 ? nullptr
470 : new ElfCache{std::max(countLoadedElfFiles(), elfCacheSize)}),
471 printer_(std::move(printer)),
472 symbolizer_(
473 elfCache_ ? elfCache_.get() : defaultElfCache(),
474 Dwarf::LocationInfoMode::FULL,
475 symbolCacheSize) {}
476
477FastStackTracePrinter::~FastStackTracePrinter() {}
478
479void FastStackTracePrinter::printStackTrace(bool symbolize) {
480 SCOPE_EXIT {
481 printer_->flush();
482 };
483
484 FrameArray<kMaxStackTraceDepth> addresses;
485
486 if (!getStackTraceSafe(addresses)) {
487 printer_->print("(error retrieving stack trace)\n");
488 } else if (symbolize) {
489 symbolizer_.symbolize(addresses);
490
491 // Skip the top 2 frames:
492 // getStackTraceSafe
493 // FastStackTracePrinter::printStackTrace (here)
494 printer_->println(addresses, 2);
495 } else {
496 printer_->print("(safe mode, symbolizer not available)\n");
497 AddressFormatter formatter;
498 for (size_t i = 0; i < addresses.frameCount; ++i) {
499 printer_->print(formatter.format(addresses.addresses[i]));
500 printer_->print("\n");
501 }
502 }
503}
504
505void FastStackTracePrinter::flush() {
506 printer_->flush();
507}
508
509// Stack utilities used by UnsafeSelfAllocateStackTracePrinter
510namespace {
511// Size of mmap-allocated stack. Not to confuse with sigaltstack.
512const size_t kMmapStackSize = 1 * 1024 * 1024;
513
514using MmapPtr = std::unique_ptr<char, void (*)(char*)>;
515
516MmapPtr getNull() {
517 return MmapPtr(nullptr, [](char*) {});
518}
519
520// Assign a mmap-allocated stack to oucp.
521// Return a non-empty smart pointer on success.
522MmapPtr allocateStack(ucontext_t* oucp, size_t pageSize) {
523 MmapPtr p(
524 (char*)mmap(
525 nullptr,
526 kMmapStackSize,
527 PROT_WRITE | PROT_READ,
528 MAP_ANONYMOUS | MAP_PRIVATE,
529 /* fd */ -1,
530 /* offset */ 0),
531 [](char* addr) {
532 // Usually runs inside a fatal signal handler.
533 // Error handling is skipped.
534 munmap(addr, kMmapStackSize);
535 });
536
537 if (!p) {
538 return getNull();
539 }
540
541 // Prepare read-only guard pages on both ends
542 if (pageSize * 2 >= kMmapStackSize) {
543 return getNull();
544 }
545 size_t upperBound = ((kMmapStackSize - 1) / pageSize) * pageSize;
546 if (mprotect(p.get(), pageSize, PROT_NONE) != 0) {
547 return getNull();
548 }
549 if (mprotect(p.get() + upperBound, kMmapStackSize - upperBound, PROT_NONE) !=
550 0) {
551 return getNull();
552 }
553
554 oucp->uc_stack.ss_sp = p.get() + pageSize;
555 oucp->uc_stack.ss_size = upperBound - pageSize;
556 oucp->uc_stack.ss_flags = 0;
557
558 return p;
559}
560
561} // namespace
562
563void UnsafeSelfAllocateStackTracePrinter::printSymbolizedStackTrace() {
564 if (pageSizeUnchecked_ <= 0) {
565 return;
566 }
567
568 ucontext_t cur;
569 memset(&cur, 0, sizeof(cur));
570 ucontext_t alt;
571 memset(&alt, 0, sizeof(alt));
572
573 if (getcontext(&alt) != 0) {
574 return;
575 }
576 alt.uc_link = &cur;
577
578 MmapPtr p = allocateStack(&alt, (size_t)pageSizeUnchecked_);
579 if (!p) {
580 return;
581 }
582
583 auto contextStart = [](UnsafeSelfAllocateStackTracePrinter* that) {
584 that->SafeStackTracePrinter::printSymbolizedStackTrace();
585 };
586
587 makecontext(
588 &alt,
589 (void (*)())(void (*)(UnsafeSelfAllocateStackTracePrinter*))(
590 contextStart),
591 /* argc */ 1,
592 /* arg */ this);
593 // NOTE: swapcontext is not async-signal-safe
594 if (swapcontext(&cur, &alt) != 0) {
595 return;
596 }
597}
598
599} // namespace symbolizer
600} // namespace folly
601