bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) { if (context == nullptr) { BACK_LOGW("The context is needed for offline backtracing."); return false; } context_ = context; unw_addr_space_t addr_space = unw_create_addr_space(&accessors, 0); unw_cursor_t cursor; int ret = unw_init_remote(&cursor, addr_space, this); if (ret != 0) { BACK_LOGW("unw_init_remote failed %d", ret); unw_destroy_addr_space(addr_space); return false; } size_t num_frames = 0; do { unw_word_t pc; ret = unw_get_reg(&cursor, UNW_REG_IP, &pc); if (ret < 0) { BACK_LOGW("Failed to read IP %d", ret); break; } unw_word_t sp; ret = unw_get_reg(&cursor, UNW_REG_SP, &sp); if (ret < 0) { BACK_LOGW("Failed to read SP %d", ret); break; } if (num_ignore_frames == 0) { frames_.resize(num_frames + 1); backtrace_frame_data_t* frame = &frames_[num_frames]; frame->num = num_frames; frame->pc = static_cast<uintptr_t>(pc); frame->sp = static_cast<uintptr_t>(sp); frame->stack_size = 0; if (num_frames > 0) { backtrace_frame_data_t* prev = &frames_[num_frames - 1]; prev->stack_size = frame->sp - prev->sp; } frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); FillInMap(frame->pc, &frame->map); num_frames++; } else { num_ignore_frames--; } ret = unw_step(&cursor); } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES); unw_destroy_addr_space(addr_space); context_ = nullptr; return true; }
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) */ }
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; } }
/*++ Function: PAL_VirtualUnwindOutOfProc Unwind the stack given the context for a "remote" target using the provided read memory callback. Assumes the IP is in the module of the base address provided (coreclr). Parameters: context - the start context in the target contextPointers - the context of the next frame baseAddress - base address of the module to find the unwind info readMemoryCallback - reads memory from the target --*/ BOOL PALAPI PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback) { unw_addr_space_t addrSpace = 0; unw_cursor_t cursor; libunwindInfo info; BOOL result = FALSE; int st; info.BaseAddress = baseAddress; info.Context = context; info.ReadMemory = readMemoryCallback; addrSpace = unw_create_addr_space(&unwind_accessors, 0); st = unw_init_remote(&cursor, addrSpace, &info); 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, NULL, contextPointers); } result = TRUE; exit: 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 DestroyUnwindAddressSpaceCallback::execute_real() { unw_destroy_addr_space(as_); 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) { unw_addr_space_t as; unw_cursor_t cursor; struct UCD_info *ui; unw_word_t ip, sp, off; char buf[512], name[256]; int line; int depth = 0; int ret; bool pybt_done = false; #define TEST_NAME_LEN 256 install_signal_handler(); if (argc != 3) { fprintf(stderr, "Usage: %s <binary> <corefile>", argv[0]); exit(1); } as = unw_create_addr_space(&_UCD_accessors, 0); if (!as) { fprintf(stderr, "unw_create_addr_space() failed"); exit(1); } ui = _UCD_create(argv[2]); if (!ui) { fprintf(stderr,"_UCD_create('%s') failed", argv[1]); exit(1); } ret = unw_init_remote(&cursor, as, ui); if (ret < 0) { fprintf(stderr,"unw_init_remote() failed: ret=%d\n", ret); exit(1); } read_elfnotes(argv[2], ui); while (unw_step(&cursor) > 0) { // Avoid going too deep if (depth++ > MAX_STACK_DEPTH) { exit(1); } unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); if (unw_get_proc_name(&cursor, name, sizeof (name), &off) == 0) { if (off) { snprintf(buf, sizeof (buf), "<%s+0x%lx>", name, (long) off); } else { snprintf(buf, sizeof (buf), "<%s>", name); } } /* Check for Python backtrace */ if (!strncmp(name,"PyEval_EvalFrameEx", 18) && !pybt_done) { pybacktrace(cursor); pybt_done = true; } rw_get_file_and_line((long)ip, name, argv[1], 256, &line); printf("%016lx %s <%s:%d> (sp=%016lx)\n", (long) ip, buf, basename(name), line, (long) sp); } _UCD_destroy(ui); unw_destroy_addr_space(as); 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; }
static int backtrace_thread(unw_accessors_t *accessors, void *arg) { unw_addr_space_t addr_space; unw_cursor_t cursor; int rc = 0, n = 0; if ((addr_space = unw_create_addr_space(accessors, 0)) == NULL) { fprintf(stderr, "failed to create address space for unwinding\n"); return -1; } if ((rc = unw_init_remote(&cursor, addr_space, arg)) < 0) { fprintf(stderr, "failed to init cursor for unwinding: rc=%d\n", rc); return -1; } do { unw_word_t ip, sp = -1, off; static char buf[512]; size_t len; if ((rc = unw_get_reg(&cursor, UNW_REG_IP, &ip)) < 0) { fprintf(stderr, "failed to get IP: rc=%d\n", rc); break; } buf[0] = '\0'; unw_get_proc_name(&cursor, buf, sizeof(buf), &off); if (buf[0] == '\0') { buf[0] = '?'; buf[1] = '\0'; len = 1; } else { len = strlen(buf); } if (len >= sizeof(buf) - 32) len = sizeof(buf) - 32; if (!ip) break; if (off) { sprintf(buf + len, " + 0x%lx", (unsigned long)off); } if (!opt_show_rsp) { printf(" %016lx %s\n", (long)ip, buf); } else { unw_get_reg(&cursor, UNW_REG_SP, &sp); printf(" %016lx %016lx %s\n", (long)ip, (long)sp, buf); } if ((rc = unw_step(&cursor)) < 0) { if (!opt_show_rsp) printf(" ???????????????? <stack breaks here>\n"); else printf(" ???????????????? ???????????????? <stack breaks here>\n"); if (opt_verbose) { fprintf(stderr, "unwind step failed: n=%d rc=%d\n", n, rc); } break; } if (++n == 64 && rc) { puts(" ???????????????? <stack is too long>\n"); break; } } while (rc > 0); unw_destroy_addr_space(addr_space); return rc; }
struct sr_core_stacktrace * sr_parse_coredump(const char *core_file, const char *exe_file, char **error_msg) { struct sr_core_stacktrace *stacktrace = NULL; /* Initialize error_msg to 'no error'. */ if (error_msg) *error_msg = NULL; struct core_handle *ch = open_coredump(core_file, exe_file, error_msg); if (*error_msg) return NULL; unw_addr_space_t as; struct UCD_info *ui; as = unw_create_addr_space(&_UCD_accessors, 0); if (!as) { set_error("Failed to create address space"); goto fail_destroy_handle; } ui = _UCD_create(core_file); if (!ui) { set_error("Failed to set up core dump accessors for '%s'", core_file); goto fail_destroy_as; } struct exe_mapping_data *s; for (s = ch->segments; s != NULL; s = s->next) { if (_UCD_add_backing_file_at_vaddr(ui, s->start, s->filename) < 0) { /* Sometimes produces: * >_UCD_add_backing_file_at_segment: * Error reading from '/usr/lib/modules/3.6.9-2.fc17.x86_64/vdso/vdso.so' * Ignore errors for now & fail later. */ warn("Can't add backing file '%s' at addr 0x%jx", s->filename, (uintmax_t)s->start); /* goto fail_destroy_ui; */ } } stacktrace = sr_core_stacktrace_new(); int tnum, nthreads = _UCD_get_num_threads(ui); for (tnum = 0; tnum < nthreads; ++tnum) { struct sr_core_thread *trace = unwind_thread(ui, as, ch->dwfl, tnum, error_msg); if (trace) { stacktrace->threads = sr_core_thread_append(stacktrace->threads, trace); } else { sr_core_stacktrace_free(stacktrace); stacktrace = NULL; break; } } stacktrace->executable = realpath(exe_file, NULL); stacktrace->signal = get_signal_number_libunwind(ui); /* FIXME: is this the best we can do? */ stacktrace->crash_thread = stacktrace->threads; _UCD_destroy(ui); fail_destroy_as: unw_destroy_addr_space(as); fail_destroy_handle: core_handle_free(ch); return stacktrace; }
int main(int ac, char** av) { const char* outputFile = 0; const char* symbolFile = 0; int commandStart = 1; bool mainThreadOnly = false; for (int i = 1; i < ac; ++i) { if (strcmp(av[i], "-o") == 0) { if (i + 1 < ac) { outputFile = av[++i]; } else { usage(av[0]); return -1; } } else if (strcmp(av[i], "-s") == 0) { if (i + 1 < ac) { symbolFile = av[++i]; } else { usage(av[0]); return -1; } } else if (strcmp(av[i], "--main-thread-only") == 0) { mainThreadOnly = true; } else { commandStart = i; break; } } if (commandStart == ac) { usage(av[0]); return -1; } const char* command = av[commandStart]; pid_t process = fork(); if (process == 0) { // child execv(command, av + commandStart); } else if (process < 0) { // error fprintf(stderr, "unable to fork\n"); return -1; } else { // parent struct sigaction action; action.sa_sigaction = handleSignal; action.sa_flags = SA_SIGINFO; sigaction(SIGCHLD, &action, 0); sigaction(SIGINT, &action, 0); timespec interval = { 0, 1000000L }; Context context(process); space = unw_create_addr_space(&_UPT_accessors, __LITTLE_ENDIAN); while (not done) { sample(&context, mainThreadOnly); timespec remainder; do { if (nanosleep(&interval, &remainder)) { if (errno != EINTR) { fprintf(stderr, "nanosleep errno %s\n", strerror(errno)); done = true; } } else { break; } } while ((not done) and (remainder.tv_sec or remainder.tv_nsec)); } unw_destroy_addr_space(space); FILE* out = stdout; if (outputFile) { out = fopen(outputFile, "wb"); if (out == 0) { fprintf(stderr, "unable to open %s\n", outputFile); return -1; } } SymbolTable* symbols = loadElfSymbols(command); if (symbolFile) { symbols = append(symbols, loadTextSymbols(symbolFile)); } dump(&context, symbols, out); symbolTableDispose(symbols); if (outputFile) { fclose(out); } } return 0; }
void trace_free(ptrace_context *ctx) { _UPT_destroy(ctx->unwind_rctx); unw_destroy_addr_space(ctx->addr_space); free(ctx->cmdline); }
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; }
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; }
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; }