Added a frame enumerator to backtrace_t
[strongswan.git] / src / libstrongswan / utils / backtrace.c
1 /*
2 * Copyright (C) 2006-2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17
18 #ifdef HAVE_DLADDR
19 # include <dlfcn.h>
20 #endif /* HAVE_DLADDR */
21
22 #ifdef HAVE_BACKTRACE
23 # include <execinfo.h>
24 #endif /* HAVE_BACKTRACE */
25
26 #include <string.h>
27
28 #include "backtrace.h"
29
30 typedef struct private_backtrace_t private_backtrace_t;
31
32 /**
33 * Private data of an backtrace_t object.
34 */
35 struct private_backtrace_t {
36
37 /**
38 * Public backtrace_t interface.
39 */
40 backtrace_t public;
41
42 /**
43 * Number of stacks frames obtained in stack_frames
44 */
45 int frame_count;
46
47 /**
48 * Recorded stack frames.
49 */
50 void *frames[];
51 };
52
53 METHOD(backtrace_t, log_, void,
54 private_backtrace_t *this, FILE *file, bool detailed)
55 {
56 #ifdef HAVE_BACKTRACE
57 size_t i;
58 char **strings;
59
60 strings = backtrace_symbols(this->frames, this->frame_count);
61
62 fprintf(file, " dumping %d stack frame addresses:\n", this->frame_count);
63 for (i = 0; i < this->frame_count; i++)
64 {
65 #ifdef HAVE_DLADDR
66 Dl_info info;
67
68 if (dladdr(this->frames[i], &info))
69 {
70 char cmd[1024];
71 FILE *output;
72 int c;
73 void *ptr = this->frames[i];
74
75 if (strstr(info.dli_fname, ".so"))
76 {
77 ptr = (void*)(this->frames[i] - info.dli_fbase);
78 }
79 if (info.dli_sname)
80 {
81 fprintf(file, " \e[33m%s\e[0m @ %p (\e[31m%s\e[0m+0x%tx) [%p]\n",
82 info.dli_fname, info.dli_fbase, info.dli_sname,
83 this->frames[i] - info.dli_saddr, this->frames[i]);
84 }
85 else
86 {
87 fprintf(file, " \e[33m%s\e[0m @ %p [%p]\n", info.dli_fname,
88 info.dli_fbase, this->frames[i]);
89 }
90 if (detailed)
91 {
92 fprintf(file, " -> \e[32m");
93 snprintf(cmd, sizeof(cmd), "addr2line -e %s %p",
94 info.dli_fname, ptr);
95 output = popen(cmd, "r");
96 if (output)
97 {
98 while (TRUE)
99 {
100 c = getc(output);
101 if (c == '\n' || c == EOF)
102 {
103 break;
104 }
105 fputc(c, file);
106 }
107 pclose(output);
108 }
109 else
110 {
111 #endif /* HAVE_DLADDR */
112 fprintf(file, " %s\n", strings[i]);
113 #ifdef HAVE_DLADDR
114 }
115 fprintf(file, "\n\e[0m");
116 }
117 }
118 else
119 {
120 fprintf(file, " %s\n", strings[i]);
121 }
122 #endif /* HAVE_DLADDR */
123 }
124 free (strings);
125 #else /* !HAVE_BACKTRACE */
126 fprintf(file, "C library does not support backtrace().\n");
127 #endif /* HAVE_BACKTRACE */
128 }
129
130 METHOD(backtrace_t, contains_function, bool,
131 private_backtrace_t *this, char *function[], int count)
132 {
133 #ifdef HAVE_DLADDR
134 int i, j;
135
136 for (i = 0; i< this->frame_count; i++)
137 {
138 Dl_info info;
139
140 if (dladdr(this->frames[i], &info) && info.dli_sname)
141 {
142 for (j = 0; j < count; j++)
143 {
144 if (streq(info.dli_sname, function[j]))
145 {
146 return TRUE;
147 }
148 }
149 }
150 }
151 #endif /* HAVE_DLADDR */
152 return FALSE;
153 }
154
155 METHOD(backtrace_t, equals, bool,
156 private_backtrace_t *this, backtrace_t *other_public)
157 {
158 private_backtrace_t *other = (private_backtrace_t*)other_public;
159 int i;
160
161 if (this == other)
162 {
163 return TRUE;
164 }
165 if (this->frame_count != other->frame_count)
166 {
167 return FALSE;
168 }
169 for (i = 0; i < this->frame_count; i++)
170 {
171 if (this->frames[i] != other->frames[i])
172 {
173 return FALSE;
174 }
175 }
176 return TRUE;
177 }
178
179 /**
180 * Frame enumerator
181 */
182 typedef struct {
183 /** implements enumerator_t */
184 enumerator_t public;
185 /** reference to backtrace */
186 private_backtrace_t *bt;
187 /** current position */
188 int i;
189 } frame_enumerator_t;
190
191 METHOD(enumerator_t, frame_enumerate, bool,
192 frame_enumerator_t *this, void **addr)
193 {
194 if (this->i < this->bt->frame_count)
195 {
196 *addr = this->bt->frames[this->i++];
197 return TRUE;
198 }
199 return FALSE;
200 }
201
202 METHOD(backtrace_t, create_frame_enumerator, enumerator_t*,
203 private_backtrace_t *this)
204 {
205 frame_enumerator_t *enumerator;
206
207 INIT(enumerator,
208 .public = {
209 .enumerate = (void*)_frame_enumerate,
210 .destroy = (void*)free,
211 },
212 .bt = this,
213 );
214 return &enumerator->public;
215 }
216
217 METHOD(backtrace_t, destroy, void,
218 private_backtrace_t *this)
219 {
220 free(this);
221 }
222
223 /**
224 * See header
225 */
226 backtrace_t *backtrace_create(int skip)
227 {
228 private_backtrace_t *this;
229 void *frames[50];
230 int frame_count = 0;
231
232 #ifdef HAVE_BACKTRACE
233 frame_count = backtrace(frames, countof(frames));
234 #endif /* HAVE_BACKTRACE */
235 frame_count = max(frame_count - skip, 0);
236 this = malloc(sizeof(private_backtrace_t) + frame_count * sizeof(void*));
237 memcpy(this->frames, frames + skip, frame_count * sizeof(void*));
238 this->frame_count = frame_count;
239
240 this->public = (backtrace_t) {
241 .log = _log_,
242 .contains_function = _contains_function,
243 .equals = _equals,
244 .create_frame_enumerator = _create_frame_enumerator,
245 .destroy = _destroy,
246 };
247
248 return &this->public;
249 }
250