I am trying to read a dump to get the call stack where a crash happened.
1st Scenario - When reading and writing the dump file in the same application, I am getting the right call stack from the dump file.
2nd Scenario - When the crash dump is created in one application and the dump file is read in another application, my code is not working in this scenario.
Below code snippet works in the first scenario, but does not work in the second scenario.
// Define a callback function to process the stack frames.
HANDLE hThread ;
BOOL CALLBACK MyStackWalkProc(
_In_ LPVOID pContext,
_In_ ULONG_PTR dwFrameAddr,
_In_ ULONG dwFrameNum
)
{
// Cast the context pointer to a STACKFRAME pointer.
LPSTACKFRAME64 pStackFrame = reinterpret_cast<LPSTACKFRAME64>(pContext);
// Advance to the next stack frame.
return StackWalk64(
IMAGE_FILE_MACHINE_I386, // machine type
hProcess, // process handle
hThread , // thread handle
pStackFrame, // current stack frame
NULL, // context pointer (unused)
NULL, // read memory callback (unused)
SymFunctionTableAccess64, // function table access callback
SymGetModuleBase64, // module base address callback
NULL // address translation callback (unused)
);
}
// Calls ProcessChunk with each chunk of the file.
void ReadInChunks(const WCHAR* pszFileName) {
// Offsets must be a multiple of the system's allocation granularity. We
// guarantee this by making our view size equal to the allocation granularity.
SYSTEM_INFO sysinfo = { 0 };
::GetSystemInfo(&sysinfo);
DWORD cbView = sysinfo.dwAllocationGranularity;
HANDLE hfile = ::CreateFileW(pszFileName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hfile != INVALID_HANDLE_VALUE) {
LARGE_INTEGER file_size = { 0 };
::GetFileSizeEx(hfile, &file_size);
const unsigned long long cbFile =
static_cast<unsigned long long>(file_size.QuadPart);
HANDLE hmap = ::CreateFileMappingW(hfile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hmap != NULL)
{
const char* pView = static_cast<const char*>(
::MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, 0));
PVOID strptr = NULL;
ULONG ssiz = 0;
PMINIDUMP_DIRECTORY dudir;
bool res = MiniDumpReadDumpStream((PVOID)pView, ThreadListStream, &dudir, &strptr, &ssiz);
if (res && strptr != NULL)
{
PMINIDUMP_THREAD_LIST tlist = (PMINIDUMP_THREAD_LIST)strptr;
for (ULONG32 i = 0; i < tlist->NumberOfThreads; i++)
{
ULONG32 ThreadId = tlist->Threads[i].ThreadId;
ULONG32 SuspendCount = tlist->Threads[i].SuspendCount;
ULONG32 PriorityClass = tlist->Threads[i].PriorityClass;
ULONG64 dsiz = tlist->Threads[i].ThreadContext.DataSize;
ULONG64 rva = tlist->Threads[i].ThreadContext.Rva;
ULONG64 memsta = tlist->Threads[i].Stack.StartOfMemoryRange;
ULONG64 memsiz = tlist->Threads[i].Stack.Memory.DataSize;
ULONG64 memrva = tlist->Threads[i].Stack.Memory.Rva;
std::cout << "ThreadID : " << ThreadId << "\t";
std::cout << "SuspendCount : " << SuspendCount << "\t";
std::cout << "PriorityClass : " << PriorityClass << "\t";
printf("look in debugger %I64x\t%I64x\t%I64x\t%I64x\t%I64x\n",
dsiz, rva, memsta, memsiz, memrva);
}
_tagSTACKFRAME64 tsf = { 0 };
printf("%zx\n", sizeof(tsf));
}
PVOID excepPtr = NULL;
bool res1 = MiniDumpReadDumpStream((PVOID)pView, ExceptionStream, &dudir, &excepPtr, &ssiz);
if (res1 && excepPtr != NULL)
{
PMINIDUMP_EXCEPTION_STREAM streamData = (PMINIDUMP_EXCEPTION_STREAM)excepPtr;
std::cout << "Thread ID: " << streamData->ThreadId << std::endl;
std::cout << "Reason of exception: " << std::hex << streamData->ExceptionRecord.ExceptionCode << std::endl;
DWORD error;
HANDLE hProcess;
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
hProcess = GetCurrentProcess();
std::cout << "Process HANDLE: " << hProcess << std::endl;
//DWORD processId = GetCurrentProcessId();
if (!SymInitialize(hProcess, "C:\\Workspace\\Code\\Test\\WriteCrashDump\\Debug", TRUE))
{
// SymInitialize failed
error = GetLastError();
printf("SymInitialize returned error : %d\n", error);
return;
}
CHAR path[MAX_PATH];
if (SymGetSearchPath(hProcess, path, MAX_PATH))
{
std::cout << path << std::endl;
}
// Walk the call stack of the specified thread.
hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, streamData->ThreadId);//GetCurrentThread();
std::cout << "Thread HANDLE: " << hProcess << std::endl;
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
memcpy(&context, (char*)pView + streamData->ThreadContext.Rva, min(sizeof(context), streamData->ThreadContext.DataSize));
context.ContextFlags = CONTEXT_FULL;
//GetThreadContext(hThread, &context);
STACKFRAME64 stackFrame;
memset(&stackFrame, 0, sizeof(stackFrame));
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrStack.Mode = AddrModeFlat;
stackFrame.AddrPC.Offset = context.Eip;
stackFrame.AddrFrame.Offset = context.Ebp;
stackFrame.AddrStack.Offset = context.Esp;
while (true)
{
if (!MyStackWalkProc(&stackFrame, 0, 0)) {
break;
}
else
{
IMAGEHLP_LINE64 line;
ZeroMemory(&line, sizeof(line));
line.SizeOfStruct = sizeof(line);
DWORD displacement;
SymGetLineFromAddr64(GetCurrentProcess(), stackFrame.AddrPC.Offset, &displacement, &line);
DWORD64 functionOffset = stackFrame.AddrPC.Offset;
char symbolBuffer[sizeof(IMAGEHLP_SYMBOL64) + 256];
PIMAGEHLP_SYMBOL64 symbol = (PIMAGEHLP_SYMBOL64)symbolBuffer;
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol->MaxNameLength = 256;
if (SymGetSymFromAddr64(GetCurrentProcess(), functionOffset, NULL, symbol))
{
std::cout<<"Function: "<< symbol->Name<< "\t Line no: "<< std::dec<<line.LineNumber << "\t File: "<<line.FileName<<std::endl;
}
}
}
SymCleanup(hProcess);
CloseHandle(hProcess);
}
UnmapViewOfFile(pView);
::CloseHandle(hmap);
}
::CloseHandle(hfile);
}
else
{
std::cout << "File open Error = " << GetLastError() << std::endl;
}
}
int main()
{
ReadInChunks((WCHAR*)L"C:\\Path\\To\\v1.0-20230508-122121-12184-12324.dmp");
std::cout << "Hello World!\n";
}
Please help me to find what I am doing wrong.
HANDLE
were to be stored in the mini dump, it is meaningless now. The mini dump is all you have, but even the tiniest of dumps contains enough information to reconstruct the call stack at the point of failure. If you need more information than is available you will have to change how much information is included. CallingMiniDumpWriteDump
, either from custom code or through the system, allows you to include the entire memory of that process, so it's unusual that you want to read the memory after the fact. – Ketchan