Merge "Create a single backtrace library."
This commit is contained in:
commit
f41cf2086d
|
@ -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 */
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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';
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue