/* * ut_dump_backtrace -- dump stacktrace to error log using libunwind */ void ut_dump_backtrace(void) { unw_context_t context; unw_proc_info_t pip; pip.unwind_info = NULL; int ret = unw_getcontext(&context); if (ret) { ERR("unw_getcontext: %s [%d]", unw_strerror(ret), ret); return; } unw_cursor_t cursor; ret = unw_init_local(&cursor, &context); if (ret) { ERR("unw_init_local: %s [%d]", unw_strerror(ret), ret); return; } ret = unw_step(&cursor); char procname[PROCNAMELEN]; unsigned i = 0; while (ret > 0) { ret = unw_get_proc_info(&cursor, &pip); if (ret) { ERR("unw_get_proc_info: %s [%d]", unw_strerror(ret), ret); break; } unw_word_t off; ret = unw_get_proc_name(&cursor, procname, PROCNAMELEN, &off); if (ret && ret != -UNW_ENOMEM) { if (ret != -UNW_EUNSPEC) { ERR("unw_get_proc_name: %s [%d]", unw_strerror(ret), ret); } strcpy(procname, "?"); } void *ptr = (void *)(pip.start_ip + off); Dl_info dlinfo; const char *fname = "?"; if (dladdr(ptr, &dlinfo) && dlinfo.dli_fname && *dlinfo.dli_fname) fname = dlinfo.dli_fname; ERR("%u: %s (%s%s+0x%lx) [%p]", i++, fname, procname, ret == -UNW_ENOMEM ? "..." : "", off, ptr); ret = unw_step(&cursor); if (ret < 0) ERR("unw_step: %s [%d]", unw_strerror(ret), ret); } }
// Find LSDA and start address for a function at address controlPC bool FindProcInfo(UIntNative controlPC, UIntNative* startAddress, UIntNative* lsda) { #ifndef CAN_LINK_SHARED_LIBUNWIND return false; #else // CAN_LINK_SHARED_LIBUNWIND unw_context_t unwContext; unw_cursor_t cursor; REGDISPLAY regDisplay; memset(®Display, 0, sizeof(REGDISPLAY)); regDisplay.SetIP((PCODE)controlPC); if (!InitializeUnwindContextAndCursor(®Display, &cursor, &unwContext)) { return false; } unw_proc_info_t procInfo; int st = unw_get_proc_info(&cursor, &procInfo); if (st < 0) { return false; } assert((procInfo.start_ip <= controlPC) && (controlPC < procInfo.end_ip)); *lsda = procInfo.lsda; *startAddress = procInfo.start_ip; return true; #endif // CAN_LINK_SHARED_LIBUNWIND }
void collectStackRoots(TraceStack *stack) { unw_cursor_t cursor; unw_context_t uc; unw_word_t ip, sp, bp; // force callee-save registers onto the stack: // Actually, I feel like this is pretty brittle: // collectStackRoots itself is allowed to save the callee-save registers // on its own stack. jmp_buf registers __attribute__((aligned(sizeof(void*)))); #ifdef VALGRIND memset(®isters, 0, sizeof(registers)); memset(&cursor, 0, sizeof(cursor)); memset(&uc, 0, sizeof(uc)); memset(&ip, 0, sizeof(ip)); memset(&sp, 0, sizeof(sp)); memset(&bp, 0, sizeof(bp)); #endif setjmp(registers); assert(sizeof(registers) % 8 == 0); //void* stack_bottom = __builtin_frame_address(0); collectRoots(®isters, ®isters + 1, stack); unw_getcontext(&uc); unw_init_local(&cursor, &uc); TraceStackGCVisitor visitor(stack); int code; while (true) { int code = unw_step(&cursor); assert(code > 0 && "something broke unwinding!"); unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); unw_get_reg(&cursor, UNW_TDEP_BP, &bp); void* cur_sp = (void*)sp; void* cur_bp = (void*)bp; //std::string name = g.func_addr_registry.getFuncNameAtAddress((void*)ip, true); //if (VERBOSITY()) printf("ip = %lx (%s), stack = [%p, %p)\n", (long) ip, name.c_str(), cur_sp, cur_bp); unw_proc_info_t pip; unw_get_proc_info(&cursor, &pip); if (pip.start_ip == (uintptr_t)&__libc_start_main) { break; } if (pip.start_ip == (intptr_t)interpretFunction) { // TODO Do we still need to crawl the interpreter itself? gatherInterpreterRootsForFrame(&visitor, cur_bp); } collectRoots(cur_sp, (char*)cur_bp, stack); } }
PROTECTED unsigned long _Unwind_GetRegionStart (struct _Unwind_Context *context) { unw_proc_info_t pi; pi.start_ip = 0; unw_get_proc_info (&context->cursor, &pi); return pi.start_ip; }
PROTECTED unsigned long _Unwind_GetDataRelBase (struct _Unwind_Context *context) { unw_proc_info_t pi; pi.gp = 0; unw_get_proc_info (&context->cursor, &pi); return pi.gp; }
void jffi_longjmp (jmp_buf env, int val) { extern int _jffi_longjmp_cont; unw_context_t uc; unw_cursor_t c; unw_word_t sp, ip, bp = 0; uintptr_t *wp = (uintptr_t *) env; int i, setjmp_frame; if (unw_getcontext (&uc) < 0 || unw_init_local (&c, &uc) < 0) { debug("failed to get context"); abort (); } #ifdef __x86_86__ # define UNW_REG_BP UNW_X86_64_RBP #else # define UNW_REG_BP UNW_X86_EBP #endif setjmp_frame = 0; do { char name[256]; unw_proc_info_t pi; unw_word_t off; if (unw_get_reg (&c, UNW_REG_BP, &bp) < 0) { abort(); } if (unw_get_reg (&c, UNW_REG_SP, &sp) < 0) { abort(); } if (unw_get_reg (&c, UNW_REG_IP, &ip) < 0) { abort(); } unw_get_proc_name(&c, name, sizeof(name), &off); unw_get_proc_info(&c, &pi); // debug("frame %s ip=%llx sp=%llx bp=%llx wp[RP]=%p wp[SP]=%p, pi.start_ip=%llx, pi.end_ip=%llx", // name, (long long) ip, (long long) sp, (long long) bp, (void *) wp[JB_RP], (void *) wp[JB_SP], // pi.start_ip, pi.end_ip); if (wp[JB_SP] > sp || wp[JB_RP] < pi.start_ip || wp[JB_RP] > pi.end_ip) continue; /* found the right frame: */ // debug("found frame to jump back to"); assert (UNW_NUM_EH_REGS >= 2); if (unw_set_reg (&c, UNW_REG_EH + 0, wp[JB_RP]) < 0 || unw_set_reg (&c, UNW_REG_EH + 1, val) < 0 || unw_set_reg (&c, UNW_REG_IP, (unw_word_t) (uintptr_t) &_jffi_longjmp_cont)) abort (); unw_resume (&c); // should not reach here abort (); } while (unw_step (&c) > 0); // debug("failed to find correct frame to jmp to"); }
// // Called by personality handler during phase 2 to find the start of the function // EXPORT uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context* context) { unw_cursor_t* cursor = (unw_cursor_t*)context; unw_proc_info_t frameInfo; uintptr_t result = 0; if ( unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS ) result = frameInfo.start_ip; DEBUG_PRINT_API("_Unwind_GetRegionStart(context=%p) => 0x%lX\n", context, result); return result; }
/// Called by personality handler during phase 2 to find the start of the /// function. _LIBUNWIND_EXPORT uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *context) { unw_cursor_t *cursor = (unw_cursor_t *)context; unw_proc_info_t frameInfo; uintptr_t result = 0; if (unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) result = (uintptr_t)frameInfo.start_ip; _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIXPTR "\n", context, result); return result; }
// // Called by personality handler during phase 2 to get LSDA for current frame // EXPORT uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context) { unw_cursor_t* cursor = (unw_cursor_t*)context; unw_proc_info_t frameInfo; uintptr_t result = 0; if ( unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS ) result = frameInfo.lsda; DEBUG_PRINT_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%lX\n", context, result); if ( result != 0 ) { if ( *((uint8_t*)result) != 0xFF ) DEBUG_MESSAGE("lsda at 0x%lX does not start with 0xFF\n", result); } return result; }
/// Called by personality handler during phase 2 to get LSDA for current frame. _LIBUNWIND_EXPORT uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { unw_cursor_t *cursor = (unw_cursor_t *)context; unw_proc_info_t frameInfo; uintptr_t result = 0; if (unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) result = (uintptr_t)frameInfo.lsda; _LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p)" "=> 0x%" PRIXPTR "\n", context, result); if (result != 0) { if (*((uint8_t *)result) != 0xFF) _LIBUNWIND_DEBUG_LOG("lsda at 0x%" PRIXPTR " does not start with 0xFF\n", result); } return result; }
/// Scans unwind information to find the function that contains the /// specified code address "pc". _LIBUNWIND_EXPORT void *_Unwind_FindEnclosingFunction(void *pc) { _LIBUNWIND_TRACE_API("_Unwind_FindEnclosingFunction(pc=%p)\n", pc); // This is slow, but works. // We create an unwind cursor then alter the IP to be pc unw_cursor_t cursor; unw_context_t uc; unw_proc_info_t info; unw_getcontext(&uc); unw_init_local(&cursor, &uc); unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(long) pc); if (unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS) return (void *)(long) info.start_ip; else return NULL; }
void fill_in_backtrace(FaultData* fdp) { unw_context_t uc; unw_cursor_t c; int i, boff; memset(&uc, 0, sizeof(uc)); memset(&c, 0, sizeof(c)); if (unw_getcontext(&uc) < 0) { abort(); } if (unw_init_local(&c, &uc) < 0) { abort(); } // Skip the signal handler, and the signal trampoline for (i = 0; i < SKIP_FRAME_COUNT; i++) { if (unw_step(&c) <= 0) { break; } } memset(fdp->frame, 0, sizeof(fdp->frame)); fdp->frame_count = 0; boff = 0; do { char fn[256]; unw_word_t off, ip; Dl_info dli; unw_proc_info_t pi; unw_get_reg (&c, UNW_REG_IP, &ip); fdp->frame[fdp->frame_count].addr = (uintptr_t) ip; fdp->frame[fdp->frame_count].procname = (uintptr_t) &fdp->backtrace_buf[boff]; unw_get_proc_name(&c, (char *) fdp->frame[fdp->frame_count].procname, sizeof(fdp->backtrace_buf) - boff, &off); unw_get_proc_info(&c, &pi); boff += strlen((char *) fdp->frame[fdp->frame_count].procname) + 1; fdp->frame[fdp->frame_count].libname = (uintptr_t) &fdp->backtrace_buf[boff]; dladdr((void *)(uintptr_t) ip, &dli); strcpy((char *) (uintptr_t) fdp->frame[fdp->frame_count].libname, dli.dli_fname); boff += strlen((char *) fdp->frame[fdp->frame_count].libname) + 1; fdp->frame_count++; } while (unw_step(&c) > 0); }
G_GNUC_UNUSED static void print_ctx (MonoContext *ctx) { char name[256]; unw_word_t off, ip, sp; unw_proc_info_t pi; int res; unw_get_proc_name (&ctx->cursor, name, 256, &off); unw_get_proc_info(&ctx->cursor, &pi); res = unw_get_reg (&ctx->cursor, UNW_IA64_IP, &ip); g_assert (res == 0); res = unw_get_reg (&ctx->cursor, UNW_IA64_SP, &sp); g_assert (res == 0); printf ("%s:%lx [%lx-%lx] SP: %lx\n", name, ip - pi.start_ip, pi.start_ip, pi.end_ip, sp); }
void zmq::print_backtrace (void) { static zmq::mutex_t mtx; mtx.lock (); Dl_info dl_info; unw_cursor_t cursor; unw_context_t ctx; unsigned frame_n = 0; unw_getcontext (&ctx); unw_init_local (&cursor, &ctx); while (unw_step (&cursor) > 0) { unw_word_t offset; unw_proc_info_t p_info; const char *file_name; char *demangled_name; char func_name[256] = ""; void *addr; int rc; if (unw_get_proc_info (&cursor, &p_info)) break; addr = (void *)(p_info.start_ip + offset); if (dladdr (addr, &dl_info) && dl_info.dli_fname) file_name = dl_info.dli_fname; else file_name = "?"; rc = unw_get_proc_name (&cursor, func_name, 256, &offset); if (rc == -UNW_ENOINFO) strcpy(func_name, "?"); demangled_name = abi::__cxa_demangle (func_name, NULL, NULL, &rc); printf ("#%u %p in %s (%s+0x%lx)\n", frame_n++, addr, file_name, rc ? func_name : demangled_name, (unsigned long) offset); free (demangled_name); } puts (""); fflush (stdout); mtx.unlock (); }
void unwindExc(Box* exc_obj) { unw_cursor_t cursor; unw_context_t uc; unw_word_t ip, sp; unw_getcontext(&uc); unw_init_local(&cursor, &uc); int code; unw_proc_info_t pip; while (unw_step(&cursor) > 0) { unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); printf("ip = %lx, sp = %lx\n", (long)ip, (long)sp); code = unw_get_proc_info(&cursor, &pip); RELEASE_ASSERT(code == 0, ""); // printf("%lx %lx %lx %lx %lx %lx %d %d %p\n", pip.start_ip, pip.end_ip, pip.lsda, pip.handler, pip.gp, // pip.flags, pip.format, pip.unwind_info_size, pip.unwind_info); assert((pip.lsda == 0) == (pip.handler == 0)); assert(pip.flags == 0); if (pip.handler == 0) { if (VERBOSITY()) printf("Skipping frame without handler\n"); continue; } printf("%lx %lx %lx\n", pip.lsda, pip.handler, pip.flags); // assert(pip.handler == (uintptr_t)__gxx_personality_v0 || pip.handler == (uintptr_t)__py_personality_v0); // auto handler_fn = (int (*)(int, int, uint64_t, void*, void*))pip.handler; ////handler_fn(1, 1 /* _UA_SEARCH_PHASE */, 0 /* exc_class */, NULL, NULL); // handler_fn(2, 2 /* _UA_SEARCH_PHASE */, 0 /* exc_class */, NULL, NULL); unw_set_reg(&cursor, UNW_REG_IP, 1); // TODO testing: // unw_resume(&cursor); } abort(); }
/// Find dwarf unwind info for an address 'pc' in some function. _LIBUNWIND_EXPORT const void *_Unwind_Find_FDE(const void *pc, struct dwarf_eh_bases *bases) { // This is slow, but works. // We create an unwind cursor then alter the IP to be pc unw_cursor_t cursor; unw_context_t uc; unw_proc_info_t info; unw_getcontext(&uc); unw_init_local(&cursor, &uc); unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(long) pc); unw_get_proc_info(&cursor, &info); bases->tbase = (uintptr_t)info.extra; bases->dbase = 0; // dbase not used on Mac OS X bases->func = (uintptr_t)info.start_ip; _LIBUNWIND_TRACE_API("_Unwind_Find_FDE(pc=%p) => %p\n", pc, (void *)(long) info.unwind_info); return (void *)(long) info.unwind_info; }
static std::vector<const LineInfo*> getTracebackEntries() { std::vector<const LineInfo*> entries; unw_cursor_t cursor; unw_context_t uc; unw_word_t ip, bp; unw_getcontext(&uc); unw_init_local(&cursor, &uc); int code; unw_proc_info_t pip; while (unw_step(&cursor) > 0) { unw_get_reg(&cursor, UNW_REG_IP, &ip); const LineInfo* line = getLineInfoFor((uint64_t)ip); if (line) { entries.push_back(line); } else { unw_get_reg(&cursor, UNW_TDEP_BP, &bp); unw_proc_info_t pip; code = unw_get_proc_info(&cursor, &pip); RELEASE_ASSERT(code == 0, "%d", code); if (pip.start_ip == (intptr_t)interpretFunction) { line = getLineInfoForInterpretedFrame((void*)bp); assert(line); entries.push_back(line); } } } std::reverse(entries.begin(), entries.end()); return entries; }
void debug_backtrace_capture(struct debug_stack_frame *backtrace, unsigned start_frame, unsigned nr_frames) { unw_cursor_t cursor; unw_context_t context; unw_proc_info_t pip; unsigned i = 0; pip.unwind_info = NULL; unw_getcontext(&context); unw_init_local(&cursor, &context); while ((start_frame > 0) && (unw_step(&cursor) > 0)) start_frame--; while ((i < nr_frames) && (unw_step(&cursor) > 0)) { unw_word_t ip; unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_proc_info(&cursor, &pip); backtrace[i].start_ip = pip.start_ip; backtrace[i].off = ip - pip.start_ip; backtrace[i].procname = symbol_name_cached(&cursor, &pip); i++; } while (i < nr_frames) { backtrace[i].start_ip = 0; i++; } }
int main(int argc UNUSED, char **argv) { unw_addr_space_t as; struct UCD_info *ui; unw_cursor_t c; int ret; #define TEST_FRAMES 4 #define TEST_NAME_LEN 32 int testcase = 0; int test_cur = 0; long test_start_ips[TEST_FRAMES]; char test_names[TEST_FRAMES][TEST_NAME_LEN]; install_sigsegv_handler(); const char *progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; if (!argv[1]) error_msg_and_die("Usage: %s COREDUMP [VADDR:BINARY_FILE]...", progname); msg_prefix = progname; as = unw_create_addr_space(&_UCD_accessors, 0); if (!as) error_msg_and_die("unw_create_addr_space() failed"); ui = _UCD_create(argv[1]); if (!ui) error_msg_and_die("_UCD_create('%s') failed", argv[1]); ret = unw_init_remote(&c, as, ui); if (ret < 0) error_msg_and_die("unw_init_remote() failed: ret=%d\n", ret); argv += 2; /* Enable checks for the crasher test program? */ if (*argv && !strcmp(*argv, "-testcase")) { testcase = 1; logmode = LOGMODE_NONE; argv++; } while (*argv) { char *colon; unsigned long vaddr = strtoul(*argv, &colon, 16); if (*colon != ':') error_msg_and_die("Bad format: '%s'", *argv); if (_UCD_add_backing_file_at_vaddr(ui, vaddr, colon + 1) < 0) error_msg_and_die("Can't add backing file '%s'", colon + 1); argv++; } for (;;) { unw_word_t ip; ret = unw_get_reg(&c, UNW_REG_IP, &ip); if (ret < 0) error_msg_and_die("unw_get_reg(UNW_REG_IP) failed: ret=%d\n", ret); unw_proc_info_t pi; ret = unw_get_proc_info(&c, &pi); if (ret < 0) error_msg_and_die("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret); if (!testcase) printf("\tip=0x%08lx proc=%08lx-%08lx handler=0x%08lx lsda=0x%08lx\n", (long) ip, (long) pi.start_ip, (long) pi.end_ip, (long) pi.handler, (long) pi.lsda); if (testcase && test_cur < TEST_FRAMES) { unw_word_t off; test_start_ips[test_cur] = (long) pi.start_ip; if (unw_get_proc_name(&c, test_names[test_cur], sizeof(test_names[0]), &off) != 0) { test_names[test_cur][0] = '\0'; } test_cur++; } log("step"); ret = unw_step(&c); log("step done:%d", ret); if (ret < 0) error_msg_and_die("FAILURE: unw_step() returned %d", ret); if (ret == 0) break; } log("stepping ended"); /* Check that the second and third frames are equal, but distinct of the * others */ if (testcase && (test_cur != 4 || test_start_ips[1] != test_start_ips[2] || test_start_ips[0] == test_start_ips[1] || test_start_ips[2] == test_start_ips[3] ) ) { fprintf(stderr, "FAILURE: start IPs incorrect\n"); return -1; } if (testcase && ( strcmp(test_names[0], "a") || strcmp(test_names[1], "b") || strcmp(test_names[2], "b") || strcmp(test_names[3], "main") ) ) { fprintf(stderr, "FAILURE: procedure names are missing/incorrect\n"); return -1; } _UCD_destroy(ui); unw_destroy_addr_space(as); return 0; }
PROTECTED _Unwind_Reason_Code _Unwind_RaiseException (struct _Unwind_Exception *exception_object) { uint64_t exception_class = exception_object->exception_class; _Unwind_Personality_Fn personality; struct _Unwind_Context context; _Unwind_Reason_Code reason; unw_proc_info_t pi; unw_context_t uc; unw_word_t ip; int ret; if (_Unwind_InitContext (&context, &uc) < 0) return _URC_FATAL_PHASE1_ERROR; Debug (1, "(exception_object=%p)\n", exception_object); /* Phase 1 (search phase) */ while (1) { if ((ret = unw_step (&context.cursor)) <= 0) { if (ret == 0) { Debug (1, "no handler found\n"); return _URC_END_OF_STACK; } else return _URC_FATAL_PHASE1_ERROR; } if (unw_get_proc_info (&context.cursor, &pi) < 0) return _URC_FATAL_PHASE1_ERROR; personality = (_Unwind_Personality_Fn) (uintptr_t) pi.handler; if (personality) { reason = (*personality) (_U_VERSION, _UA_SEARCH_PHASE, exception_class, exception_object, &context); if (reason != _URC_CONTINUE_UNWIND) { if (reason == _URC_HANDLER_FOUND) break; else { Debug (1, "personality returned %d\n", reason); return _URC_FATAL_PHASE1_ERROR; } } } } /* Exceptions are associated with IP-ranges. If a given exception is handled at a particular IP, it will _always_ be handled at that IP. If this weren't true, we'd have to track the tuple (IP,SP,BSP) to uniquely identify the stack frame that's handling the exception. */ if (unw_get_reg (&context.cursor, UNW_REG_IP, &ip) < 0) return _URC_FATAL_PHASE1_ERROR; exception_object->private_1 = 0; /* clear "stop" pointer */ exception_object->private_2 = ip; /* save frame marker */ Debug (1, "found handler for IP=%lx; entering cleanup phase\n", (long) ip); /* Reset the cursor to the first frame: */ if (unw_init_local (&context.cursor, &uc) < 0) return _URC_FATAL_PHASE1_ERROR; return _Unwind_Phase2 (exception_object, &context); }
/// Walk every frame and call trace function at each one. If trace function /// returns anything other than _URC_NO_REASON, then walk is terminated. _LIBUNWIND_EXPORT _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { unw_cursor_t cursor; unw_context_t uc; unw_getcontext(&uc); unw_init_local(&cursor, &uc); _LIBUNWIND_TRACE_API("_Unwind_Backtrace(callback=%p)\n", callback); // walk each frame while (true) { _Unwind_Reason_Code result; // ask libuwind to get next frame (skip over first frame which is // _Unwind_Backtrace()) if (unw_step(&cursor) <= 0) { _LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because cursor reached " "bottom of stack, returning %d\n", _URC_END_OF_STACK); return _URC_END_OF_STACK; } #if LIBCXXABI_ARM_EHABI // Get the information for this frame. unw_proc_info_t frameInfo; if (unw_get_proc_info(&cursor, &frameInfo) != UNW_ESUCCESS) { return _URC_END_OF_STACK; } struct _Unwind_Context *context = (struct _Unwind_Context *)&cursor; const uint32_t* unwindInfo = (uint32_t *) frameInfo.unwind_info; if ((*unwindInfo & 0x80000000) == 0) { // 6.2: Generic Model // EHT entry is a prel31 pointing to the PR, followed by data understood // only by the personality routine. Since EHABI doesn't guarantee the // location or availability of the unwind opcodes in the generic model, // we have to call personality functions with (_US_VIRTUAL_UNWIND_FRAME | // _US_FORCE_UNWIND) state. // Create a mock exception object for force unwinding. _Unwind_Exception ex; ex.exception_class = 0x434C4E47554E5700; // CLNGUNW\0 ex.pr_cache.fnstart = frameInfo.start_ip; ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo; ex.pr_cache.additional= frameInfo.flags; // Get and call the personality function to unwind the frame. __personality_routine pr = (__personality_routine) readPrel31(unwindInfo); if (pr(_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, &ex, context) != _URC_CONTINUE_UNWIND) { return _URC_END_OF_STACK; } } else { size_t off, len; unwindInfo = decode_eht_entry(unwindInfo, &off, &len); if (unwindInfo == NULL) { return _URC_FAILURE; } result = _Unwind_VRS_Interpret(context, unwindInfo, off, len); if (result != _URC_CONTINUE_UNWIND) { return _URC_END_OF_STACK; } } #endif // LIBCXXABI_ARM_EHABI // debugging if (_LIBUNWIND_TRACING_UNWINDING) { char functionName[512]; unw_proc_info_t frame; unw_word_t offset; unw_get_proc_name(&cursor, functionName, 512, &offset); unw_get_proc_info(&cursor, &frame); _LIBUNWIND_TRACE_UNWINDING( " _backtrace: start_ip=0x%llX, func=%s, lsda=0x%llX, context=%p\n", (long long)frame.start_ip, functionName, (long long)frame.lsda, &cursor); } // call trace function with this frame result = (*callback)((struct _Unwind_Context *)(&cursor), ref); if (result != _URC_NO_REASON) { _LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because callback " "returned %d\n", result); return result; } } }
// The stack-unwinding loop. static inline void unwind_loop(ExcInfo* exc_data) { // NB. https://monoinfinito.wordpress.com/series/exception-handling-in-c/ is a very useful resource // as are http://www.airs.com/blog/archives/460 and http://www.airs.com/blog/archives/464 unw_cursor_t cursor; unw_context_t uc; // exists only to initialize cursor #ifndef NDEBUG // poison stack memory. have had problems with these structures being insufficiently initialized. memset(&uc, 0xef, sizeof uc); memset(&cursor, 0xef, sizeof cursor); #endif unw_getcontext(&uc); unw_init_local(&cursor, &uc); auto unwind_session = getActivePythonUnwindSession(); while (unw_step(&cursor) > 0) { unw_proc_info_t pip; static StatCounter frames_unwound("num_frames_unwound_cxx"); frames_unwound.log(); // NB. unw_get_proc_info is slow; a significant chunk of all time spent unwinding is spent here. check(unw_get_proc_info(&cursor, &pip)); assert((pip.lsda == 0) == (pip.handler == 0)); assert(pip.flags == 0); if (VERBOSITY("cxx_unwind") >= 4) { print_frame(&cursor, &pip); } // let the PythonUnwindSession know that we're in a new frame, // giving it a chance to possibly add a traceback entry for // it. unwindingThroughFrame(unwind_session, &cursor); // Skip frames without handlers if (pip.handler == 0) { continue; } RELEASE_ASSERT(pip.handler == (uintptr_t)__gxx_personality_v0, "personality function other than __gxx_personality_v0; " "don't know how to unwind through non-C++ functions"); // Don't call __gxx_personality_v0; we perform dispatch ourselves. // 1. parse LSDA header lsda_info_t info; parse_lsda_header(&pip, &info); call_site_entry_t entry; { // 2. Find our current IP in the call site table. unw_word_t ip; unw_get_reg(&cursor, UNW_REG_IP, &ip); // ip points to the instruction *after* the instruction that caused the error - which is generally (always?) // a call instruction - UNLESS we're in a signal frame, in which case it points at the instruction that // caused the error. For now, we assume we're never in a signal frame. So, we decrement it by one. // // TODO: double-check that we never hit a signal frame. --ip; bool found = find_call_site_entry(&info, (const uint8_t*)ip, &entry); // If we didn't find an entry, an exception happened somewhere exceptions should never happen; terminate // immediately. if (!found) { panic(); } } // 3. Figure out what to do based on the call site entry. if (!entry.landing_pad) { // No landing pad means no exception handling or cleanup; keep unwinding! continue; } // After this point we are guaranteed to resume something rather than unwinding further. if (VERBOSITY("cxx_unwind") >= 4) { print_lsda(&info); } int64_t switch_value = determine_action(&info, &entry); if (switch_value != CLEANUP_ACTION) { // we're transfering control to a non-cleanup landing pad. // i.e. a catch block. thus ends our unwind session. endPythonUnwindSession(unwind_session); #if STAT_TIMERS pyston::StatTimer::finishOverride(); #endif } static_assert(THREADING_USE_GIL, "have to make the unwind session usage in this file thread safe!"); // there is a python unwinding implementation detail leaked // here - that the unwind session can be ended but its // exception storage is still around. // // this manifests itself as this short window here where we've // (possibly) ended the unwind session above but we still need // to pass exc_data (which is the exceptionStorage for this // unwind session) to resume(). // // the only way this could bite us is if we somehow clobber // the PythonUnwindSession's storage, or cause a GC to occur, before // transfering control to the landing pad in resume(). // resume(&cursor, entry.landing_pad, switch_value, exc_data); } // Hit end of stack! return & let unwindException determine what to do. }
void do_backtrace(void) { unw_word_t ip, sp, start_ip = 0, off; int n = 0, ret; unw_proc_info_t pi; unw_cursor_t c; char buf[512]; size_t len; ret = unw_init_remote(&c, as, ui); if (ret < 0) panic ("unw_init_remote() failed: ret=%d\n", ret); do { if ((ret = unw_get_reg(&c, UNW_REG_IP, &ip)) < 0 || (ret = unw_get_reg(&c, UNW_REG_SP, &sp)) < 0) panic ("unw_get_reg/unw_get_proc_name() failed: ret=%d\n", ret); if (n == 0) start_ip = ip; buf[0] = '\0'; if (print_names) unw_get_proc_name(&c, buf, sizeof(buf), &off); if (verbose) { if (off) { len = strlen(buf); if (len >= sizeof(buf) - 32) len = sizeof(buf) - 32; sprintf(buf + len, "+0x%lx", (unsigned long) off); } printf("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp); } if ((ret = unw_get_proc_info(&c, &pi)) < 0) panic ("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret); else if (verbose) printf("\tproc=%016lx-%016lx\n\thandler=%lx lsda=%lx", (long) pi.start_ip, (long) pi.end_ip, (long) pi.handler, (long) pi.lsda); if (verbose) printf("\n"); ret = unw_step(&c); if (ret < 0) { unw_get_reg(&c, UNW_REG_IP, &ip); panic ("FAILURE: unw_step() returned %d for ip=%lx (start ip=%lx)\n", ret, (long) ip, (long) start_ip); } if (++n > 64) { /* guard against bad unwind info in old libraries... */ panic ("too deeply nested---assuming bogus unwind (start ip=%lx)\n", (long) start_ip); break; } if (nerrors > nerrors_max) { panic ("Too many errors (%d)!\n", nerrors); break; } } while (ret > 0); if (ret < 0) panic ("unwind failed with ret=%d\n", ret); if (verbose) printf("================\n\n"); }
static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, _Unwind_Exception *exception_object) { unw_cursor_t cursor2; unw_init_local(&cursor2, uc); _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object); // Walk each frame until we reach where search phase said to stop. while (true) { // Ask libuwind to get next frame (skip over first which is // _Unwind_RaiseException). int stepResult = unw_step(&cursor2); if (stepResult == 0) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached " "bottom => _URC_END_OF_STACK\n", exception_object); return _URC_END_OF_STACK; } else if (stepResult < 0) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step failed => " "_URC_FATAL_PHASE1_ERROR\n", exception_object); return _URC_FATAL_PHASE2_ERROR; } // Get info about this frame. unw_word_t sp; unw_proc_info_t frameInfo; unw_get_reg(&cursor2, UNW_REG_SP, &sp); if (unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_get_proc_info " "failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); return _URC_FATAL_PHASE2_ERROR; } // When tracing, print state information. if (_LIBUNWIND_TRACING_UNWINDING) { char functionName[512]; unw_word_t offset; if ((unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip + offset > frameInfo.end_ip)) strcpy(functionName, ".anonymous."); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2(ex_ojb=%p): start_ip=0x%llX, func=%s, sp=0x%llX, " "lsda=0x%llX, personality=0x%llX\n", exception_object, frameInfo.start_ip, functionName, sp, frameInfo.lsda, frameInfo.handler); } // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { __personality_routine p = (__personality_routine)(long)(frameInfo.handler); _Unwind_Action action = _UA_CLEANUP_PHASE; if (sp == exception_object->private_2) { // Tell personality this was the frame it marked in phase 1. action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME); } _Unwind_Reason_Code personalityResult = (*p)(1, action, exception_object->exception_class, exception_object, (struct _Unwind_Context *)(&cursor2)); switch (personalityResult) { case _URC_CONTINUE_UNWIND: // Continue unwinding _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); if (sp == exception_object->private_2) { // Phase 1 said we would stop at this frame, but we did not... _LIBUNWIND_ABORT("during phase1 personality function said it would " "stop here, but now in phase2 it did not stop here"); } break; case _URC_INSTALL_CONTEXT: _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT\n", exception_object); // Personality routine says to transfer control to landing pad. // We may get control back if landing pad calls _Unwind_Resume(). if (_LIBUNWIND_TRACING_UNWINDING) { unw_word_t pc; unw_get_reg(&cursor2, UNW_REG_IP, &pc); unw_get_reg(&cursor2, UNW_REG_SP, &sp); _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering " "user code with ip=0x%llX, sp=0x%llX\n", exception_object, pc, sp); } unw_resume(&cursor2); // unw_resume() only returns if there was an error. return _URC_FATAL_PHASE2_ERROR; default: // Personality routine returned an unknown result code. _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", personalityResult); return _URC_FATAL_PHASE2_ERROR; } } } // Clean up phase did not resume at the frame that the search phase // said it would... return _URC_FATAL_PHASE2_ERROR; }
static _Unwind_Reason_Code unwind_phase2_forced(unw_context_t *uc, _Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, void *stop_parameter) { unw_cursor_t cursor2; unw_init_local(&cursor2, uc); // Walk each frame until we reach where search phase said to stop while (unw_step(&cursor2) > 0) { // Update info about this frame. unw_proc_info_t frameInfo; if (unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): unw_step " "failed => _URC_END_OF_STACK\n", exception_object); return _URC_FATAL_PHASE2_ERROR; } // When tracing, print state information. if (_LIBUNWIND_TRACING_UNWINDING) { char functionName[512]; unw_word_t offset; if ((unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip + offset > frameInfo.end_ip)) strcpy(functionName, ".anonymous."); _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " "start_ip=0x%llX, func=%s, lsda=0x%llX, " " personality=0x%llX\n", exception_object, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler); } // Call stop function at each frame. _Unwind_Action action = (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); _Unwind_Reason_Code stopResult = (*stop)(1, action, exception_object->exception_class, exception_object, (struct _Unwind_Context *)(&cursor2), stop_parameter); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n", exception_object, stopResult); if (stopResult != _URC_NO_REASON) { _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n", exception_object); return _URC_FATAL_PHASE2_ERROR; } // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { __personality_routine p = (__personality_routine)(long)(frameInfo.handler); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n", exception_object, p); _Unwind_Reason_Code personalityResult = (*p)(1, action, exception_object->exception_class, exception_object, (struct _Unwind_Context *)(&cursor2)); switch (personalityResult) { case _URC_CONTINUE_UNWIND: _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " "personality returned _URC_CONTINUE_UNWIND\n", exception_object); // Destructors called, continue unwinding break; case _URC_INSTALL_CONTEXT: _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " "personality returned _URC_INSTALL_CONTEXT\n", exception_object); // We may get control back if landing pad calls _Unwind_Resume(). unw_resume(&cursor2); break; default: // Personality routine returned an unknown result code. _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " "personality returned %d, " "_URC_FATAL_PHASE2_ERROR\n", exception_object, personalityResult); return _URC_FATAL_PHASE2_ERROR; } } } // Call stop function one last time and tell it we've reached the end // of the stack. _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " "function with _UA_END_OF_STACK\n", exception_object); _Unwind_Action lastAction = (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); (*stop)(1, lastAction, exception_object->exception_class, exception_object, (struct _Unwind_Context *)(&cursor2), stop_parameter); // Clean up phase did not resume at the frame that the search phase said it // would. return _URC_FATAL_PHASE2_ERROR; }
static _Unwind_Reason_Code unwind_phase1(unw_context_t *uc, struct _Unwind_Exception *exception_object) { // EHABI #7.3 discusses preserving the VRS in a "temporary VRS" during // phase 1 and then restoring it to the "primary VRS" for phase 2. The // effect is phase 2 doesn't see any of the VRS manipulations from phase 1. // In this implementation, the phases don't share the VRS backing store. // Instead, they are passed the original |uc| and they create a new VRS // from scratch thus achieving the same effect. unw_cursor_t cursor1; unw_init_local(&cursor1, uc); // Walk each frame looking for a place to stop. for (bool handlerNotFound = true; handlerNotFound;) { // Ask libuwind to get next frame (skip over first which is // _Unwind_RaiseException). int stepResult = unw_step(&cursor1); if (stepResult == 0) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step() reached " "bottom => _URC_END_OF_STACK\n", exception_object); return _URC_END_OF_STACK; } else if (stepResult < 0) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step failed => " "_URC_FATAL_PHASE1_ERROR\n", exception_object); return _URC_FATAL_PHASE1_ERROR; } // See if frame has code to run (has personality routine). unw_proc_info_t frameInfo; unw_word_t sp; if (unw_get_proc_info(&cursor1, &frameInfo) != UNW_ESUCCESS) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_get_proc_info " "failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); return _URC_FATAL_PHASE1_ERROR; } // When tracing, print state information. if (_LIBUNWIND_TRACING_UNWINDING) { char functionName[512]; unw_word_t offset; if ((unw_get_proc_name(&cursor1, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip + offset > frameInfo.end_ip)) strcpy(functionName, ".anonymous."); unw_word_t pc; unw_get_reg(&cursor1, UNW_REG_IP, &pc); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase1(ex_ojb=%p): pc=0x%llX, start_ip=0x%lX, func=%s, " "lsda=0x%llX, personality=0x%llX\n", exception_object, pc, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler); } // If there is a personality routine, ask it if it will want to stop at // this frame. if (frameInfo.handler != 0) { __personality_routine p = (__personality_routine)(long)(frameInfo.handler); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase1(ex_ojb=%p): calling personality function %p\n", exception_object, p); struct _Unwind_Context *context = (struct _Unwind_Context *)(&cursor1); #ifdef __arm__ exception_object->pr_cache.fnstart = frameInfo.start_ip; exception_object->pr_cache.ehtp = (_Unwind_EHT_Header *)frameInfo.unwind_info; exception_object->pr_cache.additional = frameInfo.flags; _Unwind_Reason_Code personalityResult = (*p)(_US_VIRTUAL_UNWIND_FRAME, exception_object, context); #else _Unwind_Reason_Code personalityResult = (*p)(1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object, context); #endif switch (personalityResult) { case _URC_HANDLER_FOUND: // found a catch clause or locals that need destructing in this frame // stop search and remember stack pointer at the frame handlerNotFound = false; #ifndef __arm__ unw_get_reg(&cursor1, UNW_REG_SP, &sp); exception_object->private_2 = (uintptr_t)sp; #else // p should have initialized barrier_cache. #7.3.5 #endif _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): " "_URC_HANDLER_FOUND \n", exception_object); return _URC_NO_REASON; case _URC_CONTINUE_UNWIND: _LIBUNWIND_TRACE_UNWINDING( "unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); // continue unwinding break; #ifdef __arm__ // # 7.3.3 case _URC_FAILURE: return _URC_FAILURE; #endif default: // something went wrong _LIBUNWIND_TRACE_UNWINDING( "unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n", exception_object); return _URC_FATAL_PHASE1_ERROR; } } } return _URC_NO_REASON; }
static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, struct _Unwind_Exception *exception_object, bool resume) { // See comment at the start of unwind_phase1 regarding VRS integrity. unw_cursor_t cursor2; unw_init_local(&cursor2, uc); _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object); int frame_count = 0; // Walk each frame until we reach where search phase said to stop. while (true) { // Ask libuwind to get next frame (skip over first which is // _Unwind_RaiseException or _Unwind_Resume). // // Resume only ever makes sense for 1 frame. _Unwind_State state = resume ? _US_UNWIND_FRAME_RESUME : _US_UNWIND_FRAME_STARTING; if (resume && frame_count == 1) { // On a resume, first unwind the _Unwind_Resume() frame. The next frame // is now the landing pad for the cleanup from a previous execution of // phase2. To continue unwindingly correctly, replace VRS[15] with the // IP of the frame that the previous run of phase2 installed the context // for. After this, continue unwinding as if normal. // // See #7.4.6 for details. unw_set_reg(&cursor2, UNW_REG_IP, exception_object->unwinder_cache.reserved2); resume = false; } int stepResult = unw_step(&cursor2); if (stepResult == 0) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached " "bottom => _URC_END_OF_STACK\n", exception_object); return _URC_END_OF_STACK; } else if (stepResult < 0) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step failed => " "_URC_FATAL_PHASE1_ERROR\n", exception_object); return _URC_FATAL_PHASE2_ERROR; } // Get info about this frame. unw_word_t sp; unw_proc_info_t frameInfo; unw_get_reg(&cursor2, UNW_REG_SP, &sp); if (unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_get_proc_info " "failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); return _URC_FATAL_PHASE2_ERROR; } // When tracing, print state information. if (_LIBUNWIND_TRACING_UNWINDING) { char functionName[512]; unw_word_t offset; if ((unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip + offset > frameInfo.end_ip)) strcpy(functionName, ".anonymous."); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2(ex_ojb=%p): start_ip=0x%llX, func=%s, sp=0x%llX, " "lsda=0x%llX, personality=0x%llX\n", exception_object, frameInfo.start_ip, functionName, sp, frameInfo.lsda, frameInfo.handler); } // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { __personality_routine p = (__personality_routine)(long)(frameInfo.handler); struct _Unwind_Context *context = (struct _Unwind_Context *)(&cursor2); #ifdef __arm__ exception_object->pr_cache.fnstart = frameInfo.start_ip; exception_object->pr_cache.ehtp = (_Unwind_EHT_Header *)frameInfo.unwind_info; exception_object->pr_cache.additional = frameInfo.flags; _Unwind_Reason_Code personalityResult = (*p)(state, exception_object, context); #else _Unwind_Action action = _UA_CLEANUP_PHASE; if (sp == exception_object->private_2) { // Tell personality this was the frame it marked in phase 1. action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME); } _Unwind_Reason_Code personalityResult = (*p)(1, action, exception_object->exception_class, exception_object, context); #endif switch (personalityResult) { case _URC_CONTINUE_UNWIND: // Continue unwinding _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); #ifdef __arm__ if (sp == exception_object->barrier_cache.sp) { #else if (sp == exception_object->private_2) { #endif // Phase 1 said we would stop at this frame, but we did not... _LIBUNWIND_ABORT("during phase1 personality function said it would " "stop here, but now in phase2 it did not stop here"); } break; case _URC_INSTALL_CONTEXT: _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT\n", exception_object); // Personality routine says to transfer control to landing pad. // We may get control back if landing pad calls _Unwind_Resume(). if (_LIBUNWIND_TRACING_UNWINDING) { unw_word_t pc; unw_get_reg(&cursor2, UNW_REG_IP, &pc); unw_get_reg(&cursor2, UNW_REG_SP, &sp); _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering " "user code with ip=0x%llX, sp=0x%llX\n", exception_object, pc, sp); } #ifdef __arm__ // #7.4.1 says we need to preserve pc for when _Unwind_Resume is called // back, to find this same frame. unw_word_t pc; unw_get_reg(&cursor2, UNW_REG_IP, &pc); exception_object->unwinder_cache.reserved2 = (uint32_t)pc; #endif unw_resume(&cursor2); // unw_resume() only returns if there was an error. return _URC_FATAL_PHASE2_ERROR; #ifdef __arm__ // # 7.4.3 case _URC_FAILURE: abort(); #endif default: // Personality routine returned an unknown result code. _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", personalityResult); return _URC_FATAL_PHASE2_ERROR; } } frame_count++; } // Clean up phase did not resume at the frame that the search phase // said it would... return _URC_FATAL_PHASE2_ERROR; } #ifndef __arm__ static _Unwind_Reason_Code unwind_phase2_forced(unw_context_t *uc, struct _Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, void *stop_parameter) { unw_cursor_t cursor2; unw_init_local(&cursor2, uc); // Walk each frame until we reach where search phase said to stop while (unw_step(&cursor2) > 0) { // Update info about this frame. unw_proc_info_t frameInfo; if (unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): unw_step " "failed => _URC_END_OF_STACK\n", exception_object); return _URC_FATAL_PHASE2_ERROR; } // When tracing, print state information. if (_LIBUNWIND_TRACING_UNWINDING) { char functionName[512]; unw_word_t offset; if ((unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip + offset > frameInfo.end_ip)) strcpy(functionName, ".anonymous."); _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " "start_ip=0x%llX, func=%s, lsda=0x%llX, " " personality=0x%llX\n", exception_object, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler); } // Call stop function at each frame. _Unwind_Action action = (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); _Unwind_Reason_Code stopResult = (*stop)(1, action, exception_object->exception_class, exception_object, (struct _Unwind_Context *)(&cursor2), stop_parameter); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n", exception_object, stopResult); if (stopResult != _URC_NO_REASON) { _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n", exception_object); return _URC_FATAL_PHASE2_ERROR; } // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { __personality_routine p = (__personality_routine)(long)(frameInfo.handler); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n", exception_object, p); _Unwind_Reason_Code personalityResult = (*p)(1, action, exception_object->exception_class, exception_object, (struct _Unwind_Context *)(&cursor2)); switch (personalityResult) { case _URC_CONTINUE_UNWIND: _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " "personality returned _URC_CONTINUE_UNWIND\n", exception_object); // Destructors called, continue unwinding break; case _URC_INSTALL_CONTEXT: _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " "personality returned _URC_INSTALL_CONTEXT\n", exception_object); // We may get control back if landing pad calls _Unwind_Resume(). unw_resume(&cursor2); break; default: // Personality routine returned an unknown result code. _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " "personality returned %d, " "_URC_FATAL_PHASE2_ERROR\n", exception_object, personalityResult); return _URC_FATAL_PHASE2_ERROR; } } } // Call stop function one last time and tell it we've reached the end // of the stack. _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " "function with _UA_END_OF_STACK\n", exception_object); _Unwind_Action lastAction = (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); (*stop)(1, lastAction, exception_object->exception_class, exception_object, (struct _Unwind_Context *)(&cursor2), stop_parameter); // Clean up phase did not resume at the frame that the search phase said it // would. return _URC_FATAL_PHASE2_ERROR; }
static void chpl_stack_unwind(void){ // This is just a prototype using libunwind unw_cursor_t cursor; unw_context_t uc; unw_word_t wordValue; char buffer[128]; unsigned int line; #ifdef __linux__ unw_proc_info_t info; // Get the exec path and name for the precise line printing char process_name[128]; line = readlink("/proc/self/exe", process_name, sizeof(process_name)); // It unlikely to happen but this means that the process name is too big // for our buffer. In this case, we truncate the name if(line == sizeof(process_name)) line = sizeof(process_name)-1; process_name[line] = '\0'; #endif // Check if we need to print the stack trace (default = yes) if(! chpl_get_rt_env_bool("UNWIND", true)) { return; } line = 0; unw_getcontext(&uc); unw_init_local(&cursor, &uc); if(chpl_sizeSymTable > 0) fprintf(stderr,"Stacktrace\n\n"); // This loop does the effective stack unwind, see libunwind documentation while (unw_step(&cursor) > 0) { unw_get_proc_name(&cursor, buffer, sizeof(buffer), &wordValue); // Since this stack trace is printed out a program exit, we do not believe // it is performance sensitive. Additionally, this initial implementation // favors simplicity over performance. // // If it becomes necessary to improve performance, this code could use be // faster using one of these two strategies: // 1) Use a hashtable or map to find entries in chpl_funSymTable, or // 2) Emit chpl_funSymTable in sorted order and use binary search on it for(int t = 0; t < chpl_sizeSymTable; t+=2 ){ if (!strcmp(chpl_funSymTable[t], buffer)){ #ifdef __linux__ // Maybe we can get a more precise line number unw_get_proc_info(&cursor, &info); line = chpl_unwind_getLineNum(process_name, (void *)(info.start_ip + wordValue)); // We wasn't able to obtain the line number, let's use the procedure // line number if(line == 0) line = chpl_filenumSymTable[t+1]; #else line = chpl_filenumSymTable[t+1]; #endif fprintf(stderr,"%s() at %s:%d\n", chpl_funSymTable[t+1], chpl_lookupFilename(chpl_filenumSymTable[t]), line); break; } } } }
/* Benefit from maps being sorted by address */ if (addr < mapsList[i].start) { break; } } return NULL; } #ifndef __ANDROID__ size_t arch_unwindStack(pid_t pid, funcs_t * funcs) { size_t num_frames = 0, mapsCnt = 0; procMap_t *mapsList = arch_parsePidMaps(pid, &mapsCnt); defer { free(mapsList); }; unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER); if (!as) { LOG_E("[pid='%d'] unw_create_addr_space failed", pid); return num_frames; } defer { unw_destroy_addr_space(as); }; void *ui = _UPT_create(pid); if (ui == NULL) { LOG_E("[pid='%d'] _UPT_create failed", pid); return num_frames; } defer { _UPT_destroy(ui); }; unw_cursor_t c; int ret = unw_init_remote(&c, as, ui); if (ret < 0) { LOG_E("[pid='%d'] unw_init_remote failed (%s)", pid, UNW_ER[-ret]); return num_frames; } for (num_frames = 0; unw_step(&c) > 0 && num_frames < _HF_MAX_FUNCS; num_frames++) { unw_word_t ip; char *mapName = NULL; ret = unw_get_reg(&c, UNW_REG_IP, &ip); if (ret < 0) { LOG_E("[pid='%d'] [%zd] failed to read IP (%s)", pid, num_frames, UNW_ER[-ret]); funcs[num_frames].pc = 0; } else { funcs[num_frames].pc = (void *)ip; } if (mapsCnt > 0 && (mapName = arch_searchMaps(ip, mapsCnt, mapsList)) != NULL) { memcpy(funcs[num_frames].mapName, mapName, sizeof(funcs[num_frames].mapName)); } else { strncpy(funcs[num_frames].mapName, "UNKNOWN", sizeof(funcs[num_frames].mapName)); } } return num_frames; } #else /* !defined(__ANDROID__) */ size_t arch_unwindStack(pid_t pid, funcs_t * funcs) { size_t num_frames = 0, mapsCnt = 0; procMap_t *mapsList = arch_parsePidMaps(pid, &mapsCnt); defer { free(mapsList); } unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, __BYTE_ORDER); if (!as) { LOG_E("[pid='%d'] unw_create_addr_space failed", pid); return num_frames; } defer { unw_destroy_addr_space(as); }; struct UPT_info *ui = (struct UPT_info *)_UPT_create(pid); if (ui == NULL) { LOG_E("[pid='%d'] _UPT_create failed", pid); return num_frames; } defer { _UPT_destroy(ui); }; unw_cursor_t cursor; int ret = unw_init_remote(&cursor, as, ui); if (ret < 0) { LOG_E("[pid='%d'] unw_init_remote failed (%s)", pid, UNW_ER[-ret]); return num_frames; } do { char *mapName = NULL; unw_word_t pc = 0, offset = 0; char buf[_HF_FUNC_NAME_SZ] = { 0 }; ret = unw_get_reg(&cursor, UNW_REG_IP, &pc); if (ret < 0) { LOG_E("[pid='%d'] [%zd] failed to read IP (%s)", pid, num_frames, UNW_ER[-ret]); // We don't want to try to extract info from an arbitrary IP // TODO: Maybe abort completely (goto out)) goto skip_frame_info; } unw_proc_info_t frameInfo; ret = unw_get_proc_info(&cursor, &frameInfo); if (ret < 0) { LOG_D("[pid='%d'] [%zd] unw_get_proc_info (%s)", pid, num_frames, UNW_ER[-ret]); // Not safe to keep parsing frameInfo goto skip_frame_info; } ret = unw_get_proc_name(&cursor, buf, sizeof(buf), &offset); if (ret < 0) { LOG_D("[pid='%d'] [%zd] unw_get_proc_name() failed (%s)", pid, num_frames, UNW_ER[-ret]); buf[0] = '\0'; } skip_frame_info: // Compared to bfd, line var plays the role of offset from func_name // Reports format is adjusted accordingly to reflect in saved file funcs[num_frames].line = offset; funcs[num_frames].pc = (void *)pc; memcpy(funcs[num_frames].func, buf, sizeof(funcs[num_frames].func)); if (mapsCnt > 0 && (mapName = arch_searchMaps(pc, mapsCnt, mapsList)) != NULL) { memcpy(funcs[num_frames].mapName, mapName, sizeof(funcs[num_frames].mapName)); } else { strncpy(funcs[num_frames].mapName, "UNKNOWN", sizeof(funcs[num_frames].mapName)); } num_frames++; ret = unw_step(&cursor); } while (ret > 0 && num_frames < _HF_MAX_FUNCS); return num_frames; }
static _Unwind_Reason_Code unwind_phase1(unw_context_t *uc, _Unwind_Exception *exception_object) { unw_cursor_t cursor1; unw_init_local(&cursor1, uc); // Walk each frame looking for a place to stop. for (bool handlerNotFound = true; handlerNotFound;) { // Ask libuwind to get next frame (skip over first which is // _Unwind_RaiseException). int stepResult = unw_step(&cursor1); if (stepResult == 0) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step() reached " "bottom => _URC_END_OF_STACK\n", exception_object); return _URC_END_OF_STACK; } else if (stepResult < 0) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step failed => " "_URC_FATAL_PHASE1_ERROR\n", exception_object); return _URC_FATAL_PHASE1_ERROR; } // See if frame has code to run (has personality routine). unw_proc_info_t frameInfo; unw_word_t sp; if (unw_get_proc_info(&cursor1, &frameInfo) != UNW_ESUCCESS) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_get_proc_info " "failed => _URC_FATAL_PHASE1_ERROR\n", exception_object); return _URC_FATAL_PHASE1_ERROR; } // When tracing, print state information. if (_LIBUNWIND_TRACING_UNWINDING) { char functionName[512]; unw_word_t offset; if ((unw_get_proc_name(&cursor1, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip + offset > frameInfo.end_ip)) strcpy(functionName, ".anonymous."); unw_word_t pc; unw_get_reg(&cursor1, UNW_REG_IP, &pc); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase1(ex_ojb=%p): pc=0x%llX, start_ip=0x%llX, func=%s, " "lsda=0x%llX, personality=0x%llX\n", exception_object, pc, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler); } // If there is a personality routine, ask it if it will want to stop at // this frame. if (frameInfo.handler != 0) { __personality_routine p = (__personality_routine)(long)(frameInfo.handler); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase1(ex_ojb=%p): calling personality function %p\n", exception_object, p); _Unwind_Reason_Code personalityResult = (*p)(1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object, (struct _Unwind_Context *)(&cursor1)); switch (personalityResult) { case _URC_HANDLER_FOUND: // found a catch clause or locals that need destructing in this frame // stop search and remember stack pointer at the frame handlerNotFound = false; unw_get_reg(&cursor1, UNW_REG_SP, &sp); exception_object->private_2 = (uintptr_t)sp; _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): " "_URC_HANDLER_FOUND \n", exception_object); return _URC_NO_REASON; case _URC_CONTINUE_UNWIND: _LIBUNWIND_TRACE_UNWINDING( "unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object); // continue unwinding break; default: // something went wrong _LIBUNWIND_TRACE_UNWINDING( "unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n", exception_object); return _URC_FATAL_PHASE1_ERROR; } } } return _URC_NO_REASON; }