[gimp] app: add GimpBacktrace



commit 80bf686c9447f59bdc72cc9adb018ef1a0d83444
Author: Ell <ell_se yahoo com>
Date:   Sun Sep 2 01:15:35 2018 -0400

    app: add GimpBacktrace
    
    GimpBacktrace provides an interface for creating and traversing
    multi-threaded backtraces, as well as querying symbol information.
    While we already have some backtrace functionality, it relies on
    external tools for the most part, and as such is rather expensive,
    and is only meant for producing opaque backtraces.  GimpBacktrace,
    on the other hand, is meant to be relatively cheap (we're going to
    use it for profiling,) and allow inspection of the backtrace data.
    In the future, it might make sense to replace some, or all, of the
    other backtrace functions with GimpBacktrace.
    
    GimpBacktrace currently only supports Linux.  By default, it uses
    dladdr() to query symbol information, which is somewhat limited (in
    particular, it doesn't work for static functions.)  When libunwind
    is installed, GimpBacktrace uses it to get more complete symbol
    information.  libunwind is currently an optional dependency, but it
    might make sense to promote it to a mandatory, or opt-out,
    dependency, as it's lightweight and widely available.
    
    On other platforms, the GimpBacktrace interface can still be used,
    but it always returns NULL backtraces.

 app/Makefile.am                  |   8 +-
 app/core/Makefile.am             |   5 +
 app/core/core-types.h            |   1 +
 app/core/gimpbacktrace-backend.h |  32 +++
 app/core/gimpbacktrace-linux.c   | 591 +++++++++++++++++++++++++++++++++++++++
 app/core/gimpbacktrace-none.c    | 116 ++++++++
 app/core/gimpbacktrace.h         |  62 ++++
 configure.ac                     |  50 ++++
 8 files changed, 864 insertions(+), 1 deletion(-)
---
diff --git a/app/Makefile.am b/app/Makefile.am
index 57b493d601..22b3b75704 100644
--- a/app/Makefile.am
+++ b/app/Makefile.am
@@ -74,6 +74,10 @@ libapp_a_SOURCES = $(libapp_sources)
 gimp_@GIMP_APP_VERSION@_SOURCES = $(libapp_sources) main.c
 
 
+if PLATFORM_LINUX
+libdl = -ldl
+endif
+
 if PLATFORM_OSX
 framework_cocoa = -framework Cocoa
 endif
@@ -174,9 +178,11 @@ gimpconsoleldadd = \
        $(Z_LIBS)                                               \
        $(JSON_C_LIBS)                                          \
        $(LIBMYPAINT_LIBS)                                      \
+       $(LIBUNWIND_LIBS)                                       \
        $(INTLLIBS)                                             \
        $(RT_LIBS)                                              \
-       $(libm)
+       $(libm)                                                 \
+       $(libdl)
 
 gimp_@GIMP_APP_VERSION@_LDFLAGS = \
        $(AM_LDFLAGS)                                           \
diff --git a/app/core/Makefile.am b/app/core/Makefile.am
index 1ca2ed30f8..0730818cc3 100644
--- a/app/core/Makefile.am
+++ b/app/core/Makefile.am
@@ -23,6 +23,7 @@ AM_CPPFLAGS = \
        $(LIBMYPAINT_CFLAGS)                            \
        $(MYPAINT_BRUSHES_CFLAGS)                       \
        $(GEXIV2_CFLAGS)                                \
+       $(LIBUNWIND_CFLAGS)                             \
        -I$(includedir)
 
 AM_CFLAGS = \
@@ -95,6 +96,10 @@ libappcore_a_sources = \
        gimpauxitem.h                           \
        gimpauxitemundo.c                       \
        gimpauxitemundo.h                       \
+       gimpbacktrace.h                         \
+       gimpbacktrace-backend.h                 \
+       gimpbacktrace-linux.c                   \
+       gimpbacktrace-none.c                    \
        gimpbezierdesc.h                        \
        gimpbezierdesc.c                        \
        gimpboundary.c                          \
diff --git a/app/core/core-types.h b/app/core/core-types.h
index 83595fdacc..b427704f14 100644
--- a/app/core/core-types.h
+++ b/app/core/core-types.h
@@ -220,6 +220,7 @@ typedef struct _GimpWaitable                    GimpWaitable;    /* dummy typede
 
 /*  non-object types  */
 
+typedef struct _GimpBacktrace                   GimpBacktrace;
 typedef struct _GimpBoundSeg                    GimpBoundSeg;
 typedef struct _GimpCoords                      GimpCoords;
 typedef struct _GimpGradientSegment             GimpGradientSegment;
diff --git a/app/core/gimpbacktrace-backend.h b/app/core/gimpbacktrace-backend.h
new file mode 100644
index 0000000000..a464d65e09
--- /dev/null
+++ b/app/core/gimpbacktrace-backend.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpbacktrace-backend.h
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_BACKTRACE_BACKEND_H__
+#define __GIMP_BACKTRACE_BACKEND_H__
+
+
+#ifdef __gnu_linux__
+# define GIMP_BACKTRACE_BACKEND_LINUX
+#else
+# define GIMP_BACKTRACE_BACKEND_NONE
+#endif
+
+
+#endif  /*  __GIMP_BACKTRACE_BACKEND_H__  */
diff --git a/app/core/gimpbacktrace-linux.c b/app/core/gimpbacktrace-linux.c
new file mode 100644
index 0000000000..dc3db99e5e
--- /dev/null
+++ b/app/core/gimpbacktrace-linux.c
@@ -0,0 +1,591 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
+ *
+ * backtrace-linux.c
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+#define _GNU_SOURCE
+
+
+#include "config.h"
+
+#include "gimpbacktrace-backend.h"
+
+
+#ifdef GIMP_BACKTRACE_BACKEND_LINUX
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <signal.h>
+#include <execinfo.h>
+#include <dlfcn.h>
+#include <string.h>
+
+#ifdef HAVE_LIBUNWIND
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#endif
+
+#include <gio/gio.h>
+
+#include "core-types.h"
+
+#include "gimpbacktrace.h"
+
+
+#define MAX_N_THREADS        256
+#define MAX_N_FRAMES         256
+#define MAX_THREAD_NAME_SIZE 32
+#define MAX_SYMBOL_NAME_SIZE 128
+#define N_SKIPPED_FRAMES     2
+#define MAX_WAIT_TIME        (G_TIME_SPAN_SECOND / 2)
+#define BACKTRACE_SIGNAL     SIGUSR1
+
+
+typedef struct _GimpBacktraceThread GimpBacktraceThread;
+typedef struct _GimpBacktraceFrame  GimpBacktraceFrame;
+
+
+struct _GimpBacktraceThread
+{
+  pid_t    tid;
+  gchar    name[MAX_THREAD_NAME_SIZE];
+
+  guintptr frames[MAX_N_FRAMES];
+  gint     n_frames;
+};
+
+struct _GimpBacktrace
+{
+  GimpBacktraceThread *threads;
+  gint                 n_threads;
+  gint                 n_remaining_threads;
+};
+
+
+/*  local function prototypes  */
+
+static inline gint   gimp_backtrace_normalize_frame   (GimpBacktrace *backtrace,
+                                                       gint           thread,
+                                                       gint           frame);
+
+static gint          gimp_backtrace_enumerate_threads (gboolean       include_current_thread,
+                                                       pid_t         *threads,
+                                                       gint           size);
+static void          gimp_backtrace_read_thread_name  (pid_t          tid,
+                                                       gchar         *name,
+                                                       gint           size);
+
+static void          gimp_backtrace_signal_handler    (gint           signum);
+
+
+/*  static variables  */
+
+static GMutex            mutex;
+static gint              n_initializations;
+static gboolean          initialized;
+static struct sigaction  orig_action;
+static pid_t             blacklisted_threads[MAX_N_THREADS];
+static gint              n_blacklisted_threads;
+static GimpBacktrace    *handler_backtrace;
+
+static const gchar *blacklisted_thread_names[] =
+{
+  "gmain"
+};
+
+
+/*  private functions  */
+
+
+static inline gint
+gimp_backtrace_normalize_frame (GimpBacktrace *backtrace,
+                                gint           thread,
+                                gint           frame)
+{
+  if (frame >= 0)
+    return frame + N_SKIPPED_FRAMES;
+  else
+    return backtrace->threads[thread].n_frames + frame;
+}
+
+static gint
+gimp_backtrace_enumerate_threads (gboolean  include_current_thread,
+                                  pid_t    *threads,
+                                  gint      size)
+{
+  DIR           *dir;
+  struct dirent *dirent;
+  pid_t          tid;
+  gint           n_threads;
+
+  dir = opendir ("/proc/self/task");
+
+  if (! dir)
+    return 0;
+
+  tid = syscall (SYS_gettid);
+
+  n_threads = 0;
+
+  while (n_threads < size && (dirent = readdir (dir)))
+    {
+      pid_t id = g_ascii_strtoull (dirent->d_name, NULL, 10);
+
+      if (id)
+        {
+          if (! include_current_thread && id == tid)
+            id = 0;
+        }
+
+      if (id)
+        {
+          gint i;
+
+          for (i = 0; i < n_blacklisted_threads; i++)
+            {
+              if (id == blacklisted_threads[i])
+                {
+                  id = 0;
+
+                  break;
+                }
+            }
+        }
+
+      if (id)
+        threads[n_threads++] = id;
+    }
+
+  closedir (dir);
+
+  return n_threads;
+}
+
+static void
+gimp_backtrace_read_thread_name (pid_t  tid,
+                                 gchar *name,
+                                 gint   size)
+{
+  gchar filename[64];
+  gint  fd;
+
+  if (size <= 0)
+    return;
+
+  name[0] = '\0';
+
+  g_snprintf (filename, sizeof (filename),
+              "/proc/self/task/%llu/comm",
+              (unsigned long long) tid);
+
+  fd = open (filename, O_RDONLY);
+
+  if (fd >= 0)
+    {
+      gint n = read (fd, name, size);
+
+      if (n > 0)
+        name[n - 1] = '\0';
+
+      close (fd);
+    }
+}
+
+static void
+gimp_backtrace_signal_handler (gint signum)
+{
+  GimpBacktrace *curr_backtrace = g_atomic_pointer_get (&handler_backtrace);
+  pid_t          tid            = syscall (SYS_gettid);
+  gint           i;
+
+  for (i = 0; i < curr_backtrace->n_threads; i++)
+    {
+      GimpBacktraceThread *thread = &curr_backtrace->threads[i];
+
+      if (thread->tid == tid)
+        {
+          thread->n_frames = backtrace ((gpointer *) thread->frames,
+                                        MAX_N_FRAMES);
+
+          g_atomic_int_dec_and_test (&curr_backtrace->n_remaining_threads);
+
+          break;
+        }
+    }
+}
+
+
+/*  public functions  */
+
+
+gboolean
+gimp_backtrace_init (void)
+{
+  g_mutex_lock (&mutex);
+
+  if (n_initializations == 0)
+    {
+      struct sigaction action = {};
+
+      action.sa_handler = gimp_backtrace_signal_handler;
+
+      sigemptyset (&action.sa_mask);
+
+      if (sigaction (BACKTRACE_SIGNAL, &action, &orig_action) == 0)
+        {
+          pid_t *threads;
+          gint   n_threads;
+          gint   i;
+
+          n_blacklisted_threads = 0;
+
+          threads = g_new (pid_t, MAX_N_THREADS);
+
+          n_threads = gimp_backtrace_enumerate_threads (TRUE,
+                                                        threads, MAX_N_THREADS);
+
+          for (i = 0; i < n_threads; i++)
+            {
+              gchar name[MAX_THREAD_NAME_SIZE];
+              gint  j;
+
+              gimp_backtrace_read_thread_name (threads[i],
+                                               name, MAX_THREAD_NAME_SIZE);
+
+              for (j = 0; j < G_N_ELEMENTS (blacklisted_thread_names); j++)
+                {
+                  if (! strcmp (name, blacklisted_thread_names[j]))
+                    {
+                      blacklisted_threads[n_blacklisted_threads++] = threads[i];
+                    }
+                }
+            }
+
+          g_free (threads);
+
+          initialized = TRUE;
+        }
+    }
+
+  n_initializations++;
+
+  g_mutex_unlock (&mutex);
+
+  return initialized;
+}
+
+void
+gimp_backtrace_shutdown (void)
+{
+  g_return_if_fail (n_initializations > 0);
+
+  g_mutex_lock (&mutex);
+
+  n_initializations--;
+
+  if (n_initializations == 0 && initialized)
+    {
+      if (sigaction (BACKTRACE_SIGNAL, &orig_action, NULL) < 0)
+        g_warning ("failed to restore origianl backtrace signal handler");
+
+      initialized = FALSE;
+    }
+
+  g_mutex_unlock (&mutex);
+}
+
+GimpBacktrace *
+gimp_backtrace_new (gboolean include_current_thread)
+{
+  GimpBacktrace *backtrace;
+  pid_t          pid;
+  pid_t         *threads;
+  gint           n_threads;
+  gint           n_remaining_threads;
+  gint           prev_n_remaining_threads;
+  gint64         start_time;
+  gint           i;
+
+  if (! initialized)
+    return NULL;
+
+  pid = getpid ();
+
+  threads = g_new (pid_t, MAX_N_THREADS);
+
+  n_threads = gimp_backtrace_enumerate_threads (include_current_thread,
+                                                threads, MAX_N_THREADS);
+
+  if (n_threads == 0)
+    {
+      g_free (threads);
+
+      return NULL;
+    }
+
+  g_mutex_lock (&mutex);
+
+  backtrace = g_slice_new (GimpBacktrace);
+
+  backtrace->threads             = g_new (GimpBacktraceThread, n_threads);
+  backtrace->n_threads           = n_threads;
+  backtrace->n_remaining_threads = n_threads;
+
+  g_atomic_pointer_set (&handler_backtrace, backtrace);
+
+  for (i = 0; i < n_threads; i++)
+    {
+      GimpBacktraceThread *thread = &backtrace->threads[i];
+
+      thread->tid      = threads[i];
+      thread->n_frames = 0;
+
+      gimp_backtrace_read_thread_name (thread->tid,
+                                       thread->name, MAX_THREAD_NAME_SIZE);
+
+      syscall (SYS_tgkill, pid, threads[i], BACKTRACE_SIGNAL);
+    }
+
+  g_free (threads);
+
+  start_time = g_get_monotonic_time ();
+
+  prev_n_remaining_threads =
+    g_atomic_int_get (&backtrace->n_remaining_threads);
+
+  while ((n_remaining_threads =
+            g_atomic_int_get (&backtrace->n_remaining_threads) > 0))
+    {
+      gint64 time = g_get_monotonic_time ();
+
+      if (n_remaining_threads < prev_n_remaining_threads)
+        {
+          prev_n_remaining_threads = n_remaining_threads;
+
+          start_time = time;
+        }
+      else if (time - start_time > MAX_WAIT_TIME)
+        {
+          break;
+        }
+    }
+
+  handler_backtrace = NULL;
+
+  if (n_remaining_threads > 0)
+    {
+      for (i = 0; i < n_threads; i++)
+        {
+          if (backtrace->threads[i].n_frames == 0)
+            {
+              if (n_blacklisted_threads < MAX_N_THREADS)
+                {
+                  g_printerr ("blacklisted %d\n", backtrace->threads[i].tid);
+
+                  blacklisted_threads[n_blacklisted_threads++] =
+                    backtrace->threads[i].tid;
+                }
+
+              g_mutex_unlock (&mutex);
+
+              return NULL;
+            }
+        }
+    }
+
+  g_mutex_unlock (&mutex);
+
+  if (n_threads == 0)
+    {
+      gimp_backtrace_free (backtrace);
+
+      return NULL;
+    }
+
+  return backtrace;
+}
+
+void
+gimp_backtrace_free (GimpBacktrace *backtrace)
+{
+  if (! backtrace || backtrace->n_remaining_threads > 0)
+    return;
+
+  g_free (backtrace->threads);
+
+  g_slice_free (GimpBacktrace, backtrace);
+}
+
+gint
+gimp_backtrace_get_n_threads (GimpBacktrace *backtrace)
+{
+  g_return_val_if_fail (backtrace != NULL, 0);
+
+  return backtrace->n_threads;
+}
+
+guintptr
+gimp_backtrace_get_thread_id (GimpBacktrace *backtrace,
+                              gint           thread)
+{
+  g_return_val_if_fail (backtrace != NULL, 0);
+  g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
+
+  return backtrace->threads[thread].tid;
+}
+
+const gchar *
+gimp_backtrace_get_thread_name (GimpBacktrace *backtrace,
+                                gint           thread)
+{
+  g_return_val_if_fail (backtrace != NULL, 0);
+  g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, NULL);
+
+  if (backtrace->threads[thread].name[0])
+    return backtrace->threads[thread].name;
+  else
+    return NULL;
+}
+
+gint
+gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace,
+                                  guintptr       thread_id,
+                                  gint           thread_hint)
+{
+  pid_t tid = thread_id;
+  gint  i;
+
+  g_return_val_if_fail (backtrace != NULL, -1);
+
+  if (thread_hint < backtrace->n_threads &&
+      backtrace->threads[thread_hint].tid == tid)
+    {
+      return thread_hint;
+    }
+
+  for (i = 0; i < backtrace->n_threads; i++)
+    {
+      if (backtrace->threads[i].tid == tid)
+        return i;
+    }
+
+  return -1;
+}
+
+gint
+gimp_backtrace_get_n_frames (GimpBacktrace *backtrace,
+                             gint           thread)
+{
+  g_return_val_if_fail (backtrace != NULL, 0);
+  g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
+
+  return backtrace->threads[thread].n_frames - N_SKIPPED_FRAMES;
+}
+
+guintptr
+gimp_backtrace_get_frame_address (GimpBacktrace *backtrace,
+                                  gint           thread,
+                                  gint           frame)
+{
+  g_return_val_if_fail (backtrace != NULL, 0);
+  g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
+
+  frame = gimp_backtrace_normalize_frame (backtrace, thread, frame);
+
+  g_return_val_if_fail (frame >= N_SKIPPED_FRAMES &&
+                        frame <  backtrace->threads[thread].n_frames, 0);
+
+  return backtrace->threads[thread].frames[frame];
+}
+
+gboolean
+gimp_backtrace_get_symbol_info (guintptr                 address,
+                                GimpBacktraceSymbolInfo *info)
+{
+  Dl_info dl_info;
+
+  g_return_val_if_fail (info != NULL, FALSE);
+
+#ifdef HAVE_LIBUNWIND
+  {
+    unw_context_t context = {};
+    unw_cursor_t  cursor;
+    unw_word_t    offset;
+
+    if (dladdr ((gpointer) address, &dl_info) && dl_info.dli_fname)
+      {
+        g_strlcpy (info->object_name, dl_info.dli_fname,
+                   sizeof (info->object_name));
+      }
+    else
+      {
+        info->object_name[0] = '\0';
+      }
+
+    if (unw_init_local (&cursor, &context)         == 0 &&
+        unw_set_reg (&cursor, UNW_REG_IP, address) == 0 &&
+        unw_get_proc_name (&cursor,
+                           info->symbol_name, sizeof (info->symbol_name),
+                           &offset)                == 0)
+      {
+        info->symbol_address = address - offset;
+      }
+    else
+      {
+        info->symbol_name[0] = '\0';
+        info->symbol_address = 0;
+      }
+  }
+#else
+  if (! dladdr ((gpointer) address, &dl_info))
+    return FALSE;
+
+  if (dl_info.dli_fname)
+    {
+      g_strlcpy (info->object_name, dl_info.dli_fname,
+                 sizeof (info->object_name));
+    }
+  else
+    {
+      info->object_name[0] = '\0';
+    }
+
+  if (dl_info.dli_sname)
+    {
+      g_strlcpy (info->symbol_name, dl_info.dli_sname,
+                 sizeof (info->symbol_name));
+    }
+  else
+    {
+      info->symbol_name[0] = '\0';
+    }
+
+  info->symbol_address = (guintptr) dl_info.dli_saddr;
+#endif
+
+  return TRUE;
+}
+
+
+#endif /* GIMP_BACKTRACE_BACKEND_LINUX */
diff --git a/app/core/gimpbacktrace-none.c b/app/core/gimpbacktrace-none.c
new file mode 100644
index 0000000000..e0b069b7f2
--- /dev/null
+++ b/app/core/gimpbacktrace-none.c
@@ -0,0 +1,116 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
+ *
+ * backtrace-none.c
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+#include "config.h"
+
+#include "gimpbacktrace-backend.h"
+
+
+#ifdef GIMP_BACKTRACE_BACKEND_NONE
+
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "core-types.h"
+
+#include "gimpbacktrace.h"
+
+
+/*  public functions  */
+
+
+gboolean
+gimp_backtrace_init (void)
+{
+  return FALSE;
+}
+
+void
+gimp_backtrace_shutdown (void)
+{
+}
+
+GimpBacktrace *
+gimp_backtrace_new (gboolean include_current_thread)
+{
+  return NULL;
+}
+
+void
+gimp_backtrace_free (GimpBacktrace *backtrace)
+{
+  g_return_if_fail (backtrace == NULL);
+}
+
+gint
+gimp_backtrace_get_n_threads (GimpBacktrace *backtrace)
+{
+  g_return_val_if_reached (0);
+}
+
+guintptr
+gimp_backtrace_get_thread_id (GimpBacktrace *backtrace,
+                              gint           thread)
+{
+  g_return_val_if_reached (0);
+}
+
+const gchar *
+gimp_backtrace_get_thread_name (GimpBacktrace *backtrace,
+                                gint           thread)
+{
+  g_return_val_if_reached (NULL);
+}
+
+gint
+gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace,
+                                  guintptr       thread_id,
+                                  gint           thread_hint)
+{
+  g_return_val_if_reached (-1);
+}
+
+gint
+gimp_backtrace_get_n_frames (GimpBacktrace *backtrace,
+                             gint           thread)
+{
+  g_return_val_if_reached (0);
+}
+
+gpointer
+gimp_backtrace_get_frame_address (GimpBacktrace *backtrace,
+                                  gint           thread,
+                                  gint           frame)
+{
+  g_return_val_if_reached (NULL);
+}
+
+gboolean
+gimp_backtrace_get_symbol_info (guintptr                 address,
+                                GimpBacktraceSymbolInfo *info)
+{
+  return FALSE;
+}
+
+
+#endif /* GIMP_BACKTRACE_BACKEND_NONE */
diff --git a/app/core/gimpbacktrace.h b/app/core/gimpbacktrace.h
new file mode 100644
index 0000000000..d2ecde59ea
--- /dev/null
+++ b/app/core/gimpbacktrace.h
@@ -0,0 +1,62 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpbacktrace.h
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_BACKTRACE_H__
+#define __GIMP_BACKTRACE_H__
+
+
+typedef struct _GimpBacktraceSymbolInfo GimpBacktraceSymbolInfo;
+
+
+struct _GimpBacktraceSymbolInfo
+{
+  gchar    object_name[256];
+  gchar    symbol_name[256];
+  guintptr symbol_address;
+};
+
+
+gboolean        gimp_backtrace_init              (void);
+void            gimp_backtrace_shutdown          (void);
+
+GimpBacktrace * gimp_backtrace_new               (gboolean                include_current_thread);
+void            gimp_backtrace_free              (GimpBacktrace          *backtrace);
+
+gint            gimp_backtrace_get_n_threads     (GimpBacktrace          *backtrace);
+guintptr        gimp_backtrace_get_thread_id     (GimpBacktrace          *backtrace,
+                                                  gint                    thread);
+const gchar   * gimp_backtrace_get_thread_name   (GimpBacktrace          *backtrace,
+                                                  gint                    thread);
+
+gint            gimp_backtrace_find_thread_by_id (GimpBacktrace          *backtrace,
+                                                  guintptr                thread_id,
+                                                  gint                    thread_hint);
+
+gint            gimp_backtrace_get_n_frames      (GimpBacktrace          *backtrace,
+                                                  gint                    thread);
+guintptr        gimp_backtrace_get_frame_address (GimpBacktrace          *backtrace,
+                                                  gint                    thread,
+                                                  gint                    frame);
+
+gboolean        gimp_backtrace_get_symbol_info   (guintptr                 address,
+                                                  GimpBacktraceSymbolInfo *info);
+
+
+#endif  /*  __GIMP_BACKTRACE_H__  */
diff --git a/configure.ac b/configure.ac
index 4beb9b1d8d..4130b493ea 100644
--- a/configure.ac
+++ b/configure.ac
@@ -82,6 +82,7 @@ m4_define([rsvg_required_version], [2.40.6])
 m4_define([webkitgtk_required_version], [2.20.3])
 m4_define([webp_required_version], [0.6.0])
 m4_define([wmf_required_version], [0.2.8])
+m4_define([libunwind_required_version], [1.1.0])
 
 # Current test considers only 2 version numbers. If we update the recommended
 # version of gettext with more version numbers, please update the tests.
@@ -177,6 +178,7 @@ RSVG_REQUIRED_VERSION=rsvg_required_version
 WEBKITGTK_REQUIRED_VERSION=webkitgtk_required_version
 WEBP_REQUIRED_VERSION=webp_required_version
 WMF_REQUIRED_VERSION=wmf_required_version
+LIBUNWIND_REQUIRED_VERSION=libunwind_required_version
 XGETTEXT_REQUIRED_VERSION=xgettext_required_version
 AC_SUBST(APPSTREAM_GLIB_REQUIRED_VERSION)
 AC_SUBST(ATK_REQUIRED_VERSION)
@@ -210,6 +212,7 @@ AC_SUBST(RSVG_REQUIRED_VERSION)
 AC_SUBST(WEBKITGTK_REQUIRED_VERSION)
 AC_SUBST(WEBP_REQUIRED_VERSION)
 AC_SUBST(WMF_REQUIRED_VERSION)
+AC_SUBST(LIBUNWIND_REQUIRED_VERSION)
 AC_SUBST(XGETTEXT_REQUIRED_VERSION)
 
 # The symbol GIMP_UNSTABLE is defined above for substitution in
