mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 04:19:12 +00:00
Add support to send crash reports from macOS
This commit is contained in:
parent
e80dfbbdaf
commit
99cb95357a
@ -606,7 +606,8 @@ crash::DataRecovery* App::dataRecovery() const
|
||||
#ifdef ENABLE_UI
|
||||
void App::showNotification(INotificationDelegate* del)
|
||||
{
|
||||
m_mainWindow->showNotification(del);
|
||||
if (m_mainWindow)
|
||||
m_mainWindow->showNotification(del);
|
||||
}
|
||||
|
||||
void App::showBackupNotification(bool state)
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -421,11 +421,10 @@ public:
|
||||
|
||||
// Links
|
||||
locateFile()->Click.connect(base::Bind<void>(&OptionsWindow::onLocateConfigFile, this));
|
||||
#if _WIN32
|
||||
locateCrashFolder()->Click.connect(base::Bind<void>(&OptionsWindow::onLocateCrashFolder, this));
|
||||
#else
|
||||
locateCrashFolder()->setVisible(false);
|
||||
#endif
|
||||
if (!App::instance()->memoryDumpFilename().empty())
|
||||
locateCrashFolder()->Click.connect(base::Bind<void>(&OptionsWindow::onLocateCrashFolder, this));
|
||||
else
|
||||
locateCrashFolder()->setVisible(false);
|
||||
|
||||
// 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() {
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user