util: improve backtrace feature
authorJinhyung Jo <jinhyung.jo@samsung.com>
Mon, 19 Sep 2016 11:04:13 +0000 (20:04 +0900)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Thu, 6 Oct 2016 02:28:22 +0000 (11:28 +0900)
improve & clean up the code:
  - correct coding convention
  - add backtrace feature to Mac OS X
    : remove #ifdef CONFIG_DARWIN ... #endif
      The 'backtarce' family functions first appeared in Mac OS X 10.5
  - modify the Windows code using the APIs in DbgHelp
  - change debug channel: from debug_ch to new_debug_ch
  - remove some lines like redundant/unclear/unwanted/etc.
  - modify output form

Change-Id: I8b57d996fa9daabf990932fcfb82e45a5e453636
Signed-off-by: Jinhyung Jo <jinhyung.jo@samsung.com>
tizen/src/util/Makefile.objs
tizen/src/util/error_handler.c

index a4a2969..a322caf 100644 (file)
@@ -30,6 +30,9 @@ obj-y += qt5_error_report.o
 
 # error handler
 obj-y += error_handler.o
+ifdef CONFIG_WIN32
+LIBS += -ldbghelp
+endif
 
 # debug channel
 obj-y += new_debug_ch.o
index 1f91e8d..5a41b05 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
  *
  * Contact:
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
  * SeokYeon Hwang <syeon.hwang@samsung.com>
  * GiWoong Kim <giwoong.kim@samsung.com>
  *
@@ -37,6 +38,7 @@
 
 #ifdef CONFIG_WIN32
 #include <windows.h>
+#include <dbghelp.h>
 #else
 #include <execinfo.h>
 #endif
 #include "emulator_common.h"
 #include "emulator.h"
 
-#include "debug_ch.h"
-
-MULTI_DEBUG_CHANNEL(qemu, backtrace);
-
 #ifdef CONFIG_QT
 #include "qt5_error_report.h"
 #endif
 
-#if defined(CONFIG_WIN32)
-static LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionFilter;
-#elif defined(CONFIG_LINUX)
-static pthread_spinlock_t siglock;
-#endif
+#include "new_debug_ch.h"
 
-bool print_backtrace_at_normal_exit_enabled = false;
+DECLARE_DEBUG_CHANNEL(backtrace);
 
-/* Print 'backtrace' */
-#ifdef _WIN32
-struct frame_layout {
-    void *pNext;
-    void *pReturnAddr;
-};
+bool print_backtrace_at_normal_exit_enabled;
 
-static char *get_filename_from_path(char *path_buf)
+void enable_print_backtrace_at_normal_exit(void)
 {
-    char *ret_slash;
-    char *ret_rslash;
-
-    ret_slash = strrchr(path_buf, '/');
-    ret_rslash = strrchr(path_buf, '\\');
-
-    if (ret_slash || ret_rslash) {
-        if (ret_slash > ret_rslash) {
-            return ret_slash + 1;
-        } else{
-            return ret_rslash + 1;
-        }
-    }
-
-    return path_buf;
+    print_backtrace_at_normal_exit_enabled = true;
 }
 
+#ifdef CONFIG_WIN32
+static LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionFilter;
+
+/* Windows 32 bit */
+#ifndef _WIN64
+struct frame_layout {
+    void *pNext;
+    void *pReturnAddr;
+};
 
 static HMODULE get_module_handle(void *dwAddress)
 {
     MEMORY_BASIC_INFORMATION Buffer;
-    return VirtualQuery((LPCVOID) dwAddress, &Buffer, sizeof(Buffer))
-            ? (HMODULE) Buffer.AllocationBase : (HMODULE) 0;
+    return VirtualQuery((LPCVOID)dwAddress, &Buffer, sizeof(Buffer))
+            ? (HMODULE)Buffer.AllocationBase : (HMODULE)0;
 }
-#endif
 
