e: full rewrite of our WBOD.

NOTE: the new behaviour require a parent process,
here enlightenment_start, to watch over enlightenment.
It does use ptrace to monitor his child and track
what is going on. When a crash happen it ask gdb to
add the backtrace directly into .xsession-errors.

If you use systemd user session, you may want to offload
that work to systemd. You can do so, but don't forget to
pass -nopause to enlightenment when you start it.

WARNING: For all user, you will need to exit your
current session and start a fresh one for this new WBOD
to work at all. Just restarting Enlightenment will not
help you.


SVN revision: 78742
This commit is contained in:
Cedric BAIL 2012-11-01 06:23:49 +00:00
parent 0b5db2653e
commit 380386817b
7 changed files with 200 additions and 156 deletions

View File

@ -278,6 +278,7 @@ extern EAPI Eina_Bool evil;
extern EAPI Eina_Bool starting;
extern EAPI Eina_Bool stopping;
extern EAPI Eina_Bool restart;
extern EAPI Eina_Bool e_nopause;
extern EAPI Eina_Bool e_precache_end;
extern EAPI Eina_Bool x_fatal;

View File

@ -30,62 +30,12 @@ e_alert_composite_win(Ecore_X_Window root, Ecore_X_Window comp)
}
EAPI void
e_alert_show(int sig)
e_alert_show(void)
{
char *args[4];
pid_t pid;
#define E_ALERT_EXE "/enlightenment/utils/enlightenment_alert"
args[0] = alloca(strlen(e_prefix_lib_get()) + strlen(E_ALERT_EXE) + 1);
strcpy(args[0], e_prefix_lib_get());
strcat(args[0], E_ALERT_EXE);
args[1] = alloca(10);
snprintf(args[1], 10, "%d", sig);
args[2] = alloca(21);
snprintf(args[2], 21, "%lu", (long unsigned int)getpid());
args[3] = NULL;
pid = fork();
if (pid < -1)
goto restart_e;
if (pid == 0)
if (!e_nopause)
{
/* The child process */
execvp(args[0], args);
fprintf(stderr, "PAUSE !\n");
pause();
}
else
{
/* The parent process */
pid_t ret;
int status = 0;
do
{
ret = waitpid(pid, &status, 0);
if (errno == ECHILD)
break ;
}
while (ret != pid);
if (status == 0)
goto restart_e;
if (!WIFEXITED(status))
goto restart_e;
if (WEXITSTATUS(status) == 1)
goto restart_e;
exit(-11);
}
restart_e:
if (getenv("E_START_MTRACK"))
e_util_env_set("MTRACK", "track");
ecore_app_restart();
return ;
}

View File

@ -13,7 +13,7 @@ typedef enum _E_Alert_Op_Type
EINTERN int e_alert_init(void);
EINTERN int e_alert_shutdown(void);
EAPI void e_alert_show(int sig);
EAPI void e_alert_show(void);
#endif
#endif

View File

