utility.c revision 19b39f371be5250e7b9e88016be1e5e665367b3f
1/* system/debuggerd/utility.c
2**
3** Copyright 2008, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include <signal.h>
19#include <string.h>
20#include <cutils/logd.h>
21#include <sys/ptrace.h>
22#include <errno.h>
23#include <corkscrew/demangle.h>
24
25#include "utility.h"
26
27#define STACK_DEPTH 32
28#define STACK_WORDS 16
29
30void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...) {
31    char buf[512];
32
33    va_list ap;
34    va_start(ap, fmt);
35
36    if (tfd >= 0) {
37        int len;
38        vsnprintf(buf, sizeof(buf), fmt, ap);
39        len = strlen(buf);
40        if(tfd >= 0) write(tfd, buf, len);
41    }
42
43    if (!in_tombstone_only)
44        __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap);
45    va_end(ap);
46}
47
48bool signal_has_address(int sig) {
49    switch (sig) {
50        case SIGILL:
51        case SIGFPE:
52        case SIGSEGV:
53        case SIGBUS:
54            return true;
55        default:
56            return false;
57    }
58}
59
60static void dump_backtrace(const ptrace_context_t* context __attribute((unused)),
61        int tfd, pid_t tid __attribute((unused)), bool at_fault,
62        const backtrace_frame_t* backtrace, size_t frames) {
63    _LOG(tfd, !at_fault, "\nbacktrace:\n");
64
65    backtrace_symbol_t backtrace_symbols[STACK_DEPTH];
66    get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols);
67    for (size_t i = 0; i < frames; i++) {
68        char line[MAX_BACKTRACE_LINE_LENGTH];
69        format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i],
70                line, MAX_BACKTRACE_LINE_LENGTH);
71        _LOG(tfd, !at_fault, "    %s\n", line);
72    }
73    free_backtrace_symbols(backtrace_symbols, frames);
74}
75
76static void dump_stack_segment(const ptrace_context_t* context, int tfd, pid_t tid,
77        bool only_in_tombstone, uintptr_t* sp, size_t words, int label) {
78    for (size_t i = 0; i < words; i++) {
79        uint32_t stack_content;
80        if (!try_get_word_ptrace(tid, *sp, &stack_content)) {
81            break;
82        }
83
84        const map_info_t* mi;
85        const symbol_t* symbol;
86        find_symbol_ptrace(context, stack_content, &mi, &symbol);
87
88        if (symbol) {
89            char* demangled_name = demangle_symbol_name(symbol->name);
90            const char* symbol_name = demangled_name ? demangled_name : symbol->name;
91            uint32_t offset = stack_content - (mi->start + symbol->start);
92            if (!i && label >= 0) {
93                if (offset) {
94                    _LOG(tfd, only_in_tombstone, "    #%02d  %08x  %08x  %s (%s+%u)\n",
95                            label, *sp, stack_content, mi ? mi->name : "", symbol_name, offset);
96                } else {
97                    _LOG(tfd, only_in_tombstone, "    #%02d  %08x  %08x  %s (%s)\n",
98                            label, *sp, stack_content, mi ? mi->name : "", symbol_name);
99                }
100            } else {
101                if (offset) {
102                    _LOG(tfd, only_in_tombstone, "         %08x  %08x  %s (%s+%u)\n",
103                            *sp, stack_content, mi ? mi->name : "", symbol_name, offset);
104                } else {
105                    _LOG(tfd, only_in_tombstone, "         %08x  %08x  %s (%s)\n",
106                            *sp, stack_content, mi ? mi->name : "", symbol_name);
107                }
108            }
109            free(demangled_name);
110        } else {
111            if (!i && label >= 0) {
112                _LOG(tfd, only_in_tombstone, "    #%02d  %08x  %08x  %s\n",
113                        label, *sp, stack_content, mi ? mi->name : "");
114            } else {
115                _LOG(tfd, only_in_tombstone, "         %08x  %08x  %s\n",
116                        *sp, stack_content, mi ? mi->name : "");
117            }
118        }
119
120        *sp += sizeof(uint32_t);
121    }
122}
123
124static void dump_stack(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault,
125        const backtrace_frame_t* backtrace, size_t frames) {
126    bool have_first = false;
127    size_t first, last;
128    for (size_t i = 0; i < frames; i++) {
129        if (backtrace[i].stack_top) {
130            if (!have_first) {
131                have_first = true;
132                first = i;
133            }
134            last = i;
135        }
136    }
137    if (!have_first) {
138        return;
139    }
140
141    _LOG(tfd, !at_fault, "\nstack:\n");
142
143    // Dump a few words before the first frame.
144    bool only_in_tombstone = !at_fault;
145    uintptr_t sp = backtrace[first].stack_top - STACK_WORDS * sizeof(uint32_t);
146    dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, STACK_WORDS, -1);
147
148    // Dump a few words from all successive frames.
149    // Only log the first 3 frames, put the rest in the tombstone.
150    for (size_t i = first; i <= last; i++) {
151        const backtrace_frame_t* frame = &backtrace[i];
152        if (sp != frame->stack_top) {
153            _LOG(tfd, only_in_tombstone, "         ........  ........\n");
154            sp = frame->stack_top;
155        }
156        if (i - first == 3) {
157            only_in_tombstone = true;
158        }
159        if (i == last) {
160            dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, STACK_WORDS, i);
161            if (sp < frame->stack_top + frame->stack_size) {
162                _LOG(tfd, only_in_tombstone, "         ........  ........\n");
163            }
164        } else {
165            size_t words = frame->stack_size / sizeof(uint32_t);
166            if (words == 0) {
167                words = 1;
168            } else if (words > STACK_WORDS) {
169                words = STACK_WORDS;
170            }
171            dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, words, i);
172        }
173    }
174}
175
176void dump_backtrace_and_stack(const ptrace_context_t* context, int tfd, pid_t tid,
177        bool at_fault) {
178    backtrace_frame_t backtrace[STACK_DEPTH];
179    ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH);
180    if (frames > 0) {
181        dump_backtrace(context, tfd, tid, at_fault, backtrace, frames);
182        dump_stack(context, tfd, tid, at_fault, backtrace, frames);
183    }
184}
185
186void dump_memory(int tfd, pid_t tid, uintptr_t addr, bool at_fault) {
187    char code_buffer[64];       /* actual 8+1+((8+1)*4) + 1 == 45 */
188    char ascii_buffer[32];      /* actual 16 + 1 == 17 */
189    uintptr_t p, end;
190
191    p = addr & ~3;
192    p -= 32;
193    if (p > addr) {
194        /* catch underflow */
195        p = 0;
196    }
197    end = p + 80;
198    /* catch overflow; 'end - p' has to be multiples of 16 */
199    while (end < p)
200        end -= 16;
201
202    /* Dump the code around PC as:
203     *  addr     contents                             ascii
204     *  00008d34 ef000000 e8bd0090 e1b00000 512fff1e  ............../Q
205     *  00008d44 ea00b1f9 e92d0090 e3a070fc ef000000  ......-..p......
206     */
207    while (p < end) {
208        char* asc_out = ascii_buffer;
209
210        sprintf(code_buffer, "%08x ", p);
211
212        int i;
213        for (i = 0; i < 4; i++) {
214            /*
215             * If we see (data == -1 && errno != 0), we know that the ptrace
216             * call failed, probably because we're dumping memory in an
217             * unmapped or inaccessible page.  I don't know if there's
218             * value in making that explicit in the output -- it likely
219             * just complicates parsing and clarifies nothing for the
220             * enlightened reader.
221             */
222            long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL);
223            sprintf(code_buffer + strlen(code_buffer), "%08lx ", data);
224
225            int j;
226            for (j = 0; j < 4; j++) {
227                /*
228                 * Our isprint() allows high-ASCII characters that display
229                 * differently (often badly) in different viewers, so we
230                 * just use a simpler test.
231                 */
232                char val = (data >> (j*8)) & 0xff;
233                if (val >= 0x20 && val < 0x7f) {
234                    *asc_out++ = val;
235                } else {
236                    *asc_out++ = '.';
237                }
238            }
239            p += 4;
240        }
241        *asc_out = '\0';
242        _LOG(tfd, !at_fault, "    %s %s\n", code_buffer, ascii_buffer);
243    }
244}
245
246void dump_nearby_maps(const ptrace_context_t* context, int tfd, pid_t tid) {
247    siginfo_t si;
248    memset(&si, 0, sizeof(si));
249    if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) {
250        _LOG(tfd, false, "cannot get siginfo for %d: %s\n",
251                tid, strerror(errno));
252        return;
253    }
254    if (!signal_has_address(si.si_signo)) {
255        return;
256    }
257
258    uintptr_t addr = (uintptr_t) si.si_addr;
259    addr &= ~0xfff;     /* round to 4K page boundary */
260    if (addr == 0) {    /* null-pointer deref */
261        return;
262    }
263
264    _LOG(tfd, false, "\nmemory map around fault addr %08x:\n", (int)si.si_addr);
265
266    /*
267     * Search for a match, or for a hole where the match would be.  The list
268     * is backward from the file content, so it starts at high addresses.
269     */
270    bool found = false;
271    map_info_t* map = context->map_info_list;
272    map_info_t *next = NULL;
273    map_info_t *prev = NULL;
274    while (map != NULL) {
275        if (addr >= map->start && addr < map->end) {
276            found = true;
277            next = map->next;
278            break;
279        } else if (addr >= map->end) {
280            /* map would be between "prev" and this entry */
281            next = map;
282            map = NULL;
283            break;
284        }
285
286        prev = map;
287        map = map->next;
288    }
289
290    /*
291     * Show "next" then "match" then "prev" so that the addresses appear in
292     * ascending order (like /proc/pid/maps).
293     */
294    if (next != NULL) {
295        _LOG(tfd, false, "    %08x-%08x %s\n", next->start, next->end, next->name);
296    } else {
297        _LOG(tfd, false, "    (no map below)\n");
298    }
299    if (map != NULL) {
300        _LOG(tfd, false, "    %08x-%08x %s\n", map->start, map->end, map->name);
301    } else {
302        _LOG(tfd, false, "    (no map for address)\n");
303    }
304    if (prev != NULL) {
305        _LOG(tfd, false, "    %08x-%08x %s\n", prev->start, prev->end, prev->name);
306    } else {
307        _LOG(tfd, false, "    (no map above)\n");
308    }
309}
310