summaryrefslogtreecommitdiff
path: root/src/bin/eina
diff options
context:
space:
mode:
authorJean Guyomarc'h <jean@guyomarch.bzh>2016-05-28 18:26:10 +0200
committerJean Guyomarc'h <jean@guyomarch.bzh>2016-05-28 19:19:02 +0200
commit0d8e054556de4ef8a6d77db5fcb42e0fb8b837f2 (patch)
tree51cf9c851a01628c452636d7b51ca27b830bbe3d /src/bin/eina
parent0bb75b5fb6b2f9ae294c9c615028b420c842aa68 (diff)
eina_btlog: add Mac OS X support for backtrace
This was actually difficult... Mac OS X can use addr2line (sometimes called gaddr2line in function of the package managers). However, addr2line does NOT handle specific cases. It was therefore necessary to use Mac OS X' own tool: atos, which gracefully handles all backtraces, including the one containing objective-c messages or fat archives. eina_btlog now tests different utilities one by one, and determines whether it is supported or not. Fixes T3711
Diffstat (limited to 'src/bin/eina')
-rw-r--r--src/bin/eina/eina_btlog.c169
1 files changed, 164 insertions, 5 deletions
diff --git a/src/bin/eina/eina_btlog.c b/src/bin/eina/eina_btlog.c
index d3cbf77c5c..6c03fd8e47 100644
--- a/src/bin/eina/eina_btlog.c
+++ b/src/bin/eina/eina_btlog.c
@@ -27,14 +27,18 @@
27// shared objects, source files, and line numbers. even nicely colored and 27// shared objects, source files, and line numbers. even nicely colored and
28// columnated. this is more the start of a bunch of debug tools for efl to make 28// columnated. this is more the start of a bunch of debug tools for efl to make
29// it easier to identify issues. 29// it easier to identify issues.
30// 30//
31// how to use: 31// how to use:
32// 32//
33// cat mybacktrace.txt | eina_btlog 33// cat mybacktrace.txt | eina_btlog
34// 34//
35// (or just run it and copy & paste in on stdin - what i do mostly, and out 35// (or just run it and copy & paste in on stdin - what i do mostly, and out
36// pops a nice backtrace, hit ctrl+d to end) 36// pops a nice backtrace, hit ctrl+d to end)
37 37
38#if defined (__MacOSX__) || (defined (__MACH__) && defined (__APPLE__))
39# define ATOS_COMPATIBLE
40#endif
41
38typedef struct _Bt Bt; 42typedef struct _Bt Bt;
39 43
40struct _Bt 44struct _Bt
@@ -47,6 +51,25 @@ struct _Bt
47 int line; 51 int line;
48}; 52};
49 53
54typedef Eina_Bool (*Translate_Func)(const char *bin_dir,
55 const char *bin_name,
56 unsigned long long addr,
57 char **file_dir,
58 char **file_name,
59 char **func_name,
60 int *file_line);
61
62typedef struct _Translation_Desc Translation_Desc;
63
64struct _Translation_Desc
65{
66 const char *name;
67 const char *test;
68 Translate_Func func;
69};
70
71static Translate_Func _translate = NULL;
72
50static void 73static void
51path_split(const char *path, char **dir, char **file) 74path_split(const char *path, char **dir, char **file)
52{ 75{
@@ -119,6 +142,88 @@ _addr2line(const char *bin_dir, const char *bin_name, unsigned long long addr,
119 return ok; 142 return ok;
120} 143}
121 144
145#ifdef ATOS_COMPATIBLE
146static Eina_Bool
147_atos(const char *bin_dir, const char *bin_name, unsigned long long addr,
148 char **file_dir, char **file_name, char **func_name, int *file_line)
149{
150 char buf[4096];
151 FILE *p = NULL;
152 char *f1 = NULL, *s;
153 Eina_Bool ret = EINA_FALSE;
154 unsigned int count = 0, len;
155 Eina_Bool func_done = EINA_FALSE;
156 unsigned int spaces = 0, func_space_count;
157
158 // Example of what we want to parse
159 // $ atos -o /usr/local/lib/libevas.1.dylib 0xa82d
160 // evas_object_clip_recalc (in libevas.1.dylib) (evas_inline.x:353)
161 //
162 // WARNING! Sometimes:
163 // tlv_load_notification (in libdyld.dylib) + 382
164 //
165 // WARNING! Objective-C methods:
166 // -[EcoreCocoaWindow windowDidResize:] (in libecore_cocoa.1.dylib) (ecore_cocoa_window.m:97)
167
168 snprintf(buf, sizeof(buf), "atos -o %s/%s 0x%llx", bin_dir, bin_name, addr);
169 p = popen(buf, "r");
170 if (!p) goto end;
171
172 s = fgets(buf, sizeof(buf), p);
173 if (!s) goto end;
174
175 /* Default value, used as a fallback when cannot be determined */
176 *file_line = -1;
177
178 if (*s == '-') /* objc method... will contain an extra space */
179 func_space_count = 2;
180 else
181 func_space_count = 1;
182
183 do
184 {
185 if (*s == ' ') spaces++;
186
187 if ((spaces == func_space_count) && (func_done == EINA_FALSE))
188 {
189 *s = '\0';
190 *func_name = strndup(buf, (int)(s - &(buf[0])));
191 func_done = EINA_TRUE;
192 }
193 else if (*s == '(')
194 {
195 count++;
196 if ((count == 2) && (f1 == NULL))
197 {
198 f1 = s + 1; /* skip the leading '(' */
199 }
200 }
201 else if ((*s == ':') && (func_done == EINA_TRUE))
202 {
203 *s = '\0';
204 *file_name = strndup(f1, (int)(s - f1));
205 s++;
206 len = strlen(s);
207 s[len - 1] = '\0'; /* Remove the closing parenthesis */
208 *file_line = atoi(s);
209 break; /* Done */
210 }
211 }
212 while (*(++s) != '\0');
213
214 /* Cannot be determined */
215 *file_dir = strdup("??");
216
217 if (!*func_name) *func_name = strdup("??");
218 if (!*file_name) *file_name = strdup("??");
219
220 ret = EINA_TRUE;
221end:
222 if (p) pclose(p);
223 return ret;
224}
225#endif
226
122static Eina_List * 227static Eina_List *
123bt_append(Eina_List *btl, const char *btline) 228bt_append(Eina_List *btl, const char *btline)
124{ 229{
@@ -139,11 +244,11 @@ bt_append(Eina_List *btl, const char *btline)
139 path_split(bin, &(bt->bin_dir), &(bt->bin_name)); 244 path_split(bin, &(bt->bin_dir), &(bt->bin_name));
140 if (!bt->bin_dir) bt->bin_dir = strdup(""); 245 if (!bt->bin_dir) bt->bin_dir = strdup("");
141 if (!bt->bin_name) bt->bin_name = strdup(""); 246 if (!bt->bin_name) bt->bin_name = strdup("");
142 if (!_addr2line(bt->bin_dir, bt->bin_name, offset - base, 247 if (!_translate(bt->bin_dir, bt->bin_name, offset - base,
143 &(bt->file_dir), &(bt->file_name), 248 &(bt->file_dir), &(bt->file_name),
144 &(bt->func_name), &(bt->line))) 249 &(bt->func_name), &(bt->line)))
145 { 250 {
146 if (!_addr2line(bt->bin_dir, bt->bin_name, offset, 251 if (!_translate(bt->bin_dir, bt->bin_name, offset,
147 &(bt->file_dir), &(bt->file_name), 252 &(bt->file_dir), &(bt->file_name),
148 &(bt->func_name), &(bt->line))) 253 &(bt->func_name), &(bt->line)))
149 { 254 {
@@ -159,6 +264,32 @@ bt_append(Eina_List *btl, const char *btline)
159 return btl; 264 return btl;
160} 265}
161 266
267static Eina_Bool
268_translation_function_detect(const Translation_Desc *desc)
269{
270 const Translation_Desc *d = desc;
271 FILE *p;
272 int ret;
273
274 while ((d->name != NULL) && (d->func != NULL) && (d->test != NULL))
275 {
276 p = popen(d->test, "r");
277 if (p)
278 {
279 ret = pclose(p);
280 ret = WEXITSTATUS(ret);
281 if (ret == 0)
282 {
283 _translate = d->func;
284 break;
285 }
286 }
287 d++;
288 }
289
290 return (_translate == NULL) ? EINA_FALSE : EINA_TRUE;
291}
292
162int 293int
163main(void) 294main(void)
164{ 295{
@@ -166,8 +297,36 @@ main(void)
166 char buf[4096]; 297 char buf[4096];
167 Bt *bt; 298 Bt *bt;
168 int cols[6] = { 0 }, len, i; 299 int cols[6] = { 0 }, len, i;
300 const Translation_Desc desc[] = {
301#ifdef ATOS_COMPATIBLE
302 { /* Mac OS X */
303 .name = "atos",
304 .test = "atos --help &> /dev/null",
305 .func = _atos
306 },
307#endif
308 { /* GNU binutils */
309 .name = "addr2line",
310 .test = "addr2line --help &> /dev/null",
311 .func = _addr2line
312 },
313 { /* For imported GNU binutils */
314 .name = "GNU addr2line",
315 .test = "gaddr2line --help &> /dev/null",
316 .func = _addr2line
317 },
318 { NULL, NULL, NULL } /* Sentinel */
319 };
169 320
170 eina_init(); 321 eina_init();
322
323 if (!_translation_function_detect(desc))
324 {
325 EINA_LOG_CRIT("Fail to determine a program to translate backtrace "
326 "into human-readable text");
327 return 1;
328 }
329
171 while (fgets(buf, sizeof(buf) - 1, stdin)) 330 while (fgets(buf, sizeof(buf) - 1, stdin))
172 { 331 {
173 btl = bt_append(btl, buf); 332 btl = bt_append(btl, buf);