@ -51,6 +51,7 @@ static const char *title = NULL, *str1 = NULL, *str2 = NULL;
static int ret = 0, sig = 0;
static pid_t pid;
static Eina_Bool tainted = EINA_TRUE;
static const char *backtrace = NULL;
int
main(int argc, char **argv)
@ -72,6 +73,8 @@ main(int argc, char **argv)
sig = atoi(argv[i]); // signal
else if (i == 2)
pid = atoi(argv[i]); // E's pid
else if (i == 3)
backtrace = argv[i];
}
tmp = getenv("E17_TAINTED");
@ -519,15 +522,33 @@ _e_alert_draw_text(void)
if (!tainted)
{
snprintf(msg, sizeof(msg),
"This is not meant to happen and is likely a sign of \n"
"a bug in Enlightenment or the libraries it relies \n"
"on. You can gdb attach to this process (%d) now \n"
"to try debug it or you could exit, or just hit \n"
"restart to try and get your desktop back the way \n"
"it was.\n"
"\n"
"Please compile E17 and EFL with -g in your CFLAGS.\n", pid);
if (backtrace)
{
snprintf(msg, sizeof(msg),
"This is not meant to happen and is likely a sign of \n"
"a bug in Enlightenment or the libraries it relies \n"
"on. You will find an backtrace of E17 (%d) in :\n"
"'%s'\n"
"Before reporting issue, compile latest E17 and EFL\n"
"from svn with '-g -ggdb3' in your CFLAGS.\n"
"You can then report this crash on :\n"
"http://trac.enlightenment.org/e/.\n",
pid, backtrace);
}
else
{
snprintf(msg, sizeof(msg),
"This is not meant to happen and is likely a sign of \n"
"a bug in Enlightenment or the libraries it relies \n"
"on. You can gdb attach to this process (%d) now \n"
"to try debug it or you could exit, or just hit \n"
"restart to try and get your desktop back the way \n"
"it was.\n"
"\n"
"Please compile latest svn E17 and EFL with\n"
"-g and -ggdb3 in your CFLAGS.\n", pid);
}
}
else
{
@ -536,7 +557,8 @@ _e_alert_draw_text(void)
"a sign of a bug, but you are using unsupported\n"
"modules; before reporting this issue, please\n"
"unload them and try to see if the bug is still\n"
"there.\n");
"there. Also update to latest svn and be sure to\n"
"compile E17 and EFL with -g and -ggdb3 in your CFLAGS");
}
strcpy(warn, "");

View File

@ -122,6 +122,7 @@ EAPI Eina_Bool evil = EINA_FALSE;
EAPI Eina_Bool starting = EINA_TRUE;
EAPI Eina_Bool stopping = EINA_FALSE;
EAPI Eina_Bool restart = EINA_FALSE;
EAPI Eina_Bool e_nopause = EINA_FALSE;
static void
_xdg_data_dirs_augment(void)
@ -1200,6 +1201,8 @@ _e_main_parse_arguments(int argc, char **argv)
really_know = EINA_TRUE;
else if (!strcmp(argv[i], "-locked"))
locked = EINA_TRUE;
else if (!strcmp(argv[i], "-nopause"))
e_nopause = EINA_TRUE;
else if ((!strcmp(argv[i], "-h")) ||
(!strcmp(argv[i], "-help")) ||
(!strcmp(argv[i], "--help")))

View File

@ -54,121 +54,60 @@ _e_write_safe_int(int fd, const char *buf, size_t size)
}
}
static void
_e_gdb_print_backtrace(int fd __UNUSED__)
{
// FIXME: we are in a segv'd state. do as few function calls and things
// depending on a known working state as possible. this also prevents the
// white box allowing recovery or deeper gdbing, thus until this works
// properly, it's disabled (properly means always reliable, always
// printf bt and allows e to continue and pop up box, perferably allowing
// debugging in the gui etc. etc.
#if 0
char cmd[1024];
size_t size;
int ret;
if (getenv("E_NO_GDB_BACKTRACE"))
return;
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");
ret = system(cmd); // TODO: use popen() or fork()+pipe()+exec() and save to 'fd'
#endif
}
#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;
return; // disable. causes hangs and problems
_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);
}
/* a tricky little devil, requires e and it's libs to be built
* with the -rdynamic flag to GCC for any sort of decent output.
*/
EAPI void
e_sigseg_act(int x __UNUSED__, siginfo_t *info __UNUSED__, void *data __UNUSED__)
{
_e_backtrace("**** SEGMENTATION FAULT ****");
_e_x_composite_shutdown();
ecore_x_pointer_ungrab();
ecore_x_keyboard_ungrab();
ecore_x_ungrab();
ecore_x_sync();
e_alert_show(SIGSEGV);
e_alert_show();
}
EAPI void
e_sigill_act(int x __UNUSED__, siginfo_t *info __UNUSED__, void *data __UNUSED__)
{
_e_backtrace("**** ILLEGAL INSTRUCTION ****");
_e_x_composite_shutdown();
ecore_x_pointer_ungrab();
ecore_x_keyboard_ungrab();
ecore_x_ungrab();
ecore_x_sync();
e_alert_show(SIGILL);
e_alert_show();
}
EAPI void
e_sigfpe_act(int x __UNUSED__, siginfo_t *info __UNUSED__, void *data __UNUSED__)
{
_e_backtrace("**** FLOATING POINT EXCEPTION ****");
_e_x_composite_shutdown();
ecore_x_pointer_ungrab();
ecore_x_keyboard_ungrab();
ecore_x_ungrab();
ecore_x_sync();
e_alert_show(SIGFPE);
e_alert_show();
}
EAPI void
e_sigbus_act(int x __UNUSED__, siginfo_t *info __UNUSED__, void *data __UNUSED__)
{
_e_backtrace("**** BUS ERROR ****");
_e_x_composite_shutdown();
ecore_x_pointer_ungrab();
ecore_x_keyboard_ungrab();
ecore_x_ungrab();
ecore_x_sync();
e_alert_show(SIGBUS);
e_alert_show();
}
EAPI void
e_sigabrt_act(int x __UNUSED__, siginfo_t *info __UNUSED__, void *data __UNUSED__)
{
_e_backtrace("**** ABORT ****");
_e_x_composite_shutdown();
ecore_x_pointer_ungrab();
ecore_x_keyboard_ungrab();
ecore_x_ungrab();
ecore_x_sync();
e_alert_show(SIGABRT);
e_alert_show();
}

