summaryrefslogtreecommitdiff
path: root/tests/backtrace-child.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/backtrace-child.c')
-rw-r--r--tests/backtrace-child.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/tests/backtrace-child.c b/tests/backtrace-child.c
new file mode 100644
index 00000000..2bc0eded
--- /dev/null
+++ b/tests/backtrace-child.c
@@ -0,0 +1,221 @@
+/* Test child for parent backtrace test.
+ Copyright (C) 2013 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file 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.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Command line syntax: ./backtrace-child [--ptraceme|--gencore]
+ --ptraceme will call ptrace (PTRACE_TRACEME) in the two threads.
+ --gencore will call abort () at its end.
+ Main thread will signal SIGUSR2. Other thread will signal SIGUSR1.
+ On x86_64 only:
+ PC will get changed to function 'jmp' by backtrace.c function
+ prepare_thread. Then SIGUSR2 will be signalled to backtrace-child
+ which will invoke function sigusr2.
+ This is all done so that signal interrupts execution of the very first
+ instruction of a function. Properly handled unwind should not slip into
+ the previous unrelated function.
+ The tested functionality is arch-independent but the code reproducing it
+ has to be arch-specific.
+ On non-x86_64:
+ sigusr2 gets called by normal function call from function stdarg.
+ On any arch then sigusr2 calls raise (SIGUSR1) for --ptraceme.
+ abort () is called otherwise, expected for --gencore core dump.
+
+ Expected x86_64 output:
+ TID 10276:
+ # 0 0x7f7ab61e9e6b raise
+ # 1 0x7f7ab661af47 - 1 main
+ # 2 0x7f7ab5e3bb45 - 1 __libc_start_main
+ # 3 0x7f7ab661aa09 - 1 _start
+ TID 10278:
+ # 0 0x7f7ab61e9e6b raise
+ # 1 0x7f7ab661ab3c - 1 sigusr2
+ # 2 0x7f7ab5e4fa60 __restore_rt
+ # 3 0x7f7ab661ab47 jmp
+ # 4 0x7f7ab661ac92 - 1 stdarg
+ # 5 0x7f7ab661acba - 1 backtracegen
+ # 6 0x7f7ab661acd1 - 1 start
+ # 7 0x7f7ab61e2c53 - 1 start_thread
+ # 8 0x7f7ab5f0fdbd - 1 __clone
+
+ Expected non-x86_64 (i386) output; __kernel_vsyscall are skipped if found:
+ TID 10408:
+ # 0 0xf779f430 __kernel_vsyscall
+ # 1 0xf7771466 - 1 raise
+ # 2 0xf77c1d07 - 1 main
+ # 3 0xf75bd963 - 1 __libc_start_main
+ # 4 0xf77c1761 - 1 _start
+ TID 10412:
+ # 0 0xf779f430 __kernel_vsyscall
+ # 1 0xf7771466 - 1 raise
+ # 2 0xf77c18f4 - 1 sigusr2
+ # 3 0xf77c1a10 - 1 stdarg
+ # 4 0xf77c1a2c - 1 backtracegen
+ # 5 0xf77c1a48 - 1 start
+ # 6 0xf77699da - 1 start_thread
+ # 7 0xf769bbfe - 1 __clone
+ */
+
+#include <config.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <string.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+#define NOINLINE_NOCLONE __attribute__ ((noinline, noclone))
+#else
+#define NOINLINE_NOCLONE __attribute__ ((noinline))
+#endif
+
+#define NORETURN __attribute__ ((noreturn))
+#define UNUSED __attribute__ ((unused))
+#define USED __attribute__ ((used))
+
+static int ptraceme, gencore;
+
+/* Execution will arrive here from jmp by an artificial ptrace-spawn signal. */
+
+static void
+sigusr2 (int signo)
+{
+ assert (signo == SIGUSR2);
+ if (! gencore)
+ {
+ raise (SIGUSR1);
+ /* It should not be reached. */
+ abort ();
+ }
+ /* Here we dump the core for --gencore. */
+ raise (SIGABRT);
+}
+
+static NOINLINE_NOCLONE void
+dummy1 (void)
+{
+ asm volatile ("");
+}
+
+#ifdef __x86_64__
+static NOINLINE_NOCLONE USED void
+jmp (void)
+{
+ /* Not reached, signal will get ptrace-spawn to jump into sigusr2. */
+ abort ();
+}
+#endif
+
+static NOINLINE_NOCLONE void
+dummy2 (void)
+{
+ asm volatile ("");
+}
+
+static NOINLINE_NOCLONE NORETURN void
+stdarg (int f UNUSED, ...)
+{
+ sighandler_t sigusr2_orig = signal (SIGUSR2, sigusr2);
+ assert (sigusr2_orig == SIG_DFL);
+ errno = 0;
+ if (ptraceme)
+ {
+ long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+ assert_perror (errno);
+ assert (l == 0);
+ }
+#ifdef __x86_64__
+ if (! gencore)
+ {
+ /* Execution will get PC patched into function jmp. */
+ raise (SIGUSR1);
+ }
+#endif
+ sigusr2 (SIGUSR2);
+ /* Not reached. */
+ abort ();
+}
+
+static NOINLINE_NOCLONE void
+dummy3 (void)
+{
+ asm volatile ("");
+}
+
+static NOINLINE_NOCLONE void
+backtracegen (void)
+{
+ stdarg (1);
+ /* Here should be no instruction after the stdarg call as it is noreturn
+ function. It must be stdarg so that it is a call and not jump (jump as
+ a tail-call). */
+}
+
+static NOINLINE_NOCLONE void
+dummy4 (void)
+{
+ asm volatile ("");
+}
+
+static void *
+start (void *arg UNUSED)
+{
+ backtracegen ();
+ /* Not reached. */
+ abort ();
+}
+
+int
+main (int argc UNUSED, char **argv)
+{
+ setbuf (stdout, NULL);
+ assert (*argv++);
+ ptraceme = (*argv && strcmp (*argv, "--ptraceme") == 0);
+ argv += ptraceme;
+ gencore = (*argv && strcmp (*argv, "--gencore") == 0);
+ argv += gencore;
+ assert (!*argv);
+ /* These dummy* functions are there so that each of their surrounding
+ functions has some unrelated code around. The purpose of some of the
+ tests is verify unwinding the very first / after the very last instruction
+ does not inappropriately slip into the unrelated code around. */
+ dummy1 ();
+ dummy2 ();
+ dummy3 ();
+ dummy4 ();
+ if (gencore)
+ printf ("%ld\n", (long) getpid ());
+ pthread_t thread;
+ int i = pthread_create (&thread, NULL, start, NULL);
+ // pthread_* functions do not set errno.
+ assert (i == 0);
+ if (ptraceme)
+ {
+ errno = 0;
+ long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+ assert_perror (errno);
+ assert (l == 0);
+ }
+ if (gencore)
+ pthread_join (thread, NULL);
+ else
+ raise (SIGUSR2);
+ /* Not reached. */
+ abort ();
+}