am 7f80f37d: (-s ours) DO NOT MERGE Restore libcorkscrew unwinding.
* commit '7f80f37da0ce0141d241bab624a7c77aae250ddd':
DO NOT MERGE Restore libcorkscrew unwinding.
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
old mode 100644
new mode 100755
index 80cd861..c321369
--- 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_cppflags := \
-std=gnu++11 \
-common_shared_libs := \
+build_host := false
+ifeq ($(HOST_OS),linux)
+ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64))
+build_host := true
+endif
+endif
+
+#-------------------------------------------------------------------------
+# The libbacktrace library.
+#-------------------------------------------------------------------------
+libbacktrace_src_files := \
+ BacktraceImpl.cpp \
+ BacktraceMap.cpp \
+ BacktraceThread.cpp \
+ thread_utils.c \
+
+libbacktrace_shared_libraries_target := \
libcutils \
libgccdemangle \
- liblog \
-# To enable using libunwind on each arch, add it to this list.
-libunwind_architectures :=
-
-ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures)))
-
-#----------------------------------------------------------------------------
-# The native libbacktrace library with libunwind.
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- $(common_src) \
+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) \
-
-LOCAL_CONLYFLAGS += \
- $(common_conlyflags) \
-
-LOCAL_CPPFLAGS += \
- $(common_cppflags) \
-
-LOCAL_MODULE := libbacktrace
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_C_INCLUDES := \
- $(common_c_includes) \
- system/core/libcorkscrew \
-
-LOCAL_SHARED_LIBRARIES := \
- $(common_shared_libs) \
- libcorkscrew \
- libdl \
-
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(LOCAL_PATH)/Android.mk
-
-include external/stlport/libstlport.mk
-
-include $(BUILD_SHARED_LIBRARY)
-
-endif
-
-#----------------------------------------------------------------------------
-# libbacktrace test library, all optimizations turned off
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libbacktrace_test
-LOCAL_MODULE_FLAGS := debug
-
-LOCAL_SRC_FILES := \
- backtrace_testlib.c
-
-LOCAL_CFLAGS += \
- -std=gnu99 \
- -O0 \
-
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(LOCAL_PATH)/Android.mk
-
-include $(BUILD_SHARED_LIBRARY)
-
-#----------------------------------------------------------------------------
-# libbacktrace test executable
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := backtrace_test
-LOCAL_MODULE_FLAGS := debug
-
-LOCAL_SRC_FILES := \
- backtrace_test.cpp \
- thread_utils.c \
-
-LOCAL_CFLAGS += \
- $(common_cflags) \
- -fno-builtin \
- -fstack-protector-all \
- -O0 \
- -g \
- -DGTEST_OS_LINUX_ANDROID \
- -DGTEST_HAS_STD_STRING \
-
-ifeq ($(TARGET_ARCH),arm64)
- $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain)
- LOCAL_CFLAGS += -fno-stack-protector
-endif # arm64
-
-LOCAL_CONLYFLAGS += \
- $(common_conlyflags) \
-
-LOCAL_CPPFLAGS += \
- $(common_cppflags) \
-
-LOCAL_SHARED_LIBRARIES += \
- libcutils \
- libbacktrace_test \
- libbacktrace \
-
-LOCAL_LDLIBS := \
- -lpthread \
-
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(LOCAL_PATH)/Android.mk
-
-include $(BUILD_NATIVE_TEST)
-
-#----------------------------------------------------------------------------
-# Only x86 host versions of libbacktrace supported.
-#----------------------------------------------------------------------------
-ifeq ($(HOST_ARCH),x86)
-
-#----------------------------------------------------------------------------
-# The host libbacktrace library using libcorkscrew
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-
-LOCAL_CFLAGS += \
- $(common_cflags) \
-
-LOCAL_CONLYFLAGS += \
- $(common_conlyflags) \
-
-LOCAL_C_INCLUDES := \
- $(common_c_includes) \
-
-LOCAL_SHARED_LIBRARIES := \
- libgccdemangle \
+libbacktrace_shared_libraries_host := \
liblog \
-LOCAL_MODULE := libbacktrace
-LOCAL_MODULE_TAGS := optional
+libbacktrace_static_libraries_host := \
+ libcutils \
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(LOCAL_PATH)/Android.mk
+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
-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 += \
+# Don't build for unbundled branches
+ifeq (,$(TARGET_BUILD_APPS))
+#-------------------------------------------------------------------------
+# The libbacktrace library (libc++)
+#-------------------------------------------------------------------------
+libbacktrace_libc++_src_files := \
+ BacktraceImpl.cpp \
BacktraceMap.cpp \
-
-endif
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-#----------------------------------------------------------------------------
-# The host test is only supported on linux.
-#----------------------------------------------------------------------------
-ifeq ($(HOST_OS),linux)
-
-#----------------------------------------------------------------------------
-# libbacktrace host test library, all optimizations turned off
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libbacktrace_test
-LOCAL_MODULE_FLAGS := debug
-
-LOCAL_SRC_FILES := \
- backtrace_testlib.c
-
-LOCAL_CFLAGS += \
- -std=gnu99 \
- -O0 \
-
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- $(LOCAL_PATH)/Android.mk
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-#----------------------------------------------------------------------------
-# libbacktrace host test executable
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := backtrace_test
-LOCAL_MODULE_FLAGS := debug
-
-LOCAL_SRC_FILES := \
- backtrace_test.cpp \
+ BacktraceThread.cpp \
thread_utils.c \
-LOCAL_CFLAGS += \
- $(common_cflags) \
+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++_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.
+#-------------------------------------------------------------------------
+libbacktrace_test_cflags := \
+ -O0 \
+
+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_HAS_STD_STRING \
-LOCAL_SHARED_LIBRARIES := \
+backtrace_test_cflags_target := \
+ -DENABLE_PSS_TESTS \
+
+backtrace_test_src_files := \
+ backtrace_test.cpp \
+ GetPss.cpp \
+ thread_utils.c \
+
+backtrace_test_ldlibs_host := \
+ -lpthread \
+ -lrt \
+
+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_HOST_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
-endif # HOST_OS == linux
+#----------------------------------------------------------------------------
+# Special truncated libbacktrace library for mac.
+#----------------------------------------------------------------------------
+ifeq ($(HOST_OS),darwin)
-endif # HOST_ARCH == x86
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbacktrace
+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/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 @@
}
}
-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 @@
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() {
}
-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() {
}
-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 @@
// 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
old mode 100644
new mode 100755
index af014d5..d34ad05
--- 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 @@
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 @@
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..f38e484 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -93,7 +93,8 @@
}
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 @@
#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) {
@@ -136,7 +137,7 @@
#if defined(__APPLE__)
// Corkscrew and libunwind don't compile on the mac, so create a generic
// map object.
-BacktraceMap* BacktraceMap::Create(pid_t pid) {
+BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
BacktraceMap* map = new BacktraceMap(pid);
if (!map->Build()) {
delete map;
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::list_ = NULL;
+pthread_mutex_t ThreadEntry::list_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) {
+// 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;
+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 {
- if (next) {
- next->prev = prev;
- }
- prev->next = next;
+ entry->ref_count_++;
}
- pthread_mutex_unlock(&g_entry_mutex);
-
- next = NULL;
- prev = NULL;
-}
-
-ThreadEntry* ThreadEntry::AddThreadToUnwind(
- BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) {
- ThreadEntry* entry = new ThreadEntry(intf, pid, tid, num_ignore_frames);
-
- pthread_mutex_lock(&g_entry_mutex);
- ThreadEntry* cur_entry = g_list;
- while (cur_entry != NULL) {
- if (cur_entry->Match(pid, tid)) {
- // There is already an entry for this pid/tid, this is bad.
- BACK_LOGW("Entry for pid %d tid %d already exists.", pid, tid);
-
- pthread_mutex_unlock(&g_entry_mutex);
- return NULL;
- }
- cur_entry = cur_entry->next;
- }
-
- // Add the entry to the list.
- entry->next = g_list;
- if (g_list) {
- g_list->prev = entry;
- }
- g_list = entry;
- pthread_mutex_unlock(&g_entry_mutex);
+ 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;
+}
+
+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));
+ }
+}
+
+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::TriggerUnwindOnThread(ThreadEntry* entry) {
- entry->state = STATE_WAITING;
-
- if (tgkill(Pid(), Tid(), SIGURG) != 0) {
- BACK_LOGW("tgkill failed %s", strerror(errno));
- return false;
- }
-
- // Allow up to ten seconds for the dump to start.
- int wait_millis = 10000;
- int32_t state;
- while (true) {
- state = android_atomic_acquire_load(&entry->state);
- if (state != STATE_WAITING) {
- break;
- }
- if (wait_millis--) {
- usleep(1000);
- } else {
- break;
- }
- }
-
- bool cancelled = false;
- if (state == STATE_WAITING) {
- if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state) == 0) {
- BACK_LOGW("Cancelled dump of thread %d", entry->tid);
- state = STATE_CANCEL;
- cancelled = true;
- } else {
- state = android_atomic_acquire_load(&entry->state);
- }
- }
-
- // Wait for at most ten seconds for the cancel or dump to finish.
- wait_millis = 10000;
- while (android_atomic_acquire_load(&entry->state) != STATE_DONE) {
- if (wait_millis--) {
- usleep(1000);
- } else {
- BACK_LOGW("Didn't finish thread unwind in 60 seconds.");
- break;
- }
- }
- return !cancelled;
-}
-
-bool BacktraceThread::Unwind(size_t num_ignore_frames) {
- ThreadEntry* entry = ThreadEntry::AddThreadToUnwind(
- thread_intf_, Pid(), Tid(), num_ignore_frames);
- if (!entry) {
- return false;
+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.
- 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));
- }
+ 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);
- } else {
- BACK_LOGW("unable to acquire sigaction mutex.");
+ return false;
}
- if (retval) {
- FinishUnwind();
+ 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;
}
- delete entry;
- return retval;
+ // Wait for the thread to get the ucontext.
+ entry->Wait(0);
+
+ // 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/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);
+ static void Remove(ThreadEntry* entry);
+
+ void Wake();
+
+ void Wait(int);
+
+ void CopyUcontextFromSigcontext(void*);
+
+ inline void Lock() {
+ pthread_mutex_lock(&mutex_);
+ // Reset the futex value in case of multiple unwinds of the same thread.
+ futex_ = 0;
+ }
+
+ inline void Unlock() {
+ pthread_mutex_unlock(&mutex_);
+ }
+
+ inline ucontext_t* GetUcontext() { return &ucontext_; }
+
+private:
+ ThreadEntry(pid_t pid, pid_t tid);
~ThreadEntry();
- bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid && chk_tid == tid); }
+ bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid_ && chk_tid == tid_); }
- static ThreadEntry* AddThreadToUnwind(
- BacktraceThreadInterface* thread_intf, pid_t pid, pid_t tid,
- size_t num_ignored_frames);
+ pid_t pid_;
+ pid_t tid_;
+ int futex_;
+ int ref_count_;
+ pthread_mutex_t mutex_;
+ ThreadEntry* next_;
+ ThreadEntry* prev_;
+ ucontext_t ucontext_;
- BacktraceThreadInterface* thread_intf;
- pid_t pid;
- pid_t tid;
- ThreadEntry* next;
- ThreadEntry* prev;
- int32_t state;
- int num_ignore_frames;
-};
-
-// Interface class that does not contain any local storage, only defines
-// virtual functions to be defined by subclasses.
-class BacktraceThreadInterface {
-public:
- virtual ~BacktraceThreadInterface() { }
-
- virtual void ThreadUnwind(
- siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0;
+ static ThreadEntry* list_;
+ static pthread_mutex_t list_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);
+ BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map);
virtual ~BacktraceThread();
- virtual bool Unwind(size_t num_ignore_frames);
-
- virtual void ThreadUnwind(
- siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) {
- thread_intf_->ThreadUnwind(siginfo, sigcontext, num_ignore_frames);
- }
-
-private:
- virtual bool TriggerUnwindOnThread(ThreadEntry* entry);
-
- virtual void FinishUnwind();
-
- BacktraceThreadInterface* thread_intf_;
+ 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
old mode 100644
new mode 100755
index 17b71b9..b176aaf
--- 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() {
}
-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 @@
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 @@
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 @@
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 @@
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* 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 @@
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..387d768 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,86 @@
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* BacktraceMap::Create(pid_t pid, bool uncached) {
+ BacktraceMap* map;
+
+ if (uncached) {
+ // Force use of the base class to parse the maps when this call is made.
+ map = new BacktraceMap(pid);
+ } else 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 @@
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 @@
}
}
-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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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_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 @@
#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
diff --git a/libbacktrace/map_info.c b/libbacktrace/map_info.c
new file mode 100644
index 0000000..073b24a
--- /dev/null
+++ b/libbacktrace/map_info.c
@@ -0,0 +1,173 @@
+/*
+ * 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <log/log.h>
+#include <sys/time.h>
+
+#include <backtrace/backtrace.h>
+
+#if defined(__APPLE__)
+
+// Mac OS vmmap(1) output:
+// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+static backtrace_map_info_t* parse_vmmap_line(const char* line) {
+ unsigned long int start;
+ unsigned long int end;
+ char permissions[4];
+ int name_pos;
+ if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n",
+ &start, &end, permissions, &name_pos) != 3) {
+ return NULL;
+ }
+
+ const char* name = line + name_pos;
+ size_t name_len = strlen(name);
+
+ backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len);
+ if (mi != NULL) {
+ mi->start = start;
+ mi->end = end;
+ mi->is_readable = permissions[0] == 'r';
+ mi->is_writable = permissions[1] == 'w';
+ mi->is_executable = permissions[2] == 'x';
+ memcpy(mi->name, name, name_len);
+ mi->name[name_len - 1] = '\0';
+ ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
+ "is_readable=%d, is_writable=%d is_executable=%d, name=%s",
+ mi->start, mi->end,
+ mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
+ }
+ return mi;
+}
+
+backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid) {
+ char cmd[1024];
+ if (pid < 0) {
+ pid = getpid();
+ }
+ snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid);
+ FILE* fp = popen(cmd, "r");
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ char line[1024];
+ backtrace_map_info_t* milist = NULL;
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ backtrace_map_info_t* mi = parse_vmmap_line(line);
+ if (mi != NULL) {
+ mi->next = milist;
+ milist = mi;
+ }
+ }
+ pclose(fp);
+ return milist;
+}
+
+#else
+
+// Linux /proc/<pid>/maps lines:
+// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+static backtrace_map_info_t* parse_maps_line(const char* line)
+{
+ unsigned long int start;
+ unsigned long int end;
+ char permissions[5];
+ int name_pos;
+ if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end,
+ permissions, &name_pos) != 3) {
+ return NULL;
+ }
+
+ while (isspace(line[name_pos])) {
+ name_pos += 1;
+ }
+ const char* name = line + name_pos;
+ size_t name_len = strlen(name);
+ if (name_len && name[name_len - 1] == '\n') {
+ name_len -= 1;
+ }
+
+ backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len + 1);
+ if (mi) {
+ mi->start = start;
+ mi->end = end;
+ mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r';
+ mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w';
+ mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x';
+ memcpy(mi->name, name, name_len);
+ mi->name[name_len] = '\0';
+ ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
+ "is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
+ mi->start, mi->end,
+ mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
+ }
+ return mi;
+}
+
+backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid) {
+ char path[PATH_MAX];
+ char line[1024];
+ FILE* fp;
+ backtrace_map_info_t* milist = NULL;
+
+ if (tid < 0) {
+ tid = getpid();
+ }
+ snprintf(path, PATH_MAX, "/proc/%d/maps", tid);
+ fp = fopen(path, "r");
+ if (fp) {
+ while(fgets(line, sizeof(line), fp)) {
+ backtrace_map_info_t* mi = parse_maps_line(line);
+ if (mi) {
+ mi->next = milist;
+ milist = mi;
+ }
+ }
+ fclose(fp);
+ }
+ return milist;
+}
+
+#endif
+
+void backtrace_destroy_map_info_list(backtrace_map_info_t* milist) {
+ while (milist) {
+ backtrace_map_info_t* next = milist->next;
+ free(milist);
+ milist = next;
+ }
+}
+
+const backtrace_map_info_t* backtrace_find_map_info(
+ const backtrace_map_info_t* milist, uintptr_t addr) {
+ const backtrace_map_info_t* mi = milist;
+ while (mi && !(addr >= mi->start && addr < mi->end)) {
+ mi = mi->next;
+ }
+ return mi;
+}