summaryrefslogtreecommitdiff
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:10:42 +0200
commit7515155ee6c38eaa93a3277ed01cb3dac67fbceb (patch)
treed78f85091956914674633c6d25229e8fae7020b2
parent23b8f9fc09a0a6950844c06aa405a3790814aff7 (diff)
downloadefl-devs/jayji/osx-backtrace.tar.gz
eina_btlog: add Mac OS X support for backtracedevs/jayji/osx-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
-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 @@
// shared objects, source files, and line numbers. even nicely colored and
// columnated. this is more the start of a bunch of debug tools for efl to make
// it easier to identify issues.
-//
+//
// how to use:
-//
+//
// cat mybacktrace.txt | eina_btlog
-//
+//
// (or just run it and copy & paste in on stdin - what i do mostly, and out
// pops a nice backtrace, hit ctrl+d to end)
+#if defined (__MacOSX__) || (defined (__MACH__) && defined (__APPLE__))
+# define ATOS_COMPATIBLE
+#endif
+
typedef struct _Bt Bt;
struct _Bt
@@ -47,6 +51,25 @@ struct _Bt
int line;
};
+typedef Eina_Bool (*Translate_Func)(const char *bin_dir,
+ const char *bin_name,
+ unsigned long long addr,
+ char **file_dir,
+ char **file_name,
+ char **func_name,
+ int *file_line);
+
+typedef struct _Translation_Desc Translation_Desc;
+
+struct _Translation_Desc
+{
+ const char *name;
+ const char *test;
+ Translate_Func func;
+};
+
+static Translate_Func _translate = NULL;
+
static void
path_split(const char *path, char **dir, char **file)
{
@@ -119,6 +142,88 @@ _addr2line(const char *bin_dir, const char *bin_name, unsigned long long addr,
return ok;
}
+#ifdef ATOS_COMPATIBLE
+static Eina_Bool
+_atos(const char *bin_dir, const char *bin_name, unsigned long long addr,
+ char **file_dir, char **file_name, char **func_name, int *file_line)
+{
+ char buf[4096];
+ FILE *p = NULL;
+ char *f1 = NULL, *s;
+ Eina_Bool ret = EINA_FALSE;
+ unsigned int count = 0, len;
+ Eina_Bool func_done = EINA_FALSE;
+ unsigned int spaces = 0, func_space_count;
+
+ // Example of what we want to parse
+ // $ atos -o /usr/local/lib/libevas.1.dylib 0xa82d
+ // evas_object_clip_recalc (in libevas.1.dylib) (evas_inline.x:353)
+ //
+ // WARNING! Sometimes:
+ // tlv_load_notification (in libdyld.dylib) + 382
+ //
+ // WARNING! Objective-C methods:
+ // -[EcoreCocoaWindow windowDidResize:] (in libecore_cocoa.1.dylib) (ecore_cocoa_window.m:97)
+
+ snprintf(buf, sizeof(buf), "atos -o %s/%s 0x%llx", bin_dir, bin_name, addr);
+ p = popen(buf, "r");
+ if (!p) goto end;
+
+ s = fgets(buf, sizeof(buf), p);
+ if (!s) goto end;
+
+ /* Default value, used as a fallback when cannot be determined */
+ *file_line = -1;
+
+ if (*s == '-') /* objc method... will contain an extra space */
+ func_space_count = 2;
+ else
+ func_space_count = 1;
+
+ do
+ {
+ if (*s == ' ') spaces++;
+
+ if ((spaces == func_space_count) && (func_done == EINA_FALSE))
+ {
+ *s = '\0';
+ *func_name = strndup(buf, (int)(s - &(buf[0])));
+ func_done = EINA_TRUE;
+ }
+ else if (*s == '(')
+ {
+ count++;
+ if ((count == 2) && (f1 == NULL))
+ {
+ f1 = s + 1; /* skip the leading '(' */
+ }
+ }
+ else if ((*s == ':') && (func_done == EINA_TRUE))
+ {
+ *s = '\0';
+ *file_name = strndup(f1, (int)(s - f1));
+ s++;
+ len = strlen(s);
+ s[len - 1] = '\0'; /* Remove the closing parenthesis */
+ *file_line = atoi(s);
+ break; /* Done */
+ }
+ }
+ while (*(++s) != '\0');
+
+ /* Cannot be determined */
+ *file_dir = strdup("??");
+
+ if (!*func_name) *func_name = strdup("??");
+ if (!*file_name) *file_name = strdup("??");
+
+ ret = EINA_TRUE;
+end:
+ if (p) pclose(p);
+ return ret;
+}
+#endif
+
static Eina_List *
bt_append(Eina_List *btl, const char *btline)
{
@@ -139,11 +244,11 @@ bt_append(Eina_List *btl, const char *btline)
path_split(bin, &(bt->bin_dir), &(bt->bin_name));
if (!bt->bin_dir) bt->bin_dir = strdup("");
if (!bt->bin_name) bt->bin_name = strdup("");
- if (!_addr2line(bt->bin_dir, bt->bin_name, offset - base,
+ if (!_translate(bt->bin_dir, bt->bin_name, offset - base,
&(bt->file_dir), &(bt->file_name),
&(bt->func_name), &(bt->line)))
{
- if (!_addr2line(bt->bin_dir, bt->bin_name, offset,
+ if (!_translate(bt->bin_dir, bt->bin_name, offset,
&(bt->file_dir), &(bt->file_name),
&(bt->func_name), &(bt->line)))
{
@@ -159,6 +264,32 @@ bt_append(Eina_List *btl, const char *btline)
return btl;
}
+static Eina_Bool
+_translation_function_detect(const Translation_Desc *desc)
+{
+ const Translation_Desc *d = desc;
+ FILE *p;
+ int ret;
+
+ while ((d->name != NULL) && (d->func != NULL) && (d->test != NULL))
+ {
+ p = popen(d->test, "r");
+ if (p)
+ {
+ ret = pclose(p);
+ ret = WEXITSTATUS(ret);
+ if (ret == 0)
+ {
+ _translate = d->func;
+ break;
+ }
+ }
+ d++;
+ }
+
+ return (_translate == NULL) ? EINA_FALSE : EINA_TRUE;
+}
+
int
main(void)
{
@@ -166,8 +297,36 @@ main(void)
char buf[4096];
Bt *bt;
int cols[6] = { 0 }, len, i;
+ const Translation_Desc desc[] = {
+#ifdef ATOS_COMPATIBLE
+ { /* Mac OS X */
+ .name = "atos",
+ .test = "atos --help &> /dev/null",
+ .func = _atos
+ },
+#endif
+ { /* GNU binutils */
+ .name = "addr2line",
+ .test = "addr2line --help &> /dev/null",
+ .func = _addr2line
+ },
+ { /* For imported GNU binutils */
+ .name = "GNU addr2line",
+ .test = "gaddr2line --help &> /dev/null",
+ .func = _addr2line
+ },
+ { NULL, NULL, NULL } /* Sentinel */
+ };
eina_init();
+
+ if (!_translation_function_detect(desc))
+ {
+ EINA_LOG_CRIT("Fail to determine a program to translate backtrace "
+ "into human-readable text");
+ return 1;
+ }
+
while (fgets(buf, sizeof(buf) - 1, stdin))
{
btl = bt_append(btl, buf);