Merge "Create a single backtrace library."

This commit is contained in:
Christopher Ferris 2013-09-30 18:02:54 +00:00 committed by Gerrit Code Review
commit f41cf2086d
15 changed files with 1470 additions and 0 deletions

View File

@ -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 */

176
libbacktrace/Android.mk Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

113
libbacktrace/common.c Normal file
View File

@ -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;
}

25
libbacktrace/common.h Normal file
View File

@ -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 */

130
libbacktrace/corkscrew.c Normal file
View File

@ -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;
}

33
libbacktrace/demangle.c Normal file
View File

@ -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);
}

25
libbacktrace/demangle.h Normal file
View File

@ -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 */

173
libbacktrace/map_info.c Normal file
View File

@ -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;
}

53
libbacktrace/stubs.c Normal file
View File

@ -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';
}

57
libbacktrace/unwind.c Normal file
View File

@ -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);
}
}

34
libbacktrace/unwind.h Normal file
View File

@ -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 */

123
libbacktrace/unwind_local.c Normal file
View File

@ -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;
}

View File

@ -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;
}