diff options
author | Jan Kratochvil <jan.kratochvil@redhat.com> | 2012-10-30 18:06:54 +0100 |
---|---|---|
committer | Jan Kratochvil <jan.kratochvil@redhat.com> | 2012-10-30 18:06:54 +0100 |
commit | ede8a69ea4d3757403159b801bbdec8123035cbd (patch) | |
tree | 85e352d64d86e40109b289c2e7e1f9711ae03793 | |
parent | 594e98b48819f3ad83361df728cb72722935658b (diff) | |
download | elfutils-ede8a69ea4d3757403159b801bbdec8123035cbd.tar.gz |
Add sample program for dwfl_frame_state_data.
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/backtrace-data.c | 255 |
2 files changed, 257 insertions, 1 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index df1995a9..b72bcc45 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -52,7 +52,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ test-flag-nobits dwarf-getstring rerequest_tag \ alldts md5-sha1-test typeiter low_high_pc \ test-elf_cntl_gelf_getshdr dwfl-report-elf-align backtrace \ - backtrace-child + backtrace-child backtrace-data asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ asm-tst6 asm-tst7 asm-tst8 asm-tst9 @@ -308,6 +308,7 @@ dwfl_report_elf_align_LDADD = $(libdw) $(libmudflap) backtrace_LDADD = $(libdw) $(libelf) $(libmudflap) backtrace_child_CFLAGS = -fPIE backtrace_child_LDFLAGS = -pie -pthread +backtrace_data_LDADD = $(libdw) $(libelf) $(libmudflap) if GCOV check: check-am coverage diff --git a/tests/backtrace-data.c b/tests/backtrace-data.c new file mode 100644 index 00000000..bdaf99dc --- /dev/null +++ b/tests/backtrace-data.c @@ -0,0 +1,255 @@ +/* Test program for unwinding of frames. + Copyright (C) 2012 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/>. */ + +#include <config.h> +#include <assert.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <locale.h> +#include <dirent.h> +#include <stdlib.h> +#include <errno.h> +#include <error.h> +#include <unistd.h> +#include <dwarf.h> +#include <sys/resource.h> +#include <sys/ptrace.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/user.h> +#include <fcntl.h> +#include <string.h> +#include ELFUTILS_HEADER(dwfl) + +#ifndef __x86_64__ + +int +main (void) +{ + return 77; +} + +#else /* __x86_64__ */ + +static int +find_elf (Dwfl_Module *mod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *modname __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + char **file_name __attribute__ ((unused)), + Elf **elfp __attribute__ ((unused))) +{ + /* Not used as modules are reported explicitly. */ + assert (0); +} + +static bool +memory_read (Dwarf_Addr addr, Dwarf_Addr *result, void *user_data) +{ + pid_t child = (uintptr_t) user_data; + + errno = 0; + long l = ptrace (PTRACE_PEEKDATA, child, (void *) (uintptr_t) addr, NULL); + assert_perror (errno); + *result = l; + + /* We could also return false for failed ptrace. */ + return true; +} + +/* Return filename and VMA address *BASEP where its mapping starts which + contains ADDR. */ + +static char * +maps_lookup (pid_t pid, Dwarf_Addr addr, GElf_Addr *basep) +{ + char *fname; + int i = asprintf (&fname, "/proc/%ld/maps", (long) pid); + assert_perror (errno); + assert (i > 0); + FILE *f = fopen (fname, "r"); + assert_perror (errno); + assert (f); + free (fname); + for (;;) + { + // 37e3c22000-37e3c23000 rw-p 00022000 00:11 49532 /lib64/ld-2.14.90.so */ + unsigned long start, end, offset; + i = fscanf (f, "%lx-%lx %*s %lx %*x:%*x %*x", &start, &end, &offset); + assert_perror (errno); + assert (i == 3); + char *filename = strdup (""); + assert (filename); + size_t filename_len = 0; + for (;;) + { + int c = fgetc (f); + assert (c != EOF); + if (c == '\n') + break; + if (c == ' ' && *filename == '\0') + continue; + filename = realloc (filename, filename_len + 2); + assert (filename); + filename[filename_len++] = c; + filename[filename_len] = '\0'; + } + if (start <= addr && addr < end) + { + i = fclose (f); + assert_perror (errno); + assert (i == 0); + + *basep = start - offset; + return filename; + } + free (filename); + } +} + +/* Add module containing ADDR to the DWFL address space. */ + +static void +report_module (Dwfl *dwfl, pid_t child, Dwarf_Addr addr) +{ + GElf_Addr base; + char *long_name = maps_lookup (child, addr, &base); + Dwfl_Module *mod = dwfl_report_elf_baseaddr (dwfl, long_name, long_name, -1, + base); + assert (mod); + free (long_name); + assert (dwfl_addrmodule (dwfl, addr) == mod); +} + +int +main (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused))) +{ + /* We use no threads here which can interfere with handling a stream. */ + __fsetlocking (stdin, FSETLOCKING_BYCALLER); + __fsetlocking (stdout, FSETLOCKING_BYCALLER); + __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + pid_t child = fork (); + switch (child) + { + case -1: + assert_perror (errno); + assert (0); + case 0:; + long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL); + assert_perror (errno); + assert (l == 0); + raise (SIGUSR1); + assert (0); + default: + break; + } + + int status; + pid_t pid = waitpid (child, &status, 0); + assert_perror (errno); + assert (pid == child); + assert (WIFSTOPPED (status)); + assert (WSTOPSIG (status) == SIGUSR1); + + struct user_regs_struct user_regs; + long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs); + assert_perror (errno); + assert (l == 0); + + const unsigned nregs = 17; + const uint64_t regs_set = (1U << nregs) - 1; + Dwarf_Addr regs[17]; + regs[0] = user_regs.rax; + regs[1] = user_regs.rdx; + regs[2] = user_regs.rcx; + regs[3] = user_regs.rbx; + regs[4] = user_regs.rsi; + regs[5] = user_regs.rdi; + regs[6] = user_regs.rbp; + regs[7] = user_regs.rsp; + regs[8] = user_regs.r8; + regs[9] = user_regs.r9; + regs[10] = user_regs.r10; + regs[11] = user_regs.r11; + regs[12] = user_regs.r12; + regs[13] = user_regs.r13; + regs[14] = user_regs.r14; + regs[15] = user_regs.r15; + regs[16] = user_regs.rip; + + /* x86_64 has PC contained in its CFI subset of DWARF register set so + elfutils will figure out the real PC value from REGS. */ + const bool pc_set = false; + Dwarf_Addr pc = 0; + + void *memory_read_user_data = (void *) (uintptr_t) child; + + static char *debuginfo_path; + static const Dwfl_Callbacks offline_callbacks = + { + .find_debuginfo = dwfl_standard_find_debuginfo, + .debuginfo_path = &debuginfo_path, + .section_address = dwfl_offline_section_address, + .find_elf = find_elf, + }; + Dwfl *dwfl = dwfl_begin (&offline_callbacks); + assert (dwfl); + + report_module (dwfl, child, user_regs.rip); + + Dwfl_Frame_State *state; + state = dwfl_frame_state_data (dwfl, pc_set, pc, nregs, ®s_set, regs, + memory_read, memory_read_user_data); + assert (state != NULL); + + /* Multiple threads are not handled here. */ + do + { + bool minusone; + if (! dwfl_frame_state_pc (state, &pc, &minusone)) + error (1, 0, "dwfl_frame_state_pc: %s", dwfl_errmsg (-1)); + Dwarf_Addr pc_adjusted = pc - (minusone ? 1 : 0); + + printf ("%#" PRIx64 "\n", (uint64_t) pc); + + Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted); + if (mod == NULL) + report_module (dwfl, child, pc_adjusted); + + if (! dwfl_frame_unwind (&state)) + error (1, 0, "dwfl_frame_unwind: %s", dwfl_errmsg (-1)); + } + while (state); + + dwfl_end (dwfl); + kill (child, SIGKILL); + pid = waitpid (child, &status, 0); + assert_perror (errno); + assert (pid == child); + assert (WIFSIGNALED (status)); + assert (WTERMSIG (status) == SIGKILL); + + return EXIT_SUCCESS; +} + +#endif /* x86_64 */ |