Prototype version of stack trace unwinder 48/62148/6
authorAdrian Szyndela <adrian.s@samsung.com>
Fri, 18 Mar 2016 08:50:45 +0000 (09:50 +0100)
committerAdrian Szyndela <adrian.s@samsung.com>
Wed, 23 Mar 2016 11:49:09 +0000 (12:49 +0100)
Change-Id: I5ca7262aed2b2a7a3407864f23f25934653131b8

CMakeLists.txt
packaging/crash-worker.spec
src/crash-manager/crash-manager.sh.in
src/crash-stack/CMakeLists.txt
src/crash-stack/crash-stack-arm.c [new file with mode: 0644]
src/crash-stack/crash-stack-libelf.c [new file with mode: 0644]
src/crash-stack/crash-stack.c
src/crash-stack/crash-stack.h [new file with mode: 0644]

index f273bb0..0281ce3 100644 (file)
@@ -1,5 +1,5 @@
 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
-PROJECT(crash-worker C)
+PROJECT(crash-worker C CXX)
 
 SET(PREFIX ${CMAKE_INSTALL_PREFIX})
 
index ef9d30f..c577c64 100644 (file)
@@ -9,6 +9,9 @@ Source1001:    crash-worker.manifest
 BuildRequires:  pkgconfig(dlog)
 BuildRequires:  pkgconfig(libtzplatform-config)
 BuildRequires:  cmake
+BuildRequires:  libelf-devel libelf
+BuildRequires:  libebl-devel libebl
+BuildRequires:  libdw-devel libdw
 
 Requires(post): coreutils
 Requires(post): tar
@@ -35,7 +38,8 @@ export CFLAGS+=" -Werror"
           -DTZ_SYS_BIN=%{TZ_SYS_BIN} \
           -DCRASH_PATH=%{crash_path} \
           -DCRASH_TEMP=%{crash_temp} \
-          -DCRASH_PIPE_PATH=%{_libexecdir}/crash-pipe
+          -DCRASH_PIPE_PATH=%{_libexecdir}/crash-pipe \
+          -DCRASH_STACK_PATH=%{_libexecdir}/crash-stack
 
 make %{?jobs:-j%jobs}
 
index 2d78a28..176edc4 100644 (file)
@@ -31,6 +31,7 @@ log_path="${pfx}/${name}.log"
 mkdir -p "$CRASH_PATH" "$pfx"
 
 @CRASH_PIPE_PATH@ --save-core "$core_path" --report "$@" > "$info_path"
+@CRASH_STACK_PATH@ "$core_path" >> "$info_path"
 dump_systemstate -d -k -f "$log_path" || true
 
 tar czf "${temp_dir}/report.tar.gz" -C "$temp_dir" "$name"
index 2c21fa5..a95940f 100644 (file)
@@ -1,6 +1,14 @@
 set(CRASH_STACK_BIN "crash-stack")
 set(CRASH_STACK_SRCS crash-stack.c)
 
+if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7l")
+  set(CRASH_STACK_SRCS ${CRASH_STACK_SRCS} crash-stack-arm.c wind/unwarm.c wind/unwarm_thumb.c wind/unwarm_arm.c wind/unwarmmem.c)
+else()
+  set(CRASH_STACK_SRCS ${CRASH_STACK_SRCS} crash-stack-libelf.c)
+endif()
+
 add_executable(${CRASH_STACK_BIN} ${CRASH_STACK_SRCS})
-target_link_libraries(${CRASH_STACK_BIN})
+set_property(TARGET ${CRASH_STACK_BIN} APPEND PROPERTY COMPILE_FLAGS "-DUPGRADE_ARM_STACK_UNWIND")
+#set_property(TARGET ${CRASH_STACK_BIN} APPEND PROPERTY COMPILE_FLAGS "-DUPGRADE_ARM_STACK_UNWIND -DUNW_DEBUG")
+target_link_libraries(${CRASH_STACK_BIN} dw elf ebl dl stdc++)
 install(TARGETS ${CRASH_STACK_BIN} DESTINATION libexec)
