Create a single backtrace library.

This library will be used to abstract away getting backtrace
data from how it is implemented. This is the first step to
replacing libcorkscrew with libunwind.

Bug: 8410085
Change-Id: Ie8f159e96a055d378e1ddc72d40239fba4cf52b7
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;
+}