raster pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=664708b817ab0cdc7177df3743b5d9c9ab7dd2b0

commit 664708b817ab0cdc7177df3743b5d9c9ab7dd2b0
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
Date:   Tue May 5 11:35:16 2015 +0900

    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
---
 configure.ac                      |   8 +-
 src/Makefile_Efl.am               |  17 ++
 src/Makefile_Eina.am              |  25 ++-
 src/bin/efl/.gitignore            |   2 +
 src/bin/efl/efl_debug.c           | 179 +++++++++++++++++++++
 src/bin/efl/efl_debugd.c          | 219 ++++++++++++++++++++++++++
 src/bin/eina/.gitignore           |   1 +
 src/bin/eina/eina_btlog.c         | 198 +++++++++++++++++++++++
 src/lib/eina/eina_debug.c         |  69 ++++++++
 src/lib/eina/eina_debug.h         |  86 ++++++++++
 src/lib/eina/eina_debug_bt.c      |  31 ++++
 src/lib/eina/eina_debug_bt_file.c | 145 +++++++++++++++++
 src/lib/eina/eina_debug_chunk.c   | 228 +++++++++++++++++++++++++++
 src/lib/eina/eina_debug_monitor.c | 320 ++++++++++++++++++++++++++++++++++++++
 src/lib/eina/eina_debug_proto.c   |  19 +++
 src/lib/eina/eina_debug_thread.c  |  80 ++++++++++
 src/lib/eina/eina_log.c           |  32 ++--
 src/lib/eina/eina_main.c          |   2 +
 src/lib/eina/eina_thread.c        |  11 +-
 19 files changed, 1648 insertions(+), 24 deletions(-)

diff --git a/configure.ac b/configure.ac
index 63cc54d..ab76615 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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})"
diff --git a/src/Makefile_Efl.am b/src/Makefile_Efl.am
index cf6310a..b80a46c 100644
--- a/src/Makefile_Efl.am
+++ b/src/Makefile_Efl.am
@@ -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@
+
diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am
index 3591b10..a92b758 100644
--- a/src/Makefile_Eina.am
+++ b/src/Makefile_Eina.am
@@ -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
diff --git a/src/bin/efl/.gitignore b/src/bin/efl/.gitignore
new file mode 100644
index 0000000..b38a2ee
--- /dev/null
+++ b/src/bin/efl/.gitignore
@@ -0,0 +1,2 @@
+efl_debugd
+efl_debug
diff --git a/src/bin/efl/efl_debug.c b/src/bin/efl/efl_debug.c
new file mode 100644
index 0000000..b1cd730
--- /dev/null
+++ b/src/bin/efl/efl_debug.c
@@ -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();
+}
diff --git a/src/bin/efl/efl_debugd.c b/src/bin/efl/efl_debugd.c
new file mode 100644
index 0000000..7f369ac
--- /dev/null
+++ b/src/bin/efl/efl_debugd.c
@@ -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();
+}
diff --git a/src/bin/eina/.gitignore b/src/bin/eina/.gitignore
new file mode 100644
index 0000000..ec4ebaa
--- /dev/null
+++ b/src/bin/eina/.gitignore
@@ -0,0 +1 @@
+eina_btlog
diff --git a/src/bin/eina/eina_btlog.c b/src/bin/eina/eina_btlog.c
new file mode 100644
index 0000000..2b4d530
--- /dev/null
+++ b/src/bin/eina/eina_btlog.c
@@ -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;
+}
diff --git a/src/lib/eina/eina_debug.c b/src/lib/eina/eina_debug.c
new file mode 100644
index 0000000..766ec7a
--- /dev/null
+++ b/src/lib/eina/eina_debug.c
@@ -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
diff --git a/src/lib/eina/eina_debug.h b/src/lib/eina/eina_debug.h
new file mode 100644
index 0000000..f4494af
--- /dev/null
+++ b/src/lib/eina/eina_debug.h
@@ -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
diff --git a/src/lib/eina/eina_debug_bt.c b/src/lib/eina/eina_debug_bt.c
new file mode 100644
index 0000000..7f1beb1
--- /dev/null
+++ b/src/lib/eina/eina_debug_bt.c
@@ -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
diff --git a/src/lib/eina/eina_debug_bt_file.c 
b/src/lib/eina/eina_debug_bt_file.c
new file mode 100644
index 0000000..9ed5a7c
--- /dev/null
+++ b/src/lib/eina/eina_debug_bt_file.c
@@ -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
diff --git a/src/lib/eina/eina_debug_chunk.c b/src/lib/eina/eina_debug_chunk.c
new file mode 100644
index 0000000..3351ce1
--- /dev/null
+++ b/src/lib/eina/eina_debug_chunk.c
@@ -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
diff --git a/src/lib/eina/eina_debug_monitor.c 
b/src/lib/eina/eina_debug_monitor.c
new file mode 100644
index 0000000..74833f3
--- /dev/null
+++ b/src/lib/eina/eina_debug_monitor.c
@@ -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
diff --git a/src/lib/eina/eina_debug_proto.c b/src/lib/eina/eina_debug_proto.c
new file mode 100644
index 0000000..44ab536
--- /dev/null
+++ b/src/lib/eina/eina_debug_proto.c
@@ -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
diff --git a/src/lib/eina/eina_debug_thread.c b/src/lib/eina/eina_debug_thread.c
new file mode 100644
index 0000000..e3c8d7a
--- /dev/null
+++ b/src/lib/eina/eina_debug_thread.c
@@ -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
diff --git a/src/lib/eina/eina_log.c b/src/lib/eina/eina_log.c
index c3174ff..5f458f0 100644
--- a/src/lib/eina/eina_log.c
+++ b/src/lib/eina/eina_log.c
@@ -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
diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c
index ea85b30..358c3a4 100644
--- a/src/lib/eina/eina_main.c
+++ b/src/lib/eina/eina_main.c
@@ -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),
diff --git a/src/lib/eina/eina_thread.c b/src/lib/eina/eina_thread.c
index 2924fc7..86cb8a9 100644
--- a/src/lib/eina/eina_thread.c
+++ b/src/lib/eina/eina_thread.c
@@ -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);
 

-- 


Reply via email to