Program test-coredump-unwind was modified to map backing files based on virtual addresses instead of segment numbers.
The crasher.c is a program that calls some functions and then writes to invalid address causing a crash. The test itself is a shell script, which runs the program. The script then tries to determine the virtual addresses based on output of 'ldd' and 'readelf' and then runs test-coredump-unwind to check whether the stack trace obtained from the dump roughly corresponds to what it should look like. Signed-off-by: Martin Milata <[email protected]> --- .gitignore | 2 + tests/Makefile.am | 18 +++++++----- tests/crasher.c | 27 +++++++++++++++++++ tests/run-coredump-unwind | 30 +++++++++++++++++++++ tests/test-coredump-unwind.c | 60 +++++++++++++++++++++++++++++++++-------- 5 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 tests/crasher.c create mode 100755 tests/run-coredump-unwind diff --git a/.gitignore b/.gitignore index 62563e5..2f805cd 100644 --- a/.gitignore +++ b/.gitignore @@ -38,10 +38,12 @@ tests/Ltest-nomalloc tests/Ltest-nocalloc tests/Lperf-simple tests/check-namespace.sh +tests/crasher tests/forker tests/mapper tests/rs-race tests/test-async-sig +tests/test-coredump-unwind tests/test-flush-cache tests/test-init-remote tests/test-mem diff --git a/tests/Makefile.am b/tests/Makefile.am index 4b3bce3..6de90cd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,8 +1,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/include EXTRA_DIST = run-ia64-test-dyn1 run-ptrace-mapper run-ptrace-misc \ - run-check-namespace check-namespace.sh.in Gtest-nomalloc.c \ - Gtest-nocalloc.c + run-check-namespace run-coredump-unwind \ + check-namespace.sh.in Gtest-nomalloc.c Gtest-nocalloc.c MAINTAINERCLEANFILES = Makefile.in @@ -36,7 +36,8 @@ endif #USE_ALTIVEC noinst_PROGRAMS_arch = $(noinst_PROGRAMS_arch_altivec) ppc64-test-wchar endif #ARCH_PPC64 endif #ARCH_IA64 - check_SCRIPTS_cdep = run-ptrace-mapper run-ptrace-misc + check_SCRIPTS_cdep = run-ptrace-mapper run-ptrace-misc \ + run-coredump-unwind check_PROGRAMS_cdep = Gtest-bt Ltest-bt Gtest-exc Ltest-exc \ Gtest-init Ltest-init \ Gtest-concurrent Ltest-concurrent \ @@ -45,10 +46,9 @@ endif #ARCH_IA64 Gtest-trace Ltest-trace \ test-async-sig test-flush-cache test-init-remote \ test-mem test-setjmp test-ptrace \ - Ltest-nomalloc Ltest-nocalloc rs-race \ - test-coredump-unwind - noinst_PROGRAMS_cdep = forker mapper test-ptrace-misc \ - Gperf-simple Lperf-simple + Ltest-nomalloc Ltest-nocalloc rs-race + noinst_PROGRAMS_cdep = forker crasher mapper test-ptrace-misc \ + Gperf-simple Lperf-simple test-coredump-unwind if HAVE_BACKTRACE noinst_PROGRAMS_cdep += Gperf-trace Lperf-trace test-varargs @@ -111,6 +111,10 @@ Ltest_nocalloc_SOURCES = Ltest-nocalloc.c Gtest_trace_SOURCES = Gtest-trace.c ident.c Ltest_trace_SOURCES = Ltest-trace.c ident.c +# prevent function inlining +crasher: crasher.c + $(CC) -O0 -o crasher crasher.c + LIBUNWIND = $(top_builddir)/src/libunwind-$(arch).la LIBUNWIND_ptrace = $(top_builddir)/src/libunwind-ptrace.a LIBUNWIND_coredump = $(top_builddir)/src/libunwind-coredump.a diff --git a/tests/crasher.c b/tests/crasher.c new file mode 100644 index 0000000..fbee0d5 --- /dev/null +++ b/tests/crasher.c @@ -0,0 +1,27 @@ +/* This program should crash and produce coredump */ + +#include <stdio.h> + +void a(void) __attribute__((noinline)); +void b(int x) __attribute__((noinline)); + +void a(void) +{ + *(int *)NULL = 42; +} + +void b(int x) +{ + if (x) + a(); + else + b(1); +} + +int +main (int argc, char **argv) +{ + b(0); + return 0; +} + diff --git a/tests/run-coredump-unwind b/tests/run-coredump-unwind new file mode 100755 index 0000000..4c6dbc4 --- /dev/null +++ b/tests/run-coredump-unwind @@ -0,0 +1,30 @@ +#!/bin/sh + +TESTDIR=`pwd` +TEMPDIR=`mktemp -d` + +# create core dump +cd $TEMPDIR +( + ulimit -c 10000 + $TESTDIR/crasher +) 2>/dev/null +COREFILE=$TEMPDIR/core* +cd $TESTDIR + +# fail if any command fails +set -e + +# find out where to map shared libraries +BACKING="" +for LINE in `ldd crasher | sed -nre 's#^[^/]*(/[^ ]+)[ ]+\((.[^)]*)\).*$#\2:\1#p'`; do + BACKING="$BACKING $LINE" +done + +# find the base address of the binary +BASE=`readelf -l crasher | grep LOAD | head -n1 | sed -re 's#^.*LOAD[^0]+0x[^ ]+[^0]+(0x[^ ]+).*$#\1#'` + +# magic option -testcase enables checking for the specific contents of the stack +./test-coredump-unwind $COREFILE -testcase $BASE:crasher $BACKING + +rm -r -- $TEMPDIR diff --git a/tests/test-coredump-unwind.c b/tests/test-coredump-unwind.c index 54fba57..7197e76 100644 --- a/tests/test-coredump-unwind.c +++ b/tests/test-coredump-unwind.c @@ -10,11 +10,13 @@ * -oexample-core-unwind * * Run: - * objdump -sx COREDUMP * eu-unstrip -n --core COREDUMP - * figure out which segments in COREDUMP correspond to which mapped executable files + * figure out which virtual addresses in COREDUMP correspond to which mapped executable files * (binary and libraries), then supply them like this: - * ./example-core-unwind COREDUMP 3:/bin/crashed_program 6:/lib/libc.so.6 [...] + * ./example-core-unwind COREDUMP 0x400000:/bin/crashed_program 0x3458600000:/lib/libc.so.6 [...] + * + * Note: Program eu-unstrip is part of elfutils, virtual addresses of shared + * libraries can be determined by ldd (at least on linux). */ #undef _GNU_SOURCE @@ -256,6 +258,11 @@ main(int argc, char **argv) unw_cursor_t c; int ret; +#define TEST_FRAMES 4 + int testcase = 0; + int test_cur = 0; + long test_start_ips[TEST_FRAMES]; + install_sigsegv_handler(); const char *progname = strrchr(argv[0], '/'); @@ -265,7 +272,7 @@ main(int argc, char **argv) progname = argv[0]; if (!argv[1]) - error_msg_and_die("Usage: %s COREDUMP [SEGMENT_NO:BINARY_FILE]...", progname); + error_msg_and_die("Usage: %s COREDUMP [VADDR:BINARY_FILE]...", progname); msg_prefix = progname; @@ -281,16 +288,23 @@ main(int argc, char **argv) error_msg_and_die("unw_init_remote() failed: ret=%d\n", ret); argv += 2; + + /* Enable checks for the crasher test program? */ + if (*argv && !strcmp(*argv, "-testcase")) + { + testcase = 1; + logmode = LOGMODE_NONE; + argv++; + } + while (*argv) { - char *colon = strchr(*argv, ':'); - if (!colon) + char *colon; + long vaddr = strtol(*argv, &colon, 16); + if (*colon != ':') error_msg_and_die("Bad format: '%s'", *argv); - *colon = '\0'; - unsigned n = atoi(*argv); - *colon = ':'; - if (_UCD_add_backing_file_at_segment(ui, n, colon + 1) < 0) - error_msg_and_die("Can't add backing file '%s'", *argv); + if (_UCD_add_backing_file_at_vaddr(ui, vaddr, colon + 1) < 0) + error_msg_and_die("Can't add backing file '%s'", colon + 1); argv++; } @@ -305,11 +319,19 @@ main(int argc, char **argv) ret = unw_get_proc_info(&c, &pi); if (ret < 0) error_msg_and_die("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret); - printf("\tip=0x%08lx proc=%08lx-%08lx handler=0x%08lx lsda=0x%08lx\n", + + if (!testcase) + printf("\tip=0x%08lx proc=%08lx-%08lx handler=0x%08lx lsda=0x%08lx\n", (long) ip, (long) pi.start_ip, (long) pi.end_ip, (long) pi.handler, (long) pi.lsda); + if (testcase && test_cur < TEST_FRAMES) + { + test_start_ips[test_cur] = (long) pi.start_ip; + test_cur++; + } + log("step"); ret = unw_step(&c); log("step done:%d", ret); @@ -320,6 +342,20 @@ main(int argc, char **argv) } log("stepping ended"); + /* Check that the second and third frames are equal, but distinct of the + * others */ + if (testcase && + (test_cur != 4 + || test_start_ips[1] != test_start_ips[2] + || test_start_ips[0] == test_start_ips[1] + || test_start_ips[2] == test_start_ips[3] + ) + ) + { + fprintf(stderr, "FAILURE: start IPs incorrect\n"); + return -1; + } + _UCD_destroy(ui); return 0; -- 1.7.7.6 _______________________________________________ Libunwind-devel mailing list [email protected] https://lists.nongnu.org/mailman/listinfo/libunwind-devel
