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;
}
示例#2
0
	/**
	 * 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;
	}