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