diff options
Diffstat (limited to 'libbacktrace')
-rw-r--r-- | libbacktrace/Android.build.mk | 81 | ||||
-rwxr-xr-x[-rw-r--r--] | libbacktrace/Android.mk | 358 | ||||
-rw-r--r-- | libbacktrace/BacktraceImpl.cpp | 25 | ||||
-rwxr-xr-x[-rw-r--r--] | libbacktrace/BacktraceImpl.h | 11 | ||||
-rwxr-xr-x | libbacktrace/BacktraceLog.h | 28 | ||||
-rw-r--r-- | libbacktrace/BacktraceMap.cpp | 7 | ||||
-rw-r--r-- | libbacktrace/BacktraceThread.cpp | 277 | ||||
-rw-r--r-- | libbacktrace/BacktraceThread.h | 98 | ||||
-rw-r--r-- | libbacktrace/Corkscrew.cpp | 251 | ||||
-rw-r--r-- | libbacktrace/Corkscrew.h | 82 | ||||
-rw-r--r-- | libbacktrace/GetPss.cpp | 85 | ||||
-rw-r--r-- | libbacktrace/GetPss.h | 22 | ||||
-rwxr-xr-x[-rw-r--r--] | libbacktrace/UnwindCurrent.cpp | 128 | ||||
-rw-r--r-- | libbacktrace/UnwindCurrent.h | 16 | ||||
-rw-r--r-- | libbacktrace/UnwindMap.cpp | 123 | ||||
-rw-r--r-- | libbacktrace/UnwindMap.h | 19 | ||||
-rw-r--r-- | libbacktrace/UnwindPtrace.cpp | 11 | ||||
-rw-r--r-- | libbacktrace/UnwindPtrace.h | 2 | ||||
-rw-r--r-- | libbacktrace/backtrace_test.cpp | 203 |
19 files changed, 913 insertions, 914 deletions
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk new file mode 100644 index 0000000..2f55645 --- /dev/null +++ b/libbacktrace/Android.build.mk @@ -0,0 +1,81 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include $(CLEAR_VARS) + +LOCAL_MODULE := $(module) +LOCAL_MODULE_TAGS := $(module_tag) +LOCAL_MULTILIB := $($(module)_multilib) + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk \ + $(LOCAL_PATH)/Android.build.mk \ + +LOCAL_CFLAGS := \ + $(common_cflags) \ + $($(module)_cflags) \ + $($(module)_cflags_$(build_type)) \ + +LOCAL_CONLYFLAGS += \ + $(common_conlyflags) \ + $($(module)_conlyflags) \ + $($(module)_conlyflags_$(build_type)) \ + +LOCAL_CPPFLAGS += \ + $(common_cppflags) \ + $($(module)_cppflags) \ + $($(module)_cppflags_$(build_type)) \ + +LOCAL_C_INCLUDES := \ + $(common_c_includes) \ + $($(module)_c_includes) \ + $($(module)_c_includes_$(build_type)) \ + +LOCAL_SRC_FILES := \ + $($(module)_src_files) \ + $($(module)_src_files_$(build_type)) \ + +LOCAL_STATIC_LIBRARIES := \ + $($(module)_static_libraries) \ + $($(module)_static_libraries_$(build_type)) \ + +LOCAL_SHARED_LIBRARIES := \ + $($(module)_shared_libraries) \ + $($(module)_shared_libraries_$(build_type)) \ + +LOCAL_LDLIBS := \ + $($(module)_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 + include $(BUILD_HOST_$(build_target)) + endif +endif diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index 0ae8839..c321369 100644..100755 --- a/libbacktrace/Android.mk +++ b/libbacktrace/Android.mk @@ -1,14 +1,23 @@ -LOCAL_PATH:= $(call my-dir) +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# -common_src := \ - BacktraceImpl.cpp \ - BacktraceMap.cpp \ - BacktraceThread.cpp \ - thread_utils.c \ +LOCAL_PATH:= $(call my-dir) common_cflags := \ -Wall \ - -Wno-unused-parameter \ -Werror \ common_conlyflags := \ @@ -17,274 +26,185 @@ common_conlyflags := \ common_cppflags := \ -std=gnu++11 \ -common_shared_libs := \ - libcutils \ - libgccdemangle \ - liblog \ - -# To enable using libunwind on each arch, add it to this list. -libunwind_architectures := arm arm64 +build_host := false +ifeq ($(HOST_OS),linux) +ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64)) +build_host := true +endif +endif -ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures))) +#------------------------------------------------------------------------- +# The libbacktrace library. +#------------------------------------------------------------------------- +libbacktrace_src_files := \ + BacktraceImpl.cpp \ + BacktraceMap.cpp \ + BacktraceThread.cpp \ + thread_utils.c \ -#---------------------------------------------------------------------------- -# The native libbacktrace library with libunwind. -#---------------------------------------------------------------------------- -include $(CLEAR_VARS) +libbacktrace_shared_libraries_target := \ + libcutils \ + libgccdemangle \ -LOCAL_SRC_FILES:= \ - $(common_src) \ +libbacktrace_src_files += \ UnwindCurrent.cpp \ UnwindMap.cpp \ UnwindPtrace.cpp \ -LOCAL_CFLAGS := \ - $(common_cflags) \ - -LOCAL_CONLYFLAGS += \ - $(common_conlyflags) \ - -LOCAL_CPPFLAGS += \ - $(common_cppflags) \ - -LOCAL_MODULE := libbacktrace -LOCAL_MODULE_TAGS := optional - -LOCAL_C_INCLUDES := \ - $(common_c_includes) \ +libbacktrace_c_includes := \ external/libunwind/include \ -LOCAL_SHARED_LIBRARIES := \ - $(common_shared_libs) \ +libbacktrace_shared_libraries := \ libunwind \ libunwind-ptrace \ -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk - -include external/stlport/libstlport.mk - -include $(BUILD_SHARED_LIBRARY) - -else - -#---------------------------------------------------------------------------- -# The native libbacktrace library with libcorkscrew. -#---------------------------------------------------------------------------- -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - $(common_src) \ - Corkscrew.cpp \ - -LOCAL_CFLAGS := \ - $(common_cflags) \ +libbacktrace_shared_libraries_host := \ + liblog \ -LOCAL_CONLYFLAGS += \ - $(common_conlyflags) \ +libbacktrace_static_libraries_host := \ + libcutils \ -LOCAL_CPPFLAGS += \ - $(common_cppflags) \ +module := libbacktrace +module_tag := optional +build_type := target +build_target := SHARED_LIBRARY +include $(LOCAL_PATH)/Android.build.mk +build_type := host +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 \ -LOCAL_MODULE := libbacktrace -LOCAL_MODULE_TAGS := optional +libbacktrace_libc++_shared_libraries_target := \ + libcutils \ + libgccdemangle \ -LOCAL_C_INCLUDES := \ - $(common_c_includes) \ - system/core/libcorkscrew \ +libbacktrace_libc++_src_files += \ + UnwindCurrent.cpp \ + UnwindMap.cpp \ + UnwindPtrace.cpp \ -LOCAL_SHARED_LIBRARIES := \ - $(common_shared_libs) \ - libcorkscrew \ - libdl \ +libbacktrace_libc++_c_includes := \ + external/libunwind/include \ -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk +libbacktrace_libc++_shared_libraries := \ + libunwind \ + libunwind-ptrace \ -include external/stlport/libstlport.mk +libbacktrace_libc++_shared_libraries_host := \ + liblog \ -include $(BUILD_SHARED_LIBRARY) +libbacktrace_libc++_static_libraries_host := \ + libcutils \ +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 -#---------------------------------------------------------------------------- -# libbacktrace test library, all optimizations turned off -#---------------------------------------------------------------------------- -include $(CLEAR_VARS) - -LOCAL_MODULE := libbacktrace_test -LOCAL_MODULE_FLAGS := debug - -LOCAL_SRC_FILES := \ - backtrace_testlib.c - -LOCAL_CFLAGS += \ - -std=gnu99 \ +#------------------------------------------------------------------------- +# The libbacktrace_test library needed by backtrace_test. +#------------------------------------------------------------------------- +libbacktrace_test_cflags := \ -O0 \ -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk - -include $(BUILD_SHARED_LIBRARY) - -#---------------------------------------------------------------------------- -# libbacktrace test executable -#---------------------------------------------------------------------------- -include $(CLEAR_VARS) - -LOCAL_MODULE := backtrace_test -LOCAL_MODULE_FLAGS := debug - -LOCAL_SRC_FILES := \ - backtrace_test.cpp \ - thread_utils.c \ - -LOCAL_CFLAGS += \ - $(common_cflags) \ +libbacktrace_test_src_files := \ + backtrace_testlib.c \ + +module := libbacktrace_test +module_tag := debug +build_type := target +build_target := SHARED_LIBRARY +include $(LOCAL_PATH)/Android.build.mk +build_type := host +include $(LOCAL_PATH)/Android.build.mk + +#------------------------------------------------------------------------- +# The backtrace_test executable. +#------------------------------------------------------------------------- +backtrace_test_cflags := \ -fno-builtin \ - -fstack-protector-all \ -O0 \ -g \ - -DGTEST_OS_LINUX_ANDROID \ - -DGTEST_HAS_STD_STRING \ -ifeq ($(TARGET_ARCH),arm64) - $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain) - LOCAL_CFLAGS += -fno-stack-protector -endif # arm64 +backtrace_test_cflags_target := \ + -DENABLE_PSS_TESTS \ -LOCAL_CONLYFLAGS += \ - $(common_conlyflags) \ +backtrace_test_src_files := \ + backtrace_test.cpp \ + GetPss.cpp \ + thread_utils.c \ -LOCAL_CPPFLAGS += \ - $(common_cppflags) \ +backtrace_test_ldlibs_host := \ + -lpthread \ + -lrt \ -LOCAL_SHARED_LIBRARIES += \ - libcutils \ +backtrace_test_shared_libraries := \ libbacktrace_test \ libbacktrace \ -LOCAL_LDLIBS := \ - -lpthread \ +backtrace_test_shared_libraries_target := \ + libcutils \ -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk +backtrace_test_static_libraries_host := \ + libcutils \ -include $(BUILD_NATIVE_TEST) +module := backtrace_test +module_tag := debug +build_type := target +build_target := NATIVE_TEST +include $(LOCAL_PATH)/Android.build.mk +build_type := host +include $(LOCAL_PATH)/Android.build.mk #---------------------------------------------------------------------------- -# Only x86 host versions of libbacktrace supported. +# Special truncated libbacktrace library for mac. #---------------------------------------------------------------------------- -ifeq ($(HOST_ARCH),x86) +ifeq ($(HOST_OS),darwin) -#---------------------------------------------------------------------------- -# The host libbacktrace library using libcorkscrew -#---------------------------------------------------------------------------- include $(CLEAR_VARS) - -LOCAL_CFLAGS += \ - $(common_cflags) \ - -LOCAL_CONLYFLAGS += \ - $(common_conlyflags) \ - -LOCAL_C_INCLUDES := \ - $(common_c_includes) \ - -LOCAL_SHARED_LIBRARIES := \ - libgccdemangle \ - liblog \ - LOCAL_MODULE := libbacktrace LOCAL_MODULE_TAGS := optional -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk - -ifeq ($(HOST_OS),linux) -LOCAL_SRC_FILES += \ - $(common_src) \ - Corkscrew.cpp \ - -LOCAL_C_INCLUDES += \ - system/core/libcorkscrew \ - -LOCAL_SHARED_LIBRARIES := \ - libcorkscrew \ - -LOCAL_CPPFLAGS += \ - $(common_cppflags) \ - -LOCAL_LDLIBS += \ - -ldl \ - -lrt \ - -else -LOCAL_SRC_FILES += \ +LOCAL_SRC_FILES := \ BacktraceMap.cpp \ -endif - include $(BUILD_HOST_SHARED_LIBRARY) -#---------------------------------------------------------------------------- -# The host test is only supported on linux. -#---------------------------------------------------------------------------- -ifeq ($(HOST_OS),linux) - -#---------------------------------------------------------------------------- -# libbacktrace host test library, all optimizations turned off -#---------------------------------------------------------------------------- +# Don't build for unbundled branches +ifeq (,$(TARGET_BUILD_APPS)) +#------------------------------------------------------------------------- +# The libbacktrace library (libc++) +#------------------------------------------------------------------------- include $(CLEAR_VARS) -LOCAL_MODULE := libbacktrace_test -LOCAL_MODULE_FLAGS := debug +LOCAL_MODULE := libbacktrace_libc++ +LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := \ - backtrace_testlib.c - -LOCAL_CFLAGS += \ - -std=gnu99 \ - -O0 \ + BacktraceMap.cpp \ -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk +LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) -#---------------------------------------------------------------------------- -# libbacktrace host test executable -#---------------------------------------------------------------------------- -include $(CLEAR_VARS) - -LOCAL_MODULE := backtrace_test -LOCAL_MODULE_FLAGS := debug - -LOCAL_SRC_FILES := \ - backtrace_test.cpp \ - thread_utils.c \ - -LOCAL_CFLAGS += \ - $(common_cflags) \ - -fno-builtin \ - -fstack-protector-all \ - -O0 \ - -g \ - -DGTEST_HAS_STD_STRING \ - -LOCAL_SHARED_LIBRARIES := \ - libbacktrace_test \ - libbacktrace \ - -LOCAL_LDLIBS := \ - -lpthread \ - -LOCAL_ADDITIONAL_DEPENDENCIES := \ - $(LOCAL_PATH)/Android.mk - -include $(BUILD_HOST_NATIVE_TEST) - -endif # HOST_OS == linux +endif # TARGET_BUILD_APPS -endif # HOST_ARCH == x86 +endif # HOST_OS-darwin diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp index 39296b4..405b042 100644 --- a/libbacktrace/BacktraceImpl.cpp +++ b/libbacktrace/BacktraceImpl.cpp @@ -19,17 +19,16 @@ #include <string.h> #include <sys/ptrace.h> #include <sys/types.h> +#include <ucontext.h> #include <unistd.h> -#define __STDC_FORMAT_MACROS -#include <inttypes.h> - #include <string> #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> #include "BacktraceImpl.h" +#include "BacktraceLog.h" #include "thread_utils.h" //------------------------------------------------------------------------- @@ -57,8 +56,8 @@ Backtrace::~Backtrace() { } } -bool Backtrace::Unwind(size_t num_ignore_frames) { - return impl_->Unwind(num_ignore_frames); +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, @@ -82,10 +81,10 @@ std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) { return func_name; } -bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value) { - if (ptr & 3) { +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 = (uint32_t)-1; + *out_value = (word_t)-1; return false; } return true; @@ -143,18 +142,18 @@ BacktraceCurrent::BacktraceCurrent( BacktraceCurrent::~BacktraceCurrent() { } -bool BacktraceCurrent::ReadWord(uintptr_t ptr, uint32_t* out_value) { +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<uint32_t*>(ptr); + *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<uint32_t>(-1); + *out_value = static_cast<word_t>(-1); return false; } } @@ -171,7 +170,7 @@ BacktracePtrace::BacktracePtrace( BacktracePtrace::~BacktracePtrace() { } -bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) { +bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) { if (!VerifyReadWordArgs(ptr, out_value)) { return false; } @@ -184,7 +183,7 @@ bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) { // To disambiguate -1 from a valid result, we clear errno beforehand. errno = 0; *out_value = ptrace(PTRACE_PEEKTEXT, Tid(), reinterpret_cast<void*>(ptr), NULL); - if (*out_value == static_cast<uint32_t>(-1) && errno) { + 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; diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h index af014d5..d34ad05 100644..100755 --- a/libbacktrace/BacktraceImpl.h +++ b/libbacktrace/BacktraceImpl.h @@ -21,17 +21,12 @@ #include <backtrace/BacktraceMap.h> #include <sys/types.h> -#include <log/log.h> - -// Macro to log the function name along with the warning message. -#define BACK_LOGW(format, ...) \ - ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) class BacktraceImpl { public: virtual ~BacktraceImpl() { } - virtual bool Unwind(size_t num_ignore_frames) = 0; + 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. @@ -61,7 +56,7 @@ public: BacktraceCurrent(BacktraceImpl* impl, BacktraceMap* map); virtual ~BacktraceCurrent(); - bool ReadWord(uintptr_t ptr, uint32_t* out_value); + bool ReadWord(uintptr_t ptr, word_t* out_value); }; class BacktracePtrace : public Backtrace { @@ -69,7 +64,7 @@ public: BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map); virtual ~BacktracePtrace(); - bool ReadWord(uintptr_t ptr, uint32_t* out_value); + bool ReadWord(uintptr_t ptr, word_t* out_value); }; Backtrace* CreateCurrentObj(BacktraceMap* map); diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h new file mode 100755 index 0000000..1632ec2 --- /dev/null +++ b/libbacktrace/BacktraceLog.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBBACKTRACE_BACKTRACE_LOG_H +#define _LIBBACKTRACE_BACKTRACE_LOG_H + +#define LOG_TAG "libbacktrace" + +#include <log/log.h> + +// Macro to log the function name along with the warning message. +#define BACK_LOGW(format, ...) \ + ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) + +#endif // _LIBBACKTRACE_BACKTRACE_LOG_H diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp index 6320800..0056f4b 100644 --- a/libbacktrace/BacktraceMap.cpp +++ b/libbacktrace/BacktraceMap.cpp @@ -93,7 +93,8 @@ bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) { } ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s", - map->start, map->end, map->flags, map->name.c_str()); + reinterpret_cast<void*>(map->start), reinterpret_cast<void*>(map->end), + map->flags, map->name.c_str()); return true; } @@ -107,11 +108,11 @@ bool BacktraceMap::Build() { #if defined(__APPLE__) // cmd is guaranteed to always be big enough to hold this string. - sprintf(cmd, "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_); + snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_); FILE* fp = popen(cmd, "r"); #else // path is guaranteed to always be big enough to hold this string. - sprintf(path, "/proc/%d/maps", pid_); + snprintf(path, sizeof(path), "/proc/%d/maps", pid_); FILE* fp = fopen(path, "r"); #endif if (fp == NULL) { diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp index 5ffe516..b47cd2a 100644 --- a/libbacktrace/BacktraceThread.cpp +++ b/libbacktrace/BacktraceThread.cpp @@ -16,200 +16,195 @@ #include <errno.h> #include <inttypes.h> +#include <limits.h> +#include <linux/futex.h> #include <pthread.h> #include <signal.h> #include <string.h> +#include <sys/syscall.h> +#include <sys/time.h> #include <sys/types.h> +#include <ucontext.h> #include <cutils/atomic.h> +#include "BacktraceLog.h" #include "BacktraceThread.h" #include "thread_utils.h" +static inline int futex(volatile int* uaddr, int op, int val, const struct timespec* ts, volatile int* uaddr2, int val3) { + return syscall(__NR_futex, uaddr, op, val, ts, uaddr2, val3); +} + //------------------------------------------------------------------------- // ThreadEntry implementation. //------------------------------------------------------------------------- -static ThreadEntry* g_list = NULL; -static pthread_mutex_t g_entry_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER; - -ThreadEntry::ThreadEntry( - BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) - : thread_intf(intf), pid(pid), tid(tid), next(NULL), prev(NULL), - state(STATE_WAITING), num_ignore_frames(num_ignore_frames) { +ThreadEntry* ThreadEntry::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), futex_(0), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER), next_(ThreadEntry::list_), prev_(NULL) { + // Add ourselves to the list. + if (ThreadEntry::list_) { + ThreadEntry::list_->prev_ = this; + } + ThreadEntry::list_ = this; } -ThreadEntry::~ThreadEntry() { - pthread_mutex_lock(&g_entry_mutex); - if (g_list == this) { - g_list = next; - } else { - if (next) { - next->prev = prev; +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; } - prev->next = next; + entry = entry->next_; } - pthread_mutex_unlock(&g_entry_mutex); - next = NULL; - prev = NULL; + if (!entry) { + if (create) { + entry = new ThreadEntry(pid, tid); + } + } else { + entry->ref_count_++; + } + pthread_mutex_unlock(&ThreadEntry::list_mutex_); + + return entry; } -ThreadEntry* ThreadEntry::AddThreadToUnwind( - BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) { - ThreadEntry* entry = new ThreadEntry(intf, pid, tid, num_ignore_frames); +void ThreadEntry::Remove(ThreadEntry* entry) { + pthread_mutex_unlock(&entry->mutex_); - pthread_mutex_lock(&g_entry_mutex); - ThreadEntry* cur_entry = g_list; - while (cur_entry != NULL) { - if (cur_entry->Match(pid, tid)) { - // There is already an entry for this pid/tid, this is bad. - BACK_LOGW("Entry for pid %d tid %d already exists.", pid, tid); + pthread_mutex_lock(&ThreadEntry::list_mutex_); + if (--entry->ref_count_ == 0) { + delete entry; + } + pthread_mutex_unlock(&ThreadEntry::list_mutex_); +} - pthread_mutex_unlock(&g_entry_mutex); - return NULL; +// 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_; } - cur_entry = cur_entry->next; + prev_->next_ = next_; } - // Add the entry to the list. - entry->next = g_list; - if (g_list) { - g_list->prev = entry; + next_ = NULL; + prev_ = NULL; +} + +void ThreadEntry::Wait(int value) { + timespec ts; + ts.tv_sec = 10; + ts.tv_nsec = 0; + errno = 0; + futex(&futex_, FUTEX_WAIT, value, &ts, NULL, 0); + if (errno != 0 && errno != EWOULDBLOCK) { + BACK_LOGW("futex wait failed, futex = %d: %s", futex_, strerror(errno)); } - g_list = entry; - pthread_mutex_unlock(&g_entry_mutex); +} - return entry; +void ThreadEntry::Wake() { + futex_++; + futex(&futex_, FUTEX_WAKE, INT_MAX, NULL, NULL, 0); +} + +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 void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo, - void* sigcontext) { - if (pthread_mutex_lock(&g_entry_mutex) == 0) { - pid_t pid = getpid(); - pid_t tid = gettid(); - ThreadEntry* cur_entry = g_list; - while (cur_entry) { - if (cur_entry->Match(pid, tid)) { - break; - } - cur_entry = cur_entry->next; - } - pthread_mutex_unlock(&g_entry_mutex); - if (!cur_entry) { - BACK_LOGW("Unable to find pid %d tid %d information", pid, tid); - return; - } +static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER; - if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state) == 0) { - cur_entry->thread_intf->ThreadUnwind(siginfo, sigcontext, - cur_entry->num_ignore_frames); - } - android_atomic_release_store(STATE_DONE, &cur_entry->state); +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(1); + + ThreadEntry::Remove(entry); } -BacktraceThread::BacktraceThread( - BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid, - BacktraceMap* map) - : BacktraceCurrent(impl, map), thread_intf_(thread_intf) { +BacktraceThread::BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map) + : BacktraceCurrent(impl, map) { tid_ = tid; } BacktraceThread::~BacktraceThread() { } -void BacktraceThread::FinishUnwind() { - for (std::vector<backtrace_frame_data_t>::iterator it = frames_.begin(); - it != frames_.end(); ++it) { - it->map = FindMap(it->pc); - - it->func_offset = 0; - it->func_name = GetFunctionName(it->pc, &it->func_offset); +bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { + if (ucontext) { + // Unwind using an already existing ucontext. + return impl_->Unwind(num_ignore_frames, ucontext); } -} -bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { - entry->state = STATE_WAITING; - - if (tgkill(Pid(), Tid(), SIGURG) != 0) { - BACK_LOGW("tgkill failed %s", strerror(errno)); + // 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; } - // Allow up to ten seconds for the dump to start. - int wait_millis = 10000; - int32_t state; - while (true) { - state = android_atomic_acquire_load(&entry->state); - if (state != STATE_WAITING) { - break; - } - if (wait_millis--) { - usleep(1000); - } else { - break; - } + 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; } - bool cancelled = false; - if (state == STATE_WAITING) { - if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state) == 0) { - BACK_LOGW("Cancelled dump of thread %d", entry->tid); - state = STATE_CANCEL; - cancelled = true; - } else { - state = android_atomic_acquire_load(&entry->state); - } + 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 at most ten seconds for the cancel or dump to finish. - wait_millis = 10000; - while (android_atomic_acquire_load(&entry->state) != STATE_DONE) { - if (wait_millis--) { - usleep(1000); - } else { - BACK_LOGW("Didn't finish thread unwind in 60 seconds."); - break; - } - } - return !cancelled; -} + // Wait for the thread to get the ucontext. + entry->Wait(0); -bool BacktraceThread::Unwind(size_t num_ignore_frames) { - ThreadEntry* entry = ThreadEntry::AddThreadToUnwind( - thread_intf_, Pid(), Tid(), num_ignore_frames); - if (!entry) { - return false; - } + // After the thread has received the signal, allow other unwinders to + // continue. + sigaction(THREAD_SIGNAL, &oldact, NULL); + pthread_mutex_unlock(&g_sigaction_mutex); - // Prevent multiple threads trying to set the trigger action on different - // threads at the same time. - bool retval = false; - if (pthread_mutex_lock(&g_sigaction_mutex) == 0) { - struct sigaction act, oldact; - memset(&act, 0, sizeof(act)); - act.sa_sigaction = SignalHandler; - act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; - sigemptyset(&act.sa_mask); - if (sigaction(SIGURG, &act, &oldact) == 0) { - retval = TriggerUnwindOnThread(entry); - sigaction(SIGURG, &oldact, NULL); - } else { - BACK_LOGW("sigaction failed %s", strerror(errno)); - } - pthread_mutex_unlock(&g_sigaction_mutex); - } else { - BACK_LOGW("unable to acquire sigaction mutex."); - } + bool unwind_done = impl_->Unwind(num_ignore_frames, entry->GetUcontext()); - if (retval) { - FinishUnwind(); - } - delete entry; + // Tell the signal handler to exit and release the entry. + entry->Wake(); - return retval; + return unwind_done; } diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h index 3412d58..ff3e9f3 100644 --- a/libbacktrace/BacktraceThread.h +++ b/libbacktrace/BacktraceThread.h @@ -18,73 +18,71 @@ #define _LIBBACKTRACE_BACKTRACE_THREAD_H #include <inttypes.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> #include <sys/types.h> +#include <ucontext.h> #include "BacktraceImpl.h" -enum state_e { - STATE_WAITING = 0, - STATE_DUMPING, - STATE_DONE, - STATE_CANCEL, -}; +// 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 BacktraceThreadInterface; +class ThreadEntry { +public: + static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true); -struct ThreadEntry { - ThreadEntry( - BacktraceThreadInterface* impl, pid_t pid, pid_t tid, - size_t num_ignore_frames); - ~ThreadEntry(); + static void Remove(ThreadEntry* entry); - bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid && chk_tid == tid); } + void Wake(); - static ThreadEntry* AddThreadToUnwind( - BacktraceThreadInterface* thread_intf, pid_t pid, pid_t tid, - size_t num_ignored_frames); + void Wait(int); - BacktraceThreadInterface* thread_intf; - pid_t pid; - pid_t tid; - ThreadEntry* next; - ThreadEntry* prev; - int32_t state; - int num_ignore_frames; -}; + void CopyUcontextFromSigcontext(void*); -// Interface class that does not contain any local storage, only defines -// virtual functions to be defined by subclasses. -class BacktraceThreadInterface { -public: - virtual ~BacktraceThreadInterface() { } + inline void Lock() { + pthread_mutex_lock(&mutex_); + // Reset the futex value in case of multiple unwinds of the same thread. + futex_ = 0; + } - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0; -}; + inline void Unlock() { + pthread_mutex_unlock(&mutex_); + } -class BacktraceThread : public BacktraceCurrent { -public: - // impl and thread_intf should point to the same object, this allows - // the compiler to catch if an implementation does not properly - // subclass both. - BacktraceThread( - BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid, - BacktraceMap* map); - virtual ~BacktraceThread(); + inline ucontext_t* GetUcontext() { return &ucontext_; } - virtual bool Unwind(size_t num_ignore_frames); +private: + ThreadEntry(pid_t pid, pid_t tid); + ~ThreadEntry(); - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { - thread_intf_->ThreadUnwind(siginfo, sigcontext, num_ignore_frames); - } + bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid_ && chk_tid == tid_); } -private: - virtual bool TriggerUnwindOnThread(ThreadEntry* entry); + pid_t pid_; + pid_t tid_; + int futex_; + int ref_count_; + pthread_mutex_t mutex_; + ThreadEntry* next_; + ThreadEntry* prev_; + ucontext_t ucontext_; - virtual void FinishUnwind(); + static ThreadEntry* list_; + static pthread_mutex_t list_mutex_; +}; + +class BacktraceThread : public BacktraceCurrent { +public: + BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map); + virtual ~BacktraceThread(); - BacktraceThreadInterface* thread_intf_; + virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext); }; #endif // _LIBBACKTRACE_BACKTRACE_THREAD_H diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp deleted file mode 100644 index efeee2e..0000000 --- a/libbacktrace/Corkscrew.cpp +++ /dev/null @@ -1,251 +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. - */ - -#define LOG_TAG "libbacktrace" - -#include <backtrace/Backtrace.h> - -#include <string.h> - -#include <backtrace-arch.h> -#include <corkscrew/backtrace.h> - -#ifndef __USE_GNU -#define __USE_GNU -#endif -#include <dlfcn.h> - -#include "Corkscrew.h" - -//------------------------------------------------------------------------- -// CorkscrewMap functions. -//------------------------------------------------------------------------- -CorkscrewMap::CorkscrewMap(pid_t pid) : BacktraceMap(pid), map_info_(NULL) { -} - -CorkscrewMap::~CorkscrewMap() { - if (map_info_) { - free_map_info_list(map_info_); - map_info_ = NULL; - } -} - -bool CorkscrewMap::Build() { - map_info_ = load_map_info_list(pid_); - - // Use the information in map_info_ to construct the BacktraceMap data - // rather than reparsing /proc/self/maps. - map_info_t* cur_map = map_info_; - while (cur_map) { - backtrace_map_t map; - map.start = cur_map->start; - map.end = cur_map->end; - map.flags = 0; - if (cur_map->is_readable) { - map.flags |= PROT_READ; - } - if (cur_map->is_writable) { - map.flags |= PROT_WRITE; - } - if (cur_map->is_executable) { - map.flags |= PROT_EXEC; - } - map.name = cur_map->name; - - // The maps are in descending order, but we want them in ascending order. - maps_.push_front(map); - - cur_map = cur_map->next; - } - return map_info_ != NULL; -} - -//------------------------------------------------------------------------- -// CorkscrewCommon functions. -//------------------------------------------------------------------------- -bool CorkscrewCommon::GenerateFrameData( - backtrace_frame_t* cork_frames, ssize_t num_frames) { - if (num_frames < 0) { - BACK_LOGW("libcorkscrew unwind failed."); - return false; - } - - std::vector<backtrace_frame_data_t>* frames = GetFrames(); - frames->resize(num_frames); - size_t i = 0; - for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin(); - it != frames->end(); ++it, ++i) { - it->num = i; - it->pc = cork_frames[i].absolute_pc; - it->sp = cork_frames[i].stack_top; - it->stack_size = cork_frames[i].stack_size; - it->func_offset = 0; - - it->map = FindMap(it->pc); - it->func_name = GetFunctionName(it->pc, &it->func_offset); - } - return true; -} - -//------------------------------------------------------------------------- -// CorkscrewCurrent functions. -//------------------------------------------------------------------------- -CorkscrewCurrent::CorkscrewCurrent() { -} - -CorkscrewCurrent::~CorkscrewCurrent() { -} - -bool CorkscrewCurrent::Unwind(size_t num_ignore_frames) { - backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; - ssize_t num_frames = unwind_backtrace(frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); - - return GenerateFrameData(frames, num_frames); -} - -std::string CorkscrewCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { - *offset = 0; - - Dl_info info; - const backtrace_map_t* map = FindMap(pc); - if (map) { - if (dladdr((const void*)pc, &info)) { - if (info.dli_sname) { - *offset = pc - map->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase; - return info.dli_sname; - } - } else { - // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file... - symbol_table_t* symbol_table = load_symbol_table(map->name.c_str()); - if (symbol_table) { - // First check if we can find the symbol using a relative pc. - std::string name; - const symbol_t* elf_symbol = find_symbol(symbol_table, pc - map->start); - if (elf_symbol) { - name = elf_symbol->name; - *offset = pc - map->start - elf_symbol->start; - } else if ((elf_symbol = find_symbol(symbol_table, pc)) != NULL) { - // Found the symbol using the absolute pc. - name = elf_symbol->name; - *offset = pc - elf_symbol->start; - } - free_symbol_table(symbol_table); - return name; - } - } - } - return ""; -} - -//------------------------------------------------------------------------- -// CorkscrewThread functions. -//------------------------------------------------------------------------- -CorkscrewThread::CorkscrewThread() { -} - -CorkscrewThread::~CorkscrewThread() { -} - -void CorkscrewThread::ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { - backtrace_frame_t cork_frames[MAX_BACKTRACE_FRAMES]; - CorkscrewMap* map = static_cast<CorkscrewMap*>(GetMap()); - ssize_t num_frames = unwind_backtrace_signal_arch( - siginfo, sigcontext, map->GetMapInfo(), cork_frames, - num_ignore_frames, MAX_BACKTRACE_FRAMES); - if (num_frames > 0) { - std::vector<backtrace_frame_data_t>* frames = GetFrames(); - frames->resize(num_frames); - size_t i = 0; - for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin(); - it != frames->end(); ++it, ++i) { - it->num = i; - it->pc = cork_frames[i].absolute_pc; - it->sp = cork_frames[i].stack_top; - it->stack_size = cork_frames[i].stack_size; - it->map = NULL; - it->func_offset = 0; - } - } -} - -//------------------------------------------------------------------------- -// CorkscrewPtrace functions. -//------------------------------------------------------------------------- -CorkscrewPtrace::CorkscrewPtrace() : ptrace_context_(NULL) { -} - -CorkscrewPtrace::~CorkscrewPtrace() { - if (ptrace_context_) { - free_ptrace_context(ptrace_context_); - ptrace_context_ = NULL; - } -} - -bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) { - ptrace_context_ = load_ptrace_context(Tid()); - - backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; - ssize_t num_frames = unwind_backtrace_ptrace( - Tid(), ptrace_context_, frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); - - return GenerateFrameData(frames, num_frames); -} - -std::string CorkscrewPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { - // Get information about a different process. - const map_info_t* map_info; - const symbol_t* symbol; - find_symbol_ptrace(ptrace_context_, pc, &map_info, &symbol); - char* symbol_name = NULL; - if (symbol) { - if (map_info) { - *offset = pc - map_info->start - symbol->start; - } - symbol_name = symbol->name; - return symbol_name; - } - - return ""; -} - -//------------------------------------------------------------------------- -// C++ object creation functions. -//------------------------------------------------------------------------- -Backtrace* CreateCurrentObj(BacktraceMap* map) { - return new BacktraceCurrent(new CorkscrewCurrent(), map); -} - -Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) { - return new BacktracePtrace(new CorkscrewPtrace(), pid, tid, map); -} - -Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) { - CorkscrewThread* thread_obj = new CorkscrewThread(); - return new BacktraceThread(thread_obj, thread_obj, tid, map); -} - -//------------------------------------------------------------------------- -// BacktraceMap create function. -//------------------------------------------------------------------------- -BacktraceMap* BacktraceMap::Create(pid_t pid) { - BacktraceMap* map = new CorkscrewMap(pid); - if (!map->Build()) { - delete map; - return NULL; - } - return map; -} diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h deleted file mode 100644 index 1633398..0000000 --- a/libbacktrace/Corkscrew.h +++ /dev/null @@ -1,82 +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_CORKSCREW_H -#define _LIBBACKTRACE_CORKSCREW_H - -#include <inttypes.h> - -#include <string> - -#include <backtrace/Backtrace.h> -#include <backtrace/BacktraceMap.h> - -#include <corkscrew/backtrace.h> - -#include "BacktraceImpl.h" -#include "BacktraceThread.h" - -class CorkscrewMap : public BacktraceMap { -public: - CorkscrewMap(pid_t pid); - virtual ~CorkscrewMap(); - - virtual bool Build(); - - map_info_t* GetMapInfo() { return map_info_; } - -private: - map_info_t* map_info_; -}; - -class CorkscrewCommon : public BacktraceImpl { -public: - bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames); -}; - -class CorkscrewCurrent : public CorkscrewCommon { -public: - CorkscrewCurrent(); - virtual ~CorkscrewCurrent(); - - virtual bool Unwind(size_t num_ignore_threads); - - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); -}; - -class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface { -public: - CorkscrewThread(); - virtual ~CorkscrewThread(); - - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); -}; - -class CorkscrewPtrace : public CorkscrewCommon { -public: - CorkscrewPtrace(); - virtual ~CorkscrewPtrace(); - - virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); - - virtual bool Unwind(size_t num_ignore_threads); - -private: - ptrace_context_t* ptrace_context_; -}; - -#endif // _LIBBACKTRACE_CORKSCREW_H diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp new file mode 100644 index 0000000..442383b --- /dev/null +++ b/libbacktrace/GetPss.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> + +// This is an extremely simplified version of libpagemap. + +#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1)) + +#define PAGEMAP_PRESENT(x) (_BITS(x, 63, 1)) +#define PAGEMAP_SWAPPED(x) (_BITS(x, 62, 1)) +#define PAGEMAP_SHIFT(x) (_BITS(x, 55, 6)) +#define PAGEMAP_PFN(x) (_BITS(x, 0, 55)) +#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50)) +#define PAGEMAP_SWAP_TYPE(x) (_BITS(x, 0, 5)) + +static bool ReadData(int fd, unsigned long place, uint64_t *data) { + if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) { + return false; + } + if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) { + return false; + } + return true; +} + +size_t GetPssBytes() { + FILE* maps = fopen("/proc/self/maps", "r"); + assert(maps != NULL); + + int pagecount_fd = open("/proc/kpagecount", O_RDONLY); + assert(pagecount_fd >= 0); + + int pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + assert(pagemap_fd >= 0); + + char line[4096]; + size_t total_pss = 0; + int pagesize = getpagesize(); + while (fgets(line, sizeof(line), maps)) { + uintptr_t start, end; + if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) { + total_pss = 0; + break; + } + for (size_t page = start/pagesize; page < end/pagesize; page++) { + uint64_t data; + if (ReadData(pagemap_fd, page, &data)) { + if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) { + uint64_t count; + if (ReadData(pagecount_fd, PAGEMAP_PFN(data), &count)) { + total_pss += (count >= 1) ? pagesize / count : 0; + } + } + } + } + } + + fclose(maps); + + close(pagecount_fd); + close(pagemap_fd); + + return total_pss; +} diff --git a/libbacktrace/GetPss.h b/libbacktrace/GetPss.h new file mode 100644 index 0000000..787c33d --- /dev/null +++ b/libbacktrace/GetPss.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBBACKTRACE_GET_PSS_H +#define _LIBBACKTRACE_GET_PSS_H + +size_t GetPssBytes(); + +#endif // _LIBBACKTRACE_GET_PSS_H diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp index 17b71b9..b176aaf 100644..100755 --- a/libbacktrace/UnwindCurrent.cpp +++ b/libbacktrace/UnwindCurrent.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <sys/types.h> +#include <ucontext.h> #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> @@ -24,30 +23,11 @@ #define UNW_LOCAL_ONLY #include <libunwind.h> +#include "BacktraceLog.h" +#include "BacktraceThread.h" #include "UnwindCurrent.h" #include "UnwindMap.h" -// Define the ucontext_t structures needed for each supported arch. -#if defined(__arm__) - // The current version of the <signal.h> doesn't define ucontext_t. - #include <asm/sigcontext.h> // Ensure 'struct sigcontext' is defined. - - // Machine context at the time a signal was raised. - typedef struct ucontext { - uint32_t uc_flags; - struct ucontext* uc_link; - stack_t uc_stack; - struct sigcontext uc_mcontext; - uint32_t uc_sigmask; - } ucontext_t; -#elif defined(__i386__) - #include <asm/sigcontext.h> - #include <asm/ucontext.h> - typedef struct ucontext ucontext_t; -#elif !defined(__mips__) && !defined(__aarch64__) - #error Unsupported architecture. -#endif - //------------------------------------------------------------------------- // UnwindCurrent functions. //------------------------------------------------------------------------- @@ -57,13 +37,43 @@ UnwindCurrent::UnwindCurrent() { UnwindCurrent::~UnwindCurrent() { } -bool UnwindCurrent::Unwind(size_t num_ignore_frames) { - int ret = unw_getcontext(&context_); - if (ret < 0) { - BACK_LOGW("unw_getcontext failed %d", ret); - return false; +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; + } } - return UnwindFromContext(num_ignore_frames, true); + else { + GetUnwContextFromUcontext(ucontext); + } + return UnwindFromContext(num_ignore_frames, false); +} + +void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) { + unw_tdep_context_t* unw_context = reinterpret_cast<unw_tdep_context_t*>(&context_); + +#if defined(__arm__) + unw_context->regs[0] = ucontext->uc_mcontext.arm_r0; + unw_context->regs[1] = ucontext->uc_mcontext.arm_r1; + unw_context->regs[2] = ucontext->uc_mcontext.arm_r2; + unw_context->regs[3] = ucontext->uc_mcontext.arm_r3; + unw_context->regs[4] = ucontext->uc_mcontext.arm_r4; + unw_context->regs[5] = ucontext->uc_mcontext.arm_r5; + unw_context->regs[6] = ucontext->uc_mcontext.arm_r6; + unw_context->regs[7] = ucontext->uc_mcontext.arm_r7; + unw_context->regs[8] = ucontext->uc_mcontext.arm_r8; + unw_context->regs[9] = ucontext->uc_mcontext.arm_r9; + unw_context->regs[10] = ucontext->uc_mcontext.arm_r10; + unw_context->regs[11] = ucontext->uc_mcontext.arm_fp; + unw_context->regs[12] = ucontext->uc_mcontext.arm_ip; + unw_context->regs[13] = ucontext->uc_mcontext.arm_sp; + unw_context->regs[14] = ucontext->uc_mcontext.arm_lr; + unw_context->regs[15] = ucontext->uc_mcontext.arm_pc; +#else + unw_context->uc_mcontext = ucontext->uc_mcontext; +#endif } std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { @@ -78,12 +88,14 @@ std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { return ""; } -bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { +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_); if (ret < 0) { - BACK_LOGW("unw_init_local failed %d", ret); + if (!within_handler) { + BACK_LOGW("unw_init_local failed %d", ret); + } delete cursor; return false; } @@ -95,13 +107,17 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { unw_word_t pc; ret = unw_get_reg(cursor, UNW_REG_IP, &pc); if (ret < 0) { - BACK_LOGW("Failed to read IP %d", ret); + if (!within_handler) { + BACK_LOGW("Failed to read IP %d", ret); + } break; } unw_word_t sp; ret = unw_get_reg(cursor, UNW_REG_SP, &sp); if (ret < 0) { - BACK_LOGW("Failed to read SP %d", ret); + if (!within_handler) { + BACK_LOGW("Failed to read SP %d", ret); + } break; } @@ -119,7 +135,7 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { prev->stack_size = frame->sp - prev->sp; } - if (resolve) { + if (!within_handler) { frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); frame->map = FindMap(frame->pc); } else { @@ -137,47 +153,6 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { return true; } -void UnwindCurrent::ExtractContext(void* sigcontext) { - unw_tdep_context_t* context = reinterpret_cast<unw_tdep_context_t*>(&context_); - const ucontext_t* uc = reinterpret_cast<const ucontext_t*>(sigcontext); - -#if defined(__arm__) - context->regs[0] = uc->uc_mcontext.arm_r0; - context->regs[1] = uc->uc_mcontext.arm_r1; - context->regs[2] = uc->uc_mcontext.arm_r2; - context->regs[3] = uc->uc_mcontext.arm_r3; - context->regs[4] = uc->uc_mcontext.arm_r4; - context->regs[5] = uc->uc_mcontext.arm_r5; - context->regs[6] = uc->uc_mcontext.arm_r6; - context->regs[7] = uc->uc_mcontext.arm_r7; - context->regs[8] = uc->uc_mcontext.arm_r8; - context->regs[9] = uc->uc_mcontext.arm_r9; - context->regs[10] = uc->uc_mcontext.arm_r10; - context->regs[11] = uc->uc_mcontext.arm_fp; - context->regs[12] = uc->uc_mcontext.arm_ip; - context->regs[13] = uc->uc_mcontext.arm_sp; - context->regs[14] = uc->uc_mcontext.arm_lr; - context->regs[15] = uc->uc_mcontext.arm_pc; -#elif defined(__mips__) || defined(__i386__) - context->uc_mcontext = uc->uc_mcontext; -#endif -} - -//------------------------------------------------------------------------- -// UnwindThread functions. -//------------------------------------------------------------------------- -UnwindThread::UnwindThread() { -} - -UnwindThread::~UnwindThread() { -} - -void UnwindThread::ThreadUnwind( - siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) { - ExtractContext(sigcontext); - UnwindFromContext(num_ignore_frames, false); -} - //------------------------------------------------------------------------- // C++ object creation function. //------------------------------------------------------------------------- @@ -186,6 +161,5 @@ Backtrace* CreateCurrentObj(BacktraceMap* map) { } Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) { - UnwindThread* thread_obj = new UnwindThread(); - return new BacktraceThread(thread_obj, thread_obj, tid, map); + return new BacktraceThread(new UnwindCurrent(), tid, map); } diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h index acce110..2375e6e 100644 --- a/libbacktrace/UnwindCurrent.h +++ b/libbacktrace/UnwindCurrent.h @@ -20,7 +20,6 @@ #include <string> #include "BacktraceImpl.h" -#include "BacktraceThread.h" #define UNW_LOCAL_ONLY #include <libunwind.h> @@ -30,25 +29,16 @@ public: UnwindCurrent(); virtual ~UnwindCurrent(); - virtual bool Unwind(size_t num_ignore_frames); + virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext); virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); - bool UnwindFromContext(size_t num_ignore_frames, bool resolve); + bool UnwindFromContext(size_t num_ignore_frames, bool within_handler); - void ExtractContext(void* sigcontext); + void GetUnwContextFromUcontext(const ucontext_t* context); protected: unw_context_t context_; }; -class UnwindThread : public UnwindCurrent, public BacktraceThreadInterface { -public: - UnwindThread(); - virtual ~UnwindThread(); - - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); -}; - #endif // _LIBBACKTRACE_UNWIND_CURRENT_H diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp index 03bb192..4f9831b 100644 --- a/libbacktrace/UnwindMap.cpp +++ b/libbacktrace/UnwindMap.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <pthread.h> +#include <stdlib.h> #include <sys/types.h> #include <unistd.h> @@ -24,6 +23,7 @@ #include <libunwind.h> +#include "BacktraceLog.h" #include "UnwindMap.h" //------------------------------------------------------------------------- @@ -32,57 +32,21 @@ // only update the local address space once, and keep a reference count // of maps using the same map cursor. //------------------------------------------------------------------------- -static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER; -static unw_map_cursor_t g_map_cursor; -static int g_map_references = 0; - UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) { - map_cursor_.map_list = NULL; } UnwindMap::~UnwindMap() { - if (pid_ == getpid()) { - pthread_mutex_lock(&g_map_mutex); - if (--g_map_references == 0) { - // Clear the local address space map. - unw_map_set(unw_local_addr_space, NULL); - unw_map_cursor_destroy(&map_cursor_); - } - pthread_mutex_unlock(&g_map_mutex); - } else { - unw_map_cursor_destroy(&map_cursor_); - } + unw_map_cursor_destroy(&map_cursor_); + unw_map_cursor_clear(&map_cursor_); } -bool UnwindMap::Build() { - bool return_value = true; - if (pid_ == getpid()) { - pthread_mutex_lock(&g_map_mutex); - if (g_map_references == 0) { - return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); - if (return_value) { - // Set the local address space to this cursor map. - unw_map_set(unw_local_addr_space, &map_cursor_); - g_map_references = 1; - g_map_cursor = map_cursor_; - } - } else { - g_map_references++; - map_cursor_ = g_map_cursor; - } - pthread_mutex_unlock(&g_map_mutex); - } else { - return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0); - } - - if (!return_value) - return false; - +bool UnwindMap::GenerateMap() { // Use the map_cursor information to construct the BacktraceMap data // rather than reparsing /proc/self/maps. unw_map_cursor_reset(&map_cursor_); + unw_map_t unw_map; - while (unw_map_cursor_get(&map_cursor_, &unw_map)) { + while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) { backtrace_map_t map; map.start = unw_map.start; @@ -97,11 +61,82 @@ bool UnwindMap::Build() { return true; } +bool UnwindMap::Build() { + return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap(); +} + +UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) { +} + +UnwindMapLocal::~UnwindMapLocal() { + if (map_created_) { + unw_map_local_destroy(); + unw_map_cursor_clear(&map_cursor_); + } +} + +bool UnwindMapLocal::GenerateMap() { + // It's possible for the map to be regenerated while this loop is occurring. + // If that happens, get the map again, but only try at most three times + // before giving up. + for (int i = 0; i < 3; i++) { + maps_.clear(); + + unw_map_local_cursor_get(&map_cursor_); + + unw_map_t unw_map; + int ret; + while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) { + backtrace_map_t map; + + map.start = unw_map.start; + map.end = unw_map.end; + map.flags = unw_map.flags; + map.name = unw_map.path; + + free(unw_map.path); + + // The maps are in descending order, but we want them in ascending order. + maps_.push_front(map); + } + // Check to see if the map changed while getting the data. + if (ret != -UNW_EINVAL) { + return true; + } + } + + BACK_LOGW("Unable to generate the map."); + return false; +} + +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) { + // 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); + } + } + } + return map; +} + //------------------------------------------------------------------------- // BacktraceMap create function. //------------------------------------------------------------------------- BacktraceMap* BacktraceMap::Create(pid_t pid) { - BacktraceMap* map = new UnwindMap(pid); + BacktraceMap* map; + if (pid == getpid()) { + map = new UnwindMapLocal(); + } else { + map = new UnwindMap(pid); + } if (!map->Build()) { delete map; return NULL; diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h index 5a874e8..2fdb29f 100644 --- a/libbacktrace/UnwindMap.h +++ b/libbacktrace/UnwindMap.h @@ -32,8 +32,25 @@ public: unw_map_cursor_t* GetMapCursor() { return &map_cursor_; } -private: +protected: + virtual bool GenerateMap(); + unw_map_cursor_t map_cursor_; }; +class UnwindMapLocal : public UnwindMap { +public: + UnwindMapLocal(); + virtual ~UnwindMapLocal(); + + virtual bool Build(); + + virtual const backtrace_map_t* Find(uintptr_t addr); + +protected: + virtual bool GenerateMap(); + + bool map_created_; +}; + #endif // _LIBBACKTRACE_UNWIND_MAP_H diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp index 732dae8..7ba8775 100644 --- a/libbacktrace/UnwindPtrace.cpp +++ b/libbacktrace/UnwindPtrace.cpp @@ -14,17 +14,17 @@ * limitations under the License. */ -#define LOG_TAG "libbacktrace" - #include <backtrace/Backtrace.h> #include <backtrace/BacktraceMap.h> #include <sys/types.h> #include <string.h> +#include <ucontext.h> #include <libunwind.h> #include <libunwind-ptrace.h> +#include "BacktraceLog.h" #include "UnwindMap.h" #include "UnwindPtrace.h" @@ -46,7 +46,12 @@ UnwindPtrace::~UnwindPtrace() { } } -bool UnwindPtrace::Unwind(size_t num_ignore_frames) { +bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { + if (ucontext) { + BACK_LOGW("Unwinding from a specified context not supported yet."); + return false; + } + addr_space_ = unw_create_addr_space(&_UPT_accessors, 0); if (!addr_space_) { BACK_LOGW("unw_create_addr_space failed."); diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h index 1e82117..2fb7967 100644 --- a/libbacktrace/UnwindPtrace.h +++ b/libbacktrace/UnwindPtrace.h @@ -28,7 +28,7 @@ public: UnwindPtrace(); virtual ~UnwindPtrace(); - virtual bool Unwind(size_t num_ignore_frames); + virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext); virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index 23eaf92..ed6b211 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp @@ -16,9 +16,10 @@ #include <dirent.h> #include <errno.h> +#include <inttypes.h> #include <pthread.h> #include <signal.h> -#include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -32,9 +33,13 @@ #include <backtrace/BacktraceMap.h> #include <UniquePtr.h> +// For the THREAD_SIGNAL definition. +#include "BacktraceThread.h" + #include <cutils/atomic.h> #include <gtest/gtest.h> +#include <algorithm> #include <vector> #include "thread_utils.h" @@ -287,7 +292,7 @@ TEST(libbacktrace, ptrace_trace) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump); @@ -300,7 +305,7 @@ TEST(libbacktrace, ptrace_trace_shared_map) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump); @@ -314,7 +319,7 @@ TEST(libbacktrace, ptrace_max_trace) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump); @@ -339,7 +344,7 @@ TEST(libbacktrace, ptrace_ignore_frames) { pid_t pid; if ((pid = fork()) == 0) { ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames); @@ -384,7 +389,7 @@ TEST(libbacktrace, ptrace_threads) { ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0); } ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); - exit(1); + _exit(1); } // Check to see that all of the threads are running before unwinding. @@ -458,9 +463,15 @@ TEST(libbacktrace, thread_level_trace) { // Wait up to 2 seconds for the tid to be set. ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + // Make sure that the thread signal used is not visible when compiled for + // the target. +#if !defined(__GLIBC__) + ASSERT_LT(THREAD_SIGNAL, SIGRTMIN); +#endif + // Save the current signal action and make sure it is restored afterwards. struct sigaction cur_action; - ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0); + ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &cur_action) == 0); UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid)); ASSERT_TRUE(backtrace.get() != NULL); @@ -473,7 +484,7 @@ TEST(libbacktrace, thread_level_trace) { // Verify that the old action was restored. struct sigaction new_action; - ASSERT_TRUE(sigaction(SIGURG, NULL, &new_action) == 0); + ASSERT_TRUE(sigaction(THREAD_SIGNAL, NULL, &new_action) == 0); EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction); EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags); } @@ -604,6 +615,49 @@ TEST(libbacktrace, thread_multiple_dump) { } } +TEST(libbacktrace, thread_multiple_dump_same_thread) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + thread_t runner; + runner.tid = 0; + runner.state = 0; + ASSERT_TRUE(pthread_create(&runner.threadId, &attr, ThreadMaxRun, &runner) == 0); + + // Wait for tids to be set. + ASSERT_TRUE(WaitForNonZero(&runner.state, 10)); + + // Start all of the dumpers at once, they will spin until they are signalled + // to begin their dump run. + int32_t dump_now = 0; + // Dump the same thread NUM_THREADS simultaneously. + std::vector<dump_thread_t> dumpers(NUM_THREADS); + for (size_t i = 0; i < NUM_THREADS; i++) { + dumpers[i].thread.tid = runner.tid; + dumpers[i].thread.state = 0; + dumpers[i].done = 0; + dumpers[i].now = &dump_now; + + ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0); + } + + // Start all of the dumpers going at once. + android_atomic_acquire_store(1, &dump_now); + + for (size_t i = 0; i < NUM_THREADS; i++) { + ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 100)); + + ASSERT_TRUE(dumpers[i].backtrace != NULL); + VerifyMaxDump(dumpers[i].backtrace); + + delete dumpers[i].backtrace; + dumpers[i].backtrace = NULL; + } + + // Tell the runner thread to exit its infinite loop. + android_atomic_acquire_store(0, &runner.state); +} + // This test is for UnwindMaps that should share the same map cursor when // multiple maps are created for the current process at the same time. TEST(libbacktrace, simultaneous_maps) { @@ -693,3 +747,136 @@ TEST(libbacktrace, format_test) { #endif backtrace->FormatFrameData(&frame)); } + +struct map_test_t { + uintptr_t start; + uintptr_t end; +}; + +bool map_sort(map_test_t i, map_test_t j) { + return i.start < j.start; +} + +static 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); + std::vector<map_test_t> test_maps; + while (fgets(buffer, sizeof(buffer), map_file)) { + map_test_t map; + ASSERT_EQ(2, sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " ", &map.start, &map.end)); + test_maps.push_back(map); + } + fclose(map_file); + std::sort(test_maps.begin(), test_maps.end(), map_sort); + + UniquePtr<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(); + for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { + ASSERT_TRUE(test_it != test_maps.end()); + ASSERT_EQ(test_it->start, it->start); + ASSERT_EQ(test_it->end, it->end); + ++test_it; + } + ASSERT_TRUE(test_it == test_maps.end()); +} + +TEST(libbacktrace, verify_map_remote) { + pid_t pid; + + if ((pid = fork()) == 0) { + while (true) { + } + _exit(0); + } + ASSERT_LT(0, pid); + + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + + // Wait for the process to get to a stopping point. + WaitForStop(pid); + + // The maps should match exactly since the forked process has been paused. + VerifyMap(pid); + + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, NULL, 0), pid); +} + +#if defined(ENABLE_PSS_TESTS) +#include "GetPss.h" + +#define MAX_LEAK_BYTES 32*1024UL + +static 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->Unwind(0)); + delete backtrace; + } + size_t stable_pss = GetPssBytes(); + + // 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->Unwind(0)); + delete backtrace; + } + size_t new_pss = GetPssBytes(); + 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); +} + +TEST(libbacktrace, check_for_leak_local) { + CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD); +} + +TEST(libbacktrace, check_for_leak_local_thread) { + thread_t thread_data = { 0, 0, 0 }; + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, NULL, ThreadLevelRun, &thread_data) == 0); + + // Wait up to 2 seconds for the tid to be set. + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + + CheckForLeak(BACKTRACE_CURRENT_PROCESS, thread_data.tid); + + // Tell the thread to exit its infinite loop. + android_atomic_acquire_store(0, &thread_data.state); + + ASSERT_TRUE(pthread_join(thread, NULL) == 0); +} + +TEST(libbacktrace, check_for_leak_remote) { + pid_t pid; + + if ((pid = fork()) == 0) { + while (true) { + } + _exit(0); + } + ASSERT_LT(0, pid); + + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + + // Wait for the process to get to a stopping point. + WaitForStop(pid); + + CheckForLeak(pid, BACKTRACE_CURRENT_THREAD); + + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + kill(pid, SIGKILL); + ASSERT_EQ(waitpid(pid, NULL, 0), pid); +} +#endif |