Extract crashlog writing from main.cpp

This commit is contained in:
Thomas Goyne 2014-03-19 09:15:25 -07:00
parent 39823b5d89
commit 61b19a17e8
6 changed files with 139 additions and 66 deletions

View file

@ -144,6 +144,7 @@
<ClInclude Include="$(SrcDir)command\command.h" />
<ClInclude Include="$(SrcDir)compat.h" />
<ClInclude Include="$(SrcDir)config.h" />
<ClInclude Include="$(SrcDir)crash_writer.h" />
<ClInclude Include="$(SrcDir)dialog_about.h" />
<ClInclude Include="$(SrcDir)dialog_attachments.h" />
<ClInclude Include="$(SrcDir)dialog_automation.h" />
@ -347,6 +348,7 @@
<ClCompile Include="$(SrcDir)command\video.cpp" />
<ClCompile Include="$(SrcDir)command\vis_tool.cpp" />
<ClCompile Include="$(SrcDir)compat.cpp" />
<ClCompile Include="$(SrcDir)crash_writer.cpp" />
<ClCompile Include="$(SrcDir)dialog_about.cpp" />
<ClCompile Include="$(SrcDir)dialog_attachments.cpp" />
<ClCompile Include="$(SrcDir)dialog_automation.cpp" />

View file

@ -594,6 +594,9 @@
<ClInclude Include="$(SrcDir)dialog_log.h">
<Filter>Utilities\Logging</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)crash_writer.h">
<Filter>Utilities\Logging</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)utils.h">
<Filter>Utilities</Filter>
</ClInclude>
@ -1232,6 +1235,9 @@
<ClCompile Include="$(SrcDir)resolution_resampler.cpp">
<Filter>Features\Resolution resampler</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)crash_writer.cpp">
<Filter>Utilities\Logging</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="$(SrcDir)res/res.rc">

View file

@ -154,6 +154,7 @@ SRC += \
colorspace.cpp \
colour_button.cpp \
compat.cpp \
crash_writer.cpp \
dialog_about.cpp \
dialog_attachments.cpp \
dialog_automation.cpp \

92
src/crash_writer.cpp Normal file
View file

@ -0,0 +1,92 @@
// 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/util.h>
#include <boost/filesystem/fstream.hpp>
#include <boost/format.hpp>
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();
}
}
}

27
src/crash_writer.h Normal file
View file

@ -0,0 +1,27 @@
// 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 <libaegisub/fs_fwd.h>
#include <string>
namespace crash_writer {
void Initialize(agi::fs::path const& path);
void Cleanup();
void Write();
void Write(std::string const& error);
}

View file

@ -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 <libaegisub/dispatch.h>
@ -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();
}