@@ -309,6 +312,23 @@ case "$host_cpu" in
 esac
 
 
+#################
+# Check for Linux
+#################
+
+AC_MSG_CHECKING([if compiling for Linux])
+case "$host_os" in
+  linux*)
+    platform_linux=yes
+    ;;
+  *)
+    platform_linux=no
+    ;;
+esac
+AC_MSG_RESULT([$platform_linux])
+AM_CONDITIONAL(PLATFORM_LINUX, test "$platform_linux" = "yes")
+
+
 #################
 # Check for Win32
 #################
@@ -1759,6 +1779,35 @@ AC_SUBST(FILE_HEIF)
 AM_CONDITIONAL(HAVE_LIBHEIF, test "x$have_libheif" = xyes)
 
 
+#######################################
+# Check for detailed backtraces support
+#######################################
+
+detailed_backtraces=no
+
+
+#####################
+# Check for libunwind
+#####################
+
+AC_ARG_WITH(libunwind, [  --without-libunwind       build without libunwind support])
+
+have_libunwind=no
+if test "x$with_libunwind" != xno; then
+  have_libunwind=yes
+  PKG_CHECK_MODULES(LIBUNWIND, libunwind >= libunwind_required_version,
+    [],
+    [have_libunwind="no (libunwind not found)"])
+fi
+
+if test "x$have_libunwind" = xyes; then
+  AC_DEFINE(HAVE_LIBUNWIND, 1,
+            [Define to 1 if libunwind is available])
+
+  detailed_backtraces=yes
+fi
+
+
 ######################
 # Check for libmypaint
 ######################
@@ -2889,6 +2938,7 @@ Optional Features:
   Default ICC directory:     $with_icc_directory
   Debug console (Win32):     $enable_win32_debug_console
   32-bit DLL folder (Win32): $with_win32_32bit_dll_folder
+  Detailed backtraces:       $detailed_backtraces
 
 Optional Plug-Ins:
   Ascii Art:                 $have_libaa


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]