blob: 4ebc048ac0bb723502198812d999a8db3f505eef [file] [log] [blame]
Luca Clementi327064b2013-07-23 00:11:35 -07001/*
2 * Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "defs.h"
28#include <limits.h>
29#include <libunwind-ptrace.h>
30
31/*
32 * Кeep a sorted array of cache entries,
33 * so that we can binary search through it.
34 */
35struct mmap_cache_t {
36 /**
37 * example entry:
38 * 7fabbb09b000-7fabbb09f000 r--p 00179000 fc:00 1180246 /lib/libc-2.11.1.so
39 *
40 * start_addr is 0x7fabbb09b000
41 * end_addr is 0x7fabbb09f000
42 * mmap_offset is 0x179000
43 * binary_filename is "/lib/libc-2.11.1.so"
44 */
45 unsigned long start_addr;
46 unsigned long end_addr;
47 unsigned long mmap_offset;
48 char* binary_filename;
49};
50
51static unw_addr_space_t libunwind_as;
52
53void
Masatake YAMATO61413922014-04-16 15:33:02 +090054unwind_init(void)
Luca Clementi327064b2013-07-23 00:11:35 -070055{
56 libunwind_as = unw_create_addr_space(&_UPT_accessors, 0);
57 if (!libunwind_as)
58 error_msg_and_die("failed to create address space for stack tracing");
59}
60
61void
Masatake YAMATO61413922014-04-16 15:33:02 +090062unwind_tcb_init(struct tcb *tcp)
Luca Clementi327064b2013-07-23 00:11:35 -070063{
64 tcp->libunwind_ui = _UPT_create(tcp->pid);
65 if (!tcp->libunwind_ui)
66 die_out_of_memory();
67}
68
69void
Masatake YAMATO61413922014-04-16 15:33:02 +090070unwind_tcb_fin(struct tcb *tcp)
Luca Clementi327064b2013-07-23 00:11:35 -070071{
Masatake YAMATO61413922014-04-16 15:33:02 +090072 unwind_cache_invalidate(tcp);
Luca Clementi327064b2013-07-23 00:11:35 -070073 _UPT_destroy(tcp->libunwind_ui);
74 tcp->libunwind_ui = NULL;
75}
76
77/*
78 * caching of /proc/ID/maps for each process to speed up stack tracing
79 *
80 * The cache must be refreshed after some syscall: mmap, mprotect, munmap, execve
81 */
Masatake YAMATOb65042f2014-04-16 15:33:00 +090082static void
Luca Clementi327064b2013-07-23 00:11:35 -070083alloc_mmap_cache(struct tcb* tcp)
84{
85 unsigned long start_addr, end_addr, mmap_offset;
86 char filename[sizeof ("/proc/0123456789/maps")];
87 char buffer[PATH_MAX + 80];
88 char binary_path[PATH_MAX];
89 struct mmap_cache_t *cur_entry, *prev_entry;
90 /* start with a small dynamically-allocated array and then expand it */
91 size_t cur_array_size = 10;
92 struct mmap_cache_t *cache_head;
93 FILE *fp;
94
95 sprintf(filename, "/proc/%d/maps", tcp->pid);
96 fp = fopen(filename, "r");
97 if (!fp) {
98 perror_msg("fopen: %s", filename);
99 return;
100 }
101
102 cache_head = calloc(cur_array_size, sizeof(*cache_head));
103 if (!cache_head)
104 die_out_of_memory();
105
106 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
107 binary_path[0] = '\0'; // 'reset' it just to be paranoid
108
109 sscanf(buffer, "%lx-%lx %*c%*c%*c%*c %lx %*x:%*x %*d %[^\n]",
110 &start_addr, &end_addr, &mmap_offset, binary_path);
111
112 /* ignore special 'fake files' like "[vdso]", "[heap]", "[stack]", */
113 if (binary_path[0] == '[') {
114 continue;
115 }
116
117 if (binary_path[0] == '\0') {
118 continue;
119 }
120
121 if (end_addr < start_addr)
122 perror_msg_and_die("%s: unrecognized maps file format",
123 filename);
124
125 cur_entry = &cache_head[tcp->mmap_cache_size];
126 cur_entry->start_addr = start_addr;
127 cur_entry->end_addr = end_addr;
128 cur_entry->mmap_offset = mmap_offset;
129 cur_entry->binary_filename = strdup(binary_path);
130
131 /*
132 * sanity check to make sure that we're storing
133 * non-overlapping regions in ascending order
134 */
135 if (tcp->mmap_cache_size > 0) {
136 prev_entry = &cache_head[tcp->mmap_cache_size - 1];
137 if (prev_entry->start_addr >= cur_entry->start_addr)
138 perror_msg_and_die("Overlaying memory region in %s",
139 filename);
140 if (prev_entry->end_addr > cur_entry->start_addr)
141 perror_msg_and_die("Overlaying memory region in %s",
142 filename);
143 }
144 tcp->mmap_cache_size++;
145
146 /* resize doubling its size */
147 if (tcp->mmap_cache_size >= cur_array_size) {
148 cur_array_size *= 2;
149 cache_head = realloc(cache_head, cur_array_size * sizeof(*cache_head));
150 if (!cache_head)
151 die_out_of_memory();
152 }
153 }
154 fclose(fp);
155 tcp->mmap_cache = cache_head;
156}
157
158/* deleting the cache */
159void
Masatake YAMATO61413922014-04-16 15:33:02 +0900160unwind_cache_invalidate(struct tcb* tcp)
Luca Clementi327064b2013-07-23 00:11:35 -0700161{
162 unsigned int i;
163 for (i = 0; i < tcp->mmap_cache_size; i++) {
164 free(tcp->mmap_cache[i].binary_filename);
165 tcp->mmap_cache[i].binary_filename = NULL;
166 }
167 free(tcp->mmap_cache);
168 tcp->mmap_cache = NULL;
169 tcp->mmap_cache_size = 0;
170}
171
172/* use libunwind to unwind the stack and print a backtrace */
173void
Masatake YAMATO61413922014-04-16 15:33:02 +0900174unwind_print_stacktrace(struct tcb* tcp)
Luca Clementi327064b2013-07-23 00:11:35 -0700175{
176 unw_word_t ip;
177 unw_cursor_t cursor;
178 unw_word_t function_off_set;
179 int stack_depth = 0, ret_val;
180 /* these are used for the binary search through the mmap_chace */
181 unsigned int lower, upper, mid;
182 size_t symbol_name_size = 40;
183 char * symbol_name;
184 struct mmap_cache_t* cur_mmap_cache;
185 unsigned long true_offset;
186
187 if (!tcp->mmap_cache)
188 alloc_mmap_cache(tcp);
189 if (!tcp->mmap_cache || !tcp->mmap_cache_size)
190 return;
191
192 symbol_name = malloc(symbol_name_size);
193 if (!symbol_name)
194 die_out_of_memory();
195
196 if (unw_init_remote(&cursor, libunwind_as, tcp->libunwind_ui) < 0)
197 perror_msg_and_die("Can't initiate libunwind");
198
199 do {
200 /* looping on the stack frame */
201 if (unw_get_reg(&cursor, UNW_REG_IP, &ip) < 0) {
202 perror_msg("Can't walk the stack of process %d", tcp->pid);
203 break;
204 }
205
206 lower = 0;
207 upper = tcp->mmap_cache_size - 1;
208
209 while (lower <= upper) {
210 /* find the mmap_cache and print the stack frame */
211 mid = (upper + lower) / 2;
212 cur_mmap_cache = &tcp->mmap_cache[mid];
213
214 if (ip >= cur_mmap_cache->start_addr &&
215 ip < cur_mmap_cache->end_addr) {
216 for (;;) {
217 symbol_name[0] = '\0';
218 ret_val = unw_get_proc_name(&cursor, symbol_name,
219 symbol_name_size, &function_off_set);
220 if (ret_val != -UNW_ENOMEM)
221 break;
222 symbol_name_size *= 2;
223 symbol_name = realloc(symbol_name, symbol_name_size);
224 if (!symbol_name)
225 die_out_of_memory();
226 }
227
228 true_offset = ip - cur_mmap_cache->start_addr +
229 cur_mmap_cache->mmap_offset;
230 if (symbol_name[0]) {
231 /*
232 * we want to keep the format used by backtrace_symbols from the glibc
233 *
234 * ./a.out() [0x40063d]
235 * ./a.out() [0x4006bb]
236 * ./a.out() [0x4006c6]
237 * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
238 * ./a.out() [0x400569]
239 */
240 tprintf(" > %s(%s+0x%lx) [0x%lx]\n",
241 cur_mmap_cache->binary_filename,
242 symbol_name, function_off_set, true_offset);
243 } else {
244 tprintf(" > %s() [0x%lx]\n",
245 cur_mmap_cache->binary_filename, true_offset);
246 }
247 line_ended();
248 break; /* stack frame printed */
249 }
250 else if (mid == 0) {
251 /*
252 * there is a bug in libunwind >= 1.0
253 * after a set_tid_address syscall
254 * unw_get_reg returns IP == 0
255 */
256 if(ip)
257 tprintf(" > backtracing_error\n");
258 line_ended();
259 goto ret;
260 }
261 else if (ip < cur_mmap_cache->start_addr)
Masatake YAMATOb4a2de82014-04-16 15:32:59 +0900262 upper = mid;
Luca Clementi327064b2013-07-23 00:11:35 -0700263 else
264 lower = mid + 1;
265
266 }
267 if (lower > upper) {
268 tprintf(" > backtracing_error [0x%lx]\n", ip);
269 line_ended();
270 goto ret;
271 }
272
273 ret_val = unw_step(&cursor);
274
275 if (++stack_depth > 255) {
276 tprintf("> too many stack frames\n");
277 line_ended();
278 break;
279 }
280 } while (ret_val > 0);
281ret:
282 free(symbol_name);
283}