From: Tudor Bosman Date: Fri, 6 Dec 2013 01:22:01 +0000 (-0800) Subject: Use fixed size stack traces; unify getStackTrace X-Git-Tag: v0.22.0~767 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=3680f888411a4eea23aae712149932f4bb37d349 Use fixed size stack traces; unify getStackTrace Summary: Also, switch to the simpler unw_backtrace(), which has the nice advantage of actually doing IP adjustment (-1 in certain frames) correctly, unlike me :) This is in preparation for the faster backtrace in libunwind 1.1. Test Plan: folly/experimental/exception_tracer, folly/experimental/symbolizer, admarket/lib/util:memory_tracker_test Reviewed By: lucian@fb.com FB internal diff: D1088357 --- diff --git a/folly/experimental/exception_tracer/ExceptionTracer.cpp b/folly/experimental/exception_tracer/ExceptionTracer.cpp index 2e84227a..533eb009 100644 --- a/folly/experimental/exception_tracer/ExceptionTracer.cpp +++ b/folly/experimental/exception_tracer/ExceptionTracer.cpp @@ -29,17 +29,18 @@ namespace { +using namespace ::folly::exception_tracer; +using namespace ::folly::symbolizer; +using namespace __cxxabiv1; + extern "C" { -const StackTraceStack* getExceptionStackTraceStack(void) __attribute__((weak)); -typedef const StackTraceStack* (*GetExceptionStackTraceStackType)(void); +StackTraceStack* getExceptionStackTraceStack(void) __attribute__((weak)); +typedef StackTraceStack* (*GetExceptionStackTraceStackType)(void); GetExceptionStackTraceStackType getExceptionStackTraceStackFn; } } // namespace -using namespace ::folly::symbolizer; -using namespace __cxxabiv1; - namespace folly { namespace exception_tracer { @@ -54,19 +55,23 @@ std::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) { << (info.frames.size() == 1 ? " frame" : " frames") << ")\n"; try { - std::vector addresses; - addresses.reserve(info.frames.size()); - for (auto ip : info.frames) { - // Symbolize the previous address because the IP might be in the - // next function, per glog/src/signalhandler.cc - addresses.emplace_back(ip - 1); - } + ssize_t frameCount = info.frames.size(); + // Skip our own internal frames + static constexpr size_t skip = 3; + + if (frameCount > skip) { + auto addresses = info.frames.data() + skip; + frameCount -= skip; + + std::vector frames; + frames.resize(frameCount); - Symbolizer symbolizer; - symbolizer.symbolize(addresses.data(), addresses.size()); + Symbolizer symbolizer; + symbolizer.symbolize(addresses, frames.data(), frameCount); - OStreamSymbolizePrinter osp(out); - osp.print(addresses.data(), addresses.size(), addresses.size()); + OStreamSymbolizePrinter osp(out); + osp.print(addresses, frames.data(), frameCount); + } } catch (const std::exception& e) { out << "\n !! caught " << folly::exceptionStr(e) << "\n"; } catch (...) { @@ -118,8 +123,7 @@ std::vector getCurrentExceptions() { return exceptions; } - bool hasTraceStack = false; - const StackTraceStack* traceStack = nullptr; + StackTraceStack* traceStack = nullptr; if (!getExceptionStackTraceStackFn) { static bool logged = false; if (!logged) { @@ -134,10 +138,9 @@ std::vector getCurrentExceptions() { << "Exception stack trace invalid, stack traces not available"; logged = true; } - } else { - hasTraceStack = true; } + StackTrace* trace = traceStack ? traceStack->top() : nullptr; while (currentException) { ExceptionInfo info; // Dependent exceptions (thrown via std::rethrow_exception) aren't @@ -148,18 +151,16 @@ std::vector getCurrentExceptions() { isAbiCppException(currentException) ? currentException->exceptionType : nullptr; - if (hasTraceStack) { - CHECK(traceStack) << "Invalid trace stack!"; - info.frames.assign( - traceStack->trace.frameIPs, - traceStack->trace.frameIPs + traceStack->trace.frameCount); - traceStack = traceStack->next; + if (traceStack) { + CHECK(trace) << "Invalid trace stack!"; + info.frames.assign(trace->addresses, + trace->addresses + trace->frameCount); + trace = traceStack->next(trace); } currentException = currentException->nextException; exceptions.push_back(std::move(info)); } - - CHECK(!traceStack) << "Invalid trace stack!"; + CHECK(!trace) << "Invalid trace stack!"; return exceptions; } diff --git a/folly/experimental/exception_tracer/ExceptionTracerLib.cpp b/folly/experimental/exception_tracer/ExceptionTracerLib.cpp index eab33fd3..d4a8fa15 100644 --- a/folly/experimental/exception_tracer/ExceptionTracerLib.cpp +++ b/folly/experimental/exception_tracer/ExceptionTracerLib.cpp @@ -24,6 +24,7 @@ #include "folly/experimental/exception_tracer/StackTrace.h" #include "folly/experimental/exception_tracer/ExceptionAbi.h" #include "folly/experimental/exception_tracer/ExceptionTracer.h" +#include "folly/experimental/symbolizer/Symbolizer.h" namespace __cxxabiv1 { @@ -37,11 +38,13 @@ void __cxa_end_catch(void); } // namespace __cxxabiv1 +using namespace folly::exception_tracer; + namespace { __thread bool invalid; -__thread StackTraceStack* activeExceptions; -__thread StackTraceStack* caughtExceptions; +__thread StackTraceStack activeExceptions; +__thread StackTraceStack caughtExceptions; pthread_once_t initialized = PTHREAD_ONCE_INIT; extern "C" { @@ -86,34 +89,34 @@ void initialize() { } // namespace // This function is exported and may be found via dlsym(RTLD_NEXT, ...) -extern "C" const StackTraceStack* getExceptionStackTraceStack() { - return caughtExceptions; +extern "C" StackTraceStack* getExceptionStackTraceStack() { + return invalid ? nullptr : &caughtExceptions; } namespace { -// Make sure we're counting stack frames correctly for the "skip" argument to -// pushCurrentStackTrace, don't inline. + +// Make sure we're counting stack frames correctly, don't inline. void addActiveException() __attribute__((noinline)); void addActiveException() { pthread_once(&initialized, initialize); // Capture stack trace if (!invalid) { - if (pushCurrentStackTrace(3, &activeExceptions) != 0) { - clearStack(&activeExceptions); - clearStack(&caughtExceptions); + if (!activeExceptions.pushCurrent()) { + activeExceptions.clear(); + caughtExceptions.clear(); invalid = true; } } } -void moveTopException(StackTraceStack** from, StackTraceStack** to) { +void moveTopException(StackTraceStack& from, StackTraceStack& to) { if (invalid) { return; } - if (moveTop(from, to) != 0) { - clearStack(from); - clearStack(to); + if (!to.moveTopFrom(from)) { + from.clear(); + to.clear(); invalid = true; } } @@ -134,13 +137,13 @@ void __cxa_rethrow() { // we'll implement something simpler (and slower): we pop the exception from // the caught stack, and push it back onto the active stack; this way, our // implementation of __cxa_begin_catch doesn't have to do anything special. - moveTopException(&caughtExceptions, &activeExceptions); + moveTopException(caughtExceptions, activeExceptions); orig_cxa_rethrow(); } void* __cxa_begin_catch(void *excObj) { // excObj is a pointer to the unwindHeader in __cxa_exception - moveTopException(&activeExceptions, &caughtExceptions); + moveTopException(activeExceptions, caughtExceptions); return orig_cxa_begin_catch(excObj); } @@ -153,7 +156,10 @@ void __cxa_end_catch() { // In the rethrow case, we've already popped the exception off the // caught stack, so we don't do anything here. if (top->handlerCount == 1) { - popStackTrace(&caughtExceptions); + if (!caughtExceptions.pop()) { + activeExceptions.clear(); + invalid = true; + } } } orig_cxa_end_catch(); diff --git a/folly/experimental/exception_tracer/StackTrace.cpp b/folly/experimental/exception_tracer/StackTrace.cpp new file mode 100644 index 00000000..ef018275 --- /dev/null +++ b/folly/experimental/exception_tracer/StackTrace.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "folly/experimental/exception_tracer/StackTrace.h" + +#include +#include +#include +#include "folly/experimental/symbolizer/StackTrace.h" + +namespace folly { namespace exception_tracer { + +class StackTraceStack::Node : public StackTrace { + public: + static Node* allocate(); + void deallocate(); + + Node* next; + + private: + Node() : next(nullptr) { } + ~Node() { } +}; + +auto StackTraceStack::Node::allocate() -> Node* { + // Null pointer on error, please. + return new (std::nothrow) Node(); +} + +void StackTraceStack::Node::deallocate() { + delete this; +} + +bool StackTraceStack::pushCurrent() { + checkGuard(); + auto node = Node::allocate(); + if (!node) { + // cannot allocate memory + return false; + } + + ssize_t n = folly::symbolizer::getStackTrace(node->addresses, kMaxFrames); + if (n == -1) { + node->deallocate(); + return false; + } + node->frameCount = n; + + node->next = top_; + top_ = node; + return true; +} + +bool StackTraceStack::pop() { + checkGuard(); + if (!top_) { + return false; + } + + auto node = top_; + top_ = node->next; + node->deallocate(); + return true; +} + +bool StackTraceStack::moveTopFrom(StackTraceStack& other) { + checkGuard(); + if (!other.top_) { + return false; + } + + auto node = other.top_; + other.top_ = node->next; + node->next = top_; + top_ = node; + return true; +} + +void StackTraceStack::clear() { + checkGuard(); + while (top_) { + pop(); + } +} + +StackTrace* StackTraceStack::top() { + checkGuard(); + return top_; +} + +StackTrace* StackTraceStack::next(StackTrace* p) { + checkGuard(); + assert(p); + return static_cast(p)->next; +} + +}} // namespaces + diff --git a/folly/experimental/exception_tracer/StackTrace.h b/folly/experimental/exception_tracer/StackTrace.h index 6b2ab3c7..4218c5a1 100644 --- a/folly/experimental/exception_tracer/StackTrace.h +++ b/folly/experimental/exception_tracer/StackTrace.h @@ -18,66 +18,86 @@ #ifndef FOLLY_EXPERIMENTAL_EXCEPTION_TRACER_STACKTRACE_H_ #define FOLLY_EXPERIMENTAL_EXCEPTION_TRACER_STACKTRACE_H_ -#include -#include +#include +#include +#include -#ifdef __cplusplus -extern "C" { -#endif +namespace folly { namespace exception_tracer { + +constexpr size_t kMaxFrames = 500; + +struct StackTrace { + StackTrace() : frameCount(0) { } -typedef struct StackTrace { - uintptr_t* frameIPs; /* allocated with malloc() */ size_t frameCount; -} StackTrace; + uintptr_t addresses[kMaxFrames]; +}; -/** - * Get the current stack trace, allocating trace->frameIPs using malloc(). - * Skip the topmost "skip" frames. - * Return 0 on success, a negative value on error. - * On error, trace->frameIPs is NULL. - */ -int getCurrentStackTrace(size_t skip, StackTrace* trace); +// note: no constructor so this can be __thread. +// A StackTraceStack MUST be placed in zero-initialized memory. +class StackTraceStack { + class Node; + public: + /** + * Push the current stack trace onto the stack. + * Returns false on failure (not enough memory, getting stack trace failed), + * true on success. + */ + bool pushCurrent(); -/** - * Free data allocated in a StackTrace object. - */ -void destroyStackTrace(StackTrace* trace); + /** + * Pop the top stack trace from the stack. + * Returns true on success, false on failure (stack was empty). + */ + bool pop(); -/** - * A stack of stack traces. - */ -typedef struct StackTraceStack { - StackTrace trace; - struct StackTraceStack* next; -} StackTraceStack; - -/** - * Push the current stack trace onto the stack. - * Return 0 on success, a negative value on error. - * On error, the stack is unchanged. - */ -int pushCurrentStackTrace(size_t skip, StackTraceStack** head); + /** + * Move the top stack trace from other onto this. + * Returns true on success, false on failure (other was empty). + */ + bool moveTopFrom(StackTraceStack& other); -/** - * Pop (and destroy) the top stack trace from the stack. - */ -void popStackTrace(StackTraceStack** head); + /** + * Clear the stack. + */ -/** - * Completely empty the stack, destroying everything. - */ -void clearStack(StackTraceStack** head); + void clear(); -/** - * Move the top stack trace from one stack to another. - * Return 0 on success, a negative value on error (if the source stack is - * empty) - */ -int moveTop(StackTraceStack** from, StackTraceStack** to); + /** + * Is the stack empty? + */ + bool empty() const { return !top_; } + + /** + * Return the top stack trace, or nullptr if the stack is empty. + */ + StackTrace* top(); + + /** + * Return the stack trace following p, or nullptr if p is the bottom of + * the stack. + */ + StackTrace* next(StackTrace* p); -#ifdef __cplusplus -} /* extern "C" */ + private: + // In debug mode, we assert that we're in zero-initialized memory by + // checking that the two guards around top_ are zero. + void checkGuard() const { +#ifndef NDEBUG + assert(guard1_ == 0 && guard2_ == 0); #endif + } + +#ifndef NDEBUG + uintptr_t guard1_; +#endif + Node* top_; +#ifndef NDEBUG + uintptr_t guard2_; +#endif +}; + +}} // namespaces #endif /* FOLLY_EXPERIMENTAL_EXCEPTION_TRACER_STACKTRACE_H_ */ diff --git a/folly/experimental/symbolizer/SignalHandler.cpp b/folly/experimental/symbolizer/SignalHandler.cpp index 58e8b184..8cdf03a4 100644 --- a/folly/experimental/symbolizer/SignalHandler.cpp +++ b/folly/experimental/symbolizer/SignalHandler.cpp @@ -44,30 +44,28 @@ namespace { */ class FatalSignalCallbackRegistry { public: - typedef std::function Func; - FatalSignalCallbackRegistry(); - void add(Func func); + void add(SignalCallback func); void markInstalled(); void run(); private: std::atomic installed_; std::mutex mutex_; - std::vector handlers_; + std::vector handlers_; }; FatalSignalCallbackRegistry::FatalSignalCallbackRegistry() : installed_(false) { } -void FatalSignalCallbackRegistry::add(Func func) { +void FatalSignalCallbackRegistry::add(SignalCallback func) { std::lock_guard lock(mutex_); CHECK(!installed_) << "FatalSignalCallbackRegistry::add may not be used " "after installing the signal handlers."; - handlers_.push_back(std::move(func)); + handlers_.push_back(func); } void FatalSignalCallbackRegistry::markInstalled() { @@ -240,12 +238,7 @@ void innerSignalHandler(int signum, siginfo_t* info, void* uctx) { void signalHandler(int signum, siginfo_t* info, void* uctx) { SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); }; - try { - innerSignalHandler(signum, info, uctx); - } catch (...) { - // Ignore any exceptions. What? Exceptions? - print("Exception in innerSignalHandler!\n"); - } + innerSignalHandler(signum, info, uctx); gSignalThread = nullptr; // Kill ourselves with the previous handler. @@ -254,8 +247,8 @@ void signalHandler(int signum, siginfo_t* info, void* uctx) { } // namespace -void addFatalSignalCallback(std::function handler) { - gFatalSignalCallbackRegistry->add(std::move(handler)); +void addFatalSignalCallback(SignalCallback cb) { + gFatalSignalCallbackRegistry->add(cb); } namespace { diff --git a/folly/experimental/symbolizer/SignalHandler.h b/folly/experimental/symbolizer/SignalHandler.h index 2c13b194..febe6e66 100644 --- a/folly/experimental/symbolizer/SignalHandler.h +++ b/folly/experimental/symbolizer/SignalHandler.h @@ -41,7 +41,8 @@ void installFatalSignalHandler(); * All these fatal callback must be added before calling * installFatalSignalHandler(). */ -void addFatalSignalCallback(std::function callback); +typedef void (*SignalCallback)(void); +void addFatalSignalCallback(SignalCallback callback); }} // namespaces diff --git a/folly/experimental/symbolizer/StackTrace.cpp b/folly/experimental/symbolizer/StackTrace.cpp new file mode 100644 index 00000000..b6664bd8 --- /dev/null +++ b/folly/experimental/symbolizer/StackTrace.cpp @@ -0,0 +1,33 @@ +/* + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Must be first to ensure that UNW_LOCAL_ONLY is defined +#define UNW_LOCAL_ONLY 1 +#include + +#include "folly/experimental/symbolizer/StackTrace.h" + +namespace folly { namespace symbolizer { + +ssize_t getStackTrace(uintptr_t* addresses, size_t maxAddresses) { + static_assert(sizeof(uintptr_t) == sizeof(void*), + "uinptr_t / pointer size mismatch"); + int r = unw_backtrace(reinterpret_cast(addresses), maxAddresses); + return r < 0 ? -1 : r; +} + +}} // namespaces + diff --git a/folly/experimental/symbolizer/StackTrace.h b/folly/experimental/symbolizer/StackTrace.h new file mode 100644 index 00000000..3f18f5e5 --- /dev/null +++ b/folly/experimental/symbolizer/StackTrace.h @@ -0,0 +1,37 @@ +/* + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOLLY_SYMBOLIZER_STACKTRACE_H_ +#define FOLLY_SYMBOLIZER_STACKTRACE_H_ + +#include +#include + +namespace folly { namespace symbolizer { + +/** + * Get the current stack trace into addresses, which has room for at least + * maxAddresses frames. + * + * Returns the number of frames written in the array. + * Returns -1 on failure. + */ +ssize_t getStackTrace(uintptr_t* addresses, size_t maxAddresses); + +}} // namespaces + +#endif /* FOLLY_SYMBOLIZER_STACKTRACE_H_ */ + diff --git a/folly/experimental/symbolizer/Symbolizer.cpp b/folly/experimental/symbolizer/Symbolizer.cpp index 70d5a51f..563abb75 100644 --- a/folly/experimental/symbolizer/Symbolizer.cpp +++ b/folly/experimental/symbolizer/Symbolizer.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -// Must be first to ensure that UNW_LOCAL_ONLY is defined -#define UNW_LOCAL_ONLY 1 -#include - #include "folly/experimental/symbolizer/Symbolizer.h" #include @@ -148,62 +144,16 @@ bool parseProcMapsLine(StringPiece line, } // namespace -ssize_t getStackTrace(FrameInfo* addresses, - size_t maxAddresses, - size_t skip) { - unw_context_t uctx; - int r = unw_getcontext(&uctx); - if (r < 0) { - return -1; - } - - unw_cursor_t cursor; - size_t idx = 0; - bool first = true; - for (;;) { - if (first) { - first = false; - r = unw_init_local(&cursor, &uctx); - } else { - r = unw_step(&cursor); - if (r == 0) { - break; - } - } - if (r < 0) { - return -1; - } - - if (skip != 0) { - --skip; - continue; - } - - if (idx < maxAddresses) { - unw_word_t ip; - int rr = unw_get_reg(&cursor, UNW_REG_IP, &ip); - if (rr < 0) { - return -1; - } - - // If error, assume not a signal frame - rr = unw_is_signal_frame(&cursor); - addresses[idx] = FrameInfo(ip, (rr > 0)); - } - ++idx; - } - - return idx; -} - -void Symbolizer::symbolize(FrameInfo* addresses, size_t addressCount) { +void Symbolizer::symbolize(const uintptr_t* addresses, + SymbolizedFrame* frames, + size_t addressCount) { size_t remaining = 0; for (size_t i = 0; i < addressCount; ++i) { - auto& ainfo = addresses[i]; - if (!ainfo.found) { + auto& frame = frames[i]; + if (!frame.found) { ++remaining; - ainfo.name.clear(); - ainfo.location = Dwarf::LocationInfo(); + frame.name.clear(); + frame.location = Dwarf::LocationInfo(); } } @@ -240,27 +190,19 @@ void Symbolizer::symbolize(FrameInfo* addresses, size_t addressCount) { // See if any addresses are here for (size_t i = 0; i < addressCount; ++i) { - auto& ainfo = addresses[i]; - if (ainfo.found) { + auto& frame = frames[i]; + if (frame.found) { continue; } - uintptr_t address = ainfo.address; - - // If the next address (closer to the top of the stack) was a signal - // frame, then this is the *resume* address, which is the address - // after the location where the signal was caught. This might be in - // the next function, so subtract 1 before symbolizing. - if (i != 0 && addresses[i-1].isSignalFrame) { - --address; - } + uintptr_t address = addresses[i]; if (from > address || address >= to) { continue; } // Found - ainfo.found = true; + frame.found = true; --remaining; // Open the file on first use @@ -290,10 +232,10 @@ void Symbolizer::symbolize(FrameInfo* addresses, size_t addressCount) { } auto name = elfFile->getSymbolName(sym); if (name) { - ainfo.name = name; + frame.name = name; } - Dwarf(elfFile).findAddress(fileAddress, ainfo.location); + Dwarf(elfFile).findAddress(fileAddress, frame.location); } } @@ -304,8 +246,7 @@ namespace { const char kHexChars[] = "0123456789abcdef"; } // namespace -void SymbolizePrinter::print(const FrameInfo& ainfo) { - uintptr_t address = ainfo.address; +void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) { // Can't use sprintf, not async-signal-safe static_assert(sizeof(uintptr_t) <= 8, "huge uintptr_t?"); char buf[] = " @ 0000000000000000"; @@ -322,20 +263,20 @@ void SymbolizePrinter::print(const FrameInfo& ainfo) { doPrint(folly::StringPiece(buf, end)); char mangledBuf[1024]; - if (!ainfo.found) { + if (!frame.found) { doPrint(" (not found)\n"); return; } - if (ainfo.name.empty()) { + if (frame.name.empty()) { doPrint(" (unknown)\n"); - } else if (ainfo.name.size() >= sizeof(mangledBuf)) { + } else if (frame.name.size() >= sizeof(mangledBuf)) { doPrint(" "); - doPrint(ainfo.name); + doPrint(frame.name); doPrint("\n"); } else { - memcpy(mangledBuf, ainfo.name.data(), ainfo.name.size()); - mangledBuf[ainfo.name.size()] = '\0'; + memcpy(mangledBuf, frame.name.data(), frame.name.size()); + mangledBuf[frame.name.size()] = '\0'; char demangledBuf[1024]; demangle(mangledBuf, demangledBuf, sizeof(demangledBuf)); @@ -346,23 +287,23 @@ void SymbolizePrinter::print(const FrameInfo& ainfo) { char fileBuf[PATH_MAX]; fileBuf[0] = '\0'; - if (ainfo.location.hasFileAndLine) { - ainfo.location.file.toBuffer(fileBuf, sizeof(fileBuf)); + if (frame.location.hasFileAndLine) { + frame.location.file.toBuffer(fileBuf, sizeof(fileBuf)); doPrint(pad); doPrint(fileBuf); char buf[22]; - uint32_t n = uint64ToBufferUnsafe(ainfo.location.line, buf); + uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf); doPrint(":"); doPrint(StringPiece(buf, n)); doPrint("\n"); } - if (ainfo.location.hasMainFile) { + if (frame.location.hasMainFile) { char mainFileBuf[PATH_MAX]; mainFileBuf[0] = '\0'; - ainfo.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf)); - if (!ainfo.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) { + frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf)); + if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) { doPrint(pad); doPrint("-> "); doPrint(mainFileBuf); @@ -371,21 +312,11 @@ void SymbolizePrinter::print(const FrameInfo& ainfo) { } } -void SymbolizePrinter::print(const FrameInfo* addresses, - size_t addressesSize, +void SymbolizePrinter::print(const uintptr_t* addresses, + const SymbolizedFrame* frames, size_t frameCount) { - for (size_t i = 0; i < std::min(addressesSize, frameCount); ++i) { - auto& ainfo = addresses[i]; - print(ainfo); - } - - // Indicate the number of frames that we couldn't log due to space - if (frameCount > addressesSize) { - char buf[22]; - uint32_t n = uint64ToBufferUnsafe(frameCount - addressesSize, buf); - doPrint(" ("); - doPrint(StringPiece(buf, n)); - doPrint(" omitted, max buffer size reached)\n"); + for (size_t i = 0; i < frameCount; ++i) { + print(addresses[i], frames[i]); } } @@ -397,11 +328,5 @@ void FDSymbolizePrinter::doPrint(StringPiece sp) { writeFull(fd_, sp.data(), sp.size()); } -std::ostream& operator<<(std::ostream& out, const FrameInfo& ainfo) { - OStreamSymbolizePrinter osp(out); - osp.print(ainfo); - return out; -} - } // namespace symbolizer } // namespace folly diff --git a/folly/experimental/symbolizer/Symbolizer.h b/folly/experimental/symbolizer/Symbolizer.h index 84913526..d3e6d452 100644 --- a/folly/experimental/symbolizer/Symbolizer.h +++ b/folly/experimental/symbolizer/Symbolizer.h @@ -25,6 +25,7 @@ #include "folly/Range.h" #include "folly/experimental/symbolizer/Elf.h" #include "folly/experimental/symbolizer/Dwarf.h" +#include "folly/experimental/symbolizer/StackTrace.h" namespace folly { namespace symbolizer { @@ -33,14 +34,10 @@ namespace symbolizer { * Frame information: symbol name and location. * * Note that both name and location are references in the Symbolizer object, - * which must outlive this FrameInfo object. + * which must outlive this SymbolizedFrame object. */ -struct FrameInfo { - /* implicit */ FrameInfo(uintptr_t a=0, bool sf=false) - : address(a), - isSignalFrame(sf), - found(false) { } - uintptr_t address; +struct SymbolizedFrame { + SymbolizedFrame() : found(false) { } bool isSignalFrame; bool found; StringPiece name; @@ -52,34 +49,23 @@ struct FrameArray { FrameArray() : frameCount(0) { } size_t frameCount; - FrameInfo frames[N]; + uintptr_t addresses[N]; + SymbolizedFrame frames[N]; }; -/** - * Get the current stack trace into addresses, which has room for at least - * maxAddresses frames. Skip the first (topmost) skip entries. - * - * Returns the number of frames in the stack trace. Just like snprintf, - * if the number of frames is greater than maxAddresses, it will return - * the actual number of frames, so the stack trace was truncated iff - * the return value > maxAddresses. - * - * Returns -1 on failure. - */ -ssize_t getStackTrace(FrameInfo* addresses, - size_t maxAddresses, - size_t skip=0); - /** * Get stack trace into a given FrameArray, return true on success (and * set frameCount to the actual frame count, which may be > N) and false * on failure. */ template -bool getStackTrace(FrameArray& fa, size_t skip=0) { - ssize_t n = getStackTrace(fa.frames, N, skip); +bool getStackTrace(FrameArray& fa) { + ssize_t n = getStackTrace(fa.addresses, N); if (n != -1) { fa.frameCount = n; + for (size_t i = 0; i < fa.frameCount; ++i) { + fa.frames[i].found = false; + } return true; } else { fa.frameCount = 0; @@ -94,19 +80,21 @@ class Symbolizer { /** * Symbolize given addresses. */ - void symbolize(FrameInfo* addresses, size_t addressCount); + void symbolize(const uintptr_t* addresses, + SymbolizedFrame* frames, + size_t frameCount); template void symbolize(FrameArray& fa) { - symbolize(fa.frames, std::min(fa.frameCount, N)); + symbolize(fa.addresses, fa.frames, fa.frameCount); } /** * Shortcut to symbolize one address. */ - bool symbolize(FrameInfo& address) { - symbolize(&address, 1); - return address.found; + bool symbolize(uintptr_t address, SymbolizedFrame& frame) { + symbolize(&address, &frame, 1); + return frame.found; } private: @@ -122,14 +110,16 @@ class Symbolizer { */ class SymbolizePrinter { public: - void print(const FrameInfo& ainfo); - void print(const FrameInfo* addresses, - size_t addressesSize, + void print(uintptr_t address, const SymbolizedFrame& frame); + void print(const uintptr_t* addresses, + const SymbolizedFrame* frames, size_t frameCount); template - void print(const FrameArray& fa) { - print(fa.frames, N, fa.frameCount); + void print(const FrameArray& fa, size_t skip=0) { + if (skip < fa.frameCount) { + print(fa.addresses + skip, fa.frames + skip, fa.frameCount - skip); + } } virtual ~SymbolizePrinter() { } @@ -161,14 +151,6 @@ class FDSymbolizePrinter : public SymbolizePrinter { int fd_; }; -/** - * Print an FrameInfo to a stream. Note that the Symbolizer that - * symbolized the address must outlive the FrameInfo. Just like - * OStreamSymbolizePrinter (which it uses internally), this is not - * reentrant; do not use from signal handling code. - */ -std::ostream& operator<<(std::ostream& out, const FrameInfo& ainfo); - } // namespace symbolizer } // namespace folly diff --git a/folly/experimental/symbolizer/test/SymbolizerTest.cpp b/folly/experimental/symbolizer/test/SymbolizerTest.cpp index e9a09674..10bf05f8 100644 --- a/folly/experimental/symbolizer/test/SymbolizerTest.cpp +++ b/folly/experimental/symbolizer/test/SymbolizerTest.cpp @@ -27,10 +27,11 @@ void foo() { } TEST(Symbolizer, Single) { - FrameInfo a(reinterpret_cast(foo)); Symbolizer symbolizer; - ASSERT_TRUE(symbolizer.symbolize(a)); - EXPECT_EQ("folly::symbolizer::test::foo()", demangle(a.name.str().c_str())); + SymbolizedFrame a; + ASSERT_TRUE(symbolizer.symbolize(reinterpret_cast(foo), a)); + EXPECT_EQ("folly::symbolizer::test::foo()", + demangle(a.name.str().c_str())); auto path = a.location.file.toString(); folly::StringPiece basename(path);