BuildRequires: pkgconfig(rpm)
BuildRequires: cmake
-BuildRequires: pkgconfig(libunwind-ptrace)
+BuildRequires: pkgconfig(libunwind-generic)
%if "%{TIZEN_FEATURE_PTRACE_CALLSTACK}" == "on"
BuildRequires: libelf-devel libelf
BuildRequires: libebl-devel libebl
CONFIGURE_FILE(crash-manager.h.in crash-manager.h @ONLY)
ADD_EXECUTABLE(${PROJECT_NAME} ${CRASH_MANAGER_SRCS})
-TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${crash-manager_pkgs_LDFLAGS} -pie)
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${crash-manager_pkgs_LDFLAGS} -pie -lrt)
INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
#include <unistd.h>
#include <libgen.h>
#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/procfs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/prctl.h>
char sysassert_cs_path[PATH_MAX];
bool have_sysassert_report;
#endif
+ int prstatus_fd;
} crash_info;
static void get_config(void)
return ret;
}
+static int set_prstatus()
+{
+ int ret;
+ char prstatus_name[NAME_MAX+1];
+
+ ret = snprintf(prstatus_name, NAME_MAX, "/%s.prstatus", crash_info.pid_info);
+ if (ret < 0) {
+ goto close_fd;
+ }
+
+ crash_info.prstatus_fd = shm_open(prstatus_name, O_RDWR | O_CREAT, 0600);
+ if (crash_info.prstatus_fd < 0) {
+ goto close_fd;
+ }
+
+ ret = shm_unlink(prstatus_name);
+ if (ret < 0) {
+ goto close_fd;
+ }
+
+ ret = fcntl(crash_info.prstatus_fd, F_GETFD);
+ if (ret < 0) {
+ goto close_fd;
+ }
+
+ ret = fcntl(crash_info.prstatus_fd, F_SETFD, ret & ~FD_CLOEXEC);
+ if (ret < 0) {
+ goto close_fd;
+ }
+
+ ret = ftruncate(crash_info.prstatus_fd, sizeof(struct elf_prstatus));
+ if (ret < 0) {
+ goto close_fd;
+ }
+
+ return 0;
+
+close_fd:
+ if (crash_info.prstatus_fd >= 0)
+ close(crash_info.prstatus_fd);
+ return -1;
+}
+
static int set_crash_info(int argc, char *argv[])
{
int ret;
goto rm_temp;
}
#endif
+ if (set_prstatus() < 0)
+ goto rm_temp;
return 0;
#ifdef TIZEN_ENABLE_MINICOREDUMP
char *coredump_name = NULL;
+ char *prstatus_fd_str = NULL;
+
if (asprintf(&coredump_name, "%s.coredump", crash_info.name) == -1)
coredump_name = strdup("core");
+ if (asprintf(&prstatus_fd_str, "%d", crash_info.prstatus_fd) == -1)
+ prstatus_fd_str = strdup("-1");
+
/* Execute minicoredumper */
char *args[] = {
MINICOREDUMPER_PATH, // minicoredumper filename path
crash_info.pfx, // temp dir
"-o",
coredump_name, // coredump filename
+ "-P",
+ prstatus_fd_str,
NULL
};
- _D(" %s", args[0]);
+ _D(" %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s",
+ args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6], args[7],
+ args[8], args[9], args[10], args[11],
+ args[12], args[13], args[14]);
run_command_timeout(args[0], args, NULL, MINICOREDUMPER_TIMEOUT);
free(coredump_name);
+ free(prstatus_fd_str);
#endif
#ifdef TIZEN_FEATURE_PTRACE_CALLSTACK
# ifdef SYS_ASSERT
- /* Use ptrace version as fallback if sys-assert failed to
- * generate report */
+ /* Use process_vm_readv() version as fallback if sys-assert
+ * failed to generate report */
if (crash_info.have_sysassert_report)
return;
# endif
/* Execute crash-stack */
if (argc > 8)
ret = snprintf(command, sizeof(command),
- "%s --pid %s --tid %s --sig %s > %s",
+ "%s --pid %s --tid %s --sig %s --prstatus_fd %d > %s",
CRASH_STACK_PATH,
crash_info.pid_info,
crash_info.tid_info,
crash_info.sig_info,
+ crash_info.prstatus_fd,
crash_info.info_path);
else
ret = snprintf(command, sizeof(command),
- "%s --pid %s --sig %s > %s",
+ "%s --pid %s --sig %s --prstatus_fd %d > %s",
CRASH_STACK_PATH,
crash_info.pid_info,
crash_info.sig_info,
+ crash_info.prstatus_fd,
crash_info.info_path);
+ _D(" %s", command);
if (ret < 0) {
_E("Failed to snprintf for crash-stack command");
return;
launch_crash_popup();
#endif
+ close(crash_info.prstatus_fd);
return 0;
}
set(CRASH_STACK_BIN "crash-stack")
# Common source code files
-set(CRASH_STACK_SRCS crash-stack.c crash-stack-libunw.c)
+set(CRASH_STACK_SRCS crash-stack.c crash-stack-libunw.c unwind.c proc.c mem_map.c)
# Add architecture dependent source files
if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
set(CRASH_STACK_SRCS ${CRASH_STACK_SRCS} crash-stack-stub.c)
endif()
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fPIE")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fPIE -D_GNU_SOURCE=1")
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie")
# Binary
add_executable(${CRASH_STACK_BIN} ${CRASH_STACK_SRCS})
include(FindPkgConfig)
-pkg_check_modules(LIBUNWIND_PTRACE REQUIRED libunwind-ptrace)
-set_property(TARGET ${CRASH_STACK_BIN} APPEND_STRING PROPERTY COMPILE_FLAGS ${LIBUNWIND_PTRACE_CFLAGS_OTHER})
+pkg_check_modules(LIBUNWIND REQUIRED libunwind-generic)
+set_property(TARGET ${CRASH_STACK_BIN} APPEND_STRING PROPERTY COMPILE_FLAGS ${LIBUNWIND_CFLAGS_OTHER})
# Linking
-target_link_libraries(${CRASH_STACK_BIN} ${LIBUNWIND_PTRACE_LIBRARIES} ${EBL_LIBRARY} dw elf dl stdc++)
+target_link_libraries(${CRASH_STACK_BIN} ${LIBUNWIND_LIBRARIES} ${EBL_LIBRARY} elf dl stdc++)
+
# Installing
install(TARGETS ${CRASH_STACK_BIN} DESTINATION libexec)
#include <sys/user.h>
#include <string.h>
-static struct user_regs_struct g_registers; ///< static storage for ptrace buffer for registers
+static struct user_regs_struct g_registers; ///< static storage for registers
/**
* @brief Important registers for unwinding stack on aarch64
typedef struct Regs Regs; ///< convenience type definition
static Regs g_regs; ///< static storage for target register values
-void *_crash_stack_get_memory_for_ptrace_registers(size_t *size)
+void *_crash_stack_get_memory_for_registers(size_t *size)
{
if (NULL != size)
*size = sizeof(g_registers);
return &g_registers;
}
-void _crash_stack_set_ptrace_registers(void *regbuf)
+void _crash_stack_set_registers(void *regbuf)
{
struct user_regs_struct *regs = regbuf;
#include "crash-stack.h"
#include <string.h>
-#include <sys/ptrace.h>
#include <sys/user.h>
// definitions copied from previously used McTernan unwinder
typedef struct Regs Regs; ///< convenience type definition
static Regs g_regs; ///< static storage for target register values
-static struct user_regs g_ptrace_registers; ///< static storage for ptrace buffer for registers
+static struct user_regs g_registers; ///< static storage for registers
-void *_crash_stack_get_memory_for_ptrace_registers(size_t *size)
+void *_crash_stack_get_memory_for_registers(size_t *size)
{
if (NULL != size)
- *size = sizeof(g_ptrace_registers);
- return &g_ptrace_registers;
+ *size = sizeof(g_registers);
+ return &g_registers;
}
void *_get_place_for_register_value(const char *regname, int regnum)
return NULL;
}
-void _crash_stack_set_ptrace_registers(void *regbuf)
+void _crash_stack_set_registers(void *regbuf)
{
struct user_regs *registers = regbuf;
int i;
unsigned long _get_register_value(int n)
{
- return g_ptrace_registers.uregs[n];
+ return g_registers.uregs[n];
}
void _crash_stack_print_regs(FILE* outputfile)
{
fprintf(outputfile, "\nRegister Information\n");
fprintf(outputfile, "r0 = 0x%08lx, r1 = 0x%08lx\nr2 = 0x%08lx, r3 = 0x%08lx\n",
- g_ptrace_registers.uregs[0], g_ptrace_registers.uregs[1],
- g_ptrace_registers.uregs[2], g_ptrace_registers.uregs[3]);
+ g_registers.uregs[0], g_registers.uregs[1],
+ g_registers.uregs[2], g_registers.uregs[3]);
fprintf(outputfile, "r4 = 0x%08lx, r5 = 0x%08lx\nr6 = 0x%08lx, r7 = 0x%08lx\n",
- g_ptrace_registers.uregs[4], g_ptrace_registers.uregs[5],
- g_ptrace_registers.uregs[6], g_ptrace_registers.uregs[7]);
+ g_registers.uregs[4], g_registers.uregs[5],
+ g_registers.uregs[6], g_registers.uregs[7]);
fprintf(outputfile, "r8 = 0x%08lx, r9 = 0x%08lx\nr10 = 0x%08lx, fp = 0x%08lx\n",
- g_ptrace_registers.uregs[8], g_ptrace_registers.uregs[9],
- g_ptrace_registers.uregs[10], g_ptrace_registers.uregs[REG_FP]);
+ g_registers.uregs[8], g_registers.uregs[9],
+ g_registers.uregs[10], g_registers.uregs[REG_FP]);
fprintf(outputfile, "ip = 0x%08lx, sp = 0x%08lx\nlr = 0x%08lx, pc = 0x%08lx\n",
- g_ptrace_registers.uregs[REG_IP], g_ptrace_registers.uregs[REG_SP],
- g_ptrace_registers.uregs[REG_LR], g_ptrace_registers.uregs[REG_PC]);
+ g_registers.uregs[REG_IP], g_registers.uregs[REG_SP],
+ g_registers.uregs[REG_LR], g_registers.uregs[REG_PC]);
/* XXX the label should probably write "spsr" */
- fprintf(outputfile, "cpsr = 0x%08lx\n", g_ptrace_registers.uregs[REG_SPSR]);
+ fprintf(outputfile, "cpsr = 0x%08lx\n", g_registers.uregs[REG_SPSR]);
}
/*
*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2016-2018 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* @file crash-stack-libunw.c
* @brief unwinding call stacks, functions specific for archs that use only libunwind
*/
+#include <libunwind.h>
+#include <limits.h>
+#include <string.h>
+
#include "crash-stack.h"
-#include <libunwind-ptrace.h>
-#define MAXPROCNAMELEN 512
+#define MAXPROCNAMELEN 1024
-void _create_crash_stack(Dwfl *dwfl, pid_t pid, Callstack *callstack)
+void _create_crash_stack(Dwfl *dwfl, pid_t pid, Callstack *callstack, elf_gregset_t *regs)
{
// reimplemented based on libunwind tests/test-ptrace.c file
unw_addr_space_t as = 0;
//init before return
callstack->elems = 0;
- as = unw_create_addr_space(&_UPT_accessors, 0);
+ as = unw_create_addr_space(_TB_accessors(), 0);
if (!as)
break;
- ui = _UPT_create(pid);
+ ui = _TB_create(pid);
if (!ui)
break;
break;
char proc_name[MAXPROCNAMELEN];
+ char mod_name[PATH_MAX];
for (; callstack->elems < sizeof(callstack->proc)/sizeof(callstack->proc[0]); ) {
unw_word_t ip;
callstack->proc[callstack->elems].addr = ip;
proc_name[0] = '\0';
+ mod_name[0] = '\0';
unw_word_t off;
- if (unw_get_proc_name(&cursor, proc_name, sizeof(proc_name), &off) == 0)
+ unw_word_t off_m;
+ if (unw_get_proc_name(&cursor, proc_name, sizeof(proc_name), &off) == 0) {
callstack->proc[callstack->elems].offset = off;
-
+ callstack->proc[callstack->elems].name = strdup(proc_name);
+ } else {
+ callstack->proc[callstack->elems].offset = 0;
+ callstack->proc[callstack->elems].name = NULL;
+ }
+ if (get_mod_name(ip, mod_name, PATH_MAX, &off_m) == 0) {
+ callstack->proc[callstack->elems].module_offset = off_m;
+ callstack->proc[callstack->elems].module_name = strdup(mod_name);
+ } else {
+ callstack->proc[callstack->elems].module_offset = 0;
+ callstack->proc[callstack->elems].module_name = NULL;
+ }
++callstack->elems;
if (unw_step(&cursor) <= 0)
} while (0);
if (ui)
- _UPT_destroy(ui);
+ _TB_destroy(ui);
if (as)
unw_destroy_addr_space(as);
}
#include "crash-stack.h"
-void *_crash_stack_get_memory_for_ptrace_registers(size_t *size)
+void *_crash_stack_get_memory_for_registers(size_t *size)
{
return NULL;
}
-void _crash_stack_set_ptrace_registers(void *regbuf)
+void _crash_stack_set_registers(void *regbuf)
{
}
#include <sys/user.h>
#include <string.h>
-struct user_regs_struct g_registers; ///< static storage for ptrace buffer for registers
+struct user_regs_struct g_registers; ///< static storage for registers
-void *_crash_stack_get_memory_for_ptrace_registers(size_t *size)
+void *_crash_stack_get_memory_for_registers(size_t *size)
{
if (NULL != size)
*size = sizeof(g_registers);
return NULL;
}
-void _crash_stack_set_ptrace_registers(void *regbuf)
+void _crash_stack_set_registers(void *regbuf)
{
/* Make it dummy as it not used for x86 anymore
- * However it is still called by generic __get_registers_ptrace functions
+ * However it is still called by generic __get_registers functions
*/
}
/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2016-2018 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* of a crashed program. Crash-stack must be called with proper arguments:
* PID of a crashed program.
*/
-#include "crash-stack.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
-#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
-#include <elfutils/version.h>
-#include <elfutils/libdwfl.h>
+#include "crash-stack.h"
+
#define BUF_SIZE (BUFSIZ)
#define HEXA 16
OPT_TID,
OPT_SIGNUM,
OPT_OUTPUTFILE,
- OPT_ERRFILE
+ OPT_ERRFILE,
+ OPT_PRSTATUS_FD
};
/**
{ "sig", required_argument, 0, OPT_SIGNUM },
{ "output", required_argument, 0, OPT_OUTPUTFILE },
{ "erroutput", required_argument, 0, OPT_ERRFILE },
+ { "prstatus_fd", required_argument, 0, OPT_PRSTATUS_FD },
{ 0, 0, 0, 0 }
};
static void free_all_nodes(struct addr_node *start);
static char *fgets_fd(char *str, int len, int fd);
-/*
- * __cxa_demangle() is taken from libstdc++, however there is no header that we
- * can take a declaration from. Importing through 'extern' allows using it.
- */
-/// @cond false
-extern char *__cxa_demangle(const char *mangled_name, char *output_buffer,
- size_t *length, int *status);
-///@endcond
-
-/**
- * @brief A callback for dwfl_getmodules().
- *
- * This callback is called once for every module discovered by dwfl_getmodules().
- *
- * @param module the dwfl module
- * @param userdata unused, required by dwfl_getmodules()
- * @param name name of the module
- * @param address address of the module
- * @param arg 4th argument to dwfl_getmodules is passed here
- */
-static int __module_callback(Dwfl_Module *module, void **userdata,
- const char *name, Dwarf_Addr address,
- void *arg)
-{
- return DWARF_CB_OK;
-}
-
-static void __find_symbol_in_elf(ProcInfo *proc_info, Dwarf_Addr mapping_start)
-{
- Elf *elf;
- int fd;
- const char *elf_name = proc_info->module_name;
- Dwarf_Addr address = proc_info->addr;
-
- fd = open(elf_name, O_RDONLY);
- if (-1 == fd)
- return;
-
- elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
-
- if (NULL == elf) {
- close(fd);
- return;
- }
-
- Elf_Scn *scn = NULL;
- int found = 0;
-
- while ((scn = elf_nextscn(elf, scn)) != NULL && !found) {
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr = gelf_getshdr(scn, &shdr_mem);
- if (shdr != NULL && (shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM)) {
- Elf_Data *sdata = elf_getdata(scn, NULL);
- unsigned int nsyms = sdata->d_size / (gelf_getclass(elf) == ELFCLASS32 ?
- sizeof(Elf32_Sym) :
- sizeof(Elf64_Sym));
- unsigned int cnt;
- uintptr_t address_offset = address;
- if (shdr->sh_type == SHT_DYNSYM)
- address_offset -= mapping_start;
- for (cnt = 0; cnt < nsyms; ++cnt) {
- GElf_Sym sym_mem;
- Elf32_Word xndx;
- GElf_Sym *sym = gelf_getsymshndx(sdata, NULL, cnt, &sym_mem, &xndx);
- if (sym != NULL && sym->st_shndx != SHN_UNDEF) {
- if (sym->st_value <= address_offset && address_offset < sym->st_value + sym->st_size) {
- free(proc_info->name);
- proc_info->name = strdup(elf_strptr(elf, shdr->sh_link, sym->st_name));
- proc_info->offset = address_offset - sym->st_value;
- found = 1;
- break;
- }
- }
- }
- }
- }
-
- elf_end(elf);
- close(fd);
-}
-
-static int __attachable(pid_t pid, pid_t tid)
-{
- /* read /proc/<pid>/stat */
- char buf[40];
- FILE *f;
- char status;
-
- snprintf(buf, sizeof(buf), "/proc/%d/task/%d/stat", pid, tid);
-
- f = fopen(buf, "r");
- if (NULL == f)
- return -1;
-
- /* check if status is D */
- if (fscanf(f, "%*d %*s %c", &status) != 1) {
- fclose(f);
- return -1;
- }
-
- fclose(f);
-
- return status != 'D';
-}
-
-static void __print_proc_file(pid_t pid, pid_t tid, const char *name)
-{
- char buf[1024];
- FILE *f;
- int r;
-
- snprintf(buf, sizeof(buf), "/proc/%d/task/%d/%s", pid, tid, name);
-
- fprintf(outputfile, "%s:\n", buf);
-
- f = fopen(buf, "r");
- if (NULL == f) {
- fprintf(errfile, "Failed to open %s: %m\n", buf);
- return;
- }
-
- while ((r = fread(buf, 1, sizeof(buf), f)) > 0)
- fwrite(buf, r, 1, outputfile);
-
- fclose(f);
-
- fprintf(outputfile, "\n");
-}
-
-static void __print_not_attachable_process_info(pid_t pid, pid_t tid)
-{
- fprintf(outputfile, "ERROR: can't attach to process %d, thread %d - thread is in uninterruptible sleep state\n", pid, tid);
- fprintf(outputfile, "Giving some /proc info instead:\n\n");
- __print_proc_file(pid, tid, "wchan");
- fprintf(outputfile, "\n");
- __print_proc_file(pid, tid, "syscall");
- __print_proc_file(pid, tid, "stack");
-}
-
-/**
- * @brief Opens libdwfl for using with live process
- *
- * @param pid pid of the process to attach to
- * @return Dwfl handle
- */
-static Dwfl *__open_dwfl_with_pid(pid_t pid, pid_t tid)
-{
- int status;
- pid_t stopped_pid;
-
- status = __attachable(pid, tid);
- if (-1 == status) {
- fprintf(errfile, "failed to read /proc/%d/task/%d/stat: %m\n", pid, tid);
- return NULL;
- }
-
- if (!status) {
- __print_not_attachable_process_info(pid, tid);
- return NULL;
- }
-
- if (ptrace(PTRACE_SEIZE, tid, NULL, PTRACE_O_TRACEEXIT) != 0) {
- fprintf(errfile, "PTRACE_SEIZE failed on TID %d: %m\n", tid);
- return NULL;
- }
-
- ptrace(PTRACE_INTERRUPT, tid, 0, 0);
-
- stopped_pid = waitpid(tid, &status, __WALL);
- if (stopped_pid == -1 || stopped_pid != tid || !WIFSTOPPED(status)) {
- fprintf(errfile, "waitpid failed: %m, stopped_pid=%d, status=%d\n", stopped_pid, status);
- return NULL;
- }
-
- static const Dwfl_Callbacks proc_callbacks = {
- .find_elf = dwfl_linux_proc_find_elf,
- .find_debuginfo = dwfl_standard_find_debuginfo,
- .section_address = NULL,
- .debuginfo_path = NULL
- };
-
- Dwfl *dwfl = dwfl_begin(&proc_callbacks);
- if (dwfl == NULL) {
- fprintf(errfile, "process %d : Can't start dwfl (%s)\n", tid, dwfl_errmsg(-1));
- return NULL;
- }
-
- if (dwfl_linux_proc_report(dwfl, tid) < 0) {
- fprintf(errfile, "process %d : dwfl report failed (%s)\n", tid, dwfl_errmsg(-1));
- dwfl_end(dwfl);
- return NULL;
- }
-
-#if _ELFUTILS_PREREQ(0, 158)
- if (dwfl_linux_proc_attach(dwfl, tid, true) < 0) {
- fprintf(errfile, "process %d : dwfl attach failed (%s)\n", tid, dwfl_errmsg(-1));
- dwfl_end(dwfl);
- return NULL;
- }
-#endif
- return dwfl;
-}
-
/**
* @brief Gets registers information for live process
*
* @param pid pid of the live process
* @return 0 on success, -1 otherwise
*/
-static int __get_registers_ptrace(pid_t pid)
+static int __get_registers_fd(pid_t pid, int fd)
{
- struct iovec data;
- data.iov_base = _crash_stack_get_memory_for_ptrace_registers(&data.iov_len);
+ struct elf_prstatus *prstatus = NULL; ///< NT_PRSTATUS of the failed process
+ size_t size = 0;
+ char *registers = _crash_stack_get_memory_for_registers(&size);
- if (NULL == data.iov_base) {
- fprintf(errfile, "Cannot get memory for registers for ptrace (not implemented for this architecture\n");
+ if (NULL == registers) {
+ fprintf(errfile, "Cannot get memory for registers (not implemented for this architecture\n");
return -1;
}
- if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &data) != 0) {
- fprintf(errfile, "PTRACE_GETREGSET failed on PID %d: %m\n", pid);
+ prstatus = mmap(NULL, sizeof(struct elf_prstatus),
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (prstatus == MAP_FAILED)
return -1;
- }
- _crash_stack_set_ptrace_registers(data.iov_base);
+ if (prstatus->pr_pid != pid)
+ return -1;
+
+ memcpy(registers, prstatus->pr_reg, size);
return 0;
}
}
/**
- * @brief Resolves procedure and module names using libdwfl
- *
- * @param proc_info gathered call stack element
- * @param dwfl dwfl handler
- */
-static void __resolve_symbols_from_dwfl(ProcInfo *proc_info, Dwfl *dwfl)
-{
- uintptr_t address = proc_info->addr;
- Dwfl_Module *module = dwfl_addrmodule(dwfl, address);
- if (module) {
-
- Dwarf_Addr mapping_start = 0;
- const char *fname = 0;
- const char *module_name = dwfl_module_info(module, NULL, &mapping_start, NULL, NULL, NULL, &fname, NULL);
-
- proc_info->module_offset = address - mapping_start;
-
- if (!proc_info->module_name) {
- if (fname)
- proc_info->module_name = strdup(fname);
- else if (module_name)
- proc_info->module_name = strdup(module_name);
- }
-
- const char *symbol = dwfl_module_addrname(module, address);
- if (symbol) {
- free(proc_info->name);
- proc_info->name = strdup(symbol);
- } else if (proc_info->module_name != NULL)
- __find_symbol_in_elf(proc_info, mapping_start);
- }
-}
-
-/**
- * @brief Checks if symbol starts with '_Z' prefix
- *
- * @param symbol string to compare
- */
-static int is_symbol_demanglable(const char *symbol)
-{
- return symbol != 0 && (strlen(symbol) >= 2) &&
- symbol[0] == '_' && symbol[1] == 'Z';
-}
-
-/**
- * @brief Replaces symbols with demangled
- *
- * @param proc_info gathered call stack element
- */
-static void __demangle_symbols(ProcInfo *proc_info)
-{
- int status = -1;
- char *dem_buffer = NULL;
- char *demangled_symbol = __cxa_demangle(proc_info->name, dem_buffer, NULL, &status);
- if (status == 0) {
- free(proc_info->name);
- proc_info->name = demangled_symbol;
- }
-}
-
-/**
- * @brief Resolves procedure and module name
- *
- * @param proc_info gathered call stack element
- * @param dwfl dwfl handler
- * @param notes notes handler, NULL if live process analyzed
- */
-static void __resolve_symbols(ProcInfo *proc_info, Dwfl *dwfl)
-{
- __resolve_symbols_from_dwfl(proc_info, dwfl);
-
- if (is_symbol_demanglable(proc_info->name))
- __demangle_symbols(proc_info);
-}
-
-/**
* @brief Prints call stack element to the global outputfile.
*
* @param proc_info gathered call stack element
*/
int main(int argc, char **argv)
{
+ int prstatus_fd = -1;
int c;
int signo = 0;
pid_t pid = 0;
pid_t tid = 0;
char bufferfile_path[20] = "/tmp/crash.XXXXXX";
+ char *p = NULL;
while ((c = getopt_long_only(argc, argv, "", opts, NULL)) != -1) {
switch (c) {
case OPT_ERRFILE:
errfile = fopen(optarg, "w");
break;
+ case OPT_PRSTATUS_FD:
+ prstatus_fd = strtol(optarg, &p, 10);
+ if (*p != 0)
+ prstatus_fd = -1;
+ break;
}
}
elf_version(EV_CURRENT);
- /* First, prepare dwfl and modules */
- Dwfl *dwfl = NULL;
-
/* Executable File Path */
__crash_stack_print_exe(outputfile, pid);
/* Signal information */
__crash_stack_print_signal(signo);
- /*
- * Pre-ptrace info
- * Memory, thread, map info should be print in advance
- * because some of them will be lost after ptrace analysis
- */
-
/* Memory information */
__crash_stack_print_meminfo(bufferfile, pid);
/* Maps information */
__crash_stack_print_maps(bufferfile, pid);
- if (pid > 1)
- dwfl = __open_dwfl_with_pid(pid, tid);
- else {
+ if (pid <= 1) {
fprintf(errfile,
"Usage: %s [--output file] [--erroutput file] [--pid <pid> [--tid <tid>]]\n",
argv[0]);
return 1;
}
- if (NULL == dwfl)
- return 1111;
-
- dwfl_getmodules(dwfl, __module_callback, NULL, 0);
-
- if (-1 == __get_registers_ptrace(tid))
+ if (-1 == __get_registers_fd(tid, prstatus_fd))
return 3333;
/* Unwind call stack */
Callstack callstack;
callstack_constructor(&callstack);
- _create_crash_stack(dwfl, tid, &callstack);
- size_t it;
- for (it = 0; it != callstack.elems; ++it)
- __resolve_symbols(&callstack.proc[it], dwfl);
+ _create_crash_stack(NULL, tid, &callstack,
+ _crash_stack_get_memory_for_registers(NULL));
/* Print registers */
_crash_stack_print_regs(outputfile);
- /* Print pre-ptrace info */
+ /* Print info */
__print_buffer_info(bufferfile, outputfile);
/* Print the results */
/* Clean up */
callstack_destructor(&callstack);
- dwfl_report_end(dwfl, NULL, NULL);
- dwfl_end(dwfl);
return 0;
}
/*
*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
#include <stdint.h>
#include <elfutils/libdwfl.h>
+#include <libunwind.h>
+#include <sys/procfs.h>
/**
* @brief callstack.procedure info
* @param dwfl dwfl handle
* @param pid pid of the analyzed process
* @param callstack pointer to call stack database; results are put here
+ * @param regs pointer to registers of the analyzed process
*/
-void _create_crash_stack(Dwfl *dwfl, pid_t pid, Callstack *callstack);
+void _create_crash_stack(Dwfl *dwfl, pid_t pid, Callstack *callstack, elf_gregset_t *regs);
/**
- * @brief Gets allocated space for getting target registers with ptrace().
+ * @brief Gets allocated space for getting target registers.
*
* @remarks This function uses static storage. Thus, it shouldn't be used in multiple threads.
*
* @param[out] size size of the registers structure
* @return address of statically allocated storage of proper size for the registers
*/
-void *_crash_stack_get_memory_for_ptrace_registers(size_t *size);
+void *_crash_stack_get_memory_for_registers(size_t *size);
/**
- * @brief Sets current values of registers from the ptrace buffer
+ * @brief Sets current values of registers from the buffer.
*
- * @param regbuf buffer used for getting registers with ptrace()
+ * @param regbuf buffer used for getting registers
*
- * @see _crash_stack_get_memory_for_ptrace_registers()
+ * @see _crash_stack_get_memory_for_registers()
*/
-void _crash_stack_set_ptrace_registers(void *regbuf);
+void _crash_stack_set_registers(void *regbuf);
/**
- * @brief Print values of registers from the ptrace buffer
+ * @brief Return a value of a register from the buffer
+ *
+ * @param n libunwind register index, e.g. UNW_X86_EAX
+ *
+ * @return the value of the register
+ */
+unsigned long _get_register_value(int n);
+
+/**
+ * @brief Print values of registers from the buffer
*
* @param outputfile output stream
*/
void _crash_stack_print_regs(FILE* outputfile);
+
+/**
+ * @brief Return the set of ptrace(2)-free accessors for libunwind
+ *
+ * @return pointer to unw_accessors_t structure
+ */
+unw_accessors_t *_TB_accessors();
+
+/**
+ * @brief Create a context for analysis
+ *
+ * @param pid pid of the analyzed process
+ *
+ * @return a pointer to pass as the third argument for unw_inint_remote()
+ */
+void *_TB_create(pid_t pid);
+
+/**
+ * @brief Free any resources allocated by _TB_create()
+ *
+ * @param ptr pointer returned by _TB_create()
+ */
+void _TB_destroy(void *ptr);
+
+/**
+ * @brief Find the name of the module and the offset for the address
+ *
+ * @param addr the address of the code to find the module for
+ * @param bufp the output buffer for the module name
+ * @param buf_len the size of the buffer
+ * @param offp output the offset of the code inside the module
+ *
+ * @return zero on success
+ */
+int get_mod_name(unw_word_t addr, char *bufp,
+ size_t buf_len, unw_word_t *offp);
#endif /* CRASH_STACK_H */
-/*
- * tbstack -- fast stack trace utility
- *
+/* -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* Copyright (c) 2014, Tbricks AB
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
#define VSYSCALL_START (-10UL << 20)
#endif
-extern int opt_verbose;
-extern int opt_ignore_deleted;
+// extern int opt_verbose;
+// extern int opt_ignore_deleted;
+static int opt_verbose = 1;
+static int opt_ignore_deleted = 0;
+
+static void mem_region_print(const struct mem_region *region);
static int in(const void *point, const void *start, size_t size)
{
region->next = NULL;
}
-static void mem_region_add_label(struct mem_region *region,
+static int mem_region_add_label(struct mem_region *region,
void *label, size_t reserve)
{
size_t i;
- if (region->labels == NULL)
- region->labels = malloc(sizeof(void *)*reserve);
+ if (region->labels == NULL &&
+ (region->labels = malloc(sizeof(void *)*reserve)) == NULL)
+ return -1;
for (i = 0; i < region->num_labels; ++i) {
if (region->labels[i] > label) {
region->labels[i] = label;
++region->num_labels;
+ mem_region_print(region);
+ return 0;
}
static int mem_region_add_data_chunk(struct mem_region *region,
for (i = 0; i < region->num_data_chunks; ++i) {
if (in(chunk->start, (*cur)->start, (*cur)->length) ||
- in(chunk_ceil, (*cur)->start, (*cur)->length))
- {
fprintf(stderr, "error: overlapping chunks: existing: %p-%p "
"new: %p-%p\n",
(*cur)->start,
(*cur)->start + (*cur)->length,
chunk->start,
chunk_ceil);
+ in(chunk_ceil, (*cur)->start, (*cur)->length)) {
return -1;
}
if ((*cur)->start > chunk->start)
int rc;
chunk = malloc(sizeof(struct mem_data_chunk));
+ if (chunk == NULL)
+ return NULL;
mem_data_chunk_init(chunk);
chunk->start = start;
rc = posix_memalign((void **)&chunk->data, align, chunk->length);
if (rc < 0) {
perror("posix_memalign");
+ int err = errno;
+ free(chunk);
return NULL;
}
static void mem_region_print(const struct mem_region *region)
{
+ int i;
fprintf(stderr,
"region addr: %zx-%zx len: %zx off: %zx num_chunks: %zd "
- "path='%s' fd=%d type=%s\n",
+ "path='%s' fd=%d type=%s num_labels=%zd labels=[",
(size_t)region->start,
(size_t)region->start+region->length,
region->length,
region->num_data_chunks,
region->path,
region->fd,
- str_mem_region_type(region->type));
+ str_mem_region_type(region->type),
+ region->num_labels);
+ for (i = 0; i < region->num_labels; i++) {
+ fprintf(stderr, "%p%s",
+ region->labels[i],
+ (region->num_labels > i + 1) ? ", " : "");
+ }
+ fprintf(stderr,"]\n");
}
-static void mem_region_create_data_chunk_index(struct mem_region *region)
+static int mem_region_create_data_chunk_index(struct mem_region *region)
{
int i;
struct mem_data_chunk *cur;
if (!region->num_data_chunks)
- return;
+ return -1;
- region->data_index = malloc(sizeof(
- struct mem_data_chunk*) * region->num_data_chunks);
+ region->data_index = malloc(sizeof(struct mem_data_chunk*) *
+ region->num_data_chunks);
+ if (region->data_index == NULL) {
+ return -1;
+ }
cur = region->data_head;
for (i = 0; cur != NULL; cur = cur->next) {
region->data_index[i++] = cur;
fprintf(stderr, "region %p: num_data_chunks=%zd but cur != NULL\n",
region, region->num_data_chunks);
mem_region_print(region);
- break;
+ return -1;
}
}
+ return 0;
}
static char *addr_increment_clamped(char *start, char *end, size_t increment)
region_end = (char *)region->start + region->length;
cur_start = region->labels[i];
- cur_end = addr_increment_clamped(cur_start, region_end, generic_chunk_size);
+ cur_end = addr_increment_clamped(cur_start, region_end,
+ generic_chunk_size);
for (++i; i < region->num_labels; ++i) {
if ((size_t)region->labels[i] <= (size_t)cur_end) {
++n;
}
- mem_region_create_data_chunk_index(region);
+ if (mem_region_create_data_chunk_index(region) < 0)
+ return 0;
return n;
}
region->fd = open(region->path, O_RDONLY);
if (region->fd < 0) {
- perror(region->path);
+ int err = errno;
+ mem_region_print(region);
return -1;
}
}
region->data_head = malloc(sizeof(struct mem_data_chunk));
+ if (region->data_head == NULL) {
+ int err = errno;
+ munmap(data, length);
+ return -1;
+ }
mem_data_chunk_init(region->data_head);
region->data_head->start = region->start;
region->data_head->data = data;
region->data_head->length = length;
region->data_index = malloc(sizeof(struct mem_data_chunk**));
+ if (region->data_index == NULL){
+ int err = errno;
+ free(region->data_head);
+ munmap(data, length);
+ return -1;
+ }
*region->data_index = region->data_head;
++region->num_data_chunks;
return -1;
region->data_index = malloc(sizeof(struct mem_data_chunk**));
+ if (region->data_index == NULL) {
+ int err = errno;
+ return -1;
+ }
*region->data_index = region->data_head;
++region->num_data_chunks;
static int mem_region_init_vsyscall(struct mem_region *region)
{
region->data_head = malloc(sizeof(struct mem_data_chunk));
+ if (region->data_head == NULL) {
+ int err = errno;
+ return -1;
+ }
mem_data_chunk_init(region->data_head);
region->data_head->start = region->start;
region->data_head->data = (char *)VSYSCALL_START;
region->data_head->length = region->length;
-
region->data_index = malloc(sizeof(struct mem_data_chunk**));
+ if (region->data_index == NULL) {
+ int err = errno;
+ return -1;
+ }
*region->data_index = region->data_head;
++region->num_data_chunks;
case MEM_REGION_TYPE_DELETED:
if (!opt_ignore_deleted)
return -2;
+ break;
case MEM_REGION_TYPE_MMAP:
if (region->fd < 0 && mem_region_map_file(region) < 0)
if (region->data_head == NULL && mem_region_init_vsyscall(region) < 0)
return -1;
break;
-
default:
break;
}
value);
}
-static void mem_region_destroy(struct mem_region *region)
+void mem_region_destroy(struct mem_region *region)
{
if (region->data_head != NULL)
mem_data_chunk_list_destroy(region->data_head, region->type);
if (region == NULL)
return -1;
- mem_region_add_label(region, label, reserve);
- return 0;
+ return mem_region_add_label(region, label, reserve);
}
int mem_map_build_label_cover(struct mem_map *map, size_t generic_chunk_size,
int mem_map_read_word(struct mem_map *map, void *addr, uintptr_t *value)
{
struct mem_region *region;
-
+ int err;
region = mem_map_find_region(map, addr);
if (region == NULL)
return -1;
- return mem_region_read_word(region,
+ err = mem_region_read_word(region,
addr,
value);
+
+ return err;
}
void mem_map_destroy(struct mem_map *map)
-/*
- * tbstack -- fast stack trace utility
- *
+/* -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* Copyright (c) 2014, Tbricks AB
* All rights reserved.
*
void mem_region_init(struct mem_region *region);
+void mem_region_destroy(struct mem_region *region);
+
/*
* mem map
*/
-/*
- * tbstack -- fast stack trace utility
- *
+/* -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* Copyright (c) 2014, Tbricks AB
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#define SYS_process_vm_readv 365
#elif defined(__x86_64)
#define SYS_process_vm_readv 310
+#elif defined(__arm__) || defined(__aarch64__)
+#define SYS_process_vm_readv 376
#else
#error SYS_process_vm_readv is undefined
#endif
/* timeout on waiting for process to stop (us) */
extern int stop_timeout;
-static int sleep_time = 0;
+/* static int sleep_time = 0; */
/* for summary */
int sleep_count = 0;
extern struct timeval freeze_time;
extern struct timeval unfreeze_time;
-extern int opt_proc_mem;
-extern int opt_use_waitpid_timeout;
-extern int opt_verbose;
+/* extern int opt_proc_mem; */
+/* extern int opt_use_waitpid_timeout; */
+/* extern int opt_verbose; */
+
+static int opt_proc_mem = 0;
+static int opt_verbose = 0;
int proc_state(int pid)
{
char c;
int res = -1;
- sprintf(buf, "/proc/%d/status", pid);
+ snprintf(buf, sizeof(buf), "/proc/%d/status", pid);
if ((f = fopen(buf, "r")) == NULL) {
fprintf(stderr, "cannot open %s: %s\n", buf, strerror(errno));
return -1;
return res;
}
-static int proc_stopped(int pid)
-{
- int c = proc_state(pid);
- if (c == -1)
- return -1;
-
- return (c == 't' || c == 'T');
-}
-
struct mem_map *create_maps(int pid)
{
- FILE *f;
+ FILE *f = NULL;
char *buf = NULL, *str = NULL;
- size_t total_read, capacity;
+ size_t total_read, capacity, count;
size_t addr_start, addr_end, offset, len;
char r, w, x, p;
capacity = 0x100000;
buf = calloc(1, capacity);
-
- sprintf(buf, "/proc/%d/maps", pid);
+ if (buf == NULL) {
+ return NULL;
+ }
+ snprintf(buf, capacity, "/proc/%d/maps", pid);
if ((f = fopen(buf, "r")) == NULL) {
fprintf(stderr, "cannot open %s: %s\n", buf, strerror(errno));
- return NULL;
+ goto create_maps_end;
}
map = malloc(sizeof(struct mem_map));
+ if (map == NULL){
+ goto create_maps_end;
+ }
mem_map_init(map);
memset(buf, 0, capacity);
total_read = 0;
while (!feof(f)) {
- fread(&buf[total_read], capacity - total_read - 1, 1, f);
- if (errno) {
+ count = fread(&buf[total_read], capacity - total_read - 1, 1, f);
+ if (count <1 && ferror(f)) {
+ clearerr(f);
perror("maps");
mem_map_destroy(map);
map = NULL;
if ((total_read + 1) == capacity) {
capacity *= 2;
buf = realloc(buf, capacity);
+ if (buf == NULL) {
+ mem_map_destroy(map);
+ map = NULL;
+ goto create_maps_end;
+ }
memset(&buf[total_read], 0, capacity - total_read);
} else {
buf[total_read] = '\0';
}
region = malloc(sizeof(struct mem_region));
+ if (region == NULL) {
+ mem_map_destroy(map);
+ map = NULL;
+ break;
+ }
mem_region_init(region);
region->start = (void *)addr_start;
}
if (mem_map_add_region(map, region) != 0) {
+ mem_region_destroy(region);
mem_map_destroy(map);
map = NULL;
break;
mem_map_create_region_index(map);
create_maps_end:
- fclose(f);
- free(buf);
+ if (f != NULL)
+ fclose(f);
+ if (buf != NULL)
+ free(buf);
return map;
}
int print_proc_maps(int pid)
{
char cmd[32];
- sprintf(cmd, "cat /proc/%d/maps 1>&2", pid);
+ snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps 1>&2", pid);
return system(cmd);
}
return nr_tids;
}
-int attach_process(int pid)
-{
- int status = 0;
-
- gettimeofday(&freeze_time, NULL);
-
- attached_pid = pid;
- if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
- perror("attach");
- detach_process(pid);
- return -1;
- }
- if (!proc_stopped(pid)) {
- struct itimerval tm;
-
- if (opt_use_waitpid_timeout) {
- /* setup alarm to avoid long waiting on waitpid */
- tm.it_interval.tv_sec = 0;
- tm.it_interval.tv_usec = 0;
- tm.it_value.tv_sec = 1;
- tm.it_value.tv_usec = stop_timeout % 1000000;
- setitimer(ITIMER_REAL, &tm, NULL);
- }
-
- if (waitpid(pid, &status, WUNTRACED) < 0) {
- if (errno == EINTR) {
- fprintf(stderr, "timeout on waitpid\n");
- detach_process(pid);
- return -1;
- }
- fprintf(stderr, "waitpid %d: %s\n", pid, strerror(errno));
- detach_process(pid);
- return -1;
- }
-
- if (opt_use_waitpid_timeout) {
- tm.it_value.tv_sec = 0;
- tm.it_value.tv_usec = 0;
- setitimer(ITIMER_REAL, &tm, NULL);
- }
-
- if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
- fprintf(stderr, "warning: waitpid(%d) WIFSTOPPED=%d WSTOPSIG=%d\n",
- pid, WIFSTOPPED(status), WSTOPSIG(status));
- }
- if (kill(pid, SIGSTOP) < 0) {
- perror("send SIGSTOP");
- return -1;
- }
- return 0;
-}
-
-int attach_thread(int tid)
-{
- if (ptrace(PTRACE_ATTACH, tid, NULL, NULL) < 0) {
- perror("PTRACE_ATTACH");
- return -1;
- }
- if (wait_thread(tid) < 0)
- return -1;
- return 0;
-}
-
-int detach_process(int pid)
-{
- int rc = 0;
- if (ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {
- perror("detach");
- rc = -1;
- }
- if (kill(pid, SIGCONT) < 0) {
- perror("send SIGCONT");
- rc = -1;
- }
-
- attached_pid = 0;
- gettimeofday(&unfreeze_time, NULL);
- return rc;
-}
-
-int detach_thread(int tid)
-{
- long rc = ptrace(PTRACE_DETACH, tid, NULL, NULL);
- if (rc < 0) {
- perror("PTRACE_DETACH");
- return -1;
- }
- return 0;
-}
-
-int wait_thread(int tid)
-{
- int rc;
- while (!(rc = proc_stopped(tid))) {
- if (stop_timeout && sleep_time > stop_timeout) {
- fprintf(stderr, "timeout waiting for thread %d to stop", tid);
- return -1;
- }
- usleep(SLEEP_WAIT);
- sleep_time += SLEEP_WAIT;
- sleep_count++;
- }
- return (rc == -1 ? -1 : 0);
-}
-
/*
* copy memory contents using process_vm_readv(). reduces number
* of system calls comparing to /proc/pid/mem
int fd;
int rc = -1;
- sprintf(fname, "/proc/%d/mem", pid);
+ snprintf(fname, sizeof(fname), "/proc/%d/mem", pid);
if ((fd = open(fname, O_RDONLY)) == -1) {
fprintf(stderr, "cannot open %s\n", fname);
perror(fname);
ssize_t rd = pread(fd, to, count, from);
if (rd == -1) {
- fprintf(stderr, "pread() at %s:0x%jx (#%d) failed: %s [%d]\n",
+ fprintf(stderr, "pread() at %s:0x%lx (#%d) failed: %s [%d]\n",
fname, from, i, strerror(errno), errno);
goto proc_mem_end;
}
fclose(f);
return NULL;
}
-
-void quit_handler(int signum)
-{
- /*
- * We can't call PTRACE_DETACH here because we are in a signal handler.
- * Additionally ptrace will automatically detach when this process exits at
- * the end of this function. We do however always need to send the SIGCONT
- * if we have ptrace attached because when the ptrace automatically
- * detaches it will leave the process in a stopped state even if we had not
- * yet sent SIGSTOP to it.
- */
- if (attached_pid)
- kill(attached_pid, SIGCONT);
- if (signum == SIGSEGV) {
- static volatile int *n = NULL;
- *n = 1969;
- }
- _exit(1);
-}
-/*
- * tbstack -- fast stack trace utility
- *
+/* -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* Copyright (c) 2014, Tbricks AB
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
*/
void *get_vdso(void);
-/*
- * detach from process and send SIGCONT when interrupt/termination occurs
- */
-void quit_handler(int signum);
-
#endif
-/*
- * tbstack -- fast stack trace utility
- *
+/* -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* Copyright (c) 2014, Tbricks AB
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* DAMAGE.
*/
-#include "mem_map.h"
-#include "snapshot.h"
-#include "unwind.h"
-
#include <dwarf.h>
#include <gelf.h>
#include <libelf.h>
#include <libgen.h>
#include <libunwind.h>
-#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
#include <sys/user.h>
+#include <unistd.h>
+
+#include "mem_map.h"
+#include "proc.h"
+#include "crash-stack.h"
+
+size_t stack_size = 0xa00000;
/*
* some libelf implementations do not provide ELF_C_READ_MMAP
#define TBSTACK_ELF_C_READ ELF_C_READ
#endif
+#define to_unw_word(p) ((unw_word_t) (uintptr_t) (p))
+
/*
* search unwind table for a procedure (used by find_proc_info)
*/
+#if defined(__arm__)
+#define search_unwind_table UNW_OBJ(search_unwind_table)
+#else
#define search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
+#endif
extern int search_unwind_table(unw_addr_space_t as, unw_word_t ip,
unw_dyn_info_t *di, unw_proc_info_t *pip,
int need_unwind_info, void *arg);
+static struct mem_map *map = NULL;
+static unsigned long eip = 0;
+static unsigned long esp = 0;
+static FILE *debug;
+
+static Elf *elf_start(int fd, char *image, uint64_t size)
+{
+ Elf *elf;
+
+ if (fd > 0) {
+ if ((elf = elf_begin(fd, TBSTACK_ELF_C_READ, NULL)) == NULL)
+ fprintf(stderr, "error:elf_begin: %s\n", elf_errmsg(elf_errno()));
+ } else {
+ if ((elf = elf_memory(image, size)) == NULL)
+ fprintf(stderr, "error:elf_memory: %s\n", elf_errmsg(elf_errno()));
+ }
-#ifdef HAVE_DWARF
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+ return elf;
+}
-extern int dwarf_find_debug_frame(int found,
- unw_dyn_info_t *di_debug,
- unw_word_t ip,
- unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-#endif
+#if defined(__arm__)
+/*
+ * find section .ARM.exidx in ELF binary
+ */
+static int find_exidx(int fd, char *image, uint64_t size,
+ uint64_t *table_data, uint64_t *table_len)
+{
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ Elf_Scn *scn = NULL;
+ GElf_Shdr shdr;
+ uint64_t offset = 0;
+
+ if ((elf = elf_start(fd, image, size)) == NULL)
+ return -1;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ fprintf(stderr, "error:elf_getehdr: %s\n", elf_errmsg(elf_errno()));
+ goto find_exidx_end;
+ }
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ fprintf(stderr, "error:elf_getshdr: %s\n", elf_errmsg(elf_errno()));
+ break;
+ }
+ if (shdr.sh_type == SHT_ARM_EXIDX) {
+ Elf_Data *data = NULL;
+ if ((data = elf_getdata(scn, data)) == NULL) {
+ fprintf(stderr, "error:elf_getdata: %s\n", elf_errmsg(elf_errno()));
+ break;
+ }
+
+ offset = *table_data = shdr.sh_offset;
+ *table_len = shdr.sh_size;
+ }
+ }
+
+find_exidx_end:
+ elf_end(elf);
+ return (offset ? 0 : -1);
+}
+#else /* __arm__ */
/*
* get dwarf encoded value
*/
pos += 4;
if (version != 1) {
- fprintf(stderr, "unknown .ehf_frame_hdr version %d\n", version);
+ fprintf(stderr, "error:unknown .ehf_frame_hdr version %d\n", version);
return -1;
}
return 0;
}
-static Elf *elf_start(int fd, char *image, uint64_t size)
-{
- Elf *elf;
-
- if (fd > 0) {
- if ((elf = elf_begin(fd, TBSTACK_ELF_C_READ, NULL)) == NULL)
- fprintf(stderr, "elf_begin: %s\n", elf_errmsg(elf_errno()));
- } else {
- if ((elf = elf_memory(image, size)) == NULL)
- fprintf(stderr, "elf_memory: %s\n", elf_errmsg(elf_errno()));
- }
-
- return elf;
-}
-
/*
* find section .eh_frame_hdr in ELF binary
*/
char *str;
if (gelf_getshdr(scn, &shdr) == NULL) {
- fprintf(stderr, "elf_getshdr: %s\n", elf_errmsg(elf_errno()));
+ fprintf(stderr, "elf_nextscn: %s\n", elf_errmsg(elf_errno()));
break;
}
}
}
- if (!offset)
- goto elf_section_offset_end;
-
elf_section_offset_end:
elf_end(elf);
return (offset ? 0 : -1);
}
+#endif /* __arm__ */
+
+static int find_unwind_table(int fd, char *image, uint64_t size,
+ uint64_t *table_data, int *table_format,
+ uint64_t *segbase, uint64_t *table_len)
+{
+#if defined(__arm__)
+ (void) segbase;
+ *table_format = UNW_INFO_FORMAT_ARM_EXIDX;
+ return find_exidx(fd, image, size, table_data, table_len);
+#else
+ (void) table_data;
+ *table_format = UNW_INFO_FORMAT_REMOTE_TABLE;
+ return find_eh_frame_hdr(fd, image, size, table_data, segbase, table_len);
+#endif
+}
+
/*
* dynamic array of symbols
/*
* add a symbol to array
*/
-static void push_symbol(struct symbols *array, const GElf_Sym *s)
+static int push_symbol(struct symbols *array, const GElf_Sym *s)
{
++array->s_size;
if (array->s_size > array->s_cap) {
GElf_Sym *new_data;
array->s_cap <<= 1;
new_data = malloc(sizeof(GElf_Sym) * array->s_cap);
+ if (new_data == NULL) {
+ return -1;
+ }
memcpy(new_data, array->s_data, sizeof(GElf_Sym) * (array->s_size-1));
free(array->s_data);
array->s_data = new_data;
}
memcpy(array->s_data + (array->s_size-1), s, sizeof(GElf_Sym));
+ return 0;
}
/*
all.s_size = 0;
all.s_data = malloc(all.s_cap * sizeof(GElf_Sym));
+ if (all.s_data == NULL)
+ goto proc_name_end;
+
if (elf_getphdrnum (elf, &pnum))
goto proc_name_end;
/*
* save symbol in array
*/
- push_symbol(&all, &s);
+ if (push_symbol(&all, &s) < 0)
+ goto proc_name_end;
}
}
}
return 0;
}
-#ifdef HAVE_DWARF
-static int elf_is_exec(int fd, char *image, uint64_t size)
-{
- Elf *elf;
- GElf_Ehdr ehdr;
- int ret = 0;
-
- if ((elf = elf_start(fd, image, size)) == NULL)
- return 0;
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- fprintf(stderr, "elf_getehdr: %s\n", elf_errmsg(elf_errno()));
- goto elf_is_exec_end;
- }
-
- ret = ehdr.e_type == ET_EXEC;
-
-elf_is_exec_end:
- elf_end(elf);
-
- return ret;
-}
-
-static int elf_get_link_base(int fd, char *image, uint64_t size,
- uint64_t *link_base)
-{
- Elf *elf;
- GElf_Ehdr ehdr;
- GElf_Phdr phdr;
- int idx=0;
- uint64_t offset = UINT64_MAX;
-
- if ((elf = elf_start(fd, image, size)) == NULL)
- return -1;
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- fprintf(stderr, "elf_getehdr: %s\n", elf_errmsg(elf_errno()));
- goto elf_section_offset_end;
- }
-
- /* Get the vaddr of the segment with 0 offset. This is the link base of
- * the shared object. */
- while (gelf_getphdr(elf, idx, &phdr) && phdr.p_type != PT_NULL) {
- if (phdr.p_type != PT_LOAD)
- goto next;
-
- if (phdr.p_offset)
- goto next;
-
- offset = phdr.p_vaddr;
- break;
-
-next:
- idx++;
- }
-
- *link_base = offset;
- elf_end(elf);
- return 0;
-
-elf_section_offset_end:
- elf_end(elf);
- return -1;
-}
-
-#endif
-
/*
* find unwind info for function
*/
static int find_proc_info(unw_addr_space_t as, unw_word_t ip,
unw_proc_info_t *pip, int need_unwind_info, void *arg)
{
- struct snapshot *snap = arg;
struct mem_region *region;
char *elf_image = NULL;
uint64_t elf_length = 0;
unw_dyn_info_t di;
uint64_t table_data = 0;
uint64_t segbase, fde_count;
+ int table_format = -1;
int rc = -UNW_EINVAL;
+ (void) arg;
+
if (ip == 0)
return -UNW_ENOINFO;
- if ((region = mem_map_get_file_region(snap->map, (void *)ip)) == NULL)
+ if ((region = mem_map_get_file_region(map, (void *)ip)) == NULL)
return rc;
if (region->fd < 0 && region->type != MEM_REGION_TYPE_VDSO
- && region->type != MEM_REGION_TYPE_VSYSCALL)
+ && region->type != MEM_REGION_TYPE_VSYSCALL)
return rc;
if (region->fd < 0 &&
- get_elf_image_info(region, &elf_image, &elf_length, ip) < 0)
+ get_elf_image_info(region, &elf_image, &elf_length, ip) < 0)
return rc;
memset(&di, 0, sizeof(di));
- if (!find_eh_frame_hdr(region->fd, elf_image, elf_length,
- &table_data, &segbase, &fde_count)) {
-
- di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
+ if (find_unwind_table(region->fd, elf_image, elf_length,
+ &table_data, &table_format, &segbase,
+ &fde_count) !=0) {
+ rc = -UNW_ENOINFO;
+ } else {
+ di.format = table_format;
di.start_ip = (unw_word_t)region->start;
di.end_ip = (unw_word_t)region->start + region->length;
- di.u.rti.segbase = (unw_word_t)(region->start - region->offset) + segbase;
- di.u.rti.table_data = (unw_word_t)(region->start - region->offset) + table_data;
- di.u.rti.table_len =
- fde_count * sizeof(uint32_t) * 2 / sizeof(unw_word_t);
+ if (table_format == UNW_INFO_FORMAT_REMOTE_TABLE) {
+ di.u.rti.name_ptr = 0;
+ di.u.rti.segbase = (unw_word_t)(region->start - region->offset) + segbase;
+ di.u.rti.table_data = (unw_word_t)(region->start - region->offset) + table_data;
+ di.u.rti.table_len = fde_count * sizeof(uint32_t) * 2 / sizeof(unw_word_t);
+ } else if (table_format == UNW_INFO_FORMAT_ARM_EXIDX) {
+ di.u.rti.name_ptr = to_unw_word(region->path);
+ di.u.rti.table_data = (unw_word_t)region->start + table_data;
+ di.u.rti.table_len = fde_count;
+ }
rc = search_unwind_table(as, ip, &di, pip, need_unwind_info, arg);
}
- if (rc == 0)
- return rc;
-
-#ifdef HAVE_DWARF
- unw_word_t base = 0;
- if (!elf_is_exec(region->fd, elf_image, elf_length)) {
- uint64_t link_base;
- if (elf_get_link_base(region->fd, elf_image, elf_length, &link_base))
- return -UNW_EINVAL;
- base = (uintptr_t)region->start - link_base;
- }
-
- if (dwarf_find_debug_frame(0, &di, ip, base, region->path,
- (unw_word_t) region->start,
- (unw_word_t) region->start + region->length))
- return search_unwind_table(as, ip, &di, pip, need_unwind_info, arg);
-#endif
-
return rc;
}
(void) as;
(void) pip;
(void) arg;
+ fprintf(debug, "%s is not supported\n", __func__);
}
/*
(void) as;
(void) dilap;
(void) arg;
+ fprintf(debug, "%s is not supported\n", __func__);
return -UNW_ENOINFO;
}
static int access_mem(unw_addr_space_t as, unw_word_t addr,
unw_word_t *valp, int write, void *arg)
{
- struct snapshot *snap = arg;
-
(void) as;
+ (void) arg;
if (write) {
fprintf(stderr, "access_mem: requested write, rejecting\n");
return -UNW_EINVAL;
}
- return mem_map_read_word(snap->map, (void *)(uintptr_t)addr, valp);
+ return mem_map_read_word(map, (void *)(uintptr_t)addr, valp);
}
/*
static int access_reg(unw_addr_space_t as, unw_regnum_t reg,
unw_word_t *val, int write, void *arg)
{
- struct snapshot *snap = arg;
- (void) as;
+ (void) as;
+ (void) val;
+ (void) write;
+ (void) arg;
- if (write) {
- fprintf(stderr, "requested to write into register\n");
- return -UNW_EINVAL;
- }
+ fprintf(debug, "debug: requested register %d\n", reg);
+ fflush(debug);
- switch (reg) {
-#if defined(UNW_TARGET_AARCH64)
- case UNW_AARCH64_X0 ... UNW_AARCH64_X30:
- /*
- * Currently this enum directly maps to the index so this is a no-op.
- * Assert just in case.
- */
- reg -= UNW_AARCH64_X0;
- assert(reg>= 0 && reg <= 30);
- *val = snap->regs[snap->cur_thr].regs[reg];
- break;
- case UNW_AARCH64_SP:
- *val = snap->regs[snap->cur_thr].sp;
- break;
- case UNW_AARCH64_PC:
- *val = snap->regs[snap->cur_thr].pc;
- break;
- case UNW_AARCH64_PSTATE:
- *val = snap->regs[snap->cur_thr].pstate;
- break;
-#elif defined(UNW_TARGET_ARM)
- case UNW_ARM_R0 ... UNW_ARM_R15:
- /*
- * Currently this enum directly maps to the index so this is a no-op.
- * Assert just in case.
- */
- reg -= UNW_ARM_R0;
- assert(reg >= 0 && reg <= 15);
- *val = snap->regs[snap->cur_thr].uregs[reg];
- break;
-#elif defined(UNW_TARGET_X86)
- case UNW_X86_EAX:
- *val = snap->regs[snap->cur_thr].eax;
- break;
- case UNW_X86_EDX:
- *val = snap->regs[snap->cur_thr].edx;
- break;
- case UNW_X86_ECX:
- *val = snap->regs[snap->cur_thr].ecx;
- break;
- case UNW_X86_EBX:
- *val = snap->regs[snap->cur_thr].ebx;
- break;
- case UNW_X86_ESI:
- *val = snap->regs[snap->cur_thr].esi;
- break;
- case UNW_X86_EDI:
- *val = snap->regs[snap->cur_thr].edi;
- break;
- case UNW_X86_EBP:
- *val = snap->regs[snap->cur_thr].ebp;
- break;
- case UNW_X86_ESP:
- *val = snap->regs[snap->cur_thr].esp;
- break;
- case UNW_X86_EIP:
- *val = snap->regs[snap->cur_thr].eip;
- break;
-#elif defined(UNW_TARGET_X86_64)
- case UNW_X86_64_RAX:
- *val = snap->regs[snap->cur_thr].rax;
- break;
- case UNW_X86_64_RDX:
- *val = snap->regs[snap->cur_thr].rdx;
- break;
- case UNW_X86_64_RCX:
- *val = snap->regs[snap->cur_thr].rcx;
- break;
- case UNW_X86_64_RBX:
- *val = snap->regs[snap->cur_thr].rbx;
- break;
- case UNW_X86_64_RSI:
- *val = snap->regs[snap->cur_thr].rsi;
- break;
- case UNW_X86_64_RDI:
- *val = snap->regs[snap->cur_thr].rdi;
- break;
- case UNW_X86_64_RBP:
- *val = snap->regs[snap->cur_thr].rbp;
- break;
- case UNW_X86_64_RSP:
- *val = snap->regs[snap->cur_thr].rsp;
- break;
- case UNW_X86_64_R8:
- *val = snap->regs[snap->cur_thr].r8;
- break;
- case UNW_X86_64_R9:
- *val = snap->regs[snap->cur_thr].r9;
- break;
- case UNW_X86_64_R10:
- *val = snap->regs[snap->cur_thr].r10;
- break;
- case UNW_X86_64_R11:
- *val = snap->regs[snap->cur_thr].r11;
- break;
- case UNW_X86_64_R12:
- *val = snap->regs[snap->cur_thr].r12;
- break;
- case UNW_X86_64_R13:
- *val = snap->regs[snap->cur_thr].r13;
- break;
- case UNW_X86_64_R14:
- *val = snap->regs[snap->cur_thr].r14;
- break;
- case UNW_X86_64_R15:
- *val = snap->regs[snap->cur_thr].r15;
- break;
- case UNW_X86_64_RIP:
- *val = snap->regs[snap->cur_thr].rip;
- break;
-#else
-#error Need porting to this arch
-#endif
- default:
- return -UNW_EBADREG;
- }
+ if (write) {
+ fprintf(debug, "requested to write into register\n");
+ return -UNW_EINVAL;
+ }
- return 0;
+ *val = _get_register_value(reg);
+ return 0;
+}
+
+/*
+ * __cxa_demangle() is taken from libstdc++, however there is no header that we
+ * can take a declaration from. Importing through 'extern' allows using it.
+ */
+/// @cond false
+extern char *__cxa_demangle(const char *mangled_name, char *output_buffer,
+ size_t *length, int *status);
+///@endcond
+
+/**
+ * @brief Checks if symbol starts with '_Z' prefix
+ *
+ * @param symbol string to compare
+ */
+static int is_symbol_demanglable(const char *symbol)
+{
+ return symbol != 0 && (strlen(symbol) >= 2) &&
+ symbol[0] == '_' && symbol[1] == 'Z';
+}
+
+/**
+ * @brief Replaces symbols with demangled
+ *
+ * @param proc_info gathered call stack element
+ */
+static void demangle_symbol(char **symbol)
+{
+ int status = -1;
+ char *demangled_symbol = NULL;
+ if (!is_symbol_demanglable(*symbol))
+ return;
+ demangled_symbol = __cxa_demangle(*symbol, NULL, NULL, &status);
+ if (status == 0) {
+ free(*symbol);
+ *symbol = demangled_symbol;
+ }
+ return;
+}
+
+/**
+ * @brief Find the name of the module and the offset for the address
+ *
+ * @param addr the address of the code to find the module for
+ * @param bufp the output buffer for the module name
+ * @param buf_len the size of the buffer
+ * @param offp output the offset of the code inside the module
+ *
+ * @return zero on success
+ */
+int get_mod_name(unw_word_t addr, char *bufp,
+ size_t buf_len, unw_word_t *offp)
+{
+ struct mem_region *region;
+
+ region = mem_map_get_file_region(map, (void *)addr);
+ if (region == NULL || region->path == NULL || region->path[0] == '\0')
+ return -1;
+
+ strncpy(bufp, region->path, buf_len);
+ bufp[buf_len - 1] = '\0';
+ *offp = addr - (uintptr_t)region->start;
+ return 0;
}
/*
(void) write;
(void) arg;
- fprintf(stderr, "access_fpreg is not supported\n");
+ fprintf(debug, "access_fpreg is not supported\n");
+ fflush(debug);
return -UNW_ENOINFO;
}
(void) cp;
(void) arg;
- fprintf(stderr, "resume is not supported\n");
+ fprintf(debug, "resume is not supported\n");
+ fflush(debug);
return -UNW_ENOINFO;
}
static int get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp,
size_t buf_len, unw_word_t *offp, void *arg)
{
- struct snapshot *snap = arg;
struct mem_region *region;
char *name = NULL;
(void) as;
+ (void) arg;
if (addr == 0)
return -UNW_ENOINFO;
- if ((region = mem_map_get_file_region(snap->map, (void *)addr)) == NULL)
+ if ((region = mem_map_get_file_region(map, (void *)addr)) == NULL)
return -UNW_ENOINFO;
if (region->fd < 0 && region->type == MEM_REGION_TYPE_DELETED) {
name = proc_name(region->fd, elf_image, elf_length,
(uint64_t)(uintptr_t)region->start, region->offset, addr, offp);
+ demangle_symbol(&name);
}
if (name == NULL) {
* if name cannot be resolved, print binary file name or region type
*/
if (region->type == MEM_REGION_TYPE_MMAP) {
- const char *base = basename(region->path);
- snprintf(bufp, buf_len, "?? (%s)", base);
+ return -UNW_ENOINFO;
} else {
snprintf(bufp, buf_len, "?? [%s]",
- str_mem_region_type(region->type));
+ str_mem_region_type(region->type));
}
*offp = 0;
return 0;
/*
* libunwind remote callbacks
*/
-unw_accessors_t snapshot_addr_space_accessors = {
+static unw_accessors_t snapshot_addr_space_accessors = {
.find_proc_info = find_proc_info,
.put_unwind_info = put_unwind_info,
.get_dyn_info_list_addr = get_dyn_info_list_addr,
.resume = resume,
.get_proc_name = get_proc_name,
};
+
+unw_accessors_t *_TB_accessors() {
+ return &snapshot_addr_space_accessors;
+}
+
+void *_TB_create (pid_t pid) {
+#define STAT_LINE_MAXSIZE 4096
+ int n_frames;
+ struct mem_data_chunk **stacks_cover = NULL;
+ int rc;
+ void *ret = NULL;
+ char *buf = NULL;
+ char *p = NULL;
+ FILE *f = NULL;
+ int i;
+
+ long page;
+ long label;
+
+ if ((page = sysconf(_SC_PAGESIZE)) < 0) {
+ perror("get pagesize");
+ return NULL;
+ }
+ --page;
+
+ map = create_maps(pid);
+
+ buf = malloc(STAT_LINE_MAXSIZE);
+ if (!buf)
+ return NULL;
+
+ debug = stderr;
+
+ snprintf(buf, STAT_LINE_MAXSIZE, "/proc/%d/stat", pid);
+ f = fopen(buf, "r");
+ if (!f)
+ goto out_err;
+
+ if (fgets(buf, STAT_LINE_MAXSIZE, f) == NULL)
+ goto out_err;
+
+ for (i = 0, p = buf; i < 28; i++) {
+ p = strchr(p, ' ');
+ if (!p)
+ goto out_err;
+ p++;
+ }
+
+ if (sscanf(p, "%lu %lu ", &esp, &eip) != 2)
+ goto out_err;
+
+ ret = (void*)&snapshot_addr_space_accessors;
+
+ label = esp & ~page;
+ mem_map_add_label(map, (void *)label, 1);
+
+ /*
+ * arrange data chunks to copy memory contents. in most cases the chunks
+ * will start from %rsp pointing somewhere in thread's stack
+ * to the end of the stack region
+ */
+ stacks_cover = malloc(sizeof(struct mem_data_chunk*) * 1);
+ if (stacks_cover == NULL) {
+ fprintf(stderr, "error: stacks cover == NULL\n");
+ goto out_err;
+ }
+
+ n_frames = mem_map_build_label_cover(map, stack_size,
+ stacks_cover, page + 1);
+
+ /*
+ * copy memory contents
+ */
+ rc = copy_memory(pid, stacks_cover, n_frames);
+
+ if (rc < 0)
+ ret = NULL;
+
+out_err:
+ if (stacks_cover)
+ free(stacks_cover);
+ if (f)
+ fclose(f);
+ if (buf)
+ free(buf);
+ return ret;
+}
+
+void _TB_destroy (void *ptr) {
+ (void) ptr;
+ if (debug)
+ fprintf(debug, "_TB_destroy\n");
+ if (debug && debug != stdout && debug != stderr)
+ fclose(debug);
+}