summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--debuggerd/backtrace.c14
-rw-r--r--debuggerd/backtrace.h2
-rw-r--r--debuggerd/tombstone.c68
-rw-r--r--include/backtrace/Backtrace.h85
-rw-r--r--include/backtrace/backtrace.h60
-rw-r--r--libbacktrace/Android.mk179
-rw-r--r--libbacktrace/Backtrace.cpp312
-rw-r--r--libbacktrace/Backtrace.h62
-rw-r--r--libbacktrace/BacktraceThread.cpp224
-rw-r--r--libbacktrace/BacktraceThread.h93
-rw-r--r--libbacktrace/Corkscrew.cpp200
-rw-r--r--libbacktrace/Corkscrew.h74
-rw-r--r--libbacktrace/UnwindCurrent.cpp222
-rw-r--r--libbacktrace/UnwindCurrent.h56
-rw-r--r--libbacktrace/UnwindPtrace.cpp136
-rw-r--r--libbacktrace/UnwindPtrace.h (renamed from libbacktrace/unwind.h)28
-rw-r--r--libbacktrace/backtrace_test.c239
-rw-r--r--libbacktrace/backtrace_test.cpp660
-rw-r--r--libbacktrace/backtrace_testlib.c25
-rw-r--r--libbacktrace/common.c113
-rw-r--r--libbacktrace/corkscrew.c130
-rw-r--r--libbacktrace/demangle.h25
-rw-r--r--libbacktrace/stubs.c53
-rw-r--r--libbacktrace/thread_utils.c (renamed from libbacktrace/demangle.c)35
-rw-r--r--libbacktrace/thread_utils.h (renamed from libbacktrace/common.h)17
-rw-r--r--libbacktrace/unwind.c57
-rw-r--r--libbacktrace/unwind_local.c130
-rw-r--r--libbacktrace/unwind_remote.c158
28 files changed, 2398 insertions, 1059 deletions
diff --git a/debuggerd/backtrace.c b/debuggerd/backtrace.c
index 6f82792..aa7a3c2 100644
--- a/debuggerd/backtrace.c
+++ b/debuggerd/backtrace.c
@@ -89,12 +89,12 @@ static void dump_thread(log_t* log, pid_t tid, bool attached,
wait_for_stop(tid, total_sleep_time_usec);
- backtrace_t backtrace;
- if (!backtrace_get_data(&backtrace, tid)) {
+ backtrace_context_t context;
+ if (!backtrace_create_context(&context, tid, -1, 0)) {
_LOG(log, SCOPE_AT_FAULT, "Could not create backtrace context.\n");
} else {
- dump_backtrace_to_log(&backtrace, log, SCOPE_AT_FAULT, " ");
- backtrace_free_data(&backtrace);
+ dump_backtrace_to_log(&context, log, SCOPE_AT_FAULT, " ");
+ backtrace_destroy_context(&context);
}
if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
@@ -137,11 +137,11 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
dump_process_footer(&log, pid);
}
-void dump_backtrace_to_log(const backtrace_t* backtrace, log_t* log,
+void dump_backtrace_to_log(const backtrace_context_t* context, log_t* log,
int scope_flags, const char* prefix) {
char buf[512];
- for (size_t i = 0; i < backtrace->num_frames; i++) {
- backtrace_format_frame_data(&backtrace->frames[i], i, buf, sizeof(buf));
+ for (size_t i = 0; i < context->backtrace->num_frames; i++) {
+ backtrace_format_frame_data(context, i, buf, sizeof(buf));
_LOG(log, scope_flags, "%s%s\n", prefix, buf);
}
}
diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h
index 9d61e6f..54a60b2 100644
--- a/debuggerd/backtrace.h
+++ b/debuggerd/backtrace.h
@@ -31,7 +31,7 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
int* total_sleep_time_usec);
/* Dumps the backtrace in the backtrace data structure to the log. */
-void dump_backtrace_to_log(const backtrace_t* backtrace, log_t* log,
+void dump_backtrace_to_log(const backtrace_context_t* context, log_t* log,
int scope_flags, const char* prefix);
#endif // _DEBUGGERD_BACKTRACE_H
diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c
index 24debf4..48d7aa9 100644
--- a/debuggerd/tombstone.c
+++ b/debuggerd/tombstone.c
@@ -228,39 +228,39 @@ static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, int scope_flags)
}
}
-static void dump_stack_segment(const backtrace_t* backtrace, log_t* log,
+static void dump_stack_segment(const backtrace_context_t* context, log_t* log,
int scope_flags, uintptr_t *sp, size_t words, int label) {
for (size_t i = 0; i < words; i++) {
uint32_t stack_content;
- if (!backtrace_read_word(backtrace, *sp, &stack_content)) {
+ if (!backtrace_read_word(context, *sp, &stack_content)) {
break;
}
- const char* map_name = backtrace_get_map_info(backtrace, stack_content, NULL);
+ const char* map_name = backtrace_get_map_name(context, stack_content, NULL);
if (!map_name) {
map_name = "";
}
uintptr_t offset = 0;
- char* proc_name = backtrace_get_proc_name(backtrace, stack_content, &offset);
- if (proc_name) {
+ char* func_name = backtrace_get_func_name(context, stack_content, &offset);
+ if (func_name) {
if (!i && label >= 0) {
if (offset) {
_LOG(log, scope_flags, " #%02d %08x %08x %s (%s+%u)\n",
- label, *sp, stack_content, map_name, proc_name, offset);
+ label, *sp, stack_content, map_name, func_name, offset);
} else {
_LOG(log, scope_flags, " #%02d %08x %08x %s (%s)\n",
- label, *sp, stack_content, map_name, proc_name);
+ label, *sp, stack_content, map_name, func_name);
}
} else {
if (offset) {
_LOG(log, scope_flags, " %08x %08x %s (%s+%u)\n",
- *sp, stack_content, map_name, proc_name, offset);
+ *sp, stack_content, map_name, func_name, offset);
} else {
_LOG(log, scope_flags, " %08x %08x %s (%s)\n",
- *sp, stack_content, map_name, proc_name);
+ *sp, stack_content, map_name, func_name);
}
}
- free(proc_name);
+ free(func_name);
} else {
if (!i && label >= 0) {
_LOG(log, scope_flags, " #%02d %08x %08x %s\n",
@@ -275,7 +275,8 @@ static void dump_stack_segment(const backtrace_t* backtrace, log_t* log,
}
}
-static void dump_stack(const backtrace_t* backtrace, log_t* log, int scope_flags) {
+static void dump_stack(const backtrace_context_t* context, log_t* log, int scope_flags) {
+ const backtrace_t* backtrace = context->backtrace;
size_t first = 0, last;
for (size_t i = 0; i < backtrace->num_frames; i++) {
if (backtrace->frames[i].sp) {
@@ -294,7 +295,7 @@ static void dump_stack(const backtrace_t* backtrace, log_t* log, int scope_flags
// Dump a few words before the first frame.
uintptr_t sp = backtrace->frames[first].sp - STACK_WORDS * sizeof(uint32_t);
- dump_stack_segment(backtrace, log, scope_flags, &sp, STACK_WORDS, -1);
+ dump_stack_segment(context, log, scope_flags, &sp, STACK_WORDS, -1);
// Dump a few words from all successive frames.
// Only log the first 3 frames, put the rest in the tombstone.
@@ -308,7 +309,7 @@ static void dump_stack(const backtrace_t* backtrace, log_t* log, int scope_flags
scope_flags &= (~SCOPE_AT_FAULT);
}
if (i == last) {
- dump_stack_segment(backtrace, log, scope_flags, &sp, STACK_WORDS, i);
+ dump_stack_segment(context, log, scope_flags, &sp, STACK_WORDS, i);
if (sp < frame->sp + frame->stack_size) {
_LOG(log, scope_flags, " ........ ........\n");
}
@@ -319,19 +320,19 @@ static void dump_stack(const backtrace_t* backtrace, log_t* log, int scope_flags
} else if (words > STACK_WORDS) {
words = STACK_WORDS;
}
- dump_stack_segment(backtrace, log, scope_flags, &sp, words, i);
+ dump_stack_segment(context, log, scope_flags, &sp, words, i);
}
}
}
-static void dump_backtrace_and_stack(const backtrace_t* backtrace, log_t* log,
- int scope_flags) {
- if (backtrace->num_frames) {
+static void dump_backtrace_and_stack(const backtrace_context_t* context,
+ log_t* log, int scope_flags) {
+ if (context->backtrace->num_frames) {
_LOG(log, scope_flags, "\nbacktrace:\n");
- dump_backtrace_to_log(backtrace, log, scope_flags, " ");
+ dump_backtrace_to_log(context, log, scope_flags, " ");
_LOG(log, scope_flags, "\nstack:\n");
- dump_stack(backtrace, log, scope_flags);
+ dump_stack(context, log, scope_flags);
}
}
@@ -399,12 +400,13 @@ static void dump_nearby_maps(const backtrace_map_info_t* map_info_list, log_t* l
dump_map(log, prev, "map above", scope_flags);
}
-static void dump_thread(const backtrace_t* backtrace, log_t* log, int scope_flags,
- int* total_sleep_time_usec) {
+static void dump_thread(const backtrace_context_t* context, log_t* log,
+ int scope_flags, int* total_sleep_time_usec) {
+ const backtrace_t* backtrace = context->backtrace;
wait_for_stop(backtrace->tid, total_sleep_time_usec);
dump_registers(log, backtrace->tid, scope_flags);
- dump_backtrace_and_stack(backtrace, log, scope_flags);
+ dump_backtrace_and_stack(context, log, scope_flags);
if (IS_AT_FAULT(scope_flags)) {
dump_memory_and_code(log, backtrace->tid, scope_flags);
dump_nearby_maps(backtrace->map_info_list, log, backtrace->tid, scope_flags);
@@ -446,11 +448,11 @@ static bool dump_sibling_thread_report(
_LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
dump_thread_info(log, pid, new_tid, 0);
- backtrace_t new_backtrace;
- if (backtrace_get_data(&new_backtrace, new_tid)) {
- dump_thread(&new_backtrace, log, 0, total_sleep_time_usec);
+ backtrace_context_t new_context;
+ if (backtrace_create_context(&new_context, pid, new_tid, 0)) {
+ dump_thread(&new_context, log, 0, total_sleep_time_usec);
+ backtrace_destroy_context(&new_context);
}
- backtrace_free_data(&new_backtrace);
if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
@@ -606,7 +608,7 @@ static void dump_logs(log_t* log, pid_t pid, bool tailOnly)
dump_log_file(log, pid, "/dev/log/main", tailOnly);
}
-static void dump_abort_message(const backtrace_t* backtrace, log_t* log, uintptr_t address) {
+static void dump_abort_message(const backtrace_context_t* context, log_t* log, uintptr_t address) {
if (address == 0) {
return;
}
@@ -618,7 +620,7 @@ static void dump_abort_message(const backtrace_t* backtrace, log_t* log, uintptr
char* p = &msg[0];
while (p < &msg[sizeof(msg)]) {
uint32_t data;
- if (!backtrace_read_word(backtrace, address, &data)) {
+ if (!backtrace_read_word(context, address, &data)) {
break;
}
address += sizeof(uint32_t);
@@ -673,11 +675,11 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a
dump_fault_addr(log, tid, signal);
}
- backtrace_t backtrace;
- if (backtrace_get_data(&backtrace, tid)) {
- dump_abort_message(&backtrace, log, abort_msg_address);
- dump_thread(&backtrace, log, SCOPE_AT_FAULT, total_sleep_time_usec);
- backtrace_free_data(&backtrace);
+ backtrace_context_t context;
+ if (backtrace_create_context(&context, pid, tid, 0)) {
+ dump_abort_message(&context, log, abort_msg_address);
+ dump_thread(&context, log, SCOPE_AT_FAULT, total_sleep_time_usec);
+ backtrace_destroy_context(&context);
}
if (want_logs) {
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
new file mode 100644
index 0000000..0b75e83
--- /dev/null
+++ b/include/backtrace/Backtrace.h
@@ -0,0 +1,85 @@
+/*
+ * 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 _BACKTRACE_BACKTRACE_H
+#define _BACKTRACE_BACKTRACE_H
+
+#include <backtrace/backtrace.h>
+
+#include <string>
+
+class BacktraceImpl;
+
+class Backtrace {
+public:
+ Backtrace(BacktraceImpl* impl);
+ virtual ~Backtrace();
+
+ // Get the current stack trace and store in the backtrace_ structure.
+ virtual bool Unwind(size_t num_ignore_frames);
+
+ // Get the function name and offset into the function given the pc.
+ // If the string is empty, then no valid function name was found.
+ virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset);
+
+ // Get the name of the map associated with the given pc. If NULL is returned,
+ // then map_start is not set. Otherwise, map_start is the beginning of this
+ // map.
+ virtual const char* GetMapName(uintptr_t pc, uintptr_t* map_start);
+
+ // Finds the memory map associated with the given ptr.
+ virtual const backtrace_map_info_t* FindMapInfo(uintptr_t ptr);
+
+ // Read the data at a specific address.
+ virtual bool ReadWord(uintptr_t ptr, uint32_t* out_value) = 0;
+
+ // Create a string representing the formatted line of backtrace information
+ // for a single frame.
+ virtual std::string FormatFrameData(size_t frame_num);
+
+ pid_t Pid() { return backtrace_.pid; }
+ pid_t Tid() { return backtrace_.tid; }
+ size_t NumFrames() { return backtrace_.num_frames; }
+
+ const backtrace_t* GetBacktrace() { return &backtrace_; }
+
+ const backtrace_frame_data_t* GetFrame(size_t frame_num) {
+ return &backtrace_.frames[frame_num];
+ }
+
+ // Create the correct Backtrace object based on what is to be unwound.
+ // If pid < 0 or equals the current pid, then the Backtrace object
+ // corresponds to the current process.
+ // If pid < 0 or equals the current pid and tid >= 0, then the Backtrace
+ // object corresponds to a thread in the current process.
+ // If pid >= 0 and tid < 0, then the Backtrace object corresponds to a
+ // different process.
+ // Tracing a thread in a different process is not supported.
+ static Backtrace* Create(pid_t pid, pid_t tid);
+
+protected:
+ virtual bool VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value);
+
+ BacktraceImpl* impl_;
+
+ backtrace_map_info_t* map_info_;
+
+ backtrace_t backtrace_;
+
+ friend class BacktraceImpl;
+};
+
+#endif // _BACKTRACE_BACKTRACE_H
diff --git a/include/backtrace/backtrace.h b/include/backtrace/backtrace.h
index b6bff38..b35a6d5 100644
--- a/include/backtrace/backtrace.h
+++ b/include/backtrace/backtrace.h
@@ -21,9 +21,7 @@
#include <stdbool.h>
#include <inttypes.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
#define MAX_BACKTRACE_FRAMES 64
@@ -43,48 +41,58 @@ typedef struct {
size_t stack_size; /* The size of the stack, zero indicate an unknown stack size. */
const char* map_name; /* The name of the map to which this pc belongs, NULL indicates the pc doesn't belong to a known map. */
uintptr_t map_offset; /* pc relative to the start of the map, only valid if map_name is not NULL. */
- char* proc_name; /* The function name associated with this pc, NULL if not found. */
- uintptr_t proc_offset; /* pc relative to the start of the procedure, only valid if proc_name is not NULL. */
+ char* func_name; /* The function name associated with this pc, NULL if not found. */
+ uintptr_t func_offset; /* pc relative to the start of the function, only valid if func_name is not NULL. */
} backtrace_frame_data_t;
typedef struct {
backtrace_frame_data_t frames[MAX_BACKTRACE_FRAMES];
size_t num_frames;
+ pid_t pid;
pid_t tid;
backtrace_map_info_t* map_info_list;
- void* private_data;
} backtrace_t;
-/* Gather the backtrace data for tid and fill in the backtrace structure.
- * If tid < 0, then gather the backtrace for the current thread.
+typedef struct {
+ void* data;
+ const backtrace_t* backtrace;
+} backtrace_context_t;
+
+/* Create a context for the backtrace data and gather the backtrace.
+ * If pid < 0, then gather the backtrace for the current process.
*/
-bool backtrace_get_data(backtrace_t* backtrace, pid_t tid);
+bool backtrace_create_context(
+ backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames);
-/* Free any memory associated with the backtrace structure. */
-void backtrace_free_data(backtrace_t* backtrace);
+/* Gather the backtrace data for a pthread instead of a process. */
+bool backtrace_create_thread_context(
+ backtrace_context_t* context, pid_t tid, size_t num_ignore_frames);
+
+/* Free any memory allocated during the context create. */
+void backtrace_destroy_context(backtrace_context_t* context);
/* Read data at a specific address for a process. */
bool backtrace_read_word(
- const backtrace_t* backtrace, uintptr_t ptr, uint32_t* value);
+ const backtrace_context_t* context, uintptr_t ptr, uint32_t* value);
-/* Get information about the map associated with a pc. If NULL is
+/* Get information about the map name associated with a pc. If NULL is
* returned, then map_start is not set.
*/
-const char* backtrace_get_map_info(
- const backtrace_t* backtrace, uintptr_t pc, uintptr_t* map_start);
+const char* backtrace_get_map_name(
+ const backtrace_context_t* context, uintptr_t pc, uintptr_t* map_start);
-/* Get the procedure name and offest given the pc. If NULL is returned,
- * then proc_offset is not set. The returned string is allocated using
+/* Get the function name and offset given the pc. If NULL is returned,
+ * then func_offset is not set. The returned string is allocated using
* malloc and must be freed by the caller.
*/
-char* backtrace_get_proc_name(
- const backtrace_t* backtrace, uintptr_t pc, uintptr_t* proc_offset);
+char* backtrace_get_func_name(
+ const backtrace_context_t* context, uintptr_t pc, uintptr_t* func_offset);
-/* Loads memory map from /proc/<tid>/maps. If tid < 0, then load the memory
+/* Loads memory map from /proc/<pid>/maps. If pid < 0, then load the memory
* map for the current process.
*/
-backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid);
+backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid);
/* Frees memory associated with the map list. */
void backtrace_destroy_map_info_list(backtrace_map_info_t* map_info_list);
@@ -95,10 +103,12 @@ const backtrace_map_info_t* backtrace_find_map_info(
/* Create a formatted line of backtrace information for a single frame. */
void backtrace_format_frame_data(
- const backtrace_frame_data_t* frame, size_t frame_num, char *buf, size_t buf_size);
+ const backtrace_context_t* context, size_t frame_num, char* buf,
+ size_t buf_size);
+
+/* Get the backtrace data structure associated with the context. */
+const backtrace_t* backtrace_get_data(backtrace_context_t* context);
-#ifdef __cplusplus
-}
-#endif
+__END_DECLS
#endif /* _BACKTRACE_H */
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 4197bbb..66d7e62 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -1,67 +1,109 @@
LOCAL_PATH:= $(call my-dir)
+common_src := \
+ Backtrace.cpp \
+ BacktraceThread.cpp \
+ map_info.c \
+ thread_utils.c \
+
+common_cflags := \
+ -Wall \
+ -Wno-unused-parameter \
+ -Werror \
+
+common_conlyflags := \
+ -std=gnu99 \
+
+common_cppflags := \
+ -std=gnu++11 \
+
+common_shared_libs := \
+ libcutils \
+ libgccdemangle \
+ liblog \
+
+# To enable using libunwind on each arch, add it to the list below.
+ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),))
+
#----------------------------------------------------------------------------
-# The libbacktrace library using libunwind
+# The native libbacktrace library with libunwind.
#----------------------------------------------------------------------------
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- unwind.c \
- unwind_remote.c \
- unwind_local.c \
- common.c \
- demangle.c \
- map_info.c \
+ $(common_src) \
+ UnwindCurrent.cpp \
+ UnwindPtrace.cpp \
LOCAL_CFLAGS := \
- -Wall \
- -Wno-unused-parameter \
- -Werror \
- -std=gnu99 \
+ $(common_cflags) \
+
+LOCAL_CONLYFLAGS += \
+ $(common_conlyflags) \
+
+LOCAL_CPPFLAGS += \
+ $(common_cppflags) \
LOCAL_MODULE := libbacktrace
LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES := \
+ $(common_c_includes) \
+ external/libunwind/include \
+
LOCAL_SHARED_LIBRARIES := \
- liblog \
+ $(common_shared_libs) \
libunwind \
libunwind-ptrace \
- libgccdemangle \
-LOCAL_C_INCLUDES := \
- external/libunwind/include \
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
-# The libunwind code is not in the tree yet, so don't build this library yet.
-#include $(BUILD_SHARED_LIBRARY)
+include external/stlport/libstlport.mk
+
+include $(BUILD_SHARED_LIBRARY)
+
+else
#----------------------------------------------------------------------------
-# The libbacktrace library using libcorkscrew
+# The native libbacktrace library with libcorkscrew.
#----------------------------------------------------------------------------
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- corkscrew.c \
- common.c \
- demangle.c \
- map_info.c \
+ $(common_src) \
+ Corkscrew.cpp \
LOCAL_CFLAGS := \
- -Wall \
- -Wno-unused-parameter \
- -Werror \
- -std=gnu99 \
+ $(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 \
- libgccdemangle \
- liblog \
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
+include external/stlport/libstlport.mk
include $(BUILD_SHARED_LIBRARY)
+endif
+
#----------------------------------------------------------------------------
# libbacktrace test library, all optimizations turned off
#----------------------------------------------------------------------------
@@ -77,6 +119,9 @@ LOCAL_CFLAGS += \
-std=gnu99 \
-O0 \
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
include $(BUILD_SHARED_LIBRARY)
#----------------------------------------------------------------------------
@@ -88,16 +133,36 @@ LOCAL_MODULE := backtrace_test
LOCAL_MODULE_FLAGS := debug
LOCAL_SRC_FILES := \
- backtrace_test.c \
+ backtrace_test.cpp \
+ thread_utils.c \
LOCAL_CFLAGS += \
- -std=gnu99 \
+ -fno-builtin \
+ -fstack-protector-all \
+ -O0 \
+ -g \
+ -DGTEST_OS_LINUX_ANDROID \
+ -DGTEST_HAS_STD_STRING \
-LOCAL_SHARED_LIBRARIES := \
+LOCAL_CONLYFLAGS += \
+ $(common_conlyflags) \
+
+LOCAL_CPPFLAGS += \
+ $(common_cppflags) \
+ -fpermissive \
+
+LOCAL_SHARED_LIBRARIES += \
+ libcutils \
libbacktrace_test \
libbacktrace \
-include $(BUILD_EXECUTABLE)
+LOCAL_LDLIBS := \
+ -lpthread \
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_NATIVE_TEST)
#----------------------------------------------------------------------------
# Only linux-x86 host versions of libbacktrace supported.
@@ -110,22 +175,26 @@ ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
include $(CLEAR_VARS)
LOCAL_SRC_FILES += \
- corkscrew.c \
- common.c \
- demangle.c \
- map_info.c \
+ $(common_src) \
+ Corkscrew.cpp \
LOCAL_CFLAGS += \
- -Wall \
- -Wno-unused-parameter \
- -Werror \
- -std=gnu99 \
+ $(common_cflags) \
+
+LOCAL_CONLYFLAGS += \
+ $(common_conlyflags) \
+
+LOCAL_CPPFLAGS += \
+ $(common_cppflags) \
+
+LOCAL_C_INCLUDES := \
+ $(common_c_includes) \
+ system/core/libcorkscrew \
LOCAL_SHARED_LIBRARIES := \
- liblog \
- libcorkscrew \
libgccdemangle \
liblog \
+ libcorkscrew \
LOCAL_LDLIBS += \
-ldl \
@@ -134,6 +203,9 @@ LOCAL_LDLIBS += \
LOCAL_MODULE := libbacktrace
LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
include $(BUILD_HOST_SHARED_LIBRARY)
#----------------------------------------------------------------------------
@@ -151,6 +223,9 @@ LOCAL_CFLAGS += \
-std=gnu99 \
-O0 \
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
include $(BUILD_HOST_SHARED_LIBRARY)
#----------------------------------------------------------------------------
@@ -162,15 +237,29 @@ LOCAL_MODULE := backtrace_test
LOCAL_MODULE_FLAGS := debug
LOCAL_SRC_FILES := \
- backtrace_test.c \
+ backtrace_test.cpp \
+ thread_utils.c \
LOCAL_CFLAGS += \
- -std=gnu99 \
+ -fno-builtin \
+ -fstack-protector-all \
+ -O0 \
+ -g \
+ -DGTEST_HAS_STD_STRING \
LOCAL_SHARED_LIBRARIES := \
libbacktrace_test \
libbacktrace \
-include $(BUILD_HOST_EXECUTABLE)
+LOCAL_CPPFLAGS += \
+ -fpermissive \
+
+LOCAL_LDLIBS := \
+ -lpthread \
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_HOST_NATIVE_TEST)
endif # HOST_OS-HOST_ARCH == linux-x86
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
new file mode 100644
index 0000000..eca1c3d
--- /dev/null
+++ b/libbacktrace/Backtrace.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <cutils/log.h>
+
+#include "Backtrace.h"
+#include "thread_utils.h"
+
+//-------------------------------------------------------------------------
+// BacktraceImpl functions.
+//-------------------------------------------------------------------------
+backtrace_t* BacktraceImpl::GetBacktraceData() {
+ return &backtrace_obj_->backtrace_;
+}
+
+//-------------------------------------------------------------------------
+// Backtrace functions.
+//-------------------------------------------------------------------------
+Backtrace::Backtrace(BacktraceImpl* impl) : impl_(impl), map_info_(NULL) {
+ impl_->SetParent(this);
+ backtrace_.num_frames = 0;
+ backtrace_.pid = -1;
+ backtrace_.tid = -1;
+}
+
+Backtrace::~Backtrace() {
+ for (size_t i = 0; i < NumFrames(); i++) {
+ if (backtrace_.frames[i].func_name) {
+ free(backtrace_.frames[i].func_name);
+ backtrace_.frames[i].func_name = NULL;
+ }
+ }
+
+ if (map_info_) {
+ backtrace_destroy_map_info_list(map_info_);
+ map_info_ = NULL;
+ }
+
+ if (impl_) {
+ delete impl_;
+ impl_ = NULL;
+ }
+}
+
+bool Backtrace::Unwind(size_t num_ignore_frames) {
+ return impl_->Unwind(num_ignore_frames);
+}
+
+__BEGIN_DECLS
+extern char* __cxa_demangle (const char* mangled, char* buf, size_t* len,
+ int* status);
+__END_DECLS
+
+std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
+ std::string func_name = impl_->GetFunctionNameRaw(pc, offset);
+ if (!func_name.empty()) {
+#if defined(__APPLE__)
+ // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
+ if (symbol_name[0] != '_') {
+ return func_name;
+ }
+#endif
+ char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0);
+ if (name) {
+ func_name = name;
+ free(name);
+ }
+ }
+ return func_name;
+}
+
+bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value) {
+ if (ptr & 3) {
+ ALOGW("Backtrace::verifyReadWordArgs: invalid pointer %p", (void*)ptr);
+ *out_value = (uint32_t)-1;
+ return false;
+ }
+ return true;
+}
+
+const char* Backtrace::GetMapName(uintptr_t pc, uintptr_t* map_start) {
+ const backtrace_map_info_t* map_info = FindMapInfo(pc);
+ if (map_info) {
+ if (map_start) {
+ *map_start = map_info->start;
+ }
+ return map_info->name;
+ }
+ return NULL;
+}
+
+const backtrace_map_info_t* Backtrace::FindMapInfo(uintptr_t ptr) {
+ return backtrace_find_map_info(map_info_, ptr);
+}
+
+std::string Backtrace::FormatFrameData(size_t frame_num) {
+ backtrace_frame_data_t* frame = &backtrace_.frames[frame_num];
+ const char* map_name;
+ if (frame->map_name) {
+ map_name = frame->map_name;
+ } else {
+ map_name = "<unknown>";
+ }
+ uintptr_t relative_pc;
+ if (frame->map_offset) {
+ relative_pc = frame->map_offset;
+ } else {
+ relative_pc = frame->pc;
+ }
+
+ char buf[512];
+ if (frame->func_name && frame->func_offset) {
+ snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")",
+ frame_num, (int)sizeof(uintptr_t)*2, relative_pc, map_name,
+ frame->func_name, frame->func_offset);
+ } else if (frame->func_name) {
+ snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s)", frame_num,
+ (int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->func_name);
+ } else {
+ snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s", frame_num,
+ (int)sizeof(uintptr_t)*2, relative_pc, map_name);
+ }
+
+ return buf;
+}
+
+//-------------------------------------------------------------------------
+// BacktraceCurrent functions.
+//-------------------------------------------------------------------------
+BacktraceCurrent::BacktraceCurrent(BacktraceImpl* impl) : Backtrace(impl) {
+ map_info_ = backtrace_create_map_info_list(-1);
+
+ backtrace_.pid = getpid();
+}
+
+BacktraceCurrent::~BacktraceCurrent() {
+}
+
+bool BacktraceCurrent::ReadWord(uintptr_t ptr, uint32_t* out_value) {
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+ const backtrace_map_info_t* map_info = FindMapInfo(ptr);
+ if (map_info && map_info->is_readable) {
+ *out_value = *reinterpret_cast<uint32_t*>(ptr);
+ return true;
+ } else {
+ ALOGW("BacktraceCurrent::readWord: pointer %p not in a readbale map", reinterpret_cast<void*>(ptr));
+ *out_value = static_cast<uint32_t>(-1);
+ return false;
+ }
+}
+
+//-------------------------------------------------------------------------
+// BacktracePtrace functions.
+//-------------------------------------------------------------------------
+BacktracePtrace::BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid)
+ : Backtrace(impl) {
+ map_info_ = backtrace_create_map_info_list(tid);
+
+ backtrace_.pid = pid;
+ backtrace_.tid = tid;
+}
+
+BacktracePtrace::~BacktracePtrace() {
+}
+
+bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) {
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+#if defined(__APPLE__)
+ ALOGW("BacktracePtrace::readWord: MacOS does not support reading from another pid.\n");
+ return false;
+#else
+ // ptrace() returns -1 and sets errno when the operation fails.
+ // To disambiguate -1 from a valid result, we clear errno beforehand.
+ errno = 0;
+ *out_value = ptrace(PTRACE_PEEKTEXT, Tid(), reinterpret_cast<void*>(ptr), NULL);
+ if (*out_value == static_cast<uint32_t>(-1) && errno) {
+ ALOGW("BacktracePtrace::readWord: invalid pointer 0x%08x reading from tid %d, "
+ "ptrace() errno=%d", ptr, Tid(), errno);
+ return false;
+ }
+ return true;
+#endif
+}
+
+Backtrace* Backtrace::Create(pid_t pid, pid_t tid) {
+ if (pid < 0 || pid == getpid()) {
+ if (tid < 0 || tid == gettid()) {
+ return CreateCurrentObj();
+ } else {
+ return CreateThreadObj(tid);
+ }
+ } else if (tid < 0) {
+ return CreatePtraceObj(pid, pid);
+ } else {
+ return CreatePtraceObj(pid, tid);
+ }
+}
+
+//-------------------------------------------------------------------------
+// Common interface functions.
+//-------------------------------------------------------------------------
+bool backtrace_create_context(
+ backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames) {
+ Backtrace* backtrace = Backtrace::Create(pid, tid);
+ if (!backtrace) {
+ return false;
+ }
+ if (!backtrace->Unwind(num_ignore_frames)) {
+ delete backtrace;
+ return false;
+ }
+
+ context->data = backtrace;
+ context->backtrace = backtrace->GetBacktrace();
+ return true;
+}
+
+void backtrace_destroy_context(backtrace_context_t* context) {
+ if (context->data) {
+ Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
+ delete backtrace;
+ context->data = NULL;
+ }
+ context->backtrace = NULL;
+}
+
+const backtrace_t* backtrace_get_data(backtrace_context_t* context) {
+ if (context && context->data) {
+ Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
+ return backtrace->GetBacktrace();
+ }
+ return NULL;
+}
+
+bool backtrace_read_word(const backtrace_context_t* context, uintptr_t ptr, uint32_t* value) {
+ if (context->data) {
+ Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
+ return backtrace->ReadWord(ptr, value);
+ }
+ return true;
+}
+
+const char* backtrace_get_map_name(const backtrace_context_t* context, uintptr_t pc, uintptr_t* map_start) {
+ if (context->data) {
+ Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
+ return backtrace->GetMapName(pc, map_start);
+ }
+ return NULL;
+}
+
+char* backtrace_get_func_name(const backtrace_context_t* context, uintptr_t pc, uintptr_t* func_offset) {
+ if (context->data) {
+ Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
+ std::string func_name = backtrace->GetFunctionName(pc, func_offset);
+ if (!func_name.empty()) {
+ return strdup(func_name.c_str());
+ }
+ }
+ return NULL;
+}
+
+void backtrace_format_frame_data(
+ const backtrace_context_t* context, size_t frame_num, char* buf,
+ size_t buf_size) {
+ if (buf_size == 0 || buf == NULL) {
+ ALOGW("backtrace_format_frame_data: bad call buf %p buf_size %zu\n",
+ buf, buf_size);
+ return;
+ }
+ if (context->data) {
+ Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
+ std::string line = backtrace->FormatFrameData(frame_num);
+ if (line.size() > buf_size) {
+ memcpy(buf, line.c_str(), buf_size-1);
+ buf[buf_size] = '\0';
+ } else {
+ memcpy(buf, line.c_str(), line.size()+1);
+ }
+ }
+}
diff --git a/libbacktrace/Backtrace.h b/libbacktrace/Backtrace.h
new file mode 100644
index 0000000..b89bc89
--- /dev/null
+++ b/libbacktrace/Backtrace.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_H
+#define _LIBBACKTRACE_BACKTRACE_H
+
+#include <backtrace/Backtrace.h>
+
+#include <sys/types.h>
+
+class BacktraceImpl {
+public:
+ virtual ~BacktraceImpl() { }
+
+ virtual bool Unwind(size_t num_ignore_frames) = 0;
+
+ // The name returned is not demangled, Backtrace::GetFunctionName()
+ // takes care of demangling the name.
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0;
+
+ void SetParent(Backtrace* backtrace) { backtrace_obj_ = backtrace; }
+
+protected:
+ backtrace_t* GetBacktraceData();
+
+ Backtrace* backtrace_obj_;
+};
+
+class BacktraceCurrent : public Backtrace {
+public:
+ BacktraceCurrent(BacktraceImpl* impl);
+ virtual ~BacktraceCurrent();
+
+ bool ReadWord(uintptr_t ptr, uint32_t* out_value);
+};
+
+class BacktracePtrace : public Backtrace {
+public:
+ BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid);
+ virtual ~BacktracePtrace();
+
+ bool ReadWord(uintptr_t ptr, uint32_t* out_value);
+};
+
+Backtrace* CreateCurrentObj();
+Backtrace* CreatePtraceObj(pid_t pid, pid_t tid);
+Backtrace* CreateThreadObj(pid_t tid);
+
+#endif // _LIBBACKTRACE_BACKTRACE_H
diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp
new file mode 100644
index 0000000..6c3641e
--- /dev/null
+++ b/libbacktrace/BacktraceThread.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <cutils/atomic.h>
+#include <cutils/log.h>
+
+#include "BacktraceThread.h"
+#include "thread_utils.h"
+
+//-------------------------------------------------------------------------
+// ThreadEntry implementation.
+//-------------------------------------------------------------------------
+static ThreadEntry* g_list = NULL;
+static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+ThreadEntry::ThreadEntry(
+ BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames)
+ : thread_intf_(intf), pid_(pid), tid_(tid), next_(NULL), prev_(NULL),
+ state_(STATE_WAITING), num_ignore_frames_(num_ignore_frames) {
+}
+
+ThreadEntry::~ThreadEntry() {
+ pthread_mutex_lock(&g_mutex);
+ if (g_list == this) {
+ g_list = next_;
+ } else {
+ if (next_) {
+ next_->prev_ = prev_;
+ }
+ prev_->next_ = next_;
+ }
+ pthread_mutex_unlock(&g_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_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.
+ ALOGW("%s::%s(): Entry for pid %d tid %d already exists.\n",
+ __FILE__, __FUNCTION__, pid, tid);
+
+ pthread_mutex_unlock(&g_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_mutex);
+
+ return entry;
+}
+
+//-------------------------------------------------------------------------
+// BacktraceThread functions.
+//-------------------------------------------------------------------------
+static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo,
+ void* sigcontext) {
+ if (pthread_mutex_lock(&g_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_mutex);
+ if (!cur_entry) {
+ ALOGW("%s::%s(): Unable to find pid %d tid %d information\n",
+ __FILE__, __FUNCTION__, pid, tid);
+ return;
+ }
+
+ if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state_) == 0) {
+ cur_entry->thread_intf_->ThreadUnwind(siginfo, sigcontext,
+ cur_entry->num_ignore_frames_);
+ }
+ android_atomic_release_store(STATE_DONE, &cur_entry->state_);
+ }
+}
+
+BacktraceThread::BacktraceThread(
+ BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid)
+ : BacktraceCurrent(impl), thread_intf_(thread_intf) {
+ backtrace_.tid = tid;
+}
+
+BacktraceThread::~BacktraceThread() {
+}
+
+void BacktraceThread::FinishUnwind() {
+ for (size_t i = 0; i < NumFrames(); i++) {
+ backtrace_frame_data_t* frame = &backtrace_.frames[i];
+
+ frame->map_offset = 0;
+ uintptr_t map_start;
+ frame->map_name = GetMapName(frame->pc, &map_start);
+ if (frame->map_name) {
+ frame->map_offset = frame->pc - map_start;
+ }
+
+ frame->func_offset = 0;
+ std::string func_name = GetFunctionName(frame->pc, &frame->func_offset);
+ if (!func_name.empty()) {
+ frame->func_name = strdup(func_name.c_str());
+ }
+ }
+}
+
+bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) {
+ entry->state_ = STATE_WAITING;
+
+ if (tgkill(Pid(), Tid(), SIGURG) != 0) {
+ ALOGW("%s::%s(): tgkill failed %s\n", __FILE__, __FUNCTION__, strerror(errno));
+ return false;
+ }
+
+ // Allow up to a second for the dump to occur.
+ int wait_millis = 1000;
+ 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) {
+ ALOGW("%s::%s(): Cancelled dump of thread %d\n", __FILE__, __FUNCTION__,
+ entry->tid_);
+ state = STATE_CANCEL;
+ cancelled = true;
+ } else {
+ state = android_atomic_acquire_load(&entry->state_);
+ }
+ }
+
+ // Wait for at most one minute for the dump to finish.
+ wait_millis = 60000;
+ while (android_atomic_acquire_load(&entry->state_) != STATE_DONE) {
+ if (wait_millis--) {
+ usleep(1000);
+ } else {
+ ALOGW("%s::%s(): Didn't finish thread unwind in 60 seconds.\n",
+ __FILE__, __FUNCTION__);
+ break;
+ }
+ }
+ return !cancelled;
+}
+
+bool BacktraceThread::Unwind(size_t num_ignore_frames) {
+ if (!thread_intf_->Init()) {
+ return false;
+ }
+
+ ThreadEntry* entry = ThreadEntry::AddThreadToUnwind(
+ thread_intf_, Pid(), Tid(), num_ignore_frames);
+ if (!entry) {
+ return false;
+ }
+
+ bool retval = false;
+ 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 {
+ ALOGW("%s::%s(): sigaction failed %s\n", __FILE__, __FUNCTION__, strerror(errno));
+ }
+
+ if (retval) {
+ FinishUnwind();
+ }
+ delete entry;
+
+ return retval;
+}
diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h
new file mode 100644
index 0000000..afea771
--- /dev/null
+++ b/libbacktrace/BacktraceThread.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_THREAD_H
+#define _LIBBACKTRACE_BACKTRACE_THREAD_H
+
+#include <inttypes.h>
+#include <pthread.h>
+#include <sys/types.h>
+
+#include "Backtrace.h"
+
+typedef enum {
+ STATE_WAITING = 0,
+ STATE_DUMPING,
+ STATE_DONE,
+ STATE_CANCEL,
+} state_e;
+
+class BacktraceThreadInterface;
+
+class ThreadEntry {
+public:
+ ThreadEntry(
+ BacktraceThreadInterface* impl, pid_t pid, pid_t tid,
+ size_t num_ignore_frames);
+ ~ThreadEntry();
+
+ bool Match(pid_t pid, pid_t tid) { return (pid == pid_ && tid == tid_); }
+
+ static ThreadEntry* AddThreadToUnwind(
+ BacktraceThreadInterface* thread_intf, pid_t pid, pid_t tid,
+ size_t num_ignored_frames);
+
+ BacktraceThreadInterface* thread_intf_;
+ pid_t pid_;
+ pid_t tid_;
+ ThreadEntry* next_;
+ ThreadEntry* prev_;
+ int32_t state_;
+ int num_ignore_frames_;
+};
+
+// Interface class that does not contain any local storage, only defines
+// virtual functions to be defined by subclasses.
+class BacktraceThreadInterface {
+public:
+ virtual ~BacktraceThreadInterface() { }
+
+ virtual bool Init() = 0;
+
+ virtual void ThreadUnwind(
+ siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0;
+};
+
+class BacktraceThread : public BacktraceCurrent {
+public:
+ // impl and thread_intf should point to the same object, this allows
+ // the compiler to catch if an implementation does not properly
+ // subclass both.
+ BacktraceThread(
+ BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid);
+ virtual ~BacktraceThread();
+
+ virtual bool Unwind(size_t num_ignore_frames);
+
+ virtual void ThreadUnwind(
+ siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) {
+ thread_intf_->ThreadUnwind(siginfo, sigcontext, num_ignore_frames);
+ }
+
+private:
+ virtual bool TriggerUnwindOnThread(ThreadEntry* entry);
+
+ virtual void FinishUnwind();
+
+ BacktraceThreadInterface* thread_intf_;
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_THREAD_H
diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp
new file mode 100644
index 0000000..8ba1e80
--- /dev/null
+++ b/libbacktrace/Corkscrew.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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 <cutils/log.h>
+#include <corkscrew/backtrace.h>
+
+#ifndef __USE_GNU
+#define __USE_GNU
+#endif
+#include <dlfcn.h>
+
+#include "Corkscrew.h"
+
+//-------------------------------------------------------------------------
+// CorkscrewCommon functions.
+//-------------------------------------------------------------------------
+bool CorkscrewCommon::GenerateFrameData(
+ backtrace_frame_t* cork_frames, ssize_t num_frames) {
+ if (num_frames < 0) {
+ ALOGW("CorkscrewCommon::GenerateFrameData: libcorkscrew unwind failed.\n");
+ return false;
+ }
+
+ backtrace_t* data = GetBacktraceData();
+ data->num_frames = num_frames;
+ for (size_t i = 0; i < data->num_frames; i++) {
+ backtrace_frame_data_t* frame = &data->frames[i];
+ frame->pc = cork_frames[i].absolute_pc;
+ frame->sp = cork_frames[i].stack_top;
+ frame->stack_size = cork_frames[i].stack_size;
+ frame->map_name = NULL;
+ frame->map_offset = 0;
+ frame->func_name = NULL;
+ frame->func_offset = 0;
+
+ uintptr_t map_start;
+ frame->map_name = backtrace_obj_->GetMapName(frame->pc, &map_start);
+ if (frame->map_name) {
+ frame->map_offset = frame->pc - map_start;
+ }
+
+ std::string func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset);
+ if (!func_name.empty()) {
+ frame->func_name = strdup(func_name.c_str());
+ }
+ }
+ 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;
+
+ // Get information about the current thread.
+ Dl_info info;
+ const backtrace_map_info_t* map_info = backtrace_obj_->FindMapInfo(pc);
+ const char* symbol_name = NULL;
+ if (map_info && dladdr((const void*)pc, &info) && info.dli_sname) {
+ *offset = pc - map_info->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase;
+ symbol_name = info.dli_sname;
+
+ return symbol_name;
+ }
+ return "";
+}
+
+//-------------------------------------------------------------------------
+// CorkscrewThread functions.
+//-------------------------------------------------------------------------
+CorkscrewThread::CorkscrewThread() {
+}
+
+CorkscrewThread::~CorkscrewThread() {
+ if (corkscrew_map_info_) {
+ free_map_info_list(corkscrew_map_info_);
+ corkscrew_map_info_ = NULL;
+ }
+}
+
+bool CorkscrewThread::Init() {
+ corkscrew_map_info_ = load_map_info_list(backtrace_obj_->Pid());
+ return corkscrew_map_info_ != NULL;
+}
+
+void CorkscrewThread::ThreadUnwind(
+ siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) {
+ backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
+ ssize_t num_frames = unwind_backtrace_signal_arch(
+ siginfo, sigcontext, corkscrew_map_info_, frames, num_ignore_frames,
+ MAX_BACKTRACE_FRAMES);
+ if (num_frames > 0) {
+ backtrace_t* data = GetBacktraceData();
+ data->num_frames = num_frames;
+ for (size_t i = 0; i < data->num_frames; i++) {
+ backtrace_frame_data_t* frame = &data->frames[i];
+ frame->pc = frames[i].absolute_pc;
+ frame->sp = frames[i].stack_top;
+ frame->stack_size = frames[i].stack_size;
+
+ frame->map_offset = 0;
+ frame->map_name = NULL;
+ frame->map_offset = 0;
+
+ frame->func_offset = 0;
+ frame->func_name = NULL;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------
+// 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(backtrace_obj_->Tid());
+
+ backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
+ ssize_t num_frames = unwind_backtrace_ptrace(
+ backtrace_obj_->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 createion functions.
+//-------------------------------------------------------------------------
+Backtrace* CreateCurrentObj() {
+ return new BacktraceCurrent(new CorkscrewCurrent());
+}
+
+Backtrace* CreatePtraceObj(pid_t pid, pid_t tid) {
+ return new BacktracePtrace(new CorkscrewPtrace(), pid, tid);
+}
+
+Backtrace* CreateThreadObj(pid_t tid) {
+ CorkscrewThread* thread_obj = new CorkscrewThread();
+ return new BacktraceThread(thread_obj, thread_obj, tid);
+}
diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h
new file mode 100644
index 0000000..7cb125c
--- /dev/null
+++ b/libbacktrace/Corkscrew.h
@@ -0,0 +1,74 @@
+/*
+ * 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/Backtrace.h>
+
+#include <corkscrew/backtrace.h>
+
+#include "Backtrace.h"
+#include "BacktraceThread.h"
+
+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 bool Init();
+
+ virtual void ThreadUnwind(
+ siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames);
+
+private:
+ map_info_t* corkscrew_map_info_;
+};
+
+class CorkscrewPtrace : public CorkscrewCommon {
+public:
+ CorkscrewPtrace();
+ virtual ~CorkscrewPtrace();
+
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+
+ virtual bool Unwind(size_t num_ignore_threads);
+
+private:
+ ptrace_context_t* ptrace_context_;
+};
+
+#endif // _LIBBACKTRACE_CORKSCREW_H
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
new file mode 100644
index 0000000..0280e93
--- /dev/null
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libbacktrace"
+
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include <backtrace/backtrace.h>
+
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+#include "UnwindCurrent.h"
+
+#if defined(__arm__)
+ #if !defined(__BIONIC_HAVE_UCONTEXT_T)
+ // The Current version of the Android <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;
+ #endif // !__BIONIC_HAVE_UCONTEXT_T
+#endif // defined(__arm__)
+
+//-------------------------------------------------------------------------
+// UnwindCurrent functions.
+//-------------------------------------------------------------------------
+UnwindCurrent::UnwindCurrent() {
+}
+
+UnwindCurrent::~UnwindCurrent() {
+}
+
+bool UnwindCurrent::Unwind(size_t num_ignore_frames) {
+ int ret = unw_getcontext(&context_);
+ if (ret < 0) {
+ ALOGW("UnwindCurrent::Unwind: unw_getcontext failed %d\n", ret);
+ return false;
+ }
+ return UnwindFromContext(num_ignore_frames, true);
+}
+
+std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ *offset = 0;
+ char buf[512];
+ unw_word_t value;
+ if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
+ &value, &context_) >= 0 && buf[0] != '\0') {
+ *offset = static_cast<uintptr_t>(value);
+ return buf;
+ }
+ return "";
+}
+
+bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) {
+ backtrace_t* backtrace = GetBacktraceData();
+ backtrace->num_frames = 0;
+
+ // 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) {
+ ALOGW("UnwindCurrent::UnwindWithContext: unw_init_local failed %d\n", ret);
+ return false;
+ }
+
+ do {
+ unw_word_t pc;
+ ret = unw_get_reg(cursor, UNW_REG_IP, &pc);
+ if (ret < 0) {
+ ALOGW("UnwindCurrent::UnwindWithContext: Failed to read IP %d\n", ret);
+ break;
+ }
+ unw_word_t sp;
+ ret = unw_get_reg(cursor, UNW_REG_SP, &sp);
+ if (ret < 0) {
+ ALOGW("UnwindCurrent::UnwindWithContext: Failed to read SP %d\n", ret);
+ break;
+ }
+
+ if (num_ignore_frames == 0) {
+ size_t num_frames = backtrace->num_frames;
+ backtrace_frame_data_t* frame = &backtrace->frames[num_frames];
+ frame->pc = static_cast<uintptr_t>(pc);
+ frame->sp = static_cast<uintptr_t>(sp);
+ frame->stack_size = 0;
+ frame->map_name = NULL;
+ frame->map_offset = 0;
+ frame->func_name = NULL;
+ frame->func_offset = 0;
+
+ if (num_frames > 0) {
+ // Set the stack size for the previous frame.
+ backtrace_frame_data_t* prev = &backtrace->frames[num_frames-1];
+ prev->stack_size = frame->sp - prev->sp;
+ }
+
+ if (resolve) {
+ std::string func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset);
+ if (!func_name.empty()) {
+ frame->func_name = strdup(func_name.c_str());
+ }
+
+ uintptr_t map_start;
+ frame->map_name = backtrace_obj_->GetMapName(frame->pc, &map_start);
+ if (frame->map_name) {
+ frame->map_offset = frame->pc - map_start;
+ }
+ }
+
+ backtrace->num_frames++;
+ } else {
+ num_ignore_frames--;
+ }
+ ret = unw_step (cursor);
+ } while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES);
+
+ delete cursor;
+ return true;
+}
+
+void UnwindCurrent::ExtractContext(void* sigcontext) {
+ unw_tdep_context_t* context = reinterpret_cast<unw_tdep_context_t*>(&context_);
+
+#if defined(__arm__)
+ const ucontext_t* uc = reinterpret_cast<const ucontext_t*>(sigcontext);
+
+ 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__)
+
+ typedef struct ucontext {
+ uint32_t sp;
+ uint32_t ra;
+ uint32_t pc;
+ } ucontext_t;
+
+ const ucontext_t* uc = (const ucontext_t*)sigcontext;
+
+ context->uc_mcontext.sp = uc->sp;
+ context->uc_mcontext.pc = uc->pc;
+ context->uc_mcontext.ra = uc->ra;
+#elif defined(__x86__)
+
+ #include <asm/sigcontext.h>
+ #include <asm/ucontext.h>
+ typedef struct ucontext ucontext_t;
+
+ const ucontext_t* uc = (const ucontext_t*)sigcontext;
+
+ context->uc_mcontext.gregs[REG_EBP] = uc->uc_mcontext.gregs[REG_EBP];
+ context->uc_mcontext.gregs[REG_ESP] = uc->uc_mcontext.gregs[REG_ESP];
+ context->uc_mcontext.gregs[REG_EIP] = uc->uc_mcontext.gregs[REG_EIP];
+#endif
+}
+
+//-------------------------------------------------------------------------
+// UnwindThread functions.
+//-------------------------------------------------------------------------
+UnwindThread::UnwindThread() {
+}
+
+UnwindThread::~UnwindThread() {
+}
+
+bool UnwindThread::Init() {
+ return true;
+}
+
+void UnwindThread::ThreadUnwind(
+ siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) {
+ ExtractContext(sigcontext);
+ UnwindFromContext(num_ignore_frames, false);
+}
+
+//-------------------------------------------------------------------------
+// C++ object creation function.
+//-------------------------------------------------------------------------
+Backtrace* CreateCurrentObj() {
+ return new BacktraceCurrent(new UnwindCurrent());
+}
+
+Backtrace* CreateThreadObj(pid_t tid) {
+ UnwindThread* thread_obj = new UnwindThread();
+ return new BacktraceThread(thread_obj, thread_obj, tid);
+}
diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h
new file mode 100644
index 0000000..7dc977d
--- /dev/null
+++ b/libbacktrace/UnwindCurrent.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_CURRENT_H
+#define _LIBBACKTRACE_UNWIND_CURRENT_H
+
+#include <string>
+
+#include "Backtrace.h"
+#include "BacktraceThread.h"
+
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+class UnwindCurrent : public BacktraceImpl {
+public:
+ UnwindCurrent();
+ virtual ~UnwindCurrent();
+
+ virtual bool Unwind(size_t num_ignore_frames);
+
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+
+ bool UnwindFromContext(size_t num_ignore_frames, bool resolve);
+
+ void ExtractContext(void* sigcontext);
+
+protected:
+ unw_context_t context_;
+};
+
+class UnwindThread : public UnwindCurrent, public BacktraceThreadInterface {
+public:
+ UnwindThread();
+ virtual ~UnwindThread();
+
+ virtual bool Init();
+
+ virtual void ThreadUnwind(
+ siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames);
+};
+
+#endif // _LIBBACKTRACE_UNWIND_CURRENT_H
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
new file mode 100644
index 0000000..628caa0
--- /dev/null
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 <sys/types.h>
+#include <string.h>
+
+#include <cutils/log.h>
+
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+
+#include "UnwindPtrace.h"
+
+UnwindPtrace::UnwindPtrace() : addr_space_(NULL), upt_info_(NULL) {
+}
+
+UnwindPtrace::~UnwindPtrace() {
+ if (upt_info_) {
+ _UPT_destroy(upt_info_);
+ upt_info_ = NULL;
+ }
+ if (addr_space_) {
+ unw_destroy_addr_space(addr_space_);
+ addr_space_ = NULL;
+ }
+}
+
+bool UnwindPtrace::Unwind(size_t num_ignore_frames) {
+ addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
+ if (!addr_space_) {
+ ALOGW("UnwindPtrace::Unwind: unw_create_addr_space failed.\n");
+ return false;
+ }
+
+ upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(backtrace_obj_->Tid()));
+ if (!upt_info_) {
+ ALOGW("UnwindPtrace::Unwind: Failed to create upt info.\n");
+ return false;
+ }
+
+ backtrace_t* backtrace = GetBacktraceData();
+ backtrace->num_frames = 0;
+
+ unw_cursor_t cursor;
+ int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
+ if (ret < 0) {
+ ALOGW("UnwindPtrace::Unwind: unw_init_remote failed %d\n", ret);
+ return false;
+ }
+
+ do {
+ unw_word_t pc;
+ ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
+ if (ret < 0) {
+ ALOGW("UnwindPtrace::Unwind: Failed to read IP %d\n", ret);
+ break;
+ }
+ unw_word_t sp;
+ ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
+ if (ret < 0) {
+ ALOGW("UnwindPtrace::Unwind: Failed to read SP %d\n", ret);
+ break;
+ }
+
+ if (num_ignore_frames == 0) {
+ size_t num_frames = backtrace->num_frames;
+ backtrace_frame_data_t* frame = &backtrace->frames[num_frames];
+ frame->pc = static_cast<uintptr_t>(pc);
+ frame->sp = static_cast<uintptr_t>(sp);
+ frame->stack_size = 0;
+ frame->map_name = NULL;
+ frame->map_offset = 0;
+ frame->func_name = NULL;
+ frame->func_offset = 0;
+
+ if (num_frames > 0) {
+ backtrace_frame_data_t* prev = &backtrace->frames[num_frames-1];
+ prev->stack_size = frame->sp - prev->sp;
+ }
+
+ std::string func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset);
+ if (!func_name.empty()) {
+ frame->func_name = strdup(func_name.c_str());
+ }
+
+ uintptr_t map_start;
+ frame->map_name = backtrace_obj_->GetMapName(frame->pc, &map_start);
+ if (frame->map_name) {
+ frame->map_offset = frame->pc - map_start;
+ }
+
+ backtrace->num_frames++;
+ } else {
+ num_ignore_frames--;
+ }
+ ret = unw_step (&cursor);
+ } while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES);
+
+ return true;
+}
+
+std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ *offset = 0;
+ char buf[512];
+ unw_word_t value;
+ if (unw_get_proc_name_by_ip(addr_space_, pc, buf, sizeof(buf), &value,
+ upt_info_) >= 0 && buf[0] != '\0') {
+ *offset = static_cast<uintptr_t>(value);
+ return buf;
+ }
+ return "";
+}
+
+//-------------------------------------------------------------------------
+// C++ object creation function.
+//-------------------------------------------------------------------------
+Backtrace* CreatePtraceObj(pid_t pid, pid_t tid) {
+ return new BacktracePtrace(new UnwindPtrace(), pid, tid);
+}
diff --git a/libbacktrace/unwind.h b/libbacktrace/UnwindPtrace.h
index 9ba96a4..781405b 100644
--- a/libbacktrace/unwind.h
+++ b/libbacktrace/UnwindPtrace.h
@@ -14,21 +14,27 @@
* limitations under the License.
*/
-#ifndef _UNWIND_H
-#define _UNWIND_H
+#ifndef _LIBBACKTRACE_UNWIND_PTRACE_H
+#define _LIBBACKTRACE_UNWIND_PTRACE_H
-bool local_get_data(backtrace_t* backtrace);
+#include <string>
-void local_free_data(backtrace_t* backtrace);
+#include "Backtrace.h"
-char* local_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
- uintptr_t* offset);
+#include <libunwind.h>
-bool remote_get_data(backtrace_t* backtrace);
+class UnwindPtrace : public BacktraceImpl {
+public:
+ UnwindPtrace();
+ virtual ~UnwindPtrace();
-void remote_free_data(backtrace_t* backtrace);
+ virtual bool Unwind(size_t num_ignore_frames);
-char* remote_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
- uintptr_t* offset);
+ virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
-#endif /* _UNWIND_H */
+private:
+ unw_addr_space_t addr_space_;
+ struct UPT_info* upt_info_;
+};
+
+#endif // _LIBBACKTRACE_UNWIND_PTRACE_H
diff --git a/libbacktrace/backtrace_test.c b/libbacktrace/backtrace_test.c
deleted file mode 100644
index 6155c9b..0000000
--- a/libbacktrace/backtrace_test.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <sys/ptrace.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <unistd.h>
-#include <inttypes.h>
-
-#include <backtrace/backtrace.h>
-
-#define FINISH(pid) dump_frames(&backtrace); if (pid < 0) exit(1); else return false;
-
-#define WAIT_INTERVAL_USECS 1000
-
-// Prototypes for functions in the test library.
-int test_level_one(int, int, int, int, bool (*)(pid_t));
-
-int test_recursive_call(int, bool (*)(pid_t));
-
-void dump_frames(const backtrace_t* backtrace) {
- for (size_t i = 0; i < backtrace->num_frames; i++) {
- printf("%zu ", i);
- if (backtrace->frames[i].map_name) {
- printf("%s", backtrace->frames[i].map_name);
- } else {
- printf("<unknown>");
- }
- if (backtrace->frames[i].proc_name) {
- printf(" %s", backtrace->frames[i].proc_name);
- if (backtrace->frames[i].proc_offset) {
- printf("+%" PRIuPTR, backtrace->frames[i].proc_offset);
- }
- }
- printf("\n");
- }
-}
-
-void wait_for_stop(pid_t pid, size_t max_usecs_to_wait) {
- siginfo_t si;
- size_t usecs_waited = 0;
-
- while (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) < 0 && (errno == EINTR || errno == ESRCH)) {
- if (usecs_waited >= max_usecs_to_wait) {
- printf("The process did not get to a stopping point in %zu usecs.\n",
- usecs_waited);
- break;
- }
- usleep(WAIT_INTERVAL_USECS);
- usecs_waited += WAIT_INTERVAL_USECS;
- }
-}
-
-bool check_frame(const backtrace_t* backtrace, size_t frame_num,
- const char* expected_name) {
- if (backtrace->frames[frame_num].proc_name == NULL) {
- printf(" Frame %zu function name expected %s, real value is NULL.\n",
- frame_num, expected_name);
- return false;
- }
- if (strcmp(backtrace->frames[frame_num].proc_name, expected_name) != 0) {
- printf(" Frame %zu function name expected %s, real value is %s.\n",
- frame_num, expected_name, backtrace->frames[frame_num].proc_name);
- return false;
- }
- return true;
-}
-
-bool verify_level_backtrace(pid_t pid) {
- const char* test_type;
- if (pid < 0) {
- test_type = "current";
- } else {
- test_type = "running";
- }
-
- backtrace_t backtrace;
- if (!backtrace_get_data(&backtrace, pid)) {
- printf(" backtrace_get_data failed on %s process.\n", test_type);
- FINISH(pid);
- }
-
- if (backtrace.num_frames == 0) {
- printf(" backtrace_get_data returned no frames for %s process.\n",
- test_type);
- FINISH(pid);
- }
-
- // Look through the frames starting at the highest to find the
- // frame we want.
- size_t frame_num = 0;
- for (size_t i = backtrace.num_frames-1; i > 2; i--) {
- if (backtrace.frames[i].proc_name != NULL &&
- strcmp(backtrace.frames[i].proc_name, "test_level_one") == 0) {
- frame_num = i;
- break;
- }
- }
- if (!frame_num) {
- printf(" backtrace_get_data did not include the test_level_one frame.\n");
- FINISH(pid);
- }
-
- if (!check_frame(&backtrace, frame_num, "test_level_one")) {
- FINISH(pid);
- }
- if (!check_frame(&backtrace, frame_num-1, "test_level_two")) {
- FINISH(pid);
- }
- if (!check_frame(&backtrace, frame_num-2, "test_level_three")) {
- FINISH(pid);
- }
- if (!check_frame(&backtrace, frame_num-3, "test_level_four")) {
- FINISH(pid);
- }
- backtrace_free_data(&backtrace);
-
- return true;
-}
-
-bool verify_max_backtrace(pid_t pid) {
- const char* test_type;
- if (pid < 0) {
- test_type = "current";
- } else {
- test_type = "running";
- }
-
- backtrace_t backtrace;
- if (!backtrace_get_data(&backtrace, pid)) {
- printf(" backtrace_get_data failed on %s process.\n", test_type);
- FINISH(pid);
- }
-
- if (backtrace.num_frames != MAX_BACKTRACE_FRAMES) {
- printf(" backtrace_get_data %s process max frame check failed:\n",
- test_type);
- printf(" Expected num frames to be %zu, found %zu\n",
- MAX_BACKTRACE_FRAMES, backtrace.num_frames);
- FINISH(pid);
- }
- backtrace_free_data(&backtrace);
-
- return true;
-}
-
-void verify_proc_test(pid_t pid, bool (*verify_func)(pid_t)) {
- printf(" Waiting 5 seconds for process to get to infinite loop.\n");
- sleep(5);
- if (ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) {
- printf("Failed to attach to pid %d\n", pid);
- kill(pid, SIGKILL);
- exit(1);
- }
-
- // Wait up to 1 second for the process to get to a point that we can trace it.
- wait_for_stop(pid, 1000000);
-
- bool pass = verify_func(pid);
- if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) {
- printf("Failed to detach from pid %d\n", pid);
- kill(pid, SIGKILL);
- exit(1);
- }
-
- kill(pid, SIGKILL);
- int status;
- if (waitpid(pid, &status, 0) != pid) {
- printf("Forked process did not terminate properly.\n");
- exit(1);
- }
-
- if (!pass) {
- exit(1);
- }
-}
-
-int main() {
- printf("Running level test on current process...\n");
- int value = test_level_one(1, 2, 3, 4, verify_level_backtrace);
- if (value == 0) {
- printf("This should never happen.\n");
- exit(1);
- }
- printf(" Passed.\n");
-
- printf("Running max level test on current process...\n");
- value = test_recursive_call(MAX_BACKTRACE_FRAMES+10, verify_max_backtrace);
- if (value == 0) {
- printf("This should never happen.\n");
- exit(1);
- }
- printf(" Passed.\n");
-
- printf("Running level test on process...\n");
- pid_t pid;
- if ((pid = fork()) == 0) {
- value = test_level_one(1, 2, 3, 4, NULL);
- if (value == 0) {
- printf("This should never happen.\n");
- }
- exit(1);
- }
- verify_proc_test(pid, verify_level_backtrace);
- printf(" Passed.\n");
-
- printf("Running max frame test on process...\n");
- if ((pid = fork()) == 0) {
- value = test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL);
- if (value == 0) {
- printf("This should never happen.\n");
- }
- exit(1);
- }
- verify_proc_test(pid, verify_max_backtrace);
- printf(" Passed.\n");
-
- printf("All tests passed.\n");
- return 0;
-}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
new file mode 100644
index 0000000..48e2bdc
--- /dev/null
+++ b/libbacktrace/backtrace_test.cpp
@@ -0,0 +1,660 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <backtrace/backtrace.h>
+
+#include <cutils/atomic.h>
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include "thread_utils.h"
+
+// Number of microseconds per milliseconds.
+#define US_PER_MSEC 1000
+
+// Number of nanoseconds in a second.
+#define NS_PER_SEC 1000000000ULL
+
+// Number of simultaneous dumping operations to perform.
+#define NUM_THREADS 20
+
+// Number of simultaneous threads running in our forked process.
+#define NUM_PTRACE_THREADS 5
+
+typedef struct {
+ pid_t tid;
+ int32_t state;
+ pthread_t threadId;
+} thread_t;
+
+typedef struct {
+ thread_t thread;
+ backtrace_context_t context;
+ int32_t* now;
+ int32_t done;
+} dump_thread_t;
+
+extern "C" {
+// Prototypes for functions in the test library.
+int test_level_one(int, int, int, int, void (*)(void*), void*);
+
+int test_recursive_call(int, void (*)(void*), void*);
+}
+
+uint64_t NanoTime() {
+ struct timespec t = { 0, 0 };
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
+}
+
+void DumpFrames(const backtrace_context_t* context) {
+ if (context->backtrace->num_frames == 0) {
+ printf(" No frames to dump\n");
+ } else {
+ char line[512];
+ for (size_t i = 0; i < context->backtrace->num_frames; i++) {
+ backtrace_format_frame_data(context, i, line, sizeof(line));
+ printf(" %s\n", line);
+ }
+ }
+}
+
+void WaitForStop(pid_t pid) {
+ uint64_t start = NanoTime();
+
+ siginfo_t si;
+ while (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) < 0 && (errno == EINTR || errno == ESRCH)) {
+ if ((NanoTime() - start) > NS_PER_SEC) {
+ printf("The process did not get to a stopping point in 1 second.\n");
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+}
+
+bool ReadyLevelBacktrace(const backtrace_t* backtrace) {
+ // See if test_level_four is in the backtrace.
+ bool found = false;
+ for (size_t i = 0; i < backtrace->num_frames; i++) {
+ if (backtrace->frames[i].func_name != NULL &&
+ strcmp(backtrace->frames[i].func_name, "test_level_four") == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
+void VerifyLevelDump(const backtrace_t* backtrace) {
+ ASSERT_GT(backtrace->num_frames, static_cast<size_t>(0));
+ ASSERT_LT(backtrace->num_frames, static_cast<size_t>(MAX_BACKTRACE_FRAMES));
+
+ // Look through the frames starting at the highest to find the
+ // frame we want.
+ size_t frame_num = 0;
+ for (size_t i = backtrace->num_frames-1; i > 2; i--) {
+ if (backtrace->frames[i].func_name != NULL &&
+ strcmp(backtrace->frames[i].func_name, "test_level_one") == 0) {
+ frame_num = i;
+ break;
+ }
+ }
+ ASSERT_GT(frame_num, static_cast<size_t>(0));
+
+ ASSERT_TRUE(NULL != backtrace->frames[frame_num].func_name);
+ ASSERT_STREQ(backtrace->frames[frame_num].func_name, "test_level_one");
+ ASSERT_TRUE(NULL != backtrace->frames[frame_num-1].func_name);
+ ASSERT_STREQ(backtrace->frames[frame_num-1].func_name, "test_level_two");
+ ASSERT_TRUE(NULL != backtrace->frames[frame_num-2].func_name);
+ ASSERT_STREQ(backtrace->frames[frame_num-2].func_name, "test_level_three");
+ ASSERT_TRUE(NULL != backtrace->frames[frame_num-3].func_name);
+ ASSERT_STREQ(backtrace->frames[frame_num-3].func_name, "test_level_four");
+}
+
+void VerifyLevelBacktrace(void*) {
+ backtrace_context_t context;
+
+ ASSERT_TRUE(backtrace_create_context(&context, -1, -1, 0));
+
+ VerifyLevelDump(context.backtrace);
+
+ backtrace_destroy_context(&context);
+}
+
+bool ReadyMaxBacktrace(const backtrace_t* backtrace) {
+ return (backtrace->num_frames == MAX_BACKTRACE_FRAMES);
+}
+
+void VerifyMaxDump(const backtrace_t* backtrace) {
+ ASSERT_EQ(backtrace->num_frames, static_cast<size_t>(MAX_BACKTRACE_FRAMES));
+ // Verify that the last frame is our recursive call.
+ ASSERT_TRUE(NULL != backtrace->frames[MAX_BACKTRACE_FRAMES-1].func_name);
+ ASSERT_STREQ(backtrace->frames[MAX_BACKTRACE_FRAMES-1].func_name,
+ "test_recursive_call");
+}
+
+void VerifyMaxBacktrace(void*) {
+ backtrace_context_t context;
+
+ ASSERT_TRUE(backtrace_create_context(&context, -1, -1, 0));
+
+ VerifyMaxDump(context.backtrace);
+
+ backtrace_destroy_context(&context);
+}
+
+void ThreadSetState(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+ android_atomic_acquire_store(1, &thread->state);
+ volatile int i = 0;
+ while (thread->state) {
+ i++;
+ }
+}
+
+void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(const backtrace_t*)) {
+ backtrace_context_t context;
+
+ backtrace_create_context(&context, getpid(), tid, 0);
+
+ VerifyFunc(context.backtrace);
+
+ backtrace_destroy_context(&context);
+}
+
+bool WaitForNonZero(int32_t* value, uint64_t seconds) {
+ uint64_t start = NanoTime();
+ do {
+ if (android_atomic_acquire_load(value)) {
+ return true;
+ }
+ } while ((NanoTime() - start) < seconds * NS_PER_SEC);
+ return false;
+}
+
+TEST(libbacktrace, local_trace) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, NULL), 0);
+}
+
+void VerifyIgnoreFrames(
+ const backtrace_t* bt_all, const backtrace_t* bt_ign1,
+ const backtrace_t* bt_ign2, const char* cur_proc) {
+ EXPECT_EQ(bt_all->num_frames, bt_ign1->num_frames + 1);
+ EXPECT_EQ(bt_all->num_frames, bt_ign2->num_frames + 2);
+
+ // Check all of the frames are the same > the current frame.
+ bool check = (cur_proc == NULL);
+ for (size_t i = 0; i < bt_ign2->num_frames; i++) {
+ if (check) {
+ EXPECT_EQ(bt_ign2->frames[i].pc, bt_ign1->frames[i+1].pc);
+ EXPECT_EQ(bt_ign2->frames[i].sp, bt_ign1->frames[i+1].sp);
+ EXPECT_EQ(bt_ign2->frames[i].stack_size, bt_ign1->frames[i+1].stack_size);
+
+ EXPECT_EQ(bt_ign2->frames[i].pc, bt_all->frames[i+2].pc);
+ EXPECT_EQ(bt_ign2->frames[i].sp, bt_all->frames[i+2].sp);
+ EXPECT_EQ(bt_ign2->frames[i].stack_size, bt_all->frames[i+2].stack_size);
+ }
+ if (!check && bt_ign2->frames[i].func_name &&
+ strcmp(bt_ign2->frames[i].func_name, cur_proc) == 0) {
+ check = true;
+ }
+ }
+}
+
+void VerifyLevelIgnoreFrames(void*) {
+ backtrace_context_t all;
+ ASSERT_TRUE(backtrace_create_context(&all, -1, -1, 0));
+ ASSERT_TRUE(all.backtrace != NULL);
+
+ backtrace_context_t ign1;
+ ASSERT_TRUE(backtrace_create_context(&ign1, -1, -1, 1));
+ ASSERT_TRUE(ign1.backtrace != NULL);
+
+ backtrace_context_t ign2;
+ ASSERT_TRUE(backtrace_create_context(&ign2, -1, -1, 2));
+ ASSERT_TRUE(ign2.backtrace != NULL);
+
+ VerifyIgnoreFrames(all.backtrace, ign1.backtrace, ign2.backtrace,
+ "VerifyLevelIgnoreFrames");
+
+ backtrace_destroy_context(&all);
+ backtrace_destroy_context(&ign1);
+ backtrace_destroy_context(&ign2);
+}
+
+TEST(libbacktrace, local_trace_ignore_frames) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, NULL), 0);
+}
+
+TEST(libbacktrace, local_max_trace) {
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, NULL), 0);
+}
+
+void VerifyProcTest(pid_t pid, pid_t tid,
+ bool (*ReadyFunc)(const backtrace_t*),
+ void (*VerifyFunc)(const backtrace_t*)) {
+ pid_t ptrace_tid;
+ if (tid < 0) {
+ ptrace_tid = pid;
+ } else {
+ ptrace_tid = tid;
+ }
+ uint64_t start = NanoTime();
+ bool verified = false;
+ do {
+ usleep(US_PER_MSEC);
+ if (ptrace(PTRACE_ATTACH, ptrace_tid, 0, 0) == 0) {
+ // Wait for the process to get to a stopping point.
+ WaitForStop(ptrace_tid);
+
+ backtrace_context_t context;
+ ASSERT_TRUE(backtrace_create_context(&context, pid, tid, 0));
+ if (ReadyFunc(context.backtrace)) {
+ VerifyFunc(context.backtrace);
+ verified = true;
+ }
+ backtrace_destroy_context(&context);
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, ptrace_tid, 0, 0) == 0);
+ }
+ // If 5 seconds have passed, then we are done.
+ } while (!verified && (NanoTime() - start) <= 5 * NS_PER_SEC);
+ ASSERT_TRUE(verified);
+}
+
+TEST(libbacktrace, ptrace_trace) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ exit(1);
+ }
+ VerifyProcTest(pid, -1, ReadyLevelBacktrace, VerifyLevelDump);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+TEST(libbacktrace, ptrace_max_trace) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0);
+ exit(1);
+ }
+ VerifyProcTest(pid, -1, ReadyMaxBacktrace, VerifyMaxDump);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+void VerifyProcessIgnoreFrames(const backtrace_t* bt_all) {
+ pid_t pid = bt_all->pid;
+
+ backtrace_context_t ign1;
+ ASSERT_TRUE(backtrace_create_context(&ign1, pid, -1, 1));
+ ASSERT_TRUE(ign1.backtrace != NULL);
+
+ backtrace_context_t ign2;
+ ASSERT_TRUE(backtrace_create_context(&ign2, pid, -1, 2));
+ ASSERT_TRUE(ign2.backtrace != NULL);
+
+ VerifyIgnoreFrames(bt_all, ign1.backtrace, ign2.backtrace, NULL);
+
+ backtrace_destroy_context(&ign1);
+ backtrace_destroy_context(&ign2);
+}
+
+TEST(libbacktrace, ptrace_ignore_frames) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ exit(1);
+ }
+ VerifyProcTest(pid, -1, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+// Create a process with multiple threads and dump all of the threads.
+void* PtraceThreadLevelRun(void*) {
+ EXPECT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ return NULL;
+}
+
+void GetThreads(pid_t pid, std::vector<pid_t>* threads) {
+ // Get the list of tasks.
+ char task_path[128];
+ snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
+
+ DIR* tasks_dir = opendir(task_path);
+ ASSERT_TRUE(tasks_dir != NULL);
+ struct dirent* entry;
+ while ((entry = readdir(tasks_dir)) != NULL) {
+ char* end;
+ pid_t tid = strtoul(entry->d_name, &end, 10);
+ if (*end == '\0') {
+ threads->push_back(tid);
+ }
+ }
+ closedir(tasks_dir);
+}
+
+TEST(libbacktrace, ptrace_threads) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0);
+ }
+ ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+ exit(1);
+ }
+
+ // Check to see that all of the threads are running before unwinding.
+ std::vector<pid_t> threads;
+ uint64_t start = NanoTime();
+ do {
+ usleep(US_PER_MSEC);
+ threads.clear();
+ GetThreads(pid, &threads);
+ } while ((threads.size() != NUM_PTRACE_THREADS + 1) &&
+ ((NanoTime() - start) <= 5 * NS_PER_SEC));
+ ASSERT_EQ(threads.size(), static_cast<size_t>(NUM_PTRACE_THREADS + 1));
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+ WaitForStop(pid);
+ for (std::vector<int>::const_iterator it = threads.begin(); it != threads.end(); ++it) {
+ // Skip the current forked process, we only care about the threads.
+ if (pid == *it) {
+ continue;
+ }
+ VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump);
+ }
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+void VerifyLevelThread(void*) {
+ backtrace_context_t context;
+
+ ASSERT_TRUE(backtrace_create_context(&context, getpid(), gettid(), 0));
+
+ VerifyLevelDump(context.backtrace);
+
+ backtrace_destroy_context(&context);
+}
+
+TEST(libbacktrace, thread_current_level) {
+ ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, NULL), 0);
+}
+
+void VerifyMaxThread(void*) {
+ backtrace_context_t context;
+
+ ASSERT_TRUE(backtrace_create_context(&context, getpid(), gettid(), 0));
+
+ VerifyMaxDump(context.backtrace);
+
+ backtrace_destroy_context(&context);
+}
+
+TEST(libbacktrace, thread_current_max) {
+ ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, NULL), 0);
+}
+
+void* ThreadLevelRun(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+
+ thread->tid = gettid();
+ EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
+ return NULL;
+}
+
+TEST(libbacktrace, thread_level_trace) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0 };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ // Save the current signal action and make sure it is restored afterwards.
+ struct sigaction cur_action;
+ ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0);
+
+ backtrace_context_t context;
+
+ ASSERT_TRUE(backtrace_create_context(&context, getpid(), thread_data.tid,0));
+
+ VerifyLevelDump(context.backtrace);
+
+ backtrace_destroy_context(&context);
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ // Verify that the old action was restored.
+ struct sigaction new_action;
+ ASSERT_TRUE(sigaction(SIGURG, NULL, &new_action) == 0);
+ EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction);
+ EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
+}
+
+TEST(libbacktrace, thread_ignore_frames) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0 };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ backtrace_context_t all;
+ ASSERT_TRUE(backtrace_create_context(&all, getpid(), thread_data.tid, 0));
+
+ backtrace_context_t ign1;
+ ASSERT_TRUE(backtrace_create_context(&ign1, getpid(), thread_data.tid, 1));
+
+ backtrace_context_t ign2;
+ ASSERT_TRUE(backtrace_create_context(&ign2, getpid(), thread_data.tid, 2));
+
+ VerifyIgnoreFrames(all.backtrace, ign1.backtrace, ign2.backtrace, NULL);
+
+ backtrace_destroy_context(&all);
+ backtrace_destroy_context(&ign1);
+ backtrace_destroy_context(&ign2);
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+}
+
+void* ThreadMaxRun(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+
+ thread->tid = gettid();
+ EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
+ return NULL;
+}
+
+TEST(libbacktrace, thread_max_trace) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0 };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadMaxRun, &thread_data) == 0);
+
+ // Wait for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ backtrace_context_t context;
+
+ ASSERT_TRUE(backtrace_create_context(&context, getpid(), thread_data.tid, 0));
+
+ VerifyMaxDump(context.backtrace);
+
+ backtrace_destroy_context(&context);
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+}
+
+void* ThreadDump(void* data) {
+ dump_thread_t* dump = reinterpret_cast<dump_thread_t*>(data);
+ while (true) {
+ if (android_atomic_acquire_load(dump->now)) {
+ break;
+ }
+ }
+
+ dump->context.data = NULL;
+ dump->context.backtrace = NULL;
+
+ // The status of the actual unwind will be checked elsewhere.
+ backtrace_create_context(&dump->context, getpid(), dump->thread.tid, 0);
+
+ android_atomic_acquire_store(1, &dump->done);
+
+ return NULL;
+}
+
+TEST(libbacktrace, thread_multiple_dump) {
+ // Dump NUM_THREADS simultaneously.
+ std::vector<thread_t> runners(NUM_THREADS);
+ std::vector<dump_thread_t> dumpers(NUM_THREADS);
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ // Launch the runners, they will spin in hard loops doing nothing.
+ runners[i].tid = 0;
+ runners[i].state = 0;
+ ASSERT_TRUE(pthread_create(&runners[i].threadId, &attr, ThreadMaxRun, &runners[i]) == 0);
+ }
+
+ // Wait for tids to be set.
+ for (std::vector<thread_t>::iterator it = runners.begin(); it != runners.end(); ++it) {
+ ASSERT_TRUE(WaitForNonZero(&it->state, 10));
+ }
+
+ // Start all of the dumpers at once, they will spin until they are signalled
+ // to begin their dump run.
+ int32_t dump_now = 0;
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ dumpers[i].thread.tid = runners[i].tid;
+ dumpers[i].thread.state = 0;
+ dumpers[i].done = 0;
+ dumpers[i].now = &dump_now;
+
+ ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
+ }
+
+ // Start all of the dumpers going at once.
+ android_atomic_acquire_store(1, &dump_now);
+
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 10));
+
+ // Tell the runner thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &runners[i].state);
+
+ ASSERT_TRUE(dumpers[i].context.backtrace != NULL);
+ VerifyMaxDump(dumpers[i].context.backtrace);
+ backtrace_destroy_context(&dumpers[i].context);
+ }
+}
+
+TEST(libbacktrace, format_test) {
+ backtrace_context_t context;
+
+ ASSERT_TRUE(backtrace_create_context(&context, -1, -1, 0));
+ ASSERT_TRUE(context.backtrace != NULL);
+
+ backtrace_frame_data_t* frame = &context.backtrace->frames[1];
+ backtrace_frame_data_t save_frame = *frame;
+
+ memset(frame, 0, sizeof(backtrace_frame_data_t));
+ char buf[512];
+ backtrace_format_frame_data(&context, 1, buf, sizeof(buf));
+#if defined(__LP64__)
+ EXPECT_STREQ(buf, "#01 pc 0000000000000000 <unknown>");
+#else
+ EXPECT_STREQ(buf, "#01 pc 00000000 <unknown>");
+#endif
+
+ frame->pc = 0x12345678;
+ frame->map_name = "MapFake";
+ backtrace_format_frame_data(&context, 1, buf, sizeof(buf));
+#if defined(__LP64__)
+ EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake");
+#else
+ EXPECT_STREQ(buf, "#01 pc 12345678 MapFake");
+#endif
+
+ frame->func_name = "ProcFake";
+ backtrace_format_frame_data(&context, 1, buf, sizeof(buf));
+#if defined(__LP64__)
+ EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake (ProcFake)");
+#else
+ EXPECT_STREQ(buf, "#01 pc 12345678 MapFake (ProcFake)");
+#endif
+
+ frame->func_offset = 645;
+ backtrace_format_frame_data(&context, 1, buf, sizeof(buf));
+#if defined(__LP64__)
+ EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake (ProcFake+645)");
+#else
+ EXPECT_STREQ(buf, "#01 pc 12345678 MapFake (ProcFake+645)");
+#endif
+
+ *frame = save_frame;
+
+ backtrace_destroy_context(&context);
+}
diff --git a/libbacktrace/backtrace_testlib.c b/libbacktrace/backtrace_testlib.c
index 9400549..d4d15db 100644
--- a/libbacktrace/backtrace_testlib.c
+++ b/libbacktrace/backtrace_testlib.c
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-#include <stdbool.h>
-#include <unistd.h>
+#include <stdio.h>
int test_level_four(int one, int two, int three, int four,
- bool (*callback_func)(pid_t)) {
+ void (*callback_func)(void*), void* data) {
if (callback_func != NULL) {
- callback_func(-1);
+ callback_func(data);
} else {
while (1) {
}
@@ -29,25 +28,25 @@ int test_level_four(int one, int two, int three, int four,
}
int test_level_three(int one, int two, int three, int four,
- bool (*callback_func)(pid_t)) {
- return test_level_four(one+3, two+6, three+9, four+12, callback_func) + 3;
+ void (*callback_func)(void*), void* data) {
+ return test_level_four(one+3, two+6, three+9, four+12, callback_func, data) + 3;
}
int test_level_two(int one, int two, int three, int four,
- bool (*callback_func)(pid_t)) {
- return test_level_three(one+2, two+4, three+6, four+8, callback_func) + 2;
+ void (*callback_func)(void*), void* data) {
+ return test_level_three(one+2, two+4, three+6, four+8, callback_func, data) + 2;
}
int test_level_one(int one, int two, int three, int four,
- bool (*callback_func)(pid_t)) {
- return test_level_two(one+1, two+2, three+3, four+4, callback_func) + 1;
+ void (*callback_func)(void*), void* data) {
+ return test_level_two(one+1, two+2, three+3, four+4, callback_func, data) + 1;
}
-int test_recursive_call(int level, bool (*callback_func)(pid_t)) {
+int test_recursive_call(int level, void (*callback_func)(void*), void* data) {
if (level > 0) {
- return test_recursive_call(level - 1, callback_func) + level;
+ return test_recursive_call(level - 1, callback_func, data) + level;
} else if (callback_func != NULL) {
- callback_func(-1);
+ callback_func(data);
} else {
while (1) {
}
diff --git a/libbacktrace/common.c b/libbacktrace/common.c
deleted file mode 100644
index 20786f4..0000000
--- a/libbacktrace/common.c
+++ /dev/null
@@ -1,113 +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 <errno.h>
-#include <stdlib.h>
-#include <sys/ptrace.h>
-#include <inttypes.h>
-
-#include <cutils/log.h>
-#include <backtrace/backtrace.h>
-
-#include "common.h"
-
-bool backtrace_read_word(const backtrace_t* backtrace, uintptr_t ptr,
- uint32_t* out_value) {
- if (ptr & 3) {
- ALOGW("backtrace_read_word: invalid pointer %p", (void*)ptr);
- *out_value = (uint32_t)-1;
- return false;
- }
-
- // Check if reading from the current process, or a different process.
- if (backtrace->tid < 0) {
- const backtrace_map_info_t* map_info = backtrace_find_map_info(backtrace->map_info_list, ptr);
- if (map_info && map_info->is_readable) {
- *out_value = *(uint32_t*)ptr;
- return true;
- } else {
- ALOGW("backtrace_read_word: pointer %p not in a readbale map", (void*)ptr);
- *out_value = (uint32_t)-1;
- return false;
- }
- } else {
-#if defined(__APPLE__)
- ALOGW("read_word: MacOS does not support reading from another pid.\n");
- return false;
-#else
- // ptrace() returns -1 and sets errno when the operation fails.
- // To disambiguate -1 from a valid result, we clear errno beforehand.
- errno = 0;
- *out_value = ptrace(PTRACE_PEEKTEXT, backtrace->tid, (void*)ptr, NULL);
- if (*out_value == (uint32_t)-1 && errno) {
- ALOGW("try_get_word: invalid pointer 0x%08x reading from tid %d, "
- "ptrace() errno=%d", ptr, backtrace->tid, errno);
- return false;
- }
- return true;
- }
-#endif
-}
-
-const char *backtrace_get_map_info(
- const backtrace_t* backtrace, uintptr_t pc, uintptr_t* start_pc) {
- const backtrace_map_info_t* map_info = backtrace_find_map_info(backtrace->map_info_list, pc);
- if (map_info) {
- if (start_pc) {
- *start_pc = map_info->start;
- }
- return map_info->name;
- }
- return NULL;
-}
-
-void backtrace_format_frame_data(
- const backtrace_frame_data_t* frame, size_t frame_num, char *buf, size_t buf_size) {
- uintptr_t relative_pc;
- const char* map_name;
- if (frame->map_name) {
- map_name = frame->map_name;
- } else {
- map_name = "<unknown>";
- }
- if (frame->map_offset) {
- relative_pc = frame->map_offset;
- } else {
- relative_pc = frame->pc;
- }
- if (frame->proc_name && frame->proc_offset) {
- snprintf(buf, buf_size, "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")",
- frame_num, (int)sizeof(uintptr_t)*2, relative_pc, map_name,
- frame->proc_name, frame->proc_offset);
- } else if (frame->proc_name) {
- snprintf(buf, buf_size, "#%02zu pc %0*" PRIxPTR " %s (%s)", frame_num,
- (int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->proc_name);
- } else {
- snprintf(buf, buf_size, "#%02zu pc %0*" PRIxPTR " %s", frame_num,
- (int)sizeof(uintptr_t)*2, relative_pc, map_name);
- }
-}
-
-void free_frame_data(backtrace_t* backtrace) {
- for (size_t i = 0; i < backtrace->num_frames; i++) {
- if (backtrace->frames[i].proc_name) {
- free(backtrace->frames[i].proc_name);
- }
- }
- backtrace->num_frames = 0;
-}
diff --git a/libbacktrace/corkscrew.c b/libbacktrace/corkscrew.c
deleted file mode 100644
index 899409a..0000000
--- a/libbacktrace/corkscrew.c
+++ /dev/null
@@ -1,130 +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 <string.h>
-
-#include <cutils/log.h>
-#include <backtrace/backtrace.h>
-
-#include <corkscrew/backtrace.h>
-
-#define __USE_GNU
-#include <dlfcn.h>
-
-#include "common.h"
-#include "demangle.h"
-
-bool backtrace_get_data(backtrace_t* backtrace, pid_t tid) {
- backtrace->num_frames = 0;
- backtrace->tid = tid;
- backtrace->private_data = NULL;
- backtrace->map_info_list = backtrace_create_map_info_list(tid);
-
- backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
- ssize_t num_frames;
- if (tid < 0) {
- // Get data for the current thread.
- num_frames = unwind_backtrace(frames, 0, MAX_BACKTRACE_FRAMES);
- } else {
- // Get data for a different thread.
- ptrace_context_t* ptrace_context = load_ptrace_context(tid);
- backtrace->private_data = ptrace_context;
-
- num_frames = unwind_backtrace_ptrace(
- tid, ptrace_context, frames, 0, MAX_BACKTRACE_FRAMES);
- }
- if (num_frames < 0) {
- ALOGW("backtrace_get_data: unwind_backtrace_ptrace failed %d\n",
- num_frames);
- backtrace_free_data(backtrace);
- return false;
- }
-
- backtrace->num_frames = num_frames;
- backtrace_frame_data_t* frame;
- uintptr_t map_start;
- for (size_t i = 0; i < backtrace->num_frames; i++) {
- frame = &backtrace->frames[i];
- frame->pc = frames[i].absolute_pc;
- frame->sp = frames[i].stack_top;
- frame->stack_size = frames[i].stack_size;
-
- frame->map_offset = 0;
- frame->map_name = backtrace_get_map_info(backtrace, frame->pc, &map_start);
- if (frame->map_name) {
- frame->map_offset = frame->pc - map_start;
- }
-
- frame->proc_offset = 0;
- frame->proc_name = backtrace_get_proc_name(backtrace, frame->pc, &frame->proc_offset);
- }
-
- return true;
-}
-
-void backtrace_free_data(backtrace_t* backtrace) {
- free_frame_data(backtrace);
-
- if (backtrace->map_info_list) {
- backtrace_destroy_map_info_list(backtrace->map_info_list);
- backtrace->map_info_list = NULL;
- }
-
- if (backtrace->private_data) {
- ptrace_context_t* ptrace_context = (ptrace_context_t*)backtrace->private_data;
- free_ptrace_context(ptrace_context);
- backtrace->private_data = NULL;
- }
-}
-
-char* backtrace_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
- uintptr_t* offset) {
- const char* symbol_name = NULL;
- *offset = 0;
- if (backtrace->tid < 0) {
- // Get information about the current thread.
- Dl_info info;
- const backtrace_map_info_t* map_info;
- map_info = backtrace_find_map_info(backtrace->map_info_list, pc);
- if (map_info && dladdr((const void*)pc, &info) && info.dli_sname) {
- *offset = pc - map_info->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase;
- symbol_name = info.dli_sname;
- }
- } else {
- // Get information about a different thread.
- ptrace_context_t* ptrace_context = (ptrace_context_t*)backtrace->private_data;
- const map_info_t* map_info;
- const symbol_t* symbol;
- find_symbol_ptrace(ptrace_context, pc, &map_info, &symbol);
- if (symbol) {
- if (map_info) {
- *offset = pc - map_info->start - symbol->start;
- }
- symbol_name = symbol->name;
- }
- }
-
- char* name = NULL;
- if (symbol_name) {
- name = demangle_symbol_name(symbol_name);
- if (!name) {
- name = strdup(symbol_name);
- }
- }
- return name;
-}
diff --git a/libbacktrace/demangle.h b/libbacktrace/demangle.h
deleted file mode 100644
index a5318ac..0000000
--- a/libbacktrace/demangle.h
+++ /dev/null
@@ -1,25 +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 _DEMANGLE_H
-#define _DEMANGLE_H
-
-/* Called to demangle a symbol name to be printed. Returns an allocated
- * string that must be freed by the caller.
- */
-char* demangle_symbol_name(const char* name);
-
-#endif /* _DEMANGLE_H */
diff --git a/libbacktrace/stubs.c b/libbacktrace/stubs.c
deleted file mode 100644
index 1741601..0000000
--- a/libbacktrace/stubs.c
+++ /dev/null
@@ -1,53 +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 <cutils/log.h>
-#include <backtrace/backtrace.h>
-
-bool backtrace_get_data(backtrace_t* backtrace, pid_t tid) {
- ALOGW("backtrace_get_data: unsupported architecture.\n");
- return true;
-}
-
-void backtrace_free_data(backtrace_t* backtrace) {
- ALOGW("backtrace_free_data: unsupported architecture.\n");
-}
-
-bool backtrace_read_word(const backtrace_t* backtrace, uintptr_t ptr,
- uint32_t* out_value) {
- ALOGW("backtrace_read_word: unsupported architecture.\n");
- return false;
-}
-
-const char *backtrace_get_map_info(const backtrace_t* backtrace,
- uintptr_t pc, uintptr_t* start_pc) {
- ALOGW("backtrace_get_map_info: unsupported architecture.\n");
- return NULL;
-}
-
-char* backtrace_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
- uintptr_t* offset) {
- ALOGW("backtrace_get_proc_name: unsupported architecture.\n");
- return NULL;
-}
-
-void backtrace_format_frame_data(
- const backtrace_frame_data_t* frame, size_t frame_num, char *buf, size_t buf_size) {
- ALOGW("backtrace_format_frame_data: unsupported architecture.\n");
- buf[0] = '\0';
-}
diff --git a/libbacktrace/demangle.c b/libbacktrace/thread_utils.c
index de9a460..6f4cd3c 100644
--- a/libbacktrace/demangle.c
+++ b/libbacktrace/thread_utils.c
@@ -14,20 +14,29 @@
* limitations under the License.
*/
-#include <sys/types.h>
+#include "thread_utils.h"
-#include "demangle.h"
+#if defined(__APPLE__)
-extern char* __cxa_demangle (const char* mangled, char* buf, size_t* len,
- int* status);
+#include <sys/syscall.h>
-char* demangle_symbol_name(const char* name) {
-#if defined(__APPLE__)
- // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
- if (name != NULL && name[0] != '_') {
- return NULL;
- }
-#endif
- // __cxa_demangle handles NULL by returning NULL
- return __cxa_demangle(name, 0, 0, 0);
+// Mac OS >= 10.6 has a system call equivalent to Linux's gettid().
+pid_t gettid() {
+ return syscall(SYS_thread_selfid);
}
+
+#elif !defined(__BIONIC__)
+
+// glibc doesn't implement or export either gettid or tgkill.
+#include <unistd.h>
+#include <sys/syscall.h>
+
+pid_t gettid() {
+ return syscall(__NR_gettid);
+}
+
+int tgkill(int tgid, int tid, int sig) {
+ return syscall(__NR_tgkill, tgid, tid, sig);
+}
+
+#endif
diff --git a/libbacktrace/common.h b/libbacktrace/thread_utils.h
index 9eef964..ae4c929 100644
--- a/libbacktrace/common.h
+++ b/libbacktrace/thread_utils.h
@@ -14,12 +14,17 @@
* limitations under the License.
*/
-#ifndef _COMMON_H
-#define _COMMON_H
+#ifndef _LIBBACKTRACE_THREAD_UTILS_H
+#define _LIBBACKTRACE_THREAD_UTILS_H
-#include <backtrace/backtrace.h>
+#include <unistd.h>
-/* Common routine to free any data allocated to store frame information. */
-void free_frame_data(backtrace_t* backtrace);
+__BEGIN_DECLS
-#endif /* _COMMON_H */
+int tgkill(int tgid, int tid, int sig);
+
+pid_t gettid();
+
+__END_DECLS
+
+#endif /* _LIBBACKTRACE_THREAD_UTILS_H */
diff --git a/libbacktrace/unwind.c b/libbacktrace/unwind.c
deleted file mode 100644
index f75e518..0000000
--- a/libbacktrace/unwind.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <backtrace/backtrace.h>
-
-#include "common.h"
-#include "unwind.h"
-
-bool backtrace_get_data(backtrace_t* backtrace, pid_t tid) {
- backtrace->num_frames = 0;
- backtrace->tid = tid;
-
- backtrace->map_info_list = backtrace_create_map_info_list(tid);
- if (tid < 0) {
- return local_get_data(backtrace);
- } else {
- return remote_get_data(backtrace);
- }
-}
-
-/* Free any memory related to the frame data. */
-void backtrace_free_data(backtrace_t* backtrace) {
- free_frame_data(backtrace);
-
- if (backtrace->map_info_list) {
- backtrace_destroy_map_info_list(backtrace->map_info_list);
- backtrace->map_info_list = NULL;
- }
-
- if (backtrace->tid < 0) {
- local_free_data(backtrace);
- } else {
- remote_free_data(backtrace);
- }
-}
-
-char* backtrace_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
- uintptr_t* offset) {
- if (backtrace->tid < 0) {
- return local_get_proc_name(backtrace, pc, offset);
- } else {
- return remote_get_proc_name(backtrace, pc, offset);
- }
-}
diff --git a/libbacktrace/unwind_local.c b/libbacktrace/unwind_local.c
deleted file mode 100644
index d467d8a..0000000
--- a/libbacktrace/unwind_local.c
+++ /dev/null
@@ -1,130 +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 <string.h>
-
-#include <cutils/log.h>
-#include <backtrace/backtrace.h>
-
-#define UNW_LOCAL_ONLY
-#include <libunwind.h>
-#include <libunwind-ptrace.h>
-
-#include "common.h"
-#include "demangle.h"
-
-static bool local_get_frames(backtrace_t* backtrace) {
- unw_context_t* context = (unw_context_t*)backtrace->private_data;
- unw_cursor_t cursor;
-
- int ret = unw_getcontext(context);
- if (ret < 0) {
- ALOGW("local_get_frames: unw_getcontext failed %d\n", ret);
- return false;
- }
-
- ret = unw_init_local(&cursor, context);
- if (ret < 0) {
- ALOGW("local_get_frames: unw_init_local failed %d\n", ret);
- return false;
- }
-
- backtrace_frame_data_t* frame;
- bool returnValue = true;
- backtrace->num_frames = 0;
- uintptr_t map_start;
- unw_word_t value;
- do {
- frame = &backtrace->frames[backtrace->num_frames];
- frame->stack_size = 0;
- frame->map_name = NULL;
- frame->map_offset = 0;
- frame->proc_name = NULL;
- frame->proc_offset = 0;
-
- ret = unw_get_reg(&cursor, UNW_REG_IP, &value);
- if (ret < 0) {
- ALOGW("get_frames: Failed to read IP %d\n", ret);
- returnValue = false;
- break;
- }
- frame->pc = (uintptr_t)value;
- ret = unw_get_reg(&cursor, UNW_REG_SP, &value);
- if (ret < 0) {
- ALOGW("get_frames: Failed to read IP %d\n", ret);
- returnValue = false;
- break;
- }
- frame->sp = (uintptr_t)value;
-
- if (backtrace->num_frames) {
- backtrace_frame_data_t* prev = &backtrace->frames[backtrace->num_frames-1];
- prev->stack_size = frame->sp - prev->sp;
- }
-
- frame->proc_name = backtrace_get_proc_name(backtrace, frame->pc, &frame->proc_offset);
-
- frame->map_name = backtrace_get_map_info(backtrace, frame->pc, &map_start);
- if (frame->map_name) {
- frame->map_offset = frame->pc - map_start;
- }
-
- backtrace->num_frames++;
- ret = unw_step (&cursor);
- } while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES);
-
- return returnValue;
-}
-
-bool local_get_data(backtrace_t* backtrace) {
- unw_context_t *context = (unw_context_t*)malloc(sizeof(unw_context_t));
- backtrace->private_data = context;
-
- if (!local_get_frames(backtrace)) {
- backtrace_free_data(backtrace);
- return false;
- }
-
- return true;
-}
-
-void local_free_data(backtrace_t* backtrace) {
- if (backtrace->private_data) {
- free(backtrace->private_data);
- backtrace->private_data = NULL;
- }
-}
-
-char* local_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
- uintptr_t* offset) {
- unw_context_t* context = (unw_context_t*)backtrace->private_data;
- char buf[512];
-
- *offset = 0;
- unw_word_t value;
- if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
- &value, context) >= 0 && buf[0] != '\0') {
- *offset = (uintptr_t)value;
- char* symbol = demangle_symbol_name(buf);
- if (!symbol) {
- symbol = strdup(buf);
- }
- return symbol;
- }
- return NULL;
-}
diff --git a/libbacktrace/unwind_remote.c b/libbacktrace/unwind_remote.c
deleted file mode 100644
index 1c624d7..0000000
--- a/libbacktrace/unwind_remote.c
+++ /dev/null
@@ -1,158 +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 <sys/ptrace.h>
-#include <string.h>
-
-#include <cutils/log.h>
-#include <backtrace/backtrace.h>
-
-#include <libunwind.h>
-#include <libunwind-ptrace.h>
-
-#include "common.h"
-#include "demangle.h"
-
-typedef struct {
- unw_addr_space_t addr_space;
- struct UPT_info* upt_info;
-} backtrace_private_t;
-
-static bool remote_get_frames(backtrace_t* backtrace) {
- backtrace_private_t* data = (backtrace_private_t*)backtrace->private_data;
- unw_cursor_t cursor;
- int ret = unw_init_remote(&cursor, data->addr_space, data->upt_info);
- if (ret < 0) {
- ALOGW("remote_get_frames: unw_init_remote failed %d\n", ret);
- return false;
- }
-
- backtrace_frame_data_t* frame;
- bool returnValue = true;
- backtrace->num_frames = 0;
- uintptr_t map_start;
- unw_word_t value;
- do {
- frame = &backtrace->frames[backtrace->num_frames];
- frame->stack_size = 0;
- frame->map_name = NULL;
- frame->map_offset = 0;
- frame->proc_name = NULL;
- frame->proc_offset = 0;
-
- ret = unw_get_reg(&cursor, UNW_REG_IP, &value);
- if (ret < 0) {
- ALOGW("remote_get_frames: Failed to read IP %d\n", ret);
- returnValue = false;
- break;
- }
- frame->pc = (uintptr_t)value;
- ret = unw_get_reg(&cursor, UNW_REG_SP, &value);
- if (ret < 0) {
- ALOGW("remote_get_frames: Failed to read SP %d\n", ret);
- returnValue = false;
- break;
- }
- frame->sp = (uintptr_t)value;
-
- if (backtrace->num_frames) {
- backtrace_frame_data_t* prev = &backtrace->frames[backtrace->num_frames-1];
- prev->stack_size = frame->sp - prev->sp;
- }
-
- frame->proc_name = backtrace_get_proc_name(backtrace, frame->pc, &frame->proc_offset);
-
- frame->map_name = backtrace_get_map_info(backtrace, frame->pc, &map_start);
- if (frame->map_name) {
- frame->map_offset = frame->pc - map_start;
- }
-
- backtrace->num_frames++;
- ret = unw_step (&cursor);
- } while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES);
-
- return returnValue;
-}
-
-bool remote_get_data(backtrace_t* backtrace) {
- backtrace_private_t* data = (backtrace_private_t*)malloc(sizeof(backtrace_private_t));
- if (!data) {
- ALOGW("remote_get_data: Failed to allocate memory.\n");
- backtrace_free_data(backtrace);
- return false;
- }
- data->addr_space = NULL;
- data->upt_info = NULL;
-
- backtrace->private_data = data;
- data->addr_space = unw_create_addr_space(&_UPT_accessors, 0);
- if (!data->addr_space) {
- ALOGW("remote_get_data: Failed to create unw address space.\n");
- backtrace_free_data(backtrace);
- return false;
- }
-
- data->upt_info = _UPT_create(backtrace->tid);
- if (!data->upt_info) {
- ALOGW("remote_get_data: Failed to create upt info.\n");
- backtrace_free_data(backtrace);
- return false;
- }
-
- if (!remote_get_frames(backtrace)) {
- backtrace_free_data(backtrace);
- return false;
- }
-
- return true;
-}
-
-void remote_free_data(backtrace_t* backtrace) {
- if (backtrace->private_data) {
- backtrace_private_t* data = (backtrace_private_t*)backtrace->private_data;
- if (data->upt_info) {
- _UPT_destroy(data->upt_info);
- data->upt_info = NULL;
- }
- if (data->addr_space) {
- unw_destroy_addr_space(data->addr_space);
- }
-
- free(backtrace->private_data);
- backtrace->private_data = NULL;
- }
-}
-
-char* remote_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
- uintptr_t* offset) {
- backtrace_private_t* data = (backtrace_private_t*)backtrace->private_data;
- char buf[512];
-
- *offset = 0;
- unw_word_t value;
- if (unw_get_proc_name_by_ip(data->addr_space, pc, buf, sizeof(buf), &value,
- data->upt_info) >= 0 && buf[0] != '\0') {
- *offset = (uintptr_t)value;
- char* symbol = demangle_symbol_name(buf);
- if (!symbol) {
- symbol = strdup(buf);
- }
- return symbol;
- }
- return NULL;
-}