libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd
, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
This commit is contained in:
@ -38,6 +38,7 @@
|
|||||||
#include <glib/gprintf.h>
|
#include <glib/gprintf.h>
|
||||||
|
|
||||||
#if defined(G_OS_WIN32)
|
#if defined(G_OS_WIN32)
|
||||||
|
|
||||||
/* This is a hack for Windows known directory support.
|
/* This is a hack for Windows known directory support.
|
||||||
* DATADIR (autotools-generated constant) is a type defined in objidl.h
|
* DATADIR (autotools-generated constant) is a type defined in objidl.h
|
||||||
* so we must #undef it before including shlobj.h in order to avoid a
|
* so we must #undef it before including shlobj.h in order to avoid a
|
||||||
@ -45,15 +46,22 @@
|
|||||||
#undef DATADIR
|
#undef DATADIR
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
|
|
||||||
#else /* G_OS_WIN32 */
|
#else /* G_OS_WIN32 */
|
||||||
|
|
||||||
/* For waitpid() */
|
/* For waitpid() */
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
/* For thread IDs. */
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
#ifdef HAVE_SYS_PRCTL_H
|
#ifdef HAVE_SYS_PRCTL_H
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* G_OS_WIN32 */
|
#endif /* G_OS_WIN32 */
|
||||||
|
|
||||||
#include "gimpbasetypes.h"
|
#include "gimpbasetypes.h"
|
||||||
@ -1152,9 +1160,9 @@ gimp_stack_trace_available (gboolean optimal)
|
|||||||
* Since: 2.10
|
* Since: 2.10
|
||||||
**/
|
**/
|
||||||
gboolean
|
gboolean
|
||||||
gimp_stack_trace_print (const gchar *prog_name,
|
gimp_stack_trace_print (const gchar *prog_name,
|
||||||
gpointer stream,
|
gpointer stream,
|
||||||
gchar **trace)
|
gchar **trace)
|
||||||
{
|
{
|
||||||
gboolean stack_printed = FALSE;
|
gboolean stack_printed = FALSE;
|
||||||
|
|
||||||
@ -1162,13 +1170,19 @@ gimp_stack_trace_print (const gchar *prog_name,
|
|||||||
#ifndef G_OS_WIN32
|
#ifndef G_OS_WIN32
|
||||||
GString *gtrace = NULL;
|
GString *gtrace = NULL;
|
||||||
gchar gimp_pid[16];
|
gchar gimp_pid[16];
|
||||||
pid_t pid;
|
|
||||||
gchar buffer[256];
|
gchar buffer[256];
|
||||||
ssize_t read_n;
|
ssize_t read_n;
|
||||||
int sync_fd[2];
|
int sync_fd[2];
|
||||||
int out_fd[2];
|
int out_fd[2];
|
||||||
|
pid_t fork_pid;
|
||||||
|
pid_t pid = getpid();
|
||||||
|
#if defined(G_OS_WIN32)
|
||||||
|
DWORD tid = GetCurrentThreadId ();
|
||||||
|
#else
|
||||||
|
long tid = syscall (SYS_gettid);
|
||||||
|
#endif
|
||||||
|
|
||||||
g_snprintf (gimp_pid, 16, "%u", (guint) getpid ());
|
g_snprintf (gimp_pid, 16, "%u", (guint) pid);
|
||||||
|
|
||||||
if (pipe (sync_fd) == -1)
|
if (pipe (sync_fd) == -1)
|
||||||
{
|
{
|
||||||
@ -1183,17 +1197,19 @@ gimp_stack_trace_print (const gchar *prog_name,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid = fork ();
|
fork_pid = fork ();
|
||||||
if (pid == 0)
|
if (fork_pid == 0)
|
||||||
{
|
{
|
||||||
/* Child process. */
|
/* Child process. */
|
||||||
gchar *args[7] = { "gdb", "-batch", "-ex", "thread apply all backtrace full",
|
gchar *args[9] = { "gdb", "-batch",
|
||||||
|
"-ex", "info threads",
|
||||||
|
"-ex", "thread apply all backtrace full",
|
||||||
(gchar *) prog_name, NULL, NULL };
|
(gchar *) prog_name, NULL, NULL };
|
||||||
|
|
||||||
if (prog_name == NULL)
|
if (prog_name == NULL)
|
||||||
args[4] = "-p";
|
args[6] = "-p";
|
||||||
|
|
||||||
args[5] = gimp_pid;
|
args[7] = gimp_pid;
|
||||||
|
|
||||||
/* Wait until the parent enabled us to ptrace it. */
|
/* Wait until the parent enabled us to ptrace it. */
|
||||||
{
|
{
|
||||||
@ -1220,8 +1236,10 @@ gimp_stack_trace_print (const gchar *prog_name,
|
|||||||
{
|
{
|
||||||
/* LLDB as alternative if the GDB call failed or if it was in
|
/* LLDB as alternative if the GDB call failed or if it was in
|
||||||
* a too-old version. */
|
* a too-old version. */
|
||||||
gchar *args_lldb[11] = { "lldb", "--attach-pid", NULL, "--batch",
|
gchar *args_lldb[15] = { "lldb", "--attach-pid", NULL, "--batch",
|
||||||
"--one-line", "bt",
|
"--one-line", "thread list",
|
||||||
|
"--one-line", "thread backtrace all",
|
||||||
|
"--one-line", "bt all",
|
||||||
"--one-line-on-crash", "bt",
|
"--one-line-on-crash", "bt",
|
||||||
"--one-line-on-crash", "quit", NULL };
|
"--one-line-on-crash", "quit", NULL };
|
||||||
|
|
||||||
@ -1232,7 +1250,7 @@ gimp_stack_trace_print (const gchar *prog_name,
|
|||||||
|
|
||||||
_exit (0);
|
_exit (0);
|
||||||
}
|
}
|
||||||
else if (pid > 0)
|
else if (fork_pid > 0)
|
||||||
{
|
{
|
||||||
/* Main process */
|
/* Main process */
|
||||||
int status;
|
int status;
|
||||||
@ -1240,7 +1258,7 @@ gimp_stack_trace_print (const gchar *prog_name,
|
|||||||
/* Allow the child to ptrace us, and signal it to start. */
|
/* Allow the child to ptrace us, and signal it to start. */
|
||||||
close (sync_fd[0]);
|
close (sync_fd[0]);
|
||||||
#ifdef PR_SET_PTRACER
|
#ifdef PR_SET_PTRACER
|
||||||
prctl (PR_SET_PTRACER, pid, 0, 0, 0);
|
prctl (PR_SET_PTRACER, fork_pid, 0, 0, 0);
|
||||||
#endif
|
#endif
|
||||||
close (sync_fd[1]);
|
close (sync_fd[1]);
|
||||||
|
|
||||||
@ -1252,6 +1270,20 @@ gimp_stack_trace_print (const gchar *prog_name,
|
|||||||
|
|
||||||
while ((read_n = read (out_fd[0], buffer, 256)) > 0)
|
while ((read_n = read (out_fd[0], buffer, 256)) > 0)
|
||||||
{
|
{
|
||||||
|
if (! stack_printed)
|
||||||
|
{
|
||||||
|
if (stream)
|
||||||
|
g_fprintf (stream,
|
||||||
|
"\n# Stack traces obtained from PID %d - Thread %lu #\n\n",
|
||||||
|
pid, tid);
|
||||||
|
if (trace)
|
||||||
|
{
|
||||||
|
gtrace = g_string_new (NULL);
|
||||||
|
g_string_printf (gtrace,
|
||||||
|
"\n# Stack traces obtained from PID %d - Thread %lu #\n\n",
|
||||||
|
pid, tid);
|
||||||
|
}
|
||||||
|
}
|
||||||
/* It's hard to know if the debugger was found since it
|
/* It's hard to know if the debugger was found since it
|
||||||
* happened in the child. Let's just assume that any output
|
* happened in the child. Let's just assume that any output
|
||||||
* means it succeeded.
|
* means it succeeded.
|
||||||
@ -1262,11 +1294,7 @@ gimp_stack_trace_print (const gchar *prog_name,
|
|||||||
if (stream)
|
if (stream)
|
||||||
g_fprintf (stream, "%s", buffer);
|
g_fprintf (stream, "%s", buffer);
|
||||||
if (trace)
|
if (trace)
|
||||||
{
|
g_string_append (gtrace, (const gchar *) buffer);
|
||||||
if (! gtrace)
|
|
||||||
gtrace = g_string_new (NULL);
|
|
||||||
g_string_append (gtrace, (const gchar *) buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
close (out_fd[0]);
|
close (out_fd[0]);
|
||||||
|
|
||||||
@ -1275,9 +1303,9 @@ gimp_stack_trace_print (const gchar *prog_name,
|
|||||||
prctl (PR_SET_PTRACER, 0, 0, 0, 0);
|
prctl (PR_SET_PTRACER, 0, 0, 0, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
waitpid (pid, &status, 0);
|
waitpid (fork_pid, &status, 0);
|
||||||
}
|
}
|
||||||
/* else if (pid == (pid_t) -1)
|
/* else if (fork_pid == (pid_t) -1)
|
||||||
* Fork failed!
|
* Fork failed!
|
||||||
* Just continue, maybe the backtrace() API will succeed.
|
* Just continue, maybe the backtrace() API will succeed.
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user