diff --git a/src/crash_writer_minidump.cpp b/src/crash_writer_minidump.cpp index da07e5a01..61a67ff1e 100644 --- a/src/crash_writer_minidump.cpp +++ b/src/crash_writer_minidump.cpp @@ -1,153 +1,153 @@ -// Copyright (c) 2014, Thomas Goyne -// -// 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 -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -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 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(GetProcAddress(module, "MiniDumpWriteDump")); - if (!fn) { - FreeLibrary(module); - return; - } - - std::unique_lock 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; -} - -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(); -} - -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(); - } -} -} +// Copyright (c) 2014, Thomas Goyne +// +// 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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +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 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(GetProcAddress(module, "MiniDumpWriteDump")); + if (!fn) { + FreeLibrary(module); + return; + } + + std::unique_lock 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; +} + +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(); +} + +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(); + } +} +}