// ----------------------------------------------------------------------------------------------
// Copyright (c) Mattias H�gstr�m.
// ----------------------------------------------------------------------------------------------
// This source code is subject to terms and conditions of the Microsoft Public License. A
// copy of the license can be found in the License.html file at the root of this distribution.
// If you cannot locate the Microsoft Public License, please send an email to
// dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
// by the terms of the Microsoft Public License.
// ----------------------------------------------------------------------------------------------
// You must not remove this notice, or any other, from this software.
// ----------------------------------------------------------------------------------------------
#include "Stdafx.h"
#include <Windows.h>
#include <string.h>
#include <list>
#include <tlhelp32.h>
//#include <mscoree.h>
#include "sdk\inc\dbghelp.h"
#pragma comment(lib, "sdk\\lib\\dbghelp.lib")
#include "StackWalk64Trace.h"
BOOL InitSymbolLookup(HANDLE currentProcess, const char* searchPath)
{
BOOL result = SymInitialize(currentProcess, searchPath, TRUE) == TRUE;
DWORD symOptions = 0; //SymGetOptions();
symOptions |= SYMOPT_CASE_INSENSITIVE;
symOptions |= SYMOPT_UNDNAME;
symOptions |= SYMOPT_DEFERRED_LOADS;
symOptions |= SYMOPT_LOAD_LINES;
symOptions |= SYMOPT_OMAP_FIND_NEAREST;
symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
symOptions |= SYMOPT_AUTO_PUBLICS;
symOptions |= SYMOPT_NO_IMAGE_SEARCH;
// symOptions |= SYMOPT_LOAD_ANYTHING;
SymSetOptions(symOptions);
BOOL r1 = SymSetSearchPath(currentProcess, searchPath);
BOOL r2 = SymRefreshModuleList(currentProcess);
return result;
}
std::string AnnotateStack(HANDLE currentProcess, DWORD64 addr64)
{
std::string result;
IMAGEHLP_LINE64 line = { 0 };
const int NAME_BUFFER_LEN = 255;
char buffer[NAME_BUFFER_LEN];
char symbolbytes[sizeof(IMAGEHLP_SYMBOL64) + 256 * sizeof(char)];
IMAGEHLP_MODULE64 ModuleInfo = { 0 };
PIMAGEHLP_SYMBOL64 SymbolInfo = reinterpret_cast<PIMAGEHLP_SYMBOL64>(symbolbytes);
ZeroMemory(symbolbytes, sizeof(symbolbytes));
SymbolInfo->MaxNameLength = NAME_BUFFER_LEN;
SymbolInfo->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
DWORD64 displacement = 0;
DWORD64 baseAddr = SymGetModuleBase64(currentProcess, addr64);
ZeroMemory(&ModuleInfo, sizeof(ModuleInfo));
ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
BOOL bRetModuleInfo = SymGetModuleInfo64(currentProcess, baseAddr, &ModuleInfo );
std::string ModuleName;
if( !bRetModuleInfo )
{
ModuleName = std::string("UnknownModule");
}
else
{
ModuleName = std::string(ModuleInfo.ModuleName);
}
BOOL bRetSymFromAddr = SymGetSymFromAddr64(currentProcess, addr64, &displacement, SymbolInfo );
if (!bRetSymFromAddr)
{
sprintf_s<NAME_BUFFER_LEN>(buffer, "Error: SymGetSymFromAddr64() failed. Module = %s Addr64 = 0x%08I64X BaseAddr = 0x%08I64X", ModuleName.c_str(), addr64, baseAddr);
result = std::string(buffer);
return result;
}
DWORD disp = (DWORD)displacement;
BOOL bRetLineFromAddr = SymGetLineFromAddr64(currentProcess, addr64, &disp, &line);
if(bRetLineFromAddr)
{
sprintf_s<NAME_BUFFER_LEN>(buffer, "%s!%s(%i) : 0x%08I64X", ModuleName.c_str(), SymbolInfo->Name, line.LineNumber, addr64);
result = std::string(buffer);
}
else
{
sprintf_s<NAME_BUFFER_LEN>(buffer, "%s!%s(UnknownLine) : 0x%08I64X", ModuleName.c_str(), SymbolInfo->Name, addr64);
result = std::string(buffer);
}
return result;
}
std::list<DWORD64> DumpStackTraceEx(HANDLE processHandle, HANDLE threadHandle)
{
std::list<DWORD64> stackFrames;
STACKFRAME64 stackFrame = { 0 };
CONTEXT context = { 0 };
context.ContextFlags = CONTEXT_FULL;
if ((threadHandle == GetCurrentThread()) && (processHandle == GetCurrentProcess()))
{
RtlCaptureContext(&context);
}
else
{
GetThreadContext(threadHandle, &context);
}
stackFrame.AddrPC.Offset = context.Eip;
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrFrame.Offset = context.Ebp;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrStack.Offset = context.Esp;
stackFrame.AddrStack.Mode = AddrModeFlat;
while(
StackWalk64(
IMAGE_FILE_MACHINE_I386,
processHandle,
threadHandle,
&stackFrame,
(PVOID)&context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL))
{
stackFrames.push_back(stackFrame.AddrPC.Offset);
}
return stackFrames;
}
HRESULT ListProcessThreads(DWORD dwOwnerPID, std::list<DWORD>& threadList )
{
threadList.clear();
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
// Take a snapshot of all running threads
hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
if( hThreadSnap == INVALID_HANDLE_VALUE )
{
return (S_FALSE);
}
te32.dwSize = sizeof(THREADENTRY32 );
if( !Thread32First( hThreadSnap, &te32 ) )
{
CloseHandle( hThreadSnap );
return (S_FALSE);
}
do
{
if( te32.th32OwnerProcessID == dwOwnerPID )
{
threadList.push_back(te32.th32ThreadID);
}
} while( Thread32Next(hThreadSnap, &te32 ) );
CloseHandle( hThreadSnap );
return( S_OK );
}
void DumpStackTrace()
{
HANDLE processHandle = GetCurrentProcess();
HANDLE threadHandle = GetCurrentThread();
CHAR buffer[500];
DWORD bufferSize = sizeof(buffer)*sizeof(CHAR);
DWORD len = GetCurrentDirectoryA(bufferSize, buffer);
if ((len > bufferSize) || (len == 0))
throw "GetCurrentDirectory failed";
char* onlineSearchPath ="srv*C:\\symbols*symbols*http://msdl.microsoft.com/download/symbols";
std::string fullPath = std::string(buffer);
fullPath += std::string(";");
fullPath += std::string("C:\\symbols");
fullPath += std::string(";");
fullPath += std::string(onlineSearchPath);
const char* searchPath = fullPath.c_str();
printf("SymbolPath = %s\n", searchPath);
//const char* searchPath = "C:\\developer;C:\\Symbols";
//const char* searchPath = "srv*c:\symbols*symbols*http://msdl.microsoft.com/download/symbols";
InitSymbolLookup(processHandle, searchPath /* nullptr */ ); // Only needed once, per process
std::list<DWORD64> stackFrames = DumpStackTraceEx(processHandle, threadHandle);
for (std::list<DWORD64>::iterator iter = stackFrames.begin(); iter != stackFrames.end(); iter++)
{
DWORD64 addr = *iter;
std::string result = AnnotateStack(processHandle, addr);
printf("%s\n", result.c_str());
}
SymCleanup(processHandle);
}
void DumpStackTrace(DWORD pId)
{
HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS,FALSE, pId);
if (processHandle == nullptr)
return;
CHAR buffer[500];
DWORD bufferSize = sizeof(buffer)*sizeof(CHAR);
DWORD len = GetCurrentDirectoryA(bufferSize, buffer);
if ((len > bufferSize) || (len == 0))
throw "GetCurrentDirectory failed";
char* onlineSearchPath ="srv*C:\\symbols*symbols*http://msdl.microsoft.com/download/symbols";
std::string fullPath = std::string(buffer);
fullPath += std::string(";");
fullPath += std::string("C:\\symbols");
fullPath += std::string(";");
fullPath += std::string(onlineSearchPath);
const char* searchPath = fullPath.c_str();
printf("SymbolPath = %s\n", searchPath);
//const char* searchPath = "C:\\developer;C:\\Symbols";
//const char* searchPath = "srv*c:\symbols*symbols*http://msdl.microsoft.com/download/symbols";
InitSymbolLookup(processHandle, searchPath /* nullptr */ ); // Only needed once, per process
std::list<DWORD> threads;
HRESULT hr = ListProcessThreads(pId, threads);
for(std::list<DWORD>::iterator iter = threads.begin();iter != threads.end(); iter++)
{
DWORD threadId = *iter;
HANDLE threadHandle = OpenThread(PROCESS_ALL_ACCESS, FALSE, threadId);
SuspendThread(threadHandle);
std::list<DWORD64> stackFrames = DumpStackTraceEx(processHandle, threadHandle);
for (std::list<DWORD64>::iterator iter = stackFrames.begin(); iter != stackFrames.end(); iter++)
{
DWORD64 addr = *iter;
std::string result = AnnotateStack(processHandle, addr);
printf("%s\n", result.c_str());
}
ResumeThread(threadHandle);
CloseHandle(threadHandle);
}
SymCleanup(processHandle);
CloseHandle(processHandle);
return;
}