summaryrefslogtreecommitdiff
path: root/gdb-wrapper2
diff options
context:
space:
mode:
authorPaweł Redman <pawel.redman@gmail.com>2017-12-26 19:36:21 +0000
committerPaweł Redman <pawel.redman@gmail.com>2017-12-26 19:40:50 +0000
commit4fbda5a9af95345504636d178e26b248791a2262 (patch)
treeaa20acbf33686b6843273b175e487ff397abee12 /gdb-wrapper2
parent26d2028b4773aed05707faa7f525075b07da899c (diff)
Initial commit.
No history was lost as the scripts had no history in the first place.
Diffstat (limited to 'gdb-wrapper2')
-rw-r--r--gdb-wrapper2/Makefile10
-rwxr-xr-xgdb-wrapper2/gdb-wrapper2.sh92
-rwxr-xr-xgdb-wrapper2/test.sh32
-rw-r--r--gdb-wrapper2/test_program.c154
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();
+}