bool BacktraceDebugger::getStackTrace(StackTrace &trace) { init(); // Might set enabled false if (!enabled) return false; void *stack[maxStack]; int n = backtrace(stack, maxStack); SmartPointer<char *>::Malloc symbols = backtrace_symbols(stack, n); #ifdef VALGRIND_MAKE_MEM_DEFINED (void)VALGRIND_MAKE_MEM_DEFINED(stack, n * sizeof(void *)); #endif // VALGRIND_MAKE_MEM_DEFINED SmartLock lock(this); for (int i = 0; i < n; i++) { bfd_vma pc = (bfd_vma)stack[i]; const char *filename = 0; const char *function = 0; unsigned line = 0; // Find section asection *section = 0; for (asection *s = p->abfd->sections; s; s = s->next) { if ((bfd_get_section_flags(p->abfd, s) & SEC_CODE) == 0) continue; bfd_vma vma = bfd_get_section_vma(p->abfd, s); if (pc < vma) { section = s->prev; break; } } if (section) { bfd_vma vma = bfd_get_section_vma(p->abfd, section); bfd_find_nearest_line(p->abfd, section, p->syms, pc - vma, &filename, &function, &line); #ifdef VALGRIND_MAKE_MEM_DEFINED if (filename) (void)VALGRIND_MAKE_MEM_DEFINED(filename, strlen(filename)); if (function) (void)VALGRIND_MAKE_MEM_DEFINED(function, strlen(function)); (void)VALGRIND_MAKE_MEM_DEFINED(&line, sizeof(line)); #endif // VALGRIND_MAKE_MEM_DEFINED } // Fallback to backtrace symbols if ((!function || !filename) && symbols.get()) { char *sym = symbols[i]; // Parse symbol string // Expected format: <module>(<function>+0x<offset>) char *ptr = sym; while (*ptr && *ptr != '(') ptr++; if (*ptr == '(') { *ptr++ = 0; if (!filename) filename = sym; // Not really the source file if (!function) { function = ptr; while (*ptr && *ptr != '+') ptr++; if (*ptr) { *ptr++ = 0; char *offset = ptr; while (*ptr && *ptr != ')') ptr++; if (*ptr == ')') *ptr = 0; int save_errno = errno; errno = 0; line = strtol(offset, 0, 0); // Byte offset not line number if (errno) line = 0; errno = save_errno; } } } } // Filename if (!filename) filename = ""; else if (0 <= parents) { const char *ptr = filename + strlen(filename); for (int j = 0; j <= parents; j++) while (filename < ptr && *--ptr != '/') continue; if (*ptr == '/') filename = ptr + 1; } // Function name char *demangled = 0; if (!function) function = ""; else { int status = 0; demangled = abi::__cxa_demangle(function, 0, 0, &status); if (!status && demangled) function = demangled; } trace.push_back (StackFrame(stack[i], FileLocation(filename, function, line))); if (demangled) free(demangled); } return true; }
/** * This is used to obtain the list of symbols using a ucontext_t structure. * So, it is used by both the HaltedStacktrace and the SuspendedStacktrace. * Since this method is pure implementation, it's */ int thread_unwind(ucontext_t* uc, void** iparray, StackTrace& stacktrace) { assert(iparray != nullptr); assert(&stacktrace != nullptr); unw_cursor_t cursor; // Effective ucontext_t. If uc not supplied, use unw_getcontext locally. This is appropriate inside signal handlers. #if defined(__APPLE__) unw_context_t thisctx; unw_getcontext(&thisctx); #else ucontext_t thisctx; if (uc == nullptr) { unw_getcontext(&thisctx); uc = &thisctx; } #endif const int BUFR_SZ = 1000; char procbuffer[BUFR_SZ]; stacktrace.clear(); stacktrace.reserve(120); /* * Note: documentation seems to indicate that uc_link contains a pointer to a "successor" context * that is to be resumed after the current one (as you might expect in a signal handler). * In practice however, the Linux OS seems to re-use the existing context of a thread, and so this approach * does not work. The uc_link is sometimes non-NULL however, but I do not know what the relation is to the * target context (uc). * while (uc->uc_link) { uc = uc->uc_link; LOG_L(L_DEBUG, "Dereferencing uc_link"); } */ #if defined(__APPLE__) int err = unw_init_local(&cursor, &thisctx); #else int err = unw_init_local(&cursor, uc); #endif if (err) { LOG_L(L_ERROR, "unw_init_local returned %d", err); return 0; } int i=0; while (i < MAX_STACKTRACE_DEPTH && unw_step(&cursor)) { StackFrame frame; unw_word_t ip; unw_word_t offp; unw_get_reg(&cursor, UNW_REG_IP, &ip); frame.ip = reinterpret_cast<void*>(ip); frame.level = i; iparray[i] = frame.ip; if (!unw_get_proc_name(&cursor, procbuffer, BUFR_SZ-1, &offp)) { frame.mangled = std::string(procbuffer); } else { frame.mangled = std::string("UNW_ENOINFO"); } stacktrace.push_back(frame); i++; } stacktrace.resize(i); LOG_L(L_DEBUG, "thread_unwind returned %d frames", i); return i; }