forked from enlightenment/efl
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
This commit is contained in:
parent
0bb75b5fb6
commit
0d8e054556
|
@ -35,6 +35,10 @@
|
||||||
// (or just run it and copy & paste in on stdin - what i do mostly, and out
|
// (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)
|
// pops a nice backtrace, hit ctrl+d to end)
|
||||||
|
|
||||||
|
#if defined (__MacOSX__) || (defined (__MACH__) && defined (__APPLE__))
|
||||||
|
# define ATOS_COMPATIBLE
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct _Bt Bt;
|
typedef struct _Bt Bt;
|
||||||
|
|
||||||
struct _Bt
|
struct _Bt
|
||||||
|
@ -47,6 +51,25 @@ struct _Bt
|
||||||
int line;
|
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
|
static void
|
||||||
path_split(const char *path, char **dir, char **file)
|
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;
|
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 *
|
static Eina_List *
|
||||||
bt_append(Eina_List *btl, const char *btline)
|
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));
|
path_split(bin, &(bt->bin_dir), &(bt->bin_name));
|
||||||
if (!bt->bin_dir) bt->bin_dir = strdup("");
|
if (!bt->bin_dir) bt->bin_dir = strdup("");
|
||||||
if (!bt->bin_name) bt->bin_name = 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->file_dir), &(bt->file_name),
|
||||||
&(bt->func_name), &(bt->line)))
|
&(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->file_dir), &(bt->file_name),
|
||||||
&(bt->func_name), &(bt->line)))
|
&(bt->func_name), &(bt->line)))
|
||||||
{
|
{
|
||||||
|
@ -159,6 +264,32 @@ bt_append(Eina_List *btl, const char *btline)
|
||||||
return btl;
|
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
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
|
@ -166,8 +297,36 @@ main(void)
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
Bt *bt;
|
Bt *bt;
|
||||||
int cols[6] = { 0 }, len, i;
|
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();
|
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))
|
while (fgets(buf, sizeof(buf) - 1, stdin))
|
||||||
{
|
{
|
||||||
btl = bt_append(btl, buf);
|
btl = bt_append(btl, buf);
|
||||||
|
|
Loading…
Reference in New Issue