-static void dump_backtrace(void *ptr, int depth)
+static void dump_backtrace(void *ptr)
 {
-#ifdef _WIN32
     int nCount;
     void *pTopFrame;
     struct frame_layout currentFrame;
@@ -113,15 +95,11 @@ static void dump_backtrace(void *ptr, int depth)
     if (!pContext) {
         __asm__ __volatile__ ("movl   %%ebp, %0" : "=m" (pTopFrame));
     } else {
-#ifdef _WIN64
-        pTopFrame = (void *)((PCONTEXT)pContext)->Rbp;
-#else
         pTopFrame = (void *)((PCONTEXT)pContext)->Ebp;
-#endif
     }
 
     if (pTopFrame == NULL) {
-        INFO("ebp is null, skip this for now\n");
+        LOG_INFO("ebp is null, skip this for now\n");
         return ;
     }
 
@@ -130,30 +108,23 @@ static void dump_backtrace(void *ptr, int depth)
     currentFrame.pReturnAddr = ((struct frame_layout *)pTopFrame)->pReturnAddr;
     pCurrentFrame = (struct frame_layout *)pTopFrame;
 
-    ERR("\nBacktrace Dump Start :\n");
     if (pContext) {
         memset(module_buf, 0, sizeof(module_buf));
-#ifdef _WIN64
-        hModule = get_module_handle((void *)((PCONTEXT)pContext)->Rip);
-#else
         hModule = get_module_handle((void *)((PCONTEXT)pContext)->Eip);
-#endif
         if (hModule) {
             if (!GetModuleFileNameA(hModule, module_buf, sizeof(module_buf))) {
                 memset(module_buf, 0, sizeof(module_buf));
             }
         }
-#ifdef _WIN64
-        ERR("[%02d]Addr = 0x%p : %s\n", nCount, ((PCONTEXT)pContext)->Rip, get_filename_from_path(module_buf));
-#else
-        ERR("[%02d]Addr = 0x%p : %s\n", nCount, ((PCONTEXT)pContext)->Eip, get_filename_from_path(module_buf));
-#endif
+        LOG_SEVERE("#%04d 0x%08x from %s\n",
+                   nCount, ((PCONTEXT)pContext)->Eip, module_buf);
         nCount++;
     }
 
     while (1) {
         if (((void *)pCurrentFrame < pTopFrame)
-            || ((void *)pCurrentFrame >= (void *)0xC0000000)) {
+            || ((void *)pCurrentFrame >= (void *)0xC0000000)
+            || !currentFrame.pReturnAddr) {
             break;
         }
 
@@ -164,127 +135,174 @@ static void dump_backtrace(void *ptr, int depth)
                 memset(module_buf, 0, sizeof(module_buf));
             }
         }
-        ERR("[%02d]Addr = 0x%p : %s\n", nCount, currentFrame.pReturnAddr, get_filename_from_path(module_buf));
+        LOG_SEVERE("#%04d 0x%08x from %s\n",
+                   nCount, currentFrame.pReturnAddr, module_buf);
 
-    if (!ReadProcessMemory(GetCurrentProcess(), currentFrame.pNext,
+        if (!ReadProcessMemory(GetCurrentProcess(), currentFrame.pNext,
             (void *)&currentFrame, sizeof(struct frame_layout), NULL)) {
             break;
         }
         pCurrentFrame = (struct frame_layout *)pCurrentFrame->pNext;
 
-        if (depth) {
-            if (!--depth) {
-                break;
-            }
-        }
         nCount++;
     }
-#else
-    void *trace[1024];
-    int ndepth = backtrace(trace, 1024);
-    ERR("Backtrace depth is %d.\n", ndepth);
-
-    backtrace_symbols_fd(trace, ndepth, fileno(stderr));
-#endif
 }
 
