summaryrefslogtreecommitdiffstats
path: root/libbacktrace
diff options
context:
space:
mode:
Diffstat (limited to 'libbacktrace')
-rw-r--r--libbacktrace/Android.build.mk27
-rw-r--r--[-rwxr-xr-x]libbacktrace/Android.mk98
-rw-r--r--libbacktrace/Backtrace.cpp138
-rw-r--r--libbacktrace/BacktraceCurrent.cpp155
-rw-r--r--libbacktrace/BacktraceCurrent.h58
-rw-r--r--libbacktrace/BacktraceImpl.cpp207
-rwxr-xr-xlibbacktrace/BacktraceImpl.h74
-rw-r--r--[-rwxr-xr-x]libbacktrace/BacktraceLog.h0
-rw-r--r--libbacktrace/BacktraceMap.cpp16
-rw-r--r--libbacktrace/BacktracePtrace.cpp116
-rw-r--r--libbacktrace/BacktracePtrace.h37
-rw-r--r--libbacktrace/BacktraceThread.cpp227
-rw-r--r--libbacktrace/GetPss.cpp18
-rw-r--r--libbacktrace/ThreadEntry.cpp127
-rw-r--r--libbacktrace/ThreadEntry.h (renamed from libbacktrace/BacktraceThread.h)27
-rw-r--r--[-rwxr-xr-x]libbacktrace/UnwindCurrent.cpp138
-rw-r--r--libbacktrace/UnwindCurrent.h25
-rw-r--r--libbacktrace/UnwindMap.cpp13
-rw-r--r--libbacktrace/UnwindMap.h5
-rw-r--r--libbacktrace/UnwindPtrace.cpp34
-rw-r--r--libbacktrace/UnwindPtrace.h18
-rw-r--r--libbacktrace/backtrace_test.cpp439
22 files changed, 1118 insertions, 879 deletions
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk
index 2f55645..35fed6d 100644
--- a/libbacktrace/Android.build.mk
+++ b/libbacktrace/Android.build.mk
@@ -19,28 +19,37 @@ include $(CLEAR_VARS)
LOCAL_MODULE := $(module)
LOCAL_MODULE_TAGS := $(module_tag)
LOCAL_MULTILIB := $($(module)_multilib)
+ifeq ($(LOCAL_MULTILIB),both)
+ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRRARY))
+ LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+ LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+endif
+endif
LOCAL_ADDITIONAL_DEPENDENCIES := \
$(LOCAL_PATH)/Android.mk \
$(LOCAL_PATH)/Android.build.mk \
LOCAL_CFLAGS := \
- $(common_cflags) \
+ $(libbacktrace_common_cflags) \
$($(module)_cflags) \
$($(module)_cflags_$(build_type)) \
+LOCAL_CLANG_CFLAGS += \
+ $(libbacktrace_common_clang_cflags) \
+
LOCAL_CONLYFLAGS += \
- $(common_conlyflags) \
+ $(libbacktrace_common_conlyflags) \
$($(module)_conlyflags) \
$($(module)_conlyflags_$(build_type)) \
LOCAL_CPPFLAGS += \
- $(common_cppflags) \
+ $(libbacktrace_common_cppflags) \
$($(module)_cppflags) \
$($(module)_cppflags_$(build_type)) \
LOCAL_C_INCLUDES := \
- $(common_c_includes) \
+ $(libbacktrace_common_c_includes) \
$($(module)_c_includes) \
$($(module)_c_includes_$(build_type)) \
@@ -61,21 +70,13 @@ LOCAL_LDLIBS := \
$($(module)_ldlibs_$(build_type)) \
ifeq ($(build_type),target)
- ifneq ($($(module)_libc++),)
- include external/libcxx/libcxx.mk
- else
- include external/stlport/libstlport.mk
- endif
-
include $(BUILD_$(build_target))
endif
ifeq ($(build_type),host)
# Only build if host builds are supported.
ifeq ($(build_host),true)
- ifneq ($($(module)_libc++),)
- include external/libcxx/libcxx.mk
- endif
+ LOCAL_CFLAGS += -Wno-extern-c-compat -fno-omit-frame-pointer
include $(BUILD_HOST_$(build_target))
endif
endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 9588dd6..54cace9 100755..100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -16,16 +16,20 @@
LOCAL_PATH:= $(call my-dir)
-common_cflags := \
+libbacktrace_common_cflags := \
-Wall \
-Werror \
-common_conlyflags := \
+libbacktrace_common_conlyflags := \
-std=gnu99 \
-common_cppflags := \
+libbacktrace_common_cppflags := \
-std=gnu++11 \
+# The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
+libbacktrace_common_clang_cflags += \
+ -Wno-inline-asm
+
build_host := false
ifeq ($(HOST_OS),linux)
ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64))
@@ -37,26 +41,22 @@ endif
# The libbacktrace library.
#-------------------------------------------------------------------------
libbacktrace_src_files := \
- BacktraceImpl.cpp \
+ Backtrace.cpp \
+ BacktraceCurrent.cpp \
BacktraceMap.cpp \
- BacktraceThread.cpp \
+ BacktracePtrace.cpp \
thread_utils.c \
-
-libbacktrace_shared_libraries_target := \
- libcutils \
- libgccdemangle \
-
-libbacktrace_src_files += \
+ ThreadEntry.cpp \
UnwindCurrent.cpp \
UnwindMap.cpp \
UnwindPtrace.cpp \
-libbacktrace_c_includes := \
- external/libunwind/include \
+libbacktrace_shared_libraries_target := \
+ libcutils \
libbacktrace_shared_libraries := \
+ libbase \
libunwind \
- libunwind-ptrace \
libbacktrace_shared_libraries_host := \
liblog \
@@ -74,58 +74,9 @@ build_type := target
build_target := SHARED_LIBRARY
include $(LOCAL_PATH)/Android.build.mk
build_type := host
+libbacktrace_multilib := both
include $(LOCAL_PATH)/Android.build.mk
-# Don't build for unbundled branches
-ifeq (,$(TARGET_BUILD_APPS))
-#-------------------------------------------------------------------------
-# The libbacktrace library (libc++)
-#-------------------------------------------------------------------------
-libbacktrace_libc++_src_files := \
- BacktraceImpl.cpp \
- BacktraceMap.cpp \
- BacktraceThread.cpp \
- thread_utils.c \
-
-libbacktrace_libc++_shared_libraries_target := \
- libcutils \
- libgccdemangle \
-
-libbacktrace_libc++_src_files += \
- UnwindCurrent.cpp \
- UnwindMap.cpp \
- UnwindPtrace.cpp \
-
-libbacktrace_libc++_c_includes := \
- external/libunwind/include \
-
-libbacktrace_libc++_shared_libraries := \
- libunwind \
- libunwind-ptrace \
-
-libbacktrace_libc++_shared_libraries_host := \
- liblog \
-
-libbacktrace_libc++_static_libraries_host := \
- libcutils \
-
-libbacktrace_libc++_ldlibs_host := \
- -lpthread \
- -lrt \
-
-libbacktrace_libc++_libc++ := true
-
-module := libbacktrace_libc++
-module_tag := optional
-build_type := target
-build_target := SHARED_LIBRARY
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-libbacktrace_libc++_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-libbacktrace_libc++_multilib :=
-endif
-
#-------------------------------------------------------------------------
# The libbacktrace_test library needed by backtrace_test.
#-------------------------------------------------------------------------
@@ -139,6 +90,7 @@ module := libbacktrace_test
module_tag := debug
build_type := target
build_target := SHARED_LIBRARY
+libbacktrace_test_multilib := both
include $(LOCAL_PATH)/Android.build.mk
build_type := host
include $(LOCAL_PATH)/Android.build.mk
@@ -177,6 +129,7 @@ module := backtrace_test
module_tag := debug
build_type := target
build_target := NATIVE_TEST
+backtrace_test_multilib := both
include $(LOCAL_PATH)/Android.build.mk
build_type := host
include $(LOCAL_PATH)/Android.build.mk
@@ -194,25 +147,8 @@ LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
BacktraceMap.cpp \
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-# Don't build for unbundled branches
-ifeq (,$(TARGET_BUILD_APPS))
-#-------------------------------------------------------------------------
-# The libbacktrace library (libc++)
-#-------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libbacktrace_libc++
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := \
- BacktraceMap.cpp \
-
LOCAL_MULTILIB := both
include $(BUILD_HOST_SHARED_LIBRARY)
-endif # TARGET_BUILD_APPS
-
endif # HOST_OS-darwin
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
new file mode 100644
index 0000000..91ca8b7
--- /dev/null
+++ b/libbacktrace/Backtrace.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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 <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <string>
+
+#include <base/stringprintf.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceLog.h"
+#include "thread_utils.h"
+#include "UnwindCurrent.h"
+#include "UnwindPtrace.h"
+
+using android::base::StringPrintf;
+
+//-------------------------------------------------------------------------
+// Backtrace functions.
+//-------------------------------------------------------------------------
+Backtrace::Backtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+ : pid_(pid), tid_(tid), map_(map), map_shared_(true) {
+ if (map_ == nullptr) {
+ map_ = BacktraceMap::Create(pid);
+ map_shared_ = false;
+ }
+}
+
+Backtrace::~Backtrace() {
+ if (map_ && !map_shared_) {
+ delete map_;
+ map_ = nullptr;
+ }
+}
+
+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 = 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, word_t* out_value) {
+ if (ptr & (sizeof(word_t)-1)) {
+ BACK_LOGW("invalid pointer %p", reinterpret_cast<void*>(ptr));
+ *out_value = static_cast<word_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 (BacktraceMap::IsValid(frame->map) && !frame->map.name.empty()) {
+ map_name = frame->map.name.c_str();
+ } else {
+ map_name = "<unknown>";
+ }
+
+ uintptr_t relative_pc;
+ if (BacktraceMap::IsValid(frame->map)) {
+ relative_pc = frame->pc - frame->map.start;
+ } else {
+ relative_pc = frame->pc;
+ }
+
+ std::string line(StringPrintf("#%02zu pc %" PRIPTR " %s", frame->num, relative_pc, map_name));
+ if (!frame->func_name.empty()) {
+ line += " (" + frame->func_name;
+ if (frame->func_offset) {
+ line += StringPrintf("+%" PRIuPTR, frame->func_offset);
+ }
+ line += ')';
+ }
+
+ return line;
+}
+
+void Backtrace::FillInMap(uintptr_t pc, backtrace_map_t* map) {
+ map_->FillIn(pc, map);
+}
+
+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();
+ }
+ } else if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = pid;
+ }
+
+ if (pid == getpid()) {
+ return new UnwindCurrent(pid, tid, map);
+ } else {
+ return new UnwindPtrace(pid, tid, map);
+ }
+}
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
new file mode 100644
index 0000000..fd1f4da
--- /dev/null
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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 _GNU_SOURCE 1
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceCurrent.h"
+#include "BacktraceLog.h"
+#include "ThreadEntry.h"
+#include "thread_utils.h"
+
+bool BacktraceCurrent::ReadWord(uintptr_t ptr, word_t* out_value) {
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+ backtrace_map_t map;
+ FillInMap(ptr, &map);
+ if (BacktraceMap::IsValid(map) && map.flags & PROT_READ) {
+ *out_value = *reinterpret_cast<word_t*>(ptr);
+ return true;
+ } else {
+ BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
+ *out_value = static_cast<word_t>(-1);
+ return false;
+ }
+}
+
+size_t BacktraceCurrent::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+ backtrace_map_t map;
+ FillInMap(addr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return 0;
+ }
+ bytes = MIN(map.end - addr, bytes);
+ memcpy(buffer, reinterpret_cast<uint8_t*>(addr), bytes);
+ return bytes;
+}
+
+bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+ if (ucontext) {
+ return UnwindFromContext(num_ignore_frames, ucontext);
+ }
+
+ if (Tid() != gettid()) {
+ return UnwindThread(num_ignore_frames);
+ }
+
+ return UnwindFromContext(num_ignore_frames, nullptr);
+}
+
+bool BacktraceCurrent::DiscardFrame(const backtrace_frame_data_t& frame) {
+ if (BacktraceMap::IsValid(frame.map)) {
+ const std::string library = basename(frame.map.name.c_str());
+ if (library == "libunwind.so" || library == "libbacktrace.so") {
+ return true;
+ }
+ }
+ return false;
+}
+
+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;
+ }
+
+ entry->CopyUcontextFromSigcontext(sigcontext);
+
+ // Indicate the ucontext is now valid.
+ entry->Wake();
+
+ // Pause the thread until the unwind is complete. This avoids having
+ // the thread run ahead causing problems.
+ // The number indicates that we are waiting for the second Wake() call
+ // overall which is made by the thread requesting an unwind.
+ entry->Wait(2);
+
+ ThreadEntry::Remove(entry);
+}
+
+bool BacktraceCurrent::UnwindThread(size_t num_ignore_frames) {
+ // Prevent multiple threads trying to set the trigger action on different
+ // threads at the same time.
+ pthread_mutex_lock(&g_sigaction_mutex);
+
+ ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
+ entry->Lock();
+
+ 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(THREAD_SIGNAL, &act, &oldact) != 0) {
+ BACK_LOGW("sigaction failed %s", strerror(errno));
+ entry->Unlock();
+ ThreadEntry::Remove(entry);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+ return false;
+ }
+
+ if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
+ BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno));
+ sigaction(THREAD_SIGNAL, &oldact, nullptr);
+ entry->Unlock();
+ ThreadEntry::Remove(entry);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+ return false;
+ }
+
+ // Wait for the thread to get the ucontext. The number indicates
+ // that we are waiting for the first Wake() call made by the thread.
+ entry->Wait(1);
+
+ // After the thread has received the signal, allow other unwinders to
+ // continue.
+ sigaction(THREAD_SIGNAL, &oldact, nullptr);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+
+ bool unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext());
+
+ // Tell the signal handler to exit and release the entry.
+ entry->Wake();
+
+ return unwind_done;
+}
diff --git a/libbacktrace/BacktraceCurrent.h b/libbacktrace/BacktraceCurrent.h
new file mode 100644
index 0000000..8aad36d
--- /dev/null
+++ b/libbacktrace/BacktraceCurrent.h
@@ -0,0 +1,58 @@
+/*
+ * 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_CURRENT_H
+#define _LIBBACKTRACE_BACKTRACE_CURRENT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <backtrace/Backtrace.h>
+
+// The signal used to cause a thread to dump the stack.
+#if defined(__GLIBC__)
+// In order to run the backtrace_tests on the host, we can't use
+// the internal real time signals used by GLIBC. To avoid this,
+// use SIGRTMIN for the signal to dump the stack.
+#define THREAD_SIGNAL SIGRTMIN
+#else
+#define THREAD_SIGNAL (__SIGRTMIN+1)
+#endif
+
+class BacktraceMap;
+
+class BacktraceCurrent : public Backtrace {
+public:
+ BacktraceCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
+ virtual ~BacktraceCurrent() {}
+
+ size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) override;
+
+ bool ReadWord(uintptr_t ptr, word_t* out_value) override;
+
+ bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
+
+protected:
+ bool DiscardFrame(const backtrace_frame_data_t& frame);
+
+private:
+ bool UnwindThread(size_t num_ignore_frames);
+
+ virtual bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) = 0;
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_CURRENT_H
diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp
deleted file mode 100644
index 405b042..0000000
--- a/libbacktrace/BacktraceImpl.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * 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 <ucontext.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
-#include "BacktraceImpl.h"
-#include "BacktraceLog.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, ucontext_t* ucontext) {
- return impl_->Unwind(num_ignore_frames, ucontext);
-}
-
-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, word_t* out_value) {
- if (ptr & (sizeof(word_t)-1)) {
- BACK_LOGW("invalid pointer %p", (void*)ptr);
- *out_value = (word_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, word_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<word_t*>(ptr);
- return true;
- } else {
- BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
- *out_value = static_cast<word_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, word_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<word_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
deleted file mode 100755
index d34ad05..0000000
--- a/libbacktrace/BacktraceImpl.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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>
-
-class BacktraceImpl {
-public:
- virtual ~BacktraceImpl() { }
-
- virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) = 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, word_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, word_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/BacktraceLog.h b/libbacktrace/BacktraceLog.h
index 1632ec2..1632ec2 100755..100644
--- a/libbacktrace/BacktraceLog.h
+++ b/libbacktrace/BacktraceLog.h
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index f38e484..b0ada46 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -15,18 +15,15 @@
*/
#include <ctype.h>
+#include <stdint.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) {
@@ -37,14 +34,15 @@ BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
BacktraceMap::~BacktraceMap() {
}
-const backtrace_map_t* BacktraceMap::Find(uintptr_t addr) {
+void BacktraceMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
for (BacktraceMap::const_iterator it = begin();
it != end(); ++it) {
if (addr >= it->start && addr < it->end) {
- return &*it;
+ *map = *it;
+ return;
}
}
- return NULL;
+ *map = {};
}
bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
@@ -115,7 +113,7 @@ bool BacktraceMap::Build() {
snprintf(path, sizeof(path), "/proc/%d/maps", pid_);
FILE* fp = fopen(path, "r");
#endif
- if (fp == NULL) {
+ if (fp == nullptr) {
return false;
}
@@ -141,7 +139,7 @@ BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
BacktraceMap* map = new BacktraceMap(pid);
if (!map->Build()) {
delete map;
- return NULL;
+ return nullptr;
}
return map;
}
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
new file mode 100644
index 0000000..6134438
--- /dev/null
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceLog.h"
+#include "BacktracePtrace.h"
+#include "thread_utils.h"
+
+#if !defined(__APPLE__)
+static bool PtraceRead(pid_t tid, uintptr_t addr, word_t* out_value) {
+ // 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*>(addr), nullptr);
+ if (*out_value == static_cast<word_t>(-1) && errno) {
+ BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s",
+ reinterpret_cast<void*>(addr), tid, strerror(errno));
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) {
+#if defined(__APPLE__)
+ BACK_LOGW("MacOS does not support reading from another pid.");
+ return false;
+#else
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+ backtrace_map_t map;
+ FillInMap(ptr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return false;
+ }
+
+ return PtraceRead(Tid(), ptr, out_value);
+#endif
+}
+
+size_t BacktracePtrace::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+#if defined(__APPLE__)
+ BACK_LOGW("MacOS does not support reading from another pid.");
+ return 0;
+#else
+ backtrace_map_t map;
+ FillInMap(addr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return 0;
+ }
+
+ bytes = MIN(map.end - addr, bytes);
+ size_t bytes_read = 0;
+ word_t data_word;
+ size_t align_bytes = addr & (sizeof(word_t) - 1);
+ if (align_bytes != 0) {
+ if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) {
+ return 0;
+ }
+ align_bytes = sizeof(word_t) - align_bytes;
+ memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + sizeof(word_t) - align_bytes,
+ align_bytes);
+ addr += align_bytes;
+ buffer += align_bytes;
+ bytes -= align_bytes;
+ bytes_read += align_bytes;
+ }
+
+ size_t num_words = bytes / sizeof(word_t);
+ for (size_t i = 0; i < num_words; i++) {
+ if (!PtraceRead(Tid(), addr, &data_word)) {
+ return bytes_read;
+ }
+ memcpy(buffer, &data_word, sizeof(word_t));
+ buffer += sizeof(word_t);
+ addr += sizeof(word_t);
+ bytes_read += sizeof(word_t);
+ }
+
+ size_t left_over = bytes & (sizeof(word_t) - 1);
+ if (left_over) {
+ if (!PtraceRead(Tid(), addr, &data_word)) {
+ return bytes_read;
+ }
+ memcpy(buffer, &data_word, left_over);
+ bytes_read += left_over;
+ }
+ return bytes_read;
+#endif
+}
diff --git a/libbacktrace/BacktracePtrace.h b/libbacktrace/BacktracePtrace.h
new file mode 100644
index 0000000..1d49811
--- /dev/null
+++ b/libbacktrace/BacktracePtrace.h
@@ -0,0 +1,37 @@
+/*
+ * 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_PTRACE_H
+#define _LIBBACKTRACE_BACKTRACE_PTRACE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <backtrace/Backtrace.h>
+
+class BacktraceMap;
+
+class BacktracePtrace : public Backtrace {
+public:
+ BacktracePtrace(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
+ virtual ~BacktracePtrace() {}
+
+ size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes);
+
+ bool ReadWord(uintptr_t ptr, word_t* out_value);
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_PTRACE_H
diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp
deleted file mode 100644
index 439cc3b..0000000
--- a/libbacktrace/BacktraceThread.cpp
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 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 <limits.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <ucontext.h>
-#include <unistd.h>
-
-#include <cutils/atomic.h>
-
-#include "BacktraceLog.h"
-#include "BacktraceThread.h"
-#include "thread_utils.h"
-
-//-------------------------------------------------------------------------
-// ThreadEntry implementation.
-//-------------------------------------------------------------------------
-ThreadEntry* ThreadEntry::list_ = NULL;
-pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
-
-// Assumes that ThreadEntry::list_mutex_ has already been locked before
-// creating a ThreadEntry object.
-ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
- : pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER),
- wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0),
- next_(ThreadEntry::list_), prev_(NULL) {
- pthread_condattr_t attr;
- pthread_condattr_init(&attr);
- pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
- pthread_cond_init(&wait_cond_, &attr);
-
- // Add ourselves to the list.
- if (ThreadEntry::list_) {
- ThreadEntry::list_->prev_ = this;
- }
- ThreadEntry::list_ = this;
-}
-
-ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
- pthread_mutex_lock(&ThreadEntry::list_mutex_);
- ThreadEntry* entry = list_;
- while (entry != NULL) {
- if (entry->Match(pid, tid)) {
- break;
- }
- entry = entry->next_;
- }
-
- if (!entry) {
- if (create) {
- entry = new ThreadEntry(pid, tid);
- }
- } else {
- entry->ref_count_++;
- }
- pthread_mutex_unlock(&ThreadEntry::list_mutex_);
-
- return entry;
-}
-
-void ThreadEntry::Remove(ThreadEntry* entry) {
- pthread_mutex_unlock(&entry->mutex_);
-
- pthread_mutex_lock(&ThreadEntry::list_mutex_);
- if (--entry->ref_count_ == 0) {
- delete entry;
- }
- pthread_mutex_unlock(&ThreadEntry::list_mutex_);
-}
-
-// Assumes that ThreadEntry::list_mutex_ has already been locked before
-// deleting a ThreadEntry object.
-ThreadEntry::~ThreadEntry() {
- if (list_ == this) {
- list_ = next_;
- } else {
- if (next_) {
- next_->prev_ = prev_;
- }
- prev_->next_ = next_;
- }
-
- next_ = NULL;
- prev_ = NULL;
-
- pthread_cond_destroy(&wait_cond_);
-}
-
-void ThreadEntry::Wait(int value) {
- timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
- BACK_LOGW("clock_gettime failed: %s", strerror(errno));
- abort();
- }
- ts.tv_sec += 10;
-
- pthread_mutex_lock(&wait_mutex_);
- while (wait_value_ != value) {
- int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
- if (ret != 0) {
- BACK_LOGW("pthread_cond_timedwait failed: %s", strerror(ret));
- break;
- }
- }
- pthread_mutex_unlock(&wait_mutex_);
-}
-
-void ThreadEntry::Wake() {
- pthread_mutex_lock(&wait_mutex_);
- wait_value_++;
- pthread_mutex_unlock(&wait_mutex_);
-
- pthread_cond_signal(&wait_cond_);
-}
-
-void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
- ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext);
- // The only thing the unwinder cares about is the mcontext data.
- memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext));
-}
-
-//-------------------------------------------------------------------------
-// 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;
- }
-
- entry->CopyUcontextFromSigcontext(sigcontext);
-
- // Indicate the ucontext is now valid.
- entry->Wake();
-
- // Pause the thread until the unwind is complete. This avoids having
- // the thread run ahead causing problems.
- entry->Wait(2);
-
- ThreadEntry::Remove(entry);
-}
-
-BacktraceThread::BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map)
- : BacktraceCurrent(impl, map) {
- tid_ = tid;
-}
-
-BacktraceThread::~BacktraceThread() {
-}
-
-bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
- if (ucontext) {
- // Unwind using an already existing ucontext.
- return impl_->Unwind(num_ignore_frames, ucontext);
- }
-
- // Prevent multiple threads trying to set the trigger action on different
- // threads at the same time.
- if (pthread_mutex_lock(&g_sigaction_mutex) < 0) {
- BACK_LOGW("sigaction failed: %s", strerror(errno));
- return false;
- }
-
- ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
- entry->Lock();
-
- 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(THREAD_SIGNAL, &act, &oldact) != 0) {
- BACK_LOGW("sigaction failed %s", strerror(errno));
- entry->Unlock();
- ThreadEntry::Remove(entry);
- pthread_mutex_unlock(&g_sigaction_mutex);
- return false;
- }
-
- if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
- 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;
- }
-
- // Wait for the thread to get the ucontext.
- entry->Wait(1);
-
- // 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;
-}
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
index 442383b..09a721d 100644
--- a/libbacktrace/GetPss.cpp
+++ b/libbacktrace/GetPss.cpp
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#include <assert.h>
#include <inttypes.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
@@ -46,13 +45,22 @@ static bool ReadData(int fd, unsigned long place, uint64_t *data) {
size_t GetPssBytes() {
FILE* maps = fopen("/proc/self/maps", "r");
- assert(maps != NULL);
+ if (maps == nullptr) {
+ return 0;
+ }
int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
- assert(pagecount_fd >= 0);
+ if (pagecount_fd == -1) {
+ fclose(maps);
+ return 0;
+ }
int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
- assert(pagemap_fd >= 0);
+ if (pagemap_fd == -1) {
+ fclose(maps);
+ close(pagecount_fd);
+ return 0;
+ }
char line[4096];
size_t total_pss = 0;
diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp
new file mode 100644
index 0000000..e8b60c8
--- /dev/null
+++ b/libbacktrace/ThreadEntry.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 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 <pthread.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <ucontext.h>
+
+#include "BacktraceLog.h"
+#include "ThreadEntry.h"
+
+// Initialize static member variables.
+ThreadEntry* ThreadEntry::list_ = nullptr;
+pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
+
+// Assumes that ThreadEntry::list_mutex_ has already been locked before
+// creating a ThreadEntry object.
+ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
+ : pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER),
+ wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0),
+ next_(ThreadEntry::list_), prev_(nullptr) {
+ pthread_condattr_t attr;
+ pthread_condattr_init(&attr);
+ pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+ pthread_cond_init(&wait_cond_, &attr);
+
+ // Add ourselves to the list.
+ if (ThreadEntry::list_) {
+ ThreadEntry::list_->prev_ = this;
+ }
+ ThreadEntry::list_ = this;
+}
+
+ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
+ pthread_mutex_lock(&ThreadEntry::list_mutex_);
+ ThreadEntry* entry = list_;
+ while (entry != nullptr) {
+ if (entry->Match(pid, tid)) {
+ break;
+ }
+ entry = entry->next_;
+ }
+
+ if (!entry) {
+ if (create) {
+ entry = new ThreadEntry(pid, tid);
+ }
+ } else {
+ entry->ref_count_++;
+ }
+ pthread_mutex_unlock(&ThreadEntry::list_mutex_);
+
+ return entry;
+}
+
+void ThreadEntry::Remove(ThreadEntry* entry) {
+ pthread_mutex_unlock(&entry->mutex_);
+
+ pthread_mutex_lock(&ThreadEntry::list_mutex_);
+ if (--entry->ref_count_ == 0) {
+ delete entry;
+ }
+ pthread_mutex_unlock(&ThreadEntry::list_mutex_);
+}
+
+// Assumes that ThreadEntry::list_mutex_ has already been locked before
+// deleting a ThreadEntry object.
+ThreadEntry::~ThreadEntry() {
+ if (list_ == this) {
+ list_ = next_;
+ } else {
+ if (next_) {
+ next_->prev_ = prev_;
+ }
+ prev_->next_ = next_;
+ }
+
+ next_ = nullptr;
+ prev_ = nullptr;
+
+ pthread_cond_destroy(&wait_cond_);
+}
+
+void ThreadEntry::Wait(int value) {
+ timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ ts.tv_sec += 10;
+
+ pthread_mutex_lock(&wait_mutex_);
+ while (wait_value_ != value) {
+ int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
+ if (ret != 0) {
+ BACK_LOGW("pthread_cond_timedwait failed: %s", strerror(ret));
+ break;
+ }
+ }
+ pthread_mutex_unlock(&wait_mutex_);
+}
+
+void ThreadEntry::Wake() {
+ pthread_mutex_lock(&wait_mutex_);
+ wait_value_++;
+ pthread_mutex_unlock(&wait_mutex_);
+
+ pthread_cond_signal(&wait_cond_);
+}
+
+void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
+ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext);
+ // The only thing the unwinder cares about is the mcontext data.
+ memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext));
+}
diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/ThreadEntry.h
index 99a8638..94becf2 100644
--- a/libbacktrace/BacktraceThread.h
+++ b/libbacktrace/ThreadEntry.h
@@ -14,26 +14,13 @@
* limitations under the License.
*/
-#ifndef _LIBBACKTRACE_BACKTRACE_THREAD_H
-#define _LIBBACKTRACE_BACKTRACE_THREAD_H
+#ifndef _LIBBACKTRACE_THREAD_ENTRY_H
+#define _LIBBACKTRACE_THREAD_ENTRY_H
-#include <inttypes.h>
#include <pthread.h>
-#include <signal.h>
-#include <string.h>
#include <sys/types.h>
#include <ucontext.h>
-#include "BacktraceImpl.h"
-
-// The signal used to cause a thread to dump the stack.
-#if defined(__GLIBC__)
-// GLIBC reserves __SIGRTMIN signals, so use SIGRTMIN to avoid errors.
-#define THREAD_SIGNAL SIGRTMIN
-#else
-#define THREAD_SIGNAL (__SIGRTMIN+1)
-#endif
-
class ThreadEntry {
public:
static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true);
@@ -81,12 +68,4 @@ private:
static pthread_mutex_t list_mutex_;
};
-class BacktraceThread : public BacktraceCurrent {
-public:
- BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map);
- virtual ~BacktraceThread();
-
- virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext);
-};
-
-#endif // _LIBBACKTRACE_BACKTRACE_THREAD_H
+#endif // _LIBBACKTRACE_THREAD_ENTRY_H
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index b176aaf..67e583f 100755..100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -14,41 +14,30 @@
* limitations under the License.
*/
-#include <sys/types.h>
+#include <stdint.h>
#include <ucontext.h>
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
+#include <memory>
+#include <string>
#define UNW_LOCAL_ONLY
#include <libunwind.h>
+#include <backtrace/Backtrace.h>
+
#include "BacktraceLog.h"
-#include "BacktraceThread.h"
#include "UnwindCurrent.h"
-#include "UnwindMap.h"
-
-//-------------------------------------------------------------------------
-// UnwindCurrent functions.
-//-------------------------------------------------------------------------
-UnwindCurrent::UnwindCurrent() {
-}
-
-UnwindCurrent::~UnwindCurrent() {
-}
-bool UnwindCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
- if (!ucontext) {
- int ret = unw_getcontext(&context_);
- if (ret < 0) {
- BACK_LOGW("unw_getcontext failed %d", ret);
- return false;
- }
- }
- else {
- GetUnwContextFromUcontext(ucontext);
+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 UnwindFromContext(num_ignore_frames, false);
+ return "";
}
void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) {
@@ -76,90 +65,67 @@ void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) {
#endif
}
-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;
+bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
+ if (ucontext == nullptr) {
+ int ret = unw_getcontext(&context_);
+ if (ret < 0) {
+ BACK_LOGW("unw_getcontext failed %d", ret);
+ return false;
+ }
+ } else {
+ GetUnwContextFromUcontext(ucontext);
}
- return "";
-}
-bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool within_handler) {
// 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_);
+ std::unique_ptr<unw_cursor_t> cursor(new unw_cursor_t);
+ int ret = unw_init_local(cursor.get(), &context_);
if (ret < 0) {
- if (!within_handler) {
- BACK_LOGW("unw_init_local failed %d", ret);
- }
- delete cursor;
+ BACK_LOGW("unw_init_local 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);
+ ret = unw_get_reg(cursor.get(), UNW_REG_IP, &pc);
if (ret < 0) {
- if (!within_handler) {
- BACK_LOGW("Failed to read IP %d", ret);
- }
+ BACK_LOGW("Failed to read IP %d", ret);
break;
}
unw_word_t sp;
- ret = unw_get_reg(cursor, UNW_REG_SP, &sp);
+ ret = unw_get_reg(cursor.get(), UNW_REG_SP, &sp);
if (ret < 0) {
- if (!within_handler) {
- BACK_LOGW("Failed to read SP %d", ret);
- }
+ 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 (!within_handler) {
+ 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;
+
+ FillInMap(frame->pc, &frame->map);
+ // Check to see if we should skip this frame because it's coming
+ // from within the library, and we are doing a local unwind.
+ if (ucontext != nullptr || num_frames != 0 || !DiscardFrame(*frame)) {
+ if (num_ignore_frames == 0) {
+ // GetFunctionName is an expensive call, only do it if we are
+ // keeping the frame.
frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
- frame->map = FindMap(frame->pc);
+ 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;
+ }
+ num_frames++;
} else {
- frame->map = NULL;
- frame->func_offset = 0;
+ num_ignore_frames--;
}
- num_frames++;
- } else {
- num_ignore_frames--;
}
- ret = unw_step (cursor);
+ ret = unw_step (cursor.get());
} while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
- delete cursor;
return true;
}
-
-//-------------------------------------------------------------------------
-// C++ object creation function.
-//-------------------------------------------------------------------------
-Backtrace* CreateCurrentObj(BacktraceMap* map) {
- return new BacktraceCurrent(new UnwindCurrent(), map);
-}
-
-Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) {
- return new BacktraceThread(new UnwindCurrent(), tid, map);
-}
diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h
index 2375e6e..3023996 100644
--- a/libbacktrace/UnwindCurrent.h
+++ b/libbacktrace/UnwindCurrent.h
@@ -17,27 +17,32 @@
#ifndef _LIBBACKTRACE_UNWIND_CURRENT_H
#define _LIBBACKTRACE_UNWIND_CURRENT_H
+#include <stdint.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
#include <string>
-#include "BacktraceImpl.h"
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceCurrent.h"
#define UNW_LOCAL_ONLY
#include <libunwind.h>
-class UnwindCurrent : public BacktraceImpl {
+class UnwindCurrent : public BacktraceCurrent {
public:
- UnwindCurrent();
- virtual ~UnwindCurrent();
-
- virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext);
+ UnwindCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : BacktraceCurrent(pid, tid, map) {}
+ virtual ~UnwindCurrent() {}
- virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
- bool UnwindFromContext(size_t num_ignore_frames, bool within_handler);
+private:
+ void GetUnwContextFromUcontext(const ucontext_t* ucontext);
- void GetUnwContextFromUcontext(const ucontext_t* context);
+ bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
-protected:
unw_context_t context_;
};
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index 387d768..fa59d07 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <pthread.h>
+#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
@@ -113,18 +113,17 @@ bool UnwindMapLocal::Build() {
return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();;
}
-const backtrace_map_t* UnwindMapLocal::Find(uintptr_t addr) {
- const backtrace_map_t* map = BacktraceMap::Find(addr);
- if (!map) {
+void UnwindMapLocal::FillIn(uintptr_t addr, backtrace_map_t* map) {
+ BacktraceMap::FillIn(addr, map);
+ if (!IsValid(*map)) {
// Check to see if the underlying map changed and regenerate the map
// if it did.
if (unw_map_local_cursor_valid(&map_cursor_) < 0) {
if (GenerateMap()) {
- map = BacktraceMap::Find(addr);
+ BacktraceMap::FillIn(addr, map);
}
}
}
- return map;
}
//-------------------------------------------------------------------------
@@ -143,7 +142,7 @@ BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
}
if (!map->Build()) {
delete map;
- return NULL;
+ return nullptr;
}
return map;
}
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
index 2fdb29f..e292016 100644
--- a/libbacktrace/UnwindMap.h
+++ b/libbacktrace/UnwindMap.h
@@ -17,6 +17,9 @@
#ifndef _LIBBACKTRACE_UNWIND_MAP_H
#define _LIBBACKTRACE_UNWIND_MAP_H
+#include <stdint.h>
+#include <sys/types.h>
+
#include <backtrace/BacktraceMap.h>
// The unw_map_cursor_t structure is different depending on whether it is
@@ -45,7 +48,7 @@ public:
virtual bool Build();
- virtual const backtrace_map_t* Find(uintptr_t addr);
+ virtual void FillIn(uintptr_t addr, backtrace_map_t* map);
protected:
virtual bool GenerateMap();
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 7ba8775..a7c3de5 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -14,35 +14,36 @@
* limitations under the License.
*/
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
+#include <stdint.h>
#include <sys/types.h>
-#include <string.h>
#include <ucontext.h>
#include <libunwind.h>
#include <libunwind-ptrace.h>
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
#include "BacktraceLog.h"
#include "UnwindMap.h"
#include "UnwindPtrace.h"
-UnwindPtrace::UnwindPtrace() : addr_space_(NULL), upt_info_(NULL) {
+UnwindPtrace::UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+ : BacktracePtrace(pid, tid, map), addr_space_(nullptr), upt_info_(nullptr) {
}
UnwindPtrace::~UnwindPtrace() {
if (upt_info_) {
_UPT_destroy(upt_info_);
- upt_info_ = NULL;
+ upt_info_ = nullptr;
}
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_map_set(addr_space_, nullptr);
unw_destroy_addr_space(addr_space_);
- addr_space_ = NULL;
+ addr_space_ = nullptr;
}
}
@@ -74,8 +75,6 @@ bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
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;
@@ -92,21 +91,21 @@ bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
}
if (num_ignore_frames == 0) {
- frames->resize(num_frames+1);
- backtrace_frame_data_t* frame = &frames->at(num_frames);
+ 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);
+ 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);
+ FillInMap(frame->pc, &frame->map);
num_frames++;
} else {
@@ -129,10 +128,3 @@ std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
}
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
index 2fb7967..ab04abf 100644
--- a/libbacktrace/UnwindPtrace.h
+++ b/libbacktrace/UnwindPtrace.h
@@ -17,20 +17,26 @@
#ifndef _LIBBACKTRACE_UNWIND_PTRACE_H
#define _LIBBACKTRACE_UNWIND_PTRACE_H
-#include <string>
+#include <stdint.h>
+#include <sys/types.h>
-#include "BacktraceImpl.h"
+#include <string>
+#ifdef UNW_LOCAL_ONLY
+#undef UNW_LOCAL_ONLY
+#endif
#include <libunwind.h>
-class UnwindPtrace : public BacktraceImpl {
+#include "BacktracePtrace.h"
+
+class UnwindPtrace : public BacktracePtrace {
public:
- UnwindPtrace();
+ UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
virtual ~UnwindPtrace();
- virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext);
+ bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
- virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+ std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
private:
unw_addr_space_t addr_space_;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 8002ed6..4af6592 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#define _GNU_SOURCE 1
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
@@ -31,15 +32,16 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#include <UniquePtr.h>
// For the THREAD_SIGNAL definition.
-#include "BacktraceThread.h"
+#include "BacktraceCurrent.h"
#include <cutils/atomic.h>
#include <gtest/gtest.h>
#include <algorithm>
+#include <memory>
+#include <string>
#include <vector>
#include "thread_utils.h"
@@ -60,6 +62,7 @@ struct thread_t {
pid_t tid;
int32_t state;
pthread_t threadId;
+ void* data;
};
struct dump_thread_t {
@@ -82,15 +85,16 @@ uint64_t NanoTime() {
return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
}
-void DumpFrames(Backtrace* backtrace) {
+std::string DumpFrames(Backtrace* backtrace) {
if (backtrace->NumFrames() == 0) {
- printf(" No frames to dump\n");
- return;
+ return " No frames to dump.\n";
}
+ std::string frame;
for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- printf(" %s\n", backtrace->FormatFrameData(i).c_str());
+ frame += " " + backtrace->FormatFrameData(i) + '\n';
}
+ return frame;
}
void WaitForStop(pid_t pid) {
@@ -120,8 +124,10 @@ bool ReadyLevelBacktrace(Backtrace* backtrace) {
}
void VerifyLevelDump(Backtrace* backtrace) {
- ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0));
- ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES));
+ ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0))
+ << DumpFrames(backtrace);
+ ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
+ << DumpFrames(backtrace);
// Look through the frames starting at the highest to find the
// frame we want.
@@ -132,19 +138,23 @@ void VerifyLevelDump(Backtrace* backtrace) {
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");
+ ASSERT_LT(static_cast<size_t>(0), frame_num) << DumpFrames(backtrace);
+ ASSERT_LE(static_cast<size_t>(3), frame_num) << DumpFrames(backtrace);
+
+ ASSERT_EQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-2)->func_name, "test_level_three")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-3)->func_name, "test_level_four")
+ << DumpFrames(backtrace);
}
void VerifyLevelBacktrace(void*) {
- UniquePtr<Backtrace> backtrace(
+ std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
- ASSERT_TRUE(backtrace.get() != NULL);
+ ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
VerifyLevelDump(backtrace.get());
@@ -155,16 +165,17 @@ bool ReadyMaxBacktrace(Backtrace* backtrace) {
}
void VerifyMaxDump(Backtrace* backtrace) {
- ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES));
+ ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
+ << DumpFrames(backtrace);
// Verify that the last frame is our recursive call.
- ASSERT_EQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name,
- "test_recursive_call");
+ ASSERT_EQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name, "test_recursive_call")
+ << DumpFrames(backtrace);
}
void VerifyMaxBacktrace(void*) {
- UniquePtr<Backtrace> backtrace(
+ std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
- ASSERT_TRUE(backtrace.get() != NULL);
+ ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
VerifyMaxDump(backtrace.get());
@@ -180,8 +191,8 @@ void ThreadSetState(void* data) {
}
void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(Backtrace*)) {
- UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), tid));
- ASSERT_TRUE(backtrace.get() != NULL);
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
VerifyFunc(backtrace.get());
@@ -197,18 +208,38 @@ bool WaitForNonZero(int32_t* value, uint64_t seconds) {
return false;
}
+TEST(libbacktrace, local_no_unwind_frames) {
+ // Verify that a local unwind does not include any frames within
+ // libunwind or libbacktrace.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+
+ ASSERT_TRUE(backtrace->NumFrames() != 0);
+ for (const auto& frame : *backtrace ) {
+ if (BacktraceMap::IsValid(frame.map)) {
+ const std::string name = basename(frame.map.name.c_str());
+ ASSERT_TRUE(name != "libunwind.so" && name != "libbacktrace.so")
+ << DumpFrames(backtrace.get());
+ }
+ break;
+ }
+}
+
TEST(libbacktrace, local_trace) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, NULL), 0);
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 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);
+ EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1)
+ << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 1 backtrace:\n" << DumpFrames(bt_ign1);
+ EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2)
+ << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 2 backtrace:\n" << DumpFrames(bt_ign2);
// Check all of the frames are the same > the current frame.
- bool check = (cur_proc == NULL);
+ bool check = (cur_proc == nullptr);
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);
@@ -226,30 +257,30 @@ void VerifyIgnoreFrames(
}
void VerifyLevelIgnoreFrames(void*) {
- UniquePtr<Backtrace> all(
+ std::unique_ptr<Backtrace> all(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
- ASSERT_TRUE(all.get() != NULL);
+ ASSERT_TRUE(all.get() != nullptr);
ASSERT_TRUE(all->Unwind(0));
- UniquePtr<Backtrace> ign1(
+ std::unique_ptr<Backtrace> ign1(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
- ASSERT_TRUE(ign1.get() != NULL);
+ ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
- UniquePtr<Backtrace> ign2(
+ std::unique_ptr<Backtrace> ign2(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
- ASSERT_TRUE(ign2.get() != NULL);
+ ASSERT_TRUE(ign2.get() != nullptr);
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);
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
}
TEST(libbacktrace, local_max_trace) {
- ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, NULL), 0);
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
}
void VerifyProcTest(pid_t pid, pid_t tid, bool share_map,
@@ -263,35 +294,38 @@ void VerifyProcTest(pid_t pid, pid_t tid, bool share_map,
}
uint64_t start = NanoTime();
bool verified = false;
+ std::string last_dump;
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;
+ std::unique_ptr<BacktraceMap> map;
if (share_map) {
map.reset(BacktraceMap::Create(pid));
}
- UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
- ASSERT_TRUE(backtrace.get() != NULL);
if (ReadyFunc(backtrace.get())) {
VerifyFunc(backtrace.get());
verified = true;
+ } else {
+ last_dump = DumpFrames(backtrace.get());
}
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);
+ ASSERT_TRUE(verified) << "Last backtrace:\n" << last_dump;
}
TEST(libbacktrace, ptrace_trace) {
pid_t pid;
if ((pid = fork()) == 0) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
@@ -304,7 +338,7 @@ TEST(libbacktrace, ptrace_trace) {
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);
+ ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
@@ -318,7 +352,7 @@ TEST(libbacktrace, ptrace_trace_shared_map) {
TEST(libbacktrace, ptrace_max_trace) {
pid_t pid;
if ((pid = fork()) == 0) {
- ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0);
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0);
_exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
@@ -329,21 +363,21 @@ TEST(libbacktrace, ptrace_max_trace) {
}
void VerifyProcessIgnoreFrames(Backtrace* bt_all) {
- UniquePtr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
- ASSERT_TRUE(ign1.get() != NULL);
+ std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
- UniquePtr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
- ASSERT_TRUE(ign2.get() != NULL);
+ std::unique_ptr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
- VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), NULL);
+ VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
}
TEST(libbacktrace, ptrace_ignore_frames) {
pid_t pid;
if ((pid = fork()) == 0) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
@@ -355,8 +389,8 @@ TEST(libbacktrace, ptrace_ignore_frames) {
// 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;
+ EXPECT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ return nullptr;
}
void GetThreads(pid_t pid, std::vector<pid_t>* threads) {
@@ -365,9 +399,9 @@ void GetThreads(pid_t pid, std::vector<pid_t>* threads) {
snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
DIR* tasks_dir = opendir(task_path);
- ASSERT_TRUE(tasks_dir != NULL);
+ ASSERT_TRUE(tasks_dir != nullptr);
struct dirent* entry;
- while ((entry = readdir(tasks_dir)) != NULL) {
+ while ((entry = readdir(tasks_dir)) != nullptr) {
char* end;
pid_t tid = strtoul(entry->d_name, &end, 10);
if (*end == '\0') {
@@ -386,9 +420,9 @@ TEST(libbacktrace, ptrace_threads) {
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t thread;
- ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0);
+ ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
}
- ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
@@ -420,27 +454,27 @@ TEST(libbacktrace, ptrace_threads) {
}
void VerifyLevelThread(void*) {
- UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
- ASSERT_TRUE(backtrace.get() != NULL);
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
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);
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
}
void VerifyMaxThread(void*) {
- UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
- ASSERT_TRUE(backtrace.get() != NULL);
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
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);
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, nullptr), 0);
}
void* ThreadLevelRun(void* data) {
@@ -448,7 +482,7 @@ void* ThreadLevelRun(void* data) {
thread->tid = gettid();
EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
- return NULL;
+ return nullptr;
}
TEST(libbacktrace, thread_level_trace) {
@@ -456,7 +490,7 @@ TEST(libbacktrace, thread_level_trace) {
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- thread_t thread_data = { 0, 0, 0 };
+ thread_t thread_data = { 0, 0, 0, nullptr };
pthread_t thread;
ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
@@ -471,10 +505,10 @@ TEST(libbacktrace, thread_level_trace) {
// Save the current signal action and make sure it is restored afterwards.
struct sigaction cur_action;
- ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &cur_action) == 0);
+ ASSERT_TRUE(sigaction(THREAD_SIGNAL, nullptr, &cur_action) == 0);
- UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
- ASSERT_TRUE(backtrace.get() != NULL);
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
VerifyLevelDump(backtrace.get());
@@ -484,14 +518,18 @@ TEST(libbacktrace, thread_level_trace) {
// Verify that the old action was restored.
struct sigaction new_action;
- ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &new_action) == 0);
+ ASSERT_TRUE(sigaction(THREAD_SIGNAL, nullptr, &new_action) == 0);
EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction);
// The SA_RESTORER flag gets set behind our back, so a direct comparison
// doesn't work unless we mask the value off. Mips doesn't have this
// flag, so skip this on that platform.
-#ifdef SA_RESTORER
+#if defined(SA_RESTORER)
cur_action.sa_flags &= ~SA_RESTORER;
new_action.sa_flags &= ~SA_RESTORER;
+#elif defined(__GLIBC__)
+ // Our host compiler doesn't appear to define this flag for some reason.
+ cur_action.sa_flags &= ~0x04000000;
+ new_action.sa_flags &= ~0x04000000;
#endif
EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
}
@@ -501,26 +539,26 @@ TEST(libbacktrace, thread_ignore_frames) {
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- thread_t thread_data = { 0, 0, 0 };
+ thread_t thread_data = { 0, 0, 0, nullptr };
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);
+ std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(all.get() != nullptr);
ASSERT_TRUE(all->Unwind(0));
- UniquePtr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
- ASSERT_TRUE(ign1.get() != NULL);
+ std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
- UniquePtr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
- ASSERT_TRUE(ign2.get() != NULL);
+ std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
- VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), NULL);
+ VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr);
// Tell the thread to exit its infinite loop.
android_atomic_acquire_store(0, &thread_data.state);
@@ -531,7 +569,7 @@ void* ThreadMaxRun(void* data) {
thread->tid = gettid();
EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
- return NULL;
+ return nullptr;
}
TEST(libbacktrace, thread_max_trace) {
@@ -539,15 +577,15 @@ TEST(libbacktrace, thread_max_trace) {
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- thread_t thread_data = { 0, 0, 0 };
+ thread_t thread_data = { 0, 0, 0, nullptr };
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);
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
VerifyMaxDump(backtrace.get());
@@ -570,7 +608,7 @@ void* ThreadDump(void* data) {
android_atomic_acquire_store(1, &dump->done);
- return NULL;
+ return nullptr;
}
TEST(libbacktrace, thread_multiple_dump) {
@@ -614,11 +652,11 @@ TEST(libbacktrace, thread_multiple_dump) {
// Tell the runner thread to exit its infinite loop.
android_atomic_acquire_store(0, &runners[i].state);
- ASSERT_TRUE(dumpers[i].backtrace != NULL);
+ ASSERT_TRUE(dumpers[i].backtrace != nullptr);
VerifyMaxDump(dumpers[i].backtrace);
delete dumpers[i].backtrace;
- dumpers[i].backtrace = NULL;
+ dumpers[i].backtrace = nullptr;
}
}
@@ -654,11 +692,11 @@ TEST(libbacktrace, thread_multiple_dump_same_thread) {
for (size_t i = 0; i < NUM_THREADS; i++) {
ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 30));
- ASSERT_TRUE(dumpers[i].backtrace != NULL);
+ ASSERT_TRUE(dumpers[i].backtrace != nullptr);
VerifyMaxDump(dumpers[i].backtrace);
delete dumpers[i].backtrace;
- dumpers[i].backtrace = NULL;
+ dumpers[i].backtrace = nullptr;
}
// Tell the runner thread to exit its infinite loop.
@@ -673,37 +711,54 @@ TEST(libbacktrace, simultaneous_maps) {
BacktraceMap* map3 = BacktraceMap::Create(getpid());
Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
+ ASSERT_TRUE(back1 != nullptr);
EXPECT_TRUE(back1->Unwind(0));
delete back1;
delete map1;
Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
+ ASSERT_TRUE(back2 != nullptr);
EXPECT_TRUE(back2->Unwind(0));
delete back2;
delete map2;
Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
+ ASSERT_TRUE(back3 != nullptr);
EXPECT_TRUE(back3->Unwind(0));
delete back3;
delete map3;
}
+TEST(libbacktrace, fillin_erases) {
+ BacktraceMap* back_map = BacktraceMap::Create(getpid());
+
+ backtrace_map_t map;
+
+ map.start = 1;
+ map.end = 3;
+ map.flags = 1;
+ map.name = "Initialized";
+ back_map->FillIn(0, &map);
+ delete back_map;
+
+ ASSERT_FALSE(BacktraceMap::IsValid(map));
+ ASSERT_EQ(static_cast<uintptr_t>(0), map.start);
+ ASSERT_EQ(static_cast<uintptr_t>(0), map.end);
+ ASSERT_EQ(0, map.flags);
+ ASSERT_EQ("", map.name);
+}
+
TEST(libbacktrace, format_test) {
- UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
- ASSERT_TRUE(backtrace.get() != NULL);
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
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__)
@@ -714,8 +769,8 @@ TEST(libbacktrace, format_test) {
backtrace->FormatFrameData(&frame));
// Check map name empty, but exists.
- frame.map = &map;
- map.start = 1;
+ frame.map.start = 1;
+ frame.map.end = 1;
#if defined(__LP64__)
EXPECT_EQ("#01 pc 0000000000000001 <unknown>",
#else
@@ -726,9 +781,9 @@ TEST(libbacktrace, format_test) {
// Check relative pc is set and map name is set.
frame.pc = 0x12345679;
- frame.map = &map;
- map.name = "MapFake";
- map.start = 1;
+ frame.map.name = "MapFake";
+ frame.map.start = 1;
+ frame.map.end = 1;
#if defined(__LP64__)
EXPECT_EQ("#01 pc 0000000012345678 MapFake",
#else
@@ -764,12 +819,12 @@ bool map_sort(map_test_t i, map_test_t j) {
return i.start < j.start;
}
-static void VerifyMap(pid_t pid) {
+void VerifyMap(pid_t pid) {
char buffer[4096];
snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
FILE* map_file = fopen(buffer, "r");
- ASSERT_TRUE(map_file != NULL);
+ ASSERT_TRUE(map_file != nullptr);
std::vector<map_test_t> test_maps;
while (fgets(buffer, sizeof(buffer), map_file)) {
map_test_t map;
@@ -779,7 +834,7 @@ static void VerifyMap(pid_t pid) {
fclose(map_file);
std::sort(test_maps.begin(), test_maps.end(), map_sort);
- UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid));
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
// Basic test that verifies that the map is in the expected order.
std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
@@ -813,7 +868,173 @@ TEST(libbacktrace, verify_map_remote) {
ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
kill(pid, SIGKILL);
- ASSERT_EQ(waitpid(pid, NULL, 0), pid);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+}
+
+void* ThreadReadTest(void* data) {
+ thread_t* thread_data = reinterpret_cast<thread_t*>(data);
+
+ thread_data->tid = gettid();
+
+ // Create two map pages.
+ // Mark the second page as not-readable.
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+ uint8_t* memory;
+ if (posix_memalign(reinterpret_cast<void**>(&memory), pagesize, 2 * pagesize) != 0) {
+ return reinterpret_cast<void*>(-1);
+ }
+
+ if (mprotect(&memory[pagesize], pagesize, PROT_NONE) != 0) {
+ return reinterpret_cast<void*>(-1);
+ }
+
+ // Set up a simple pattern in memory.
+ for (size_t i = 0; i < pagesize; i++) {
+ memory[i] = i;
+ }
+
+ thread_data->data = memory;
+
+ // Tell the caller it's okay to start reading memory.
+ android_atomic_acquire_store(1, &thread_data->state);
+
+ // Loop waiting for the caller to finish reading the memory.
+ while (thread_data->state) {
+ }
+
+ // Re-enable read-write on the page so that we don't crash if we try
+ // and access data on this page when freeing the memory.
+ if (mprotect(&memory[pagesize], pagesize, PROT_READ | PROT_WRITE) != 0) {
+ return reinterpret_cast<void*>(-1);
+ }
+ free(memory);
+
+ android_atomic_acquire_store(1, &thread_data->state);
+
+ return nullptr;
+}
+
+void RunReadTest(Backtrace* backtrace, uintptr_t read_addr) {
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+
+ // Create a page of data to use to do quick compares.
+ uint8_t* expected = new uint8_t[pagesize];
+ for (size_t i = 0; i < pagesize; i++) {
+ expected[i] = i;
+ }
+ uint8_t* data = new uint8_t[2*pagesize];
+ // Verify that we can only read one page worth of data.
+ size_t bytes_read = backtrace->Read(read_addr, data, 2 * pagesize);
+ ASSERT_EQ(pagesize, bytes_read);
+ ASSERT_TRUE(memcmp(data, expected, pagesize) == 0);
+
+ // Verify unaligned reads.
+ for (size_t i = 1; i < sizeof(word_t); i++) {
+ bytes_read = backtrace->Read(read_addr + i, data, 2 * sizeof(word_t));
+ ASSERT_EQ(2 * sizeof(word_t), bytes_read);
+ ASSERT_TRUE(memcmp(data, &expected[i], 2 * sizeof(word_t)) == 0)
+ << "Offset at " << i << " failed";
+ }
+ delete data;
+ delete expected;
+}
+
+TEST(libbacktrace, thread_read) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_t thread;
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadReadTest, &thread_data) == 0);
+
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 10));
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ RunReadTest(backtrace.get(), reinterpret_cast<uintptr_t>(thread_data.data));
+
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 10));
+}
+
+volatile uintptr_t g_ready = 0;
+volatile uintptr_t g_addr = 0;
+
+void ForkedReadTest() {
+ // Create two map pages.
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+ uint8_t* memory;
+ if (posix_memalign(reinterpret_cast<void**>(&memory), pagesize, 2 * pagesize) != 0) {
+ perror("Failed to allocate memory\n");
+ exit(1);
+ }
+
+ // Mark the second page as not-readable.
+ if (mprotect(&memory[pagesize], pagesize, PROT_NONE) != 0) {
+ perror("Failed to mprotect memory\n");
+ exit(1);
+ }
+
+ // Set up a simple pattern in memory.
+ for (size_t i = 0; i < pagesize; i++) {
+ memory[i] = i;
+ }
+
+ g_addr = reinterpret_cast<uintptr_t>(memory);
+ g_ready = 1;
+
+ while (1) {
+ usleep(US_PER_MSEC);
+ }
+}
+
+TEST(libbacktrace, process_read) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ForkedReadTest();
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+
+ bool test_executed = false;
+ uint64_t start = NanoTime();
+ while (1) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+ WaitForStop(pid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ uintptr_t read_addr;
+ size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(&g_ready),
+ reinterpret_cast<uint8_t*>(&read_addr),
+ sizeof(uintptr_t));
+ ASSERT_EQ(sizeof(uintptr_t), bytes_read);
+ if (read_addr) {
+ // The forked process is ready to be read.
+ bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(&g_addr),
+ reinterpret_cast<uint8_t*>(&read_addr),
+ sizeof(uintptr_t));
+ ASSERT_EQ(sizeof(uintptr_t), bytes_read);
+
+ RunReadTest(backtrace.get(), read_addr);
+
+ test_executed = true;
+ break;
+ }
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+ }
+ if ((NanoTime() - start) > 5 * NS_PER_SEC) {
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+
+ ASSERT_TRUE(test_executed);
}
#if defined(ENABLE_PSS_TESTS)
@@ -821,24 +1042,26 @@ TEST(libbacktrace, verify_map_remote) {
#define MAX_LEAK_BYTES 32*1024UL
-static void CheckForLeak(pid_t pid, pid_t tid) {
+void CheckForLeak(pid_t pid, pid_t tid) {
// Do a few runs to get the PSS stable.
for (size_t i = 0; i < 100; i++) {
Backtrace* backtrace = Backtrace::Create(pid, tid);
- ASSERT_TRUE(backtrace != NULL);
+ ASSERT_TRUE(backtrace != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
delete backtrace;
}
size_t stable_pss = GetPssBytes();
+ ASSERT_TRUE(stable_pss != 0);
// Loop enough that even a small leak should be detectable.
for (size_t i = 0; i < 4096; i++) {
Backtrace* backtrace = Backtrace::Create(pid, tid);
- ASSERT_TRUE(backtrace != NULL);
+ ASSERT_TRUE(backtrace != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
delete backtrace;
}
size_t new_pss = GetPssBytes();
+ ASSERT_TRUE(new_pss != 0);
size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
// As long as the new pss is within a certain amount, consider everything okay.
ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
@@ -849,9 +1072,9 @@ TEST(libbacktrace, check_for_leak_local) {
}
TEST(libbacktrace, check_for_leak_local_thread) {
- thread_t thread_data = { 0, 0, 0 };
+ thread_t thread_data = { 0, 0, 0, nullptr };
pthread_t thread;
- ASSERT_TRUE(pthread_create(&thread, NULL, ThreadLevelRun, &thread_data) == 0);
+ ASSERT_TRUE(pthread_create(&thread, nullptr, ThreadLevelRun, &thread_data) == 0);
// Wait up to 2 seconds for the tid to be set.
ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
@@ -861,7 +1084,7 @@ TEST(libbacktrace, check_for_leak_local_thread) {
// Tell the thread to exit its infinite loop.
android_atomic_acquire_store(0, &thread_data.state);
- ASSERT_TRUE(pthread_join(thread, NULL) == 0);
+ ASSERT_TRUE(pthread_join(thread, nullptr) == 0);
}
TEST(libbacktrace, check_for_leak_remote) {
@@ -884,6 +1107,6 @@ TEST(libbacktrace, check_for_leak_remote) {
ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
kill(pid, SIGKILL);
- ASSERT_EQ(waitpid(pid, NULL, 0), pid);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
}
#endif