Add support to send crash reports from macOS

This commit is contained in:
David Capello 2020-03-06 14:45:13 -03:00
parent e80dfbbdaf
commit 99cb95357a
8 changed files with 131 additions and 37 deletions

View File

@ -606,6 +606,7 @@ crash::DataRecovery* App::dataRecovery() const
#ifdef ENABLE_UI
void App::showNotification(INotificationDelegate* del)
{
if (m_mainWindow)
m_mainWindow->showNotification(del);
}
@ -709,18 +710,4 @@ int app_get_color_to_clear_layer(Layer* layer)
return color_utils::color_for_layer(color, layer);
}
std::string memory_dump_filename()
{
#ifdef _WIN32
static const char* kDefaultCrashName = PACKAGE "-crash-" VERSION ".dmp";
app::ResourceFinder rf;
rf.includeUserDir(kDefaultCrashName);
return rf.getFirstOrCreateDefault();
#else
return "";
#endif
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -117,6 +117,9 @@ namespace app {
script::Engine* scriptEngine() { return m_engine.get(); }
#endif
const std::string& memoryDumpFilename() const { return m_memoryDumpFilename; }
void memoryDumpFilename(const std::string& fn) { m_memoryDumpFilename = fn; }
// App Signals
obs::signal<void()> Exit;
obs::signal<void()> PaletteChange;
@ -145,13 +148,16 @@ namespace app {
#ifdef ENABLE_SCRIPTING
std::unique_ptr<script::Engine> m_engine;
#endif
// Set the memory dump filename to show in the Preferences dialog
// or the "send crash" dialog. It's set by the SendCrash class.
std::string m_memoryDumpFilename;
};
void app_refresh_screen();
void app_rebuild_documents_tabs();
PixelFormat app_get_current_pixel_format();
int app_get_color_to_clear_layer(doc::Layer* layer);
std::string memory_dump_filename();
} // namespace app

View File

@ -421,11 +421,10 @@ public:
// Links
locateFile()->Click.connect(base::Bind<void>(&OptionsWindow::onLocateConfigFile, this));
#if _WIN32
if (!App::instance()->memoryDumpFilename().empty())
locateCrashFolder()->Click.connect(base::Bind<void>(&OptionsWindow::onLocateCrashFolder, this));
#else
else
locateCrashFolder()->setVisible(false);
#endif
// Undo preferences
limitUndo()->Click.connect(base::Bind<void>(&OptionsWindow::onLimitUndoCheck, this));
@ -1017,7 +1016,8 @@ private:
}
void onLocateCrashFolder() {
app::launcher::open_folder(base::get_file_path(app::memory_dump_filename()));
app::launcher::open_folder(
base::get_file_path(App::instance()->memoryDumpFilename()));
}
void onLocateConfigFile() {

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -14,23 +15,92 @@
#include "app/console.h"
#include "app/i18n/strings.h"
#include "app/resource_finder.h"
#include "app/task.h"
#include "base/bind.h"
#include "base/fs.h"
#include "base/launcher.h"
#include "ui/alert.h"
#include "ui/system.h"
#include "send_crash.xml.h"
namespace app {
// static
std::string SendCrash::DefaultMemoryDumpFilename()
{
#ifdef _WIN32
const char* kDefaultCrashName = PACKAGE "-crash-" VERSION ".dmp";
ResourceFinder rf;
rf.includeUserDir(kDefaultCrashName);
return rf.getFirstOrCreateDefault();
#else
return std::string();
#endif
}
SendCrash::~SendCrash()
{
if (m_task.running()) {
m_task.cancel();
m_task.wait();
}
}
void SendCrash::search()
{
#ifdef _WIN32
m_dumpFilename = memory_dump_filename();
// On Windows we use one mini-dump to report bugs, then we can open
// this .dmp file locally along with the .exe + .pdb to check the
// stack trace and detect the cause of the bug.
if (base::is_file(m_dumpFilename)) {
App::instance()->showNotification(this);
m_dumpFilename = SendCrash::DefaultMemoryDumpFilename();
if (!m_dumpFilename.empty() &&
base::is_file(m_dumpFilename)) {
auto app = App::instance();
app->memoryDumpFilename(fn);
app->showNotification(this);
}
#elif defined(__APPLE__)
// On macOS we can show the possibility to send the latest crash
// report from ~/Library/Logs/DiagnosticReports which is the
// location where crash reports (.crash files) are located.
m_task.run(
[this](base::task_token&){
ResourceFinder rf;
rf.includeHomeDir("Library/Logs/DiagnosticReports");
std::string dir = rf.defaultFilename();
if (base::is_directory(dir)) {
std::vector<std::string> candidates;
int n = std::strlen(PACKAGE);
for (const auto& fn : base::list_files(dir)) {
// Cancel everything
if (m_task.canceled())
return;
if (base::utf8_icmp(PACKAGE, fn, n) == 0) {
candidates.push_back(fn);
}
}
std::sort(candidates.begin(), candidates.end());
if (!candidates.empty()) {
std::string fn = base::join_path(dir, candidates.back());
if (base::is_file(fn)) {
ui::execute_from_ui_thread(
[this, fn]{
m_dumpFilename = fn;
if (auto app = App::instance()) {
app->memoryDumpFilename(fn);
app->showNotification(this);
}
});
}
}
}
});
#endif
}
@ -48,15 +118,24 @@ void SendCrash::notificationClick()
app::gen::SendCrash dlg;
// The current version is a "development" version if the VERSION
// macro contains the "dev" word.
#if _WIN32
// Only on Windows, if the current version is a "development"
// version (i.e. the VERSION macro contains the "dev" word), the
// .dmp file is useless for us. This is because we need the .exe +
// .pdb + source code used in the compilation process to make some
// sense of the .dmp file.
bool isDev = (std::string(VERSION).find("dev") != std::string::npos);
if (isDev) {
dlg.official()->setVisible(false);
dlg.devFilename()->setText(m_dumpFilename);
dlg.devFilename()->Click.connect(base::Bind(&SendCrash::onClickDevFilename, this));
}
else {
else
#endif // On other platforms the crash file might be useful even in
// the -dev version (e.g. on macOS it's a text file with stack
// traces).
{
dlg.dev()->setVisible(false);
dlg.filename()->setText(m_dumpFilename);
dlg.filename()->Click.connect(base::Bind(&SendCrash::onClickFilename, this));

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -9,6 +10,7 @@
#pragma once
#include "app/notification_delegate.h"
#include "app/task.h"
#include <string>
@ -16,6 +18,10 @@ namespace app {
class SendCrash : public INotificationDelegate {
public:
static std::string DefaultMemoryDumpFilename();
~SendCrash();
void search();
virtual std::string notificationText() override;
@ -25,6 +31,7 @@ namespace app {
void onClickFilename();
void onClickDevFilename();
Task m_task;
std::string m_dumpFilename;
};

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -11,6 +11,7 @@
#include "app/task.h"
#include "base/task.h"
#include "base/thread.h"
#include "base/thread_pool.h"
namespace app {
@ -32,4 +33,12 @@ void Task::run(base::task::func_t&& func)
m_token = &m_task.start(tasks_pool);
}
void Task::wait()
{
// TODO wait a condition variable
while (!m_task.completed()) {
base::this_thread::sleep_for(0.1);
}
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -20,6 +20,7 @@ namespace app {
~Task();
void run(base::task::func_t&& func);
void wait();
// Returns true when the task is completed (whether it was
// canceled or not)
@ -27,6 +28,10 @@ namespace app {
return m_task.completed();
}
bool running() const {
return m_task.running();
}
bool canceled() const {
if (m_token)
return m_token->canceled();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
@ -59,7 +59,7 @@ int app_main(int argc, char* argv[])
std::srand(static_cast<unsigned int>(std::time(nullptr)));
#ifdef _WIN32
::CoInitialize(NULL);
::CoInitialize(nullptr);
#endif
try {
@ -70,11 +70,12 @@ int app_main(int argc, char* argv[])
os::ScopedHandle<os::System> system(os::create_system());
app::App app;
// Change the name of the memory dump file
// Change the memory dump filename to save on disk (.dmp
// file). Note: Only useful on Windows.
{
const std::string filename = app::memory_dump_filename();
if (!filename.empty())
memoryDump.setFileName(filename);
const std::string fn = app::SendCrash::DefaultMemoryDumpFilename();
if (!fn.empty())
memoryDump.setFileName(fn);
}
const int code = app.initialize(options);