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.
(cherry picked from commit 80bf686c94
)
This commit is contained in:
@ -74,6 +74,10 @@ libapp_a_SOURCES = $(libapp_sources)
|
|||||||
gimp_@GIMP_APP_VERSION@_SOURCES = $(libapp_sources) main.c
|
gimp_@GIMP_APP_VERSION@_SOURCES = $(libapp_sources) main.c
|
||||||
|
|
||||||
|
|
||||||
|
if PLATFORM_LINUX
|
||||||
|
libdl = -ldl
|
||||||
|
endif
|
||||||
|
|
||||||
if PLATFORM_OSX
|
if PLATFORM_OSX
|
||||||
framework_cocoa = -framework Cocoa
|
framework_cocoa = -framework Cocoa
|
||||||
endif
|
endif
|
||||||
@ -173,9 +177,11 @@ gimpconsoleldadd = \
|
|||||||
$(Z_LIBS) \
|
$(Z_LIBS) \
|
||||||
$(JSON_C_LIBS) \
|
$(JSON_C_LIBS) \
|
||||||
$(LIBMYPAINT_LIBS) \
|
$(LIBMYPAINT_LIBS) \
|
||||||
|
$(LIBUNWIND_LIBS) \
|
||||||
$(INTLLIBS) \
|
$(INTLLIBS) \
|
||||||
$(RT_LIBS) \
|
$(RT_LIBS) \
|
||||||
$(libm)
|
$(libm) \
|
||||||
|
$(libdl)
|
||||||
|
|
||||||
gimp_@GIMP_APP_VERSION@_LDFLAGS = \
|
gimp_@GIMP_APP_VERSION@_LDFLAGS = \
|
||||||
$(AM_LDFLAGS) \
|
$(AM_LDFLAGS) \
|
||||||
|
@ -21,6 +21,7 @@ AM_CPPFLAGS = \
|
|||||||
$(LIBMYPAINT_CFLAGS) \
|
$(LIBMYPAINT_CFLAGS) \
|
||||||
$(MYPAINT_BRUSHES_CFLAGS) \
|
$(MYPAINT_BRUSHES_CFLAGS) \
|
||||||
$(GEXIV2_CFLAGS) \
|
$(GEXIV2_CFLAGS) \
|
||||||
|
$(LIBUNWIND_CFLAGS) \
|
||||||
-I$(includedir)
|
-I$(includedir)
|
||||||
|
|
||||||
AM_CFLAGS = \
|
AM_CFLAGS = \
|
||||||
@ -93,6 +94,10 @@ libappcore_a_sources = \
|
|||||||
gimpauxitem.h \
|
gimpauxitem.h \
|
||||||
gimpauxitemundo.c \
|
gimpauxitemundo.c \
|
||||||
gimpauxitemundo.h \
|
gimpauxitemundo.h \
|
||||||
|
gimpbacktrace.h \
|
||||||
|
gimpbacktrace-backend.h \
|
||||||
|
gimpbacktrace-linux.c \
|
||||||
|
gimpbacktrace-none.c \
|
||||||
gimpbezierdesc.h \
|
gimpbezierdesc.h \
|
||||||
gimpbezierdesc.c \
|
gimpbezierdesc.c \
|
||||||
gimpboundary.c \
|
gimpboundary.c \
|
||||||
|
@ -211,6 +211,7 @@ typedef struct _GimpWaitable GimpWaitable; /* dummy typede
|
|||||||
|
|
||||||
/* non-object types */
|
/* non-object types */
|
||||||
|
|
||||||
|
typedef struct _GimpBacktrace GimpBacktrace;
|
||||||
typedef struct _GimpBoundSeg GimpBoundSeg;
|
typedef struct _GimpBoundSeg GimpBoundSeg;
|
||||||
typedef struct _GimpCoords GimpCoords;
|
typedef struct _GimpCoords GimpCoords;
|
||||||
typedef struct _GimpGradientSegment GimpGradientSegment;
|
typedef struct _GimpGradientSegment GimpGradientSegment;
|
||||||
|
32
app/core/gimpbacktrace-backend.h
Normal file
32
app/core/gimpbacktrace-backend.h
Normal file
@ -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__ */
|
591
app/core/gimpbacktrace-linux.c
Normal file
591
app/core/gimpbacktrace-linux.c
Normal file
@ -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 */
|
116
app/core/gimpbacktrace-none.c
Normal file
116
app/core/gimpbacktrace-none.c
Normal file
@ -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 */
|
62
app/core/gimpbacktrace.h
Normal file
62
app/core/gimpbacktrace.h
Normal file
@ -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__ */
|
50
configure.ac
50
configure.ac
@ -82,6 +82,7 @@ m4_define([perl_required_version], [5.10.0])
|
|||||||
m4_define([python2_required_version], [2.5.0])
|
m4_define([python2_required_version], [2.5.0])
|
||||||
m4_define([webp_required_version], [0.6.0])
|
m4_define([webp_required_version], [0.6.0])
|
||||||
m4_define([libheif_required_version], [1.1.0])
|
m4_define([libheif_required_version], [1.1.0])
|
||||||
|
m4_define([libunwind_required_version], [1.1.0])
|
||||||
|
|
||||||
# Current test considers only 2 version numbers. If we update the recommended
|
# Current test considers only 2 version numbers. If we update the recommended
|
||||||
# version of gettext with more version numbers, please update the tests.
|
# version of gettext with more version numbers, please update the tests.
|
||||||
@ -176,6 +177,7 @@ PERL_REQUIRED_VERSION=perl_required_version
|
|||||||
PYTHON2_REQUIRED_VERSION=python2_required_version
|
PYTHON2_REQUIRED_VERSION=python2_required_version
|
||||||
WEBP_REQUIRED_VERSION=webp_required_version
|
WEBP_REQUIRED_VERSION=webp_required_version
|
||||||
LIBHEIF_REQUIRED_VERSION=libheif_required_version
|
LIBHEIF_REQUIRED_VERSION=libheif_required_version
|
||||||
|
LIBUNWIND_REQUIRED_VERSION=libunwind_required_version
|
||||||
XGETTEXT_REQUIRED_VERSION=xgettext_required_version
|
XGETTEXT_REQUIRED_VERSION=xgettext_required_version
|
||||||
AC_SUBST(GLIB_REQUIRED_VERSION)
|
AC_SUBST(GLIB_REQUIRED_VERSION)
|
||||||
AC_SUBST(GDK_PIXBUF_REQUIRED_VERSION)
|
AC_SUBST(GDK_PIXBUF_REQUIRED_VERSION)
|
||||||
@ -208,6 +210,7 @@ AC_SUBST(PERL_REQUIRED_VERSION)
|
|||||||
AC_SUBST(PYTHON2_REQUIRED_VERSION)
|
AC_SUBST(PYTHON2_REQUIRED_VERSION)
|
||||||
AC_SUBST(WEBP_REQUIRED_VERSION)
|
AC_SUBST(WEBP_REQUIRED_VERSION)
|
||||||
AC_SUBST(LIBHEIF_REQUIRED_VERSION)
|
AC_SUBST(LIBHEIF_REQUIRED_VERSION)
|
||||||
|
AC_SUBST(LIBUNWIND_REQUIRED_VERSION)
|
||||||
AC_SUBST(XGETTEXT_REQUIRED_VERSION)
|
AC_SUBST(XGETTEXT_REQUIRED_VERSION)
|
||||||
|
|
||||||
# The symbol GIMP_UNSTABLE is defined above for substitution in
|
# The symbol GIMP_UNSTABLE is defined above for substitution in
|
||||||
@ -307,6 +310,23 @@ case "$host_cpu" in
|
|||||||
esac
|
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
|
# Check for Win32
|
||||||
#################
|
#################
|
||||||
@ -1799,6 +1819,35 @@ AC_SUBST(FILE_HEIF)
|
|||||||
AM_CONDITIONAL(HAVE_LIBHEIF, test "x$have_libheif" = xyes)
|
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
|
# Check for libmypaint
|
||||||
######################
|
######################
|
||||||
@ -2933,6 +2982,7 @@ Optional Features:
|
|||||||
Default ICC directory: $with_icc_directory
|
Default ICC directory: $with_icc_directory
|
||||||
Debug console (Win32): $enable_win32_debug_console
|
Debug console (Win32): $enable_win32_debug_console
|
||||||
32-bit DLL folder (Win32): $with_win32_32bit_dll_folder
|
32-bit DLL folder (Win32): $with_win32_32bit_dll_folder
|
||||||
|
Detailed backtraces: $detailed_backtraces
|
||||||
|
|
||||||
Optional Plug-Ins:
|
Optional Plug-Ins:
|
||||||
Ascii Art: $have_libaa
|
Ascii Art: $have_libaa
|
||||||
|
Reference in New Issue
Block a user