proxygen
Symbolizer.cpp
Go to the documentation of this file.
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 
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 
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  */
50 extern struct r_debug _r_debug;
51 
52 namespace folly {
53 namespace symbolizer {
54 
55 namespace {
56 
57 ElfCache* defaultElfCache() {
58  static constexpr size_t defaultCapacity = 500;
59  static auto cache = new ElfCache(defaultCapacity);
60  return cache;
61 }
62 
63 } // namespace
64 
66  const std::shared_ptr<ElfFile>& file,
67  uintptr_t address,
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 
85  ElfCacheBase* cache,
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 
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 
179 namespace {
180 constexpr char kHexChars[] = "0123456789abcdef";
181 constexpr auto kAddressColor = SymbolizePrinter::Color::BLUE;
182 constexpr auto kFunctionColor = SymbolizePrinter::Color::PURPLE;
183 constexpr auto kFileColor = SymbolizePrinter::Color::DEFAULT;
184 } // namespace
185 
186 constexpr char AddressFormatter::bufTemplate[];
187 constexpr std::array<const char*, SymbolizePrinter::Color::NUM>
189 
191  memcpy(buf_, bufTemplate, sizeof(buf_));
192 }
193 
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 
208 void 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 
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 
285  uintptr_t address,
286  const SymbolizedFrame& frame) {
287  print(address, frame);
288  doPrint("\n");
289 }
290 
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 
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 
322 namespace {
323 
324 int 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 
347 bool 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 
360  : SymbolizePrinter(options, isColorfulTty(options, getFD(out))),
361  out_(out) {}
362 
364  out_ << sp;
365 }
366 
367 FDSymbolizePrinter::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 
373  flush();
374 }
375 
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 
391  if (buffer_ && !buffer_->empty()) {
392  writeFull(fd_, buffer_->data(), buffer_->length());
393  buffer_->clear();
394  }
395 }
396 
398  : SymbolizePrinter(options, isColorfulTty(options, fileno(file))),
399  file_(file) {}
400 
402  fwrite(sp.data(), 1, sp.size(), file_);
403 }
404 
406  buf_.append(sp.data(), sp.size());
407 }
408 
410  size_t minSignalSafeElfCacheSize,
411  int fd)
412  : fd_(fd),
413  elfCache_(std::max(countLoadedElfFiles(), minSignalSafeElfCacheSize)),
414  printer_(
415  fd,
417  size_t(64) << 10), // 64KiB
418  addresses_(std::make_unique<FrameArray<kMaxStackTraceDepth>>()) {}
419 
421  printer_.flush();
422  fsyncNoInt(fd_);
423 }
424 
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.
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.
441 }
442 
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) {
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 
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(),
475  symbolCacheSize) {}
476 
478 
480  SCOPE_EXIT {
481  printer_->flush();
482  };
483 
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 
506  printer_->flush();
507 }
508 
509 // Stack utilities used by UnsafeSelfAllocateStackTracePrinter
510 namespace {
511 // Size of mmap-allocated stack. Not to confuse with sigaltstack.
512 const size_t kMmapStackSize = 1 * 1024 * 1024;
513 
514 using MmapPtr = std::unique_ptr<char, void (*)(char*)>;
515 
516 MmapPtr 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.
522 MmapPtr 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 
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
const std::unique_ptr< SymbolizePrinter > printer_
Definition: Symbolizer.h:420
virtual std::shared_ptr< ElfFile > getFile(StringPiece path)=0
uint32_t uint64ToBufferUnsafe(uint64_t v, char *const buffer)
Definition: Conv.h:383
LogLevel max
Definition: LogLevel.cpp:31
ssize_t getStackTraceSafe(uintptr_t *addresses, size_t maxAddresses)
Definition: StackTrace.cpp:53
Dwarf::LocationInfo location
Definition: Symbolizer.h:58
void printTerse(uintptr_t address, const SymbolizedFrame &frame)
Definition: Symbolizer.cpp:291
const std::unique_ptr< ElfCache > elfCache_
Definition: Symbolizer.h:419
void doPrint(StringPiece sp) override
Definition: Symbolizer.cpp:405
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
Symbolizer(Dwarf::LocationInfoMode mode=kDefaultLocationInfoMode)
Definition: Symbolizer.h:123
const Dwarf::LocationInfoMode mode_
Definition: Symbolizer.h:153
STL namespace.
std::unique_ptr< IOBuf > buffer_
Definition: Symbolizer.h:297
constexpr size_type size() const
Definition: Range.h:431
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
FDSymbolizePrinter(int fd, int options=0, size_t bufferSize=0)
Definition: Symbolizer.cpp:367
in_place_tag in_place(in_place_tag={})
Definition: Utility.h:235
void print(uintptr_t address, const SymbolizedFrame &frame)
Definition: Symbolizer.cpp:208
FastStackTracePrinter(std::unique_ptr< SymbolizePrinter > printer, size_t elfCacheSize=0, size_t symbolCacheSize=kDefaultSymbolCacheSize)
Definition: Symbolizer.cpp:463
#define nullptr
Definition: http_parser.c:41
struct r_debug _r_debug
OStreamSymbolizePrinter(std::ostream &out, int options=0)
Definition: Symbolizer.cpp:359
folly::Optional< PskKeyExchangeMode > mode
void println(uintptr_t address, const SymbolizedFrame &frame)
Definition: Symbolizer.cpp:284
SafeStackTracePrinter(size_t minSignalSafeElfCacheSize=kDefaultMinSignalSafeElfCacheSize, int fd=STDERR_FILENO)
Definition: Symbolizer.cpp:409
int fsyncNoInt(int fd)
Definition: FileUtil.cpp:64
std::shared_ptr< ElfFile > file_
Definition: Symbolizer.h:68
const int kMaxStackTraceDepth
Definition: gtest.h:147
static constexpr char bufTemplate[]
Definition: Symbolizer.h:173
auto println
Definition: set_done_2.cpp:41
auto end(TestAdlIterable &instance)
Definition: ForeachTest.cpp:62
constexpr Iter data() const
Definition: Range.h:446
void doPrint(StringPiece sp) override
Definition: Symbolizer.cpp:363
size_t countLoadedElfFiles()
Definition: ElfCache.cpp:32
ssize_t writeFull(int fd, const void *buf, size_t count)
Definition: FileUtil.cpp:134
ElfCacheBase *const cache_
Definition: Symbolizer.h:152
size_t toBuffer(char *buf, size_t bufSize) const
Definition: Dwarf.cpp:224
void set(const std::shared_ptr< ElfFile > &file, uintptr_t address, Dwarf::LocationInfoMode mode)
Definition: Symbolizer.cpp:65
static constexpr std::array< const char *, Color::NUM > kColorMap
Definition: Symbolizer.h:258
std::enable_if<!std::is_array< T >::value, std::unique_ptr< T > >::type make_unique(Args &&...args)
Definition: Memory.h:259
FOLLY_NOINLINE void printStackTrace(bool symbolize)
Definition: Symbolizer.cpp:479
std::string & out_
Definition: json.cpp:185
folly::Optional< Synchronized< SymbolCache > > symbolCache_
Definition: Symbolizer.h:156
StringPiece format(uintptr_t address)
Definition: Symbolizer.cpp:194
FOLLY_NOINLINE void printStackTrace(bool symbolize)
Definition: Symbolizer.cpp:443
FILESymbolizePrinter(FILE *file, int options=0)
Definition: Symbolizer.cpp:397
std::unique_ptr< FrameArray< kMaxStackTraceDepth > > addresses_
Definition: Symbolizer.h:387
Range< const char * > StringPiece
void symbolize(const uintptr_t *addresses, SymbolizedFrame *frames, size_t frameCount)
Definition: Symbolizer.cpp:94
void doPrint(StringPiece sp) override
Definition: Symbolizer.cpp:376
std::unique_ptr< unsigned char[]> buffer_
Definition: Random.cpp:105
ThreadPoolListHook * addr
void doPrint(StringPiece sp) override
Definition: Symbolizer.cpp:401
GMockOutputTest ExpectedCall FILE
fbstring demangle(const char *name)
Definition: Demangle.cpp:111