From: Richard Russon Date: Mon, 3 Jun 2019 12:00:17 +0000 (+0100) Subject: show backtrace on segfault X-Git-Tag: 2019-10-25~173 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=96af407ca6fb2c6eb1fb6c36d9346d6a0f9a1391;p=neomutt show backtrace on segfault Add a configure option `--backtrace` that uses libunwind to print a basic backtrace if NeoMutt segfaults. Co-authored-by: Pietro Cerutti --- diff --git a/Makefile.autosetup b/Makefile.autosetup index 3db9275d7..ade09bf5a 100644 --- a/Makefile.autosetup +++ b/Makefile.autosetup @@ -73,6 +73,9 @@ NEOMUTTOBJS= account.o addrbook.o alias.o bcache.o browser.o color.o commands.o recvcmd.o resize.o rfc1524.o rfc3676.o \ score.o send.o sendlib.o sidebar.o smtp.o sort.o state.o \ status.o system.o terminal.o version.o icommands.o +@if HAVE_LIBUNWIND +NEOMUTTOBJS+= backtrace.o +@endif @if !HAVE_WCSCASECMP NEOMUTTOBJS+= wcscasecmp.o diff --git a/auto.def b/auto.def index 5988a9571..50778650f 100644 --- a/auto.def +++ b/auto.def @@ -87,6 +87,9 @@ options { with-qdbm:path => "Location of QDBM" tokyocabinet=0 => "Use TokyoCabinet for the header cache" with-tokyocabinet:path => "Location of TokyoCabinet" +# libunwind + backtrace=0 => "Enable backtrace support with libunwind" + with-backtrace:path => "Location of libunwind" # System with-sysroot:path => "Target system root" # Testing @@ -104,9 +107,9 @@ options { if {1} { # Keep sorted, please. foreach opt { - bdb coverage doc everything fmemopen full-doc gdbm gnutls gpgme gss - homespool idn idn2 inotify kyotocabinet lmdb locales-fix lua mixmaster nls - notmuch pgp qdbm sasl smime ssl testing tokyocabinet + bdb backtrace coverage doc everything fmemopen full-doc gdbm gnutls gpgme + gss homespool idn idn2 inotify kyotocabinet lmdb locales-fix lua mixmaster + nls notmuch pgp qdbm sasl smime ssl testing tokyocabinet } { define want-$opt [opt-bool $opt] } @@ -634,6 +637,16 @@ if {[get-define want-homespool]} { define MAILPATH [opt-val with-mailpath /var/mail] } +############################################################################### +# Backtrace support with libunwind +if {[get-define want-backtrace]} { + if {![check-inc-and-lib libunwind [opt-val with-backtrace $prefix] \ + libunwind.h unw_backtrace unwind]} { + user-error "Unable to find libunwind" + } + define LIBS "-lunwind-generic [get-define LIBS]" +} + ############################################################################### # Mixmaster if {[get-define want-mixmaster]} { diff --git a/backtrace.c b/backtrace.c new file mode 100644 index 000000000..c950be337 --- /dev/null +++ b/backtrace.c @@ -0,0 +1,58 @@ +/** + * @file + * Code backtrace + * + * @authors + * Copyright (C) 2018-2019 Richard Russon + * + * @copyright + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * @page backtrace Code backtrace + * + * Code backtrace + */ + +#include "config.h" +#include +#include "mutt/mutt.h" + +/** + * show_backtrace - Log the program's call stack + */ +void show_backtrace(void) +{ + unw_cursor_t cursor; + unw_context_t uc; + unw_word_t ip, sp; + char buf[256]; + + printf("\nBacktrace\n"); + mutt_debug(LL_DEBUG1, "\nBacktrace\n"); + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + while (unw_step(&cursor) > 0) + { + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + unw_get_proc_name(&cursor, buf, sizeof(buf), &ip); + if (buf[0] == '_') + break; + printf("\t%s() ip = %lx, sp = %lx\n", buf, (long) ip, (long) sp); + mutt_debug(LL_DEBUG1, "\t%s() ip = %lx, sp = %lx\n", buf, (long) ip, (long) sp); + } + printf("\n"); +} diff --git a/mutt.h b/mutt.h index 0e6b1c809..f2c833634 100644 --- a/mutt.h +++ b/mutt.h @@ -153,4 +153,8 @@ enum CommandResult mutt_parse_rc_line(/* const */ char *line, struct Buffer *tok int mutt_query_variables(struct ListHead *queries); void reset_value(const char *name); +#ifdef HAVE_LIBUNWIND +void show_backtrace(void); +#endif + #endif /* MUTT_MUTT_H */ diff --git a/mutt/signal.c b/mutt/signal.c index afad44484..f7bdf38b3 100644 --- a/mutt/signal.c +++ b/mutt/signal.c @@ -33,8 +33,12 @@ #include #include #include +#include "curs_lib.h" #include "message.h" #include "signal2.h" +#ifdef HAVE_LIBUNWIND +#include "mutt.h" +#endif static sigset_t Sigset; static sigset_t SigsetSys; @@ -45,6 +49,7 @@ static bool SysSignalsBlocked; static sig_handler_t sig_handler = mutt_sig_empty_handler; static sig_handler_t exit_handler = mutt_sig_exit_handler; +static sig_handler_t segv_handler = mutt_sig_exit_handler; /** * mutt_sig_empty_handler - Dummy signal handler @@ -79,11 +84,12 @@ void mutt_sig_exit_handler(int sig) * mutt_sig_init - Initialise the signal handling * @param sig_fn Function to handle signals * @param exit_fn Function to call on uncaught signals + * @param segv_fn Function to call on a segfault (Segmentation Violation) * * Set up handlers to ignore or catch signals of interest. * We use three handlers for the signals we want to catch, ignore, or exit. */ -void mutt_sig_init(sig_handler_t sig_fn, sig_handler_t exit_fn) +void mutt_sig_init(sig_handler_t sig_fn, sig_handler_t exit_fn, sig_handler_t segv_fn) { if (sig_fn) sig_handler = sig_fn; @@ -91,6 +97,9 @@ void mutt_sig_init(sig_handler_t sig_fn, sig_handler_t exit_fn) if (exit_fn) exit_handler = exit_fn; + if (segv_fn) + segv_handler = segv_fn; + struct sigaction act; sigemptyset(&act.sa_mask); @@ -98,6 +107,9 @@ void mutt_sig_init(sig_handler_t sig_fn, sig_handler_t exit_fn) act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); + act.sa_handler = segv_handler; + sigaction(SIGSEGV, &act, NULL); + act.sa_handler = exit_handler; sigaction(SIGTERM, &act, NULL); sigaction(SIGHUP, &act, NULL); diff --git a/mutt/signal2.h b/mutt/signal2.h index bb56deff6..f6ddeec3a 100644 --- a/mutt/signal2.h +++ b/mutt/signal2.h @@ -36,7 +36,7 @@ void mutt_sig_block(void); void mutt_sig_block_system(void); void mutt_sig_empty_handler(int sig); void mutt_sig_exit_handler(int sig); -void mutt_sig_init(sig_handler_t sig_fn, sig_handler_t exit_fn); +void mutt_sig_init(sig_handler_t sig_fn, sig_handler_t exit_fn, sig_handler_t segv_fn); void mutt_sig_unblock(void); void mutt_sig_unblock_system(bool catch); diff --git a/mutt_signal.c b/mutt_signal.c index 3f24dbc82..f5109ab71 100644 --- a/mutt_signal.c +++ b/mutt_signal.c @@ -78,7 +78,7 @@ static void curses_signal_handler(int sig) /** * curses_exit_handler - Notify the user and shutdown gracefully - * @param sig Signal number, e.g. SIGINT + * @param sig Signal number, e.g. SIGTERM */ static void curses_exit_handler(int sig) { @@ -88,6 +88,27 @@ static void curses_exit_handler(int sig) mutt_sig_exit_handler(sig); /* DOES NOT RETURN */ } +/** + * curses_segv_handler - Catch a segfault and print a backtrace + * @param sig Signal number, e.g. SIGSEGV + */ +static void curses_segv_handler(int sig) +{ + curs_set(1); + endwin(); /* just to be safe */ +#ifdef HAVE_LIBUNWIND + show_backtrace(); +#endif + + struct sigaction act; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = SIG_DFL; + sigaction(sig, &act, NULL); + // Re-raise the signal to give outside handlers a chance to deal with it + raise(sig); +} + #ifdef USE_SLANG_CURSES /** * mutt_intr_hook - Workaround handler for slang @@ -104,7 +125,7 @@ static int mutt_intr_hook(void) */ void mutt_signal_init(void) { - mutt_sig_init(curses_signal_handler, curses_exit_handler); + mutt_sig_init(curses_signal_handler, curses_exit_handler, curses_segv_handler); #ifdef USE_SLANG_CURSES /* This bit of code is required because of the implementation of diff --git a/test/signal/mutt_sig_init.c b/test/signal/mutt_sig_init.c index c8882eaaa..4d290ffcc 100644 --- a/test/signal/mutt_sig_init.c +++ b/test/signal/mutt_sig_init.c @@ -27,17 +27,26 @@ void test_mutt_sig_init(void) { - // void mutt_sig_init(sig_handler_t sig_fn, sig_handler_t exit_fn); + // void mutt_sig_init(sig_handler_t sig_fn, sig_handler_t exit_fn, sig_handler_t segv_fn) { sig_handler_t exit_fn = mutt_sig_empty_handler; - mutt_sig_init(NULL, exit_fn); - TEST_CHECK_(1, "mutt_sig_init(NULL, exit_fn)"); + sig_handler_t segv_fn = mutt_sig_empty_handler; + mutt_sig_init(NULL, exit_fn, segv_fn); + TEST_CHECK_(1, "mutt_sig_init(NULL, exit_fn, segv_fn)"); } { sig_handler_t sig_fn = mutt_sig_empty_handler; - mutt_sig_init(sig_fn, NULL); - TEST_CHECK_(1, "mutt_sig_init(sig_fn, NULL)"); + sig_handler_t segv_fn = mutt_sig_empty_handler; + mutt_sig_init(sig_fn, NULL, segv_fn); + TEST_CHECK_(1, "mutt_sig_init(sig_fn, NULL, segv_fn)"); + } + + { + sig_handler_t sig_fn = mutt_sig_empty_handler; + sig_handler_t exit_fn = mutt_sig_empty_handler; + mutt_sig_init(sig_fn, exit_fn, NULL); + TEST_CHECK_(1, "mutt_sig_init(sig_fn, exit_fn, NULL)"); } }