diff options
Diffstat (limited to 'libbacktrace')
-rw-r--r-- | libbacktrace/Android.build.mk | 27 | ||||
-rw-r--r--[-rwxr-xr-x] | libbacktrace/Android.mk | 98 | ||||
-rw-r--r-- | libbacktrace/Backtrace.cpp | 138 | ||||
-rw-r--r-- | libbacktrace/BacktraceCurrent.cpp | 155 | ||||
-rw-r--r-- | libbacktrace/BacktraceCurrent.h | 58 | ||||
-rw-r--r-- | libbacktrace/BacktraceImpl.cpp | 207 | ||||
-rwxr-xr-x | libbacktrace/BacktraceImpl.h | 74 | ||||
-rw-r--r--[-rwxr-xr-x] | libbacktrace/BacktraceLog.h | 0 | ||||
-rw-r--r-- | libbacktrace/BacktraceMap.cpp | 16 | ||||
-rw-r--r-- | libbacktrace/BacktracePtrace.cpp | 116 | ||||
-rw-r--r-- | libbacktrace/BacktracePtrace.h | 37 | ||||
-rw-r--r-- | libbacktrace/BacktraceThread.cpp | 227 | ||||
-rw-r--r-- | libbacktrace/GetPss.cpp | 18 | ||||
-rw-r--r-- | libbacktrace/ThreadEntry.cpp | 127 | ||||
-rw-r--r-- | libbacktrace/ThreadEntry.h (renamed from libbacktrace/BacktraceThread.h) | 27 | ||||
-rw-r--r--[-rwxr-xr-x] | libbacktrace/UnwindCurrent.cpp | 138 | ||||
-rw-r--r-- | libbacktrace/UnwindCurrent.h | 25 | ||||
-rw-r--r-- | libbacktrace/UnwindMap.cpp | 13 | ||||
-rw-r--r-- | libbacktrace/UnwindMap.h | 5 | ||||
-rw-r--r-- | libbacktrace/UnwindPtrace.cpp | 34 | ||||
-rw-r--r-- | libbacktrace/UnwindPtrace.h | 18 | ||||
-rw-r--r-- | libbacktrace/backtrace_test.cpp | 439 |
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.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.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 |