summaryrefslogtreecommitdiff
path: root/src/mongo/util/stacktrace_windows.cpp
diff options
context:
space:
mode:
authorJonathan Reams <jbreams@mongodb.com>2015-03-30 12:14:42 -0400
committerJonathan Reams <jbreams@mongodb.com>2015-03-30 12:14:42 -0400
commit06785ef692bae4beb4df859c3d4da2e0517ba0d5 (patch)
tree0e54cb8720569d302042c80dde66580420e203a1 /src/mongo/util/stacktrace_windows.cpp
parentc7ad4563a086eb9e9aeaf5f71e7e70b3b33a178a (diff)
downloadmongo-06785ef692bae4beb4df859c3d4da2e0517ba0d5.tar.gz
SERVER-9555 Better OS detection in scons
Diffstat (limited to 'src/mongo/util/stacktrace_windows.cpp')
-rw-r--r--src/mongo/util/stacktrace_windows.cpp294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/mongo/util/stacktrace_windows.cpp b/src/mongo/util/stacktrace_windows.cpp
new file mode 100644
index 00000000000..4abc27e3c97
--- /dev/null
+++ b/src/mongo/util/stacktrace_windows.cpp
@@ -0,0 +1,294 @@
+/* Copyright 2009 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kControl
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/util/stacktrace.h"
+
+#include <DbgHelp.h>
+#include <boost/filesystem/operations.hpp>
+#include <boost/smart_ptr/scoped_array.hpp>
+#include <cstdio>
+#include <cstdlib>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "mongo/util/assert_util.h"
+#include "mongo/util/concurrency/mutex.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+
+ /**
+ * 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;
+ boost::scoped_array<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";
+ }
+ return symbolSearchPath.c_str();
+ }
+
+ /**
+ * Get the display name of the executable module containing the specified address.
+ *
+ * @param process Process handle
+ * @param address Address to find
+ * @param returnedModuleName Returned module name
+ */
+ static void getModuleName( HANDLE process, DWORD64 address, std::string* returnedModuleName ) {
+ IMAGEHLP_MODULE64 module64;
+ memset ( &module64, 0, sizeof(module64) );
+ module64.SizeOfStruct = sizeof(module64);
+ BOOL ret = SymGetModuleInfo64( process, address, &module64 );
+ if ( FALSE == ret ) {
+ returnedModuleName->clear();
+ return;
+ }
+ char* moduleName = module64.LoadedImageName;
+ char* backslash = strrchr( moduleName, '\\' );
+ if ( backslash ) {
+ moduleName = backslash + 1;
+ }
+ *returnedModuleName = moduleName;
+ }
+
+ /**
+ * Get the display name and line number of the source file containing the specified address.
+ *
+ * @param process Process handle
+ * @param address Address to find
+ * @param returnedSourceAndLine Returned source code file name with line number
+ */
+ static void getSourceFileAndLineNumber( HANDLE process,
+ DWORD64 address,
+ std::string* returnedSourceAndLine ) {
+ IMAGEHLP_LINE64 line64;
+ memset( &line64, 0, sizeof(line64) );
+ line64.SizeOfStruct = sizeof(line64);
+ DWORD displacement32;
+ BOOL ret = SymGetLineFromAddr64( process, address, &displacement32, &line64 );
+ if ( FALSE == ret ) {
+ returnedSourceAndLine->clear();
+ return;
+ }
+
+ std::string filename( line64.FileName );
+ std::string::size_type start = filename.find( "\\src\\mongo\\" );
+ if ( start == std::string::npos ) {
+ start = filename.find( "\\src\\third_party\\" );
+ }
+ if ( start != std::string::npos ) {
+ std::string shorter( "..." );
+ shorter += filename.substr( start );
+ filename.swap( shorter );
+ }
+ static const size_t bufferSize = 32;
+ boost::scoped_array<char> lineNumber( new char[bufferSize] );
+ _snprintf( lineNumber.get(), bufferSize, "(%u)", line64.LineNumber );
+ filename += lineNumber.get();
+ returnedSourceAndLine->swap( filename );
+ }
+
+ /**
+ * Get the display text of the symbol and offset of the specified address.
+ *
+ * @param process Process handle
+ * @param address Address to find
+ * @param symbolInfo Caller's pre-built SYMBOL_INFO struct (for efficiency)
+ * @param returnedSymbolAndOffset Returned symbol and offset
+ */
+ static void getsymbolAndOffset( HANDLE process,
+ DWORD64 address,
+ SYMBOL_INFO* symbolInfo,
+ std::string* returnedSymbolAndOffset ) {
+ DWORD64 displacement64;
+ BOOL ret = SymFromAddr( process, address, &displacement64, symbolInfo );
+ if ( FALSE == ret ) {
+ *returnedSymbolAndOffset = "???";
+ return;
+ }
+ std::string symbolString( symbolInfo->Name );
+ static const size_t bufferSize = 32;
+ boost::scoped_array<char> symbolOffset( new char[bufferSize] );
+ _snprintf( symbolOffset.get(), bufferSize, "+0x%x", displacement64 );
+ symbolString += symbolOffset.get();
+ returnedSymbolAndOffset->swap( symbolString );
+ }
+
+ struct TraceItem {
+ std::string moduleName;
+ std::string sourceAndLine;
+ std::string symbolAndOffset;
+ };
+
+ static const int maxBackTraceFrames = 100;
+
+ /**
+ * Print a stack backtrace for the current thread to the specified ostream.
+ *
+ * @param os ostream& to receive printed stack backtrace
+ */
+ void printStackTrace( std::ostream& os ) {
+ CONTEXT context;
+ memset( &context, 0, sizeof(context) );
+ context.ContextFlags = CONTEXT_CONTROL;
+ RtlCaptureContext( &context );
+ printWindowsStackTrace( context, os );
+ }
+
+ static SimpleMutex _stackTraceMutex( "stackTraceMutex" );
+
+ /**
+ * Print stack trace (using a specified stack context) to "os"
+ *
+ * @param context CONTEXT record for stack trace
+ * @param os ostream& to receive printed stack backtrace
+ */
+ void printWindowsStackTrace( CONTEXT& context, std::ostream& os ) {
+ SimpleMutex::scoped_lock 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 << std::endl;
+ return;
+ }
+ DWORD options = SymGetOptions();
+ options |= SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS;
+ SymSetOptions( options );
+
+ STACKFRAME64 frame64;
+ memset( &frame64, 0, sizeof(frame64) );
+
+#if defined(_M_AMD64)
+ DWORD imageType = IMAGE_FILE_MACHINE_AMD64;
+ frame64.AddrPC.Offset = context.Rip;
+ frame64.AddrFrame.Offset = context.Rbp;
+ frame64.AddrStack.Offset = context.Rsp;
+#elif defined(_M_IX86)
+ DWORD imageType = IMAGE_FILE_MACHINE_I386;
+ frame64.AddrPC.Offset = context.Eip;
+ frame64.AddrFrame.Offset = context.Ebp;
+ frame64.AddrStack.Offset = context.Esp;
+#else
+#error Neither _M_IX86 nor _M_AMD64 were defined
+#endif
+ frame64.AddrPC.Mode = AddrModeFlat;
+ frame64.AddrFrame.Mode = AddrModeFlat;
+ frame64.AddrStack.Mode = AddrModeFlat;
+
+ const size_t nameSize = 1024;
+ const size_t symbolBufferSize = sizeof(SYMBOL_INFO) + nameSize;
+ boost::scoped_array<char> symbolCharBuffer( new char[symbolBufferSize] );
+ memset( symbolCharBuffer.get(), 0, symbolBufferSize );
+ SYMBOL_INFO* symbolBuffer = reinterpret_cast<SYMBOL_INFO*>( symbolCharBuffer.get() );
+ symbolBuffer->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbolBuffer->MaxNameLen = nameSize;
+
+ // build list
+ std::vector<TraceItem> traceList;
+ TraceItem traceItem;
+ 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 );
+ if ( ret == FALSE || frame64.AddrReturn.Offset == 0 ) {
+ break;
+ }
+ DWORD64 address = frame64.AddrPC.Offset;
+ getModuleName( process, address, &traceItem.moduleName );
+ size_t width = traceItem.moduleName.length();
+ if ( width > moduleWidth ) {
+ moduleWidth = width;
+ }
+ getSourceFileAndLineNumber( process, address, &traceItem.sourceAndLine );
+ width = traceItem.sourceAndLine.length();
+ if ( width > sourceWidth ) {
+ sourceWidth = width;
+ }
+ getsymbolAndOffset( process, address, symbolBuffer, &traceItem.symbolAndOffset );
+ traceList.push_back( traceItem );
+ }
+ SymCleanup( process );
+
+ // print list
+ ++moduleWidth;
+ ++sourceWidth;
+ size_t frameCount = traceList.size();
+ for ( size_t i = 0; i < frameCount; ++i ) {
+ std::stringstream ss;
+ ss << traceList[i].moduleName << " ";
+ size_t width = traceList[i].moduleName.length();
+ while ( width < moduleWidth ) {
+ ss << " ";
+ ++width;
+ }
+ ss << traceList[i].sourceAndLine << " ";
+ width = traceList[i].sourceAndLine.length();
+ while ( width < sourceWidth ) {
+ ss << " ";
+ ++width;
+ }
+ ss << traceList[i].symbolAndOffset;
+ log() << ss.str() << std::endl;
+ }
+ }
+
+ // Print error message from C runtime, then fassert
+ int crtDebugCallback(int, char* originalMessage, int*) {
+ StringData message(originalMessage);
+ log() << "*** C runtime error: "
+ << message.substr(0, message.find('\n'))
+ << ", terminating" << std::endl;
+ fassertFailed( 17006 );
+ }
+
+}