summaryrefslogtreecommitdiffstats
path: root/src/OSSupport/MiniDumpWriter.h
blob: e5cd1a0acf419fbc6ebaff4d7c011f4e9fc930cf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

#pragma once





/** Flags to control minidump contents on supported platforms. */
enum class MiniDumpFlags
{
	WithDataSegments,
	WithFullMemory
};





#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)  // 32-bit Windows app compiled in MSVC

#include <dbghelp.h>





/** Windows 32-bit stuff:
When the server crashes, create a "dump file" containing the callstack of each thread and some variables;
let the user send us that crash file for analysis */
class MiniDumpWriter
{
	typedef BOOL(WINAPI *pMiniDumpWriteDump)(
		HANDLE hProcess,
		DWORD ProcessId,
		HANDLE hFile,
		MINIDUMP_TYPE DumpType,
		PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
		PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
		PMINIDUMP_CALLBACK_INFORMATION CallbackParam
	);

public:

	MiniDumpWriter()
	{
		// Magic code to produce dump-files on Windows if the server crashes:

		m_DbgHelp = LoadLibrary(L"DBGHELP.DLL");
		if (m_DbgHelp == INVALID_HANDLE_VALUE)
		{
			return;
		}

		s_WriteMiniDump = (pMiniDumpWriteDump)GetProcAddress(m_DbgHelp, "MiniDumpWriteDump");
		if (s_WriteMiniDump != nullptr)
		{
			ASSERT(swprintf(s_DumpFileName, ARRAYCOUNT(s_DumpFileName), L"crash_mcs_%x.dmp", GetCurrentProcessId()) > 0);
			SetUnhandledExceptionFilter(LastChanceExceptionFilter);
		}

		// End of dump-file magic
	}

	void AddDumpFlags(const MiniDumpFlags a_Flags)
	{
		switch (a_Flags)
		{
			case MiniDumpFlags::WithDataSegments:
			{
				s_DumpFlags = static_cast<MINIDUMP_TYPE>(s_DumpFlags | MINIDUMP_TYPE::MiniDumpWithDataSegs);
				break;
			}
			case MiniDumpFlags::WithFullMemory:
			{
				s_DumpFlags = static_cast<MINIDUMP_TYPE>(s_DumpFlags | MINIDUMP_TYPE::MiniDumpWithFullMemory);
				break;
			}
		}
	}

	~MiniDumpWriter()
	{
		FreeLibrary(m_DbgHelp);
	}

private:

	/** This function gets called just before the "program executed an illegal instruction and will be terminated" or similar.
	Its purpose is to create the crashdump using the dbghlp DLLs */
	static LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_ExceptionInfo)
	{
		char * newStack = &s_ExceptionStack[sizeof(s_ExceptionStack) - 1];
		char * oldStack;

		// Use the substitute stack:
		// This code is the reason why we don't support 64-bit (yet)
		_asm
		{
			mov oldStack, esp
			mov esp, newStack
		}

		MINIDUMP_EXCEPTION_INFORMATION  ExcInformation;
		ExcInformation.ThreadId = GetCurrentThreadId();
		ExcInformation.ExceptionPointers = a_ExceptionInfo;
		ExcInformation.ClientPointers = 0;

		// Write the dump file:
		HANDLE dumpFile = CreateFile(s_DumpFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
		s_WriteMiniDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, s_DumpFlags, (a_ExceptionInfo) ? &ExcInformation : nullptr, nullptr, nullptr);
		CloseHandle(dumpFile);

		// Revert to old stack:
		_asm
		{
			mov esp, oldStack
		}

		return 0;
	}

	HINSTANCE m_DbgHelp;

	static inline pMiniDumpWriteDump s_WriteMiniDump;  // The function in dbghlp DLL that creates dump files
	static inline wchar_t s_DumpFileName[MAX_PATH];  // Filename of the dump file; hes to be created before the dump handler kicks in
	static inline char s_ExceptionStack[128 * 1024];  // Substitute stack, just in case the handler kicks in because of "insufficient stack space"
	static inline MINIDUMP_TYPE s_DumpFlags = MiniDumpNormal;  // By default dump only the stack and some helpers
};

#else

struct MiniDumpWriter
{
	void AddDumpFlags(const MiniDumpFlags)
	{
	}
};

#endif