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);