Bug 793514 - Adding version check for gdb.
It seems that older GDB (under version 7) are not handling very well some common debug information format, in particular DWARF > 3. Such version of GDB is usually not a problem since it is quite old (more than 10 years old, it would seem) so you don't see it anymore on any modern GNU/Linux distribution. On FreeBSD on the other hand, it is still available (probably for license reasons) and even installed by default! As a consequence, it makes debugging fail, even though LLDB is also installed by default. That is even more of a problem because it would seem that GIMP is killed (most likely by FreeBSD kernel according to the reporter tests) as a side-effect of GDB failing, which is seriously bad, in particular since we also use the debug dialog for non-fatal errors (which could therefore end up killing GIMP as side effect of a bad GDB!). So I add some GDB version check. I implement this without any dynamic memory management, as usual, since this needs to happen also during crash handling where the state is unstable and prone to memory allocation failure. I also add gimp_utils_backtrace_available() public API which can be used by the Preferences.
This commit is contained in:
parent
87525c8911
commit
6975188d6f
2 changed files with 182 additions and 3 deletions
|
|
@ -64,6 +64,13 @@
|
|||
* Utilities of general interest
|
||||
**/
|
||||
|
||||
static gboolean gimp_utils_generic_available (const gchar *program,
|
||||
gint major,
|
||||
gint minor);
|
||||
static gboolean gimp_utils_gdb_available (gint major,
|
||||
gint minor);
|
||||
static gboolean gimp_utils_lldb_available (gint major,
|
||||
gint minor);
|
||||
|
||||
/**
|
||||
* gimp_utf8_strtrim:
|
||||
|
|
@ -1146,10 +1153,17 @@ gimp_print_stack_trace (const gchar *prog_name,
|
|||
close (out_fd[0]);
|
||||
close (out_fd[1]);
|
||||
|
||||
/* Run GDB. */
|
||||
if (execvp (args[0], args) == -1)
|
||||
/* Run GDB if version 7.0 or over. Why I do such a check is that
|
||||
* it turns out older versions may not only fail, but also have
|
||||
* very undesirable side effects like terminating the debugged
|
||||
* program, at least on FreeBSD where GDB 6.1 is apparently
|
||||
* installed by default on the stable release at day of writing.
|
||||
* See bug 793514. */
|
||||
if (! gimp_utils_gdb_available (7, 0) ||
|
||||
execvp (args[0], args) == -1)
|
||||
{
|
||||
/* LLDB as alternative. */
|
||||
/* LLDB as alternative if the GDB call failed or if it was in
|
||||
* a too-old version. */
|
||||
gchar *args_lldb[11] = { "lldb", "--attach-pid", NULL, "--batch",
|
||||
"--one-line", "bt",
|
||||
"--one-line-on-crash", "bt",
|
||||
|
|
@ -1306,3 +1320,167 @@ gimp_on_error_query (const gchar *prog_name)
|
|||
goto retry;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_utils_backtrace_available:
|
||||
* @optimal: whether we get optimal traces.
|
||||
*
|
||||
* Returns #TRUE if we have dependencies to generate backtraces. If
|
||||
* @optimal is #TRUE, the function will return #TRUE only when we
|
||||
* are able to generate optimal traces (i.e. with GDB or LLDB);
|
||||
* otherwise we return #TRUE even if only backtrace() API is available.
|
||||
*
|
||||
* On Win32, we return TRUE if Dr. Mingw is built-in, FALSE otherwise.
|
||||
*
|
||||
* Since: 2.10
|
||||
**/
|
||||
gboolean
|
||||
gimp_utils_backtrace_available (gboolean optimal)
|
||||
{
|
||||
#ifndef G_OS_WIN32
|
||||
if (gimp_utils_gdb_available (7, 0) ||
|
||||
gimp_utils_lldb_available (0, 0))
|
||||
return TRUE;
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
if (! optimal)
|
||||
return TRUE;
|
||||
#endif
|
||||
#else /* G_OS_WIN32 */
|
||||
#ifdef HAVE_EXCHNDL
|
||||
return TRUE;
|
||||
#endif
|
||||
#endif /* G_OS_WIN32 */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Private functions. */
|
||||
|
||||
static gboolean
|
||||
gimp_utils_generic_available (const gchar *program,
|
||||
gint major,
|
||||
gint minor)
|
||||
{
|
||||
#ifndef G_OS_WIN32
|
||||
pid_t pid;
|
||||
int out_fd[2];
|
||||
|
||||
if (pipe (out_fd) == -1)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* XXX: I don't use g_spawn_sync() or similar glib functions because
|
||||
* to read the contents of the stdout, these functions would allocate
|
||||
* memory dynamically. As we know, when debugging crashes, this is a
|
||||
* definite blocker. So instead I simply use a buffer on the stack
|
||||
* with a lower level fork() call.
|
||||
*/
|
||||
pid = fork ();
|
||||
if (pid == 0)
|
||||
{
|
||||
/* Child process. */
|
||||
gchar *args[3] = { (gchar *) program, "--version", NULL };
|
||||
|
||||
/* Redirect the debugger output. */
|
||||
dup2 (out_fd[1], STDOUT_FILENO);
|
||||
close (out_fd[0]);
|
||||
close (out_fd[1]);
|
||||
|
||||
/* Run version check. */
|
||||
execvp (args[0], args);
|
||||
_exit (-1);
|
||||
}
|
||||
else if (pid > 0)
|
||||
{
|
||||
/* Main process */
|
||||
gchar buffer[256];
|
||||
ssize_t read_n;
|
||||
int status;
|
||||
gint installed_major = 0;
|
||||
gint installed_minor = 0;
|
||||
gboolean major_reading = FALSE;
|
||||
gboolean minor_reading = FALSE;
|
||||
gint i;
|
||||
gchar c;
|
||||
|
||||
waitpid (pid, &status, 0);
|
||||
|
||||
if (! WIFEXITED (status) || WEXITSTATUS (status) != 0)
|
||||
return FALSE;
|
||||
|
||||
/* It is important to close the writing side of the pipe, otherwise
|
||||
* the read() will wait forever without getting the information that
|
||||
* writing is finished.
|
||||
*/
|
||||
close (out_fd[1]);
|
||||
|
||||
/* I could loop forever until EOL, but I am pretty sure the
|
||||
* version information is stored on the first line and one call to
|
||||
* read() with 256 characters should be more than enough.
|
||||
*/
|
||||
read_n = read (out_fd[0], buffer, 256);
|
||||
|
||||
/* This is quite a very stupid parser. I only look for the first
|
||||
* numbers and consider them as version information. This works
|
||||
* fine for both GDB and LLDB as far as I can see for the output
|
||||
* of `${program} --version` but this should obviously not be
|
||||
* considered as a *really* generic version test.
|
||||
*/
|
||||
for (i = 0; i < read_n; i++)
|
||||
{
|
||||
c = buffer[i];
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
if (minor_reading)
|
||||
{
|
||||
installed_minor = 10 * installed_minor + (c - '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
major_reading = TRUE;
|
||||
installed_major = 10 * installed_major + (c - '0');
|
||||
}
|
||||
}
|
||||
else if (c == '.')
|
||||
{
|
||||
if (major_reading)
|
||||
{
|
||||
minor_reading = TRUE;
|
||||
major_reading = FALSE;
|
||||
}
|
||||
else if (minor_reading)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
/* Version information should be in the first line. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
close (out_fd[0]);
|
||||
|
||||
return (installed_major > 0 &&
|
||||
(installed_major > major ||
|
||||
(installed_major == major && installed_minor >= minor)));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fork failed, or Win32. */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gimp_utils_gdb_available (gint major,
|
||||
gint minor)
|
||||
{
|
||||
return gimp_utils_generic_available ("gdb", major, minor);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gimp_utils_lldb_available (gint major,
|
||||
gint minor)
|
||||
{
|
||||
return gimp_utils_generic_available ("lldb", major, minor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ gboolean gimp_print_stack_trace (const gchar *prog_name,
|
|||
gpointer stream,
|
||||
gchar **trace);
|
||||
void gimp_on_error_query (const gchar *prog_name);
|
||||
gboolean gimp_utils_backtrace_available (gboolean optimal);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
|||
Loading…
Reference in a new issue