diff --git a/build/Aegisub/Aegisub.vcxproj b/build/Aegisub/Aegisub.vcxproj index 2d0e81f3f..ebd159711 100644 --- a/build/Aegisub/Aegisub.vcxproj +++ b/build/Aegisub/Aegisub.vcxproj @@ -144,6 +144,7 @@ + @@ -347,6 +348,7 @@ + diff --git a/build/Aegisub/Aegisub.vcxproj.filters b/build/Aegisub/Aegisub.vcxproj.filters index de7a42e05..bb8fba4db 100644 --- a/build/Aegisub/Aegisub.vcxproj.filters +++ b/build/Aegisub/Aegisub.vcxproj.filters @@ -594,6 +594,9 @@ Utilities\Logging + + Utilities\Logging + Utilities @@ -1232,6 +1235,9 @@ Features\Resolution resampler + + Utilities\Logging + diff --git a/src/Makefile b/src/Makefile index bb4132655..292ee01e4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -154,6 +154,7 @@ SRC += \ colorspace.cpp \ colour_button.cpp \ compat.cpp \ + crash_writer.cpp \ dialog_about.cpp \ dialog_attachments.cpp \ dialog_automation.cpp \ diff --git a/src/crash_writer.cpp b/src/crash_writer.cpp new file mode 100644 index 000000000..e20ba8513 --- /dev/null +++ b/src/crash_writer.cpp @@ -0,0 +1,92 @@ +// 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 + +using namespace agi; + +namespace { +fs::path crashlog_path; + +#if wxUSE_STACKWALKER == 1 +class StackWalker : public wxStackWalker { + boost::filesystem::ofstream fp; + +public: + StackWalker(std::string const& cause) + : fp(crashlog_path, std::ios::app) + { + if (!fp.good()) return; + + fp << util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n"); + fp << boost::format("VER - %s\n") % GetAegisubLongVersionString(); + fp << boost::format("FTL - Beginning stack dump for \"%s\": \n") % cause; + } + + ~StackWalker() { + if (!fp.good()) return; + + fp << "End of stack dump.\n"; + fp << "----------------------------------------\n\n"; + } + + void OnStackFrame(wxStackFrame const& frame) override final { + if (!fp.good()) return; + + fp << boost::format("%03u - %p: %s") % frame.GetLevel() % frame.GetAddress() % frame.GetName().utf8_str().data(); + if (frame.HasSourceLocation()) + fp << boost::format(" on %s:%u") % frame.GetFileName().utf8_str().data() % frame.GetLine(); + + fp << "\n"; + } +}; +#endif +} + +namespace crash_writer { +void Initialize(fs::path const& path) { + crashlog_path = path / "crashlog.txt"; +} + +void Cleanup() { } + +void Write() { +#if wxUSE_STACKWALKER == 1 + StackWalker walker("Fatal exception"); + walker.WalkFromException(); +#endif +} + +void Write(std::string const& error) { + boost::filesystem::ofstream file(crashlog_path, std::ios::app); + if (file.is_open()) { + file << 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(); + } +} +} diff --git a/src/crash_writer.h b/src/crash_writer.h new file mode 100644 index 000000000..ab1c7e717 --- /dev/null +++ b/src/crash_writer.h @@ -0,0 +1,27 @@ +// 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 + +#include + +namespace crash_writer { + void Initialize(agi::fs::path const& path); + void Cleanup(); + + void Write(); + void Write(std::string const& error); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 6dc63d506..179ec6377 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,6 +34,8 @@ #include "config.h" +#include "main.h" + #include "command/command.h" #include "include/aegisub/hotkey.h" @@ -41,18 +43,18 @@ #include "ass_file.h" #include "auto4_base.h" #include "compat.h" +#include "crash_writer.h" #include "export_fixstyle.h" #include "export_framerate.h" #include "frame_main.h" #include "include/aegisub/context.h" -#include "main.h" #include "libresrc/libresrc.h" #include "options.h" #include "plugin_manager.h" #include "subs_controller.h" #include "subtitle_format.h" -#include "version.h" #include "video_context.h" +#include "version.h" #include "utils.h" #include @@ -140,6 +142,7 @@ bool AegisubApp::OnInit() { }); config::path = new agi::Path; + crash_writer::Initialize(config::path->Decode("?user")); agi::log::log = new agi::log::LogSink; #ifdef _DEBUG @@ -158,6 +161,7 @@ bool AegisubApp::OnInit() { // Local config, make ?user mean ?data so all user settings are placed in install dir config::path->SetToken("?user", config::path->Decode("?data")); config::path->SetToken("?local", config::path->Decode("?data")); + crash_writer::Initialize(config::path->Decode("?user")); } catch (agi::fs::FileSystemError const&) { // File doesn't exist or we can't read it // Might be worth displaying an error in the second case @@ -306,49 +310,6 @@ int AegisubApp::OnExit() { return wxApp::OnExit(); } -#if wxUSE_STACKWALKER == 1 -class StackWalker: public wxStackWalker { - boost::filesystem::ofstream fp; - -public: - StackWalker(std::string const& cause); - ~StackWalker(); - void OnStackFrame(wxStackFrame const& frame) override; -}; - -/// @brief Called at the start of walking the stack. -/// @param cause cause of the crash. -StackWalker::StackWalker(std::string const& cause) -: fp(config::path->Decode("?user/crashlog.txt"), std::ios::app) -{ - if (!fp.good()) return; - - fp << agi::util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n"); - fp << boost::format("VER - %s\n") % GetAegisubLongVersionString(); - fp << boost::format("FTL - Beginning stack dump for \"%s\": \n") % cause; -} - -/// @brief Callback to format a single frame -/// @param frame frame to parse. -void StackWalker::OnStackFrame(wxStackFrame const& frame) { - if (!fp.good()) return; - - fp << boost::format("%03u - %p: %s") % frame.GetLevel() % frame.GetAddress() % frame.GetName().utf8_str().data(); - if (frame.HasSourceLocation()) - fp << boost::format(" on %s:%u") % frame.GetFileName().utf8_str().data() % frame.GetLine(); - - fp << "\n"; -} - -/// @brief Called at the end of walking the stack. -StackWalker::~StackWalker() { - if (!fp.good()) return; - - fp << "End of stack dump.\n"; - fp << "----------------------------------------\n\n"; -} -#endif - /// Message displayed when an exception has occurred. const static wxString exception_message = _("Oops, Aegisub has crashed!\n\nAn attempt has been made to save a copy of your file to:\n\n%s\n\nAegisub will now close."); @@ -363,23 +324,15 @@ static void UnhandledExeception(bool stackWalk, agi::Context *c) { path /= filename; c->subsController->Save(path); -#if wxUSE_STACKWALKER == 1 - if (stackWalk) { - StackWalker walker("Fatal exception"); - walker.WalkFromException(); - } -#endif + if (stackWalk) + crash_writer::Write(); // Inform user of crash. wxMessageBox(wxString::Format(exception_message, path.wstring()), _("Program error"), wxOK | wxICON_ERROR | wxCENTER, nullptr); } else if (LastStartupState) { -#if wxUSE_STACKWALKER == 1 - if (stackWalk) { - StackWalker walker("Fatal exception"); - walker.WalkFromException(); - } -#endif + if (stackWalk) + crash_writer::Write(); wxMessageBox(wxString::Format("Aegisub has crashed while starting up!\n\nThe last startup step attempted was: %s.", LastStartupState), _("Program error"), wxOK | wxICON_ERROR | wxCENTER); } #endif @@ -444,15 +397,7 @@ int AegisubApp::OnRun() { // Report errors if (!error.empty()) { - boost::filesystem::ofstream file(config::path->Decode("?user/crashlog.txt"), 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(); - } - + crash_writer::Write(error); OnUnhandledException(); }