ACK: [PATCH] fwts: catch segfaults and bus errors and dump stack
ivanhu
ivan.hu at canonical.com
Fri Dec 5 08:47:29 UTC 2014
On 2014年11月25日 22:19, Colin King wrote:
> From: Colin Ian King <colin.king at canonical.com>
>
> Bugs such as LP#1376448 have been hard to track down because
> the target machine is not accessible and the user is not easily
> able to pass over core dump information. In an attempt to at
> least identify where a fault is occurring, this patch adds in
> a SIGSEGV and SIGBUS handler that will dump to stderr a stack
> trace.
>
> This patch also re-works some of signal() handlers in fwts to
> ensure we save and restore the dump stack handler in a more
> generic way for fwts. This also tidies up the old fashioned
> signal() handler with the more modern signal handling mechanisms.
>
> Signed-off-by: Colin Ian King <colin.king at canonical.com>
> ---
> src/lib/include/fwts.h | 1 +
> src/lib/include/fwts_backtrace.h | 29 ++++++++
> src/lib/src/Makefile.am | 1 +
> src/lib/src/fwts_backtrace.c | 145 +++++++++++++++++++++++++++++++++++++++
> src/lib/src/fwts_cpu.c | 5 +-
> src/lib/src/fwts_ioport.c | 27 ++++----
> src/lib/src/fwts_safe_mem.c | 7 +-
> src/main.c | 2 +
> 8 files changed, 198 insertions(+), 19 deletions(-)
> create mode 100644 src/lib/include/fwts_backtrace.h
> create mode 100644 src/lib/src/fwts_backtrace.c
>
> diff --git a/src/lib/include/fwts.h b/src/lib/include/fwts.h
> index d98932b..d5fc231 100644
> --- a/src/lib/include/fwts.h
> +++ b/src/lib/include/fwts.h
> @@ -35,6 +35,7 @@
> #define FWTS_JSON_DATA_PATH DATAROOTDIR "/fwts"
>
> #include "fwts_version.h"
> +#include "fwts_backtrace.h"
> #include "fwts_types.h"
> #include "fwts_binpaths.h"
> #include "fwts_framework.h"
> diff --git a/src/lib/include/fwts_backtrace.h b/src/lib/include/fwts_backtrace.h
> new file mode 100644
> index 0000000..e0b0dde
> --- /dev/null
> +++ b/src/lib/include/fwts_backtrace.h
> @@ -0,0 +1,29 @@
> +/*
> + * Copyright (C) 2010-2014 Canonical
> + *
> + * 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, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + */
> +#ifndef __FWTS_BACKTRACE__
> +#define __FWTS_BACKTRACE__
> +
> +#include <signal.h>
> +
> +void fwts_print_backtrace(void);
> +int fwts_fault_catch(void);
> +void fwts_sig_handler_set(int signum, void (*handler)(int), struct sigaction *old_action);
> +void fwts_sig_handler_restore(int signum, struct sigaction *old_action);
> +
> +#endif
> diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am
> index 33f81f4..2f0bbcc 100644
> --- a/src/lib/src/Makefile.am
> +++ b/src/lib/src/Makefile.am
> @@ -29,6 +29,7 @@ libfwts_la_SOURCES = \
> fwts_acpid.c \
> fwts_alloc.c \
> fwts_args.c \
> + fwts_backtrace.c \
> fwts_battery.c \
> fwts_binpaths.c \
> fwts_button.c \
> diff --git a/src/lib/src/fwts_backtrace.c b/src/lib/src/fwts_backtrace.c
> new file mode 100644
> index 0000000..eb84b48
> --- /dev/null
> +++ b/src/lib/src/fwts_backtrace.c
> @@ -0,0 +1,145 @@
> +/*
> + * Copyright (C) 2010-2014 Canonical
> + *
> + * 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, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + */
> +#include <execinfo.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <inttypes.h>
> +#include <string.h>
> +#include <signal.h>
> +#include <setjmp.h>
> +
> +#include "fwts.h"
> +
> +#define BACK_TRACE_SIZE (512)
> +
> +static sigjmp_buf jmp_env;
> +static void *bt_buff[BACK_TRACE_SIZE];
> +static size_t bt_size = 0;
> +
> +/*
> + * fwts_print_backtrace()
> + * parse symbol backtrace and dump it in a easy
> + * to read format.
> + */
> +void fwts_print_backtrace(void)
> +{
> + char **bt_strings;
> + size_t i;
> +
> + fprintf(stderr, "Backtrace:\n");
> + if (bt_size) {
> + bt_strings = backtrace_symbols(bt_buff, bt_size);
> +
> + for (i = 0; i < bt_size; i++) {
> + /*
> + * convert trace into output in form:
> + * 0x00007fb04a493ec5 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)
> + */
> + char *addrstr = strstr(bt_strings[i], " [0x");
> + if (addrstr) {
> + uint64_t addr;
> +
> + *addrstr = '\0';
> + addr = (uint64_t)strtoull(addrstr + 2, NULL, 16);
> + fprintf(stderr, "0x%16.16" PRIx64 " %s\n", addr, bt_strings[i]);
> + }
> + }
> + free(bt_strings);
> + } else {
> + fprintf(stderr, " No data\n");
> + }
> + fprintf(stderr, "\n");
> + fflush(stdout);
> +}
> +
> +/*
> + * fwts_fault_handler()
> + * catch a signal, save stack dump, jmp back or abort
> + */
> +static void fwts_fault_handler(int signum)
> +{
> + static bool already_handled = false;
> +
> + /* Capture backtrace and jmp back */
> +
> + if (!already_handled) {
> + already_handled = true;
> + /* Capture backtrace from this stack context */
> + bt_size = backtrace(bt_buff, BACK_TRACE_SIZE);
> + /* Jmp back to the sigsetjmp context */
> + siglongjmp(jmp_env, signum);
> + }
> + /* We've hit a fault before, so abort */
> + _exit(EXIT_FAILURE);
> +}
> +
> +/*
> + * fwts_sig_handler_set()
> + * helper to set signal handler
> + */
> +void fwts_sig_handler_set(int signum, void (*handler)(int), struct sigaction *old_action)
> +{
> + struct sigaction new_action;
> +
> + memset(&new_action, 0, sizeof new_action);
> + new_action.sa_handler = handler;
> + sigemptyset(&new_action.sa_mask);
> +
> + (void)sigaction(signum, &new_action, old_action);
> +}
> +
> +/*
> + * fwts_sig_handler_restore()
> + * helper to restore signal handler
> + */
> +void fwts_sig_handler_restore(int signum, struct sigaction *old_action)
> +{
> + (void)sigaction(signum, old_action, NULL);
> +}
> +
> +/*
> + * fwts_fault_catch()
> + * catch segfaults and bus errors, dump stack
> + * trace so we can see what's causing them
> + */
> +int fwts_fault_catch(void)
> +{
> + int ret;
> +
> + /* Trap segfaults and bus errors */
> + fwts_sig_handler_set(SIGSEGV, fwts_fault_handler, NULL);
> + fwts_sig_handler_set(SIGBUS, fwts_fault_handler, NULL);
> +
> + ret = sigsetjmp(jmp_env, 1);
> + /*
> + * We reach here with ret == SIGNUM if the fault handler
> + * longjmps back to here. Or we reach here with
> + * ret == 0 if sigsetjmp has set the jmp_env up
> + * correctly.
> + */
> + if (ret) {
> + fprintf(stderr, "\nCaught SIGNAL %d (%s), aborting.\n",
> + ret, strsignal(ret));
> + fwts_print_backtrace();
> + exit(EXIT_FAILURE);
> + }
> + return FWTS_OK;
> +}
> diff --git a/src/lib/src/fwts_cpu.c b/src/lib/src/fwts_cpu.c
> index 9cbbdef..fe78ee5 100644
> --- a/src/lib/src/fwts_cpu.c
> +++ b/src/lib/src/fwts_cpu.c
> @@ -377,7 +377,7 @@ int fwts_cpu_performance(
> */
> static void fwts_cpu_consume_cycles(void)
> {
> - signal(SIGUSR1, fwts_cpu_consume_sighandler);
> + fwts_sig_handler_set(SIGUSR1, fwts_cpu_consume_sighandler, NULL);
> uint64_t i = 0;
>
> for (;;) {
> @@ -410,8 +410,7 @@ int fwts_cpu_consume_start(void)
> if ((fwts_cpu_pids = (pid_t*)calloc(fwts_cpu_num, sizeof(pid_t))) == NULL)
> return FWTS_ERROR;
>
> - signal(SIGINT, fwts_cpu_sigint_handler);
> -
> + fwts_sig_handler_set(SIGINT, fwts_cpu_sigint_handler, NULL);
> for (i=0;i<fwts_cpu_num;i++) {
> pid_t pid;
>
> diff --git a/src/lib/src/fwts_ioport.c b/src/lib/src/fwts_ioport.c
> index e433995..d0d661a 100644
> --- a/src/lib/src/fwts_ioport.c
> +++ b/src/lib/src/fwts_ioport.c
> @@ -27,6 +27,7 @@
> #include <setjmp.h>
>
> static sigjmp_buf jmpbuf;
> +static struct sigaction old_action;
>
> /*
> * If we hit a SIGSEGV then the port read
> @@ -37,7 +38,7 @@ static void segv_handler(int dummy)
> {
> FWTS_UNUSED(dummy);
>
> - signal(SIGSEGV, SIG_DFL);
> + fwts_sig_handler_restore(SIGSEGV, &old_action);
> siglongjmp(jmpbuf, 1);
> }
>
> @@ -50,9 +51,9 @@ int fwts_inb(uint32_t port, uint8_t *value)
> if (sigsetjmp(jmpbuf, 1) != 0)
> return FWTS_ERROR;
>
> - signal(SIGSEGV, segv_handler);
> + fwts_sig_handler_set(SIGSEGV, segv_handler, &old_action);
> *value = inb(port);
> - signal(SIGSEGV, SIG_DFL);
> + fwts_sig_handler_restore(SIGSEGV, &old_action);
>
> return FWTS_OK;
> }
> @@ -66,9 +67,9 @@ int fwts_inw(uint32_t port, uint16_t *value)
> if (sigsetjmp(jmpbuf, 1) != 0)
> return FWTS_ERROR;
>
> - signal(SIGSEGV, segv_handler);
> + fwts_sig_handler_set(SIGSEGV, segv_handler, &old_action);
> *value = inw(port);
> - signal(SIGSEGV, SIG_DFL);
> + fwts_sig_handler_restore(SIGSEGV, &old_action);
>
> return FWTS_OK;
> }
> @@ -82,9 +83,9 @@ int fwts_inl(uint32_t port, uint32_t *value)
> if (sigsetjmp(jmpbuf, 1) != 0)
> return FWTS_ERROR;
>
> - signal(SIGSEGV, segv_handler);
> + fwts_sig_handler_set(SIGSEGV, segv_handler, &old_action);
> *value = inl(port);
> - signal(SIGSEGV, SIG_DFL);
> + fwts_sig_handler_restore(SIGSEGV, &old_action);
>
> return FWTS_OK;
> }
> @@ -98,9 +99,9 @@ int fwts_outb(uint32_t port, uint8_t value)
> if (sigsetjmp(jmpbuf, 1) != 0)
> return FWTS_ERROR;
>
> - signal(SIGSEGV, segv_handler);
> + fwts_sig_handler_set(SIGSEGV, segv_handler, &old_action);
> outb(port, value);
> - signal(SIGSEGV, SIG_DFL);
> + fwts_sig_handler_restore(SIGSEGV, &old_action);
>
> return FWTS_OK;
> }
> @@ -114,9 +115,9 @@ int fwts_outw(uint32_t port, uint16_t value)
> if (sigsetjmp(jmpbuf, 1) != 0)
> return FWTS_ERROR;
>
> - signal(SIGSEGV, segv_handler);
> + fwts_sig_handler_set(SIGSEGV, segv_handler, &old_action);
> outw(port, value);
> - signal(SIGSEGV, SIG_DFL);
> + fwts_sig_handler_restore(SIGSEGV, &old_action);
>
> return FWTS_OK;
> }
> @@ -130,9 +131,9 @@ int fwts_outl(uint32_t port, uint32_t value)
> if (sigsetjmp(jmpbuf, 1) != 0)
> return FWTS_ERROR;
>
> - signal(SIGSEGV, segv_handler);
> + fwts_sig_handler_set(SIGSEGV, segv_handler, &old_action);
> outl(port, value);
> - signal(SIGSEGV, SIG_DFL);
> + fwts_sig_handler_restore(SIGSEGV, &old_action);
>
> return FWTS_OK;
> }
> diff --git a/src/lib/src/fwts_safe_mem.c b/src/lib/src/fwts_safe_mem.c
> index 6fcfe1f..4d75ab3 100644
> --- a/src/lib/src/fwts_safe_mem.c
> +++ b/src/lib/src/fwts_safe_mem.c
> @@ -23,6 +23,7 @@
> #include "fwts.h"
>
> static sigjmp_buf jmpbuf;
> +static struct sigaction old_action;
>
> /*
> * If we hit a SIGSEGV then the port read
> @@ -33,7 +34,7 @@ static void segv_handler(int dummy)
> {
> FWTS_UNUSED(dummy);
>
> - signal(SIGSEGV, SIG_DFL);
> + fwts_sig_handler_restore(SIGSEGV, &old_action);
> siglongjmp(jmpbuf, 1);
> }
>
> @@ -48,9 +49,9 @@ int fwts_safe_memcpy(void *dst, const void *src, const size_t n)
> if (sigsetjmp(jmpbuf, 1) != 0)
> return FWTS_ERROR;
>
> - signal(SIGSEGV, segv_handler);
> + fwts_sig_handler_set(SIGSEGV, segv_handler, &old_action);
> memcpy(dst, src, n);
> - signal(SIGSEGV, SIG_DFL);
> + fwts_sig_handler_restore(SIGSEGV, &old_action);
>
> return FWTS_OK;
> }
> diff --git a/src/main.c b/src/main.c
> index 56981d5..ef67b4c 100644
> --- a/src/main.c
> +++ b/src/main.c
> @@ -24,6 +24,8 @@
>
> int main(int argc, char **argv)
> {
> + (void)fwts_fault_catch();
> +
> if (fwts_framework_args(argc, argv) == FWTS_OK)
> exit(EXIT_SUCCESS);
> else
Acked-by: Ivan Hu <ivan.hu at canonical.com>
More information about the fwts-devel
mailing list