forked from enlightenment/efl
eina - start a much improved eina dbug infra and have eina_log use it
this makes eina_log give bt's for all error logs. this is very useful in finding just where a problem happens. the problem int he past is that these have not been too useful due to backtrace_symbols() being "useless". thus use the eina_btlog tool i added too. also started infra for a debug monitor that can use the backtrace infra to collect runtime stats ANY TIME for a process (don't need to run under a debugger). @feat
This commit is contained in:
parent
38faeacee1
commit
664708b817
|
@ -849,6 +849,12 @@ case "${build_profile}" in
|
|||
;;
|
||||
esac
|
||||
|
||||
PKG_CHECK_MODULES(UNWIND, [libunwind libunwind-generic],
|
||||
[have_unwind=yes], [have_unwind=no])
|
||||
AS_IF([test "x$have_unwind" = "xyes"],
|
||||
[AC_DEFINE([HAVE_UNWIND], [1], [Have libunwind])])
|
||||
AM_CONDITIONAL(HAVE_UNWIND, test "x$have_unwind" = "xyes")
|
||||
|
||||
EINA_CONFIG([HAVE_ALLOCA_H], [test "x${ac_cv_working_alloca_h}" = "xyes"])
|
||||
EINA_CONFIG([SAFETY_CHECKS], [test "x${have_safety_checks}" = "xyes"])
|
||||
EINA_CONFIG([DEFAULT_MEMPOOL], [test "x${want_default_mempool}" = "xyes"])
|
||||
|
@ -4801,7 +4807,7 @@ echo " Cryptography..: ${build_crypto}"
|
|||
echo " X11...........: ${with_x11}"
|
||||
echo " OpenGL........: ${with_opengl}"
|
||||
echo " C++11.........: ${have_cxx11}"
|
||||
echo "Eina............: yes (${features_eina})"
|
||||
echo "Eina............: yes (${features_eina} unwind=$have_unwind)"
|
||||
echo "Eo..............: yes (${features_eo})"
|
||||
echo "Eolian..........: yes (${features_eolian})"
|
||||
echo "Emile...........: yes (${features_emile})"
|
||||
|
|
|
@ -67,3 +67,20 @@ installed_eflluadir = $(datadir)/elua/modules/efl
|
|||
nodist_installed_efllua_DATA = $(generated_efl_lua_all)
|
||||
|
||||
endif
|
||||
|
||||
### Binary
|
||||
|
||||
bin_PROGRAMS += \
|
||||
bin/efl/efl_debugd \
|
||||
bin/efl/efl_debug
|
||||
|
||||
bin_efl_efl_debugd_SOURCES = bin/efl/efl_debugd.c
|
||||
bin_efl_efl_debugd_CPPFLAGS = -I$(top_builddir)/src/bin/efl @EINA_CFLAGS@ @ECORE_CFLAGS@ @ECORE_CON_CFLAGS@
|
||||
bin_efl_efl_debugd_LDADD = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
|
||||
bin_efl_efl_debugd_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
|
||||
|
||||
bin_efl_efl_debug_SOURCES = bin/efl/efl_debug.c
|
||||
bin_efl_efl_debug_CPPFLAGS = -I$(top_builddir)/src/bin/efl @EINA_CFLAGS@ @ECORE_CFLAGS@ @ECORE_CON_CFLAGS@
|
||||
bin_efl_efl_debug_LDADD = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
|
||||
bin_efl_efl_debug_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
|
||||
|
||||
|
|
|
@ -101,6 +101,14 @@ lib/eina/eina_convert.c \
|
|||
lib/eina/eina_counter.c \
|
||||
lib/eina/eina_cow.c \
|
||||
lib/eina/eina_cpu.c \
|
||||
lib/eina/eina_debug.h \
|
||||
lib/eina/eina_debug.c \
|
||||
lib/eina/eina_debug_chunk.c \
|
||||
lib/eina/eina_debug_bt.c \
|
||||
lib/eina/eina_debug_bt_file.c \
|
||||
lib/eina/eina_debug_thread.c \
|
||||
lib/eina/eina_debug_monitor.c \
|
||||
lib/eina/eina_debug_proto.c \
|
||||
lib/eina/eina_error.c \
|
||||
lib/eina/eina_file_common.h \
|
||||
lib/eina/eina_file_common.c \
|
||||
|
@ -221,15 +229,30 @@ endif
|
|||
|
||||
lib_eina_libeina_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
|
||||
@EINA_CFLAGS@ \
|
||||
@UNWIND_CFLAGS@ \
|
||||
-DPACKAGE_BIN_DIR=\"$(bindir)\" \
|
||||
-DPACKAGE_LIB_DIR=\"$(libdir)\" \
|
||||
-DPACKAGE_DATA_DIR=\"$(datadir)/eina\" \
|
||||
@VALGRIND_CFLAGS@
|
||||
|
||||
lib_eina_libeina_la_LIBADD = @EINA_LIBS@ @DL_LIBS@
|
||||
lib_eina_libeina_la_LIBADD = @EINA_LIBS@ @DL_LIBS@ @UNWIND_LIBS@
|
||||
lib_eina_libeina_la_DEPENDENCIES = @EINA_INTERNAL_LIBS@ @DL_INTERNAL_LIBS@
|
||||
lib_eina_libeina_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@
|
||||
|
||||
### Binaries
|
||||
|
||||
bin_PROGRAMS += bin/eina/eina_btlog
|
||||
|
||||
bin_eina_eina_btlog_SOURCES = bin/eina/eina_btlog.c
|
||||
bin_eina_eina_btlog_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
|
||||
-DPACKAGE_BIN_DIR=\"$(bindir)\" \
|
||||
-DPACKAGE_LIB_DIR=\"$(libdir)\" \
|
||||
-DPACKAGE_DATA_DIR=\"$(datadir)/eina\" \
|
||||
@EINA_CFLAGS@
|
||||
|
||||
bin_eina_eina_btlog_LDADD = @USE_EINA_LIBS@
|
||||
bin_eina_eina_btlog_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@
|
||||
|
||||
### Script
|
||||
|
||||
bin_SCRIPTS += scripts/eina/eina-bench-cmp
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
efl_debugd
|
||||
efl_debug
|
|
@ -0,0 +1,179 @@
|
|||
#include <Eina.h>
|
||||
#include <Ecore.h>
|
||||
#include <Ecore_Con.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
static unsigned char *buf;
|
||||
static unsigned int buf_size;
|
||||
|
||||
static int my_argc;
|
||||
static char **my_argv;
|
||||
static const char *expect = NULL;
|
||||
|
||||
static Ecore_Con_Server *svr;
|
||||
|
||||
static void
|
||||
_proto(unsigned char *d, unsigned int size)
|
||||
{
|
||||
if (size >= 4)
|
||||
{
|
||||
char *cmd = (char *)d;
|
||||
|
||||
if (!strncmp(cmd, "CLST", 4))
|
||||
{
|
||||
int i, n;
|
||||
|
||||
n = (size - 4) / sizeof(int);
|
||||
if (n < 10000)
|
||||
{
|
||||
int *pids = malloc(n * sizeof(int));
|
||||
memcpy(pids, d + 4, n * sizeof(int));
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (pids[i] > 0) printf("%i\n", pids[i]);
|
||||
}
|
||||
free(pids);
|
||||
}
|
||||
}
|
||||
if ((expect) && (!strncmp(cmd, expect, 4)))
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Eina_Bool
|
||||
_server_proto(void)
|
||||
{
|
||||
unsigned int size, newsize;
|
||||
unsigned char *b;
|
||||
if (!buf) return EINA_FALSE;
|
||||
if (buf_size < 4) return EINA_FALSE;
|
||||
memcpy(&size, buf, 4);
|
||||
if (buf_size < (size + 4)) return EINA_FALSE;
|
||||
_proto(buf + 4, size);
|
||||
newsize = buf_size - (size + 4);
|
||||
if (buf_size == newsize)
|
||||
{
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
buf_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
b = malloc(newsize);
|
||||
memcpy(b, buf + size + 4, newsize);
|
||||
free(buf);
|
||||
buf = b;
|
||||
buf_size = newsize;
|
||||
}
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
_server_add(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Server_Add *ev)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < my_argc; i++)
|
||||
{
|
||||
if (!strcmp(my_argv[i], "list"))
|
||||
{
|
||||
unsigned int size = 4;
|
||||
char *head = "LIST";
|
||||
expect = "CLST";
|
||||
ecore_con_server_send(svr, &size, 4);
|
||||
ecore_con_server_send(svr, head, 4);
|
||||
}
|
||||
else if ((!strcmp(my_argv[i], "pon")) &&
|
||||
(i < (my_argc - 2)))
|
||||
{
|
||||
unsigned int size = 12;
|
||||
char *head = "PLON";
|
||||
int pid = atoi(my_argv[i + 1]);
|
||||
unsigned int freq = atoi(my_argv[i + 2]);
|
||||
i++;
|
||||
ecore_con_server_send(svr, &size, 4);
|
||||
ecore_con_server_send(svr, head, 4);
|
||||
ecore_con_server_send(svr, &pid, 4);
|
||||
ecore_con_server_send(svr, &freq, 4);
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
else if ((!strcmp(my_argv[i], "poff")) &&
|
||||
(i < (my_argc - 1)))
|
||||
{
|
||||
unsigned int size = 8;
|
||||
char *head = "PLOFF";
|
||||
int pid = atoi(my_argv[i + 1]);
|
||||
i++;
|
||||
ecore_con_server_send(svr, &size, 4);
|
||||
ecore_con_server_send(svr, head, 4);
|
||||
ecore_con_server_send(svr, &pid, 4);
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
}
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
_server_del(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Server_Del *ev)
|
||||
{
|
||||
ecore_main_loop_quit();
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_server_data(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Server_Data *ev)
|
||||
{
|
||||
if (!buf)
|
||||
{
|
||||
buf = malloc(ev->size);
|
||||
if (buf)
|
||||
{
|
||||
buf_size = ev->size;
|
||||
memcpy(buf, ev->data, ev->size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char *b = realloc(buf, buf_size + ev->size);
|
||||
if (b)
|
||||
{
|
||||
buf = b;
|
||||
memcpy(buf + buf_size, ev->data, ev->size);
|
||||
buf_size += ev->size;
|
||||
}
|
||||
}
|
||||
while (_server_proto());
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
eina_init();
|
||||
ecore_init();
|
||||
ecore_con_init();
|
||||
|
||||
my_argc = argc;
|
||||
my_argv = argv;
|
||||
|
||||
svr = ecore_con_server_connect(ECORE_CON_LOCAL_USER, "efl_debug", 0, NULL);
|
||||
if (!svr)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Cannot connetc to debug daemon.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD, (Ecore_Event_Handler_Cb)_server_add, NULL);
|
||||
ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL, (Ecore_Event_Handler_Cb)_server_del, NULL);
|
||||
ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA, (Ecore_Event_Handler_Cb)_server_data, NULL);
|
||||
|
||||
ecore_main_loop_begin();
|
||||
ecore_con_server_flush(svr);
|
||||
|
||||
ecore_con_shutdown();
|
||||
ecore_shutdown();
|
||||
eina_shutdown();
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
#include <Eina.h>
|
||||
#include <Ecore.h>
|
||||
#include <Ecore_Con.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct _Client Client;
|
||||
|
||||
struct _Client
|
||||
{
|
||||
Ecore_Con_Client *client;
|
||||
int version;
|
||||
pid_t pid;
|
||||
unsigned char *buf;
|
||||
unsigned int buf_size;
|
||||
};
|
||||
|
||||
static Ecore_Con_Server *svr = NULL;
|
||||
static Eina_List *clients = NULL;
|
||||
|
||||
static void
|
||||
_proto(Client *c, unsigned char *d, unsigned int size)
|
||||
{
|
||||
if (size >= 4)
|
||||
{
|
||||
char *cmd = (char *)d;
|
||||
|
||||
if (!strncmp(cmd, "HELO", 4))
|
||||
{
|
||||
int version;
|
||||
int pid;
|
||||
|
||||
memcpy(&version, d + 4, 4);
|
||||
memcpy(&pid, d + 8, 4);
|
||||
c->version = version;
|
||||
c->pid = pid;
|
||||
}
|
||||
else if (!strncmp(cmd, "LIST", 4))
|
||||
{
|
||||
int n = eina_list_count(clients), i;
|
||||
unsigned int *pids, size2;
|
||||
Client *c2;
|
||||
Eina_List *l;
|
||||
char *head = "CLST";
|
||||
|
||||
pids = malloc(n * sizeof(int));
|
||||
i = 0;
|
||||
size2 = 4 + (n * sizeof(int));
|
||||
EINA_LIST_FOREACH(clients, l, c2)
|
||||
{
|
||||
pids[i] = c2->pid;
|
||||
i++;
|
||||
}
|
||||
ecore_con_client_send(c->client, &size2, 4);
|
||||
ecore_con_client_send(c->client, head, 4);
|
||||
ecore_con_client_send(c->client, pids, n * sizeof(int));
|
||||
free(pids);
|
||||
}
|
||||
else if (!strncmp(cmd, "PLON", 4))
|
||||
{
|
||||
int pid;
|
||||
unsigned int freq = 1000;
|
||||
Client *c2;
|
||||
Eina_List *l;
|
||||
|
||||
memcpy(&pid, d + 4, 4);
|
||||
memcpy(&freq, d + 8, 4);
|
||||
if (pid > 0)
|
||||
{
|
||||
EINA_LIST_FOREACH(clients, l, c2)
|
||||
{
|
||||
if (c2->pid == pid)
|
||||
{
|
||||
unsigned int size2 = 8;
|
||||
|
||||
ecore_con_client_send(c2->client, &size2, 4);
|
||||
ecore_con_client_send(c2->client, d, 4);
|
||||
ecore_con_client_send(c2->client, &freq, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strncmp(cmd, "PLOF", 4))
|
||||
{
|
||||
int pid;
|
||||
Client *c2;
|
||||
Eina_List *l;
|
||||
|
||||
memcpy(&pid, d + 4, 4);
|
||||
if (pid > 0)
|
||||
{
|
||||
EINA_LIST_FOREACH(clients, l, c2)
|
||||
{
|
||||
if (c2->pid == pid)
|
||||
{
|
||||
unsigned int size2 = 4;
|
||||
|
||||
ecore_con_client_send(c2->client, &size2, 4);
|
||||
ecore_con_client_send(c2->client, d, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_client_proto(Client *c)
|
||||
{
|
||||
unsigned int size, newsize;
|
||||
unsigned char *b;
|
||||
if (!c->buf) return EINA_FALSE;
|
||||
if (c->buf_size < 4) return EINA_FALSE;
|
||||
memcpy(&size, c->buf, 4);
|
||||
if (c->buf_size < (size + 4)) return EINA_FALSE;
|
||||
_proto(c, c->buf + 4, size);
|
||||
newsize = c->buf_size - (size + 4);
|
||||
if (c->buf_size == newsize)
|
||||
{
|
||||
free(c->buf);
|
||||
c->buf = NULL;
|
||||
c->buf_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
b = malloc(newsize);
|
||||
memcpy(b, c->buf + size + 4, newsize);
|
||||
free(c->buf);
|
||||
c->buf = b;
|
||||
c->buf_size = newsize;
|
||||
}
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_client_add(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Client_Add *ev)
|
||||
{
|
||||
Client *c = calloc(1, sizeof(Client));
|
||||
if (c)
|
||||
{
|
||||
c->client = ev->client;
|
||||
clients = eina_list_append(clients, c);
|
||||
ecore_con_client_data_set(c->client, c);
|
||||
}
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_client_del(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Client_Del *ev)
|
||||
{
|
||||
Client *c = ecore_con_client_data_get(ev->client);
|
||||
if (c)
|
||||
{
|
||||
clients = eina_list_remove(clients, c);
|
||||
free(c);
|
||||
}
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_client_data(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Client_Data *ev)
|
||||
{
|
||||
Client *c = ecore_con_client_data_get(ev->client);
|
||||
if (c)
|
||||
{
|
||||
if (!c->buf)
|
||||
{
|
||||
c->buf = malloc(ev->size);
|
||||
if (c->buf)
|
||||
{
|
||||
c->buf_size = ev->size;
|
||||
memcpy(c->buf, ev->data, ev->size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char *b = realloc(c->buf, c->buf_size + ev->size);
|
||||
if (b)
|
||||
{
|
||||
c->buf = b;
|
||||
memcpy(c->buf + c->buf_size, ev->data, ev->size);
|
||||
c->buf_size += ev->size;
|
||||
}
|
||||
}
|
||||
while (_client_proto(c));
|
||||
}
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
|
||||
{
|
||||
eina_init();
|
||||
ecore_init();
|
||||
ecore_con_init();
|
||||
|
||||
svr = ecore_con_server_add(ECORE_CON_LOCAL_USER, "efl_debug", 0, NULL);
|
||||
if (!svr)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Cannot create debug daemon.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_ADD, (Ecore_Event_Handler_Cb)_client_add, NULL);
|
||||
ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DEL, (Ecore_Event_Handler_Cb)_client_del, NULL);
|
||||
ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DATA, (Ecore_Event_Handler_Cb)_client_data, NULL);
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
||||
ecore_con_server_del(svr);
|
||||
|
||||
ecore_con_shutdown();
|
||||
ecore_shutdown();
|
||||
eina_shutdown();
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
eina_btlog
|
|
@ -0,0 +1,198 @@
|
|||
#include <Eina.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// right now this is quick and dirty and may have some parsing ... frailty,
|
||||
// so don't put malicious data through it... :) but cat in eina bt's through
|
||||
// this to get a nicely clean and readable bt with filenames of binaries,
|
||||
// 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)
|
||||
|
||||
typedef struct _Bt Bt;
|
||||
|
||||
struct _Bt
|
||||
{
|
||||
char *bin_dir;
|
||||
char *bin_name;
|
||||
char *file_dir;
|
||||
char *file_name;
|
||||
char *func_name;
|
||||
int line;
|
||||
};
|
||||
|
||||
static void
|
||||
path_split(const char *path, char **dir, char **file)
|
||||
{
|
||||
const char *p = strrchr(path, '/');
|
||||
|
||||
if (!path)
|
||||
{
|
||||
*dir = NULL;
|
||||
*file = NULL;
|
||||
return;
|
||||
}
|
||||
if (!p)
|
||||
{
|
||||
*dir = NULL;
|
||||
*file = strdup(path);
|
||||
return;
|
||||
}
|
||||
*dir = malloc(p - path + 1);
|
||||
if (!dir)
|
||||
{
|
||||
*dir = NULL;
|
||||
*file = NULL;
|
||||
return;
|
||||
}
|
||||
strncpy(*dir, path, p - path);
|
||||
(*dir)[p - path] = 0;
|
||||
*file = strdup(p + 1);
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_addr2line(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], func[4096], *f1 = NULL, *f2 = NULL;
|
||||
Eina_Bool ok = EINA_FALSE;
|
||||
int line;
|
||||
FILE *p;
|
||||
|
||||
snprintf(buf, sizeof(buf), "addr2line -f -e %s/%s -C -a 0x%llx",
|
||||
bin_dir, bin_name, addr);
|
||||
p = popen(buf, "r");
|
||||
if (!p) return EINA_FALSE;
|
||||
fscanf(p, "%s\n", buf);
|
||||
if (fscanf(p, "%s\n", func) == 1)
|
||||
{
|
||||
if (fscanf(p, "%[^:]:%i\n", buf, &line) == 2)
|
||||
{
|
||||
path_split(buf, &(f1), &(f2));
|
||||
if ((!f1) || (!f2))
|
||||
{
|
||||
free(f1);
|
||||
free(f2);
|
||||
pclose(p);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
f1 = strdup("??");
|
||||
f2 = strdup("??");
|
||||
}
|
||||
*file_dir = f1;
|
||||
*file_name = f2;
|
||||
*func_name = strdup(func);
|
||||
*file_line = line;
|
||||
ok = EINA_TRUE;
|
||||
}
|
||||
pclose(p);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static Eina_List *
|
||||
bt_append(Eina_List *btl, const char *btline)
|
||||
{
|
||||
Bt *bt = calloc(1, sizeof(Bt));
|
||||
if (!bt) return btl;
|
||||
char *bin = strdup(btline);
|
||||
unsigned long long offset = 0, base = 0;
|
||||
|
||||
// parse:
|
||||
// /usr/local/lib/libeina.so.1 0x1ec88
|
||||
// /usr/local/lib/libelementary.so.1 0x10f695
|
||||
// /usr/local/lib/libeo.so.1 0xa474
|
||||
// /usr/local/lib/libelementary.so.1 0x139bd6
|
||||
// /usr/local/bin/elementary_test 0x8196d
|
||||
// /usr/local/bin/elementary_test 0x81b6a
|
||||
if (sscanf(btline, "%s %llx %llx", bin, &offset, &base) == 3)
|
||||
{
|
||||
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,
|
||||
&(bt->file_dir), &(bt->file_name),
|
||||
&(bt->func_name), &(bt->line)))
|
||||
{
|
||||
if (!_addr2line(bt->bin_dir, bt->bin_name, offset,
|
||||
&(bt->file_dir), &(bt->file_name),
|
||||
&(bt->func_name), &(bt->line)))
|
||||
{
|
||||
bt->file_dir = strdup("");
|
||||
bt->file_name = strdup("");
|
||||
bt->func_name = strdup("");
|
||||
}
|
||||
}
|
||||
btl = eina_list_append(btl, bt);
|
||||
}
|
||||
free(bin);
|
||||
return btl;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
Eina_List *btl = NULL, *l;
|
||||
char buf[4096];
|
||||
Bt *bt;
|
||||
int cols[6] = { 0 }, len, i;
|
||||
|
||||
eina_init();
|
||||
while (fgets(buf, sizeof(buf) - 1, stdin))
|
||||
{
|
||||
btl = bt_append(btl, buf);
|
||||
}
|
||||
EINA_LIST_FOREACH(btl, l, bt)
|
||||
{
|
||||
len = strlen(bt->bin_dir);
|
||||
if (len > cols[0]) cols[0] = len;
|
||||
len = strlen(bt->bin_name);
|
||||
if (len > cols[1]) cols[1] = len;
|
||||
|
||||
len = strlen(bt->file_dir);
|
||||
if (len > cols[2]) cols[2] = len;
|
||||
len = strlen(bt->file_name);
|
||||
if (len > cols[3]) cols[3] = len;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%i", bt->line);
|
||||
len = strlen(buf);
|
||||
if (len > cols[4]) cols[4] = len;
|
||||
|
||||
len = strlen(bt->func_name);
|
||||
if (len > cols[5]) cols[5] = len;
|
||||
}
|
||||
EINA_LIST_FOREACH(btl, l, bt)
|
||||
{
|
||||
len = strlen(bt->bin_dir);
|
||||
for (i = 0; i < (cols[0] - len); i++) printf(" ");
|
||||
printf("\033[34m%s\033[01m\033[36m/\033[37m%s\033[0m",
|
||||
bt->bin_dir, bt->bin_name);
|
||||
len = strlen(bt->bin_name);
|
||||
for (i = 0; i < (cols[1] - len); i++) printf(" ");
|
||||
printf(" | ");
|
||||
len = strlen(bt->file_dir);
|
||||
for (i = 0; i < (cols[2] - len); i++) printf(" ");
|
||||
printf("\033[34m%s\033[01m\033[36m/\033[37m%s\033[0m",
|
||||
bt->file_dir, bt->file_name);
|
||||
len = strlen(bt->file_name);
|
||||
for (i = 0; i < (cols[3] - len); i++) printf(" ");
|
||||
|
||||
printf(" : ");
|
||||
snprintf(buf, sizeof(buf), "%i", bt->line);
|
||||
len = strlen(buf);
|
||||
for (i = 0; i < (cols[4] - len); i++) printf(" ");
|
||||
printf("\033[01m\033[33m%s\033[0m @ \033[32m%s\033[36m()", buf, bt->func_name);
|
||||
printf("\033[0m\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
#include "eina_debug.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
extern pthread_t _eina_debug_thread_mainloop;
|
||||
extern pthread_t *_eina_debug_thread_active;
|
||||
extern int _eina_debug_thread_active_num;
|
||||
|
||||
|
||||
// yes - a global debug spinlock. i expect contention to be low for now, and
|
||||
// when needed we can split this up into mroe locks to reduce contention when
|
||||
// and if that day comes
|
||||
Eina_Spinlock _eina_debug_lock;
|
||||
|
||||
static Eina_Bool _inited = EINA_FALSE;
|
||||
|
||||
Eina_Bool
|
||||
eina_debug_init(void)
|
||||
{
|
||||
pthread_t self;
|
||||
|
||||
if (_inited)
|
||||
{
|
||||
eina_spinlock_release(&_eina_debug_thread_lock);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
_inited = EINA_TRUE;
|
||||
eina_spinlock_new(&_eina_debug_lock);
|
||||
eina_spinlock_new(&_eina_debug_thread_lock);
|
||||
eina_semaphore_new(&_eina_debug_monitor_return_sem, 0);
|
||||
self = pthread_self();
|
||||
_eina_debug_thread_mainloop_set(&self);
|
||||
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
|
||||
// if we are setuid - don't debug!
|
||||
if (getuid() != geteuid()) return EINA_TRUE;
|
||||
#endif
|
||||
if (getenv("EFL_NODEBUG")) return EINA_TRUE;
|
||||
_eina_debug_monitor_service_connect();
|
||||
if (_eina_debug_monitor_service_fd >= 0)
|
||||
{
|
||||
_eina_debug_monitor_service_greet();
|
||||
_eina_debug_monitor_signal_init();
|
||||
_eina_debug_monitor_thread_start();
|
||||
}
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
eina_debug_shutdown(void)
|
||||
{
|
||||
eina_spinlock_take(&_eina_debug_thread_lock);
|
||||
// yes - we never free on shutdown - this is because the monitor thread
|
||||
// never exits. this is not a leak - we intend to never free up any
|
||||
// resources here because they are allocated once only ever.
|
||||
return EINA_TRUE;
|
||||
}
|
||||
#else
|
||||
Eina_Bool
|
||||
eina_debug_init(void)
|
||||
{
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
eina_debug_shutdown(void)
|
||||
{
|
||||
return EINA_TRUE;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,86 @@
|
|||
#ifndef EINA_DEBUG_H_
|
||||
# define EINA_DEBUG_H_
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# include <stdio.h>
|
||||
# include <string.h>
|
||||
# include <stdlib.h>
|
||||
# include <unistd.h>
|
||||
# if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && defined(HAVE_DLADDR) && defined(HAVE_UNWIND)
|
||||
# include <execinfo.h>
|
||||
# ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
# endif
|
||||
# include <errno.h>
|
||||
# include <stdio.h>
|
||||
# include <string.h>
|
||||
# include <unistd.h>
|
||||
# include <dlfcn.h>
|
||||
# include <sys/select.h>
|
||||
# include <sys/time.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# include <pthread.h>
|
||||
# include <signal.h>
|
||||
# include <time.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/un.h>
|
||||
# include <fcntl.h>
|
||||
# include <libunwind.h>
|
||||
|
||||
# include "eina_config.h"
|
||||
# include "eina_private.h"
|
||||
# include "eina_inlist.h"
|
||||
# include "eina_lock.h"
|
||||
# include "eina_thread.h"
|
||||
# include "eina_convert.h"
|
||||
# include "eina_strbuf.h"
|
||||
# include "eina_safety_checks.h"
|
||||
# include "eina_log.h"
|
||||
# include "eina_inline_private.h"
|
||||
|
||||
# define EINA_HAVE_DEBUG 1
|
||||
|
||||
# define EINA_MAX_BT 256
|
||||
|
||||
extern Eina_Spinlock _eina_debug_lock;
|
||||
extern Eina_Spinlock _eina_debug_thread_lock;
|
||||
extern Eina_Semaphore _eina_debug_monitor_return_sem;
|
||||
extern int _eina_debug_monitor_service_fd;
|
||||
|
||||
void _eina_debug_thread_add(void *th);
|
||||
void _eina_debug_thread_del(void *th);
|
||||
void _eina_debug_thread_mainloop_set(void *th);
|
||||
|
||||
void *_eina_debug_chunk_push(int size);
|
||||
void *_eina_debug_chunk_realloc(int size);
|
||||
char *_eina_debug_chunk_strdup(const char *str);
|
||||
void *_eina_debug_chunk_tmp_push(int size);
|
||||
void _eina_debug_chunk_tmp_reset(void);
|
||||
|
||||
const char *_eina_debug_file_get(const char *fname);
|
||||
|
||||
void _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen);
|
||||
|
||||
void _eina_debug_monitor_thread_start(void);
|
||||
void _eina_debug_monitor_signal_init(void);
|
||||
void _eina_debug_monitor_service_connect(void);
|
||||
|
||||
void _eina_debug_monitor_service_greet(void);
|
||||
|
||||
# define EINA_BT(file) \
|
||||
do { \
|
||||
void *bt[EINA_MAX_BT]; \
|
||||
int btlen = backtrace((void **)bt, EINA_MAX_BT); \
|
||||
_eina_debug_dump_fhandle_bt(file, bt, btlen); \
|
||||
} while (0)
|
||||
# else
|
||||
# define EINA_BT(file) do { } while (0)
|
||||
# endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
#include "eina_debug.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
void
|
||||
_eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
|
||||
{
|
||||
int i;
|
||||
Dl_info info;
|
||||
const char *file;
|
||||
unsigned long long offset, base;
|
||||
|
||||
for (i = 0; i < btlen; i++)
|
||||
{
|
||||
file = NULL;
|
||||
offset = base = 0;
|
||||
// we have little choice but to hgope/assume dladdr() doesn't alloc
|
||||
// anything here
|
||||
if ((dladdr(bt[i], &info)) && (info.dli_fname[0]))
|
||||
{
|
||||
offset = (unsigned long long)bt[i];
|
||||
base = (unsigned long long)info.dli_fbase;
|
||||
file = _eina_debug_file_get(info.dli_fname);
|
||||
}
|
||||
// rely on normal libc buffering for file ops to avoid syscalls.
|
||||
// may or may not be a good idea. good enough for now.
|
||||
if (file) fprintf(f, "%s\t 0x%llx 0x%llx\n", file, offset, base);
|
||||
else fprintf(f, "??\t -\n");
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,145 @@
|
|||
#include "eina_debug.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
static unsigned int _table_num = 0;
|
||||
static unsigned int _table_size = 0;
|
||||
static const char **_table = NULL;
|
||||
|
||||
// a very simple "fast lookup" of a filename to a path. we expect this table
|
||||
// of lookups to remain very small as it most likely includes just the
|
||||
// application executable itself as this was run from $PATH or using
|
||||
// a relative path relative to cwd of shell at time of exec. this is really
|
||||
// the only likely content, but just in case, handle more. it is much faster
|
||||
// than going through systemcalls like realpath() every time (well this libc
|
||||
// function will at least be a system call or use system calls to do its work)
|
||||
static const char *
|
||||
_eina_debug_file_lookup(const char *fname)
|
||||
{
|
||||
unsigned int n;
|
||||
|
||||
if (!_table) return NULL;
|
||||
for (n = 0; _table[n]; n += 2)
|
||||
{
|
||||
if (!strcmp(_table[n], fname)) return _table[n + 1];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// record a new filename -> path entry in our table. the table really is just
|
||||
// odd/even strings like fname, path, fname2, path2, fnamr3, path3, ...
|
||||
// and we are unlikely to have much more than 1 entry here. see above
|
||||
static const char *
|
||||
_eina_debug_file_store(const char *fname, const char *file)
|
||||
{
|
||||
static const char **table2;
|
||||
|
||||
_table_num += 2;
|
||||
if (_table_num >= _table_size)
|
||||
{
|
||||
_table_size += 32;
|
||||
table2 = _eina_debug_chunk_realloc(_table_size * sizeof(const char *));
|
||||
if (!table2) return NULL;
|
||||
_table = table2;
|
||||
}
|
||||
_table[_table_num - 2] = _eina_debug_chunk_strdup(fname);
|
||||
_table[_table_num - 1] = _eina_debug_chunk_strdup(file);
|
||||
return _table[_table_num - 1];
|
||||
}
|
||||
|
||||
// do a "fast lookup" of a filename to a file path for debug output. this
|
||||
// relies on caching to avoid system calls and assumes that once we know
|
||||
// the full path of a given filename, we can know its full path reliably.
|
||||
// if we can't we'd be in trouble anyway as the filename and path lookup
|
||||
// failure is due maybe to a deleted file or renamed file and then we are
|
||||
// going to have a bad day either way.
|
||||
const char *
|
||||
_eina_debug_file_get(const char *fname)
|
||||
{
|
||||
char buf[4096];
|
||||
const char *file;
|
||||
static const char **path = NULL;
|
||||
static char *pathstrs = NULL;
|
||||
|
||||
// no filename provided
|
||||
if ((!fname) || (!fname[0])) return NULL;
|
||||
// it's a full path so return as-is
|
||||
if (fname[0] == '/') return fname;
|
||||
// first look in cache for filename -> full path lookup and if
|
||||
// there, return that (yes - assuming filesystem paths doesn't change
|
||||
// which is unlikely as they were set up at star most likely)
|
||||
eina_spinlock_take(&_eina_debug_lock);
|
||||
file = _eina_debug_file_lookup(fname);
|
||||
eina_spinlock_release(&_eina_debug_lock);
|
||||
if (file) return file;
|
||||
|
||||
// store PATH permanently - yes. if it changes runtime it will break.
|
||||
// for speed reasons we need to assume it won't change. store path broken
|
||||
// down into an array of ptrs to strings with NULL ptr at the end. this
|
||||
// will only execute once as an "init" for a breoken up path so it should
|
||||
// not matter speed-wise
|
||||
eina_spinlock_take(&_eina_debug_lock);
|
||||
if (!path)
|
||||
{
|
||||
unsigned int n;
|
||||
char *p1, *p2;
|
||||
const char *p;
|
||||
const char *pathstr = getenv("PATH");
|
||||
|
||||
if (!pathstr) return NULL;
|
||||
// dup the entire env as we will rpelace : with 0 bytes to break str
|
||||
pathstrs = _eina_debug_chunk_strdup(pathstr);
|
||||
for (n = 0, p = pathstr; *p;)
|
||||
{
|
||||
n++;
|
||||
p = strchr(p, ':');
|
||||
if (!p) break;
|
||||
p++;
|
||||
}
|
||||
path = _eina_debug_chunk_push(sizeof(const char *) * (n + 1));
|
||||
for (n = 0, p1 = pathstrs; *p1; n++)
|
||||
{
|
||||
path[n] = p1;
|
||||
p2 = strchr(p1, ':');
|
||||
if (!p2) break;
|
||||
*p2 = 0;
|
||||
p1 = p2 + 1;
|
||||
}
|
||||
path[n] = NULL;
|
||||
}
|
||||
eina_spinlock_release(&_eina_debug_lock);
|
||||
|
||||
// a relative path - resolve with realpath. due to the cache store above
|
||||
// we shouldn't have to do this very often
|
||||
if ((!strncmp(fname, "./", 2)) || (!strncmp(fname, "../", 3)))
|
||||
{ // relative path
|
||||
if (realpath(fname, buf)) file = buf;
|
||||
else file = NULL;
|
||||
}
|
||||
// search in $PATH for the file then - this should also be very rare as
|
||||
// we will store and cache results permanently
|
||||
else if (path)
|
||||
{
|
||||
struct stat st;
|
||||
unsigned int n;
|
||||
|
||||
for (n = 0; path[n]; n++)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s/%s", path[n], fname);
|
||||
if (stat(buf, &st) == 0)
|
||||
{
|
||||
file = buf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if it's found - store it in cache for later
|
||||
if (file)
|
||||
{
|
||||
eina_spinlock_take(&_eina_debug_lock);
|
||||
file = _eina_debug_file_store(fname, file);
|
||||
eina_spinlock_release(&_eina_debug_lock);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,228 @@
|
|||
#include "eina_debug.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
# ifdef HAVE_MMAP
|
||||
# include <sys/mman.h>
|
||||
# endif
|
||||
|
||||
// custom memory allocators to avoid malloc/free during backtrace handling
|
||||
// just in case we're inside some signal handler due to mem corruption and
|
||||
// are inside a malloc/free lock and thus would deadlock ourselves if we
|
||||
// allocated memory, so implement scratch space just big enough for what we
|
||||
// need and then some via either a static 8k+4k buffer pair or via a growable
|
||||
// mmaped mem chunk pair
|
||||
# ifdef HAVE_MMAP
|
||||
// implement using mmap so we can grow if needed - unlikelt though
|
||||
static unsigned char *chunk1 = NULL;
|
||||
static unsigned char *chunk2 = NULL;
|
||||
static unsigned char *chunk3 = NULL;
|
||||
static int chunk1_size = 0;
|
||||
static int chunk1_num = 0;
|
||||
static int chunk2_size = 0;
|
||||
static int chunk2_num = 0;
|
||||
static int chunk3_size = 0;
|
||||
static int chunk3_num = 0;
|
||||
|
||||
// get a new chunk of "anonymous mmaped memory"
|
||||
static void *
|
||||
_eina_debug_chunk_need(int size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
if (ptr == MAP_FAILED) return NULL;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// release a chunk of this mmaped anon mem if we don't need it anymore
|
||||
static void
|
||||
_eina_debug_chunk_noneed(void *ptr, int size)
|
||||
{
|
||||
munmap(ptr, size);
|
||||
}
|
||||
|
||||
// push a new bit of mem on our growing stack of mem - given our workload,
|
||||
// we never free anything here, only ever grow new things on this stack
|
||||
void *
|
||||
_eina_debug_chunk_push(int size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
// no initial chunk1 block - allocate it
|
||||
if (!chunk1)
|
||||
{
|
||||
chunk1 = _eina_debug_chunk_need(8 * 1024);
|
||||
if (!chunk1) return NULL;
|
||||
chunk1_size = 8 * 1024;
|
||||
}
|
||||
// round size up to the nearest pointer size for alignment
|
||||
size = sizeof(void *) * ((size + sizeof(void *) - 1) / sizeof(void *));
|
||||
// if our chunk is too small - grow it
|
||||
if ((chunk1_num + size) > chunk1_size)
|
||||
{
|
||||
// get a new chunk twice as big
|
||||
void *newchunk = _eina_debug_chunk_need(chunk1_size * 2);
|
||||
if (!newchunk) return NULL;
|
||||
// copy content over
|
||||
memcpy(newchunk, chunk1, chunk1_num);
|
||||
// release old chunk
|
||||
_eina_debug_chunk_noneed(chunk1, chunk1_size);
|
||||
// switch to our new 2x as big chunk
|
||||
chunk1 = newchunk;
|
||||
chunk1_size = chunk1_size * 2;
|
||||
}
|
||||
// get the mem at the top of this stack and return it, then move along
|
||||
ptr = chunk1 + chunk1_num;
|
||||
chunk1_num += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// grow a single existing chunk (we use this for the filename -> path lookup)
|
||||
void *
|
||||
_eina_debug_chunk_realloc(int size)
|
||||
{
|
||||
// we have a null/empty second chunk - allocate one
|
||||
if (!chunk2)
|
||||
{
|
||||
chunk2 = _eina_debug_chunk_need(4 * 1024);
|
||||
if (!chunk2) return NULL;
|
||||
chunk2_size = 4 * 1024;
|
||||
}
|
||||
// if our chunk is too small - grow it
|
||||
if (size > chunk2_size)
|
||||
{
|
||||
// get a new chunk twice as big
|
||||
void *newchunk = _eina_debug_chunk_need(chunk2_size * 2);
|
||||
if (!newchunk) return NULL;
|
||||
// copy content over
|
||||
memcpy(newchunk, chunk2, chunk2_num);
|
||||
// release old chunk
|
||||
_eina_debug_chunk_noneed(chunk2, chunk2_size);
|
||||
// switch to our new 2x as big chunk
|
||||
chunk2 = newchunk;
|
||||
chunk2_size = chunk2_size * 2;
|
||||
}
|
||||
// record new size and return chunk ptr as we just re-use it
|
||||
chunk2_num = size;
|
||||
return chunk2;
|
||||
}
|
||||
|
||||
// grow a single existing chunk (we use this for the filename -> path lookup)
|
||||
void *
|
||||
_eina_debug_chunk_tmp_push(int size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
// no initial chunk1 block - allocate it
|
||||
if (!chunk3)
|
||||
{
|
||||
chunk3 = _eina_debug_chunk_need(32 * 1024);
|
||||
if (!chunk3) return NULL;
|
||||
chunk3_size = 32 * 1024;
|
||||
}
|
||||
// round size up to the nearest pointer size for alignment
|
||||
size = sizeof(void *) * ((size + sizeof(void *) - 1) / sizeof(void *));
|
||||
// if our chunk is too small - grow it
|
||||
if ((chunk3_num + size) > chunk3_size)
|
||||
{
|
||||
// get a new chunk twice as big
|
||||
void *newchunk = _eina_debug_chunk_need(chunk3_size * 2);
|
||||
if (!newchunk) return NULL;
|
||||
// copy content over
|
||||
memcpy(newchunk, chunk3, chunk3_num);
|
||||
// release old chunk
|
||||
_eina_debug_chunk_noneed(chunk3, chunk3_size);
|
||||
// switch to our new 2x as big chunk
|
||||
chunk3 = newchunk;
|
||||
chunk3_size = chunk3_size * 2;
|
||||
}
|
||||
// get the mem at the top of this stack and return it, then move along
|
||||
ptr = chunk3 + chunk3_num;
|
||||
chunk3_num += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void
|
||||
_eina_debug_chunk_tmp_reset(void)
|
||||
{
|
||||
chunk3_num = 0;
|
||||
}
|
||||
# else
|
||||
// implement with static buffers - once we exceed these we will fail. sorry
|
||||
// maybe one day find another solution, but these buffers should be enough
|
||||
// for now for thos eplatforms (like windows) where we can't do the mmap
|
||||
// tricks above.
|
||||
static unsigned char chunk1[8 * 1024];
|
||||
static unsigned char chunk2[4 * 1024];
|
||||
static unsigned char chunk3[128 * 1024];
|
||||
static int chunk1_size = sizeof(chunk1);
|
||||
static int chunk1_num = 0;
|
||||
static int chunk2_size = sizeof(chunk2);
|
||||
static int chunk2_num = 0;
|
||||
static int chunk3_size = sizeof(chunk3);
|
||||
static int chunk3_num = 0;
|
||||
|
||||
// push a new bit of mem on our growing stack of mem - given our workload,
|
||||
// we never free anything here, only ever grow new things on this stack
|
||||
void *
|
||||
_eina_debug_chunk_push(int size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
// round size up to the nearest pointer size for alignment
|
||||
size = sizeof(void *) * ((size + sizeof(void *) - 1) / sizeof(void *));
|
||||
// if we ran out of space - fail
|
||||
if ((chunk1_num + size) > chunk1_size) return NULL;
|
||||
// get the mem at the top of this stack and return it, then move along
|
||||
ptr = chunk1 + chunk1_num;
|
||||
chunk1_num += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// grow a single existing chunk (we use this for the filename -> path lookup)
|
||||
void *
|
||||
_eina_debug_chunk_realloc(int size)
|
||||
{
|
||||
// if we ran out of space - fail
|
||||
if (size > chunk2_size) return NULL;
|
||||
// record new size and return chunk ptr as we just re-use it
|
||||
chunk2_num = size;
|
||||
return chunk2;
|
||||
}
|
||||
|
||||
// grow a single existing chunk (we use this for the filename -> path lookup)
|
||||
void *
|
||||
_eina_debug_chunk_tmp_push(int size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
// round size up to the nearest pointer size for alignment
|
||||
size = sizeof(void *) * ((size + sizeof(void *) - 1) / sizeof(void *));
|
||||
// if we ran out of space - fail
|
||||
if ((chunk3_num + size) > chunk3_size) return NULL;
|
||||
// get the mem at the top of this stack and return it, then move along
|
||||
ptr = chunk3 + chunk1_num;
|
||||
chunk3_num += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void
|
||||
_eina_debug_chunk_tmp_reset(void)
|
||||
{
|
||||
chunk3_num = 0;
|
||||
}
|
||||
# endif
|
||||
|
||||
// handy - duplicate a string on our growing stack - never expect to free it
|
||||
char *
|
||||
_eina_debug_chunk_strdup(const char *str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
char *s = _eina_debug_chunk_push(len + 1);
|
||||
if (!s) return NULL;
|
||||
strcpy(s, str);
|
||||
return s;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,320 @@
|
|||
#include "eina_debug.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
#define DEBUG_SERVER ".ecore/efl_debug/0"
|
||||
|
||||
extern pthread_t _eina_debug_thread_mainloop;
|
||||
extern volatile pthread_t *_eina_debug_thread_active;
|
||||
extern volatile int _eina_debug_thread_active_num;
|
||||
|
||||
int _eina_debug_monitor_service_fd = -1;
|
||||
Eina_Semaphore _eina_debug_monitor_return_sem;
|
||||
|
||||
static Eina_Bool _monitor_thread_runs = EINA_FALSE;
|
||||
static pthread_t _monitor_thread;
|
||||
|
||||
// _bt_buf[0] is always for mainloop, 1 + is for extra threads
|
||||
static void ***_bt_buf;
|
||||
static int *_bt_buf_len;
|
||||
static struct timespec *_bt_ts;
|
||||
static int *_bt_cpu;
|
||||
|
||||
static inline int
|
||||
_eina_debug_unwind_bt(void **bt, int max)
|
||||
{
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t uc;
|
||||
unw_word_t p;
|
||||
int total;
|
||||
|
||||
unw_getcontext(&uc);
|
||||
unw_init_local(&cursor, &uc);
|
||||
for (total = 0; (unw_step(&cursor) > 0) && (total < max); total++)
|
||||
{
|
||||
unw_get_reg(&cursor, UNW_REG_IP, &p);
|
||||
bt[total] = (void *)p;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static void
|
||||
_eina_debug_signal(int sig EINA_UNUSED,
|
||||
siginfo_t *si EINA_UNUSED,
|
||||
void *foo EINA_UNUSED)
|
||||
{
|
||||
int i, slot = 0;
|
||||
pthread_t self = pthread_self();
|
||||
clockid_t cid;
|
||||
// XXX: use pthread_getcpuclockid() to get cpu time used since last poll
|
||||
//
|
||||
// clockid_t cid;
|
||||
// struct timespec ts ts;
|
||||
// pthread_getcpuclockid(pthread_self(), &cid);
|
||||
// clock_gettime(cid, &ts);
|
||||
// printf("%4ld.%03ld\n", ts.tv_sec, ts.tv_nsec / 1000000);
|
||||
//
|
||||
// also get current cpu with:
|
||||
// getcpu()
|
||||
if (self != _eina_debug_thread_mainloop)
|
||||
{
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
if (self == _eina_debug_thread_active[i])
|
||||
{
|
||||
slot = i + 1;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "EINA DEBUG ERROR: can't find thread slot!\n");
|
||||
eina_semaphore_release(&_eina_debug_monitor_return_sem, 1);
|
||||
return;
|
||||
}
|
||||
found:
|
||||
// printf("dump into slot %i for %p\n", slot, (void *)self);
|
||||
_bt_cpu[slot] = sched_getcpu();
|
||||
pthread_getcpuclockid(self, &cid);
|
||||
clock_gettime(cid, &(_bt_ts[slot]));
|
||||
// _bt_buf_len[slot] = backtrace(_bt_buf[slot], EINA_MAX_BT);
|
||||
_bt_buf_len[slot] = _eina_debug_unwind_bt(_bt_buf[slot], EINA_MAX_BT);
|
||||
eina_semaphore_release(&_eina_debug_monitor_return_sem, 1);
|
||||
}
|
||||
|
||||
#define SIG SIGPROF
|
||||
//#define SIG ((SIGRTMIN + SIGRTMAX) / 2)
|
||||
|
||||
static inline double
|
||||
get_time(void)
|
||||
{
|
||||
struct timeval timev;
|
||||
gettimeofday(&timev, NULL);
|
||||
return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
|
||||
}
|
||||
|
||||
static void
|
||||
_eina_debug_collect_bt(pthread_t pth)
|
||||
{
|
||||
// this async signals the thread to switch to the deebug signal handler
|
||||
// and collect a backtrace and other info from inside the thread
|
||||
pthread_kill(pth, SIG);
|
||||
}
|
||||
|
||||
// this is a DEDICATED debug thread to monitor the application so it works
|
||||
// even if the mainloop is blocked or the app otherwise deadlocked in some
|
||||
// way. this is an alternative to using external debuggers so we can get
|
||||
// users or developers to get useful information about an app at all times
|
||||
static void *
|
||||
_eina_debug_monitor(void *data EINA_UNUSED)
|
||||
{
|
||||
int bts = 0, ret, max_fd;
|
||||
double t0, t;
|
||||
fd_set rfds, wfds, exfds;
|
||||
struct timeval tv = { 0 };
|
||||
unsigned int poll_time = 1000;
|
||||
Eina_Bool poll_on = EINA_FALSE;
|
||||
|
||||
t0 = get_time();
|
||||
for (;;)
|
||||
{
|
||||
int i;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
FD_ZERO(&exfds);
|
||||
FD_SET(_eina_debug_monitor_service_fd, &rfds);
|
||||
max_fd = _eina_debug_monitor_service_fd;
|
||||
if (poll_on)
|
||||
{
|
||||
if ((tv.tv_sec == 0) && (tv.tv_usec == 0))
|
||||
{
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = poll_time;
|
||||
}
|
||||
ret = select(max_fd + 1, &rfds, &wfds, &exfds, &tv);
|
||||
}
|
||||
else ret = select(max_fd + 1, &rfds, &wfds, &exfds, NULL);
|
||||
if ((ret == 1) && (FD_ISSET(_eina_debug_monitor_service_fd, &rfds)))
|
||||
{
|
||||
unsigned int size;
|
||||
int rret;
|
||||
|
||||
// XXX: handle protocol
|
||||
rret = read(_eina_debug_monitor_service_fd, &size, 4);
|
||||
if ((rret == 4) && (size > 0) && (size < 63356))
|
||||
{
|
||||
char *buf = alloca(size);
|
||||
|
||||
rret = read(_eina_debug_monitor_service_fd, buf, size);
|
||||
if ((rret == (int)size) && (size >= 4))
|
||||
{
|
||||
if (!strncmp(buf, "PLON", 4))
|
||||
{
|
||||
if (size >= 8) memcpy(&poll_time, buf + 4, 4);
|
||||
poll_on = EINA_TRUE;
|
||||
}
|
||||
else if (!strncmp(buf, "PLOF", 4))
|
||||
{
|
||||
poll_time = 1000;
|
||||
poll_on = EINA_FALSE;
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "EINA DEBUG ERROR: Uunknown command\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rret <= 0)
|
||||
{
|
||||
fprintf(stderr, "EINA DEBUG ERROR: Lost debug daemon!\n");
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rret <= 0)
|
||||
{
|
||||
fprintf(stderr, "EINA_DEBUG ERROR: Lost debug daemon!\n");
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "EINA DEBUG ERROR: Invalid message size %i\n", size);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (poll_on)
|
||||
{
|
||||
// take a lock on grabbing thread debug info like backtraces
|
||||
eina_spinlock_take(&_eina_debug_thread_lock);
|
||||
// reset our "stack" of memory se use to dump thread info into
|
||||
_eina_debug_chunk_tmp_reset();
|
||||
// get an array of pointers for the backtrace array for main + th
|
||||
_bt_buf = _eina_debug_chunk_tmp_push
|
||||
((1 + _eina_debug_thread_active_num) * sizeof(void *));
|
||||
if (!_bt_buf) goto err;
|
||||
// get an array of pointers for the timespec array for mainloop + th
|
||||
_bt_ts = _eina_debug_chunk_tmp_push
|
||||
((1 + _eina_debug_thread_active_num) * sizeof(struct timespec));
|
||||
if (!_bt_ts) goto err;
|
||||
// get an array of pointers for the cpuid array for mainloop + th
|
||||
_bt_cpu = _eina_debug_chunk_tmp_push
|
||||
((1 + _eina_debug_thread_active_num) * sizeof(int));
|
||||
if (!_bt_cpu) goto err;
|
||||
// now get an array of void pts for mainloop bt
|
||||
_bt_buf[0] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *));
|
||||
if (!_bt_buf[0]) goto err;
|
||||
// get an array of void ptrs for each thread we know about for bt
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
_bt_buf[i + 1] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *));
|
||||
if (!_bt_buf[i + 1]) goto err;
|
||||
}
|
||||
// get an array of ints to stor the bt len for mainloop + threads
|
||||
_bt_buf_len = _eina_debug_chunk_tmp_push
|
||||
((1 + _eina_debug_thread_active_num) * sizeof(int));
|
||||
// collect bt from the mainloop - always there
|
||||
_eina_debug_collect_bt(_eina_debug_thread_mainloop);
|
||||
// now collect per thread
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
_eina_debug_collect_bt(_eina_debug_thread_active[i]);
|
||||
// we're done probing. now collec all the "i'm done" msgs on the
|
||||
// semaphore for every thread + mainloop
|
||||
for (i = 0; i < (_eina_debug_thread_active_num + 1); i++)
|
||||
eina_semaphore_lock(&_eina_debug_monitor_return_sem);
|
||||
// we now have gotten all the data from all threadd + mainloop.
|
||||
// we can process it now as we see fit, so release thread lock
|
||||
// for (i = 0; i < (_eina_debug_thread_active_num + 1); i++)
|
||||
// {
|
||||
// _eina_debug_dump_fhandle_bt(stderr, _bt_buf[i], _bt_buf_len[i]);
|
||||
// }
|
||||
err:
|
||||
eina_spinlock_release(&_eina_debug_thread_lock);
|
||||
bts++;
|
||||
if (bts >= 10000)
|
||||
{
|
||||
t = get_time();
|
||||
fprintf(stderr, "%1.5f bt's per sec\n", (double)bts / (t - t0));
|
||||
t0 = t;
|
||||
bts = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
fail:
|
||||
close(_eina_debug_monitor_service_fd);
|
||||
_eina_debug_monitor_service_fd = -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// start up the debug monitor if we haven't already
|
||||
void
|
||||
_eina_debug_monitor_thread_start(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (_monitor_thread_runs) return;
|
||||
// XXX: set up socket conn to debug daemon and then have thread deal with
|
||||
// it from there on
|
||||
err = pthread_create(&_monitor_thread, NULL, _eina_debug_monitor, NULL);
|
||||
if (err != 0)
|
||||
{
|
||||
fprintf(stderr, "EINA DEBUG ERROR: Can't create debug thread!\n");
|
||||
abort();
|
||||
}
|
||||
else _monitor_thread_runs = EINA_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
_eina_debug_monitor_signal_init(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_sigaction = _eina_debug_signal;
|
||||
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
if (sigaction(SIG, &sa, NULL) != 0)
|
||||
fprintf(stderr, "EINA DEBUG ERROR: Can't set up sig %i handler!\n", SIG);
|
||||
}
|
||||
|
||||
static const char *
|
||||
_socket_home_get()
|
||||
{
|
||||
const char *dir = getenv("XDG_RUNTIME_DIR");
|
||||
if (!dir) dir = getenv("HOME");
|
||||
if (!dir) dir = getenv("TMPDIR");
|
||||
if (!dir) dir = "/tmp";
|
||||
return dir;
|
||||
}
|
||||
|
||||
void
|
||||
_eina_debug_monitor_service_connect(void)
|
||||
{
|
||||
char buf[4096];
|
||||
int fd, socket_unix_len, curstate = 0;
|
||||
struct sockaddr_un socket_unix;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/%s", _socket_home_get(), DEBUG_SERVER);
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) goto err;
|
||||
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto err;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate,
|
||||
sizeof(curstate)) < 0)
|
||||
goto err;
|
||||
socket_unix.sun_family = AF_UNIX;
|
||||
strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path));
|
||||
#define LENGTH_OF_SOCKADDR_UN(s) \
|
||||
(strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
|
||||
socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
|
||||
if (connect(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
|
||||
goto err;
|
||||
_eina_debug_monitor_service_fd = fd;
|
||||
return;
|
||||
err:
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,19 @@
|
|||
#include "eina_debug.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
void
|
||||
_eina_debug_monitor_service_greet(void)
|
||||
{
|
||||
const char *hello = "HELO";
|
||||
unsigned int version = 1;
|
||||
unsigned int msize = 4 + 4 + 4;
|
||||
unsigned int pid = getpid();
|
||||
unsigned char buf[16];
|
||||
memcpy(buf + 0, &msize, 4);
|
||||
memcpy(buf + 4, hello, 4);
|
||||
memcpy(buf + 8, &version, 4);
|
||||
memcpy(buf + 12, &pid, 4);
|
||||
write(_eina_debug_monitor_service_fd, buf, sizeof(buf));
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,80 @@
|
|||
#include "eina_debug.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
// a really simple store of currently known active threads. the mainloop is
|
||||
// special and inittied at debug init time - assuming eina inits in the
|
||||
// mainloop thread (whihc is expected). also a growable array of thread
|
||||
// id's for other threads is held here so we can loop over them and do things
|
||||
// like get them to stop and dump a backtrace for us
|
||||
Eina_Spinlock _eina_debug_thread_lock;
|
||||
|
||||
pthread_t _eina_debug_thread_mainloop = 0;
|
||||
pthread_t *_eina_debug_thread_active = NULL;
|
||||
int _eina_debug_thread_active_num = 0;
|
||||
|
||||
static int _thread_active_size = 0;
|
||||
|
||||
// add a thread id to our tracking array - very simple. add to end, and
|
||||
// if array to small, reallocate it to be bigger by 16 slots AND double that
|
||||
// size (so grows should slow down FAST). we will never shrink this array
|
||||
void
|
||||
_eina_debug_thread_add(void *th)
|
||||
{
|
||||
pthread_t *pth = th;
|
||||
// take thread tracking lock
|
||||
eina_spinlock_take(&_eina_debug_thread_lock);
|
||||
// if we don't have enough space to store thread id's - make some more
|
||||
if (_thread_active_size < (_eina_debug_thread_active_num + 1))
|
||||
{
|
||||
pthread_t *threads = realloc
|
||||
(_eina_debug_thread_active,
|
||||
((_eina_debug_thread_active_num + 16) * 2) * sizeof(pthread_t *));
|
||||
if (threads)
|
||||
{
|
||||
_eina_debug_thread_active = threads;
|
||||
_thread_active_size = (_eina_debug_thread_active_num + 16) * 2;
|
||||
}
|
||||
}
|
||||
// add new thread id to the end
|
||||
_eina_debug_thread_active[_eina_debug_thread_active_num] = *pth;
|
||||
_eina_debug_thread_active_num++;
|
||||
// release our lock cleanly
|
||||
eina_spinlock_release(&_eina_debug_thread_lock);
|
||||
}
|
||||
|
||||
// remove a thread id from our tracking array - simply find and shuffle all
|
||||
// later elements down. this array should be small almsot all the time and
|
||||
// shouldn't bew changing THAT often for this to matter
|
||||
void
|
||||
_eina_debug_thread_del(void *th)
|
||||
{
|
||||
pthread_t *pth = th;
|
||||
int i;
|
||||
// take a thread tracking lock
|
||||
eina_spinlock_take(&_eina_debug_thread_lock);
|
||||
// find the thread id to remove
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
if (_eina_debug_thread_active[i] == *pth)
|
||||
{
|
||||
// found it - now shuffle down all further thread id's in array
|
||||
for (; i < (_eina_debug_thread_active_num - 1); i++)
|
||||
_eina_debug_thread_active[i] = _eina_debug_thread_active[i + 1];
|
||||
// reduce our counter and get out of loop
|
||||
_eina_debug_thread_active_num--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// release lock cleanly
|
||||
eina_spinlock_release(&_eina_debug_thread_lock);
|
||||
}
|
||||
|
||||
// register the thread that is the mainloop - always there
|
||||
void
|
||||
_eina_debug_thread_mainloop_set(void *th)
|
||||
{
|
||||
pthread_t *pth = th;
|
||||
_eina_debug_thread_mainloop = *pth;
|
||||
}
|
||||
#endif
|
|
@ -29,11 +29,6 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined HAVE_EXECINFO_H && defined HAVE_BACKTRACE && defined HAVE_BACKTRACE_SYMBOLS
|
||||
# include <execinfo.h>
|
||||
# define EINA_LOG_BACKTRACE
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
# include <systemd/sd-journal.h>
|
||||
#endif
|
||||
|
@ -42,6 +37,11 @@
|
|||
# include <Evil.h>
|
||||
#endif
|
||||
|
||||
#include "eina_debug.h"
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
# define EINA_LOG_BACKTRACE
|
||||
#endif
|
||||
|
||||
#include "eina_config.h"
|
||||
#include "eina_private.h"
|
||||
#include "eina_inlist.h"
|
||||
|
@ -121,7 +121,7 @@ static Eina_Bool _disable_timing = EINA_TRUE;
|
|||
static int _abort_level_on_critical = EINA_LOG_LEVEL_CRITICAL;
|
||||
|
||||
#ifdef EINA_LOG_BACKTRACE
|
||||
static int _backtrace_level = -1;
|
||||
static int _backtrace_level = 999;
|
||||
#endif
|
||||
|
||||
static Eina_Bool _threads_enabled = EINA_FALSE;
|
||||
|
@ -1849,21 +1849,11 @@ eina_log_domain_registered_level_set(int domain, int level)
|
|||
}
|
||||
|
||||
#ifdef EINA_LOG_BACKTRACE
|
||||
# define DISPLAY_BACKTRACE(File, Level) \
|
||||
if (EINA_UNLIKELY(Level < _backtrace_level)) \
|
||||
{ \
|
||||
void *bt[256]; \
|
||||
char **strings; \
|
||||
int btlen; \
|
||||
int i; \
|
||||
\
|
||||
btlen = backtrace((void **)bt, 256); \
|
||||
strings = backtrace_symbols((void **)bt, btlen); \
|
||||
fprintf(File, "*** Backtrace ***\n"); \
|
||||
for (i = 0; i < btlen; ++i) \
|
||||
fprintf(File, "%s\n", strings[i]); \
|
||||
free(strings); \
|
||||
}
|
||||
# define DISPLAY_BACKTRACE(File, Level) \
|
||||
if (EINA_UNLIKELY(Level < _backtrace_level)) { \
|
||||
fprintf(File, "*** Backtrace ***\n"); \
|
||||
EINA_BT(File); \
|
||||
}
|
||||
#else
|
||||
# define DISPLAY_BACKTRACE(File, Level)
|
||||
#endif
|
||||
|
|
|
@ -120,6 +120,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL;
|
|||
*/
|
||||
#define S(x) extern Eina_Bool eina_ ## x ## _init(void); \
|
||||
extern Eina_Bool eina_ ## x ## _shutdown(void)
|
||||
S(debug);
|
||||
S(log);
|
||||
S(error);
|
||||
S(safety_checks);
|
||||
|
@ -165,6 +166,7 @@ struct eina_desc_setup
|
|||
static const struct eina_desc_setup _eina_desc_setup[] = {
|
||||
#define S(x) {# x, eina_ ## x ## _init, eina_ ## x ## _shutdown}
|
||||
/* log is a special case as it needs printf */
|
||||
S(debug),
|
||||
S(stringshare),
|
||||
S(error),
|
||||
S(safety_checks),
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
|
||||
#include "eina_safety_checks.h"
|
||||
|
||||
#include "eina_debug.h"
|
||||
|
||||
# include <pthread.h>
|
||||
# include <errno.h>
|
||||
|
||||
|
@ -100,13 +102,20 @@ _eina_internal_call(void *context)
|
|||
{
|
||||
Eina_Thread_Call *c = context;
|
||||
void *r;
|
||||
pthread_t self;
|
||||
|
||||
if (c->prio == EINA_THREAD_BACKGROUND ||
|
||||
c->prio == EINA_THREAD_IDLE)
|
||||
eina_sched_prio_drop();
|
||||
|
||||
/* FIXME: set priority and affinity */
|
||||
self = pthread_self();
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
_eina_debug_thread_add(&self);
|
||||
#endif
|
||||
r = c->func((void*) c->data, eina_thread_self());
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
_eina_debug_thread_del(&self);
|
||||
#endif
|
||||
|
||||
free(c);
|
||||
|
||||
|
|
Loading…
Reference in New Issue