diff options
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; -} |