// in our signal handler at a time.
//
// Leak it so we don't have to worry about destruction order
-constexpr size_t kMinSignalSafeElfCacheSize = 500;
-auto gSignalSafeElfCache = new SignalSafeElfCache(
- std::max(countLoadedElfFiles(), kMinSignalSafeElfCacheSize));
-
-// Buffered writer (using a fixed-size buffer). We try to write only once
-// to prevent interleaving with messages written from other threads.
-//
-// Leak it so we don't have to worry about destruction order.
-auto gPrinter = new FDSymbolizePrinter(STDERR_FILENO,
- SymbolizePrinter::COLOR_IF_TTY,
- size_t(64) << 10); // 64KiB
-
-// Flush gPrinter, also fsync, in case we're about to crash again...
-void flush() {
- gPrinter->flush();
- fsyncNoInt(STDERR_FILENO);
-}
+StackTracePrinter* gStackTracePrinter = new StackTracePrinter();
void printDec(uint64_t val) {
char buf[20];
uint32_t n = uint64ToBufferUnsafe(val, buf);
- gPrinter->print(StringPiece(buf, n));
+ gStackTracePrinter->print(StringPiece(buf, n));
}
const char kHexChars[] = "0123456789abcdef";
*--p = 'x';
*--p = '0';
- gPrinter->print(StringPiece(p, end));
+ gStackTracePrinter->print(StringPiece(p, end));
}
void print(StringPiece sp) {
- gPrinter->print(sp);
+ gStackTracePrinter->print(sp);
+}
+
+void flush() {
+ gStackTracePrinter->flush();
}
void dumpTimeInfo() {
print("), stack trace: ***\n");
}
-FOLLY_NOINLINE void dumpStackTrace(bool symbolize);
-
-void dumpStackTrace(bool symbolize) {
- SCOPE_EXIT { flush(); };
- // Get and symbolize stack trace
- constexpr size_t kMaxStackTraceDepth = 100;
- FrameArray<kMaxStackTraceDepth> addresses;
-
- // Skip the getStackTrace frame
- if (!getStackTraceSafe(addresses)) {
- print("(error retrieving stack trace)\n");
- } else if (symbolize) {
- // Do our best to populate location info, process is going to terminate,
- // so performance isn't critical.
- Symbolizer symbolizer(gSignalSafeElfCache, Dwarf::LocationInfoMode::FULL);
- symbolizer.symbolize(addresses);
-
- // Skip the top 2 frames:
- // getStackTraceSafe
- // dumpStackTrace (here)
- //
- // Leaving signalHandler on the stack for clarity, I think.
- gPrinter->println(addresses, 2);
- } else {
- print("(safe mode, symbolizer not available)\n");
- AddressFormatter formatter;
- for (size_t i = 0; i < addresses.frameCount; ++i) {
- print(formatter.format(addresses.addresses[i]));
- print("\n");
- }
- }
-}
-
// On Linux, pthread_t is a pointer, so 0 is an invalid value, which we
// take to indicate "no thread in the signal handler".
//
// next time around.
if (!gInRecursiveSignalHandler.exchange(true)) {
print("Entered fatal signal handler recursively. We're in trouble.\n");
- dumpStackTrace(false); // no symbolization
+ gStackTracePrinter->printStackTrace(false); // no symbolization
}
return;
}
dumpTimeInfo();
dumpSignalInfo(signum, info);
- dumpStackTrace(true); // with symbolization
+ gStackTracePrinter->printStackTrace(true); // with symbolization
// Run user callbacks
gFatalSignalCallbackRegistry->run();
buf_.append(sp.data(), sp.size());
}
-} // namespace symbolizer
+StackTracePrinter::StackTracePrinter(size_t minSignalSafeElfCacheSize, int fd)
+ : fd_(fd),
+ elfCache_(std::max(countLoadedElfFiles(), minSignalSafeElfCacheSize)),
+ printer_(
+ fd,
+ SymbolizePrinter::COLOR_IF_TTY,
+ size_t(64) << 10) // 64KiB
+{}
+
+void StackTracePrinter::flush() {
+ printer_.flush();
+ fsyncNoInt(fd_);
+}
+
+void StackTracePrinter::printStackTrace(bool symbolize) {
+ SCOPE_EXIT {
+ flush();
+ };
+ // Get and symbolize stack trace
+ constexpr size_t kMaxStackTraceDepth = 100;
+ FrameArray<kMaxStackTraceDepth> addresses;
+
+ // Skip the getStackTrace frame
+ if (!getStackTraceSafe(addresses)) {
+ print("(error retrieving stack trace)\n");
+ } else if (symbolize) {
+ // Do our best to populate location info, process is going to terminate,
+ // so performance isn't critical.
+ Symbolizer symbolizer(&elfCache_, Dwarf::LocationInfoMode::FULL);
+ symbolizer.symbolize(addresses);
+
+ // Skip the top 2 frames:
+ // getStackTraceSafe
+ // StackTracePrinter::printStackTrace (here)
+ //
+ // Leaving signalHandler on the stack for clarity, I think.
+ printer_.println(addresses, 2);
+ } else {
+ print("(safe mode, symbolizer not available)\n");
+ AddressFormatter formatter;
+ for (size_t i = 0; i < addresses.frameCount; ++i) {
+ print(formatter.format(addresses.addresses[i]));
+ print("\n");
+ }
+ }
+}
+} // namespace symbolizer
} // namespace folly
fbstring buf_;
};
+/**
+ * Use this class to print a stack trace from a signal handler, or other place
+ * where you shouldn't allocate memory on the heap, and fsync()ing your file
+ * descriptor is more important than performance.
+ *
+ * Make sure to create one of these on startup, not in the signal handler, as
+ * the constructo allocates on the heap, whereas the other methods don't. Best
+ * practice is to just leak this object, rather than worry about destruction
+ * order.
+ *
+ * These methods aren't thread safe, so if you could have signals on multiple
+ * threads at the same time, you need to do your own locking to ensure you don't
+ * call these methods from multiple threads. They are signal safe, however.
+ */
+class StackTracePrinter {
+ public:
+ static constexpr size_t kDefaultMinSignalSafeElfCacheSize = 500;
+ explicit StackTracePrinter(
+ size_t minSignalSafeElfCacheSize = kDefaultMinSignalSafeElfCacheSize,
+ int fd = STDERR_FILENO);
+
+ /**
+ * Only allocates on the stack and is signal-safe but not thread-safe. Don't
+ * call printStackTrace() on the same StackTracePrinter object from multiple
+ * threads at the same time.
+ */
+ FOLLY_NOINLINE void printStackTrace(bool symbolize);
+
+ void print(StringPiece sp) {
+ printer_.print(sp);
+ }
+
+ // Flush printer_, also fsync, in case we're about to crash again...
+ void flush();
+
+ private:
+ int fd_;
+ SignalSafeElfCache elfCache_;
+ FDSymbolizePrinter printer_;
+};
+
} // namespace symbolizer
} // namespace folly