View File

@ -8,13 +8,18 @@
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <sys/ptrace.h>
#include <limits.h>
#include <fcntl.h>
#ifdef HAVE_ALLOCA_H
# include <alloca.h>
#endif
#include <signal.h>
#include <Eina.h>
static Eina_Bool tainted = EINA_FALSE;
static void env_set(const char *var, const char *val);
EAPI int prefix_determine(char *argv0);
@ -217,12 +222,28 @@ main(int argc, char **argv)
int i, valgrind_mode = 0;
int valgrind_tool = 0;
int valgrind_gdbserver = 0;
char buf[16384], **args, *p;
char buf[16384], **args, *home;
char valgrind_path[PATH_MAX] = "";
const char *valgrind_log = NULL;
Eina_Bool really_know = EINA_FALSE;
Eina_Bool restart = EINA_TRUE;
eina_init();
/* reexcute myself with dbus-launch if dbus-launch is not running yet */
if ((!getenv("DBUS_SESSION_BUS_ADDRESS")) &&
(!getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET")))
{
char **dbus_argv;
dbus_argv = alloca(argc + 3 + sizeof (char *));
dbus_argv[0] = "dbus-launch";
dbus_argv[1] = "--exit-with-session";
copy_args(dbus_argv + 2, argv, argc);
dbus_argv[2 + argc] = NULL;
execvp("dbus-launch", dbus_argv);
}
prefix_determine(argv[0]);
env_set("E_START", argv[0]);
@ -254,6 +275,11 @@ main(int argc, char **argv)
else
printf("Unknown valgrind option: %s\n", argv[i]);
}
else if (!strcmp(argv[i], "-display"))
{
i++;
env_set("DISPLAY", argv[i]);
}
else if (!strcmp(argv[i], "-massif"))
valgrind_tool = 1;
else if (!strcmp(argv[i], "-callgrind"))
@ -312,15 +338,15 @@ main(int argc, char **argv)
putchar('\n');
/* mtrack memory tracker support */
p = getenv("HOME");
if (p)
home = getenv("HOME");
if (home)
{
FILE *f;
/* if you have ~/.e-mtrack, then the tracker will be enabled
* using the content of this file as the path to the mtrack.so
* shared object that is the mtrack preload */
snprintf(buf, sizeof(buf), "%s/.e-mtrack", p);
snprintf(buf, sizeof(buf), "%s/.e-mtrack", home);
f = fopen(buf, "r");
if (f)
{
@ -335,40 +361,143 @@ main(int argc, char **argv)
env_set("LD_PRELOAD", buf);
env_set("MTRACK", "track");
env_set("E_START_MTRACK", "track");
snprintf(buf, sizeof(buf), "%s/.e-mtrack.log", p);
snprintf(buf, sizeof(buf), "%s/.e-mtrack.log", home);
env_set("MTRACK_TRACE_FILE", buf);
}
fclose(f);
}
}
/* try dbus-launch */
/* run e directly now */
snprintf(buf, sizeof(buf), "%s/enlightenment", eina_prefix_bin_get(pfx));
args = alloca((argc + 2 + VALGRIND_MAX_ARGS) * sizeof(char *));
if ((!getenv("DBUS_SESSION_BUS_ADDRESS")) &&
(!getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET")))
{
args[0] = "dbus-launch";
args[1] = "--exit-with-session";
i = 2 + valgrind_append(args + 2, valgrind_gdbserver, valgrind_mode, valgrind_tool, 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 */
i = valgrind_append(args, valgrind_gdbserver, valgrind_mode, valgrind_tool, valgrind_path, valgrind_log);
args[i++] = buf;
copy_args(args + i, argv + 1, argc - 1);
args[i + argc - 1] = NULL;
execv(args[0], args);
/* execv(args[0], args); */
/* not run at the moment !! */
/* Now looping until */
while (restart)
{
pid_t child;
tainted = EINA_FALSE;
child = fork();
if (child < 0) /* failed attempt */
return -1;
else if (child == 0)
{
/* in the child */
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execv(args[0], args);
return 0; /* We failed, 0 mean normal exit from E with no restart or crash so let exit */
}
else
{
/* in the parent */
pid_t result;
int status;
Eina_Bool done = EINA_FALSE;
ptrace(PTRACE_ATTACH, child, NULL, NULL);
result = waitpid(child, &status, 0);
if (WIFSTOPPED(status))
ptrace(PTRACE_CONT, child, NULL, NULL);
while (!done)
{
result = waitpid(child, &status, 0);
if (result == child)
{
if (WIFSTOPPED(status))
{
char buffer[4096];
char *backtrace = NULL;
siginfo_t sig;
int r;
int back;
r = ptrace(PTRACE_GETSIGINFO, child, NULL, &sig);
back = r == 0 &&
sig.si_signo != SIGTRAP ? sig.si_signo : 0;
if (r != 0 ||
(sig.si_signo != SIGSEGV &&
sig.si_signo != SIGILL &&
sig.si_signo != SIGFPE &&
sig.si_signo != SIGBUS &&
sig.si_signo != SIGABRT))
{
ptrace(PTRACE_CONT, child, NULL, back);
continue ;
}
/* E17 should be in pause, we can detach */
ptrace(PTRACE_DETACH, child, NULL, back);
/* And call gdb if available */
if (home)
{
/* call e_sys gdb */
snprintf(buffer, 4096,
"%s/enlightenment/utils/enlightenment_sys gdb %i %s/.xsession-errors",
eina_prefix_lib_get(pfx),
child,
home);
r = system(buffer);
fprintf(stderr, "called gdb with '%s' = %i\n",
buffer, WEXITSTATUS(r));
snprintf(buffer, 4096,
"%s/.xsession-errors",
home);
backtrace = strdup(buffer);
}
/* call e_alert */
snprintf(buffer, 4096,
backtrace ? "%s/enlightenment/utils/enlightenment_alert %i %i %s" : "%s/enlightenment/utils/enlightenment_alert %i %i %s",
eina_prefix_lib_get(pfx),
sig.si_signo,
child,
backtrace);
r = system(buffer);
/* kill e */
kill(child, SIGKILL);
if (WEXITSTATUS(r) != 1)
{
restart = EINA_FALSE;
}
}
else if (!WIFEXITED(status))
{
done = EINA_TRUE;
}
}
else if (result == - 1)
{
done = EINA_TRUE;
restart = EINA_FALSE;
}
}
}
}
printf("FAILED TO RUN:\n");
printf(" %s\n", buf);
perror("execv");
return -1;
}