diff --git a/src/crash-stack/crash-stack-arm.c b/src/crash-stack/crash-stack-arm.c
new file mode 100644 (file)
index 0000000..81dfe4e
--- /dev/null
@@ -0,0 +1,167 @@
+#include "crash-stack.h"
+#include "wind/unwarm.h"
+
+#include <string.h>
+
+static Elf *g_core = NULL;
+static Dwfl *g_dwfl = NULL;
+
+struct Regs
+{
+  Dwarf_Addr regs[REGS_REGULAR_NUM];
+  Dwarf_Addr sp;
+  Dwarf_Addr lr;
+  Dwarf_Addr pc;
+  Dwarf_Addr spsr;
+};
+
+static Regs g_regs;
+
+Regs *get_regs_struct (void)
+{
+  return &g_regs;
+}
+
+void *get_place_for_register_value (const char *regname, int regnum)
+{
+  if (strcmp (regname, "pc") == 0)
+  {
+    return &g_regs.pc;
+  }
+  else if (strcmp (regname, "sp") == 0)
+  {
+    return &g_regs.sp;
+  }
+  else if (strcmp (regname, "lr") == 0)
+  {
+    return &g_regs.lr;
+  }
+  else if (strcmp (regname, "spsr") == 0)
+  {
+    return &g_regs.spsr;
+  }
+  else if (regnum < REGS_REGULAR_NUM)
+  {
+    return &g_regs.regs[regnum];
+  }
+  return NULL;
+}
+
+static Boolean report (void *data, Int32 address)
+{
+  Callstack *callstack = (Callstack *)(data);
+  callstack->tab[callstack->elems++] = address;
+
+  return callstack->elems < MAX_CALLSTACK_LEN ? TRUE : FALSE;
+}
+
+Boolean readT (Int32 a, void *v, size_t size)
+{
+  Dwfl_Module *module = 0;
+  Boolean result = FALSE;
+
+  int segment = dwfl_addrsegment (g_dwfl, a, &module);
+
+  if (module != NULL)
+  {
+    Dwarf_Addr start;
+    dwfl_module_info (module, NULL, &start, NULL, NULL, NULL, NULL, NULL);
+
+    GElf_Addr bias;
+    Elf *elf = dwfl_module_getelf (module, &bias);
+
+    Elf_Data *data = elf_getdata_rawchunk (elf, a-start, size, ELF_T_BYTE);
+    if (data != NULL)
+    {
+      memcpy (v, data->d_buf, size);
+      result = TRUE;
+    }
+  }
+  if (!result && segment != -1)
+  {
+    // get data from segment
+    GElf_Phdr mem;
+    GElf_Phdr *phdr = gelf_getphdr (g_core, segment, &mem);
+    Dwarf_Addr offset_in_segment = a - phdr->p_vaddr;
+    Dwarf_Addr offset_in_file = phdr->p_offset + offset_in_segment;
+
+    Elf_Data *data = elf_getdata_rawchunk (g_core, offset_in_file, size, ELF_T_BYTE);
+    if (data != NULL)
+    {
+      memcpy (v, data->d_buf, size);
+      result = TRUE;
+    }
+  }
+
+  return result;
+}
+
+static Boolean readW (Int32 a, Int32 *v)
+{
+  return readT(a,v,sizeof(*v));
+}
+
+static Boolean readH (Int32 a, Int16 *v)
+{
+  return readT(a,v,sizeof(*v));
+}
+
+static Boolean readB (Int32 a, Int8 *v)
+{
+  return readT(a,v,sizeof(*v));
+}
+
+static Int32 getProloguePC (Int32 current_pc)
+{
+    Int32 result = 0;
+    Dwfl_Module *module = dwfl_addrmodule (g_dwfl, current_pc);
+    if (module)
+    {
+        GElf_Off offset;
+        GElf_Sym sym;
+        dwfl_module_addrinfo (module, current_pc, &offset, &sym, NULL, NULL, NULL);
+        result = current_pc - offset;
+    }
+    return result;
+}
+
+void create_crash_stack (Regs *regs, Dwfl *dwfl, Elf *core, Callstack *callstack)
+{
+  UnwindCallbacks callbacks =
+  {
+    report,
+    readW,
+    readH,
+    readB,
+    getProloguePC
+#ifdef UNW_DEBUG
+    ,
+    printf
+#endif
+  };
+  UnwState state;
+
+  g_dwfl = dwfl;
+  g_core = core;
+
+  callstack->tab[0] = regs->pc;
+  callstack->elems = 1;
+
+  UnwInitState (&state, &callbacks, callstack, regs->pc, regs->sp);
+  int i;
+  for (i = 0; i < REGS_REGULAR_NUM; i++)
+  {
+    state.regData[i].v = regs->regs[i];
+    state.regData[i].o = REG_VAL_FROM_CONST;
+  }
+  state.regData[REG_LR].v = regs->lr;
+  state.regData[REG_LR].o = REG_VAL_FROM_STACK;
+  state.regData[REG_SPSR].v = regs->spsr;
+  state.regData[REG_SPSR].o = REG_VAL_FROM_CONST;
+
+  if (UnwIsAddrThumb (regs->pc, regs->spsr))
+    UnwStartThumb (&state);
+  else
+    UnwStartArm (&state);
+}
+
diff --git a/src/crash-stack/crash-stack-libelf.c b/src/crash-stack/crash-stack-libelf.c
new file mode 100644 (file)
index 0000000..f09b572
--- /dev/null
@@ -0,0 +1,16 @@
+#include "crash-stack.h"
+
+Regs *get_regs_struct (void)
+{
+  return 0;
+}
+
+void *get_place_for_register_value (const char *regname, int regnum)
+{
+  return 0;
+}
+
+void create_crash_stack (Regs *regs, Dwfl *dwfl, Elf *core, Callstack *callstack)
+{
+}
+
index c60a2d9..69a1e0d 100644 (file)
@@ -1,6 +1,275 @@
 #include <stdlib.h>
