]> git.saurik.com Git - apple/libc.git/blob - gen/backtrace.c
c075de253b56d4c822b76c3a8781fcb33df9bd55
[apple/libc.git] / gen / backtrace.c
1 /*
2 * Copyright (c) 2007, 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <mach/vm_types.h>
25 #include <sys/uio.h>
26
27 #include <dlfcn.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32
33 #include "stack_logging.h"
34 #include "execinfo.h"
35
36 int backtrace(void** buffer, int size) {
37 extern void _thread_stack_pcs(vm_address_t *buffer, unsigned max, unsigned *nb, unsigned skip);
38 unsigned int num_frames;
39 _thread_stack_pcs((vm_address_t*)buffer, size, &num_frames, 1);
40 while (num_frames >= 1 && buffer[num_frames-1] == NULL) num_frames -= 1;
41 return num_frames;
42 }
43
44 #if __LP64__
45 #define _BACKTRACE_FORMAT "%-4d%-35s 0x%016lx %s + %lu"
46 #define _BACKTRACE_FORMAT_SIZE 83 /* %lu can take up to 20, does not include %s, includes NUL */
47 #define _BACKTRACE_ADDRESS_LEN 18 /* 0x + 16 (no NUL) */
48 #else
49 #define _BACKTRACE_FORMAT "%-4d%-35s 0x%08lx %s + %lu"
50 #define _BACKTRACE_FORMAT_SIZE 65 /* %lu can take up to 10, does not include %s, includes NUL */
51 #define _BACKTRACE_ADDRESS_LEN 10 /* 0x + 8 (no NUL) */
52 #endif
53
54 static int _backtrace_snprintf(char* buf, size_t size, int frame, const void* addr, const Dl_info* info) {
55 char symbuf[_BACKTRACE_ADDRESS_LEN + 1];
56 const char* image = "???";
57 const char* symbol = "0x0";
58 uintptr_t symbol_offset = 0;
59
60 if (info->dli_fname) {
61 const char *tmp = strrchr(info->dli_fname, '/');
62 if(tmp == NULL)
63 image = info->dli_fname;
64 else
65 image = tmp + 1;
66 }
67
68 if (info->dli_sname) {
69 symbol = info->dli_sname;
70 symbol_offset = (uintptr_t)addr - (uintptr_t)info->dli_saddr;
71 } else if(info->dli_fname) {
72 symbol = image;
73 symbol_offset = (uintptr_t)addr - (uintptr_t)info->dli_fbase;
74 } else if(0 < snprintf(symbuf, sizeof(symbuf), "0x%lx", (uintptr_t)info->dli_saddr)) {
75 symbol = symbuf;
76 symbol_offset = (uintptr_t)addr - (uintptr_t)info->dli_saddr;
77 } else {
78 symbol_offset = (uintptr_t)addr;
79 }
80
81 return snprintf(buf, size,
82 _BACKTRACE_FORMAT,
83 frame,
84 image,
85 (uintptr_t)addr,
86 symbol,
87 symbol_offset) + 1;
88 }
89
90 char** backtrace_symbols(void* const* buffer, int size) {
91 int i;
92 size_t total_bytes;
93 char** result;
94 char** ptrs;
95 intptr_t strs, end;
96 Dl_info* info = calloc(size, sizeof (Dl_info));
97
98 if (info == NULL) return NULL;
99
100 // Compute the total size for the block that is returned.
101 // The block will contain size number of pointers to the
102 // symbol descriptions.
103
104 total_bytes = sizeof(char*) * size;
105
106 // Plus each symbol description
107 for (i = 0 ; i < size; ++i) {
108 dladdr(buffer[i], &info[i]);
109 total_bytes += _BACKTRACE_FORMAT_SIZE;
110 if (info[i].dli_sname) {
111 total_bytes += strlen(info[i].dli_sname);
112 } else if(info[i].dli_fname) {
113 const char *tmp = strrchr(info->dli_fname, '/');
114 if(tmp == NULL)
115 total_bytes += strlen(info->dli_fname);
116 else
117 total_bytes += strlen(tmp + 1);
118 } else {
119 total_bytes += _BACKTRACE_ADDRESS_LEN;
120 }
121 }
122
123 result = (char**)malloc(total_bytes);
124 if (result == NULL) {
125 free(info);
126 return NULL;
127 }
128 end = (intptr_t)result + total_bytes;
129
130 // Fill in the array of pointers and append the strings for
131 // each symbol description.
132
133 ptrs = result;
134 strs = ((intptr_t)result) + sizeof(char*) * size;
135
136 for (i = 0; i < size; ++i) {
137 int chk = _backtrace_snprintf((char*)strs, end - (intptr_t)strs, i, buffer[i], &info[i]);
138
139 if(chk < 0) {
140 free(info);
141 return NULL;
142 }
143
144 ptrs[i] = (char*)strs;
145 strs += chk;
146 }
147
148 free(info);
149
150 return result;
151 }
152
153 void backtrace_symbols_fd(void* const* buffer, int size, int fd) {
154 int i;
155 char buf[BUFSIZ];
156 Dl_info info;
157 struct iovec iov[2];
158
159 iov[0].iov_base = buf;
160
161 iov[1].iov_base = "\n";
162 iov[1].iov_len = 1;
163
164 for (i = 0; i < size; ++i) {
165 memset(&info, 0, sizeof(info));
166 dladdr(buffer[i], &info);
167
168 iov[0].iov_len = _backtrace_snprintf(buf, sizeof(buf), i, buffer[i], &info);
169
170 writev(fd, iov, 2);
171 }
172 }