From f3c1679442e7cdf3090ea0e47e0e59e2ade86f37 Mon Sep 17 00:00:00 2001 From: Gustavo Sverzut Barbieri Date: Sat, 11 Jul 2009 16:27:37 +0000 Subject: [PATCH] make it easy to run e under valgrind and debug crashes. this should help us in near future: * -valgrind[=MODE], to run under valgrind and toggle interesting options as we often request users to give us valgrind logs but they're often running valgrind on wrong binaries and with wrong parameters. See enlightenment_start -help for more, but in sort: -valgrind=all should give us enough information. * -valgrind-log-file=path, so the log will not mix with lots of output we do. * debug: on crashes, always list traceback (execinfo/glibc functions) and check if gdb exists, if so ask it to print out traceback of all threads to stdout. gdb will be detached as soon as messages were printed and everything should work without gdb as well. SVN revision: 41309 --- src/bin/e_signals.c | 85 ++++++++++++++++++--- src/bin/e_start_main.c | 162 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 224 insertions(+), 23 deletions(-) diff --git a/src/bin/e_signals.c b/src/bin/e_signals.c index 0b7890b63..4cea02396 100644 --- a/src/bin/e_signals.c +++ b/src/bin/e_signals.c @@ -25,6 +25,73 @@ _e_x_composite_shutdown(void) _e_x_composite_shutdown_try = 0; } +#define _e_write_safe(fd, buf) _e_write_safe_int(fd, buf, sizeof(buf)) +static void +_e_write_safe_int(int fd, const char *buf, size_t size) +{ + while (size > 0) + { + ssize_t done = write(fd, buf, size); + if (done >= 0) + { + buf += done; + size -= done; + } + else + { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + else + { + perror("write"); + return; + } + } + } +} + +static void +_e_gdb_print_backtrace(int fd) +{ + char cmd[1024]; + size_t size; + + size = snprintf(cmd, sizeof(cmd), + "gdb --pid=%d " + "-ex 'thread apply all bt' " + "-ex detach -ex quit", getpid()); + + if (size >= sizeof(cmd)) + return; + + _e_write_safe(fd, "EXECUTING GDB AS: "); + _e_write_safe_int(fd, cmd, size); + _e_write_safe(fd, "\n"); + system(cmd); // TODO: use popen() or fork()+pipe()+exec() and save to 'fd' +} + +#define _e_backtrace(msg) _e_backtrace_int(2, msg, sizeof(msg)) +static void +_e_backtrace_int(int fd, const char *msg, size_t msg_len) +{ + char attachmsg[1024]; + void *array[255]; + size_t size; + + _e_write_safe_int(fd, msg, msg_len); + _e_write_safe(fd, "\nBEGIN TRACEBACK\n"); + size = backtrace(array, 255); + backtrace_symbols_fd(array, size, fd); + _e_write_safe(fd, "END TRACEBACK\n"); + + size = snprintf(attachmsg, sizeof(attachmsg), + "debug with: gdb --pid=%d\n", getpid()); + if (size < sizeof(attachmsg)) + _e_write_safe_int(fd, attachmsg, size); + + _e_gdb_print_backtrace(fd); +} + #ifdef OBJECT_PARANOIA_CHECK /* a tricky little devil, requires e and it's libs to be built @@ -33,13 +100,7 @@ _e_x_composite_shutdown(void) EAPI void e_sigseg_act(int x, siginfo_t *info, void *data) { - void *array[255]; - size_t size; - - write(2, "**** SEGMENTATION FAULT ****\n", 29); - write(2, "**** Printing Backtrace... *****\n\n", 34); - size = backtrace(array, 255); - backtrace_symbols_fd(array, size, 2); + _e_backtrace("**** SEGMENTATION FAULT ****"); _e_x_composite_shutdown(); ecore_x_pointer_ungrab(); ecore_x_keyboard_ungrab(); @@ -60,7 +121,7 @@ e_sigseg_act(int x, siginfo_t *info, void *data) EAPI void e_sigseg_act(int x, siginfo_t *info, void *data) { - write(2, "**** SEGMENTATION FAULT ****\n", 29); + _e_backtrace("**** SEGMENTATION FAULT ****"); _e_x_composite_shutdown(); ecore_x_pointer_ungrab(); ecore_x_keyboard_ungrab(); @@ -82,7 +143,7 @@ e_sigseg_act(int x, siginfo_t *info, void *data) EAPI void e_sigill_act(int x, siginfo_t *info, void *data) { - write(2, "**** ILLEGAL INSTRUCTION ****\n", 30); + _e_backtrace("**** ILLEGAL INSTRUCTION ****"); _e_x_composite_shutdown(); ecore_x_pointer_ungrab(); ecore_x_keyboard_ungrab(); @@ -103,7 +164,7 @@ e_sigill_act(int x, siginfo_t *info, void *data) EAPI void e_sigfpe_act(int x, siginfo_t *info, void *data) { - write(2, "**** FLOATING POINT EXCEPTION ****\n", 35); + _e_backtrace("**** FLOATING POINT EXCEPTION ****"); _e_x_composite_shutdown(); ecore_x_pointer_ungrab(); ecore_x_keyboard_ungrab(); @@ -124,7 +185,7 @@ e_sigfpe_act(int x, siginfo_t *info, void *data) EAPI void e_sigbus_act(int x, siginfo_t *info, void *data) { - write(2, "**** BUS ERROR ****\n", 21); + _e_backtrace("**** BUS ERROR ****"); _e_x_composite_shutdown(); ecore_x_pointer_ungrab(); ecore_x_keyboard_ungrab(); @@ -145,7 +206,7 @@ e_sigbus_act(int x, siginfo_t *info, void *data) EAPI void e_sigabrt_act(int x, siginfo_t *info, void *data) { - write(2, "**** ABORT ****\n", 21); + _e_backtrace("**** ABORT ****"); _e_x_composite_shutdown(); ecore_x_pointer_ungrab(); ecore_x_keyboard_ungrab(); diff --git a/src/bin/e_start_main.c b/src/bin/e_start_main.c index 96536da81..8771e8a1d 100644 --- a/src/bin/e_start_main.c +++ b/src/bin/e_start_main.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #ifdef HAVE_ALLOCA_H #include @@ -316,13 +317,96 @@ precache(void) children--; } exit(0); -} +} + +static int +find_valgrind(char *path, size_t path_len) +{ + char *env = getenv("PATH"); + + while (env) + { + const char *p = strchr(env, ':'); + ssize_t p_len; + + if (p) + p_len = p - env; + else + p_len = strlen(env); + + if (p_len <= 0) + goto next; + else if (p_len + sizeof("/valgrind") >= path_len) + goto next; + + memcpy(path, env, p_len); + memcpy(path + p_len, "/valgrind", sizeof("/valgrind")); + if (access(path, X_OK | R_OK) == 0) + return 1; + + next: + if (p) + env = p + 1; + else + break; + } + path[0] = '\0'; + return 0; +} + + +/* maximum number of arguments added above */ +#define VALGRIND_MAX_ARGS 9 +/* bitmask with all supported bits set */ +#define VALGRIND_MODE_ALL 15 + +static int +valgrind_append(char **dst, int valgrind_mode, const char *valgrind_path, const char *valgrind_log) +{ + int i = 0; + + if (!valgrind_mode) + return 0; + dst[i++] = valgrind_path; + dst[i++] = "--track-origins=yes"; + dst[i++] = "--malloc-fill=0xabc79"; /* invalid pointer, make it crash */ + + if (valgrind_log) + { + static char logparam[PATH_MAX + sizeof("--log-file=")]; + snprintf(logparam, sizeof(logparam), "--log-file=%s", valgrind_log); + dst[i++] = logparam; + } + + if (valgrind_mode & 2) + dst[i++] = "--trace-children=yes"; + + if (valgrind_mode & 4) + { + dst[i++] = "--leak-check=full"; + dst[i++] = "--leak-resolution=high"; + dst[i++] = "--track-fds=yes"; + } + + if (valgrind_mode & 8) + dst[i++] = "--show-reachable=yes"; + + return i; +} + +static void +copy_args(char **dst, const char **src, size_t count) +{ + for (; count > 0; count--, dst++, src++) + *dst = *src; +} int main(int argc, char **argv) { - int i, do_precache = 0; + int i, do_precache = 0, valgrind_mode = 0; char buf[16384], **args, *p; + char valgrind_path[PATH_MAX] = "", *valgrind_log = NULL; prefix_determine(argv[0]); @@ -341,6 +425,32 @@ main(int argc, char **argv) for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-no-precache")) do_precache = 0; + else if (!strncmp(argv[i], "-valgrind", sizeof("-valgrind") - 1)) + { + const char *val = argv[i] + sizeof("-valgrind") - 1; + if (*val == '\0') + valgrind_mode = 1; + else if (*val == '-') + { + val++; + if (!strncmp(val, "log-file=", sizeof("log-file=") - 1)) + { + valgrind_log = val + sizeof("log-file=") - 1; + if (*valgrind_log == '\0') + valgrind_log = NULL; + } + } + else if (*val == '=') + { + val++; + if (!strcmp(val, "all")) + valgrind_mode = VALGRIND_MODE_ALL; + else + valgrind_mode = atoi(val); + } + else + printf("Unknown valgrind option: %s\n", argv[i]); + } else if ((!strcmp(argv[i], "-h")) || (!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help"))) @@ -349,6 +459,15 @@ main(int argc, char **argv) ("Options:\n" "\t-no-precache\n" "\t\tDisable pre-caching of files\n" + "\t-valgrind[=MODE]\n" + "\t\tRun enlightenment from inside valgrind, mode is OR of:\n" + "\t\t 1 = plain valgrind to catch crashes (default)\n" + "\t\t 2 = trace children (thumbnailer, efm slaves, ...)\n" + "\t\t 4 = check leak\n" + "\t\t 8 = show reachable after processes finish.\n" + "\t\t all = all of above\n" + "\t-valgrind-log-file=\n" + "\t\tSave valgrind log to file, see valgrind's --log-file for details.\n" "\n" "Please run:\n" "\tenlightenment %s\n" @@ -358,7 +477,25 @@ main(int argc, char **argv) exit(0); } } - printf("E - PID=%i, do_precache=%i\n", getpid(), do_precache); + + if (valgrind_mode) + { + if (!find_valgrind(valgrind_path, sizeof(valgrind_path))) + { + printf("E - valgrind required but no binary found! Ignoring request.\n"); + valgrind_mode = 0; + } + } + + printf("E - PID=%i, do_precache=%i, valgrind=%d", getpid(), do_precache, valgrind_mode); + if (valgrind_mode) + { + printf(" valgrind-command='%s'", valgrind_path); + if (valgrind_log) + printf(" valgrind-log-file='%s'", valgrind_log); + } + putchar('\n'); + if (do_precache) { void *lib, *func; @@ -392,22 +529,25 @@ main(int argc, char **argv) /* try dbus-launch */ snprintf(buf, sizeof(buf), "%s/bin/enlightenment", _prefix_path); - args = alloca((argc + 3) * sizeof(char *)); + args = alloca((argc + 2 + VALGRIND_MAX_ARGS) * sizeof(char *)); if (!getenv("DBUS_SESSION_BUS_ADDRESS")) { args[0] = "dbus-launch"; args[1] = "--exit-with-session"; - args[2] = buf; - for (i = 1; i < argc; i++) args[2 + i] = argv[i]; - args[2 + i] = NULL; + + i = 2 + valgrind_append(args + 2, valgrind_mode, valgrind_path, valgrind_log); + args[i++] = buf; + copy_args(args + i, argv + 1, argc - 1); + args[i + argc - 1] = NULL; execvp("dbus-launch", args); } /* dbus-launch failed - run e direct */ - args[0] = "enlightenment"; - for (i = 1; i < argc; i++) args[i] = argv[i]; - args[i] = NULL; - execv(buf, args); + i = valgrind_append(args, valgrind_mode, valgrind_path, valgrind_log); + args[i++] = buf; + copy_args(args + i, argv + 1, argc - 1); + args[i + argc - 1] = NULL; + execv(args[0], args); printf("FAILED TO RUN:\n"); printf(" %s\n", buf);