-static void handle_error_at_exit(void)
+#else
+
+/* Windows 64 bit */
+static void dump_backtrace(void *ptr)
 {
-    if (print_backtrace_at_normal_exit_enabled) {
-        INFO("Stack backtrace for tracing...\n");
-        INFO("This is not an error.\n");
-        dump_backtrace(NULL, 0);
+    int i;
+    DWORD image;
+    STACKFRAME64 frame;
+    CONTEXT context;
+    HANDLE hProcess = GetCurrentProcess();
+    HANDLE hThread = GetCurrentThread();
+
+    if (!ptr) {
+        ZeroMemory(&context, sizeof(CONTEXT));
+        context.ContextFlags = CONTEXT_FULL;
+        RtlCaptureContext(&context);
+    } else {
+        CopyMemory(&context, ptr, sizeof(CONTEXT));
     }
-}
 
-void enable_print_backtrace_at_normal_exit(void) {
-    print_backtrace_at_normal_exit_enabled = true;
+    SymSetOptions(SYMOPT_LOAD_LINES);
+    SymInitialize(hProcess, NULL, TRUE);
+
+    ZeroMemory(&frame, sizeof(STACKFRAME64));
+    image = IMAGE_FILE_MACHINE_AMD64;
+    frame.AddrPC.Offset = context.Rip;
+    frame.AddrPC.Mode = AddrModeFlat;
+    frame.AddrFrame.Offset = context.Rbp;
+    frame.AddrFrame.Mode = AddrModeFlat;
+    frame.AddrStack.Offset = context.Rsp;
+    frame.AddrStack.Mode = AddrModeFlat;
+
+    i = 0;
+    while (1) {
+        BOOL result = StackWalk64(image, hProcess, hThread,
+                                  &frame, &context, NULL,
+                                  SymFunctionTableAccess64,
+                                  SymGetModuleBase64, NULL);
+        if (!result) {
+            break;
+        }
+        TCHAR buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
+        PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
+        pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+        pSymbol->MaxNameLen = MAX_SYM_NAME;
+        DWORD64 displacement = 0;
+        TCHAR pFileName[MAX_PATH] = {0, };
+        DWORD64 dwBase = SymGetModuleBase64(hProcess, frame.AddrPC.Offset);
+        if (dwBase) {
+            HMODULE hModule = (HMODULE)((DWORD_PTR)dwBase);
+            if (!GetModuleFileNameA(hModule, pFileName, MAX_PATH)) {
+                snprintf(pFileName, MAX_PATH, "Unknown Module");
+            }
+        }
+        if (SymFromAddr(hProcess,
+                        frame.AddrPC.Offset,
+                        &displacement, pSymbol)) {
+            LOG_SEVERE("#%04d 0x%016x in %s from %s\n",
+                        i, frame.AddrPC.Offset, pSymbol->Name, pFileName);
+        } else {
+            LOG_SEVERE("#%04d 0x%016x in ???????? from %s\n",
+                        i, frame.AddrPC.Offset, pFileName);
+        }
+        i++;
+    }
+    SymCleanup(hProcess);
 }
+#endif
 
-#ifdef CONFIG_WIN32
-static WINAPI LONG maru_unhandled_exception_filter(LPEXCEPTION_POINTERS pExceptionInfo){
-    char module_buf[1024];
+static WINAPI LONG
+maru_unhandled_exception_filter(LPEXCEPTION_POINTERS pException)
+{
+    LOG_SEVERE("Exception occurred: Code[0x%x], Address[0x%016x]\n",
+               pException->ExceptionRecord->ExceptionCode,
+               pException->ExceptionRecord->ExceptionAddress);
 
-    // print system information again
-    print_system_info();
+    dump_backtrace(pException->ContextRecord);
 
-    DWORD dwException = pExceptionInfo->ExceptionRecord->ExceptionCode;
-    ERR("%d\n ", (int)dwException);
+    return EXCEPTION_CONTINUE_SEARCH;
+}
 
-    PEXCEPTION_RECORD pExceptionRecord;
-    HMODULE hModule;
-    PCONTEXT pContext;
+static void register_exception_handler(void)
+{
+    prevExceptionFilter =
+        SetUnhandledExceptionFilter(maru_unhandled_exception_filter);
+}
 
-    pExceptionRecord = pExceptionInfo->ExceptionRecord;
+#else /* END FOR WINDOWS, START FOR LINUX & DARWIN */
 
-    memset(module_buf, 0, sizeof(module_buf));
-    hModule = get_module_handle(pExceptionRecord->ExceptionAddress);
-    if(hModule){
-        if(!GetModuleFileNameA(hModule, module_buf, sizeof(module_buf))){
-            memset(module_buf, 0, sizeof(module_buf));
-        }
-    }
+/* prevent an interrupt by another signal */
+QemuMutex siglock;
+/* uses in case SIGSEGV */
+struct sigaction old_sa;
 
-    ERR("Exception [%X] occured at %s:0x%08x\n",
-        pExceptionRecord->ExceptionCode,
-        get_filename_from_path(module_buf),
-        pExceptionRecord->ExceptionAddress
-       );
+/* Print 'backtrace' */
+static void dump_backtrace(void *ptr)
+{
+    int i;
+    void *trace[1024];
+    int ndepth = backtrace(trace, 1024);
+    char **syms = backtrace_symbols(trace, ndepth);
 
-    pContext = pExceptionInfo->ContextRecord;
-    dump_backtrace(pContext, 0);
-    _exit(0);
-    //return EXCEPTION_CONTINUE_SEARCH;
+    LOG_SEVERE("Backtrace depth is %d\n", ndepth);
+    for (i = 0; i < ndepth; i++) {
+        LOG_SEVERE("#%04d %s\n", i, syms[i]);
+    }
+    free(syms);
 }
-#endif
 
-#ifdef CONFIG_LINUX
 static void maru_sighandler(int sig)
 {
-    ERR("Got signal %d\n", sig);
-    // print system information again
-    print_system_info();
-
-    pthread_spin_lock(&siglock);
-    dump_backtrace(NULL, 0);
-    pthread_spin_unlock(&siglock);
-    _exit(0);
+    LOG_SEVERE("Got signal: %d(%s)\n", sig, strsignal(sig));
+
+    qemu_mutex_lock(&siglock);
+    dump_backtrace(NULL);
+    qemu_mutex_unlock(&siglock);
+    qemu_mutex_destroy(&siglock);
+    if (sig == SIGSEGV) {
+        sigaction(SIGSEGV, &old_sa, NULL);
+        raise(SIGSEGV);
+    }
 }
-#endif
 
-
-#ifndef CONFIG_DARWIN
 static void register_exception_handler(void)
 {
- #ifdef CONFIG_WIN32
-    prevExceptionFilter = SetUnhandledExceptionFilter(maru_unhandled_exception_filter);
- #else // LINUX
     void *trace[1];
     struct sigaction sa;
 
-    // make dummy call to explicitly load glibc library
+    /* make dummy call to explicitly load glibc library */
     backtrace(trace, 1);
 
-    pthread_spin_init(&siglock,0);
-    sa.sa_handler = (void*) maru_sighandler;
+    qemu_mutex_init(&siglock);
+    sa.sa_handler = maru_sighandler;
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = SA_RESTART;
-    sigaction(SIGSEGV, &sa, NULL);
+    /* store previous signal action */
+    sigaction(SIGSEGV, &sa, &old_sa);
     sigaction(SIGBUS, &sa, NULL);
     sigaction(SIGILL, &sa, NULL);
     sigaction(SIGFPE, &sa, NULL);
     sigaction(SIGABRT, &sa, NULL);
-    // main thread only
+    /* main thread only */
     sigaction(SIGTERM, &sa, NULL);
     sigaction(SIGINT, &sa, NULL);
- #endif
 }
-#else // CONFIG_DARWIN
-static void register_exception_handler(void)
+#endif  /* END FOR LINUX & DARWIN */
+
+static void handle_error_at_exit(void)
 {
-    // TODO: Exception handling on darwin
+    if (print_backtrace_at_normal_exit_enabled) {
+        LOG_INFO("Stack backtrace for tracing...\n");
+        LOG_INFO("This is not an error\n");
+        dump_backtrace(NULL);
+    }
 }
-#endif
 
 #define MAX_MESSAGE_LEN 2048
 static size_t message_len;
@@ -296,9 +314,9 @@ static void report(const char *fmt, va_list ap)
     message_len = g_strlcat(message, new_message, MAX_MESSAGE_LEN);
     g_free(new_message);
 
-    // We are wating for '\n'
+    /* We are waiting for '\n' */
     if (message[message_len - 1] == '\n') {
-#if defined(CONFIG_QT)
+#ifdef CONFIG_QT
         start_qt5_msgbox(CRITICAL_ICON, message);
 #endif
 
@@ -307,10 +325,12 @@ static void report(const char *fmt, va_list ap)
     }
 }
 
-static ErrorReporter error_reporter =
-    { .report = report };
+static ErrorReporter error_reporter = {
+    .report = report
+};
 
-void init_error_handler(void) {
+void init_error_handler(void)
+{
     register_exception_handler();
     add_error_reporter(&error_reporter);