/* 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 .
*
* 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::kDefault
#include "mongo/platform/basic.h"
#include "mongo/util/stacktrace.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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 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 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 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 symbolCharBuffer( new char[symbolBufferSize] );
memset( symbolCharBuffer.get(), 0, symbolBufferSize );
SYMBOL_INFO* symbolBuffer = reinterpret_cast( symbolCharBuffer.get() );
symbolBuffer->SizeOfStruct = sizeof(SYMBOL_INFO);
symbolBuffer->MaxNameLen = nameSize;
// build list
std::vector 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 );
}
}