diff --git a/CMakeLists.txt b/CMakeLists.txt index 752b13a..0af5296 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ endif( CMAKE_SYSTEM_NAME STREQUAL "Linux" ) include( FindPkgConfig ) include( CheckLibraryExists ) +include( CheckCSourceCompiles ) pkg_check_modules( X11 REQUIRED x11 xcomposite xdamage xinerama xrender xrandr>=1.3 ) pkg_check_modules( PANGOCAIRO REQUIRED pangocairo ) pkg_check_modules( PANGO REQUIRED pango ) @@ -21,6 +22,19 @@ pkg_check_modules( GLIB2 REQUIRED glib-2.0 ) pkg_check_modules( GOBJECT2 REQUIRED gobject-2.0 ) pkg_check_modules( IMLIB2 REQUIRED imlib2>=1.4.2 ) +check_c_source_compiles( + "#include \n#include \nint main () { backtrace(NULL, 0); }" + BACKTRACE_LIBC) + +if(BACKTRACE_LIBC) + set(BACKTRACE_LIBC_FOUND TRUE) + set(BACKTRACE_L_FLAGS "-rdynamic") +else() + pkg_check_modules( UNWIND libunwind ) + pkg_check_modules( EXECINFO execinfo ) + set(BACKTRACE_L_FLAGS "") +endif() + if( ENABLE_RSVG ) pkg_check_modules( RSVG librsvg-2.0>=2.14.0 ) endif( ENABLE_RSVG ) @@ -129,6 +143,18 @@ if( ENABLE_UEVENT ) set( SOURCES ${SOURCES} src/util/uevent.c) endif( ENABLE_UEVENT ) +if(BACKTRACE_LIBC_FOUND) + add_definitions( -DENABLE_EXECINFO ) +endif() + +if( UNWIND_FOUND ) + add_definitions( -DENABLE_LIBUNWIND ) +endif( UNWIND_FOUND ) + +if( EXECINFO_FOUND ) + add_definitions( -DENABLE_EXECINFO ) +endif( EXECINFO_FOUND ) + if( ENABLE_TINT2CONF ) add_definitions( -DHAVE_VERSION_H ) add_subdirectory( src/tint2conf ) @@ -166,7 +192,9 @@ target_link_libraries( tint2 ${X11_LIBRARIES} ${CAIRO_LIBRARIES} ${GLIB2_LIBRARIES} ${GOBJECT2_LIBRARIES} - ${IMLIB2_LIBRARIES} ) + ${IMLIB2_LIBRARIES} + ${UNWIND_LIBRARIES} + ${EXECINFO_LIBRARIES} ) if( ENABLE_RSVG ) target_link_libraries( tint2 ${RSVG_LIBRARIES} ) endif( ENABLE_RSVG ) @@ -181,7 +209,7 @@ target_link_libraries( tint2 m ) add_dependencies( tint2 version ) set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=c99 ${ASAN_C_FLAGS}" ) -set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS}" ) +set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS} ${BACKTRACE_L_FLAGS}" ) install( TARGETS tint2 DESTINATION bin ) install( FILES tint2.svg DESTINATION ${DATADIR}/icons/hicolor/scalable/apps ) diff --git a/src/tint.c b/src/tint.c index c7aab58..b8be492 100644 --- a/src/tint.c +++ b/src/tint.c @@ -51,6 +51,15 @@ #include "xsettings-client.h" #include "uevent.h" +#ifdef ENABLE_LIBUNWIND +#define UNW_LOCAL_ONLY +#include +#else +#ifdef ENABLE_EXECINFO +#include +#endif +#endif + // Drag and Drop state variables Window dnd_source_window; Window dnd_target_window; @@ -104,6 +113,98 @@ void signal_handler(int sig) signal_pending = sig; } +void write_string(int fd, const char *s) +{ + int len = strlen(s); + while (len > 0) { + int count = write(fd, s, len); + if (count >= 0) { + s += count; + len -= count; + } + } +} + +const char *signal_name(int sig) +{ + switch (sig) { + case SIGHUP: return "SIGHUP: Hangup (POSIX)."; + case SIGINT: return "SIGINT: Interrupt (ANSI)."; + case SIGQUIT: return "SIGQUIT: Quit (POSIX)."; + case SIGILL: return "SIGILL: Illegal instruction (ANSI)."; + case SIGTRAP: return "SIGTRAP: Trace trap (POSIX)."; + case SIGABRT: return "SIGABRT/SIGIOT: Abort (ANSI) / IOT trap (4.2 BSD)."; + case SIGBUS: return "SIGBUS: BUS error (4.2 BSD)."; + case SIGFPE: return "SIGFPE: Floating-point exception (ANSI)."; + case SIGKILL: return "SIGKILL: Kill, unblockable (POSIX)."; + case SIGUSR1: return "SIGUSR1: User-defined signal 1 (POSIX)."; + case SIGSEGV: return "SIGSEGV: Segmentation violation (ANSI)."; + case SIGUSR2: return "SIGUSR2: User-defined signal 2 (POSIX)."; + case SIGPIPE: return "SIGPIPE: Broken pipe (POSIX)."; + case SIGALRM: return "SIGALRM: Alarm clock (POSIX)."; + case SIGTERM: return "SIGTERM: Termination (ANSI)."; + case SIGSTKFLT: return "SIGSTKFLT: Stack fault."; + case SIGCHLD: return "SIGCHLD: Child status has changed (POSIX)."; + case SIGCONT: return "SIGCONT: Continue (POSIX)."; + case SIGSTOP: return "SIGSTOP: Stop, unblockable (POSIX)."; + case SIGTSTP: return "SIGTSTP: Keyboard stop (POSIX)."; + case SIGTTIN: return "SIGTTIN: Background read from tty (POSIX)."; + case SIGTTOU: return "SIGTTOU: Background write to tty (POSIX)."; + case SIGURG: return "SIGURG: Urgent condition on socket (4.2 BSD)."; + case SIGXCPU: return "SIGXCPU: CPU limit exceeded (4.2 BSD)."; + case SIGXFSZ: return "SIGXFSZ: File size limit exceeded (4.2 BSD)."; + case SIGVTALRM: return "SIGVTALRM: Virtual alarm clock (4.2 BSD)."; + case SIGPROF: return "SIGPROF: Profiling alarm clock (4.2 BSD)."; + case SIGWINCH: return "SIGWINCH: Window size change (4.3 BSD, Sun)."; + case SIGIO: return "SIGIO: Pollable event occurred (System V) / I/O now possible (4.2 BSD)."; + case SIGPWR: return "SIGPWR: Power failure restart (System V)."; + case SIGSYS: return "SIGSYS: Bad system call."; + } + static char s[64]; + sprintf(s, "SIG=%d: Unknown", sig); + return s; +} + +void crash_handler(int sig) +{ + write_string(2, "Crashing with signal "); + write_string(2, signal_name(sig)); + write_string(2, "\nBacktrace:\n"); + +#ifdef ENABLE_LIBUNWIND + unw_cursor_t cursor; + unw_context_t context; + unw_getcontext(&context); + unw_init_local(&cursor, &context); + + while (unw_step(&cursor) > 0) { + unw_word_t offset; + char fname[128]; + fname[0] = '\0'; + (void) unw_get_proc_name(&cursor, fname, sizeof(fname), &offset); + write_string(2, fname); + write_string(2, "\n"); + } +#else +#ifdef ENABLE_EXECINFO +#define MAX_TRACE_SIZE 128 + void *array[MAX_TRACE_SIZE]; + size_t size = backtrace(array, MAX_TRACE_SIZE); + char **strings = backtrace_symbols(array, size); + + for (size_t i = 0; i < size; i++) { + write_string(2, strings[i]); + write_string(2, "\n"); + } + + free(strings); +#else + write_string(2, "Backtrace not supported on this system. Install libunwind or libexecinfo.\n"); +#endif +#endif + _exit(sig); +} + void init(int argc, char *argv[]) { // Make stdout/stderr flush after a newline (for some reason they don't even if tint2 is started from a terminal) @@ -162,11 +263,18 @@ void init(int argc, char *argv[]) signal_pending = 0; struct sigaction sa = {.sa_handler = signal_handler}; struct sigaction sa_chld = {.sa_handler = SIG_DFL, .sa_flags = SA_NOCLDWAIT}; + struct sigaction sa_crash = {.sa_handler = crash_handler}; sigaction(SIGUSR1, &sa, 0); sigaction(SIGINT, &sa, 0); sigaction(SIGTERM, &sa, 0); sigaction(SIGHUP, &sa, 0); sigaction(SIGCHLD, &sa_chld, 0); + sigaction(SIGSEGV, &sa_crash, 0); + sigaction(SIGFPE, &sa_crash, 0); + sigaction(SIGPIPE, &sa_crash, 0); + sigaction(SIGBUS, &sa_crash, 0); + sigaction(SIGABRT, &sa_crash, 0); + sigaction(SIGSYS, &sa_crash, 0); } static int sn_pipe_valid = 0;