libunwind-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Libunwind-devel] [PATCHv2] coredump: add test


From: Martin Milata
Subject: [Libunwind-devel] [PATCHv2] coredump: add test
Date: Thu, 31 May 2012 11:15:40 +0200

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 essentially calls some functions and
then writes to invalid address causing a crash. Before that, it detects
which executables are mapped to which virtual addresses and writes this
information to a file suitable for consumption by test-coredump-unwind.
The mapping information is obtained form /proc/self/maps, so currently
it only works on linux.

The test itself is a shell script, which first runs the program and then
runs test-coredump-unwind on the resulting core and address space
map file to check whether the stack trace obtained from the dump roughly
corresponds to what it should look like.

Signed-off-by: Martin Milata <address@hidden>
---
 .gitignore                   |    2 +
 tests/Makefile.am            |   18 ++++++++---
 tests/crasher.c              |   63 ++++++++++++++++++++++++++++++++++++++++
 tests/run-coredump-unwind    |   20 ++++++++++++
 tests/test-coredump-unwind.c |   66 ++++++++++++++++++++++++++++++++---------
 5 files changed, 149 insertions(+), 20 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..8061b30 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
 
@@ -45,9 +45,8 @@ 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                  \
+                       Ltest-nomalloc Ltest-nocalloc rs-race
+ noinst_PROGRAMS_cdep = forker crasher mapper test-ptrace-misc          \
                        Gperf-simple Lperf-simple
 
 if HAVE_BACKTRACE
@@ -58,6 +57,11 @@ if SUPPORT_CXX_EXCEPTIONS
  check_PROGRAMS_cdep += Ltest-cxx-exceptions
 endif
 
+if OS_LINUX
+ check_SCRIPTS_cdep += run-coredump-unwind
+ noinst_PROGRAMS_cdep += test-coredump-unwind
+endif
+
 perf: perf-startup Gperf-simple Lperf-simple Lperf-trace
        @echo "########## Basic performance of generic libunwind:"
        @./Gperf-simple
@@ -111,6 +115,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..65e1b7e
--- /dev/null
+++ b/tests/crasher.c
@@ -0,0 +1,63 @@
+/* This program should crash and produce coredump */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+void a(void) __attribute__((noinline));
+void b(int x) __attribute__((noinline));
+
+#ifdef __linux__
+void write_maps(char *fname)
+{
+    char buf[512], path[128];
+    char exec;
+    uintmax_t addr;
+    FILE *maps = fopen("/proc/self/maps", "r");
+    FILE *out = fopen(fname, "w");
+
+    if (!maps || !out)
+        exit(EXIT_FAILURE);
+
+    while (fgets(buf, sizeof(buf), maps))
+    {
+        if (sscanf(buf, "%jx-%*jx %*c%*c%c%*c %*x %*s %*d /%126[^\n]", &addr, 
&exec, path+1) != 3)
+            continue;
+
+        if (exec != 'x')
+            continue;
+
+        path[0] = '/';
+        fprintf(out, "0x%jx:%s ", addr, path);
+    }
+    fprintf(out, "\n");
+
+    fclose(out);
+    fclose(maps);
+}
+#else
+#error Port me
+#endif
+
+void a(void)
+{
+  *(int *)NULL = 42;
+}
+
+void b(int x)
+{
+  if (x)
+    a();
+  else
+    b(1);
+}
+
+int
+main (int argc, char **argv)
+{
+  if (argc > 1)
+      write_maps(argv[1]);
+  b(0);
+  return 0;
+}
+
diff --git a/tests/run-coredump-unwind b/tests/run-coredump-unwind
new file mode 100755
index 0000000..18060c5
--- /dev/null
+++ b/tests/run-coredump-unwind
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+TESTDIR=`pwd`
+TEMPDIR=`mktemp -d`
+
+# create core dump
+(
+    cd $TEMPDIR
+    ulimit -c 10000
+    $TESTDIR/crasher $TEMPDIR/backing_files
+) 2>/dev/null
+COREFILE=$TEMPDIR/core*
+
+# fail if any command fails
+set -e
+
+# magic option -testcase enables checking for the specific contents of the 
stack
+./test-coredump-unwind $COREFILE -testcase `cat $TEMPDIR/backing_files`
+
+rm -r -- $TEMPDIR
diff --git a/tests/test-coredump-unwind.c b/tests/test-coredump-unwind.c
index 7bb88a7..96752f2 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
@@ -207,11 +209,11 @@ void handle_sigsegv(int sig, siginfo_t *info, void 
*ucontext)
 
   uc = ucontext;
 #if defined(__linux__)
-#ifdef TARGET_X86
+#ifdef UNW_TARGET_X86
        ip = uc->uc_mcontext.gregs[REG_EIP];
-#elif defined(TARGET_X86_64)
+#elif defined(UNW_TARGET_X86_64)
        ip = uc->uc_mcontext.gregs[REG_RIP];
-#elif defined(TARGET_ARM)
+#elif defined(UNW_TARGET_ARM)
        ip = uc->uc_mcontext.arm_ip;
 #endif
 #elif defined(__FreeBSD__)
@@ -266,6 +268,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], '/');
@@ -275,7 +282,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;
 
@@ -291,16 +298,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++;
     }
 
@@ -315,11 +329,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);
@@ -330,6 +352,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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]