+#include <stdio.h>
+#include <libelf.h>
+#include <elfutils/libdwfl.h>
+#include <elfutils/libebl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+#include <linux/prctl.h>
+#include "crash-stack.h"
+
+extern char *__cxa_demangle (const char *mangled_name, char *output_buffer,
+                            size_t *length, int *status);
+
+/*
+static int frame_callback (Dwfl_Frame *state, void *arg)
+{
+  Regs *regs = static_cast<Regs*>(arg);
+  dwfl_frame_pc (state, &regs->pc, NULL);
+  return DWARF_CB_ABORT;
+}
+
+static int thread_callback (Dwfl_Thread *thread, void *thread_arg)
+{
+  dwfl_thread_getframes (thread, frame_callback, thread_arg);
+  return DWARF_CB_ABORT;
+}
+*/
+
+static int module_callback (Dwfl_Module *module, void **userdata,
+                                                  const char *name, Dwarf_Addr address,
+                                                  void *arg)
+{
+  /* To get something from module file do something like:
+
+     GElf_Addr bias;
+     Elf *elf = dwfl_module_getelf (module, &bias);
+
+     cout << "Module : " << name << " @" << hex << address << " bias " << bias << endl;
+
+     Elf_Data *data = elf_getdata_rawchunk (elf, 0, 4, ELF_T_BYTE);
+     cout << " " << static_cast<char*>(data->d_buf)+1 << endl;
+   */
+  return DWARF_CB_OK;
+}
+
+void getvalue (Elf *core, const void *from, size_t size, void *to)
+{
+  Elf_Data out =
+  {
+    .d_buf = to,
+    .d_type = size == 32 ? ELF_T_WORD : ELF_T_XWORD,
+    .d_version = EV_CURRENT,
+    .d_size = size/8,
+    .d_off = 0,
+    .d_align = 0
+  };
+  Elf_Data in =
+  {
+    .d_buf = (void*)(from),
+    .d_type = out.d_type,
+    .d_version = out.d_version,
+    .d_size = out.d_size,
+    .d_off = 0,
+    .d_align = 0
+  };
+  Elf_Data *data;
+  if (gelf_getclass (core) == ELFCLASS32)
+    data = elf32_xlatetom (&out, &in, elf_getident (core, NULL)[EI_DATA]);
+  else
+    data = elf64_xlatetom (&out, &in, elf_getident (core, NULL)[EI_DATA]);
+  if (data == NULL)
+    fprintf (stderr, "failed to get value from core file\n");
+}
 
 int main(int argc, char **argv)
 {
+  prctl (PR_SET_DUMPABLE, 0);
+
+  if (argc != 2)
+  {
+    fprintf (stderr, "Usage: %s <core-file>\n", argv[0]);
+    return 1;
+  }
+
+  int core_fd = open (argv[1], O_RDONLY);
+  if (core_fd < 0)
+  {
+    perror (argv[1]);
+    return 2;
+  }
+
+  elf_version (EV_CURRENT);
+
+  Elf *core = elf_begin (core_fd, ELF_C_READ_MMAP, NULL);
+  if (core == NULL)
+  {
+    fprintf (stderr, "%s : Can't open ELF (%s)\n", argv[1], elf_errmsg(-1));
+    return 3;
+  }
+
+  const Dwfl_Callbacks core_callbacks =
+  {
+    .find_elf = dwfl_build_id_find_elf,
+    .find_debuginfo = dwfl_standard_find_debuginfo,
+    .section_address = NULL,
+    .debuginfo_path = NULL
+  };
+
+  Dwfl *dwfl = dwfl_begin (&core_callbacks);
+  if (dwfl == NULL)
+  {
+    fprintf (stderr, "%s : Can't start dwfl (%s)\n", argv[1], dwfl_errmsg(-1));
+    return 4;
+  }
+
+  if (dwfl_core_file_report (dwfl, core, NULL) < 0)
+  {
+    fprintf (stderr, "%s : dwfl report failed (%s)\n", argv[1], dwfl_errmsg(-1));
+    return 5;
+  }
+
+  if (dwfl_core_file_attach (dwfl, core) < 0)
+  {
+    fprintf (stderr, "%s : dwfl attach failed (%s)\n", argv[1], dwfl_errmsg(-1));
+    return 6;
+  }
+
+   Regs *regs = get_regs_struct();
+/*
+   To unwind with libelf do this:
+
+  dwfl_getthreads (dwfl, thread_callback, regs);
+
+*/
+
+  dwfl_getmodules (dwfl, module_callback, 0, 0);
+
+  GElf_Phdr mem;
+  GElf_Phdr *phdr = gelf_getphdr (core, 0, &mem);
+  if (phdr == NULL || phdr->p_type != PT_NOTE)
+  {
+    fprintf (stderr, "%s : Missing note section at the first position in core file\n", argv[1]);
+    return 8;
+  }
+
+  Elf_Data *notes = elf_getdata_rawchunk (core, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR);
+  if (notes == NULL)
+  {
+    fprintf (stderr, "%s : error getting notes (%s)\n", argv[1], dwfl_errmsg(-1));
+    return 9;
+  }
+
+  Ebl *ebl = ebl_openbackend (core);
+  if (ebl == NULL)
+  {
+    fprintf (stderr, "%s : Can't initialize ebl\n", argv[1]);
+    return 7;
+  }
+
+  GElf_Nhdr nhdr;
+  size_t name_pos;
+  size_t desc_pos;
+  size_t pos = 0;
+  /* registers should be in the first note! */
+  if (gelf_getnote (notes, pos, &nhdr, &name_pos, &desc_pos) > 0)
+  {
+    if (nhdr.n_type == NT_PRSTATUS)
+    {
+      GElf_Word regs_offset;
+      size_t nregloc;
+      const Ebl_Register_Location *reglocs;
+      size_t nitems;
+      const Ebl_Core_Item *items;
+
+      if (0 == ebl_core_note (ebl, &nhdr, "CORE", &regs_offset, &nregloc, &reglocs, &nitems, &items))
+      {
+        fprintf (stderr, "%s : error parsing notes\n", argv[1]);
+        return 10;
+      }
+
+      const char *regs_location = (const char *)(notes->d_buf) + pos + desc_pos + regs_offset;
+      unsigned i;
+
+      for (i = 0; i < nregloc; i++)
+      {
+        const char *register_location = regs_location + reglocs[i].offset;
+        int regnum;
+        for (regnum = reglocs[i].regno; regnum < reglocs[i].regno + reglocs[i].count; regnum++)
+        {
+          char regname[5];
+          int bits, type;
+          const char *prefix = 0;
+          const char *setname = 0;
+          ssize_t ret = ebl_register_info (ebl, regnum, regname, sizeof(regname), &prefix, &setname, &bits, &type);
+          if (ret < 0)
+          {
+            fprintf (stderr, "%s : can't get register info\n", argv[1]);
+            return 11;
+          }
+          void *place_for_reg_value = get_place_for_register_value (regname, regnum);
+
+          if (place_for_reg_value != NULL)
+            getvalue (core, register_location, bits, place_for_reg_value);
+
+          register_location += bits / 8 + reglocs[i].pad;
+        }
+      }
+    }
+  }
+
+/*  for (int i = 0; i < 20; i++)
+  {
+    char name[100] = {0};
+    int bits = 0, type = 0;
+    const char *setname = 0;
+    const char *prefix = 0;
+    ssize_t ret = ebl_register_info (ebl, i, name, sizeof(name), &prefix, &setname, &bits, &type);
+    printf ("ebl_register_info %d ret: %d, name: %s, prefix: %s, setname: %s, bits: %d, type: %d\n",
+        i, ret, name, prefix, setname, bits, type);
+  }
+*/
+/*  printf ("PC: 0x%llx\n", (unsigned long long)regs.pc);
+  printf ("SP: 0x%llx\n", (unsigned long long)regs.sp);*/
+
+  Callstack callstack;
+
+  create_crash_stack (regs, dwfl, core, &callstack);
+
+  char *dem_buffer = NULL;
+  size_t it;
+
+  printf ("Call stack:\n");
+  for (it = 0; it != callstack.elems; ++it)
+  {
+    if (sizeof (callstack.tab[0]) > 4)
+      printf ("0x%016llx: ", (int64_t)callstack.tab[it]);
+    else
+      printf ("0x%08x: ", (int32_t)callstack.tab[it]);
+    Dwfl_Module *module = dwfl_addrmodule (dwfl, callstack.tab[it]);
+    if (module)
+    {
+      char *demangled_symbol = 0;
+      const char *symbol = dwfl_module_addrname (module, callstack.tab[it]);
+      if (symbol != 0 && symbol[0] == '_' && symbol[1] == 'Z')
+      {
+        int status = -1;
+
+        demangled_symbol = __cxa_demangle (symbol, dem_buffer, NULL, &status);
+        if (status == 0)
+          symbol = demangled_symbol;
+      }
+      if (symbol != 0)
+        printf ("%s()", symbol);
+
+      if (demangled_symbol != 0)
+        free (demangled_symbol);
+
+      printf (" from %s\n", dwfl_module_info (module, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
+    }
+    else
+    {
+      printf ("unknown function\n");
+    }
+  }
+
+  dwfl_report_end (dwfl, NULL, NULL);
+  dwfl_end (dwfl);
+  elf_end (core);
+  close (core_fd);
+
   return 0;
 }
diff --git a/src/crash-stack/crash-stack.h b/src/crash-stack/crash-stack.h
new file mode 100644 (file)
index 0000000..6560d31
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef CRASH_STACK_H
+#define CRASH_STACK_H
+
+#include <stdint.h>
+#include <elfutils/libdwfl.h>
+#include <elfutils/libebl.h>
+
+#define MAX_CALLSTACK_LEN 1000
+
+typedef struct Callstack
+{
+  uintptr_t tab[MAX_CALLSTACK_LEN];
+  size_t elems;
+} Callstack;
+
+struct Regs;
+typedef struct Regs Regs;
+
+Regs *get_regs_struct (void);
+void *get_place_for_register_value (const char *regname, int regnum);
+void create_crash_stack (Regs *regs, Dwfl *dwfl, Elf *core, Callstack *callstack);
+
+#endif /* CRASH_STACK_H */