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:
Carsten Haitzler 2015-05-05 11:35:16 +09:00
parent 38faeacee1
commit 664708b817
19 changed files with 1648 additions and 24 deletions

View File

@ -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})"

View File

@ -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@

View File

@ -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

2
src/bin/efl/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
efl_debugd
efl_debug

179
src/bin/efl/efl_debug.c Normal file
View File

@ -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();
}

219
src/bin/efl/efl_debugd.c Normal file
View File

@ -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();
}

1
src/bin/eina/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
eina_btlog

198
src/bin/eina/eina_btlog.c Normal file
View File

@ -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;
}

69
src/lib/eina/eina_debug.c Normal file
View File

@ -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

86
src/lib/eina/eina_debug.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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),

View File

@ -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);