summaryrefslogtreecommitdiffstats
path: root/libbacktrace
diff options
context:
space:
mode:
Diffstat (limited to 'libbacktrace')
-rw-r--r--libbacktrace/Android.mk290
-rw-r--r--libbacktrace/BacktraceImpl.cpp208
-rw-r--r--libbacktrace/BacktraceImpl.h79
-rw-r--r--libbacktrace/BacktraceMap.cpp147
-rw-r--r--libbacktrace/BacktraceThread.cpp215
-rw-r--r--libbacktrace/BacktraceThread.h90
-rw-r--r--libbacktrace/Corkscrew.cpp251
-rw-r--r--libbacktrace/Corkscrew.h82
-rw-r--r--libbacktrace/UnwindCurrent.cpp191
-rw-r--r--libbacktrace/UnwindCurrent.h54
-rw-r--r--libbacktrace/UnwindMap.cpp110
-rw-r--r--libbacktrace/UnwindMap.h39
-rw-r--r--libbacktrace/UnwindPtrace.cpp133
-rw-r--r--libbacktrace/UnwindPtrace.h40
-rw-r--r--libbacktrace/backtrace_test.cpp695
-rw-r--r--libbacktrace/backtrace_testlib.c55
-rw-r--r--libbacktrace/thread_utils.c42
-rw-r--r--libbacktrace/thread_utils.h30
18 files changed, 2751 insertions, 0 deletions
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
new file mode 100644
index 0000000..80cd861
--- /dev/null
+++ b/libbacktrace/Android.mk
@@ -0,0 +1,290 @@
+LOCAL_PATH:= $(call my-dir)
+
+common_src := \
+ BacktraceImpl.cpp \
+ BacktraceMap.cpp \
+ BacktraceThread.cpp \
+ thread_utils.c \
+
+common_cflags := \
+ -Wall \
+ -Wno-unused-parameter \
+ -Werror \
+
+common_conlyflags := \
+ -std=gnu99 \
+
+common_cppflags := \
+ -std=gnu++11 \
+
+common_shared_libs := \
+ libcutils \
+ libgccdemangle \
+ liblog \
+
+# To enable using libunwind on each arch, add it to this list.
+libunwind_architectures :=
+
+ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures)))
+
+#----------------------------------------------------------------------------
+# The native libbacktrace library with libunwind.
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ $(common_src) \
+ UnwindCurrent.cpp \
+ UnwindMap.cpp \
+ UnwindPtrace.cpp \
+
+LOCAL_CFLAGS := \
+ $(common_cflags) \
+
+LOCAL_CONLYFLAGS += \
+ $(common_conlyflags) \
+
+LOCAL_CPPFLAGS += \
+ $(common_cppflags) \
+
+LOCAL_MODULE := libbacktrace
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := \
+ $(common_c_includes) \
+ external/libunwind/include \
+
+LOCAL_SHARED_LIBRARIES := \
+ $(common_shared_libs) \
+ libunwind \
+ libunwind-ptrace \
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
+include external/stlport/libstlport.mk
+
+include $(BUILD_SHARED_LIBRARY)
+
+else
+
+#----------------------------------------------------------------------------
+# The native libbacktrace library with libcorkscrew.
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ $(common_src) \
+ Corkscrew.cpp \
+
+LOCAL_CFLAGS := \
+ $(common_cflags) \
+
+LOCAL_CONLYFLAGS += \
+ $(common_conlyflags) \
+
+LOCAL_CPPFLAGS += \
+ $(common_cppflags) \
+
+LOCAL_MODULE := libbacktrace
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := \
+ $(common_c_includes) \
+ system/core/libcorkscrew \
+
+LOCAL_SHARED_LIBRARIES := \
+ $(common_shared_libs) \
+ libcorkscrew \
+ libdl \
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
+include external/stlport/libstlport.mk
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
+
+#----------------------------------------------------------------------------
+# libbacktrace test library, all optimizations turned off
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbacktrace_test
+LOCAL_MODULE_FLAGS := debug
+
+LOCAL_SRC_FILES := \
+ backtrace_testlib.c
+
+LOCAL_CFLAGS += \
+ -std=gnu99 \
+ -O0 \
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_SHARED_LIBRARY)
+
+#----------------------------------------------------------------------------
+# libbacktrace test executable
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := backtrace_test
+LOCAL_MODULE_FLAGS := debug
+
+LOCAL_SRC_FILES := \
+ backtrace_test.cpp \
+ thread_utils.c \
+
+LOCAL_CFLAGS += \
+ $(common_cflags) \
+ -fno-builtin \
+ -fstack-protector-all \
+ -O0 \
+ -g \
+ -DGTEST_OS_LINUX_ANDROID \
+ -DGTEST_HAS_STD_STRING \
+
+ifeq ($(TARGET_ARCH),arm64)
+ $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain)
+ LOCAL_CFLAGS += -fno-stack-protector
+endif # arm64
+
+LOCAL_CONLYFLAGS += \
+ $(common_conlyflags) \
+
+LOCAL_CPPFLAGS += \
+ $(common_cppflags) \
+
+LOCAL_SHARED_LIBRARIES += \
+ libcutils \
+ libbacktrace_test \
+ libbacktrace \
+
+LOCAL_LDLIBS := \
+ -lpthread \
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_NATIVE_TEST)
+
+#----------------------------------------------------------------------------
+# Only x86 host versions of libbacktrace supported.
+#----------------------------------------------------------------------------
+ifeq ($(HOST_ARCH),x86)
+
+#----------------------------------------------------------------------------
+# The host libbacktrace library using libcorkscrew
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+
+LOCAL_CFLAGS += \
+ $(common_cflags) \
+
+LOCAL_CONLYFLAGS += \
+ $(common_conlyflags) \
+
+LOCAL_C_INCLUDES := \
+ $(common_c_includes) \
+
+LOCAL_SHARED_LIBRARIES := \
+ libgccdemangle \
+ liblog \
+
+LOCAL_MODULE := libbacktrace
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
+ifeq ($(HOST_OS),linux)
+LOCAL_SRC_FILES += \
+ $(common_src) \
+ Corkscrew.cpp \
+
+LOCAL_C_INCLUDES += \
+ system/core/libcorkscrew \
+
+LOCAL_SHARED_LIBRARIES := \
+ libcorkscrew \
+
+LOCAL_CPPFLAGS += \
+ $(common_cppflags) \
+
+LOCAL_LDLIBS += \
+ -ldl \
+ -lrt \
+
+else
+LOCAL_SRC_FILES += \
+ BacktraceMap.cpp \
+
+endif
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+#----------------------------------------------------------------------------
+# The host test is only supported on linux.
+#----------------------------------------------------------------------------
+ifeq ($(HOST_OS),linux)
+
+#----------------------------------------------------------------------------
+# libbacktrace host test library, all optimizations turned off
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbacktrace_test
+LOCAL_MODULE_FLAGS := debug
+
+LOCAL_SRC_FILES := \
+ backtrace_testlib.c
+
+LOCAL_CFLAGS += \
+ -std=gnu99 \
+ -O0 \
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+#----------------------------------------------------------------------------
+# libbacktrace host test executable
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := backtrace_test
+LOCAL_MODULE_FLAGS := debug
+
+LOCAL_SRC_FILES := \
+ backtrace_test.cpp \
+ thread_utils.c \
+
+LOCAL_CFLAGS += \
+ $(common_cflags) \
+ -fno-builtin \
+ -fstack-protector-all \
+ -O0 \
+ -g \
+ -DGTEST_HAS_STD_STRING \
+
+LOCAL_SHARED_LIBRARIES := \
+ libbacktrace_test \
+ libbacktrace \
+
+LOCAL_LDLIBS := \
+ -lpthread \
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+endif # HOST_OS == linux
+
+endif # HOST_ARCH == x86
diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp
new file mode 100644
index 0000000..39296b4
--- /dev/null
+++ b/libbacktrace/BacktraceImpl.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceImpl.h"
+#include "thread_utils.h"
+
+//-------------------------------------------------------------------------
+// Backtrace functions.
+//-------------------------------------------------------------------------
+Backtrace::Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map)
+ : pid_(pid), tid_(-1), map_(map), map_shared_(true), impl_(impl) {
+ impl_->SetParent(this);
+
+ if (map_ == NULL) {
+ map_ = BacktraceMap::Create(pid);
+ map_shared_ = false;
+ }
+}
+
+Backtrace::~Backtrace() {
+ if (impl_) {
+ delete impl_;
+ impl_ = NULL;
+ }
+
+ if (map_ && !map_shared_) {
+ delete map_;
+ map_ = NULL;
+ }
+}
+
+bool Backtrace::Unwind(size_t num_ignore_frames) {
+ return impl_->Unwind(num_ignore_frames);
+}
+
+extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len,
+ int* status);
+
+std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
+ std::string func_name = impl_->GetFunctionNameRaw(pc, offset);
+ if (!func_name.empty()) {
+#if defined(__APPLE__)
+ // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
+ if (func_name[0] != '_') {
+ return func_name;
+ }
+#endif
+ char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0);
+ if (name) {
+ func_name = name;
+ free(name);
+ }
+ }
+ return func_name;
+}
+
+bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value) {
+ if (ptr & 3) {
+ BACK_LOGW("invalid pointer %p", (void*)ptr);
+ *out_value = (uint32_t)-1;
+ return false;
+ }
+ return true;
+}
+
+std::string Backtrace::FormatFrameData(size_t frame_num) {
+ if (frame_num >= frames_.size()) {
+ return "";
+ }
+ return FormatFrameData(&frames_[frame_num]);
+}
+
+std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
+ const char* map_name;
+ if (frame->map && !frame->map->name.empty()) {
+ map_name = frame->map->name.c_str();
+ } else {
+ map_name = "<unknown>";
+ }
+
+ uintptr_t relative_pc;
+ if (frame->map) {
+ relative_pc = frame->pc - frame->map->start;
+ } else {
+ relative_pc = frame->pc;
+ }
+
+ char buf[512];
+ if (!frame->func_name.empty() && frame->func_offset) {
+ snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")",
+ frame->num, (int)sizeof(uintptr_t)*2, relative_pc, map_name,
+ frame->func_name.c_str(), frame->func_offset);
+ } else if (!frame->func_name.empty()) {
+ snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s)", frame->num,
+ (int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->func_name.c_str());
+ } else {
+ snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s", frame->num,
+ (int)sizeof(uintptr_t)*2, relative_pc, map_name);
+ }
+
+ return buf;
+}
+
+const backtrace_map_t* Backtrace::FindMap(uintptr_t pc) {
+ return map_->Find(pc);
+}
+
+//-------------------------------------------------------------------------
+// BacktraceCurrent functions.
+//-------------------------------------------------------------------------
+BacktraceCurrent::BacktraceCurrent(
+ BacktraceImpl* impl, BacktraceMap* map) : Backtrace(impl, getpid(), map) {
+}
+
+BacktraceCurrent::~BacktraceCurrent() {
+}
+
+bool BacktraceCurrent::ReadWord(uintptr_t ptr, uint32_t* out_value) {
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+ const backtrace_map_t* map = FindMap(ptr);
+ if (map && map->flags & PROT_READ) {
+ *out_value = *reinterpret_cast<uint32_t*>(ptr);
+ return true;
+ } else {
+ BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
+ *out_value = static_cast<uint32_t>(-1);
+ return false;
+ }
+}
+
+//-------------------------------------------------------------------------
+// BacktracePtrace functions.
+//-------------------------------------------------------------------------
+BacktracePtrace::BacktracePtrace(
+ BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map)
+ : Backtrace(impl, pid, map) {
+ tid_ = tid;
+}
+
+BacktracePtrace::~BacktracePtrace() {
+}
+
+bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) {
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+#if defined(__APPLE__)
+ BACK_LOGW("MacOS does not support reading from another pid.");
+ return false;
+#else
+ // ptrace() returns -1 and sets errno when the operation fails.
+ // To disambiguate -1 from a valid result, we clear errno beforehand.
+ errno = 0;
+ *out_value = ptrace(PTRACE_PEEKTEXT, Tid(), reinterpret_cast<void*>(ptr), NULL);
+ if (*out_value == static_cast<uint32_t>(-1) && errno) {
+ BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s",
+ reinterpret_cast<void*>(ptr), Tid(), strerror(errno));
+ return false;
+ }
+ return true;
+#endif
+}
+
+Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) {
+ if (pid == BACKTRACE_CURRENT_PROCESS || pid == getpid()) {
+ if (tid == BACKTRACE_CURRENT_THREAD || tid == gettid()) {
+ return CreateCurrentObj(map);
+ } else {
+ return CreateThreadObj(tid, map);
+ }
+ } else if (tid == BACKTRACE_CURRENT_THREAD) {
+ return CreatePtraceObj(pid, pid, map);
+ } else {
+ return CreatePtraceObj(pid, tid, map);
+ }
+}
diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h
new file mode 100644
index 0000000..af014d5
--- /dev/null
+++ b/libbacktrace/BacktraceImpl.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_IMPL_H
+#define _LIBBACKTRACE_BACKTRACE_IMPL_H
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <sys/types.h>
+#include <log/log.h>
+
+// Macro to log the function name along with the warning message.
+#define BACK_LOGW(format, ...) \
+ ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+
+class BacktraceImpl {
+public:
+ virtual ~BacktraceImpl() { }
+
+ virtual bool Unwind(size_t num_ignore_frames) = 0;
+
+ // The name returned is not demangled, Backtrace::GetFunctionName()
+ // takes care of demangling the name.
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0;
+
+ void SetParent(Backtrace* backtrace) { backtrace_obj_ = backtrace; }
+
+ inline pid_t Pid() { return backtrace_obj_->Pid(); }
+ inline pid_t Tid() { return backtrace_obj_->Tid(); }
+
+ inline const backtrace_map_t* FindMap(uintptr_t addr) {
+ return backtrace_obj_->FindMap(addr);
+ }
+ inline std::string GetFunctionName(uintptr_t pc, uintptr_t* offset) {
+ return backtrace_obj_->GetFunctionName(pc, offset);
+ }
+ inline BacktraceMap* GetMap() { return backtrace_obj_->GetMap(); }
+
+protected:
+ inline std::vector<backtrace_frame_data_t>* GetFrames() { return &backtrace_obj_->frames_; }
+
+ Backtrace* backtrace_obj_;
+};
+
+class BacktraceCurrent : public Backtrace {
+public:
+ BacktraceCurrent(BacktraceImpl* impl, BacktraceMap* map);
+ virtual ~BacktraceCurrent();
+
+ bool ReadWord(uintptr_t ptr, uint32_t* out_value);
+};
+
+class BacktracePtrace : public Backtrace {
+public:
+ BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map);
+ virtual ~BacktracePtrace();
+
+ bool ReadWord(uintptr_t ptr, uint32_t* out_value);
+};
+
+Backtrace* CreateCurrentObj(BacktraceMap* map);
+Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map);
+Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map);
+
+#endif // _LIBBACKTRACE_BACKTRACE_IMPL_H
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
new file mode 100644
index 0000000..6320800
--- /dev/null
+++ b/libbacktrace/BacktraceMap.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014 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 <ctype.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <backtrace/backtrace_constants.h>
+#include <backtrace/BacktraceMap.h>
+#include <log/log.h>
+
+#include "thread_utils.h"
+#include "BacktraceImpl.h"
+
+BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
+ if (pid_ < 0) {
+ pid_ = getpid();
+ }
+}
+
+BacktraceMap::~BacktraceMap() {
+}
+
+const backtrace_map_t* BacktraceMap::Find(uintptr_t addr) {
+ for (BacktraceMap::const_iterator it = begin();
+ it != end(); ++it) {
+ if (addr >= it->start && addr < it->end) {
+ return &*it;
+ }
+ }
+ return NULL;
+}
+
+bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
+ unsigned long int start;
+ unsigned long int end;
+ char permissions[5];
+ int name_pos;
+
+#if defined(__APPLE__)
+// Mac OS vmmap(1) output:
+// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+ if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n",
+ &start, &end, permissions, &name_pos) != 3) {
+#else
+// Linux /proc/<pid>/maps lines:
+// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+ if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n",
+ &start, &end, permissions, &name_pos) != 3) {
+#endif
+ return false;
+ }
+
+ map->start = start;
+ map->end = end;
+ map->flags = PROT_NONE;
+ if (permissions[0] == 'r') {
+ map->flags |= PROT_READ;
+ }
+ if (permissions[1] == 'w') {
+ map->flags |= PROT_WRITE;
+ }
+ if (permissions[2] == 'x') {
+ map->flags |= PROT_EXEC;
+ }
+
+ while (isspace(line[name_pos])) {
+ name_pos += 1;
+ }
+ map->name = line+name_pos;
+ if (!map->name.empty() && map->name[map->name.length()-1] == '\n') {
+ map->name.erase(map->name.length()-1);
+ }
+
+ ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s",
+ map->start, map->end, map->flags, map->name.c_str());
+ return true;
+}
+
+bool BacktraceMap::Build() {
+#if defined(__APPLE__)
+ char cmd[sizeof(pid_t)*3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
+#else
+ char path[sizeof(pid_t)*3 + sizeof("/proc//maps") + 1];
+#endif
+ char line[1024];
+
+#if defined(__APPLE__)
+ // cmd is guaranteed to always be big enough to hold this string.
+ sprintf(cmd, "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
+ FILE* fp = popen(cmd, "r");
+#else
+ // path is guaranteed to always be big enough to hold this string.
+ sprintf(path, "/proc/%d/maps", pid_);
+ FILE* fp = fopen(path, "r");
+#endif
+ if (fp == NULL) {
+ return false;
+ }
+
+ while(fgets(line, sizeof(line), fp)) {
+ backtrace_map_t map;
+ if (ParseLine(line, &map)) {
+ maps_.push_back(map);
+ }
+ }
+#if defined(__APPLE__)
+ pclose(fp);
+#else
+ fclose(fp);
+#endif
+
+ return true;
+}
+
+#if defined(__APPLE__)
+// Corkscrew and libunwind don't compile on the mac, so create a generic
+// map object.
+BacktraceMap* BacktraceMap::Create(pid_t pid) {
+ BacktraceMap* map = new BacktraceMap(pid);
+ if (!map->Build()) {
+ delete map;
+ return NULL;
+ }
+ return map;
+}
+#endif
diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp
new file mode 100644
index 0000000..5ffe516
--- /dev/null
+++ b/libbacktrace/BacktraceThread.cpp
@@ -0,0 +1,215 @@
+/*
+ * 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 "BacktraceThread.h"
+#include "thread_utils.h"
+
+//-------------------------------------------------------------------------
+// ThreadEntry implementation.
+//-------------------------------------------------------------------------
+static ThreadEntry* g_list = NULL;
+static pthread_mutex_t g_entry_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t g_sigaction_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_entry_mutex);
+ if (g_list == this) {
+ g_list = next;
+ } else {
+ if (next) {
+ next->prev = prev;
+ }
+ prev->next = next;
+ }
+ pthread_mutex_unlock(&g_entry_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_entry_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.
+ BACK_LOGW("Entry for pid %d tid %d already exists.", pid, tid);
+
+ pthread_mutex_unlock(&g_entry_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_entry_mutex);
+
+ return entry;
+}
+
+//-------------------------------------------------------------------------
+// BacktraceThread functions.
+//-------------------------------------------------------------------------
+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) {
+ 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,
+ BacktraceMap* map)
+ : BacktraceCurrent(impl, map), thread_intf_(thread_intf) {
+ tid_ = tid;
+}
+
+BacktraceThread::~BacktraceThread() {
+}
+
+void BacktraceThread::FinishUnwind() {
+ for (std::vector<backtrace_frame_data_t>::iterator it = frames_.begin();
+ it != frames_.end(); ++it) {
+ it->map = FindMap(it->pc);
+
+ it->func_offset = 0;
+ it->func_name = GetFunctionName(it->pc, &it->func_offset);
+ }
+}
+
+bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) {
+ entry->state = STATE_WAITING;
+
+ if (tgkill(Pid(), Tid(), SIGURG) != 0) {
+ BACK_LOGW("tgkill failed %s", strerror(errno));
+ return false;
+ }
+
+ // Allow up to ten seconds for the dump to start.
+ int wait_millis = 10000;
+ 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) {
+ 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.
+ wait_millis = 10000;
+ while (android_atomic_acquire_load(&entry->state) != STATE_DONE) {
+ if (wait_millis--) {
+ usleep(1000);
+ } else {
+ BACK_LOGW("Didn't finish thread unwind in 60 seconds.");
+ break;
+ }
+ }
+ return !cancelled;
+}
+
+bool BacktraceThread::Unwind(size_t num_ignore_frames) {
+ ThreadEntry* entry = ThreadEntry::AddThreadToUnwind(
+ thread_intf_, Pid(), Tid(), num_ignore_frames);
+ if (!entry) {
+ return false;
+ }
+
+ // Prevent multiple threads trying to set the trigger action on different
+ // threads at the same time.
+ bool retval = false;
+ if (pthread_mutex_lock(&g_sigaction_mutex) == 0) {
+ 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 {
+ BACK_LOGW("sigaction failed %s", strerror(errno));
+ }
+ pthread_mutex_unlock(&g_sigaction_mutex);
+ } else {
+ BACK_LOGW("unable to acquire sigaction mutex.");
+ }
+
+ if (retval) {
+ FinishUnwind();
+ }
+ delete entry;
+
+ return retval;
+}
diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h
new file mode 100644
index 0000000..3412d58
--- /dev/null
+++ b/libbacktrace/BacktraceThread.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_THREAD_H
+#define _LIBBACKTRACE_BACKTRACE_THREAD_H
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "BacktraceImpl.h"
+
+enum state_e {
+ STATE_WAITING = 0,
+ STATE_DUMPING,
+ STATE_DONE,
+ STATE_CANCEL,
+};
+
+class BacktraceThreadInterface;
+
+struct ThreadEntry {
+ ThreadEntry(
+ BacktraceThreadInterface* impl, pid_t pid, pid_t tid,
+ size_t num_ignore_frames);
+ ~ThreadEntry();
+
+ bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid && chk_tid == tid); }
+
+ static ThreadEntry* AddThreadToUnwind(
+ BacktraceThreadInterface* thread_intf, pid_t pid, pid_t tid,
+ size_t num_ignored_frames);
+
+ BacktraceThreadInterface* thread_intf;
+ pid_t pid;
+ pid_t tid;
+ ThreadEntry* next;
+ ThreadEntry* prev;
+ int32_t state;
+ int num_ignore_frames;
+};
+
+// Interface class that does not contain any local storage, only defines
+// virtual functions to be defined by subclasses.
+class BacktraceThreadInterface {
+public:
+ virtual ~BacktraceThreadInterface() { }
+
+ virtual void ThreadUnwind(
+ siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0;
+};
+
+class BacktraceThread : public BacktraceCurrent {
+public:
+ // impl and thread_intf should point to the same object, this allows
+ // the compiler to catch if an implementation does not properly
+ // subclass both.
+ BacktraceThread(
+ BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid,
+ BacktraceMap* map);
+ virtual ~BacktraceThread();
+
+ virtual bool Unwind(size_t num_ignore_frames);
+
+ virtual void ThreadUnwind(
+ siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) {
+ thread_intf_->ThreadUnwind(siginfo, sigcontext, num_ignore_frames);
+ }
+
+private:
+ virtual bool TriggerUnwindOnThread(ThreadEntry* entry);
+
+ virtual void FinishUnwind();
+
+ BacktraceThreadInterface* thread_intf_;
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_THREAD_H
diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp
new file mode 100644
index 0000000..efeee2e
--- /dev/null
+++ b/libbacktrace/Corkscrew.cpp
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libbacktrace"
+
+#include <backtrace/Backtrace.h>
+
+#include <string.h>
+
+#include <backtrace-arch.h>
+#include <corkscrew/backtrace.h>
+
+#ifndef __USE_GNU
+#define __USE_GNU
+#endif
+#include <dlfcn.h>
+
+#include "Corkscrew.h"
+
+//-------------------------------------------------------------------------
+// CorkscrewMap functions.
+//-------------------------------------------------------------------------
+CorkscrewMap::CorkscrewMap(pid_t pid) : BacktraceMap(pid), map_info_(NULL) {
+}
+
+CorkscrewMap::~CorkscrewMap() {
+ if (map_info_) {
+ free_map_info_list(map_info_);
+ map_info_ = NULL;
+ }
+}
+
+bool CorkscrewMap::Build() {
+ map_info_ = load_map_info_list(pid_);
+
+ // Use the information in map_info_ to construct the BacktraceMap data
+ // rather than reparsing /proc/self/maps.
+ map_info_t* cur_map = map_info_;
+ while (cur_map) {
+ backtrace_map_t map;
+ map.start = cur_map->start;
+ map.end = cur_map->end;
+ map.flags = 0;
+ if (cur_map->is_readable) {
+ map.flags |= PROT_READ;
+ }
+ if (cur_map->is_writable) {
+ map.flags |= PROT_WRITE;
+ }
+ if (cur_map->is_executable) {
+ map.flags |= PROT_EXEC;
+ }
+ map.name = cur_map->name;
+
+ // The maps are in descending order, but we want them in ascending order.
+ maps_.push_front(map);
+
+ cur_map = cur_map->next;
+ }
+ return map_info_ != NULL;
+}
+
+//-------------------------------------------------------------------------
+// CorkscrewCommon functions.
+//-------------------------------------------------------------------------
+bool CorkscrewCommon::GenerateFrameData(
+ backtrace_frame_t* cork_frames, ssize_t num_frames) {
+ if (num_frames < 0) {
+ BACK_LOGW("libcorkscrew unwind failed.");
+ return false;
+ }
+
+ std::vector<backtrace_frame_data_t>* frames = GetFrames();
+ frames->resize(num_frames);
+ size_t i = 0;
+ for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin();
+ it != frames->end(); ++it, ++i) {
+ it->num = i;
+ it->pc = cork_frames[i].absolute_pc;
+ it->sp = cork_frames[i].stack_top;
+ it->stack_size = cork_frames[i].stack_size;
+ it->func_offset = 0;
+
+ it->map = FindMap(it->pc);
+ it->func_name = GetFunctionName(it->pc, &it->func_offset);
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------
+// CorkscrewCurrent functions.
+//-------------------------------------------------------------------------
+CorkscrewCurrent::CorkscrewCurrent() {
+}
+
+CorkscrewCurrent::~CorkscrewCurrent() {
+}
+
+bool CorkscrewCurrent::Unwind(size_t num_ignore_frames) {
+ backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
+ ssize_t num_frames = unwind_backtrace(frames, num_ignore_frames, MAX_BACKTRACE_FRAMES);
+
+ return GenerateFrameData(frames, num_frames);
+}
+
+std::string CorkscrewCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ *offset = 0;
+
+ Dl_info info;
+ const backtrace_map_t* map = FindMap(pc);
+ if (map) {
+ if (dladdr((const void*)pc, &info)) {
+ if (info.dli_sname) {
+ *offset = pc - map->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase;
+ return info.dli_sname;
+ }
+ } else {
+ // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file...
+ symbol_table_t* symbol_table = load_symbol_table(map->name.c_str());
+ if (symbol_table) {
+ // First check if we can find the symbol using a relative pc.
+ std::string name;
+ const symbol_t* elf_symbol = find_symbol(symbol_table, pc - map->start);
+ if (elf_symbol) {
+ name = elf_symbol->name;
+ *offset = pc - map->start - elf_symbol->start;
+ } else if ((elf_symbol = find_symbol(symbol_table, pc)) != NULL) {
+ // Found the symbol using the absolute pc.
+ name = elf_symbol->name;
+ *offset = pc - elf_symbol->start;
+ }
+ free_symbol_table(symbol_table);
+ return name;
+ }
+ }
+ }
+ return "";
+}
+
+//-------------------------------------------------------------------------
+// CorkscrewThread functions.
+//-------------------------------------------------------------------------
+CorkscrewThread::CorkscrewThread() {
+}
+
+CorkscrewThread::~CorkscrewThread() {
+}
+
+void CorkscrewThread::ThreadUnwind(
+ siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) {
+ backtrace_frame_t cork_frames[MAX_BACKTRACE_FRAMES];
+ CorkscrewMap* map = static_cast<CorkscrewMap*>(GetMap());
+ ssize_t num_frames = unwind_backtrace_signal_arch(
+ siginfo, sigcontext, map->GetMapInfo(), cork_frames,
+ num_ignore_frames, MAX_BACKTRACE_FRAMES);
+ if (num_frames > 0) {
+ std::vector<backtrace_frame_data_t>* frames = GetFrames();
+ frames->resize(num_frames);
+ size_t i = 0;
+ for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin();
+ it != frames->end(); ++it, ++i) {
+ it->num = i;
+ it->pc = cork_frames[i].absolute_pc;
+ it->sp = cork_frames[i].stack_top;
+ it->stack_size = cork_frames[i].stack_size;
+ it->map = NULL;
+ it->func_offset = 0;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------
+// CorkscrewPtrace functions.
+//-------------------------------------------------------------------------
+CorkscrewPtrace::CorkscrewPtrace() : ptrace_context_(NULL) {
+}
+
+CorkscrewPtrace::~CorkscrewPtrace() {
+ if (ptrace_context_) {
+ free_ptrace_context(ptrace_context_);
+ ptrace_context_ = NULL;
+ }
+}
+
+bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) {
+ ptrace_context_ = load_ptrace_context(Tid());
+
+ backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
+ ssize_t num_frames = unwind_backtrace_ptrace(
+ Tid(), ptrace_context_, frames, num_ignore_frames, MAX_BACKTRACE_FRAMES);
+
+ return GenerateFrameData(frames, num_frames);
+}
+
+std::string CorkscrewPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ // Get information about a different process.
+ const map_info_t* map_info;
+ const symbol_t* symbol;
+ find_symbol_ptrace(ptrace_context_, pc, &map_info, &symbol);
+ char* symbol_name = NULL;
+ if (symbol) {
+ if (map_info) {
+ *offset = pc - map_info->start - symbol->start;
+ }
+ symbol_name = symbol->name;
+ return symbol_name;
+ }
+
+ return "";
+}
+
+//-------------------------------------------------------------------------
+// C++ object creation functions.
+//-------------------------------------------------------------------------
+Backtrace* CreateCurrentObj(BacktraceMap* map) {
+ return new BacktraceCurrent(new CorkscrewCurrent(), map);
+}
+
+Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) {
+ return new BacktracePtrace(new CorkscrewPtrace(), pid, tid, map);
+}
+
+Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) {
+ CorkscrewThread* thread_obj = new CorkscrewThread();
+ return new BacktraceThread(thread_obj, thread_obj, tid, map);
+}
+
+//-------------------------------------------------------------------------
+// BacktraceMap create function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::Create(pid_t pid) {
+ BacktraceMap* map = new CorkscrewMap(pid);
+ if (!map->Build()) {
+ delete map;
+ return NULL;
+ }
+ return map;
+}
diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h
new file mode 100644
index 0000000..1633398
--- /dev/null
+++ b/libbacktrace/Corkscrew.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBBACKTRACE_CORKSCREW_H
+#define _LIBBACKTRACE_CORKSCREW_H
+
+#include <inttypes.h>
+
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <corkscrew/backtrace.h>
+
+#include "BacktraceImpl.h"
+#include "BacktraceThread.h"
+
+class CorkscrewMap : public BacktraceMap {
+public:
+ CorkscrewMap(pid_t pid);
+ virtual ~CorkscrewMap();
+
+ virtual bool Build();
+
+ map_info_t* GetMapInfo() { return map_info_; }
+
+private:
+ map_info_t* map_info_;
+};
+
+class CorkscrewCommon : public BacktraceImpl {
+public:
+ bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames);
+};
+
+class CorkscrewCurrent : public CorkscrewCommon {
+public:
+ CorkscrewCurrent();
+ virtual ~CorkscrewCurrent();
+
+ virtual bool Unwind(size_t num_ignore_threads);
+
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+};
+
+class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface {
+public:
+ CorkscrewThread();
+ virtual ~CorkscrewThread();
+
+ virtual void ThreadUnwind(
+ siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames);
+};
+
+class CorkscrewPtrace : public CorkscrewCommon {
+public:
+ CorkscrewPtrace();
+ virtual ~CorkscrewPtrace();
+
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+
+ virtual bool Unwind(size_t num_ignore_threads);
+
+private:
+ ptrace_context_t* ptrace_context_;
+};
+
+#endif // _LIBBACKTRACE_CORKSCREW_H
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
new file mode 100644
index 0000000..17b71b9
--- /dev/null
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libbacktrace"
+
+#include <sys/types.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+#include "UnwindCurrent.h"
+#include "UnwindMap.h"
+
+// Define the ucontext_t structures needed for each supported arch.
+#if defined(__arm__)
+ // The current version of the <signal.h> doesn't define ucontext_t.
+ #include <asm/sigcontext.h> // Ensure 'struct sigcontext' is defined.
+
+ // Machine context at the time a signal was raised.
+ typedef struct ucontext {
+ uint32_t uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ struct sigcontext uc_mcontext;
+ uint32_t uc_sigmask;
+ } ucontext_t;
+#elif defined(__i386__)
+ #include <asm/sigcontext.h>
+ #include <asm/ucontext.h>
+ typedef struct ucontext ucontext_t;
+#elif !defined(__mips__) && !defined(__aarch64__)
+ #error Unsupported architecture.
+#endif
+
+//-------------------------------------------------------------------------
+// UnwindCurrent functions.
+//-------------------------------------------------------------------------
+UnwindCurrent::UnwindCurrent() {
+}
+
+UnwindCurrent::~UnwindCurrent() {
+}
+
+bool UnwindCurrent::Unwind(size_t num_ignore_frames) {
+ int ret = unw_getcontext(&context_);
+ if (ret < 0) {
+ BACK_LOGW("unw_getcontext failed %d", ret);
+ return false;
+ }
+ return UnwindFromContext(num_ignore_frames, true);
+}
+
+std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ *offset = 0;
+ char buf[512];
+ unw_word_t value;
+ if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
+ &value, &context_) >= 0 && buf[0] != '\0') {
+ *offset = static_cast<uintptr_t>(value);
+ return buf;
+ }
+ return "";
+}
+
+bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) {
+ // The cursor structure is pretty large, do not put it on the stack.
+ unw_cursor_t* cursor = new unw_cursor_t;
+ int ret = unw_init_local(cursor, &context_);
+ if (ret < 0) {
+ BACK_LOGW("unw_init_local failed %d", ret);
+ delete cursor;
+ return false;
+ }
+
+ std::vector<backtrace_frame_data_t>* frames = GetFrames();
+ frames->reserve(MAX_BACKTRACE_FRAMES);
+ 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->at(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) {
+ // Set the stack size for the previous frame.
+ backtrace_frame_data_t* prev = &frames->at(num_frames-1);
+ prev->stack_size = frame->sp - prev->sp;
+ }
+
+ if (resolve) {
+ frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
+ frame->map = FindMap(frame->pc);
+ } else {
+ frame->map = NULL;
+ frame->func_offset = 0;
+ }
+ num_frames++;
+ } else {
+ num_ignore_frames--;
+ }
+ ret = unw_step (cursor);
+ } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
+
+ delete cursor;
+ return true;
+}
+
+void UnwindCurrent::ExtractContext(void* sigcontext) {
+ unw_tdep_context_t* context = reinterpret_cast<unw_tdep_context_t*>(&context_);
+ const ucontext_t* uc = reinterpret_cast<const ucontext_t*>(sigcontext);
+
+#if defined(__arm__)
+ context->regs[0] = uc->uc_mcontext.arm_r0;
+ context->regs[1] = uc->uc_mcontext.arm_r1;
+ context->regs[2] = uc->uc_mcontext.arm_r2;
+ context->regs[3] = uc->uc_mcontext.arm_r3;
+ context->regs[4] = uc->uc_mcontext.arm_r4;
+ context->regs[5] = uc->uc_mcontext.arm_r5;
+ context->regs[6] = uc->uc_mcontext.arm_r6;
+ context->regs[7] = uc->uc_mcontext.arm_r7;
+ context->regs[8] = uc->uc_mcontext.arm_r8;
+ context->regs[9] = uc->uc_mcontext.arm_r9;
+ context->regs[10] = uc->uc_mcontext.arm_r10;
+ context->regs[11] = uc->uc_mcontext.arm_fp;
+ context->regs[12] = uc->uc_mcontext.arm_ip;
+ context->regs[13] = uc->uc_mcontext.arm_sp;
+ context->regs[14] = uc->uc_mcontext.arm_lr;
+ context->regs[15] = uc->uc_mcontext.arm_pc;
+#elif defined(__mips__) || defined(__i386__)
+ context->uc_mcontext = uc->uc_mcontext;
+#endif
+}
+
+//-------------------------------------------------------------------------
+// UnwindThread functions.
+//-------------------------------------------------------------------------
+UnwindThread::UnwindThread() {
+}
+
+UnwindThread::~UnwindThread() {
+}
+
+void UnwindThread::ThreadUnwind(
+ siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) {
+ ExtractContext(sigcontext);
+ UnwindFromContext(num_ignore_frames, false);
+}
+
+//-------------------------------------------------------------------------
+// C++ object creation function.
+//-------------------------------------------------------------------------
+Backtrace* CreateCurrentObj(BacktraceMap* map) {
+ return new BacktraceCurrent(new UnwindCurrent(), map);
+}
+
+Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) {
+ UnwindThread* thread_obj = new UnwindThread();
+ return new BacktraceThread(thread_obj, thread_obj, tid, map);
+}
diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h
new file mode 100644
index 0000000..acce110
--- /dev/null
+++ b/libbacktrace/UnwindCurrent.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_CURRENT_H
+#define _LIBBACKTRACE_UNWIND_CURRENT_H
+
+#include <string>
+
+#include "BacktraceImpl.h"
+#include "BacktraceThread.h"
+
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+class UnwindCurrent : public BacktraceImpl {
+public:
+ UnwindCurrent();
+ virtual ~UnwindCurrent();
+
+ virtual bool Unwind(size_t num_ignore_frames);
+
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+
+ bool UnwindFromContext(size_t num_ignore_frames, bool resolve);
+
+ void ExtractContext(void* sigcontext);
+
+protected:
+ unw_context_t context_;
+};
+
+class UnwindThread : public UnwindCurrent, public BacktraceThreadInterface {
+public:
+ UnwindThread();
+ virtual ~UnwindThread();
+
+ virtual void ThreadUnwind(
+ siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames);
+};
+
+#endif // _LIBBACKTRACE_UNWIND_CURRENT_H
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
new file mode 100644
index 0000000..03bb192
--- /dev/null
+++ b/libbacktrace/UnwindMap.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define LOG_TAG "libbacktrace"
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <backtrace/BacktraceMap.h>
+
+#include <libunwind.h>
+
+#include "UnwindMap.h"
+
+//-------------------------------------------------------------------------
+// libunwind has a single shared address space for the current process
+// aka local. If multiple maps are created for the current pid, then
+// only update the local address space once, and keep a reference count
+// of maps using the same map cursor.
+//-------------------------------------------------------------------------
+static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER;
+static unw_map_cursor_t g_map_cursor;
+static int g_map_references = 0;
+
+UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
+ map_cursor_.map_list = NULL;
+}
+
+UnwindMap::~UnwindMap() {
+ if (pid_ == getpid()) {
+ pthread_mutex_lock(&g_map_mutex);
+ if (--g_map_references == 0) {
+ // Clear the local address space map.
+ unw_map_set(unw_local_addr_space, NULL);
+ unw_map_cursor_destroy(&map_cursor_);
+ }
+ pthread_mutex_unlock(&g_map_mutex);
+ } else {
+ unw_map_cursor_destroy(&map_cursor_);
+ }
+}
+
+bool UnwindMap::Build() {
+ bool return_value = true;
+ if (pid_ == getpid()) {
+ pthread_mutex_lock(&g_map_mutex);
+ if (g_map_references == 0) {
+ return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
+ if (return_value) {
+ // Set the local address space to this cursor map.
+ unw_map_set(unw_local_addr_space, &map_cursor_);
+ g_map_references = 1;
+ g_map_cursor = map_cursor_;
+ }
+ } else {
+ g_map_references++;
+ map_cursor_ = g_map_cursor;
+ }
+ pthread_mutex_unlock(&g_map_mutex);
+ } else {
+ return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
+ }
+
+ if (!return_value)
+ return false;
+
+ // Use the map_cursor information to construct the BacktraceMap data
+ // rather than reparsing /proc/self/maps.
+ unw_map_cursor_reset(&map_cursor_);
+ unw_map_t unw_map;
+ while (unw_map_cursor_get(&map_cursor_, &unw_map)) {
+ backtrace_map_t map;
+
+ map.start = unw_map.start;
+ map.end = unw_map.end;
+ map.flags = unw_map.flags;
+ map.name = unw_map.path;
+
+ // The maps are in descending order, but we want them in ascending order.
+ maps_.push_front(map);
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------
+// BacktraceMap create function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::Create(pid_t pid) {
+ BacktraceMap* map = new UnwindMap(pid);
+ if (!map->Build()) {
+ delete map;
+ return NULL;
+ }
+ return map;
+}
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
new file mode 100644
index 0000000..5a874e8
--- /dev/null
+++ b/libbacktrace/UnwindMap.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_MAP_H
+#define _LIBBACKTRACE_UNWIND_MAP_H
+
+#include <backtrace/BacktraceMap.h>
+
+// The unw_map_cursor_t structure is different depending on whether it is
+// the local or remote version. In order to get the correct version, include
+// libunwind.h first then this header.
+
+class UnwindMap : public BacktraceMap {
+public:
+ UnwindMap(pid_t pid);
+ virtual ~UnwindMap();
+
+ virtual bool Build();
+
+ unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
+
+private:
+ unw_map_cursor_t map_cursor_;
+};
+
+#endif // _LIBBACKTRACE_UNWIND_MAP_H
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
new file mode 100644
index 0000000..732dae8
--- /dev/null
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libbacktrace"
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+
+#include "UnwindMap.h"
+#include "UnwindPtrace.h"
+
+UnwindPtrace::UnwindPtrace() : addr_space_(NULL), upt_info_(NULL) {
+}
+
+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;
+ }
+}
+
+bool UnwindPtrace::Unwind(size_t num_ignore_frames) {
+ addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
+ if (!addr_space_) {
+ BACK_LOGW("unw_create_addr_space failed.");
+ return false;
+ }
+
+ UnwindMap* map = static_cast<UnwindMap*>(GetMap());
+ unw_map_set(addr_space_, map->GetMapCursor());
+
+ upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
+ if (!upt_info_) {
+ BACK_LOGW("Failed to create upt info.");
+ return false;
+ }
+
+ unw_cursor_t cursor;
+ int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
+ if (ret < 0) {
+ BACK_LOGW("unw_init_remote failed %d", ret);
+ return false;
+ }
+
+ std::vector<backtrace_frame_data_t>* frames = GetFrames();
+ frames->reserve(MAX_BACKTRACE_FRAMES);
+ 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->at(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->at(num_frames-1);
+ prev->stack_size = frame->sp - prev->sp;
+ }
+
+ frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
+
+ frame->map = FindMap(frame->pc);
+
+ num_frames++;
+ } else {
+ num_ignore_frames--;
+ }
+ ret = unw_step (&cursor);
+ } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
+
+ return true;
+}
+
+std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ *offset = 0;
+ char buf[512];
+ unw_word_t value;
+ if (unw_get_proc_name_by_ip(addr_space_, pc, buf, sizeof(buf), &value,
+ upt_info_) >= 0 && buf[0] != '\0') {
+ *offset = static_cast<uintptr_t>(value);
+ return buf;
+ }
+ return "";
+}
+
+//-------------------------------------------------------------------------
+// C++ object creation function.
+//-------------------------------------------------------------------------
+Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) {
+ return new BacktracePtrace(new UnwindPtrace(), pid, tid, map);
+}
diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h
new file mode 100644
index 0000000..1e82117
--- /dev/null
+++ b/libbacktrace/UnwindPtrace.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_PTRACE_H
+#define _LIBBACKTRACE_UNWIND_PTRACE_H
+
+#include <string>
+
+#include "BacktraceImpl.h"
+
+#include <libunwind.h>
+
+class UnwindPtrace : public BacktraceImpl {
+public:
+ UnwindPtrace();
+ virtual ~UnwindPtrace();
+
+ virtual bool Unwind(size_t num_ignore_frames);
+
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+
+private:
+ unw_addr_space_t addr_space_;
+ struct UPT_info* upt_info_;
+};
+
+#endif // _LIBBACKTRACE_UNWIND_PTRACE_H
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
new file mode 100644
index 0000000..23eaf92
--- /dev/null
+++ b/libbacktrace/backtrace_test.cpp
@@ -0,0 +1,695 @@
+/*
+ * 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 <dirent.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+#include <UniquePtr.h>
+
+#include <cutils/atomic.h>
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include "thread_utils.h"
+
+// Number of microseconds per milliseconds.
+#define US_PER_MSEC 1000
+
+// Number of nanoseconds in a second.
+#define NS_PER_SEC 1000000000ULL
+
+// Number of simultaneous dumping operations to perform.
+#define NUM_THREADS 20
+
+// Number of simultaneous threads running in our forked process.
+#define NUM_PTRACE_THREADS 5
+
+struct thread_t {
+ pid_t tid;
+ int32_t state;
+ pthread_t threadId;
+};
+
+struct dump_thread_t {
+ thread_t thread;
+ Backtrace* backtrace;
+ int32_t* now;
+ int32_t done;
+};
+
+extern "C" {
+// Prototypes for functions in the test library.
+int test_level_one(int, int, int, int, void (*)(void*), void*);
+
+int test_recursive_call(int, void (*)(void*), void*);
+}
+
+uint64_t NanoTime() {
+ struct timespec t = { 0, 0 };
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
+}
+
+void DumpFrames(Backtrace* backtrace) {
+ if (backtrace->NumFrames() == 0) {
+ printf(" No frames to dump\n");
+ return;
+ }
+
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ printf(" %s\n", backtrace->FormatFrameData(i).c_str());
+ }
+}
+
+void WaitForStop(pid_t pid) {
+ uint64_t start = NanoTime();
+
+ siginfo_t si;
+ while (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) < 0 && (errno == EINTR || errno == ESRCH)) {
+ if ((NanoTime() - start) > NS_PER_SEC) {
+ printf("The process did not get to a stopping point in 1 second.\n");
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+}
+
+bool ReadyLevelBacktrace(Backtrace* backtrace) {
+ // See if test_level_four is in the backtrace.
+ bool found = false;
+ for (Backtrace::const_iterator it = backtrace->begin(); it != backtrace->end(); ++it) {
+ if (it->func_name == "test_level_four") {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
+void VerifyLevelDump(Backtrace* backtrace) {
+ ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0));
+ ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES));
+
+ // Look through the frames starting at the highest to find the
+ // frame we want.
+ size_t frame_num = 0;
+ for (size_t i = backtrace->NumFrames()-1; i > 2; i--) {
+ if (backtrace->GetFrame(i)->func_name == "test_level_one") {
+ frame_num = i;
+ break;
+ }
+ }
+ ASSERT_LT(static_cast<size_t>(0), frame_num);
+ ASSERT_LE(static_cast<size_t>(3), frame_num);
+
+ ASSERT_EQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one");
+ ASSERT_EQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two");
+ ASSERT_EQ(backtrace->GetFrame(frame_num-2)->func_name, "test_level_three");
+ ASSERT_EQ(backtrace->GetFrame(frame_num-3)->func_name, "test_level_four");
+}
+
+void VerifyLevelBacktrace(void*) {
+ UniquePtr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyLevelDump(backtrace.get());
+}
+
+bool ReadyMaxBacktrace(Backtrace* backtrace) {
+ return (backtrace->NumFrames() == MAX_BACKTRACE_FRAMES);
+}
+
+void VerifyMaxDump(Backtrace* backtrace) {
+ ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES));
+ // Verify that the last frame is our recursive call.
+ ASSERT_EQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name,
+ "test_recursive_call");
+}
+
+void VerifyMaxBacktrace(void*) {
+ UniquePtr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyMaxDump(backtrace.get());
+}
+
+void ThreadSetState(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+ android_atomic_acquire_store(1, &thread->state);
+ volatile int i = 0;
+ while (thread->state) {
+ i++;
+ }
+}
+
+void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(Backtrace*)) {
+ UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), tid));
+ ASSERT_TRUE(backtrace.get() != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyFunc(backtrace.get());
+}
+
+bool WaitForNonZero(int32_t* value, uint64_t seconds) {
+ uint64_t start = NanoTime();
+ do {
+ if (android_atomic_acquire_load(value)) {
+ return true;
+ }
+ } while ((NanoTime() - start) < seconds * NS_PER_SEC);
+ return false;
+}
+
+TEST(libbacktrace, local_trace) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, NULL), 0);
+}
+
+void VerifyIgnoreFrames(
+ Backtrace* bt_all, Backtrace* bt_ign1,
+ Backtrace* bt_ign2, const char* cur_proc) {
+ EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1);
+ EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2);
+
+ // Check all of the frames are the same > the current frame.
+ bool check = (cur_proc == NULL);
+ for (size_t i = 0; i < bt_ign2->NumFrames(); i++) {
+ if (check) {
+ EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_ign1->GetFrame(i+1)->pc);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->sp, bt_ign1->GetFrame(i+1)->sp);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->stack_size, bt_ign1->GetFrame(i+1)->stack_size);
+
+ EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_all->GetFrame(i+2)->pc);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->sp, bt_all->GetFrame(i+2)->sp);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->stack_size, bt_all->GetFrame(i+2)->stack_size);
+ }
+ if (!check && bt_ign2->GetFrame(i)->func_name == cur_proc) {
+ check = true;
+ }
+ }
+}
+
+void VerifyLevelIgnoreFrames(void*) {
+ UniquePtr<Backtrace> all(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(all.get() != NULL);
+ ASSERT_TRUE(all->Unwind(0));
+
+ UniquePtr<Backtrace> ign1(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign1.get() != NULL);
+ ASSERT_TRUE(ign1->Unwind(1));
+
+ UniquePtr<Backtrace> ign2(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign2.get() != NULL);
+ ASSERT_TRUE(ign2->Unwind(2));
+
+ VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
+}
+
+TEST(libbacktrace, local_trace_ignore_frames) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, NULL), 0);
+}
+
+TEST(libbacktrace, local_max_trace) {
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, NULL), 0);
+}
+
+void VerifyProcTest(pid_t pid, pid_t tid, bool share_map,
+ bool (*ReadyFunc)(Backtrace*),
+ void (*VerifyFunc)(Backtrace*)) {
+ pid_t ptrace_tid;
+ if (tid < 0) {
+ ptrace_tid = pid;
+ } else {
+ ptrace_tid = tid;
+ }
+ uint64_t start = NanoTime();
+ bool verified = false;
+ do {
+ usleep(US_PER_MSEC);
+ if (ptrace(PTRACE_ATTACH, ptrace_tid, 0, 0) == 0) {
+ // Wait for the process to get to a stopping point.
+ WaitForStop(ptrace_tid);
+
+ UniquePtr<BacktraceMap> map;
+ if (share_map) {
+ map.reset(BacktraceMap::Create(pid));
+ }
+ UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
+ ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_TRUE(backtrace.get() != NULL);
+ if (ReadyFunc(backtrace.get())) {
+ VerifyFunc(backtrace.get());
+ verified = true;
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, ptrace_tid, 0, 0) == 0);
+ }
+ // If 5 seconds have passed, then we are done.
+ } while (!verified && (NanoTime() - start) <= 5 * NS_PER_SEC);
+ ASSERT_TRUE(verified);
+}
+
+TEST(libbacktrace, ptrace_trace) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+TEST(libbacktrace, ptrace_trace_shared_map) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ exit(1);
+ }
+
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+TEST(libbacktrace, ptrace_max_trace) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0);
+ exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+void VerifyProcessIgnoreFrames(Backtrace* bt_all) {
+ UniquePtr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign1.get() != NULL);
+ ASSERT_TRUE(ign1->Unwind(1));
+
+ UniquePtr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign2.get() != NULL);
+ ASSERT_TRUE(ign2->Unwind(2));
+
+ VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), NULL);
+}
+
+TEST(libbacktrace, ptrace_ignore_frames) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+// Create a process with multiple threads and dump all of the threads.
+void* PtraceThreadLevelRun(void*) {
+ EXPECT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ return NULL;
+}
+
+void GetThreads(pid_t pid, std::vector<pid_t>* threads) {
+ // Get the list of tasks.
+ char task_path[128];
+ snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
+
+ DIR* tasks_dir = opendir(task_path);
+ ASSERT_TRUE(tasks_dir != NULL);
+ struct dirent* entry;
+ while ((entry = readdir(tasks_dir)) != NULL) {
+ char* end;
+ pid_t tid = strtoul(entry->d_name, &end, 10);
+ if (*end == '\0') {
+ threads->push_back(tid);
+ }
+ }
+ closedir(tasks_dir);
+}
+
+TEST(libbacktrace, ptrace_threads) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0);
+ }
+ ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ exit(1);
+ }
+
+ // Check to see that all of the threads are running before unwinding.
+ std::vector<pid_t> threads;
+ uint64_t start = NanoTime();
+ do {
+ usleep(US_PER_MSEC);
+ threads.clear();
+ GetThreads(pid, &threads);
+ } while ((threads.size() != NUM_PTRACE_THREADS + 1) &&
+ ((NanoTime() - start) <= 5 * NS_PER_SEC));
+ ASSERT_EQ(threads.size(), static_cast<size_t>(NUM_PTRACE_THREADS + 1));
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+ WaitForStop(pid);
+ for (std::vector<int>::const_iterator it = threads.begin(); it != threads.end(); ++it) {
+ // Skip the current forked process, we only care about the threads.
+ if (pid == *it) {
+ continue;
+ }
+ VerifyProcTest(pid, *it, false, ReadyLevelBacktrace, VerifyLevelDump);
+ }
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+void VerifyLevelThread(void*) {
+ UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+ ASSERT_TRUE(backtrace.get() != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyLevelDump(backtrace.get());
+}
+
+TEST(libbacktrace, thread_current_level) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, NULL), 0);
+}
+
+void VerifyMaxThread(void*) {
+ UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+ ASSERT_TRUE(backtrace.get() != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyMaxDump(backtrace.get());
+}
+
+TEST(libbacktrace, thread_current_max) {
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, NULL), 0);
+}
+
+void* ThreadLevelRun(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+
+ thread->tid = gettid();
+ EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
+ return NULL;
+}
+
+TEST(libbacktrace, thread_level_trace) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0 };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ // Save the current signal action and make sure it is restored afterwards.
+ struct sigaction cur_action;
+ ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0);
+
+ UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyLevelDump(backtrace.get());
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ // Verify that the old action was restored.
+ struct sigaction new_action;
+ ASSERT_TRUE(sigaction(SIGURG, NULL, &new_action) == 0);
+ EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction);
+ EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
+}
+
+TEST(libbacktrace, thread_ignore_frames) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0 };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ UniquePtr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(all.get() != NULL);
+ ASSERT_TRUE(all->Unwind(0));
+
+ UniquePtr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(ign1.get() != NULL);
+ ASSERT_TRUE(ign1->Unwind(1));
+
+ UniquePtr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(ign2.get() != NULL);
+ ASSERT_TRUE(ign2->Unwind(2));
+
+ VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), NULL);
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+}
+
+void* ThreadMaxRun(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+
+ thread->tid = gettid();
+ EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
+ return NULL;
+}
+
+TEST(libbacktrace, thread_max_trace) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0 };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadMaxRun, &thread_data) == 0);
+
+ // Wait for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != NULL);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ VerifyMaxDump(backtrace.get());
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+}
+
+void* ThreadDump(void* data) {
+ dump_thread_t* dump = reinterpret_cast<dump_thread_t*>(data);
+ while (true) {
+ if (android_atomic_acquire_load(dump->now)) {
+ break;
+ }
+ }
+
+ // The status of the actual unwind will be checked elsewhere.
+ dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid);
+ dump->backtrace->Unwind(0);
+
+ android_atomic_acquire_store(1, &dump->done);
+
+ return NULL;
+}
+
+TEST(libbacktrace, thread_multiple_dump) {
+ // Dump NUM_THREADS simultaneously.
+ std::vector<thread_t> runners(NUM_THREADS);
+ std::vector<dump_thread_t> dumpers(NUM_THREADS);
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ // Launch the runners, they will spin in hard loops doing nothing.
+ runners[i].tid = 0;
+ runners[i].state = 0;
+ ASSERT_TRUE(pthread_create(&runners[i].threadId, &attr, ThreadMaxRun, &runners[i]) == 0);
+ }
+
+ // Wait for tids to be set.
+ for (std::vector<thread_t>::iterator it = runners.begin(); it != runners.end(); ++it) {
+ ASSERT_TRUE(WaitForNonZero(&it->state, 10));
+ }
+
+ // Start all of the dumpers at once, they will spin until they are signalled
+ // to begin their dump run.
+ int32_t dump_now = 0;
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ dumpers[i].thread.tid = runners[i].tid;
+ dumpers[i].thread.state = 0;
+ dumpers[i].done = 0;
+ dumpers[i].now = &dump_now;
+
+ ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
+ }
+
+ // Start all of the dumpers going at once.
+ android_atomic_acquire_store(1, &dump_now);
+
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 10));
+
+ // Tell the runner thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &runners[i].state);
+
+ ASSERT_TRUE(dumpers[i].backtrace != NULL);
+ VerifyMaxDump(dumpers[i].backtrace);
+
+ delete dumpers[i].backtrace;
+ dumpers[i].backtrace = NULL;
+ }
+}
+
+// This test is for UnwindMaps that should share the same map cursor when
+// multiple maps are created for the current process at the same time.
+TEST(libbacktrace, simultaneous_maps) {
+ BacktraceMap* map1 = BacktraceMap::Create(getpid());
+ BacktraceMap* map2 = BacktraceMap::Create(getpid());
+ BacktraceMap* map3 = BacktraceMap::Create(getpid());
+
+ Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
+ EXPECT_TRUE(back1->Unwind(0));
+ delete back1;
+ delete map1;
+
+ Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
+ EXPECT_TRUE(back2->Unwind(0));
+ delete back2;
+ delete map2;
+
+ Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
+ EXPECT_TRUE(back3->Unwind(0));
+ delete back3;
+ delete map3;
+}
+
+TEST(libbacktrace, format_test) {
+ UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != NULL);
+
+ backtrace_frame_data_t frame;
+ frame.num = 1;
+ frame.pc = 2;
+ frame.sp = 0;
+ frame.stack_size = 0;
+ frame.map = NULL;
+ frame.func_offset = 0;
+
+ backtrace_map_t map;
+ map.start = 0;
+ map.end = 0;
+
+ // Check no map set.
+ frame.num = 1;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000000000002 <unknown>",
+#else
+ EXPECT_EQ("#01 pc 00000002 <unknown>",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check map name empty, but exists.
+ frame.map = &map;
+ map.start = 1;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000000000001 <unknown>",
+#else
+ EXPECT_EQ("#01 pc 00000001 <unknown>",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+
+ // Check relative pc is set and map name is set.
+ frame.pc = 0x12345679;
+ frame.map = &map;
+ map.name = "MapFake";
+ map.start = 1;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000012345678 MapFake",
+#else
+ EXPECT_EQ("#01 pc 12345678 MapFake",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check func_name is set, but no func offset.
+ frame.func_name = "ProcFake";
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000012345678 MapFake (ProcFake)",
+#else
+ EXPECT_EQ("#01 pc 12345678 MapFake (ProcFake)",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check func_name is set, and func offset is non-zero.
+ frame.func_offset = 645;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000012345678 MapFake (ProcFake+645)",
+#else
+ EXPECT_EQ("#01 pc 12345678 MapFake (ProcFake+645)",
+#endif
+ backtrace->FormatFrameData(&frame));
+}
diff --git a/libbacktrace/backtrace_testlib.c b/libbacktrace/backtrace_testlib.c
new file mode 100644
index 0000000..d4d15db
--- /dev/null
+++ b/libbacktrace/backtrace_testlib.c
@@ -0,0 +1,55 @@
+/*
+ * 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 <stdio.h>
+
+int test_level_four(int one, int two, int three, int four,
+ void (*callback_func)(void*), void* data) {
+ if (callback_func != NULL) {
+ callback_func(data);
+ } else {
+ while (1) {
+ }
+ }
+ return one + two + three + four;
+}
+
+int test_level_three(int one, int two, int three, int four,
+ void (*callback_func)(void*), void* data) {
+ return test_level_four(one+3, two+6, three+9, four+12, callback_func, data) + 3;
+}
+
+int test_level_two(int one, int two, int three, int four,
+ void (*callback_func)(void*), void* data) {
+ return test_level_three(one+2, two+4, three+6, four+8, callback_func, data) + 2;
+}
+
+int test_level_one(int one, int two, int three, int four,
+ void (*callback_func)(void*), void* data) {
+ return test_level_two(one+1, two+2, three+3, four+4, callback_func, data) + 1;
+}
+
+int test_recursive_call(int level, void (*callback_func)(void*), void* data) {
+ if (level > 0) {
+ return test_recursive_call(level - 1, callback_func, data) + level;
+ } else if (callback_func != NULL) {
+ callback_func(data);
+ } else {
+ while (1) {
+ }
+ }
+ return 0;
+}
diff --git a/libbacktrace/thread_utils.c b/libbacktrace/thread_utils.c
new file mode 100644
index 0000000..6f4cd3c
--- /dev/null
+++ b/libbacktrace/thread_utils.c
@@ -0,0 +1,42 @@
+/*
+ * 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 "thread_utils.h"
+
+#if defined(__APPLE__)
+
+#include <sys/syscall.h>
+
+// Mac OS >= 10.6 has a system call equivalent to Linux's gettid().
+pid_t gettid() {
+ return syscall(SYS_thread_selfid);
+}
+
+#elif !defined(__BIONIC__)
+
+// glibc doesn't implement or export either gettid or tgkill.
+#include <unistd.h>
+#include <sys/syscall.h>
+
+pid_t gettid() {
+ return syscall(__NR_gettid);
+}
+
+int tgkill(int tgid, int tid, int sig) {
+ return syscall(__NR_tgkill, tgid, tid, sig);
+}
+
+#endif
diff --git a/libbacktrace/thread_utils.h b/libbacktrace/thread_utils.h
new file mode 100644
index 0000000..ae4c929
--- /dev/null
+++ b/libbacktrace/thread_utils.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBBACKTRACE_THREAD_UTILS_H
+#define _LIBBACKTRACE_THREAD_UTILS_H
+
+#include <unistd.h>
+
+__BEGIN_DECLS
+
+int tgkill(int tgid, int tid, int sig);
+
+pid_t gettid();
+
+__END_DECLS
+
+#endif /* _LIBBACKTRACE_THREAD_UTILS_H */