diff options
author | Jonathan Reams <jbreams@mongodb.com> | 2016-10-18 10:04:03 -0400 |
---|---|---|
committer | Jonathan Reams <jbreams@mongodb.com> | 2016-11-03 16:51:45 -0400 |
commit | 3c5f3e81edb5a49db5e66b9cb549e6c0ae58ca47 (patch) | |
tree | df18a2a004c30cc1e62a7474667e8a9c1b915496 /src/mongo/util/stacktrace_windows.cpp | |
parent | 1c543d56ab0ebd5f061682f2808d37f73e91f5d6 (diff) | |
download | mongo-3c5f3e81edb5a49db5e66b9cb549e6c0ae58ca47.tar.gz |
SERVER-26270 Initialize windows stacktraces at startup
Diffstat (limited to 'src/mongo/util/stacktrace_windows.cpp')
-rw-r--r-- | src/mongo/util/stacktrace_windows.cpp | 133 |
1 files changed, 100 insertions, 33 deletions
diff --git a/src/mongo/util/stacktrace_windows.cpp b/src/mongo/util/stacktrace_windows.cpp index bc7f9ee1313..ae0ed8a0997 100644 --- a/src/mongo/util/stacktrace_windows.cpp +++ b/src/mongo/util/stacktrace_windows.cpp @@ -33,6 +33,7 @@ #include <DbgHelp.h> #include <boost/filesystem/operations.hpp> +#include <boost/optional.hpp> #include <cstdio> #include <cstdlib> #include <iostream> @@ -40,32 +41,96 @@ #include <string> #include <vector> +#include "mongo/base/disallow_copying.h" +#include "mongo/base/init.h" +#include "mongo/stdx/memory.h" #include "mongo/util/assert_util.h" #include "mongo/util/concurrency/mutex.h" #include "mongo/util/log.h" +#include "mongo/util/text.h" namespace mongo { +namespace { +const auto kPathBufferSize = 1024; -/** - * Get the path string to be used when searching for PDB files. - * - * @param process Process handle - * @return searchPath Returned search path string - */ -static const char* getSymbolSearchPath(HANDLE process) { - static std::string symbolSearchPath; - - if (symbolSearchPath.empty()) { - static const size_t bufferSize = 1024; - std::unique_ptr<char[]> pathBuffer(new char[bufferSize]); - GetModuleFileNameA(NULL, pathBuffer.get(), bufferSize); - boost::filesystem::path exePath(pathBuffer.get()); - symbolSearchPath = exePath.parent_path().string(); - symbolSearchPath += ";C:\\Windows\\System32;C:\\Windows"; +// On Windows the symbol handler must be initialized at process startup and cleaned up at shutdown. +// This class wraps up that logic and gives access to the process handle associated with the +// symbol handler. Because access to the symbol handler API is not thread-safe, it also provides +// a lock/unlock method so the whole symbol handler can be used with a stdx::lock_guard. +class SymbolHandler { + MONGO_DISALLOW_COPYING(SymbolHandler); + +public: + SymbolHandler() { + auto handle = GetCurrentProcess(); + + std::wstring modulePath(kPathBufferSize, 0); + const auto pathSize = GetModuleFileNameW(nullptr, &modulePath.front(), modulePath.size()); + invariant(pathSize != 0); + modulePath.resize(pathSize); + boost::filesystem::wpath exePath(modulePath); + + std::wstringstream symbolPathBuilder; + symbolPathBuilder << exePath.parent_path().wstring() + << L";C:\\Windows\\System32;C:\\Windows"; + const auto symbolPath = symbolPathBuilder.str(); + + BOOL ret = SymInitializeW(handle, symbolPath.c_str(), TRUE); + if (ret == FALSE) { + error() << "Stack trace initialization failed, SymInitialize failed with error " + << errnoWithDescription(); + return; + } + + _processHandle = handle; + _origOptions = SymGetOptions(); + SymSetOptions(_origOptions | SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS); + } + + ~SymbolHandler() { + SymSetOptions(_origOptions); + SymCleanup(getHandle()); } - return symbolSearchPath.c_str(); + + HANDLE getHandle() const { + return _processHandle.value(); + } + + explicit operator bool() const { + return static_cast<bool>(_processHandle); + } + + void lock() { + _mutex.lock(); + } + + void unlock() { + _mutex.unlock(); + } + + static SymbolHandler& instance() { + static SymbolHandler globalSymbolHandler; + return globalSymbolHandler; + } + +private: + boost::optional<HANDLE> _processHandle; + stdx::mutex _mutex; + DWORD _origOptions; +}; + +MONGO_INITIALIZER(IntializeSymbolHandler)(::mongo::InitializerContext* ctx) { + // We call this to ensure that the symbol handler is initialized in a single-threaded + // context. The constructor of SymbolHandler does all the error handling, so we don't need to + // do anything with the return value. Just make sure it gets called. + SymbolHandler::instance(); + + // Initializing the symbol handler is not a fatal error, so we always return Status::OK() here. + return Status::OK(); } +} // namespace + /** * Get the display name of the executable module containing the specified address. * @@ -174,7 +239,6 @@ void printStackTrace(std::ostream& os) { printWindowsStackTrace(context, os); } -static SimpleMutex _stackTraceMutex; /** * Print stack trace (using a specified stack context) to "os" @@ -183,17 +247,13 @@ static SimpleMutex _stackTraceMutex; * @param os ostream& to receive printed stack backtrace */ void printWindowsStackTrace(CONTEXT& context, std::ostream& os) { - stdx::lock_guard<SimpleMutex> lk(_stackTraceMutex); - HANDLE process = GetCurrentProcess(); - BOOL ret = SymInitialize(process, getSymbolSearchPath(process), TRUE); - if (ret == FALSE) { - DWORD dosError = GetLastError(); - log() << "Stack trace failed, SymInitialize failed with error " << std::dec << dosError; + auto& symbolHandler = SymbolHandler::instance(); + stdx::lock_guard<SymbolHandler> lk(symbolHandler); + + if (!symbolHandler) { + error() << "Stack trace failed, symbol handler returned an invalid handle."; return; } - DWORD options = SymGetOptions(); - options |= SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS; - SymSetOptions(options); STACKFRAME64 frame64; memset(&frame64, 0, sizeof(frame64)); @@ -229,26 +289,33 @@ void printWindowsStackTrace(CONTEXT& context, std::ostream& os) { size_t moduleWidth = 0; size_t sourceWidth = 0; for (size_t i = 0; i < maxBackTraceFrames; ++i) { - ret = StackWalk64( - imageType, process, GetCurrentThread(), &frame64, &context, NULL, NULL, NULL, NULL); + BOOL ret = StackWalk64(imageType, + symbolHandler.getHandle(), + GetCurrentThread(), + &frame64, + &context, + NULL, + NULL, + NULL, + NULL); if (ret == FALSE || frame64.AddrReturn.Offset == 0) { break; } DWORD64 address = frame64.AddrPC.Offset; - getModuleName(process, address, &traceItem.moduleName); + getModuleName(symbolHandler.getHandle(), address, &traceItem.moduleName); size_t width = traceItem.moduleName.length(); if (width > moduleWidth) { moduleWidth = width; } - getSourceFileAndLineNumber(process, address, &traceItem.sourceAndLine); + getSourceFileAndLineNumber(symbolHandler.getHandle(), address, &traceItem.sourceAndLine); width = traceItem.sourceAndLine.length(); if (width > sourceWidth) { sourceWidth = width; } - getsymbolAndOffset(process, address, symbolBuffer, &traceItem.symbolAndOffset); + getsymbolAndOffset( + symbolHandler.getHandle(), address, symbolBuffer, &traceItem.symbolAndOffset); traceList.push_back(traceItem); } - SymCleanup(process); // print list ++moduleWidth; |