blob: ece1e188450088034dacd5e30c0365a296b9764d [file] [log] [blame]
Christopher Ferris7fb22872013-09-27 12:43:15 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "libbacktrace"
18
19#include <sys/ptrace.h>
20#include <string.h>
21
Christopher Ferris6d229602013-10-01 23:46:23 -070022#include <log/log.h>
Christopher Ferris7fb22872013-09-27 12:43:15 -070023#include <backtrace/backtrace.h>
24
25#include <libunwind.h>
26#include <libunwind-ptrace.h>
27
28#include "common.h"
29#include "demangle.h"
30
31typedef struct {
32 unw_addr_space_t addr_space;
33 struct UPT_info* upt_info;
34} backtrace_private_t;
35
36static bool remote_get_frames(backtrace_t* backtrace) {
37 backtrace_private_t* data = (backtrace_private_t*)backtrace->private_data;
38 unw_cursor_t cursor;
39 int ret = unw_init_remote(&cursor, data->addr_space, data->upt_info);
40 if (ret < 0) {
41 ALOGW("remote_get_frames: unw_init_remote failed %d\n", ret);
42 return false;
43 }
44
45 backtrace_frame_data_t* frame;
46 bool returnValue = true;
47 backtrace->num_frames = 0;
48 uintptr_t map_start;
Christopher Ferrisa16a4e12013-10-01 17:54:11 -070049 unw_word_t value;
Christopher Ferris7fb22872013-09-27 12:43:15 -070050 do {
51 frame = &backtrace->frames[backtrace->num_frames];
52 frame->stack_size = 0;
53 frame->map_name = NULL;
54 frame->map_offset = 0;
55 frame->proc_name = NULL;
56 frame->proc_offset = 0;
57
Christopher Ferrisa16a4e12013-10-01 17:54:11 -070058 ret = unw_get_reg(&cursor, UNW_REG_IP, &value);
Christopher Ferris7fb22872013-09-27 12:43:15 -070059 if (ret < 0) {
60 ALOGW("remote_get_frames: Failed to read IP %d\n", ret);
61 returnValue = false;
62 break;
63 }
Christopher Ferrisa16a4e12013-10-01 17:54:11 -070064 frame->pc = (uintptr_t)value;
65 ret = unw_get_reg(&cursor, UNW_REG_SP, &value);
Christopher Ferris7fb22872013-09-27 12:43:15 -070066 if (ret < 0) {
67 ALOGW("remote_get_frames: Failed to read SP %d\n", ret);
68 returnValue = false;
69 break;
70 }
Christopher Ferrisa16a4e12013-10-01 17:54:11 -070071 frame->sp = (uintptr_t)value;
72
Christopher Ferris7fb22872013-09-27 12:43:15 -070073 if (backtrace->num_frames) {
74 backtrace_frame_data_t* prev = &backtrace->frames[backtrace->num_frames-1];
75 prev->stack_size = frame->sp - prev->sp;
76 }
77
78 frame->proc_name = backtrace_get_proc_name(backtrace, frame->pc, &frame->proc_offset);
79
80 frame->map_name = backtrace_get_map_info(backtrace, frame->pc, &map_start);
81 if (frame->map_name) {
82 frame->map_offset = frame->pc - map_start;
83 }
84
85 backtrace->num_frames++;
86 ret = unw_step (&cursor);
87 } while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES);
88
89 return returnValue;
90}
91
92bool remote_get_data(backtrace_t* backtrace) {
93 backtrace_private_t* data = (backtrace_private_t*)malloc(sizeof(backtrace_private_t));
94 if (!data) {
95 ALOGW("remote_get_data: Failed to allocate memory.\n");
96 backtrace_free_data(backtrace);
97 return false;
98 }
99 data->addr_space = NULL;
100 data->upt_info = NULL;
101
102 backtrace->private_data = data;
103 data->addr_space = unw_create_addr_space(&_UPT_accessors, 0);
104 if (!data->addr_space) {
105 ALOGW("remote_get_data: Failed to create unw address space.\n");
106 backtrace_free_data(backtrace);
107 return false;
108 }
109
110 data->upt_info = _UPT_create(backtrace->tid);
111 if (!data->upt_info) {
112 ALOGW("remote_get_data: Failed to create upt info.\n");
113 backtrace_free_data(backtrace);
114 return false;
115 }
116
117 if (!remote_get_frames(backtrace)) {
118 backtrace_free_data(backtrace);
119 return false;
120 }
121
122 return true;
123}
124
125void remote_free_data(backtrace_t* backtrace) {
126 if (backtrace->private_data) {
127 backtrace_private_t* data = (backtrace_private_t*)backtrace->private_data;
128 if (data->upt_info) {
129 _UPT_destroy(data->upt_info);
130 data->upt_info = NULL;
131 }
132 if (data->addr_space) {
133 unw_destroy_addr_space(data->addr_space);
134 }
135
136 free(backtrace->private_data);
137 backtrace->private_data = NULL;
138 }
139}
140
141char* remote_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
142 uintptr_t* offset) {
143 backtrace_private_t* data = (backtrace_private_t*)backtrace->private_data;
144 char buf[512];
145
Christopher Ferrisa16a4e12013-10-01 17:54:11 -0700146 *offset = 0;
147 unw_word_t value;
148 if (unw_get_proc_name_by_ip(data->addr_space, pc, buf, sizeof(buf), &value,
Christopher Ferris7fb22872013-09-27 12:43:15 -0700149 data->upt_info) >= 0 && buf[0] != '\0') {
Christopher Ferrisa16a4e12013-10-01 17:54:11 -0700150 *offset = (uintptr_t)value;
Christopher Ferris7fb22872013-09-27 12:43:15 -0700151 char* symbol = demangle_symbol_name(buf);
152 if (!symbol) {
153 symbol = strdup(buf);
154 }
155 return symbol;
156 }
157 return NULL;
158}