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
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
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]
}
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]} {
--- /dev/null
+/**
+ * @file
+ * Code backtrace
+ *
+ * @authors
+ * Copyright (C) 2018-2019 Richard Russon <rich@flatcap.org>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @page backtrace Code backtrace
+ *
+ * Code backtrace
+ */
+
+#include "config.h"
+#include <libunwind.h>
+#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");
+}
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 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#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;
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
* 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;
if (exit_fn)
exit_handler = exit_fn;
+ if (segv_fn)
+ segv_handler = segv_fn;
+
struct sigaction act;
sigemptyset(&act.sa_mask);
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);
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);
/**
* 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)
{
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
*/
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
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)");
}
}