From 2b1fb9ed1702d69c897a059f8e5fdbdebce8533b Mon Sep 17 00:00:00 2001 From: Fedora GDB patches Date: Fri, 27 Oct 2017 21:07:50 +0200 Subject: gdb-6.5-BEA-testsuite.patch FileName: gdb-6.5-BEA-testsuite.patch ;; Improved testsuite results by the testsuite provided by the courtesy of BEA. ;;=fedoratest: For upstream it should be rewritten as a dejagnu test, the test of no "??" was useful. --- gdb/testsuite/gdb.threads/threadcrash.c | 301 ++++++++++++++++++++++++ gdb/testsuite/gdb.threads/threadcrash.exp | 37 +++ gdb/testsuite/gdb.threads/threadcrash.sh | 324 ++++++++++++++++++++++++++ gdb/testsuite/gdb.threads/threadcrash.sh-orig | 248 ++++++++++++++++++++ 4 files changed, 910 insertions(+) create mode 100644 gdb/testsuite/gdb.threads/threadcrash.c create mode 100644 gdb/testsuite/gdb.threads/threadcrash.exp create mode 100644 gdb/testsuite/gdb.threads/threadcrash.sh create mode 100644 gdb/testsuite/gdb.threads/threadcrash.sh-orig diff --git a/gdb/testsuite/gdb.threads/threadcrash.c b/gdb/testsuite/gdb.threads/threadcrash.c new file mode 100644 index 0000000000..80c599d0fb --- /dev/null +++ b/gdb/testsuite/gdb.threads/threadcrash.c @@ -0,0 +1,301 @@ +/* + * The point of this program is to crash in a multi-threaded app. + * There are seven threads, doing the following things: + * * Spinning + * * Spinning inside a signal handler + * * Spinning inside a signal handler executing on the altstack + * * In a syscall + * * In a syscall inside a signal handler + * * In a syscall inside a signal handler executing on the altstack + * * Finally, the main thread crashes in main, with no frills. + * + * These are the things threads in JRockit tend to be doing. If gdb + * can handle those things, both in core files and during live + * debugging, that will help (at least) JRockit development. + * + * Let the program create a core file, then load the core file into + * gdb. Inside gdb, you should be able to do something like this: + * + * (gdb) t a a bt + * + * Thread 7 (process 4352): + * #0 0x001ba7dc in __nanosleep_nocancel () from /lib/tls/libc.so.6 + * #1 0x001ba5ff in sleep () from /lib/tls/libc.so.6 + * #2 0x080488a2 in makeSyscall (ignored=0x0) at threadcrash.c:118 + * #3 0x006aadec in start_thread () from /lib/tls/libpthread.so.0 + * #4 0x001ed19a in clone () from /lib/tls/libc.so.6 + * + * Thread 6 (process 4353): + * #0 0x001ba7dc in __nanosleep_nocancel () from /lib/tls/libc.so.6 + * #1 0x001ba5ff in sleep () from /lib/tls/libc.so.6 + * #2 0x0804898f in syscallingSighandler (signo=10, info=0xb6be76f0, context=0xb6be7770) + * at threadcrash.c:168 + * #3 + * #4 0x006adf5e in pthread_kill () from /lib/tls/libpthread.so.0 + * #5 0x08048a51 in makeSyscallFromSighandler (ignored=0x0) at threadcrash.c:204 + * #6 0x006aadec in start_thread () from /lib/tls/libpthread.so.0 + * #7 0x001ed19a in clone () from /lib/tls/libc.so.6 + * + * Thread 5 (process 4354): + * #0 0x001ba7dc in __nanosleep_nocancel () from /lib/tls/libc.so.6 + * #1 0x001ba5ff in sleep () from /lib/tls/libc.so.6 + * #2 0x08048936 in syscallingAltSighandler (signo=3, info=0x959cd70, context=0x959cdf0) + * at threadcrash.c:144 + * #3 + * #4 0x006adf5e in pthread_kill () from /lib/tls/libpthread.so.0 + * #5 0x080489e2 in makeSyscallFromAltSighandler (ignored=0x0) at threadcrash.c:190 + * #6 0x006aadec in start_thread () from /lib/tls/libpthread.so.0 + * #7 0x001ed19a in clone () from /lib/tls/libc.so.6 + * + * Thread 4 (process 4355): + * #0 spin (ignored=0x0) at threadcrash.c:242 + * #1 0x006aadec in start_thread () from /lib/tls/libpthread.so.0 + * #2 0x001ed19a in clone () from /lib/tls/libc.so.6 + * + * Thread 3 (process 4356): + * #0 spinningSighandler (signo=12, info=0xb4de46f0, context=0xb4de4770) at threadcrash.c:180 + * #1 + * #2 0x006adf5e in pthread_kill () from /lib/tls/libpthread.so.0 + * #3 0x08048b2f in spinFromSighandler (ignored=0x0) at threadcrash.c:232 + * #4 0x006aadec in start_thread () from /lib/tls/libpthread.so.0 + * #5 0x001ed19a in clone () from /lib/tls/libc.so.6 + * + * Thread 2 (process 4357): + * #0 spinningAltSighandler (signo=14, info=0x959ee50, context=0x959eed0) at threadcrash.c:156 + * #1 + * #2 0x006adf5e in pthread_kill () from /lib/tls/libpthread.so.0 + * #3 0x08048ac0 in spinFromAltSighandler (ignored=0x0) at threadcrash.c:218 + * #4 0x006aadec in start_thread () from /lib/tls/libpthread.so.0 + * #5 0x001ed19a in clone () from /lib/tls/libc.so.6 + * + * Thread 1 (process 4351): + * #0 0x08048cf3 in main (argc=1, argv=0xbfff9d74) at threadcrash.c:273 + * (gdb) + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SIGSYSCALL_ALT SIGQUIT +#define SIGSYSCALL SIGUSR1 +#define SIGSPIN_ALT SIGALRM +#define SIGSPIN SIGUSR2 + +typedef void (*sigaction_t)(int, siginfo_t *, void *); + +static void installHandler(int signo, sigaction_t handler, int onAltstack) { + struct sigaction action; + sigset_t sigset; + int result; + stack_t altstack; + stack_t oldaltstack; + + memset(&action, 0, sizeof(action)); + memset(&altstack, 0, sizeof(altstack)); + memset(&oldaltstack, 0, sizeof(oldaltstack)); + + if (onAltstack) { + altstack.ss_sp = malloc(SIGSTKSZ); + assert(altstack.ss_sp != NULL); + altstack.ss_size = SIGSTKSZ; + altstack.ss_flags = 0; + result = sigaltstack(&altstack, &oldaltstack); + assert(result == 0); + assert(oldaltstack.ss_flags == SS_DISABLE); + } + + sigemptyset(&sigset); + + action.sa_handler = NULL; + action.sa_sigaction = handler; + action.sa_mask = sigset; + action.sa_flags = SA_SIGINFO; + if (onAltstack) { + action.sa_flags |= SA_ONSTACK; + } + + result = sigaction(signo, &action, NULL); + assert(result == 0); +} + +static void installNormalHandler(int signo, sigaction_t handler) { + installHandler(signo, handler, 0); +} + +static void installAlthandler(int signo, sigaction_t handler) { + installHandler(signo, handler, 1); +} + +static void *makeSyscall(void *ignored) { + (void)ignored; + + sleep(42); + + fprintf(stderr, "%s: returning\n", __FUNCTION__); + return NULL; +} + +/* Return true if we're currently executing on the altstack */ +static int onAltstack(void) { + stack_t stack; + int result; + + result = sigaltstack(NULL, &stack); + assert(result == 0); + + return stack.ss_flags & SS_ONSTACK; +} + +static void syscallingAltSighandler(int signo, siginfo_t *info, void *context) { + (void)signo; + (void)info; + (void)context; + + if (!onAltstack()) { + printf("%s() not running on altstack!\n", __FUNCTION__); + } + + sleep(42); +} + +static void spinningAltSighandler(int signo, siginfo_t *info, void *context) { + (void)signo; + (void)info; + (void)context; + + if (!onAltstack()) { + printf("%s() not running on altstack!\n", __FUNCTION__); + } + + while (1); +} + +static void syscallingSighandler(int signo, siginfo_t *info, void *context) { + (void)signo; + (void)info; + (void)context; + + if (onAltstack()) { + printf("%s() running on altstack!\n", __FUNCTION__); + } + + sleep(42); +} + +static void spinningSighandler(int signo, siginfo_t *info, void *context) { + (void)signo; + (void)info; + (void)context; + + if (onAltstack()) { + printf("%s() running on altstack!\n", __FUNCTION__); + } + + while (1); +} + +static void *makeSyscallFromAltSighandler(void *ignored) { + (void)ignored; + + int result; + + installAlthandler(SIGSYSCALL_ALT, syscallingAltSighandler); + + result = pthread_kill(pthread_self(), SIGSYSCALL_ALT); + assert(result == 0); + + fprintf(stderr, "%s: returning\n", __FUNCTION__); + return NULL; +} + +static void *makeSyscallFromSighandler(void *ignored) { + (void)ignored; + + int result; + + installNormalHandler(SIGSYSCALL, syscallingSighandler); + + result = pthread_kill(pthread_self(), SIGSYSCALL); + assert(result == 0); + + fprintf(stderr, "%s: returning\n", __FUNCTION__); + return NULL; +} + +static void *spinFromAltSighandler(void *ignored) { + (void)ignored; + + int result; + + installAlthandler(SIGSPIN_ALT, spinningAltSighandler); + + result = pthread_kill(pthread_self(), SIGSPIN_ALT); + assert(result == 0); + + fprintf(stderr, "%s: returning\n", __FUNCTION__); + return NULL; +} + +static void *spinFromSighandler(void *ignored) { + (void)ignored; + + int result; + + installNormalHandler(SIGSPIN, spinningSighandler); + + result = pthread_kill(pthread_self(), SIGSPIN); + assert(result == 0); + + fprintf(stderr, "%s: returning\n", __FUNCTION__); + return NULL; +} + +static void *spin(void *ignored) { + (void)ignored; + + while (1); + + fprintf(stderr, "%s: returning\n", __FUNCTION__); + return NULL; +} + +int main(int argc, char *argv[]) { + int result; + pthread_t thread; + volatile int bad; + + result = pthread_create(&thread, NULL, makeSyscall, NULL); + assert(result == 0); + result = pthread_create(&thread, NULL, makeSyscallFromSighandler, NULL); + assert(result == 0); + result = pthread_create(&thread, NULL, makeSyscallFromAltSighandler, NULL); + assert(result == 0); + result = pthread_create(&thread, NULL, spin, NULL); + assert(result == 0); + result = pthread_create(&thread, NULL, spinFromSighandler, NULL); + assert(result == 0); + result = pthread_create(&thread, NULL, spinFromAltSighandler, NULL); + assert(result == 0); + + // Give threads some time to get going + sleep(3); + + // Crash + bad = *(int*)7; + + /* Workaround: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29628 + Simulate use to ensure `DW_AT_location' for them: + readelf -a --debug threadcrash|grep -A5 -w argc + --> DW_AT_location : 2 byte block: 71 0 (DW_OP_breg1: 0) + This case verified on: gcc-4.1.1-30.i386 + Keep it late to ensure persistency in the registers. */ + bad = (int) argc; + bad = (unsigned long) argv; + + return 0; +} diff --git a/gdb/testsuite/gdb.threads/threadcrash.exp b/gdb/testsuite/gdb.threads/threadcrash.exp new file mode 100644 index 0000000000..af6b919f58 --- /dev/null +++ b/gdb/testsuite/gdb.threads/threadcrash.exp @@ -0,0 +1,37 @@ +# threadcrash.exp - The point of this program is to crash in a multi-threaded app. + + +set testfile threadcrash +set srcfile ${testfile}.c +set shellfile ${srcdir}/${subdir}/${testfile}.sh +set binfile [standard_output_file ${testfile}] + +set GDB_abs ${GDB} +if [regexp "^\[^/\]" ${GDB_abs}] { + set GDB_abs $env(PWD)/${GDB_abs} +} + +if [istarget "*-*-linux"] then { + set target_cflags "-D_MIT_POSIX_THREADS" +} else { + set target_cflags "" +} + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + return -1 +} + +# ${shellfile} argument must not contain any directories. +set fd [open "|bash ${shellfile} ${binfile} $GDB $INTERNAL_GDBFLAGS $GDBFLAGS [host_info gdb_opts]" r] +while { [gets $fd line] >= 0 } { + if [regexp " PASS: (.*)$" $line trash message] { + pass $message + } elseif [regexp " FAIL: (.*)$" $line trash message] { + fail $message + } +} +catch { + close $fd +} + +return 0 diff --git a/gdb/testsuite/gdb.threads/threadcrash.sh b/gdb/testsuite/gdb.threads/threadcrash.sh new file mode 100644 index 0000000000..7f7e6520d6 --- /dev/null +++ b/gdb/testsuite/gdb.threads/threadcrash.sh @@ -0,0 +1,324 @@ +#! /bin/bash + +# NOTE: threadcrash.c *must* be built with debugging symbols +# +# The point of this shell script is to crash treadcrash.c, load the +# resulting core file into gdb and verify that gdb can extract enough +# information from the core file. +# +# The return code from this script is the number of failed tests. + +LOG=gdbresult.log + +if [ $# = 0 ] ; then + echo >&2 Syntax: $0 \ [\ \] + exit 1 +fi +RUNME="$1" +shift +GDB="${*:-gdb}" + + +pf_prefix="" +function pf_prefix() { + pf_prefix="$*" +} + +set_test="" +function set_test() { + if [ -n "$set_test" ] ; then + echo >&2 "DEJAGNU-BASH ERROR: set_test already set" + exit 1 + fi + set_test="$*" + if [ -n "$pf_prefix" ] ; then + set_test="$pf_prefix: $set_test" + fi +} + +# INTERNAL +function record_test { + if [ -z "$set_test" ] ; then + echo >&2 "DEJAGNU-BASH ERROR: set_test not set" + exit 1 + fi + # Provide the leading whitespace delimiter: + echo " $1: $set_test" + set_test="" +} + +function pass() { + record_test PASS +} +function fail() { + record_test FAIL +} + + +# Verify that the gdb output doesn't contain $1. +function mustNotHave() { + local BADWORD=$1 + set_test gdb output contains "$BADWORD" + if grep -q "$BADWORD" $LOG ; then + fail + return 1 + fi + pass + return 0 +} + +# Verify that the gdb output contains exactly $1 $2s. +function mustHaveCorrectAmount() { + local WANTEDNUMBER=$1 + local GOODWORD=$2 + local ACTUALNUMBER=$(grep "$GOODWORD" $LOG | wc -l) + set_test gdb output contained $ACTUALNUMBER \""$GOODWORD"\", not $WANTEDNUMBER as expected + if [ $ACTUALNUMBER != $WANTEDNUMBER ] ; then + fail + return 1 + fi + pass + return 0 +} + +# Verify that the gdb output contains seven threads +function mustHaveSevenThreads() { + NTHREADS=$(egrep "^Thread [1-7] \(" $LOG | wc -l) + set_test gdb output contains $NTHREADS threads, not 7 as expected + if [ $NTHREADS != 7 ] ; then + fail + return 1 + fi + pass + return 0 +} + +# Verify that the gdb output has all parameters on consecutive lines +function mustHaveSequence() { + SEQUENCE="$*" + NPARTS=$# + grep "$1" -A$((NPARTS - 1)) $LOG > matches.log + + while [ $# -gt 1 ] ; do + shift + ((NPARTS--)) + grep "$1" -A$((NPARTS - 1)) matches.log > temp.log + mv temp.log matches.log + done + LASTPART=$1 + + set_test gdb output does not contain the sequence: $SEQUENCE + if ! grep -q "$LASTPART" matches.log ; then + fail + return 1 + fi + pass + return 0 +} + +# Verify that $LOG contains all information we want +function verifyLog() { + local FAILURES=0 + + mustNotHave '??' || ((FAILURES++)) + mustHaveCorrectAmount 11 threadcrash.c: || ((FAILURES++)) + + mustHaveSevenThreads || ((FAILURES++)) + mustHaveSequence sleep "makeSyscall (ignored=" || ((FAILURES++)) + + mustHaveSequence sleep "syscallingSighandler (signo=" "signal handler called" 0x || ((FAILURES++)) + mustHaveSequence pthread_kill "makeSyscallFromSighandler (ignored=" || ((FAILURES++)) + + mustHaveSequence sleep "syscallingAltSighandler (signo=" "signal handler called" 0x || ((FAILURES++)) + mustHaveSequence pthread_kill "makeSyscallFromAltSighandler (ignored=" || ((FAILURES++)) + + mustHaveSequence Thread "spin (ignored=" || ((FAILURES++)) + + mustHaveSequence "spinningSighandler (signo=" "signal handler called" 0x || ((FAILURES++)) + mustHaveSequence pthread_kill "spinFromSighandler (ignored=" || ((FAILURES++)) + + mustHaveSequence "spinningAltSighandler (signo=" "signal handler called" 0x || ((FAILURES++)) + mustHaveSequence pthread_kill "spinFromAltSighandler (ignored=" || ((FAILURES++)) + + mustHaveSequence Thread "main (argc=1, argv=" || ((FAILURES++)) + + return $FAILURES +} + +# Put result of debugging a core file in $LOG +function getLogFromCore() { + # Make sure we get a core file + set_test Make sure we get a core file + if ! ulimit -c unlimited ; then + fail + exit 1 + fi + pass + + # Run the crasher + ./$(basename "$RUNME") + EXITCODE=$? + + # Verify that we actually crashed + set_test $RUNME should have been killed by a signal, got non-signal exit code $EXITCODE + if [ $EXITCODE -lt 128 ] ; then + fail + exit 1 + fi + pass + + # Verify that we got a core file + set_test $RUNME did not create a core file + if [ ! -r core* ] ; then + fail + exit 1 + fi + pass + + # Run gdb + cat > gdbscript.gdb < $LOG + EXITCODE=$? + + set_test gdb exited with error code + if [ $EXITCODE != 0 ] ; then + ((FAILURES++)) + echo >&2 gdb exited with error code $EXITCODE + fail + fi + pass +} + +# Put result of debugging a gcore file in $LOG +function getLogFromGcore() { + # Create the core file + rm -f core* + cat > gdbscript.gdb < /dev/null + EXITCODE=$? + + set_test gdb exited with error code when creating gcore file + if [ $EXITCODE != 0 ] ; then + ((FAILURES++)) + echo >&2 gdb exited with error code $EXITCODE when creating gcore file + fail + fi + pass + + # Verify that we got a core file from gcore + set_test gdb gcore did not create a core file + if [ ! -r core* ] ; then + fail + exit 1 + fi + pass + + # Run gdb on the gcore file + cat > gdbscript.gdb < $LOG + EXITCODE=$? + + set_test gdb exited with error code when examining gcore file + if [ $EXITCODE != 0 ] ; then + ((FAILURES++)) + echo >&2 gdb exited with error code $EXITCODE when examining gcore file + fail + fi + pass +} + +# Put result of debugging a core file in $LOG +function getLogFromLiveProcess() { + # Run gdb + cat > gdbscript.gdb < $LOG + EXITCODE=$? + + set_test gdb exited with error code + if [ $EXITCODE != 0 ] ; then + ((FAILURES++)) + echo >&2 gdb exited with error code $EXITCODE + fail + fi + pass +} + +####### Main program follows ##################### + +# Make sure we don't clobber anybody else's (core) file(s) +WORKDIR=/tmp/$PPID +mkdir -p $WORKDIR +cp "$RUNME" $WORKDIR +cd $WORKDIR + +# Count problems +FAILURES=0 + +echo === Testing gdb vs core file... +pf_prefix core file +getLogFromCore +verifyLog +((FAILURES+=$?)) +pf_prefix +echo === Core file tests done. + +echo + +echo === Testing gdb vs gcore file... +pf_prefix gcore file +getLogFromGcore +verifyLog +((FAILURES+=$?)) +pf_prefix +echo === Gcore file tests done. + +echo + +echo === Testing gdb vs live process... +pf_prefix live process +getLogFromLiveProcess +verifyLog +((FAILURES+=$?)) +pf_prefix +echo === Live process tests done. + +# Executive summary +echo +if [ $FAILURES == 0 ] ; then + echo All tests passed! +else + echo $FAILURES tests failed! + echo + echo Make sure the threadcrash binary contains debugging information \(build with \"gcc -g\"\). +fi + +# Clean up +cd / +rm -rf $WORKDIR + +exit $FAILURES diff --git a/gdb/testsuite/gdb.threads/threadcrash.sh-orig b/gdb/testsuite/gdb.threads/threadcrash.sh-orig new file mode 100644 index 0000000000..eb602036c2 --- /dev/null +++ b/gdb/testsuite/gdb.threads/threadcrash.sh-orig @@ -0,0 +1,248 @@ +#! /bin/bash + +# NOTE: threadcrash.c *must* be built with debugging symbols +# +# The point of this shell script is to crash treadcrash.c, load the +# resulting core file into gdb and verify that gdb can extract enough +# information from the core file. +# +# The return code from this script is the number of failed tests. + +LOG=gdbresult.log + +if [ $# != 1 ] ; then + echo > /dev/stderr Syntax: $0 \ + exit 1 +fi +RUNME="$1" + +# Verify that the gdb output doesn't contain $1. +function mustNotHave() { + local BADWORD=$1 + if grep -q "$BADWORD" $LOG ; then + echo >> /dev/stderr WARNING: gdb output contains "$BADWORD" + return 1 + fi + return 0 +} + +# Verify that the gdb output contains exactly $1 $2s. +function mustHaveCorrectAmount() { + local WANTEDNUMBER=$1 + local GOODWORD=$2 + local ACTUALNUMBER=$(grep "$GOODWORD" $LOG | wc -l) + if [ $ACTUALNUMBER != $WANTEDNUMBER ] ; then + echo >> /dev/stderr WARNING: gdb output contained $ACTUALNUMBER \""$GOODWORD"\", not $WANTEDNUMBER as expected + return 1 + fi + return 0 +} + +# Verify that the gdb output contains seven threads +function mustHaveSevenThreads() { + NTHREADS=$(egrep "^Thread [1-7] \(" $LOG | wc -l) + if [ $NTHREADS != 7 ] ; then + echo >> /dev/stderr WARNING: gdb output contains $NTHREADS threads, not 7 as expected + return 1 + fi + return 0 +} + +# Verify that the gdb output has all parameters on consecutive lines +function mustHaveSequence() { + SEQUENCE="$*" + NPARTS=$# + grep "$1" -A$((NPARTS - 1)) $LOG > matches.log + + while [ $# -gt 1 ] ; do + shift + ((NPARTS--)) + grep "$1" -A$((NPARTS - 1)) matches.log > temp.log + mv temp.log matches.log + done + LASTPART=$1 + + if ! grep -q "$LASTPART" matches.log ; then + echo >> /dev/stderr WARNING: gdb output does not contain the sequence: $SEQUENCE + return 1 + fi + return 0 +} + +# Verify that $LOG contains all information we want +function verifyLog() { + local FAILURES=0 + + mustNotHave '??' || ((FAILURES++)) + mustHaveCorrectAmount 12 threadcrash.c: || ((FAILURES++)) + + mustHaveSevenThreads || ((FAILURES++)) + mustHaveSequence sleep "makeSyscall (ignored=" || ((FAILURES++)) + + mustHaveSequence sleep "syscallingSighandler (signo=" "signal handler called" 0x || ((FAILURES++)) + mustHaveSequence pthread_kill "makeSyscallFromSighandler (ignored=" || ((FAILURES++)) + + mustHaveSequence sleep "syscallingAltSighandler (signo=" "signal handler called" 0x || ((FAILURES++)) + mustHaveSequence pthread_kill "makeSyscallFromAltSighandler (ignored=" || ((FAILURES++)) + + mustHaveSequence Thread "spin (ignored=" || ((FAILURES++)) + + mustHaveSequence "spinningSighandler (signo=" "signal handler called" 0x || ((FAILURES++)) + mustHaveSequence pthread_kill "spinFromSighandler (ignored=" || ((FAILURES++)) + + mustHaveSequence "spinningAltSighandler (signo=" "signal handler called" 0x || ((FAILURES++)) + mustHaveSequence pthread_kill "spinFromAltSighandler (ignored=" || ((FAILURES++)) + + mustHaveSequence Thread "main (argc=1, argv=" || ((FAILURES++)) + + return $FAILURES +} + +# Put result of debugging a core file in $LOG +function getLogFromCore() { + # Make sure we get a core file + ulimit -c unlimited || exit 1 + + # Run the crasher + ./$(basename "$RUNME") + EXITCODE=$? + + # Verify that we actually crashed + if [ $EXITCODE -lt 128 ] ; then + echo >> /dev/stderr ERROR: $RUNME should have been killed by a signal, got non-signal exit code $EXITCODE + exit 1 + fi + + # Verify that we got a core file + if [ ! -r core* ] ; then + echo >> /dev/stderr ERROR: $RUNME did not create a core file + exit 1 + fi + + # Run gdb + cat > gdbscript.gdb < $LOG + EXITCODE=$? + + if [ $EXITCODE != 0 ] ; then + ((FAILURES++)) + echo >> /dev/stderr WARNING: gdb exited with error code $EXITCODE + fi +} + +# Put result of debugging a gcore file in $LOG +function getLogFromGcore() { + # Create the core file + rm -f core* + cat > gdbscript.gdb < /dev/null + EXITCODE=$? + + if [ $EXITCODE != 0 ] ; then + ((FAILURES++)) + echo >> /dev/stderr WARNING: gdb exited with error code $EXITCODE when creating gcore file + fi + + # Verify that we got a core file from gcore + if [ ! -r core* ] ; then + echo >> /dev/stderr ERROR: gdb gcore did not create a core file + exit 1 + fi + + # Run gdb on the gcore file + cat > gdbscript.gdb < $LOG + EXITCODE=$? + + if [ $EXITCODE != 0 ] ; then + ((FAILURES++)) + echo >> /dev/stderr WARNING: gdb exited with error code $EXITCODE when examining gcore file + fi +} + +# Put result of debugging a core file in $LOG +function getLogFromLiveProcess() { + # Run gdb + cat > gdbscript.gdb < $LOG + EXITCODE=$? + + if [ $EXITCODE != 0 ] ; then + ((FAILURES++)) + echo >> /dev/stderr WARNING: gdb exited with error code $EXITCODE + fi +} + +####### Main program follows ##################### + +# Make sure we don't clobber anybody else's (core) file(s) +WORKDIR=/tmp/$PPID +mkdir -p $WORKDIR +cp "$RUNME" $WORKDIR +cd $WORKDIR + +# Count problems +FAILURES=0 + +echo === Testing gdb vs core file... +getLogFromCore +verifyLog +((FAILURES+=$?)) +echo === Core file tests done. + +echo + +echo === Testing gdb vs gcore file... +getLogFromGcore +verifyLog +((FAILURES+=$?)) +echo === Gcore file tests done. + +echo + +echo === Testing gdb vs live process... +getLogFromLiveProcess +verifyLog +((FAILURES+=$?)) +echo === Live process tests done. + +# Executive summary +echo +if [ $FAILURES == 0 ] ; then + echo All tests passed! +else + echo $FAILURES tests failed! + echo + echo Make sure the threadcrash binary contains debugging information \(build with \"gcc -g\"\). +fi + +# Clean up +cd / +rm -rf $WORKDIR + +exit $FAILURES -- 2.14.3