| /* |
| * 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; |
| } |