blob: 7c179bf2f35dc049d744c5d88c46a82b4f4f9845 [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
54init_unwind_addr_space(void)
55{
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
62init_libunwind_ui(struct tcb *tcp)
63{
64 tcp->libunwind_ui = _UPT_create(tcp->pid);
65 if (!tcp->libunwind_ui)
66 die_out_of_memory();
67}
68
69void
70free_libunwind_ui(struct tcb *tcp)
71{
72 _UPT_destroy(tcp->libunwind_ui);
73 tcp->libunwind_ui = NULL;
74}
75
76/*
77 * caching of /proc/ID/maps for each process to speed up stack tracing
78 *
79 * The cache must be refreshed after some syscall: mmap, mprotect, munmap, execve
80 */
Masatake YAMATOb65042f2014-04-16 15:33:00 +090081static void
Luca Clementi327064b2013-07-23 00:11:35 -070082alloc_mmap_cache(struct tcb* tcp)
83{
84 unsigned long start_addr, end_addr, mmap_offset;
85 char filename[sizeof ("/proc/0123456789/maps")];
86 char buffer[PATH_MAX + 80];
87 char binary_path[PATH_MAX];
88 struct mmap_cache_t *cur_entry, *prev_entry;
89 /* start with a small dynamically-allocated array and then expand it */
90 size_t cur_array_size = 10;
91 struct mmap_cache_t *cache_head;
92 FILE *fp;
93
94 sprintf(filename, "/proc/%d/maps", tcp->pid);
95 fp = fopen(filename, "r");
96 if (!fp) {
97 perror_msg("fopen: %s", filename);
98 return;
99 }
100
101 cache_head = calloc(cur_array_size, sizeof(*cache_head));
102 if (!cache_head)
103 die_out_of_memory();
104
105 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
106 binary_path[0] = '\0'; // 'reset' it just to be paranoid
107
108 sscanf(buffer, "%lx-%lx %*c%*c%*c%*c %lx %*x:%*x %*d %[^\n]",
109 &start_addr, &end_addr, &mmap_offset, binary_path);
110
111 /* ignore special 'fake files' like "[vdso]", "[heap]", "[stack]", */
112 if (binary_path[0] == '[') {
113 continue;
114 }
115
116 if (binary_path[0] == '\0') {
117 continue;
118 }
119
120 if (end_addr < start_addr)
121 perror_msg_and_die("%s: unrecognized maps file format",
122 filename);
123
124 cur_entry = &cache_head[tcp->mmap_cache_size];
125 cur_entry->start_addr = start_addr;
126 cur_entry->end_addr = end_addr;
127 cur_entry->mmap_offset = mmap_offset;
128 cur_entry->binary_filename = strdup(binary_path);
129
130 /*
131 * sanity check to make sure that we're storing
132 * non-overlapping regions in ascending order
133 */
134 if (tcp->mmap_cache_size > 0) {
135 prev_entry = &cache_head[tcp->mmap_cache_size - 1];
136 if (prev_entry->start_addr >= cur_entry->start_addr)
137 perror_msg_and_die("Overlaying memory region in %s",
138 filename);
139 if (prev_entry->end_addr > cur_entry->start_addr)
140 perror_msg_and_die("Overlaying memory region in %s",
141 filename);
142 }
143 tcp->mmap_cache_size++;
144
145 /* resize doubling its size */
146 if (tcp->mmap_cache_size >= cur_array_size) {
147 cur_array_size *= 2;
148 cache_head = realloc(cache_head, cur_array_size * sizeof(*cache_head));
149 if (!cache_head)
150 die_out_of_memory();
151 }
152 }
153 fclose(fp);
154 tcp->mmap_cache = cache_head;
155}
156
157/* deleting the cache */
158void
159delete_mmap_cache(struct tcb* tcp)
160{
161 unsigned int i;
162 for (i = 0; i < tcp->mmap_cache_size; i++) {
163 free(tcp->mmap_cache[i].binary_filename);
164 tcp->mmap_cache[i].binary_filename = NULL;
165 }
166 free(tcp->mmap_cache);
167 tcp->mmap_cache = NULL;
168 tcp->mmap_cache_size = 0;
169}
170
171/* use libunwind to unwind the stack and print a backtrace */
172void
173print_stacktrace(struct tcb* tcp)
174{
175 unw_word_t ip;
176 unw_cursor_t cursor;
177 unw_word_t function_off_set;
178 int stack_depth = 0, ret_val;
179 /* these are used for the binary search through the mmap_chace */
180 unsigned int lower, upper, mid;
181 size_t symbol_name_size = 40;
182 char * symbol_name;
183 struct mmap_cache_t* cur_mmap_cache;
184 unsigned long true_offset;
185
186 if (!tcp->mmap_cache)
187 alloc_mmap_cache(tcp);
188 if (!tcp->mmap_cache || !tcp->mmap_cache_size)
189 return;
190
191 symbol_name = malloc(symbol_name_size);
192 if (!symbol_name)
193 die_out_of_memory();
194
195 if (unw_init_remote(&cursor, libunwind_as, tcp->libunwind_ui) < 0)
196 perror_msg_and_die("Can't initiate libunwind");
197
198 do {
199 /* looping on the stack frame */
200 if (unw_get_reg(&cursor, UNW_REG_IP, &ip) < 0) {
201 perror_msg("Can't walk the stack of process %d", tcp->pid);
202 break;
203 }
204
205 lower = 0;
206 upper = tcp->mmap_cache_size - 1;
207
208 while (lower <= upper) {
209 /* find the mmap_cache and print the stack frame */
210 mid = (upper + lower) / 2;
211 cur_mmap_cache = &tcp->mmap_cache[mid];
212
213 if (ip >= cur_mmap_cache->start_addr &&
214 ip < cur_mmap_cache->end_addr) {
215 for (;;) {
216 symbol_name[0] = '\0';
217 ret_val = unw_get_proc_name(&cursor, symbol_name,
218 symbol_name_size, &function_off_set);
219 if (ret_val != -UNW_ENOMEM)
220 break;
221 symbol_name_size *= 2;
222 symbol_name = realloc(symbol_name, symbol_name_size);
223 if (!symbol_name)
224 die_out_of_memory();
225 }
226
227 true_offset = ip - cur_mmap_cache->start_addr +
228 cur_mmap_cache->mmap_offset;
229 if (symbol_name[0]) {
230 /*
231 * we want to keep the format used by backtrace_symbols from the glibc
232 *
233 * ./a.out() [0x40063d]
234 * ./a.out() [0x4006bb]
235 * ./a.out() [0x4006c6]
236 * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
237 * ./a.out() [0x400569]
238 */
239 tprintf(" > %s(%s+0x%lx) [0x%lx]\n",
240 cur_mmap_cache->binary_filename,
241 symbol_name, function_off_set, true_offset);
242 } else {
243 tprintf(" > %s() [0x%lx]\n",
244 cur_mmap_cache->binary_filename, true_offset);
245 }
246 line_ended();
247 break; /* stack frame printed */
248 }
249 else if (mid == 0) {
250 /*
251 * there is a bug in libunwind >= 1.0
252 * after a set_tid_address syscall
253 * unw_get_reg returns IP == 0
254 */
255 if(ip)
256 tprintf(" > backtracing_error\n");
257 line_ended();
258 goto ret;
259 }
260 else if (ip < cur_mmap_cache->start_addr)
Masatake YAMATOb4a2de82014-04-16 15:32:59 +0900261 upper = mid;
Luca Clementi327064b2013-07-23 00:11:35 -0700262 else
263 lower = mid + 1;
264
265 }
266 if (lower > upper) {
267 tprintf(" > backtracing_error [0x%lx]\n", ip);
268 line_ended();
269 goto ret;
270 }
271
272 ret_val = unw_step(&cursor);
273
274 if (++stack_depth > 255) {
275 tprintf("> too many stack frames\n");
276 line_ended();
277 break;
278 }
279 } while (ret_val > 0);
280ret:
281 free(symbol_name);
282}