summaryrefslogtreecommitdiff
path: root/src/mongo/util/stacktrace_windows.cpp
diff options
context:
space:
mode:
authorJonathan Reams <jbreams@mongodb.com>2016-10-18 10:04:03 -0400
committerJonathan Reams <jbreams@mongodb.com>2016-11-03 16:51:45 -0400
commit3c5f3e81edb5a49db5e66b9cb549e6c0ae58ca47 (patch)
treedf18a2a004c30cc1e62a7474667e8a9c1b915496 /src/mongo/util/stacktrace_windows.cpp
parent1c543d56ab0ebd5f061682f2808d37f73e91f5d6 (diff)
downloadmongo-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.cpp133
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;