Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a088524 authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

Merge "Rewrite unwind thread handling."

parents dc0ea862 a2efd3ac
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@


#include <inttypes.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdint.h>
#include <ucontext.h>


#include <string>
#include <string>
#include <vector>
#include <vector>
@@ -64,7 +65,7 @@ public:
  virtual ~Backtrace();
  virtual ~Backtrace();


  // Get the current stack trace and store in the backtrace_ structure.
  // Get the current stack trace and store in the backtrace_ structure.
  virtual bool Unwind(size_t num_ignore_frames);
  virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL);


  // Get the function name and offset into the function given the pc.
  // Get the function name and offset into the function given the pc.
  // If the string is empty, then no valid function name was found.
  // If the string is empty, then no valid function name was found.
+3 −0
Original line number Original line Diff line number Diff line
@@ -116,6 +116,9 @@ backtrace_test_shared_libraries := \
backtrace_test_shared_libraries_target := \
backtrace_test_shared_libraries_target := \
	libcutils \
	libcutils \


backtrace_test_static_libraries_host := \
	libcutils \

module := backtrace_test
module := backtrace_test
module_tag := debug
module_tag := debug
build_type := target
build_type := target
+2 −2
Original line number Original line Diff line number Diff line
@@ -55,8 +55,8 @@ Backtrace::~Backtrace() {
  }
  }
}
}


bool Backtrace::Unwind(size_t num_ignore_frames) {
bool Backtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
  return impl_->Unwind(num_ignore_frames);
  return impl_->Unwind(num_ignore_frames, ucontext);
}
}


extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len,
extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len,
+2 −1
Original line number Original line Diff line number Diff line
@@ -21,12 +21,13 @@
#include <backtrace/BacktraceMap.h>
#include <backtrace/BacktraceMap.h>


#include <sys/types.h>
#include <sys/types.h>
#include <ucontext.h>


class BacktraceImpl {
class BacktraceImpl {
public:
public:
  virtual ~BacktraceImpl() { }
  virtual ~BacktraceImpl() { }


  virtual bool Unwind(size_t num_ignore_frames) = 0;
  virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) = 0;


  // The name returned is not demangled, Backtrace::GetFunctionName()
  // The name returned is not demangled, Backtrace::GetFunctionName()
  // takes care of demangling the name.
  // takes care of demangling the name.
+129 −141
Original line number Original line Diff line number Diff line
@@ -16,10 +16,15 @@


#include <errno.h>
#include <errno.h>
#include <inttypes.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/futex.h>
#include <pthread.h>
#include <pthread.h>
#include <signal.h>
#include <signal.h>
#include <string.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/types.h>
#include <ucontext.h>


#include <cutils/atomic.h>
#include <cutils/atomic.h>


@@ -27,190 +32,173 @@
#include "BacktraceThread.h"
#include "BacktraceThread.h"
#include "thread_utils.h"
#include "thread_utils.h"


static inline int futex(volatile int* uaddr, int op, int val, const struct timespec* ts, volatile int* uaddr2, int val3) {
  return syscall(__NR_futex, uaddr, op, val, ts, uaddr2, val3);
}

//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// ThreadEntry implementation.
// ThreadEntry implementation.
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
static ThreadEntry* g_list = NULL;
ThreadEntry* ThreadEntry::list_ = NULL;
static pthread_mutex_t g_entry_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;


ThreadEntry::ThreadEntry(
// Assumes that ThreadEntry::list_mutex_ has already been locked before
    BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames)
// creating a ThreadEntry object.
    : thread_intf(intf), pid(pid), tid(tid), next(NULL), prev(NULL),
ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
      state(STATE_WAITING), num_ignore_frames(num_ignore_frames) {
    : pid_(pid), tid_(tid), futex_(0), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER), next_(ThreadEntry::list_), prev_(NULL) {
  // Add ourselves to the list.
  if (ThreadEntry::list_) {
    ThreadEntry::list_->prev_ = this;
  }
  ThreadEntry::list_ = this;
}
}


ThreadEntry::~ThreadEntry() {
ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
  pthread_mutex_lock(&g_entry_mutex);
  pthread_mutex_lock(&ThreadEntry::list_mutex_);
  if (g_list == this) {
  ThreadEntry* entry = list_;
    g_list = next;
  while (entry != NULL) {
  } else {
    if (entry->Match(pid, tid)) {
    if (next) {
      break;
      next->prev = prev;
    }
    }
    prev->next = next;
    entry = entry->next_;
  }
  }
  pthread_mutex_unlock(&g_entry_mutex);


  next = NULL;
  if (!entry) {
  prev = NULL;
    if (create) {
      entry = new ThreadEntry(pid, tid);
    }
  } else {
    entry->ref_count_++;
  }
  }
  pthread_mutex_unlock(&ThreadEntry::list_mutex_);


ThreadEntry* ThreadEntry::AddThreadToUnwind(
  return entry;
    BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) {
}
  ThreadEntry* entry = new ThreadEntry(intf, pid, tid, num_ignore_frames);


  pthread_mutex_lock(&g_entry_mutex);
void ThreadEntry::Remove(ThreadEntry* entry) {
  ThreadEntry* cur_entry = g_list;
  pthread_mutex_unlock(&entry->mutex_);
  while (cur_entry != NULL) {
    if (cur_entry->Match(pid, tid)) {
      // There is already an entry for this pid/tid, this is bad.
      BACK_LOGW("Entry for pid %d tid %d already exists.", pid, tid);


      pthread_mutex_unlock(&g_entry_mutex);
  pthread_mutex_lock(&ThreadEntry::list_mutex_);
      return NULL;
  if (--entry->ref_count_ == 0) {
    delete entry;
  }
  }
    cur_entry = cur_entry->next;
  pthread_mutex_unlock(&ThreadEntry::list_mutex_);
}
}


  // Add the entry to the list.
// Assumes that ThreadEntry::list_mutex_ has already been locked before
  entry->next = g_list;
// deleting a ThreadEntry object.
  if (g_list) {
ThreadEntry::~ThreadEntry() {
    g_list->prev = entry;
  if (list_ == this) {
    list_ = next_;
  } else {
    if (next_) {
      next_->prev_ = prev_;
    }
    }
  g_list = entry;
    prev_->next_ = next_;
  pthread_mutex_unlock(&g_entry_mutex);

  return entry;
  }
  }


//-------------------------------------------------------------------------
  next_ = NULL;
// BacktraceThread functions.
  prev_ = NULL;
//-------------------------------------------------------------------------
static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo,
                          void* sigcontext) {
  if (pthread_mutex_lock(&g_entry_mutex) == 0) {
    pid_t pid = getpid();
    pid_t tid = gettid();
    ThreadEntry* cur_entry = g_list;
    while (cur_entry) {
      if (cur_entry->Match(pid, tid)) {
        break;
      }
      cur_entry = cur_entry->next;
    }
    pthread_mutex_unlock(&g_entry_mutex);
    if (!cur_entry) {
      BACK_LOGW("Unable to find pid %d tid %d information", pid, tid);
      return;
}
}


    if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state) == 0) {
void ThreadEntry::Wait(int value) {
      cur_entry->thread_intf->ThreadUnwind(siginfo, sigcontext,
  timespec ts;
                                           cur_entry->num_ignore_frames);
  ts.tv_sec = 10;
    }
  ts.tv_nsec = 0;
    android_atomic_release_store(STATE_DONE, &cur_entry->state);
  errno = 0;
  futex(&futex_, FUTEX_WAIT, value, &ts, NULL, 0);
  if (errno != 0 && errno != EWOULDBLOCK) {
    BACK_LOGW("futex wait failed, futex = %d: %s", futex_, strerror(errno));
  }
  }
}
}


BacktraceThread::BacktraceThread(
void ThreadEntry::Wake() {
    BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid,
  futex_++;
    BacktraceMap* map)
  futex(&futex_, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
    : BacktraceCurrent(impl, map), thread_intf_(thread_intf) {
  tid_ = tid;
}
}


BacktraceThread::~BacktraceThread() {
//-------------------------------------------------------------------------
// BacktraceThread functions.
//-------------------------------------------------------------------------
static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;

static void SignalHandler(int, siginfo_t*, void* sigcontext) {
  ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
  if (!entry) {
    BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid());
    return;
  }
  }


void BacktraceThread::FinishUnwind() {
  entry->CopyUcontext(reinterpret_cast<ucontext_t*>(sigcontext));
  for (std::vector<backtrace_frame_data_t>::iterator it = frames_.begin();
       it != frames_.end(); ++it) {
    it->map = FindMap(it->pc);


    it->func_offset = 0;
  // Indicate the ucontext is now valid.
    it->func_name = GetFunctionName(it->pc, &it->func_offset);
  entry->Wake();
  }
}


bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) {
  // Pause the thread until the unwind is complete. This avoids having
  entry->state = STATE_WAITING;
  // the thread run ahead causing problems.
  entry->Wait(1);


  if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
  ThreadEntry::Remove(entry);
    BACK_LOGW("tgkill failed %s", strerror(errno));
    return false;
}
}


  // Allow up to ten seconds for the dump to start.
BacktraceThread::BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map)
  int wait_millis = 10000;
    : BacktraceCurrent(impl, map) {
  int32_t state;
  tid_ = tid;
  while (true) {
    state = android_atomic_acquire_load(&entry->state);
    if (state != STATE_WAITING) {
      break;
    }
    if (wait_millis--) {
      usleep(1000);
    } else {
      break;
    }
}
}


  bool cancelled = false;
BacktraceThread::~BacktraceThread() {
  if (state == STATE_WAITING) {
    if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state) == 0) {
      BACK_LOGW("Cancelled dump of thread %d", entry->tid);
      state = STATE_CANCEL;
      cancelled = true;
    } else {
      state = android_atomic_acquire_load(&entry->state);
    }
}
}


  // Wait for at most ten seconds for the cancel or dump to finish.
bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
  wait_millis = 10000;
  if (ucontext) {
  while (android_atomic_acquire_load(&entry->state) != STATE_DONE) {
    // Unwind using an already existing ucontext.
    if (wait_millis--) {
    return impl_->Unwind(num_ignore_frames, ucontext);
      usleep(1000);
    } else {
      BACK_LOGW("Didn't finish thread unwind in 60 seconds.");
      break;
    }
  }
  return !cancelled;
  }
  }


bool BacktraceThread::Unwind(size_t num_ignore_frames) {
  // Prevent multiple threads trying to set the trigger action on different
  ThreadEntry* entry = ThreadEntry::AddThreadToUnwind(
  // threads at the same time.
      thread_intf_, Pid(), Tid(), num_ignore_frames);
  if (pthread_mutex_lock(&g_sigaction_mutex) < 0) {
  if (!entry) {
    BACK_LOGW("sigaction failed: %s", strerror(errno));
    return false;
    return false;
  }
  }


  // Prevent multiple threads trying to set the trigger action on different
  ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
  // threads at the same time.
  entry->Lock();
  bool retval = false;

  if (pthread_mutex_lock(&g_sigaction_mutex) == 0) {
  struct sigaction act, oldact;
  struct sigaction act, oldact;
  memset(&act, 0, sizeof(act));
  memset(&act, 0, sizeof(act));
  act.sa_sigaction = SignalHandler;
  act.sa_sigaction = SignalHandler;
  act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
  act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
  sigemptyset(&act.sa_mask);
  sigemptyset(&act.sa_mask);
    if (sigaction(THREAD_SIGNAL, &act, &oldact) == 0) {
  if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
      retval = TriggerUnwindOnThread(entry);
      sigaction(THREAD_SIGNAL, &oldact, NULL);
    } else {
    BACK_LOGW("sigaction failed %s", strerror(errno));
    BACK_LOGW("sigaction failed %s", strerror(errno));
    }
    entry->Unlock();
    ThreadEntry::Remove(entry);
    pthread_mutex_unlock(&g_sigaction_mutex);
    pthread_mutex_unlock(&g_sigaction_mutex);
  } else {
    return false;
    BACK_LOGW("unable to acquire sigaction mutex.");
  }
  }


  if (retval) {
  if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
    FinishUnwind();
    BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno));
    sigaction(THREAD_SIGNAL, &oldact, NULL);
    entry->Unlock();
    ThreadEntry::Remove(entry);
    pthread_mutex_unlock(&g_sigaction_mutex);
    return false;
  }
  }
  delete entry;


  return retval;
  // Wait for the thread to get the ucontext.
  entry->Wait(0);

  // After the thread has received the signal, allow other unwinders to
  // continue.
  sigaction(THREAD_SIGNAL, &oldact, NULL);
  pthread_mutex_unlock(&g_sigaction_mutex);

  bool unwind_done = impl_->Unwind(num_ignore_frames, entry->GetUcontext());

  // Tell the signal handler to exit and release the entry.
  entry->Wake();

  return unwind_done;
}
}
Loading