[gimp] app: make the backtrace GUI actually work on Win32.
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: make the backtrace GUI actually work on Win32.
- Date: Sun, 28 Jan 2018 14:45:56 +0000 (UTC)
commit 4e5a5dbb87e36053c4ffbdd3d0208fcf41cc1a0e
Author: Jehan <jehan girinstud io>
Date: Sat Jan 27 16:43:43 2018 +0100
app: make the backtrace GUI actually work on Win32.
It was previously untested, hence as expected needed fixes. First I add
our own exception handler using Win32 API SetUnhandledExceptionFilter().
Second, I reorder things so that ExcHndlInit() is run after this setter,
since they will be executed as a FILO and we need backtraces to be
generated before our separate GUI runs. Last I run the backtrace GUI as
async. No need to keep the main GIMP waiting since the traces have
already been generated into a separate file.
Also replace gtk_show_uri() by the implementation taken straight from
our web-browser plug-in, since apparently gtk_show_uri() doesn't work in
Windows (and probably not macOS either since I see we have a separate
implementation for this platform as well). I would like to be able to
use the PDB but can't because this code needs to be usable both within
the main process and into a separate tool process. Ideally, this should
just be a utils function which could be included without a problem.
app/errors.c | 33 ++++++++--
app/main.c | 38 +-----------
app/signals.c | 89 ++++++++++++++++++++++++----
app/signals.h | 2 +-
app/widgets/gimpcriticaldialog.c | 123 +++++++++++++++++++++++++++++++++++--
5 files changed, 224 insertions(+), 61 deletions(-)
---
diff --git a/app/errors.c b/app/errors.c
index 693a400..f4c0549 100644
--- a/app/errors.c
+++ b/app/errors.c
@@ -284,14 +284,18 @@ gimp_eek (const gchar *reason,
#ifndef GIMP_CONSOLE_COMPILATION
if (generate_backtrace && ! the_errors_gimp->no_interface)
{
- /* If enabled (it is disabled by default), the GUI preference
+ /* If GUI backtrace enabled (it is disabled by default), it
* takes precedence over the command line argument.
*/
- gchar *args[7] = { "gimpdebug-2.0", full_prog_name, NULL,
+#ifdef G_OS_WIN32
+ const gchar *gimpdebug = "gimpdebug-2.0.exe";
+#else
+ const gchar *gimpdebug = "gimpdebug-2.0";
+#endif
+ gchar *args[7] = { (gchar *) gimpdebug, full_prog_name, NULL,
(gchar *) reason, (gchar *) message,
backtrace_file, NULL };
gchar pid[16];
- gint exit_status;
g_snprintf (pid, 16, "%u", (guint) getpid ());
args[2] = pid;
@@ -302,10 +306,25 @@ gimp_eek (const gchar *reason,
* to keep GIMP up long enough for the debugger to get its
* trace.
*/
- g_spawn_sync (NULL, args, NULL,
- G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
- NULL, NULL, NULL, NULL, &exit_status, NULL);
- eek_handled = TRUE;
+#ifdef HAVE_EXCHNDL
+ /* On Win32, the trace has already been processed by ExcHnl
+ * and is waiting for us in a text file.
+ * We just want to spawn the trace GUI and exit GIMP directly.
+ */
+ if (g_file_test (backtrace_file, G_FILE_TEST_IS_REGULAR) &&
+ g_spawn_async (NULL, args, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
+ NULL, NULL, NULL, NULL))
+#else
+ /* On Unix machines, the spawned process will attach to the
+ * main GIMP process and will generate the backtrace. So we
+ * run it as a synced process.
+ */
+ if (g_spawn_sync (NULL, args, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL))
+#endif
+ eek_handled = TRUE;
}
#endif /* !GIMP_CONSOLE_COMPILATION */
diff --git a/app/main.c b/app/main.c
index 7b68167..a25ef51 100644
--- a/app/main.c
+++ b/app/main.c
@@ -36,11 +36,6 @@
#ifdef G_OS_WIN32
#include <io.h> /* get_osfhandle */
-#ifdef HAVE_EXCHNDL
-#include <time.h>
-#include <exchndl.h>
-#endif
-
#endif /* G_OS_WIN32 */
#ifndef GIMP_CONSOLE_COMPILATION
@@ -318,6 +313,9 @@ main (int argc,
argv = __argv;
#endif
+ /* Start signal handlers early. */
+ gimp_init_signal_handlers (&backtrace_file);
+
#ifdef G_OS_WIN32
/* Reduce risks */
{
@@ -364,34 +362,6 @@ main (int argc,
g_free (bin_dir);
}
-#ifdef HAVE_EXCHNDL
- /* Use Dr. Mingw (dumps backtrace on crash) if it is available. */
- {
- time_t t;
- gchar *filename;
- gchar *dir;
-
- /* This has to be the non-roaming directory (i.e., the local
- directory) as backtraces correspond to the binaries on this
- system. */
- dir = g_build_filename (g_get_user_data_dir (),
- GIMPDIR, GIMP_USER_VERSION, "CrashLog",
- NULL);
- /* Ensure the path exists. */
- g_mkdir_with_parents (dir, 0700);
-
- time (&t);
- filename = g_strdup_printf ("%s-crash-%" G_GUINT64_FORMAT ".txt",
- g_get_prgname(), t);
- backtrace_file = g_build_filename (dir, filename, NULL);
- g_free (filename);
- g_free (dir);
-
- ExcHndlInit ();
- ExcHndlSetLogFileNameA (backtrace_file);
- }
-#endif
-
#ifndef _WIN64
{
typedef BOOL (WINAPI *t_SetProcessDEPPolicy) (DWORD dwFlags);
@@ -538,8 +508,6 @@ main (int argc,
if (abort_message)
app_abort (no_interface, abort_message);
- gimp_init_signal_handlers ();
-
if (system_gimprc)
system_gimprc_file = g_file_new_for_commandline_arg (system_gimprc);
diff --git a/app/signals.c b/app/signals.c
index 76d5d4a..c70748c 100644
--- a/app/signals.c
+++ b/app/signals.c
@@ -30,22 +30,68 @@
#include "errors.h"
#include "signals.h"
+#ifdef HAVE_EXCHNDL
+#include <windows.h>
+#include <time.h>
+#include <exchndl.h>
+
+static LPTOP_LEVEL_EXCEPTION_FILTER g_prevExceptionFilter = NULL;
+
+static LONG WINAPI gimp_sigfatal_handler (PEXCEPTION_POINTERS pExceptionInfo);
+
+#else
+
+static void gimp_sigfatal_handler (gint sig_num) G_GNUC_NORETURN;
-#ifndef G_OS_WIN32
-static void gimp_sigfatal_handler (gint sig_num) G_GNUC_NORETURN;
#endif
void
-gimp_init_signal_handlers (void)
+gimp_init_signal_handlers (gchar **backtrace_file)
{
-#ifndef G_OS_WIN32
- /* No use catching these on Win32, the user won't get any stack
- * trace from glib anyhow. It's better to let Windows inform about
- * the program error, and offer debugging (if the user has installed
- * MSVC or some other compiler that knows how to install itself as a
+#ifdef G_OS_WIN32
+ /* Use Dr. Mingw (dumps backtrace on crash) if it is available. Do
+ * nothing otherwise on Win32.
+ * The user won't get any stack trace from glib anyhow.
+ * Without Dr. MinGW, It's better to let Windows inform about the
+ * program error, and offer debugging (if the user has installed MSVC
+ * or some other compiler that knows how to install itself as a
* handler for program errors).
*/
+#ifdef HAVE_EXCHNDL
+ time_t t;
+ gchar *filename;
+ gchar *dir;
+
+ /* Order is very important here. We need to add our signal handler
+ * first, then run ExcHndlInit() which will add its own handler, so
+ * that ExcHnl's handler runs first since that's in FILO order.
+ */
+ if (! g_prevExceptionFilter)
+ g_prevExceptionFilter = SetUnhandledExceptionFilter (gimp_sigfatal_handler);
+
+ /* This has to be the non-roaming directory (i.e., the local
+ directory) as backtraces correspond to the binaries on this
+ system. */
+ dir = g_build_filename (g_get_user_data_dir (),
+ GIMPDIR, GIMP_USER_VERSION, "CrashLog",
+ NULL);
+ /* Ensure the path exists. */
+ g_mkdir_with_parents (dir, 0700);
+
+ time (&t);
+ filename = g_strdup_printf ("%s-crash-%" G_GUINT64_FORMAT ".txt",
+ g_get_prgname(), t);
+ *backtrace_file = g_build_filename (dir, filename, NULL);
+ g_free (filename);
+ g_free (dir);
+
+ ExcHndlInit ();
+ ExcHndlSetLogFileNameA (*backtrace_file);
+
+#endif /* HAVE_EXCHNDL */
+
+#else
/* Handle fatal signals */
@@ -67,11 +113,32 @@ gimp_init_signal_handlers (void)
/* Restart syscalls on SIGCHLD */
gimp_signal_private (SIGCHLD, SIG_DFL, SA_RESTART);
-#endif /* ! G_OS_WIN32 */
+#endif /* G_OS_WIN32 */
}
-#ifndef G_OS_WIN32
+#ifdef G_OS_WIN32
+
+#ifdef HAVE_EXCHNDL
+static LONG WINAPI
+gimp_sigfatal_handler (PEXCEPTION_POINTERS pExceptionInfo)
+{
+ /* Just in case, so that we don't loop or anything similar, just
+ * re-establish previous handler.
+ */
+ SetUnhandledExceptionFilter (g_prevExceptionFilter);
+
+ /* Now process the exception. */
+ gimp_fatal_error ("unhandled exception");
+
+ if (g_prevExceptionFilter && g_prevExceptionFilter != gimp_sigfatal_handler)
+ return g_prevExceptionFilter (pExceptionInfo);
+ else
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif
+
+#else
/* gimp core signal handler for fatal signals */
@@ -97,4 +164,4 @@ gimp_sigfatal_handler (gint sig_num)
}
}
-#endif /* ! G_OS_WIN32 */
+#endif /* G_OS_WIN32 */
diff --git a/app/signals.h b/app/signals.h
index c846f96..97409f2 100644
--- a/app/signals.h
+++ b/app/signals.h
@@ -23,7 +23,7 @@
#endif
-void gimp_init_signal_handlers (void);
+void gimp_init_signal_handlers (gchar **backtrace_file);
#endif /* __SIGNALS_H__ */
diff --git a/app/widgets/gimpcriticaldialog.c b/app/widgets/gimpcriticaldialog.c
index a046d2a..c5f9edd 100644
--- a/app/widgets/gimpcriticaldialog.c
+++ b/app/widgets/gimpcriticaldialog.c
@@ -31,6 +31,15 @@
#include <glib.h>
#include <gtk/gtk.h>
+#ifdef PLATFORM_OSX
+#import <Cocoa/Cocoa.h>
+#endif
+
+#ifdef G_OS_WIN32
+#undef DATADIR
+#include <windows.h>
+#endif
+
#include "gimpcriticaldialog.h"
#include "version.h"
@@ -44,9 +53,12 @@ typedef struct _GimpCriticalDialog GimpCriticalDialog;
#define GIMP_CRITICAL_RESPONSE_RESTART 3
-static void gimp_critical_dialog_finalize (GObject *object);
-static void gimp_critical_dialog_response (GtkDialog *dialog,
- gint response_id);
+static void gimp_critical_dialog_finalize (GObject *object);
+static void gimp_critical_dialog_response (GtkDialog *dialog,
+ gint response_id);
+
+static gboolean browser_open_url (const gchar *url,
+ GError **error);
G_DEFINE_TYPE (GimpCriticalDialog, gimp_critical_dialog, GTK_TYPE_DIALOG)
@@ -166,6 +178,102 @@ gimp_critical_dialog_finalize (GObject *object)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
+
+/* XXX This is taken straight from plug-ins/common/web-browser.c
+ * This really sucks but this class also needs to be called by
+ * tools/gimpdebug.c as a separate process and therefore cannot make use
+ * of the PDB. Anyway shouldn't we just move this as a utils function?
+ * Why does such basic feature as opening a URL in a cross-platform way
+ * need to be a plug-in?
+ */
+static gboolean
+browser_open_url (const gchar *url,
+ GError **error)
+{
+#ifdef G_OS_WIN32
+
+ HINSTANCE hinst = ShellExecute (GetDesktopWindow(),
+ "open", url, NULL, NULL, SW_SHOW);
+
+ if ((gint) hinst <= 32)
+ {
+ const gchar *err;
+
+ switch ((gint) hinst)
+ {
+ case 0 :
+ err = _("The operating system is out of memory or resources.");
+ break;
+ case ERROR_FILE_NOT_FOUND :
+ err = _("The specified file was not found.");
+ break;
+ case ERROR_PATH_NOT_FOUND :
+ err = _("The specified path was not found.");
+ break;
+ case ERROR_BAD_FORMAT :
+ err = _("The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image).");
+ break;
+ case SE_ERR_ACCESSDENIED :
+ err = _("The operating system denied access to the specified file.");
+ break;
+ case SE_ERR_ASSOCINCOMPLETE :
+ err = _("The file name association is incomplete or invalid.");
+ break;
+ case SE_ERR_DDEBUSY :
+ err = _("DDE transaction busy");
+ break;
+ case SE_ERR_DDEFAIL :
+ err = _("The DDE transaction failed.");
+ break;
+ case SE_ERR_DDETIMEOUT :
+ err = _("The DDE transaction timed out.");
+ break;
+ case SE_ERR_DLLNOTFOUND :
+ err = _("The specified DLL was not found.");
+ break;
+ case SE_ERR_NOASSOC :
+ err = _("There is no application associated with the given file name extension.");
+ break;
+ case SE_ERR_OOM :
+ err = _("There was not enough memory to complete the operation.");
+ break;
+ case SE_ERR_SHARE:
+ err = _("A sharing violation occurred.");
+ break;
+ default :
+ err = _("Unknown Microsoft Windows error.");
+ }
+
+ g_set_error (error, 0, 0, _("Failed to open '%s': %s"), url, err);
+
+ return FALSE;
+ }
+
+ return TRUE;
+
+#elif defined(PLATFORM_OSX)
+
+ NSURL *ns_url;
+ gboolean retval;
+
+ @autoreleasepool
+ {
+ ns_url = [NSURL URLWithString: [NSString stringWithUTF8String: url]];
+ retval = [[NSWorkspace sharedWorkspace] openURL: ns_url];
+ }
+
+ return retval;
+
+#else
+
+ return gtk_show_uri (gdk_screen_get_default (),
+ url,
+ gtk_get_current_event_time(),
+ error);
+
+#endif
+}
+
static void
gimp_critical_dialog_response (GtkDialog *dialog,
gint response_id)
@@ -207,10 +315,7 @@ gimp_critical_dialog_response (GtkDialog *dialog,
* much time digging into it.
*/
url = "https://bugzilla.gnome.org/enter_bug.cgi?product=GIMP";
- gtk_show_uri (gdk_screen_get_default (),
- url,
- gtk_get_current_event_time(),
- NULL);
+ browser_open_url (url, NULL);
}
break;
case GIMP_CRITICAL_RESPONSE_RESTART:
@@ -218,6 +323,10 @@ gimp_critical_dialog_response (GtkDialog *dialog,
gchar *args[2] = { critical->program , NULL };
#ifndef G_OS_WIN32
+ /* It is unneeded to kill the process on Win32. This was run
+ * as an async call and the main process should already be
+ * dead by now.
+ */
if (critical->pid > 0)
kill ((pid_t ) critical->pid, SIGINT);
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]