void _dump_crash_report(unsigne pid) // shortened code from android's debuggerd // to get a backtrace on ARM { unw_addr_space_t as; struct UPT_info *ui; unw_cursor_t cursor; as = unw_create_addr_space(&_UPT_accessors, 0); ui = _UPT_create(pid); int ret = unw_init_remote(&cursor, as, ui); if (ret < 0) { PRINTF("WARNING: unw_init_remote() failed [pid %i]\n", pid); _UPT_destroy(ui); return; } PRINTF("DEBUG: backtrace of the remote process (pid %d) using libunwind-ptrace:\n", pid); do { unw_word_t ip, sp, offp; char buf[512]; unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); unw_get_proc_name(&cursor, buf, sizeof (buf), &offp); PRINTF("DEBUG: ip: %10p, sp: %10p %s\n", (void*) ip, (void*) sp, buf); } while ((ret = unw_step (&cursor)) > 0); _UPT_destroy (ui); }
static void destroy_unwind(struct Process *proc) { #if defined(HAVE_LIBUNWIND) _UPT_destroy(proc->unwind_priv); unw_destroy_addr_space(proc->unwind_as); #endif /* defined(HAVE_LIBUNWIND) */ }
int backtrace_ptrace(int pid, int *tids, int *index, int nr_tids) { #if !defined (NO_LIBUNWIND_PTRACE) int i, count, rc = 0; int *threads = NULL; count = get_threads(pid, &threads); if (!count || threads == NULL) return -1; if (tids != NULL) { if (adjust_threads(threads, count, tids, index, nr_tids) < 0) return -1; free(threads); count = nr_tids; threads = tids; } if (attach_process(pid) < 0) return -1; for (i = 0; i < count; ++i) { void *upt_info; printf("-------------------- thread %d (%d) --------------------\n", (index != NULL ? index[i] : i+1), threads[i]); if (threads[i] != pid && attach_thread(threads[i]) < 0) { rc = -1; break; } upt_info = _UPT_create(threads[i]); if (backtrace_thread(&_UPT_accessors, upt_info) < 0) rc = -1; _UPT_destroy(upt_info); if (threads[i] != pid && detach_thread(threads[i])) rc = -1; if (rc < 0) break; } free(threads); if (detach_process(pid) < 0) return -1; return rc; #else return -1; #endif /* NO_LIBUNWIND_PTRACE */ }
UnwindPtrace::~UnwindPtrace() { if (upt_info_) { _UPT_destroy(upt_info_); upt_info_ = NULL; } if (addr_space_) { unw_destroy_addr_space(addr_space_); addr_space_ = NULL; } }
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; }
UnwindPtrace::~UnwindPtrace() { if (upt_info_) { _UPT_destroy(upt_info_); upt_info_ = NULL; } if (addr_space_) { // Remove the map from the address space before destroying it. // It will be freed in the UnwindMap destructor. unw_map_set(addr_space_, NULL); unw_destroy_addr_space(addr_space_); addr_space_ = NULL; } }
BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, DWORD pid, ReadMemoryWordCallback readMemCallback) { // This function can be executed only by one thread at a time. // The reason for this is that we need to pass context and read mem function to libunwind callbacks // but "arg" is already used by the pointer returned from _UPT_create(). // So we resort to using global variables and a lock. struct Lock { CRITICAL_SECTION cs; Lock() { // ctor of a static variable is a thread-safe way to initialize critical section exactly once (clang,gcc) InitializeCriticalSection(&cs); } }; struct LockHolder { CRITICAL_SECTION *cs; LockHolder(CRITICAL_SECTION *cs) { this->cs = cs; EnterCriticalSection(cs); } ~LockHolder() { LeaveCriticalSection(cs); cs = NULL; } }; static Lock lock; LockHolder lockHolder(&lock.cs); int st; unw_context_t unwContext; unw_cursor_t cursor; unw_addr_space_t addrSpace = 0; void *libunwindUptPtr = NULL; BOOL result = FALSE; LibunwindCallbacksInfo.Context = context; LibunwindCallbacksInfo.readMemCallback = readMemCallback; WinContextToUnwindContext(context, &unwContext); addrSpace = unw_create_addr_space(&unwind_accessors, 0); libunwindUptPtr = _UPT_create(pid); st = unw_init_remote(&cursor, addrSpace, libunwindUptPtr); if (st < 0) { result = FALSE; goto Exit; } st = unw_step(&cursor); if (st < 0) { result = FALSE; goto Exit; } UnwindContextToWinContext(&cursor, context); if (contextPointers != NULL) { GetContextPointers(&cursor, &unwContext, contextPointers); } result = TRUE; Exit: if (libunwindUptPtr != nullptr) { _UPT_destroy(libunwindUptPtr); } if (addrSpace != 0) { unw_destroy_addr_space(addrSpace); } return result; }
static void print_stack_trace(pid_t pid, int * count) { void * pinfo = NULL; unw_addr_space_t aspace = NULL; unw_cursor_t cursor; unw_word_t ip, sp; char nbuf[256]; unw_word_t off; int ret; if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) { fprintf(stderr, "Failed to attach to process %llu: %s\n", (unsigned long long)pid, strerror(errno)); return; } /* Wait until the attach is complete. */ waitpid(pid, NULL, 0); if (((pinfo = _UPT_create(pid)) == NULL) || ((aspace = unw_create_addr_space(&_UPT_accessors, 0)) == NULL)) { /* Probably out of memory. */ fprintf(stderr, "Unable to initialize stack unwind for process %llu\n", (unsigned long long)pid); goto cleanup; } if ((ret = unw_init_remote(&cursor, aspace, pinfo))) { fprintf(stderr, "Unable to unwind stack for process %llu: %s\n", (unsigned long long)pid, unw_strerror(ret)); goto cleanup; } if (*count > 0) { printf("\n"); } if (procname(pid, nbuf, sizeof(nbuf))) { printf("Stack trace for process %llu (%s):\n", (unsigned long long)pid, nbuf); } else { printf("Stack trace for process %llu:\n", (unsigned long long)pid); } while (unw_step(&cursor) > 0) { ip = sp = off = 0; unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); ret = unw_get_proc_name(&cursor, nbuf, sizeof(nbuf), &off); if (ret != 0 && ret != -UNW_ENOMEM) { snprintf(nbuf, sizeof(nbuf), "<unknown symbol>"); } printf(" %s + %#llx [ip=%#llx] [sp=%#llx]\n", nbuf, (long long)off, (long long)ip, (long long)sp); } (*count)++; cleanup: if (aspace) { unw_destroy_addr_space(aspace); } if (pinfo) { _UPT_destroy(pinfo); } ptrace(PTRACE_DETACH, pid, NULL, NULL); }
bool UPTDestroyCallback::execute_real() { _UPT_destroy(handle_); return true; }
void bt_finish(struct bt_data *btd) { _UPT_destroy(btd->ui); unw_destroy_addr_space(btd->as); free(btd); }
int main(int argc, char **argv) { int status, pid, pending_sig, optind = 1, state = 1; as = unw_create_addr_space(&_UPT_accessors, 0); if (!as) panic ("unw_create_addr_space() failed"); if (argc == 1) { static char *args[] = {"self", "/bin/ls", "/usr", NULL}; /* automated test case */ argv = args; } else if (argc > 1) while (argv[optind][0] == '-') { if (strcmp(argv[optind], "-v") == 0) ++optind, verbose = 1; else if (strcmp(argv[optind], "-i") == 0) ++optind, trace_mode = INSTRUCTION; /* backtrace at each insn */ else if (strcmp(argv[optind], "-s") == 0) ++optind, trace_mode = SYSCALL; /* backtrace at each syscall */ else if (strcmp(argv[optind], "-t") == 0) /* Execute until raise(SIGUSR1), then backtrace at each insn until raise(SIGUSR2). */ ++optind, trace_mode = TRIGGER; else if (strcmp(argv[optind], "-c") == 0) /* Enable caching of unwind-info. */ ++optind, unw_set_caching_policy(as, UNW_CACHE_GLOBAL); else if (strcmp(argv[optind], "-n") == 0) /* Don't look-up and print symbol names. */ ++optind, print_names = 0; else fprintf(stderr, "unrecognized option: %s\n", argv[optind++]); if (optind >= argc) break; } target_pid = fork(); if (!target_pid) { /* child */ if (!verbose) dup2(open("/dev/null", O_WRONLY), 1); ptrace(PTRACE_TRACEME, 0, 0, 0); if ((argc > 1) && (optind == argc)) { fprintf(stderr, "Need to specify a command line for the child\n"); exit(-1); } execve(argv[optind], argv + optind, environ); _exit(-1); } atexit(target_pid_kill); ui = _UPT_create(target_pid); while (nerrors <= nerrors_max) { pid = wait4(-1, &status, 0, NULL); if (pid == -1) { if (errno == EINTR) continue; panic ("wait4() failed (errno=%d)\n", errno); } pending_sig = 0; if (WIFSIGNALED (status) || WIFEXITED (status) || (WIFSTOPPED (status) && WSTOPSIG (status) != SIGTRAP)) { if (WIFEXITED (status)) { if (WEXITSTATUS (status) != 0) panic ("child's exit status %d\n", WEXITSTATUS(status)); break; } else if (WIFSIGNALED (status)) { if (!killed) panic ("child terminated by signal %d\n", WTERMSIG(status)); break; } else { pending_sig = WSTOPSIG (status); /* Avoid deadlock: */ if (WSTOPSIG (status) == SIGKILL) break; if (trace_mode == TRIGGER) { if (WSTOPSIG (status) == SIGUSR1) state = 0; else if (WSTOPSIG (status) == SIGUSR2) state = 1; } if (WSTOPSIG (status) != SIGUSR1 && WSTOPSIG (status) != SIGUSR2) { static int count = 0; if (count++ > 100) { panic ("Too many child unexpected signals (now %d)\n", WSTOPSIG(status)); killed = 1; } } } } switch (trace_mode) { case TRIGGER: if (state) ptrace(PTRACE_CONT, target_pid, 0, 0); else { do_backtrace(); if (ptrace(PTRACE_SINGLESTEP, target_pid, 0, pending_sig) < 0) { panic ("ptrace(PTRACE_SINGLESTEP) failed (errno=%d)\n", errno); killed = 1; } } break; case SYSCALL: if (!state) do_backtrace(); state ^= 1; ptrace(PTRACE_SYSCALL, target_pid, 0, pending_sig); break; case INSTRUCTION: do_backtrace(); ptrace(PTRACE_SINGLESTEP, target_pid, 0, pending_sig); break; } if (killed) kill(target_pid, SIGKILL); } _UPT_destroy(ui); unw_destroy_addr_space(as); if (nerrors) { printf("FAILURE: detected %d errors\n", nerrors); exit(-1); } if (verbose) printf("SUCCESS\n"); return 0; }
/* 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; }
int backtrace_ptrace(int pid, int *tids, int *index, int nr_tids) { #if !defined (NO_LIBUNWIND_PTRACE) int i, count, rc = 0; int *threads = NULL; count = get_threads(pid, &threads); if (!count || threads == NULL) return -1; if (tids != NULL) { if (adjust_threads(threads, count, tids, index, nr_tids) < 0) return -1; free(threads); count = nr_tids; threads = tids; } if (attach_process(pid) < 0) return -1; for (i = 0; i < count; ++i) { void *upt_info; int x; char comm[16]; char end_pad[25] = "------------------------"; x = get_thread_comm(threads[i], comm, sizeof(comm)); if (x > 0 && x <= sizeof(end_pad)) { end_pad[sizeof(end_pad) - x] = '\0'; printf("-------------- thread %d (%d) (%s) %s\n", (index != NULL ? index[i] : i + 1), threads[i], comm, end_pad); } if (threads[i] != pid && attach_thread(threads[i]) < 0) { rc = -1; break; } upt_info = _UPT_create(threads[i]); if (backtrace_thread(&_UPT_accessors, upt_info) < 0) rc = -1; _UPT_destroy(upt_info); if (threads[i] != pid && detach_thread(threads[i])) rc = -1; if (rc < 0) break; } free(threads); if (detach_process(pid) < 0) return -1; return rc; #else return -1; #endif /* NO_LIBUNWIND_PTRACE */ }
void trace_free(ptrace_context *ctx) { _UPT_destroy(ctx->unwind_rctx); unw_destroy_addr_space(ctx->addr_space); free(ctx->cmdline); }
size_t StackCorkscrewLibunwind::Unwind (pid_t ppid, pid_t tid, size_t ignoreDepth, size_t maxDepth) { assert (ppid > 0); assert (tid >= ppid); (void) ignoreDepth; m_frames.clear (); if (maxDepth == 0) { return 0; } static unw_addr_space_t addr_space; (void) addr_space; #if UNWIND_SUPPORTED && UNWIND_REMOTE_SUPPORTED addr_space = unw_create_addr_space (&_UPT_accessors, 0); if (!addr_space) { fprintf (stderr, "unw_create_addr_space failed.\n"); fflush (stderr); return 0; } #endif #if UNWIND_MAP_SUPPORTED && UNWIND_FUNCTION_NAME unw_map_cursor_t map_cursor; if (unw_map_cursor_create (&map_cursor, tid) < 0) { fprintf (stderr, "Failed to create map cursor.\n"); fflush (stderr); return 0; } unw_map_set (addr_space, &map_cursor); #endif #if UNWIND_REMOTE_SUPPORTED struct UPT_info* upt_info = reinterpret_cast<struct UPT_info*> (_UPT_create (tid)); if (!upt_info) { fprintf (stderr, "Failed to create upt info.\n"); fflush (stderr); return 0; } unw_cursor_t cursor; { int error = unw_init_remote (&cursor, addr_space, upt_info); if (error < 0) { fprintf (stderr, "unw_init_remote failed (%d)\n", error); fflush (stderr); return 0; } } #endif bool shouldContinue = false; size_t numFrames = 0; do { // // Evaluate instruction pointer / program counter address. // #if UNWIND_REMOTE_SUPPORTED uint64_t pc = 0; { unw_word_t unwound_pc; int error = unw_get_reg (&cursor, UNW_REG_IP, &unwound_pc); if (error < 0) { fprintf (stderr, "Failed to read IP (%d)\n", error); fflush (stderr); break; } pc = unwound_pc; } #if UNWIND_STACK_POINTER uint64_t sp = 0; { unw_word_t unwound_sp; int error = unw_get_reg (&cursor, UNW_REG_SP, &unwound_sp); if (error < 0) { fprintf (stderr, "Failed to read SP (%d)\n", error); fflush (stderr); break; } sp = unwound_sp; } #endif if (ignoreDepth == 0) { const char *function = "??"; #if UNWIND_FUNCTION_NAME uintptr_t offset = 0; char buffer [128]; unw_word_t value; const int result = unw_get_proc_name_by_ip (addr_space, pc, buffer, sizeof (buffer), &value, upt_info); if (result >= 0 && buffer [0] != '\0') { function = buffer; offset = static_cast<uintptr_t>(value); } #endif StackFrame frame; frame.m_level = numFrames; frame.m_pc = pc; #if UNWIND_STACK_POINTER frame.m_sp = sp; #endif strncpy (frame.m_function, function, sizeof (frame.m_function)); m_frames.push_back (frame); numFrames++; } else { ignoreDepth--; } shouldContinue = (unw_step (&cursor) > 0); #endif } while (shouldContinue && numFrames < maxDepth); #if UNWIND_REMOTE_SUPPORTED _UPT_destroy (upt_info); #endif #if UNWIND_MAP_SUPPORTED && UNWIND_FUNCTION_NAME unw_map_cursor_destroy (&map_cursor); unw_map_cursor_clear (&map_cursor); #endif return m_frames.size (); }