Merge "Create a single backtrace library."
diff --git a/include/backtrace/backtrace.h b/include/backtrace/backtrace.h
new file mode 100644
index 0000000..38a9645
--- /dev/null
+++ b/include/backtrace/backtrace.h
@@ -0,0 +1,104 @@
+/*
+ * 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_H
+#define _BACKTRACE_H
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_BACKTRACE_FRAMES 64
+
+typedef struct backtrace_map_info {
+ struct backtrace_map_info* next;
+ uintptr_t start;
+ uintptr_t end;
+ bool is_readable;
+ bool is_writable;
+ bool is_executable;
+ char name[];
+} backtrace_map_info_t;
+
+typedef struct {
+ uintptr_t pc; /* The absolute pc. */
+ uintptr_t sp; /* The top of the stack. */
+ 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 no not found. */
+ uintptr_t proc_offset; /* pc relative to the start of the procedure, only valid if proc_name is not NULL. */
+} backtrace_frame_data_t;
+
+typedef struct {
+ backtrace_frame_data_t frames[MAX_BACKTRACE_FRAMES];
+ size_t num_frames;
+
+ 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.
+ */
+bool backtrace_get_data(backtrace_t* backtrace, pid_t tid);
+
+/* Free any memory associated with the backtrace structure. */
+void backtrace_free_data(backtrace_t* backtrace);
+
+/* Read data at a specific address for a process. */
+bool backtrace_read_word(
+ const backtrace_t* backtrace, uintptr_t ptr, uint32_t* value);
+
+/* Get information about the map 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);
+
+/* 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
+ * malloc and must be freed by the caller.
+ */
+char* backtrace_get_proc_name(
+ const backtrace_t* backtrace, uintptr_t pc, uintptr_t* proc_offset);
+
+/* Loads memory map from /proc/<tid>/maps. If tid < 0, then load the memory
+ * map for the current process.
+ */
+backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid);
+
+/* Frees memory associated with the map list. */
+void backtrace_destroy_map_info_list(backtrace_map_info_t* map_info_list);
+
+/* Finds the memory map that contains the specified pc. */
+const backtrace_map_info_t* backtrace_find_map_info(
+ const backtrace_map_info_t* map_info_list, uintptr_t pc);
+
+/* 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BACKTRACE_H */
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
new file mode 100644
index 0000000..f7b084d
--- /dev/null
+++ b/libbacktrace/Android.mk
@@ -0,0 +1,176 @@
+LOCAL_PATH:= $(call my-dir)
+
+#----------------------------------------------------------------------------
+# The libbacktrace library using libunwind
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ unwind.c \
+ unwind_remote.c \
+ unwind_local.c \
+ common.c \
+ demangle.c \
+ map_info.c \
+
+LOCAL_CFLAGS := \
+ -Wall \
+ -Wno-unused-parameter \
+ -Werror \
+ -std=gnu99 \
+
+LOCAL_MODULE := libbacktrace
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libunwind \
+ libunwind-ptrace \
+ libgccdemangle \
+
+LOCAL_C_INCLUDES := \
+ external/libunwind/include \
+
+# The libunwind code is not in the tree yet, so don't build this library yet.
+#include $(BUILD_SHARED_LIBRARY)
+
+#----------------------------------------------------------------------------
+# The libbacktrace library using libcorkscrew
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ corkscrew.c \
+ common.c \
+ demangle.c \
+ map_info.c \
+
+LOCAL_CFLAGS := \
+ -Wall \
+ -Wno-unused-parameter \
+ -Werror \
+ -std=gnu99 \
+
+LOCAL_MODULE := libbacktrace
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := \
+ libcorkscrew \
+ libdl \
+ libgccdemangle \
+ liblog \
+
+include $(BUILD_SHARED_LIBRARY)
+
+#----------------------------------------------------------------------------
+# The host libbacktrace library using libcorkscrew
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES += \
+ corkscrew.c \
+ common.c \
+ demangle.c \
+ map_info.c \
+
+LOCAL_CFLAGS += \
+ -Wall \
+ -Wno-unused-parameter \
+ -Werror \
+ -std=gnu99 \
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libcorkscrew \
+ libgccdemangle \
+ liblog \
+
+LOCAL_LDLIBS += \
+ -ldl \
+ -lrt \
+
+LOCAL_MODULE := libbacktrace
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+#----------------------------------------------------------------------------
+# libbacktrace test library, all optimizations turned off
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbacktrace_test
+LOCAL_MODULE_FLAGS := debug
+
+LOCAL_SRC_FILES := \
+ backtrace_testlib.c
+
+LOCAL_CFLAGS += \
+ -std=gnu99 \
+ -O0 \
+
+include $(BUILD_SHARED_LIBRARY)
+
+#----------------------------------------------------------------------------
+# libbacktrace test executable
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := backtrace_test
+LOCAL_MODULE_FLAGS := debug
+
+LOCAL_SRC_FILES := \
+ backtrace_test.c \
+
+LOCAL_CFLAGS += \
+ -std=gnu99 \
+
+LOCAL_SHARED_LIBRARIES := \
+ libbacktrace_test \
+ libbacktrace \
+
+include $(BUILD_EXECUTABLE)
+
+#----------------------------------------------------------------------------
+# Only linux-x86 host versions of libbacktrace supported.
+#----------------------------------------------------------------------------
+ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
+
+#----------------------------------------------------------------------------
+# libbacktrace host test library, all optimizations turned off
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbacktrace_test
+LOCAL_MODULE_FLAGS := debug
+
+LOCAL_SRC_FILES := \
+ backtrace_testlib.c
+
+LOCAL_CFLAGS += \
+ -std=gnu99 \
+ -O0 \
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+#----------------------------------------------------------------------------
+# libbacktrace host test executable
+#----------------------------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := backtrace_test
+LOCAL_MODULE_FLAGS := debug
+
+LOCAL_SRC_FILES := \
+ backtrace_test.c \
+
+LOCAL_CFLAGS += \
+ -std=gnu99 \
+
+LOCAL_SHARED_LIBRARIES := \
+ libbacktrace_test \
+ libbacktrace \
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # HOST_OS-HOST_ARCH == linux-x86
diff --git a/libbacktrace/backtrace_test.c b/libbacktrace/backtrace_test.c
new file mode 100644
index 0000000..34d3519
--- /dev/null
+++ b/libbacktrace/backtrace_test.c
@@ -0,0 +1,217 @@
+/*
+ * 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 <unistd.h>
+#include <inttypes.h>
+
+#include <backtrace/backtrace.h>
+
+#define FINISH(pid) dump_frames(&backtrace); if (pid < 0) exit(1); else return false;
+
+// 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");
+ }
+}
+
+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);
+ }
+ 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_testlib.c b/libbacktrace/backtrace_testlib.c
new file mode 100644
index 0000000..9400549
--- /dev/null
+++ b/libbacktrace/backtrace_testlib.c
@@ -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.
+ */
+
+#include <stdbool.h>
+#include <unistd.h>
+
+int test_level_four(int one, int two, int three, int four,
+ bool (*callback_func)(pid_t)) {
+ if (callback_func != NULL) {
+ callback_func(-1);
+ } else {
+ while (1) {
+ }
+ }
+ return one + two + three + 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;
+}
+
+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;
+}
+
+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;
+}
+
+int test_recursive_call(int level, bool (*callback_func)(pid_t)) {
+ if (level > 0) {
+ return test_recursive_call(level - 1, callback_func) + level;
+ } else if (callback_func != NULL) {
+ callback_func(-1);
+ } else {
+ while (1) {
+ }
+ }
+ return 0;
+}
diff --git a/libbacktrace/common.c b/libbacktrace/common.c
new file mode 100644
index 0000000..20786f4
--- /dev/null
+++ b/libbacktrace/common.c
@@ -0,0 +1,113 @@
+/*
+ * 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/common.h b/libbacktrace/common.h
new file mode 100644
index 0000000..9eef964
--- /dev/null
+++ b/libbacktrace/common.h
@@ -0,0 +1,25 @@
+/*
+ * 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 _COMMON_H
+#define _COMMON_H
+
+#include <backtrace/backtrace.h>
+
+/* Common routine to free any data allocated to store frame information. */
+void free_frame_data(backtrace_t* backtrace);
+
+#endif /* _COMMON_H */
diff --git a/libbacktrace/corkscrew.c b/libbacktrace/corkscrew.c
new file mode 100644
index 0000000..899409a
--- /dev/null
+++ b/libbacktrace/corkscrew.c
@@ -0,0 +1,130 @@
+/*
+ * 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.c b/libbacktrace/demangle.c
new file mode 100644
index 0000000..de9a460
--- /dev/null
+++ b/libbacktrace/demangle.c
@@ -0,0 +1,33 @@
+/*
+ * 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 <sys/types.h>
+
+#include "demangle.h"
+
+extern char* __cxa_demangle (const char* mangled, char* buf, size_t* len,
+ int* status);
+
+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);
+}
diff --git a/libbacktrace/demangle.h b/libbacktrace/demangle.h
new file mode 100644
index 0000000..a5318ac
--- /dev/null
+++ b/libbacktrace/demangle.h
@@ -0,0 +1,25 @@
+/*
+ * 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/map_info.c b/libbacktrace/map_info.c
new file mode 100644
index 0000000..9cc6e01
--- /dev/null
+++ b/libbacktrace/map_info.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <cutils/log.h>
+#include <sys/time.h>
+
+#include <backtrace/backtrace.h>
+
+#if defined(__APPLE__)
+
+// Mac OS vmmap(1) output:
+// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+static backtrace_map_info_t* parse_vmmap_line(const char* line) {
+ unsigned long int start;
+ unsigned long int end;
+ char permissions[4];
+ int name_pos;
+ if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n",
+ &start, &end, permissions, &name_pos) != 3) {
+ return NULL;
+ }
+
+ const char* name = line + name_pos;
+ size_t name_len = strlen(name);
+
+ backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len);
+ if (mi != NULL) {
+ mi->start = start;
+ mi->end = end;
+ mi->is_readable = permissions[0] == 'r';
+ mi->is_writable = permissions[1] == 'w';
+ mi->is_executable = permissions[2] == 'x';
+ memcpy(mi->name, name, name_len);
+ mi->name[name_len - 1] = '\0';
+ ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
+ "is_readable=%d, is_writable=%d is_executable=%d, name=%s",
+ mi->start, mi->end,
+ mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
+ }
+ return mi;
+}
+
+backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid) {
+ char cmd[1024];
+ if (pid < 0) {
+ pid = getpid();
+ }
+ snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid);
+ FILE* fp = popen(cmd, "r");
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ char line[1024];
+ backtrace_map_info_t* milist = NULL;
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ backtrace_map_info_t* mi = parse_vmmap_line(line);
+ if (mi != NULL) {
+ mi->next = milist;
+ milist = mi;
+ }
+ }
+ pclose(fp);
+ return milist;
+}
+
+#else
+
+// Linux /proc/<pid>/maps lines:
+// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+static backtrace_map_info_t* parse_maps_line(const char* line)
+{
+ unsigned long int start;
+ unsigned long int end;
+ char permissions[5];
+ int name_pos;
+ if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end,
+ permissions, &name_pos) != 3) {
+ return NULL;
+ }
+
+ while (isspace(line[name_pos])) {
+ name_pos += 1;
+ }
+ const char* name = line + name_pos;
+ size_t name_len = strlen(name);
+ if (name_len && name[name_len - 1] == '\n') {
+ name_len -= 1;
+ }
+
+ backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len + 1);
+ if (mi) {
+ mi->start = start;
+ mi->end = end;
+ mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r';
+ mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w';
+ mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x';
+ memcpy(mi->name, name, name_len);
+ mi->name[name_len] = '\0';
+ ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
+ "is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
+ mi->start, mi->end,
+ mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
+ }
+ return mi;
+}
+
+backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid) {
+ char path[PATH_MAX];
+ char line[1024];
+ FILE* fp;
+ backtrace_map_info_t* milist = NULL;
+
+ if (tid < 0) {
+ tid = getpid();
+ }
+ snprintf(path, PATH_MAX, "/proc/%d/maps", tid);
+ fp = fopen(path, "r");
+ if (fp) {
+ while(fgets(line, sizeof(line), fp)) {
+ backtrace_map_info_t* mi = parse_maps_line(line);
+ if (mi) {
+ mi->next = milist;
+ milist = mi;
+ }
+ }
+ fclose(fp);
+ }
+ return milist;
+}
+
+#endif
+
+void backtrace_destroy_map_info_list(backtrace_map_info_t* milist) {
+ while (milist) {
+ backtrace_map_info_t* next = milist->next;
+ free(milist);
+ milist = next;
+ }
+}
+
+const backtrace_map_info_t* backtrace_find_map_info(
+ const backtrace_map_info_t* milist, uintptr_t addr) {
+ const backtrace_map_info_t* mi = milist;
+ while (mi && !(addr >= mi->start && addr < mi->end)) {
+ mi = mi->next;
+ }
+ return mi;
+}
diff --git a/libbacktrace/stubs.c b/libbacktrace/stubs.c
new file mode 100644
index 0000000..1741601
--- /dev/null
+++ b/libbacktrace/stubs.c
@@ -0,0 +1,53 @@
+/*
+ * 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/unwind.c b/libbacktrace/unwind.c
new file mode 100644
index 0000000..f75e518
--- /dev/null
+++ b/libbacktrace/unwind.c
@@ -0,0 +1,57 @@
+/*
+ * 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.h b/libbacktrace/unwind.h
new file mode 100644
index 0000000..9ba96a4
--- /dev/null
+++ b/libbacktrace/unwind.h
@@ -0,0 +1,34 @@
+/*
+ * 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 _UNWIND_H
+#define _UNWIND_H
+
+bool local_get_data(backtrace_t* backtrace);
+
+void local_free_data(backtrace_t* backtrace);
+
+char* local_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
+ uintptr_t* offset);
+
+bool remote_get_data(backtrace_t* backtrace);
+
+void remote_free_data(backtrace_t* backtrace);
+
+char* remote_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
+ uintptr_t* offset);
+
+#endif /* _UNWIND_H */
diff --git a/libbacktrace/unwind_local.c b/libbacktrace/unwind_local.c
new file mode 100644
index 0000000..e0b72d8
--- /dev/null
+++ b/libbacktrace/unwind_local.c
@@ -0,0 +1,123 @@
+/*
+ * 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;
+ 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, &frame->pc);
+ if (ret < 0) {
+ ALOGW("get_frames: Failed to read IP %d\n", ret);
+ returnValue = false;
+ break;
+ }
+ ret = unw_get_reg(&cursor, UNW_REG_SP, &frame->sp);
+ if (ret < 0) {
+ ALOGW("get_frames: Failed to read IP %d\n", ret);
+ returnValue = false;
+ break;
+ }
+ 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];
+
+ if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
+ offset, context) >= 0 && buf[0] != '\0') {
+ 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
new file mode 100644
index 0000000..ebbde2e
--- /dev/null
+++ b/libbacktrace/unwind_remote.c
@@ -0,0 +1,151 @@
+/*
+ * 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;
+ 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, &frame->pc);
+ if (ret < 0) {
+ ALOGW("remote_get_frames: Failed to read IP %d\n", ret);
+ returnValue = false;
+ break;
+ }
+ ret = unw_get_reg(&cursor, UNW_REG_SP, &frame->sp);
+ if (ret < 0) {
+ ALOGW("remote_get_frames: Failed to read SP %d\n", ret);
+ returnValue = false;
+ break;
+ }
+ 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];
+
+ if (unw_get_proc_name_by_ip(data->addr_space, pc, buf, sizeof(buf), offset,
+ data->upt_info) >= 0 && buf[0] != '\0') {
+ char* symbol = demangle_symbol_name(buf);
+ if (!symbol) {
+ symbol = strdup(buf);
+ }
+ return symbol;
+ }
+ return NULL;
+}