summaryrefslogtreecommitdiff
path: root/zen/debug_minidump.cpp
blob: 29500ae42047814aa116f72dddf7ef957f473719 (plain)
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
140
141
142
143
// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl.html       *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
// **************************************************************************

#include "debug_minidump.h"
#include <string>
#include <sstream>
#include <cassert>
#include <cstdlib>   //malloc(), free()
#include <zen/file_error.h>
#include <zen/scope_guard.h>
#include <zen/time.h>
#include "win.h"     //includes "windows.h"
#include "DbgHelp.h" //available for MSC only
#pragma comment(lib, "Dbghelp.lib")

using namespace zen;


namespace
{
LONG WINAPI writeDumpOnException(EXCEPTION_POINTERS* pExceptionInfo) //blocks showing message boxes on success and error!
{
    assert(false);

    const Zstring filename = L"CrashDump " + formatTime<Zstring>(L"%Y-%m-%d %H%M%S") + L".dmp";

    {
        HANDLE hFile = ::CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
        if (hFile == INVALID_HANDLE_VALUE)
        {
            ::MessageBox(nullptr, (replaceCpy<std::wstring>(L"Cannot write file %x.", L"%x", fmtFileName(filename)) + L"\n\n" + formatSystemError(L"CreateFile", ::GetLastError())).c_str(), L"Application Crash", MB_SERVICE_NOTIFICATION | MB_ICONERROR);
            std::terminate();
        }
        ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));

        MINIDUMP_EXCEPTION_INFORMATION exInfo = {};
        exInfo.ThreadId          = ::GetCurrentThreadId();
        exInfo.ExceptionPointers = pExceptionInfo;
        exInfo.ClientPointers    = FALSE;

        MINIDUMP_EXCEPTION_INFORMATION* exceptParam = pExceptionInfo ? &exInfo : nullptr;

        if (!::MiniDumpWriteDump(::GetCurrentProcess  (), //__in  HANDLE hProcess,
                                 ::GetCurrentProcessId(), //__in  DWORD ProcessId,
                                 hFile,                   //__in  HANDLE hFile,
                                 MiniDumpWithDataSegs,    //__in  MINIDUMP_TYPE DumpType,  ->Standard: MiniDumpNormal, Medium: MiniDumpWithDataSegs, Full: MiniDumpWithFullMemory
                                 exceptParam,             //__in  PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
                                 nullptr,                 //__in  PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
                                 nullptr))                //__in  PMINIDUMP_CALLBACK_INFORMATION CallbackParam
        {
            ::MessageBox(nullptr, (replaceCpy<std::wstring>(L"Cannot write file %x.", L"%x", fmtFileName(filename)) + L"\n\n" + formatSystemError(L"MiniDumpWriteDump", ::GetLastError())).c_str(), L"Application Crash", MB_SERVICE_NOTIFICATION | MB_ICONERROR);
            std::terminate();
        }
    } //close file before showing success message

	//attention: the app has not yet officially crashed! => use MB_SERVICE_NOTIFICATION to avoid Win32 GUI callbacks while message box is shown!
    ::MessageBox(nullptr, replaceCpy<std::wstring>(L"Crash dump file %x written!", L"%x", fmtFileName(filename)).c_str(), L"Application Crash", MB_SERVICE_NOTIFICATION | MB_ICONERROR);
	std::terminate();

    return EXCEPTION_EXECUTE_HANDLER;
}

//ensure that a dump-file is written for uncaught exceptions
struct OnStartup { 
	OnStartup() 
	{
		/*LPTOP_LEVEL_EXCEPTION_FILTER oldFilter = */ ::SetUnhandledExceptionFilter(writeDumpOnException);
		//oldFilter == &__CxxUnhandledExceptionFilter() by default!
	}} dummy;
}


void debug_tools::writeMinidump()
{
    //force exception to catch the state of this thread and hopefully get a valid call stack
    __try
    {
        ::RaiseException(EXCEPTION_BREAKPOINT, 0, 0, nullptr);
    }
    __except (writeDumpOnException(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER) {}
    //don't use EXCEPTION_CONTINUE_EXECUTION: although used in most minidump examples this resulted in an infinite loop in tests
    //although it really should not: http://msdn.microsoft.com/en-us/library/c34eyfac.aspx
}


/*
No need to include the "operator new" declarations into every compilation unit:

[basic.stc.dynamic]
"A C++ program shall provide at most one definition of a replaceable allocation or deallocation function.
Any such function definition replaces the default version provided in the library (17.6.4.6).
The following allocation and deallocation functions (18.6) are implicitly declared in global scope in each translation unit of a program.
void* operator new(std::size_t);
void* operator new[](std::size_t);
void operator delete(void*);
void operator delete[](void*);"
*/

namespace
{
class BadAllocDetailed : public std::bad_alloc
{
public:
    explicit BadAllocDetailed(size_t allocSize)
    {
        errorMsg = "Memory allocation failed: ";
        errorMsg += numberToString(allocSize);
    }

    virtual const char* what() const throw()
    {
        return errorMsg.c_str();
    }

private:
    template <class T>
    static std::string numberToString(const T& number) //convert number to string the (slow) C++ way
    {
        std::ostringstream ss;
        ss << number;
        return ss.str();
    }

    std::string errorMsg;
};
}

void* operator new(size_t size)
{
    if (void* ptr = ::malloc(size))
        return ptr;

    debug_tools::writeMinidump();
    throw ::BadAllocDetailed(size);
}

void operator delete(void* ptr) { ::free(ptr); }

void* operator new[](size_t size) { return operator new(size); }
void operator delete[](void* ptr) { operator delete(ptr); }
bgstack15