Write minidumps on windows rather than just basic stack traces
This commit is contained in:
parent
4b6946dcec
commit
aab025c830
4 changed files with 161 additions and 1 deletions
|
@ -348,7 +348,10 @@
|
||||||
<ClCompile Include="$(SrcDir)command\video.cpp" />
|
<ClCompile Include="$(SrcDir)command\video.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)command\vis_tool.cpp" />
|
<ClCompile Include="$(SrcDir)command\vis_tool.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)compat.cpp" />
|
<ClCompile Include="$(SrcDir)compat.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)crash_writer.cpp" />
|
<ClCompile Include="$(SrcDir)crash_writer.cpp">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(SrcDir)crash_writer_minidump.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)dialog_about.cpp" />
|
<ClCompile Include="$(SrcDir)dialog_about.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)dialog_attachments.cpp" />
|
<ClCompile Include="$(SrcDir)dialog_attachments.cpp" />
|
||||||
<ClCompile Include="$(SrcDir)dialog_automation.cpp" />
|
<ClCompile Include="$(SrcDir)dialog_automation.cpp" />
|
||||||
|
|
|
@ -1238,6 +1238,9 @@
|
||||||
<ClCompile Include="$(SrcDir)crash_writer.cpp">
|
<ClCompile Include="$(SrcDir)crash_writer.cpp">
|
||||||
<Filter>Utilities\Logging</Filter>
|
<Filter>Utilities\Logging</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(SrcDir)crash_writer_minidump.cpp">
|
||||||
|
<Filter>Utilities\Logging</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="$(SrcDir)res/res.rc">
|
<ResourceCompile Include="$(SrcDir)res/res.rc">
|
||||||
|
|
153
src/crash_writer_minidump.cpp
Normal file
153
src/crash_writer_minidump.cpp
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "crash_writer.h"
|
||||||
|
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
#include <libaegisub/fs.h>
|
||||||
|
#include <libaegisub/util.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <DbgHelp.h>
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
extern EXCEPTION_POINTERS *wxGlobalSEInformation;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
wchar_t crash_dump_path[MAX_PATH];
|
||||||
|
agi::fs::path crashlog_path;
|
||||||
|
|
||||||
|
using MiniDumpWriteDump = BOOL(WINAPI *)(
|
||||||
|
HANDLE hProcess,
|
||||||
|
DWORD dwPid,
|
||||||
|
HANDLE hFile,
|
||||||
|
MINIDUMP_TYPE DumpType,
|
||||||
|
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||||||
|
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
||||||
|
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
||||||
|
|
||||||
|
struct dump_thread_state {
|
||||||
|
std::mutex start_mutex;
|
||||||
|
std::condition_variable start_cv;
|
||||||
|
|
||||||
|
std::atomic<bool> exit = false;
|
||||||
|
EXCEPTION_POINTERS *ep = nullptr;
|
||||||
|
DWORD thread_id = 0;
|
||||||
|
|
||||||
|
// Must be last so everything else is initialized before it
|
||||||
|
std::thread thread;
|
||||||
|
|
||||||
|
dump_thread_state() : thread([&] { main(); }) { }
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
auto module = LoadLibrary(L"dbghelp.dll");
|
||||||
|
if (!module) return;
|
||||||
|
|
||||||
|
auto fn = reinterpret_cast<MiniDumpWriteDump>(GetProcAddress(module, "MiniDumpWriteDump"));
|
||||||
|
if (!fn) {
|
||||||
|
FreeLibrary(module);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(start_mutex);
|
||||||
|
start_cv.wait(lock, [&] { return ep || exit; });
|
||||||
|
if (ep)
|
||||||
|
write_dump(fn);
|
||||||
|
FreeLibrary(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_dump(MiniDumpWriteDump fn) {
|
||||||
|
auto file = CreateFile(crash_dump_path,
|
||||||
|
GENERIC_WRITE,
|
||||||
|
0, // no sharing
|
||||||
|
nullptr,
|
||||||
|
CREATE_NEW,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
nullptr);
|
||||||
|
if (file == INVALID_HANDLE_VALUE) return;
|
||||||
|
|
||||||
|
MINIDUMP_EXCEPTION_INFORMATION info;
|
||||||
|
info.ThreadId = thread_id;
|
||||||
|
info.ExceptionPointers = ep;
|
||||||
|
info.ClientPointers = FALSE;
|
||||||
|
|
||||||
|
fn(GetCurrentProcess(), GetCurrentProcessId(), file, MiniDumpNormal, &info, nullptr, nullptr);
|
||||||
|
|
||||||
|
CloseHandle(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<dump_thread_state> dump_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace crash_writer {
|
||||||
|
void Initialize(agi::fs::path const& path) {
|
||||||
|
crashlog_path = path / "crashlog.txt";
|
||||||
|
|
||||||
|
auto dump_path = path / "crashdumps";
|
||||||
|
agi::fs::CreateDirectory(dump_path);
|
||||||
|
|
||||||
|
const auto path_str = (dump_path / GetVersionNumber()).wstring();
|
||||||
|
wcscpy_s(crash_dump_path, path_str.c_str());
|
||||||
|
auto len = path_str.size();
|
||||||
|
|
||||||
|
const auto t = time(nullptr);
|
||||||
|
struct tm tm;
|
||||||
|
localtime_s(&tm, &t);
|
||||||
|
|
||||||
|
len += wcsftime(crash_dump_path + len, MAX_PATH - len, L"-%Y-%m-%d-%H-%M-%S-", &tm);
|
||||||
|
len += swprintf_s(crash_dump_path + len, MAX_PATH - len, L"%d", GetCurrentProcessId());
|
||||||
|
wcscpy_s(crash_dump_path + len, MAX_PATH - len, L".dmp");
|
||||||
|
|
||||||
|
if (!dump_thread)
|
||||||
|
dump_thread = agi::util::make_unique<dump_thread_state>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cleanup() {
|
||||||
|
dump_thread->exit = true;
|
||||||
|
dump_thread->start_cv.notify_all();
|
||||||
|
dump_thread->thread.join();
|
||||||
|
dump_thread.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write() {
|
||||||
|
dump_thread->ep = wxGlobalSEInformation;
|
||||||
|
dump_thread->thread_id = GetCurrentThreadId();
|
||||||
|
dump_thread->start_cv.notify_all();
|
||||||
|
dump_thread->thread.join();
|
||||||
|
dump_thread.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(std::string const& error) {
|
||||||
|
boost::filesystem::ofstream file(crashlog_path, std::ios::app);
|
||||||
|
if (file.is_open()) {
|
||||||
|
file << agi::util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n");
|
||||||
|
file << boost::format("VER - %s\n") % GetAegisubLongVersionString();
|
||||||
|
file << boost::format("EXC - Aegisub has crashed with unhandled exception \"%s\".\n") % error;
|
||||||
|
file << "----------------------------------------\n\n";
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -311,6 +311,7 @@ int AegisubApp::OnExit() {
|
||||||
|
|
||||||
// Keep this last!
|
// Keep this last!
|
||||||
delete agi::log::log;
|
delete agi::log::log;
|
||||||
|
crash_writer::Cleanup();
|
||||||
|
|
||||||
return wxApp::OnExit();
|
return wxApp::OnExit();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue