summaryrefslogtreecommitdiffstats
path: root/libbacktrace/BacktraceThread.cpp
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2013-10-21 13:30:52 -0700
committerChristopher Ferris <cferris@google.com>2013-10-28 17:55:25 -0700
commit17e91d44edf5e6476a477a200bcd89d4327358a3 (patch)
tree947783229077133a7f467402607a72fe2047a7a7 /libbacktrace/BacktraceThread.cpp
parentf1296b9e92cb04f9bba8c622560d37dfc53c7aa4 (diff)
downloadsystem_core-17e91d44edf5e6476a477a200bcd89d4327358a3.zip
system_core-17e91d44edf5e6476a477a200bcd89d4327358a3.tar.gz
system_core-17e91d44edf5e6476a477a200bcd89d4327358a3.tar.bz2
Rewrite libbacktrace using C++.
The old code was essentially trying to be C++ in C and was awkward. This change makes it all objects with a thin layer that C code can use. There is a C++ backtrace object that is not very useful, this code will replace it. This change also includes moving the backtrace test to a gtest, and adding coverage of all major functionality. Bug: 8410085 Change-Id: Iae0f1b09b3dd60395f71ed66010c1ea5cdd37841
Diffstat (limited to 'libbacktrace/BacktraceThread.cpp')
-rw-r--r--libbacktrace/BacktraceThread.cpp224
1 files changed, 224 insertions, 0 deletions
diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp
new file mode 100644
index 0000000..6c3641e
--- /dev/null
+++ b/libbacktrace/BacktraceThread.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <cutils/atomic.h>
+#include <cutils/log.h>
+
+#include "BacktraceThread.h"
+#include "thread_utils.h"
+
+//-------------------------------------------------------------------------
+// ThreadEntry implementation.
+//-------------------------------------------------------------------------
+static ThreadEntry* g_list = NULL;
+static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+ThreadEntry::ThreadEntry(
+ BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames)
+ : thread_intf_(intf), pid_(pid), tid_(tid), next_(NULL), prev_(NULL),
+ state_(STATE_WAITING), num_ignore_frames_(num_ignore_frames) {
+}
+
+ThreadEntry::~ThreadEntry() {
+ pthread_mutex_lock(&g_mutex);
+ if (g_list == this) {
+ g_list = next_;
+ } else {
+ if (next_) {
+ next_->prev_ = prev_;
+ }
+ prev_->next_ = next_;
+ }
+ pthread_mutex_unlock(&g_mutex);
+
+ next_ = NULL;
+ prev_ = NULL;
+}
+
+ThreadEntry* ThreadEntry::AddThreadToUnwind(
+ 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_mutex);
+ ThreadEntry* cur_entry = g_list;
+ while (cur_entry != NULL) {
+ if (cur_entry->Match(pid, tid)) {
+ // There is already an entry for this pid/tid, this is bad.
+ ALOGW("%s::%s(): Entry for pid %d tid %d already exists.\n",
+ __FILE__, __FUNCTION__, pid, tid);
+
+ pthread_mutex_unlock(&g_mutex);
+ return NULL;
+ }
+ cur_entry = cur_entry->next_;
+ }
+
+ // Add the entry to the list.
+ entry->next_ = g_list;
+ if (g_list) {
+ g_list->prev_ = entry;
+ }
+ g_list = entry;
+ pthread_mutex_unlock(&g_mutex);
+
+ return entry;
+}
+
+//-------------------------------------------------------------------------
+// BacktraceThread functions.
+//-------------------------------------------------------------------------
+static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo,
+ void* sigcontext) {
+ if (pthread_mutex_lock(&g_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_mutex);
+ if (!cur_entry) {
+ ALOGW("%s::%s(): Unable to find pid %d tid %d information\n",
+ __FILE__, __FUNCTION__, pid, tid);
+ return;
+ }
+
+ if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state_) == 0) {
+ cur_entry->thread_intf_->ThreadUnwind(siginfo, sigcontext,
+ cur_entry->num_ignore_frames_);
+ }
+ android_atomic_release_store(STATE_DONE, &cur_entry->state_);
+ }
+}
+
+BacktraceThread::BacktraceThread(
+ BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid)
+ : BacktraceCurrent(impl), thread_intf_(thread_intf) {
+ backtrace_.tid = tid;
+}
+
+BacktraceThread::~BacktraceThread() {
+}
+
+void BacktraceThread::FinishUnwind() {
+ for (size_t i = 0; i < NumFrames(); i++) {
+ backtrace_frame_data_t* frame = &backtrace_.frames[i];
+
+ frame->map_offset = 0;
+ uintptr_t map_start;
+ frame->map_name = GetMapName(frame->pc, &map_start);
+ if (frame->map_name) {
+ frame->map_offset = frame->pc - map_start;
+ }
+
+ frame->func_offset = 0;
+ std::string func_name = GetFunctionName(frame->pc, &frame->func_offset);
+ if (!func_name.empty()) {
+ frame->func_name = strdup(func_name.c_str());
+ }
+ }
+}
+
+bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) {
+ entry->state_ = STATE_WAITING;
+
+ if (tgkill(Pid(), Tid(), SIGURG) != 0) {
+ ALOGW("%s::%s(): tgkill failed %s\n", __FILE__, __FUNCTION__, strerror(errno));
+ return false;
+ }
+
+ // Allow up to a second for the dump to occur.
+ int wait_millis = 1000;
+ int32_t state;
+ while (true) {
+ state = android_atomic_acquire_load(&entry->state_);
+ if (state != STATE_WAITING) {
+ break;
+ }
+ if (wait_millis--) {
+ usleep(1000);
+ } else {
+ break;
+ }
+ }
+
+ bool cancelled = false;
+ if (state == STATE_WAITING) {
+ if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state_) == 0) {
+ ALOGW("%s::%s(): Cancelled dump of thread %d\n", __FILE__, __FUNCTION__,
+ entry->tid_);
+ state = STATE_CANCEL;
+ cancelled = true;
+ } else {
+ state = android_atomic_acquire_load(&entry->state_);
+ }
+ }
+
+ // Wait for at most one minute for the dump to finish.
+ wait_millis = 60000;
+ while (android_atomic_acquire_load(&entry->state_) != STATE_DONE) {
+ if (wait_millis--) {
+ usleep(1000);
+ } else {
+ ALOGW("%s::%s(): Didn't finish thread unwind in 60 seconds.\n",
+ __FILE__, __FUNCTION__);
+ break;
+ }
+ }
+ return !cancelled;
+}
+
+bool BacktraceThread::Unwind(size_t num_ignore_frames) {
+ if (!thread_intf_->Init()) {
+ return false;
+ }
+
+ ThreadEntry* entry = ThreadEntry::AddThreadToUnwind(
+ thread_intf_, Pid(), Tid(), num_ignore_frames);
+ if (!entry) {
+ return false;
+ }
+
+ bool retval = false;
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalHandler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ sigemptyset(&act.sa_mask);
+ if (sigaction(SIGURG, &act, &oldact) == 0) {
+ retval = TriggerUnwindOnThread(entry);
+ sigaction(SIGURG, &oldact, NULL);
+ } else {
+ ALOGW("%s::%s(): sigaction failed %s\n", __FILE__, __FUNCTION__, strerror(errno));
+ }
+
+ if (retval) {
+ FinishUnwind();
+ }
+ delete entry;
+
+ return retval;
+}