forked from reactos/reactos
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DRWTSN32] Implement basic crash report functionality
On application crash, drwtsn32 will attach to the application and try to get a dump, consisting of: - List of loaded modules - List of loaded threads - Per thread, a stacktrace - Per thread, a small hexdump from the stack - Per thread, a dump of the most common registers This dump is saved to the desktop, and the user is notified of the dump being dropped there. CORE-14180 reactos#145
- Loading branch information
1 parent
8850233
commit b90f99a
Showing
8 changed files
with
749 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
|
||
PROJECT(drwtsn32) | ||
|
||
set_cpp(WITH_RUNTIME WITH_EXCEPTIONS WITH_STL) | ||
|
||
list(APPEND CPP_SOURCE | ||
drwtsn32.cpp | ||
main.cpp | ||
stacktrace.cpp | ||
sysinfo.cpp | ||
drwtsn32.h | ||
precomp.h) | ||
|
||
add_executable(drwtsn32 ${CPP_SOURCE}) | ||
add_pch(drwtsn32 precomp.h CPP_SOURCE) | ||
set_module_type(drwtsn32 win32gui) | ||
add_importlibs(drwtsn32 dbghelp psapi advapi32 shell32 msvcrt user32 kernel32 ntdll) | ||
add_cd_file(TARGET drwtsn32 DESTINATION reactos/system32 FOR all) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
* PROJECT: Dr. Watson crash reporter | ||
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) | ||
* PURPOSE: Debug loop | ||
* COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org) | ||
*/ | ||
|
||
#include "precomp.h" | ||
#include <psapi.h> | ||
|
||
#define MS_VC_EXCEPTION_THREAD_NAME 0x406d1388 | ||
|
||
ModuleData::ModuleData(void* addr) | ||
{ | ||
BaseAddress = addr; | ||
Size = 0; | ||
Unloaded = false; | ||
} | ||
|
||
void ModuleData::Update(HANDLE hProcess) | ||
{ | ||
MODULEINFO mi = {0}; | ||
GetModuleInformation(hProcess, (HMODULE)BaseAddress, &mi, sizeof(mi)); | ||
assert(BaseAddress == mi.lpBaseOfDll); | ||
Size = mi.SizeOfImage; | ||
|
||
ModuleName.resize(MAX_PATH); | ||
DWORD dwLen = GetModuleFileNameExA(hProcess, (HMODULE)BaseAddress, &ModuleName[0], ModuleName.size()); | ||
ModuleName.resize(dwLen); | ||
} | ||
|
||
|
||
ThreadData::ThreadData(HANDLE handle) | ||
: Handle(handle) | ||
{ | ||
memset(&Context, 0, sizeof(Context)); | ||
} | ||
|
||
void ThreadData::Update() | ||
{ | ||
Context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_DEBUG_REGISTERS; | ||
GetThreadContext(Handle, &Context); | ||
} | ||
|
||
DumpData::DumpData() | ||
:ProcessID(0) | ||
,ThreadID(0) | ||
,ProcessHandle(NULL) | ||
,Event(NULL) | ||
,FirstBPHit(false) | ||
{ | ||
memset(&ExceptionInfo, 0, sizeof(ExceptionInfo)); | ||
} | ||
|
||
|
||
bool UpdateFromEvent(DEBUG_EVENT& evt, DumpData& data) | ||
{ | ||
switch(evt.dwDebugEventCode) | ||
{ | ||
case CREATE_PROCESS_DEBUG_EVENT: | ||
{ | ||
data.ProcessPath.resize(MAX_PATH); | ||
DWORD len = GetModuleFileNameExA(evt.u.CreateProcessInfo.hProcess, NULL, &data.ProcessPath[0], data.ProcessPath.size()); | ||
if (len) | ||
{ | ||
data.ProcessPath.resize(len); | ||
std::string::size_type pos = data.ProcessPath.find_last_of("\\/"); | ||
if (pos != std::string::npos) | ||
data.ProcessName = data.ProcessPath.substr(pos+1); | ||
} | ||
else | ||
{ | ||
data.ProcessPath = "??"; | ||
} | ||
if (data.ProcessName.empty()) | ||
data.ProcessName = data.ProcessPath; | ||
|
||
CloseHandle(evt.u.CreateProcessInfo.hFile); | ||
data.ProcessID = evt.dwProcessId; | ||
data.ProcessHandle = evt.u.CreateProcessInfo.hProcess; | ||
data.Threads[evt.dwThreadId] = ThreadData(evt.u.CreateProcessInfo.hThread); | ||
} | ||
break; | ||
case CREATE_THREAD_DEBUG_EVENT: | ||
data.Threads[evt.dwThreadId] = ThreadData(evt.u.CreateThread.hThread); | ||
break; | ||
case EXIT_THREAD_DEBUG_EVENT: | ||
{ | ||
ThreadMap::iterator it = data.Threads.find(evt.dwThreadId); | ||
if (it != data.Threads.end()) | ||
{ | ||
data.Threads.erase(it); | ||
} | ||
} | ||
break; | ||
case LOAD_DLL_DEBUG_EVENT: | ||
CloseHandle(evt.u.LoadDll.hFile); | ||
for (size_t n = 0; n < data.Modules.size(); ++n) | ||
{ | ||
if (data.Modules[n].BaseAddress == evt.u.LoadDll.lpBaseOfDll) | ||
{ | ||
data.Modules[n].Unloaded = false; | ||
return true; | ||
} | ||
} | ||
data.Modules.push_back(ModuleData(evt.u.LoadDll.lpBaseOfDll)); | ||
break; | ||
case UNLOAD_DLL_DEBUG_EVENT: | ||
for (size_t n = 0; n < data.Modules.size(); ++n) | ||
{ | ||
if (data.Modules[n].BaseAddress == evt.u.UnloadDll.lpBaseOfDll) | ||
data.Modules[n].Unloaded = true; | ||
} | ||
break; | ||
case OUTPUT_DEBUG_STRING_EVENT: // ignore | ||
break; | ||
case EXCEPTION_DEBUG_EVENT: | ||
if (evt.u.Exception.dwFirstChance) | ||
{ | ||
switch(evt.u.Exception.ExceptionRecord.ExceptionCode) | ||
{ | ||
case EXCEPTION_BREAKPOINT: | ||
if (!data.FirstBPHit) | ||
{ | ||
data.FirstBPHit = true; | ||
|
||
if (data.Event) | ||
{ | ||
SetEvent(data.Event); | ||
CloseHandle(data.Event); | ||
data.Event = NULL; | ||
} | ||
return true; | ||
} | ||
break; | ||
case MS_VC_EXCEPTION_THREAD_NAME: | ||
/* Thread name */ | ||
return true; | ||
case DBG_CONTROL_C: | ||
case DBG_CONTROL_BREAK: | ||
return true; | ||
} | ||
} | ||
data.ExceptionInfo = evt.u.Exception; | ||
data.ThreadID = evt.dwThreadId; | ||
return false; | ||
case EXIT_PROCESS_DEBUG_EVENT: | ||
//assert(FALSE); | ||
return false; | ||
case RIP_EVENT: | ||
//assert(FALSE); | ||
return false; | ||
default: | ||
assert(false); | ||
} | ||
return true; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* PROJECT: Dr. Watson crash reporter | ||
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) | ||
* PURPOSE: Project header | ||
* COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org) | ||
*/ | ||
|
||
#pragma once | ||
|
||
|
||
struct ModuleData | ||
{ | ||
std::string ModuleName; | ||
void *BaseAddress; | ||
DWORD Size; | ||
bool Unloaded; | ||
|
||
|
||
ModuleData(void* addr); | ||
void Update(HANDLE hProcess); | ||
}; | ||
|
||
struct ThreadData | ||
{ | ||
HANDLE Handle; | ||
CONTEXT Context; | ||
|
||
ThreadData(HANDLE handle = NULL); | ||
|
||
void Update(); | ||
}; | ||
|
||
typedef std::vector<ModuleData> ModuleList; | ||
typedef std::map<DWORD, ThreadData> ThreadMap; | ||
|
||
class DumpData | ||
{ | ||
public: | ||
std::string ProcessPath; | ||
std::string ProcessName; | ||
DWORD ProcessID; | ||
DWORD ThreadID; | ||
HANDLE ProcessHandle; | ||
ModuleList Modules; | ||
ThreadMap Threads; | ||
EXCEPTION_DEBUG_INFO ExceptionInfo; | ||
HANDLE Event; | ||
bool FirstBPHit; | ||
|
||
DumpData(); | ||
}; | ||
|
||
#define NEWLINE "\r\n" | ||
|
||
/* main.cpp */ | ||
void xfprintf(FILE* stream, const char *fmt, ...); | ||
|
||
/* drwtsn32.cpp */ | ||
bool UpdateFromEvent(DEBUG_EVENT& evt, DumpData& data); | ||
|
||
/* sysinfo.cpp */ | ||
void PrintSystemInfo(FILE* output, DumpData& data); | ||
|
||
/* stacktrace.cpp */ | ||
void BeginStackBacktrace(DumpData& data); | ||
void PrintStackBacktrace(FILE* output, DumpData& data, ThreadData& thread); | ||
void EndStackBacktrace(DumpData& data); | ||
|
Oops, something went wrong.