diff options
Diffstat (limited to 'gdb-wrapper2')
| -rw-r--r-- | gdb-wrapper2/Makefile | 10 | ||||
| -rwxr-xr-x | gdb-wrapper2/gdb-wrapper2.sh | 92 | ||||
| -rwxr-xr-x | gdb-wrapper2/test.sh | 32 | ||||
| -rw-r--r-- | gdb-wrapper2/test_program.c | 154 | 
4 files changed, 288 insertions, 0 deletions
diff --git a/gdb-wrapper2/Makefile b/gdb-wrapper2/Makefile new file mode 100644 index 0000000..bba6e44 --- /dev/null +++ b/gdb-wrapper2/Makefile @@ -0,0 +1,10 @@ +test_program: test_program.c +	cc test_program.c -o test_program $(CFLAGS) $(LDFLAGS) + +test: test.sh test_program +	./test.sh + +clean: +	rm test_program + +.PHONY: clean diff --git a/gdb-wrapper2/gdb-wrapper2.sh b/gdb-wrapper2/gdb-wrapper2.sh new file mode 100755 index 0000000..c60b649 --- /dev/null +++ b/gdb-wrapper2/gdb-wrapper2.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# gdb-wrapper2 +# Copyright (C) 2017  Paweł Redman <pawel.redman@gmail.com> + +# 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 3 of the License, or +# (at your option) any later version. + +SELF="$0" +PROGRAM="$1" +shift 1 + +if [ -z "$PROGRAM" ]; then +        echo "$SELF [program] (args...)" +        exit 1 +fi + +# Check if the program exists before calling GDB so the errors are clearer. +if [ ! -f "$PROGRAM" ]; then +	stat "$PROGRAM" +	exit 1 +fi + +# 'catch' and 'break' can be followed by a 'commands' block to tell GDB what +# to do in case of a breakpoint/signal. This is where the stack is printed +# and the core is dumped.  +function write_commands { +	echo "commands" +		echo "thread apply all backtrace" +		echo "gcore" +		echo "echo \n" + +		# Terminate if this isn't the first fault to avoid infinite +		# recursion. +		echo "set \$faults = \$faults + 1" +		echo "if \$faults >= 2" +			echo "echo Double fault, terminating the program.\n" +			echo "kill" +			echo "quit" +		echo "else" +			# Resume the program's execution. +			# If the program can handle signals and exit gracefully +			# then this script should let it (after dumping a core +			# for debugging). +			echo "continue" +		echo "end" +	echo "end" +} + +function write_config { +	# Make sure gdb won't ask for interactive input. This is supposed +	# to never happen when using -batch but I don't trust them. +	echo "set confirm off" +	echo "set width unlimited" +	echo "set height unlimited" +	echo "set pagination off" +	echo "set backtrace limit unlimited" + +	echo "set \$faults = 0" + +	# This catches all signals except SIGINT and SIGTRAP. +	echo "catch signal" +	write_commands + +	# Catch SIGINT too, just in case. +	echo "catch signal SIGINT" +	write_commands + +	# Catch calls to Com_Error. You'll want something else here if you're +	# using this script for another program. +	echo "break Com_Error" +	write_commands + +	# Finally start the program. +	echo "run" +} + +# Disable automatic coredumps. +ulimit -c 0 + +# For some reason gdb's '-x' option won't accept a pipe as an argument. The +# workaround is to create a temporary file for the configuration. +config_path=$(mktemp /tmp/gdb-wrapper2.XXXXXX) +write_config > "$config_path" + +gdb -batch -x "$config_path" --args "$PROGRAM" "$@" +exit_status="$?" + +rm "$config_path" +exit $exit_status diff --git a/gdb-wrapper2/test.sh b/gdb-wrapper2/test.sh new file mode 100755 index 0000000..f5fea63 --- /dev/null +++ b/gdb-wrapper2/test.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +function do_test { +	../gdb-wrapper2.sh ./test_program --verbose "$@" +} + +function title { +	tput bold +	tput setaf 3 +	echo "$@" +	echo +	tput sgr0 +} + +title "Test: Raise 11." +do_test --raise 11 + +title "Test: Raise and catch 11." +do_test --raise 11 --catch 11 + +title "Test: Raise then keep recursively catching 11." +do_test --raise 11 --catch 11 --recursive + +title "Test: Call Com_Error." +do_test --Com_Error + +title "Test: Call Com_Error then keep recursively calling it." +do_test --Com_Error --recursive + +title "Test: Raise but ignore 2." +do_test --raise 2 --ignore 2 + diff --git a/gdb-wrapper2/test_program.c b/gdb-wrapper2/test_program.c new file mode 100644 index 0000000..e332d5f --- /dev/null +++ b/gdb-wrapper2/test_program.c @@ -0,0 +1,154 @@ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <signal.h> + +#define MAX_SIGNALS 32 + +#define print(fmt, ...) fprintf(stderr, "test_program: "fmt, ##__VA_ARGS__) + +_Bool do_raise = 0; +int raise_signal;    +_Bool do_Com_Error = 0; +_Bool recursive = 0; + +enum { DEFAULT = 0, CATCH, IGNORE }; +int handle[MAX_SIGNALS] = {0}; +_Bool handle_any = 0; + +_Bool verbose = 0; + +static int read_args(int argc, char **argv) +{ +	for (int i = 1; i < argc; i++) { +		if (!strcmp(argv[i], "--raise")) { +			if (i + 1 >= argc) +				goto needs_argument; + +			do_raise = 1; +			raise_signal = atoi(argv[i + 1]); +			i++; +		} else if (!strcmp(argv[i], "--Com_Error")) { +			do_Com_Error = 1; +		} else if (!strcmp(argv[i], "--recursive")) { +			recursive = 1; +		} else if (!strcmp(argv[i], "--catch") || +		           !strcmp(argv[i], "--ignore")) { +			int sig; + +			if (i + 1 >= argc) +				goto needs_argument; + +			sig = atoi(argv[i + 1]); +			if (sig < 0 || sig >= MAX_SIGNALS) { +				print("invalid argument for --catch/--ignore: %i\n", +				      sig); +				return 1; +			} + +			if (!strcmp(argv[i], "--catch")) +				handle[sig] = CATCH; +			else +				handle[sig] = IGNORE; + +			handle_any = 1; +			i++; +		} else if (!strcmp(argv[i], "--ignore")) { +			 +		} else if (!strcmp(argv[i], "--verbose")) { +			verbose = 1; +		} else { +			print("invalid option '%s'\n", argv[i]); +			return 1; +		} + +		continue; +	needs_argument: +		print("option '%s' needs an argument\n", argv[i]); +		return 1; +	} + +	if (do_Com_Error && do_raise) { +		print("--raise and --Com_Error are mutually exclusive.\n"); +		return 1; +	} + +	return 0; +} + +_Noreturn void graceful_exit(void) +{ +	print("Graceful exit.\n"); +	exit(0); +} + +void signal_catcher(int sig) +{ +	print("Caught signal %i.\n", sig); + +	if (recursive) { +		if (verbose) +			print("Raising signal %i from within the handler.\n", +			      raise_signal); +		raise(raise_signal); +	} + +	graceful_exit(); +} + +_Noreturn void Com_Error(int type, const char *str) +{ +	print("Com_Error: %s\n", str); + +	if (recursive) +		Com_Error(666, "Error while erroring"); + +	graceful_exit(); +} + +int main(int argc, char **argv) +{ +	int rv; + +	rv = read_args(argc, argv); +	if (rv) +		return rv; + +	for (int i = 0; i < MAX_SIGNALS; i++) { +		void (*fun)(int); + +		switch (handle[i]) { +		case DEFAULT: +			continue; + +		case CATCH: +			fun = signal_catcher; +			if (verbose) +				print("Signal %i will be caught.\n", i); +			break; + +		case IGNORE: +			fun = SIG_IGN; +			if (verbose) +				print("Signal %i will be ignored.\n", i); +			break; +		} + +		if (signal(i, fun) == SIG_ERR) { +			print("For signal %i: ", i); +			perror("signal"); +			return 1; +		} +	} + +	if (do_raise) { +		print("Raising signal %i.\n", raise_signal); +		raise(raise_signal); +	} + +	if (do_Com_Error) +		Com_Error(123, "Some error string"); + +	print("Reached the end of main().\n"); +	graceful_exit(); +}  | 
