First patch of the Eina Debug layer rewriting
authorDaniel Zaoui <daniel.zaoui@yahoo.com>
Sun, 27 Nov 2016 05:48:10 +0000 (07:48 +0200)
committerDaniel Zaoui <daniel.zaoui@yahoo.com>
Mon, 5 Jun 2017 05:51:49 +0000 (08:51 +0300)
Eina Debug is a new layer aimed for EFL debugging. It offers scalability
by allowing registration of operations not specific to EFL core.

The protocol is simple and the APIs try to provide as much
functionalities/freedom as possible.

20 files changed:
src/Makefile_Efl.am
src/Makefile_Eina.am
src/bin/efl/efl_debug.c
src/bin/efl/efl_debug_common.c [deleted file]
src/bin/efl/efl_debug_common.h [deleted file]
src/bin/efl/efl_debugd.c
src/lib/eina/Eina.h
src/lib/eina/eina_debug.c
src/lib/eina/eina_debug.h
src/lib/eina/eina_debug_bt.c
src/lib/eina/eina_debug_bt_file.c
src/lib/eina/eina_debug_chunk.c
src/lib/eina/eina_debug_cpu.c [new file with mode: 0644]
src/lib/eina/eina_debug_monitor.c [deleted file]
src/lib/eina/eina_debug_private.h [new file with mode: 0644]
src/lib/eina/eina_debug_proto.c [deleted file]
src/lib/eina/eina_debug_thread.c
src/lib/eina/eina_evlog.c
src/lib/eina/eina_log.c
src/lib/eina/eina_thread.c

index 554e31a..6fda23e 100644 (file)
@@ -166,18 +166,12 @@ bin_PROGRAMS += \
 bin/efl/efl_debugd \
 bin/efl/efl_debug
 
-bin_efl_efl_debugd_SOURCES = \
-bin/efl/efl_debugd.c \
-bin/efl/efl_debug_common.c \
-bin/efl/efl_debug_common.h
+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 = @EFL_LIBS@ @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_common.c \
-bin/efl/efl_debug_common.h
+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 = @EFL_LIBS@ @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@
index b99d286..60981fb 100644 (file)
@@ -12,6 +12,7 @@ installed_einaheadersdir = $(includedir)/eina-@VMAJ@/eina
 dist_installed_einaheaders_DATA = \
 lib/eina/eina_safety_checks.h \
 lib/eina/eina_error.h \
+lib/eina/eina_debug.h \
 lib/eina/eina_log.h \
 lib/eina/eina_inline_log.x \
 lib/eina/eina_fp.h \
@@ -123,9 +124,8 @@ lib/eina/eina_debug.c \
 lib/eina/eina_debug_bt.c \
 lib/eina/eina_debug_bt_file.c \
 lib/eina/eina_debug_chunk.c \
-lib/eina/eina_debug_monitor.c \
-lib/eina/eina_debug_proto.c \
 lib/eina/eina_debug_thread.c \
+lib/eina/eina_debug_cpu.c \
 lib/eina/eina_error.c \
 lib/eina/eina_evlog.c \
 lib/eina/eina_file_common.h \
@@ -171,7 +171,6 @@ lib/eina/eina_util.c \
 lib/eina/eina_value.c \
 lib/eina/eina_value_util.c \
 lib/eina/eina_xattr.c \
-lib/eina/eina_debug.h \
 lib/eina/eina_private.h \
 lib/eina/eina_share_common.h \
 lib/eina/eina_strbuf_common.h \
index 28cdb39..7876a08 100644 (file)
  * if not, see <http://www.gnu.org/licenses/>.
  */
 
-#define DECLARE_OPS
-#include "efl_debug_common.h"
+#include <Eina.h>
+#include <Ecore.h>
 
-static Eo *dialer;
+# ifdef HAVE_CONFIG_H
+#  include "config.h"
+# endif
 
-static Eina_List *waiting;
+#define EXTRACT(_buf, pval, sz) \
+{ \
+   memcpy(pval, _buf, sz); \
+   _buf += sz; \
+}
+#define _EVLOG_INTERVAL 0.2
 
-static int retval = EXIT_SUCCESS;
+static int               _evlog_max_times = 0;
+static Ecore_Timer      *_evlog_fetch_timer = NULL;
+static FILE             *_evlog_file = NULL;
 
-static void
-_process_reply(void *data EINA_UNUSED, const char op[static 4], const Eina_Slice payload)
-{
-   if (IS_OP(CLST))
-     {
-        int mypid = getpid();
-        size_t offset;
+static int _cl_stat_reg_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _cid_from_pid_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _prof_on_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _prof_off_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _cpufreq_on_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _cpufreq_off_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _evlog_get_opcode = EINA_DEBUG_OPCODE_INVALID;
 
-        waiting = eina_list_remove(waiting, OP_CLST);
+static Eina_Debug_Session *_session = NULL;
 
-        for (offset = 0; offset + sizeof(int) <= payload.len; offset += sizeof(int))
-          {
-             int p;
+static int _cid = 0;
 
-             memcpy(&p, payload.bytes + offset, sizeof(int));
+static int my_argc = 0;
+static char **my_argv = NULL;
 
-             if (p == mypid) continue;
-             if (p > 0) printf("%i\n", p);
+static Eina_Debug_Error
+_evlog_get_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
+{
+   static int received_times = 0;
+   unsigned char *d = buffer;
+   unsigned int *overflow = (unsigned int *)(d + 0);
+   unsigned char *p = d + 4;
+   unsigned int blocksize = size - 4;
+
+   if(++received_times <= _evlog_max_times)
+     {
+        if ((_evlog_file) && (blocksize > 0))
+          {
+             unsigned int header[3];
+
+             header[0] = 0xffee211;
+             header[1] = blocksize;
+             header[2] = *overflow;
+             if (fwrite(header, 1, 12, _evlog_file) < 12 ||
+                   fwrite(p, 1, blocksize, _evlog_file) < blocksize)
+                printf("Error writing bytes to evlog file\n");
           }
      }
-   else
+
+   if(received_times == _evlog_max_times)
      {
-        fprintf(stderr, "ERROR: unexpected server reply: %.4s\n", op);
-        retval = EXIT_FAILURE;
+        printf("Received last evlog response\n");
+        if (_evlog_file) fclose(_evlog_file);
+        _evlog_file = NULL;
+        ecore_main_loop_quit();
      }
 
-   if (!waiting) ecore_main_loop_quit();
+   return EINA_DEBUG_OK;
 }
 
-static void
-_on_data(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
+static Eina_Bool
+_cb_evlog(void *data EINA_UNUSED)
 {
-   if (!received_data(dialer, _process_reply, NULL))
+   static int sent_times = 0;
+   Eina_Bool ret = ECORE_CALLBACK_RENEW;
+   if(++sent_times <= _evlog_max_times)
+         eina_debug_session_send(_session, _cid, _evlog_get_opcode, NULL, 0);
+
+   if(sent_times == _evlog_max_times)
      {
-        retval = EXIT_FAILURE;
-        ecore_main_loop_quit();
+        eina_debug_session_send(_session, _cid, _cpufreq_off_opcode, NULL, 0);
+        ecore_timer_del(_evlog_fetch_timer);
+        _evlog_fetch_timer = NULL;
+        ret = ECORE_CALLBACK_CANCEL;
      }
+
+   return ret;
 }
 
-static Eina_Bool
-_command_send(const char op[static 4], const void *data, unsigned int len)
+static Eina_Debug_Error
+_cid_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer, int size EINA_UNUSED)
 {
-   if (!send_data(dialer, op, data, len))
+   _cid = *(int *)buffer;
+
+   const char *op_str = my_argv[1];
+   Eina_Bool quit = EINA_TRUE;
+
+   if ((!strcmp(op_str, "pon")) && (3 <= (my_argc - 1)))
+     {
+        int freq = atoi(my_argv[3]);
+        eina_debug_session_send(_session, _cid, _prof_on_opcode, &freq, sizeof(int));
+     }
+   else if (!strcmp(op_str, "poff"))
+      eina_debug_session_send(_session, _cid, _prof_off_opcode,  NULL, 0);
+   else if (!strcmp(op_str, "evlogon") && (3 <= (my_argc - 1)))
      {
-        retval = EXIT_FAILURE;
-        return EINA_FALSE;
+        double max_time;
+        sscanf(my_argv[3], "%lf", &max_time);
+        _evlog_max_times = max_time > 0 ? (max_time/_EVLOG_INTERVAL+1) : 1;
+        eina_debug_session_send(_session, 0, _cl_stat_reg_opcode, NULL, 0);
+        printf("Evlog request will be sent %d times\n", _evlog_max_times);
+        eina_debug_session_send(_session, _cid, _cpufreq_on_opcode,  NULL, 0);
+
+        /* Creating the evlog file and setting the timer */
+        char path[4096];
+        int pid = atoi(my_argv[2]);
+        snprintf(path, sizeof(path), "%s/efl_debug_evlog-%ld.log",
+              getenv("HOME"), (long)pid);
+        _evlog_file = fopen(path, "wb");
+        _evlog_fetch_timer = ecore_timer_add(_EVLOG_INTERVAL, _cb_evlog, NULL);
+
+        quit = EINA_FALSE;
      }
+   else if (!strcmp(op_str, "evlogoff"))
+        eina_debug_session_send(_session, _cid, _cpufreq_off_opcode,  NULL, 0);
 
-   return EINA_TRUE;
-}
+   if(quit)
+        ecore_main_loop_quit();
 
-#define command_send(op, data, len) _command_send(OP_ ## op, data, len)
+   return EINA_DEBUG_OK;
+}
 
-static void
-_write_finished(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
+static Eina_Debug_Error
+_clients_info_added_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
 {
-   if (!waiting) ecore_main_loop_quit();
+   char *buf = buffer;
+   while(size)
+     {
+        int cid, pid, len;
+        EXTRACT(buf, &cid, sizeof(int));
+        EXTRACT(buf, &pid, sizeof(int));
+        /* We dont need client notifications on evlog */
+        if(!_evlog_fetch_timer)
+           printf("Added: CID: %d - PID: %d - Name: %s\n", cid, pid, buf);
+        len = strlen(buf) + 1;
+        buf += len;
+        size -= (2 * sizeof(int) + len);
+     }
+   return EINA_DEBUG_OK;
 }
 
-static void
-_finished(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
+static Eina_Debug_Error
+_clients_info_deleted_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
 {
-   ecore_main_loop_quit();
+   char *buf = buffer;
+   while(size)
+     {
+        int cid;
+        EXTRACT(buf, &cid, sizeof(int));
+        size -= sizeof(int);
+
+        /* If client deleted dont send anymore evlog requests */
+        if(_evlog_fetch_timer)
+          {
+             if(_cid == cid)
+               {
+                  printf("Evlog debugged App closed (CID: %d), stopping evlog\n", cid);
+                  ecore_timer_del(_evlog_fetch_timer);
+                  _evlog_fetch_timer = NULL;
+                  fclose(_evlog_file);
+                  _evlog_file = NULL;
+                  ecore_main_loop_quit();
+               }
+          }
+        else
+           printf("Deleted: CID: %d\n", cid);
+     }
+   return EINA_DEBUG_OK;
 }
 
 static void
-_error(void *data EINA_UNUSED, const Efl_Event *event)
+_ecore_thread_dispatcher(void *data)
 {
-   Eina_Error *perr = event->info;
+   eina_debug_dispatch(_session, data);
+}
 
-   fprintf(stderr, "ERROR: error communicating to %s: %s\n",
-           efl_net_dialer_address_dial_get(dialer),
-           eina_error_msg_get(*perr));
-   retval = EXIT_FAILURE;
-   ecore_main_loop_quit();
+Eina_Debug_Error
+_disp_cb(Eina_Debug_Session *session EINA_UNUSED, void *buffer)
+{
+   ecore_main_loop_thread_safe_call_async(_ecore_thread_dispatcher, buffer);
+   return EINA_DEBUG_OK;
 }
 
-int
-main(int argc, char **argv)
+static void
+_args_handle(Eina_Bool flag)
 {
-   Eo *loop;
-   char *path;
-   Eina_Error err;
-   int i;
+   if (!flag) exit(0);
+   eina_debug_session_dispatch_override(_session, _disp_cb);;
 
-   if (argc < 2)
+   const char *op_str = my_argv[1];
+   if (op_str && !strcmp(op_str, "list"))
      {
-        fprintf(stderr, "ERROR: missing argument.\n");
-        return EXIT_FAILURE;
+        eina_debug_session_send(_session, 0, _cl_stat_reg_opcode, NULL, 0);
      }
-   for (i = 1; i < argc; i++)
+   else if (2 <= my_argc - 1)
      {
-        if ((strcmp(argv[i], "-h") != 0) &&
-            (strcmp(argv[i], "--help") != 0))
-          continue;
-
-        printf("Usage:\n"
-               "\n"
-               "\t%s <command> [arguments]\n"
-               "\n"
-               "where <command> is one of:\n"
-               "\tlist               list connected process (pid)\n"
-               "\tpon <pid> <freq>   enable profiling for <pid> at frequency <freq> in microseconds.\n"
-               "\tpoff <pid>         disable profiling for <pid>\n"
-               "\tevlogon <pid>      start logging events to ~/efl_debug_evlog-<pid>.log\n"
-               "\tevlogoff <pid>     stop logging events from <pid>\n",
-               argv[0]);
-
-        return EXIT_SUCCESS;
+        int pid = atoi(my_argv[2]);
+        eina_debug_session_send(_session, 0, _cid_from_pid_opcode, &pid, sizeof(int));
      }
+}
 
-   ecore_app_no_system_modules();
+static const Eina_Debug_Opcode ops[] =
+{
+     {"daemon/observer/client/register", &_cl_stat_reg_opcode,   NULL},
+     {"daemon/observer/slave_added",   NULL,                  &_clients_info_added_cb},
+     {"daemon/observer/slave_deleted", NULL,                  &_clients_info_deleted_cb},
+     {"daemon/info/cid_from_pid",      &_cid_from_pid_opcode,  &_cid_get_cb},
+     {"profiler/on",                   &_prof_on_opcode,       NULL},
+     {"profiler/off",                  &_prof_off_opcode,      NULL},
+     {"cpufreq/on",                    &_cpufreq_on_opcode,      NULL},
+     {"cpufreq/off",                   &_cpufreq_off_opcode,     NULL},
+     {"evlog/get",                     &_evlog_get_opcode,     _evlog_get_cb},
+     {NULL, NULL, NULL}
+};
 
+int
+main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
+{
    eina_init();
    ecore_init();
-   ecore_con_init();
-
-   path = ecore_con_local_path_new(EINA_FALSE, "efl_debug", 0);
-   if (!path)
-     {
-        fprintf(stderr, "ERROR: could not get local communication path\n");
-        retval = EXIT_FAILURE;
-        goto end;
-     }
 
-   loop = ecore_main_loop_get();
-
-#ifdef EFL_NET_DIALER_UNIX_CLASS
-   dialer = efl_add(EFL_NET_DIALER_SIMPLE_CLASS, loop,
-                    efl_net_dialer_simple_inner_class_set(efl_added, EFL_NET_DIALER_UNIX_CLASS));
-#elif defined(EFL_NET_DIALER_WINDOWS_CLASS)
-   dialer = efl_add(EFL_NET_DIALER_SIMPLE_CLASS, loop,
-                    efl_net_dialer_simple_inner_class_set(efl_added, EFL_NET_DIALER_WINDOWS_CLASS));
-#else
-   /* TODO: maybe start a TCP using locahost:12345?
-    * Right now eina_debug_monitor is only for AF_UNIX, so not an issue.
-    */
-   fprintf(stderr, "ERROR: your platform doesn't support Efl.Net.Dialer.*\n");
-#endif
-   if (!dialer)
-     {
-        fprintf(stderr, "ERROR: could not create communication dialer\n");
-        retval = EXIT_FAILURE;
-        goto end;
-     }
-   efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_ERROR, _error, NULL);
-   efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_SLICE_CHANGED, _on_data, NULL);
-   efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_WRITE_FINISHED, _write_finished, NULL);
-   efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_FINISHED, _finished, NULL);
-
-   for (i = 1; i < argc; i++)
-     {
-        const char *cmd = argv[i];
-
-        if (strcmp(cmd, "list") == 0)
-          {
-             if (!command_send(LIST, NULL, 0))
-               goto end;
-             waiting = eina_list_append(waiting, OP_CLST);
-          }
-        else if (strcmp(cmd, "pon") == 0)
-          {
-             if (i + 2 >= argc)
-               {
-                  fprintf(stderr, "ERROR: missing argument: pon <pid> <freq>\n");
-                  retval = EXIT_FAILURE;
-                  goto end;
-               }
-             else
-               {
-                  int data[2] = {atoi(argv[i + 1]), atoi(argv[1 + 2])};
-                  if (!command_send(PLON, data, sizeof(data)))
-                    goto end;
-                  i += 2;
-               }
-          }
-        else if (strcmp(cmd, "poff") == 0)
-          {
-             if (i + 1 >= argc)
-               {
-                  fprintf(stderr, "ERROR: missing argument: poff <pid>\n");
-                  retval = EXIT_FAILURE;
-                  goto end;
-               }
-             else
-               {
-                  int data[1] = {atoi(argv[i + 1])};
-                  if (!command_send(PLOF, data, sizeof(data)))
-                    goto end;
-                  i++;
-               }
-          }
-        else if (strcmp(cmd, "evlogon") == 0)
-          {
-             if (i + 1 >= argc)
-               {
-                  fprintf(stderr, "ERROR: missing argument: evlogon <pid>\n");
-                  retval = EXIT_FAILURE;
-                  goto end;
-               }
-             else
-               {
-                  int data[1] = {atoi(argv[i + 1])};
-                  if (!command_send(EVON, data, sizeof(data)))
-                    goto end;
-                  i++;
-               }
-          }
-        else if (strcmp(cmd, "evlogoff") == 0)
-          {
-             if (i + 1 >= argc)
-               {
-                  fprintf(stderr, "ERROR: missing argument: evlogoff <pid>\n");
-                  retval = EXIT_FAILURE;
-                  goto end;
-               }
-             else
-               {
-                  int data[1] = {atoi(argv[i + 1])};
-                  if (!command_send(EVOF, data, sizeof(data)))
-                    goto end;
-                  i++;
-               }
-          }
-        else
-          {
-             fprintf(stderr, "ERROR: unknown command: %s\n", argv[i]);
-             retval = EXIT_FAILURE;
-             goto end;
-          }
-     }
-   efl_io_buffered_stream_eos_mark(dialer);
+   my_argc = argc;
+   my_argv = argv;
 
-   err = efl_net_dialer_dial(dialer, path);
-   if (err)
+   _session = eina_debug_local_connect(EINA_TRUE);
+   if (!_session)
      {
-        fprintf(stderr, "ERROR: could not connect '%s': %s\n", path, eina_error_msg_get(err));
-        retval = EXIT_FAILURE;
-        goto end;
+        fprintf(stderr, "ERROR: Cannot connect to debug daemon.\n");
+        return -1;
      }
+   eina_debug_opcodes_register(_session, ops, _args_handle);
 
    ecore_main_loop_begin();
 
- end:
-   eina_list_free(waiting);
-   efl_del(dialer);
-   free(path);
-
-   ecore_con_shutdown();
    ecore_shutdown();
    eina_shutdown();
 
-   (void) OP_HELO;
-   (void) OP_EVLG;
-
-   return retval;
+   return 0;
 }
diff --git a/src/bin/efl/efl_debug_common.c b/src/bin/efl/efl_debug_common.c
deleted file mode 100644 (file)
index f37f17f..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/* EINA - EFL data type library
- * Copyright (C) 2015 Carsten Haitzler
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library;
- * if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "efl_debug_common.h"
-
-Eina_Bool
-received_data(Eo *sock, void (*handle)(void *data, const char op[static 4], const Eina_Slice payload), const void *data)
-{
-   Eina_Slice slice, payload;
-   Efl_Debug_Message_Header msgheader;
-
-   slice = efl_io_buffered_stream_slice_get(sock);
-   if (slice.len < sizeof(msgheader))
-     return EINA_TRUE;
-
-   memcpy(&msgheader, slice.mem, sizeof(msgheader));
-   if (msgheader.size < 4) /* must contain at last 4 byte opcode */
-     {
-        fprintf(stderr, "ERROR: invalid message header, size=%u\n", msgheader.size);
-        return EINA_FALSE;
-     }
-
-   if (msgheader.size + 4 > slice.len)
-     return EINA_TRUE;
-
-   payload.bytes = slice.bytes + sizeof(msgheader);
-   payload.len = msgheader.size - 4;
-
-   handle((void *)data, msgheader.op, payload);
-
-   efl_io_buffered_stream_discard(sock, sizeof(msgheader) + payload.len);
-   return EINA_TRUE;
-}
-
-Eina_Bool
-send_data(Eo *sock, const char op[static 4], const void *data, unsigned int len)
-{
-   Eina_Error err;
-   Efl_Debug_Message_Header msghdr = {
-     .size = 4 + len,
-   };
-   Eina_Slice s, r;
-
-   memcpy(msghdr.op, op, 4);
-
-   s.mem = &msghdr;
-   s.len = sizeof(msghdr);
-
-   err = efl_io_writer_write(sock, &s, &r);
-   if (err || r.len) goto end;
-
-   if (!len) goto end;
-
-   s.mem = data;
-   s.len = len;
-   err = efl_io_writer_write(sock, &s, &r);
-
- end:
-   if (err)
-     {
-        fprintf(stderr, "ERROR: could not queue message '%.4s': %s\n", op, eina_error_msg_get(err));
-        return EINA_FALSE;
-     }
-
-   if (r.len)
-     {
-        fprintf(stderr, "ERROR: could not queue message '%.4s': out of memory\n", op);
-        return EINA_FALSE;
-     }
-
-   return EINA_TRUE;
-}
diff --git a/src/bin/efl/efl_debug_common.h b/src/bin/efl/efl_debug_common.h
deleted file mode 100644 (file)
index 6a0ba0b..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/* EINA - EFL data type library
- * Copyright (C) 2015 Carsten Haitzler
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library;
- * if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef EFL_DEBUG_COMMON_H
-#define EFL_DEBUG_COMMON_H 1
-
-#define EFL_BETA_API_SUPPORT 1
-#define EFL_EO_API_SUPPORT 1
-
-#include <Eina.h>
-#include <Ecore.h>
-#include <Ecore_Con.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-typedef struct _Efl_Debug_Message_Header {
-   unsigned int size;
-   char op[4];
-} Efl_Debug_Message_Header;
-
-#define IS_OP(x) memcmp(op, OP_ ## x, 4) == 0
-
-#define DECLARE_OP(x) static char OP_ ## x[4] = #x
-#ifdef DECLARE_OPS
-DECLARE_OP(LIST);
-DECLARE_OP(CLST);
-DECLARE_OP(PLON);
-DECLARE_OP(PLOF);
-DECLARE_OP(EVON);
-DECLARE_OP(EVOF);
-DECLARE_OP(EVLG);
-DECLARE_OP(HELO);
-#endif
-
-Eina_Bool send_data(Eo *sock, const char op[static 4], const void *data, unsigned int len);
-Eina_Bool received_data(Eo *sock, void (*handle)(void *data, const char op[static 4], const Eina_Slice payload), const void *data);
-
-#endif
index dfe175c..9da2648 100644 (file)
  * if not, see <http://www.gnu.org/licenses/>.
  */
 
-#define DECLARE_OPS
-#include "efl_debug_common.h"
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+
+#include <Eina.h>
+#include <Ecore.h>
+
+#define STORE(_buf, pval, sz) \
+{ \
+   memcpy(_buf, pval, sz); \
+   _buf += sz; \
+}
+
+#define EXTRACT(_buf, pval, sz) \
+{ \
+   memcpy(pval, _buf, sz); \
+   _buf += sz; \
+}
 
 typedef struct _Client Client;
 
 struct _Client
 {
-   Eo *client;
-
-   unsigned char    *buf;
-   unsigned int      buf_size;
+   Eina_Stringshare *app_name;
 
    Ecore_Timer      *evlog_fetch_timer;
    int               evlog_on;
    FILE             *evlog_file;
 
    int               version;
+   int               fd;
+   int               cid;
    pid_t             pid;
+
+   Eina_Bool         cl_stat_obs : 1;
+   Eina_Bool         is_master : 1;
 };
 
-static Eo *server;
+static Eina_List *_clients = NULL;
 
-static Eina_List *clients = NULL;
+typedef Eina_Bool (*Opcode_Cb)(Client *client, void *buffer, int size);
 
-static int retval;
+static Eina_Hash *_string_to_opcode_hash = NULL;
 
-static int _log_dom = -1;
+static int _free_cid = 1;
 
-#ifdef ERR
-# undef ERR
-#endif
-#define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
+static int _clients_stat_register_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _slave_added_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _slave_deleted_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _cid_from_pid_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _test_loop_opcode = EINA_DEBUG_OPCODE_INVALID;
 
-#ifdef DBG
-# undef DBG
-#endif
-#define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
-
-#ifdef INF
-# undef INF
-#endif
-#define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
+typedef struct
+{
+   int opcode;
+   Eina_Stringshare *opcode_string;
+   Opcode_Cb cb;
+} Opcode_Information;
 
-#ifdef WRN
-# undef WRN
-#endif
-#define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
+#define MAX_OPCODES 1000
+Opcode_Information *_opcodes[MAX_OPCODES];
 
-#ifdef CRI
-# undef CRI
+/* epoll stuff */
+#ifndef _WIN32
+static int _epfd = -1, _listening_master_fd = -1, _listening_slave_fd = -1;
 #endif
-#define CRI(...) EINA_LOG_DOM_CRIT(_log_dom, __VA_ARGS__)
-
-
-#define send_cli(cl, op, data, len)                             \
-  do                                                            \
-    {                                                           \
-       if (!send_data(cl->client, OP_ ## op, data, len))        \
-         {                                                      \
-            if (!efl_io_closer_closed_get(cl->client))          \
-              efl_io_closer_close(cl->client);                  \
-         }                                                      \
-    }                                                           \
-  while (0)
 
 static Client *
-_client_pid_find(int pid)
+_client_find_by_cid(int cid)
 {
    Client *c;
    Eina_List *l;
+   EINA_LIST_FOREACH(_clients, l, c)
+      if (c->cid == cid) return c;
+   return NULL;
+}
 
-   if (pid <= 0) return NULL;
-   EINA_LIST_FOREACH(clients, l, c)
-     {
-        if (c->pid == pid) return c;
-     }
+static Client *
+_client_find_by_pid(int pid)
+{
+   Client *c;
+   Eina_List *l;
+   EINA_LIST_FOREACH(_clients, l, c)
+      if (c->pid == pid) return c;
+   return NULL;
+}
 
-   WRN("no client pid=%d", pid);
+static Client *
+_client_find_by_fd(int fd)
+{
+   Eina_List *itr;
+   Client *c;
+   EINA_LIST_FOREACH(_clients, itr, c)
+      if (c->fd == fd) return c;
    return NULL;
 }
 
-static Eina_Bool
-_cb_evlog(void *data)
+static int
+_send(Client *dest, int opcode, void *payload, int payload_size)
 {
-   Client *c = data;
-   send_cli(c, EVLG, NULL, 0);
-   return EINA_TRUE;
+   int size = sizeof(Eina_Debug_Packet_Header) + payload_size;
+   char *buf = alloca(size);
+   Eina_Debug_Packet_Header *hdr = (Eina_Debug_Packet_Header *)buf;
+   hdr->size = size;
+   hdr->cid = 0;
+   hdr->thread_id = 0;
+   hdr->opcode = opcode;
+   memcpy(buf + sizeof(Eina_Debug_Packet_Header), payload, payload_size);
+   //printf("%d bytes sent (opcode %s) to %s fd %d\n", size, _opcodes[opcode]->opcode_string, dest->app_name, dest->fd);
+   return send(dest->fd, buf, size, 0);
 }
 
 static void
-_process_command(void *data, const char op[static 4], const Eina_Slice payload)
+_client_del(Client *c)
 {
-   Client *c = data;
    Client *c2;
-   Eina_List *l;
-
-   DBG("client %p (%p) [pid:%d] op=%.4s payload=%zd", c, c->client, c->pid, op, payload.len);
+   if (!c) return;
+   Eina_List *itr;
 
-   if (IS_OP(HELO))
+   _clients = eina_list_remove(_clients, c);
+   if (c->evlog_fetch_timer)
      {
-        if (payload.len < sizeof(int) * 2)
-          {
-             fprintf(stderr, "INFO: client %p [pid: %d] sent invalid HELO\n", c, (int)c->pid);
-             if (!efl_io_closer_closed_get(c->client))
-               efl_io_closer_close(c->client);
-          }
-        else
-          {
-             memcpy(&c->version, payload.bytes, sizeof(int));
-             memcpy(&c->pid, payload.bytes + sizeof(int), sizeof(int));
-             INF("client %p (%p) HELO version=%d, pid=%d", c, c->client, c->version, c->pid);
-          }
+        ecore_timer_del(c->evlog_fetch_timer);
+        c->evlog_fetch_timer = NULL;
      }
-   else if (IS_OP(LIST))
+   if (c->evlog_file)
      {
-        int n = eina_list_count(clients);
-        unsigned int *pids = malloc(n * sizeof(int));
-        if (pids)
-          {
-             int i = 0;
-
-             EINA_LIST_FOREACH(clients, l, c2)
-               {
-                  if (c2->pid == 0) continue; /* no HELO yet */
-                  pids[i] = c2->pid;
-                  i++;
-               }
-             send_cli(c, CLST, pids, i * sizeof(int));
-             free(pids);
-          }
+        fclose(c->evlog_file);
+        c->evlog_file = NULL;
      }
-   else if (IS_OP(PLON))
+
+   /* Don't update the observers if the client is a master */
+   if (c->is_master) return;
+
+   EINA_LIST_FOREACH(_clients, itr, c2)
      {
-        if (payload.len < sizeof(int) * 2)
-          fprintf(stderr, "INFO: client %p [pid: %d] sent invalid PLON\n", c, (int)c->pid);
-        else
-          {
-             int pid;
-             unsigned int freq;
-             memcpy(&pid, payload.bytes, sizeof(int));
-             memcpy(&freq, payload.bytes + sizeof(int), sizeof(int));
-             c2 = _client_pid_find(pid);
-             if (!c2)
-               {
-                  fprintf(stderr, "INFO: client %p [pid: %d] sent PLON %d: no such client\n", c, (int)c->pid, pid);
-               }
-             else
-               {
-                  DBG("client %p (%p) [pid:%d] requested PLON on %p (%p) [pid:%d]",
-                      c, c->client, c->pid,
-                      c2, c2->client, c2->pid);
-                  send_cli(c2, PLON, &freq, sizeof(freq));
-               }
-          }
+        if (c2->cl_stat_obs) _send(c2, _slave_deleted_opcode, &c->cid, sizeof(int));
      }
-   else if (IS_OP(PLOF))
+   free(c);
+}
+
+static Eina_Bool
+_dispatch(Client *src, void *buffer, int size)
+{
+   Eina_Debug_Packet_Header *hdr = (Eina_Debug_Packet_Header *)buffer;
+   if (hdr->cid)
      {
-        if (payload.len < sizeof(int))
-          fprintf(stderr, "INFO: client %p [pid: %d] sent invalid PLOF\n", c, (int)c->pid);
-        else
+        /* If the client id is given, we forward */
+        Client *dest = _client_find_by_cid(hdr->cid);
+        if (dest)
           {
-             int pid;
-             memcpy(&pid, payload.bytes, sizeof(int));
-             c2 = _client_pid_find(pid);
-             if (!c2)
+             if (dest->is_master != src->is_master)
                {
-                  fprintf(stderr, "INFO: client %p [pid: %d] sent PLOF %d: no such client\n", c, (int)c->pid, pid);
+                  hdr->cid = src->cid;
+                  send(dest->fd, buffer, size, 0);
                }
              else
                {
-                  DBG("client %p (%p) [pid:%d] requested PLOF on %p (%p) [pid:%d]",
-                      c, c->client, c->pid,
-                      c2, c2->client, c2->pid);
-                  send_cli(c2, PLOF, NULL, 0);
+                  /*
+                   * Packets Master -> Master or Slave -> Slave are forbidden
+                   * Only Master <-> Slave packets are allowed.
+                   */
+                  printf("Packet from %d to %d: denied (same type)\n", hdr->cid, dest->cid);
                }
           }
      }
-   else if (IS_OP(EVON))
+   else
      {
-        if (payload.len < sizeof(int))
-          fprintf(stderr, "INFO: client %p [pid: %d] sent invalid EVON\n", c, (int)c->pid);
-        else
-          {
-             int pid;
-             memcpy(&pid, payload.bytes, sizeof(int));
-             c2 = _client_pid_find(pid);
-             if (!c2)
-               {
-                  fprintf(stderr, "INFO: client %p [pid: %d] sent EVON %d: no such client\n", c, (int)c->pid, pid);
-               }
-             else
-               {
-                  c2->evlog_on++;
-                  DBG("client %p (%p) [pid:%d] requested EVON (%d) on %p (%p) [pid:%d]",
-                      c, c->client, c->pid,
-                      c2->evlog_on,
-                      c2, c2->client, c2->pid);
-                  if (c2->evlog_on == 1)
-                    {
-                       char buf[4096];
-
-                       send_cli(c2, EVON, NULL, 0);
-                       c2->evlog_fetch_timer = ecore_timer_add(0.2, _cb_evlog, c2);
-                       snprintf(buf, sizeof(buf), "%s/efl_debug_evlog-%d.log",
-                                getenv("HOME"), c2->pid);
-                       c2->evlog_file = fopen(buf, "wb");
-                       DBG("client %p (%p) [pid:%d] logging to %s [%p]",
-                           c2, c2->client, c2->pid, buf, c2->evlog_file);
-                    }
-               }
-          }
+        printf("Invoke %s\n", _opcodes[hdr->opcode]->opcode_string);
+        if (_opcodes[hdr->opcode]->cb)
+           return _opcodes[hdr->opcode]->cb(src,
+                 (char *)buffer + sizeof(Eina_Debug_Packet_Header), size - sizeof(Eina_Debug_Packet_Header));
      }
-   else if (IS_OP(EVOF))
+   return EINA_TRUE;
+}
+
+static int
+_opcode_register(const char *op_name, int op_id, Opcode_Cb cb)
+{
+   static int free_opcode = 0;
+   Opcode_Information *op_info = eina_hash_find(_string_to_opcode_hash, op_name);
+   if (!op_info)
      {
-        if (payload.len < sizeof(int))
-          fprintf(stderr, "INFO: client %p [pid: %d] sent invalid EVOF\n", c, (int)c->pid);
-        else
+        op_info = calloc(1, sizeof(*op_info));
+        if (op_id == EINA_DEBUG_OPCODE_INVALID)
           {
-             int pid;
-             memcpy(&pid, payload.bytes, sizeof(int));
-             c2 = _client_pid_find(pid);
-             if (!c2)
+             do
                {
-                  fprintf(stderr, "INFO: client %p [pid: %d] sent EVOF %d: no such client\n", c, (int)c->pid, pid);
-               }
-             else
-               {
-                  c2->evlog_on--;
-                  DBG("client %p (%p) [pid:%d] requested EVOF (%d) on %p (%p) [pid:%d]",
-                      c, c->client, c->pid,
-                      c2->evlog_on,
-                      c2, c2->client, c2->pid);
-                  if (c2->evlog_on == 0)
-                    {
-                       send_cli(c2, EVOF, NULL, 0);
-                       if (c2->evlog_fetch_timer)
-                         {
-                            ecore_timer_del(c2->evlog_fetch_timer);
-                            c2->evlog_fetch_timer = NULL;
-                         }
-                       if (c2->evlog_file)
-                         {
-                            DBG("client %p (%p) [pid:%d] finished logged to %p",
-                                c2, c2->client, c2->pid, c2->evlog_file);
-                            fclose(c2->evlog_file);
-                            c2->evlog_file = NULL;
-                         }
-                    }
-                  else if (c2->evlog_on < 0)
-                    c2->evlog_on = 0;
+                  free_opcode = (free_opcode + 1) % MAX_OPCODES;
+                  op_id = free_opcode;
                }
+             while(_opcodes[op_id]);
           }
+        op_info->opcode = op_id;
+        op_info->opcode_string = eina_stringshare_add(op_name);
+        op_info->cb = cb;
+        eina_hash_add(_string_to_opcode_hash, op_name, op_info);
+        _opcodes[op_id] = op_info;
      }
-   else if (IS_OP(EVLG))
-     {
-        if (payload.len < sizeof(int))
-          fprintf(stderr, "INFO: client %p [pid: %d] sent invalid EVLG\n", c, (int)c->pid);
-        else if (!c->evlog_file)
-          fprintf(stderr, "INFO: client %p [pid: %d] no matching EVON\n", c, (int)c->pid);
-        else
-          {
-             unsigned int blocksize = payload.len - sizeof(int);
-             if (blocksize > 0)
-               {
-                  unsigned int header[3];
+   printf("Register %s -> opcode %d\n", op_name, op_info->opcode);
+   return op_info->opcode;
+}
 
-                  header[0] = 0xffee211;
-                  header[1] = blocksize;
-                  memcpy(header + 2, payload.mem, sizeof(int));
+static Eina_Bool
+_hello_cb(Client *c, void *buffer, int size)
+{
+   Eina_List *itr;
+   char *buf = (char *)buffer, *tmp;
 
-                  if ((fwrite(header, 12, 1, c->evlog_file) != 1) ||
-                      (fwrite(payload.bytes + sizeof(int), blocksize, 1, c->evlog_file) != 1))
-                    {
-                       fprintf(stderr, "INFO: failed to write log file for client %p [pid: %d]\n", c, (int)c->pid);
-                       fclose(c->evlog_file);
-                       c->evlog_file = NULL;
-                       c->evlog_on = 0;
+   EXTRACT(buf, &c->version, 4);
+   EXTRACT(buf, &c->pid, 4);
+   size -= 8;
 
-                       send_cli(c, EVOF, NULL, 0);
-                       if (c->evlog_fetch_timer)
-                         {
-                            ecore_timer_del(c->evlog_fetch_timer);
-                            c->evlog_fetch_timer = NULL;
-                         }
-                    }
-               }
-          }
+   c->cid = _free_cid++;
+   if (size > 1)
+     {
+        c->app_name = eina_stringshare_add_length(buf, size);
      }
-}
-
-static void
-_client_data(void *data, const Efl_Event *event)
-{
-   Client *c = data;
-   if (!received_data(event->object, _process_command, c))
+   printf("Connection from %s: pid %d - name %s\n",
+         c->is_master ? "Master" : "Slave",
+         c->pid, c->app_name);
+
+   if (c->is_master) return EINA_TRUE;
+
+   /* Update the observers */
+   size = 2 * sizeof(int) + (c->app_name ? strlen(c->app_name) : 0) + 1; /* cid + pid + name + \0 */
+   buf = alloca(size);
+   tmp = buf;
+   STORE(tmp, &c->cid, sizeof(int));
+   STORE(tmp, &c->pid, sizeof(int));
+   if (c->app_name)
+     {
+        STORE(tmp, c->app_name, strlen(c->app_name) + 1);
+     }
+   else
+     {
+        char end = '\0';
+        STORE(tmp, &end, 1);
+     }
+   EINA_LIST_FOREACH(_clients, itr, c)
      {
-        fprintf(stderr, "INFO: client %p [pid: %d] sent invalid data\n", c, (int)c->pid);
-        if (!efl_io_closer_closed_get(event->object))
-          efl_io_closer_close(event->object);
-        return;
+        if (c->cl_stat_obs) _send(c, _slave_added_opcode, buf, size);
      }
+   return EINA_TRUE;
 }
 
-static void
-_client_error(void *data, const Efl_Event *event)
+static Eina_Bool
+_cid_get_cb(Client *src, void *buffer, int size EINA_UNUSED)
 {
-   Client *c = data;
-   Eina_Error *perr = event->info;
-   WRN("client %p [pid: %d] error: %s",
-       c, (int)c->pid, eina_error_msg_get(*perr));
-   fprintf(stderr, "INFO: client %p [pid: %d] error: %s\n",
-           c, (int)c->pid, eina_error_msg_get(*perr));
+   int pid = *(int *)buffer;
+   Client *c = _client_find_by_pid(pid);
+   int cid = c ? c->cid : 0;
+   _send(src, _cid_from_pid_opcode, &cid, sizeof(int));
+   return EINA_TRUE;
 }
 
-static void
-_client_eos(void *data, const Efl_Event *event EINA_UNUSED)
+static Eina_Bool
+_data_test_cb(Client *src, void *buffer, int size)
 {
-   Client *c = data;
-   DBG("client %p (%p) [pid: %d] closed, pending read %zu, write %zu",
-       c, c->client, (int)c->pid,
-       efl_io_buffered_stream_pending_read_get(c->client),
-       efl_io_buffered_stream_pending_write_get(c->client));
-   efl_io_closer_close(c->client);
+   printf("Data test: loop packet of %d bytes\n", size);
+   _send(src, _test_loop_opcode, buffer, size);
+   return EINA_TRUE;
 }
 
-static void
-_client_write_finished(void *data, const Efl_Event *event EINA_UNUSED)
+static Eina_Bool
+_cl_stat_obs_register_cb(Client *src, void *buffer, int size)
 {
-   Client *c = data;
-   DBG("client %p (%p) [pid: %d] finished writing, pending read %zu",
-       c, c->client, (int)c->pid, efl_io_buffered_stream_pending_read_get(c->client));
+   Client *c;
+   if (!src) return EINA_FALSE;
+   if (!src->is_master) return EINA_FALSE;
+   if (!src->cl_stat_obs)
+     {
+        Eina_List *itr;
+        src->cl_stat_obs = EINA_TRUE;
+        size = 0;
+        EINA_LIST_FOREACH(_clients, itr, c)
+          {
+             char *tmp;
+             if (c->is_master) continue;
+             size = 2 * sizeof(int) + (c->app_name ? strlen(c->app_name) : 0) + 1;
+             buffer = alloca(size);
+             tmp = buffer;
+             STORE(tmp, &c->cid, sizeof(int));
+             STORE(tmp, &c->pid, sizeof(int));
+             if (c->app_name)
+               {
+                  STORE(tmp, c->app_name, strlen(c->app_name) + 1);
+               }
+             else
+               {
+                  char end = '\0';
+                  STORE(tmp, &end, 1);
+               }
+             _send(src, _slave_added_opcode, buffer, size);
+          }
+     }
+   return EINA_TRUE;
 }
 
-static void
-_client_read_finished(void *data, const Efl_Event *event EINA_UNUSED)
+static Eina_Bool
+_opcode_register_cb(Client *src, void *buffer, int size)
 {
-   Client *c = data;
-   DBG("client %p (%p) [pid: %d] finished reading, pending write %zu",
-       c, c->client, (int)c->pid, efl_io_buffered_stream_pending_write_get(c->client));
-}
+   char *buf = (char *)buffer;
+   char *ops_buf = buf;
+   int ops_size = size;
 
-static Efl_Callback_Array_Item *_client_cbs(void);
+   ops_buf += sizeof(uint64_t);
+   ops_size -= sizeof(uint64_t);
+   int *opcodes = (int *)ops_buf;
 
-static void
-_client_finished(void *data, const Efl_Event *event EINA_UNUSED)
-{
-   Client *c = data;
-
-   clients = eina_list_remove(clients, c);
-   if (c->evlog_fetch_timer)
+   while (ops_size > 0)
      {
-        ecore_timer_del(c->evlog_fetch_timer);
-        c->evlog_fetch_timer = NULL;
+        int len = strlen(ops_buf) + 1;
+        *opcodes++ = _opcode_register(ops_buf, EINA_DEBUG_OPCODE_INVALID, NULL);
+        ops_buf += len;
+        ops_size -= len;
      }
-   if (c->evlog_file)
-     {
-        fclose(c->evlog_file);
-        c->evlog_file = NULL;
-     }
-   efl_event_callback_array_del(c->client, _client_cbs(), c);
-   INF("finished client %p (%p) [pid:%d]", c, c->client, c->pid);
-   efl_unref(c->client);
-   free(c);
-}
-
-EFL_CALLBACKS_ARRAY_DEFINE(_client_cbs,
-                           { EFL_IO_READER_EVENT_EOS, _client_eos },
-                           { EFL_IO_BUFFERED_STREAM_EVENT_ERROR, _client_error },
-                           { EFL_IO_BUFFERED_STREAM_EVENT_READ_FINISHED, _client_read_finished },
-                           { EFL_IO_BUFFERED_STREAM_EVENT_WRITE_FINISHED, _client_write_finished },
-                           { EFL_IO_BUFFERED_STREAM_EVENT_FINISHED, _client_finished },
-                           { EFL_IO_BUFFERED_STREAM_EVENT_SLICE_CHANGED, _client_data });
 
-static void
-_client_add(void *data EINA_UNUSED, const Efl_Event *event)
-{
-   Client *c = calloc(1, sizeof(Client));
+   _send(src, EINA_DEBUG_OPCODE_REGISTER, buf, (char *)opcodes - (char *)buf);
 
-   EINA_SAFETY_ON_NULL_RETURN(c);
-   c->client = efl_ref(event->info);
-   clients = eina_list_append(clients, c);
-   efl_event_callback_array_add(c->client, _client_cbs(), c);
-   INF("server %p new client %p (%p)", event->object, c, c->client);
+   return EINA_TRUE;
 }
 
-static void
-_error(void *data EINA_UNUSED, const Efl_Event *event)
+static int
+_data_receive(Client *c, unsigned char **buffer)
 {
-   Eina_Error *perr = event->info;
-   ERR("server %p error: %s", event->object, eina_error_msg_get(*perr));
-   fprintf(stderr, "ERROR: %s\n", eina_error_msg_get(*perr));
-   ecore_main_loop_quit();
-   retval = EXIT_FAILURE;
-}
+   unsigned char *recv_buf = NULL;
+   int rret;
+   int size = 0;
 
-int
-main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
-{
-   Eo *loop;
-   char *path;
-   Eina_Error err;
+   if (!c) return -1;
 
-   ecore_app_no_system_modules();
+   rret = recv(c->fd, &size, sizeof(int), MSG_PEEK);
 
-   eina_init();
-   ecore_init();
-   ecore_con_init();
+   if (rret == sizeof(int))
+     {
+        int cur_packet_size = 0;
+        // allocate a buffer for the next bytes to receive
+        recv_buf = malloc(size);
+        if (!recv_buf) goto error;
+        while (cur_packet_size < size)
+          {
+             rret = recv(c->fd, recv_buf + cur_packet_size, size - cur_packet_size, 0);
+             if (rret <= 0) goto error;
+             cur_packet_size += rret;
+          }
+     }
+   if (buffer) *buffer = recv_buf;
+   //printf("%d bytes received from client %s fd %d\n", size, c->app_name, c->fd);
+   return size;
+error:
+   if (rret == -1) perror("Read from socket");
+   if (recv_buf) free(recv_buf);
+   return -1;
+}
 
-   _log_dom = eina_log_domain_register("efl_debugd", EINA_COLOR_CYAN);
+static void
+_monitor()
+{
+#ifndef _WIN32
+#define MAX_EVENTS 1000
+   int ret = 0;
+   struct epoll_event events[MAX_EVENTS];
+   Client *c;
 
-   path = ecore_con_local_path_new(EINA_FALSE, "efl_debug", 0);
-   if (!path)
+   // sit forever processing commands or timeouts
+   for (; ret != -1;)
      {
-        fprintf(stderr, "ERROR: could not get local communication path\n");
-        retval = EXIT_FAILURE;
-        goto end;
-     }
+        ret = epoll_wait (_epfd, events, MAX_EVENTS, -1);
 
-   loop = ecore_main_loop_get();
+        // if the fd for debug daemon says it's alive, process it
+        if (ret > 0)
+          {
+             int i;
+             //check which fd are set/ready for read
+             for (i = 0; i < ret; i++)
+               {
+                  if (events[i].events & EPOLLHUP)
+                    {
+                       c = _client_find_by_fd(events[i].data.fd);
+                       close(events[i].data.fd);
+                       if (c) _client_del(c);
+                    }
+                  if (events[i].events & EPOLLIN)
+                    {
+                       // Someone wants to connect
+                       if(events[i].data.fd == _listening_master_fd || events[i].data.fd == _listening_slave_fd)
+                         {
+                            int new_fd = accept(events[i].data.fd, NULL, NULL);
+                            if (new_fd < 0) perror("Accept");
+                            else
+                              {
+                                 struct epoll_event event;
+                                 c = calloc(1, sizeof(*c));
+                                 c->fd = new_fd;
+                                 c->is_master = (events[i].data.fd == _listening_master_fd);
+                                 _clients = eina_list_append(_clients, c);
+                                 event.data.fd = new_fd;
+                                 event.events = EPOLLIN;
+                                 epoll_ctl (_epfd, EPOLL_CTL_ADD, new_fd, &event);
+                              }
+                            continue;
+                         }
 
-#ifdef EFL_NET_SERVER_UNIX_CLASS
-   server = efl_add(EFL_NET_SERVER_SIMPLE_CLASS, loop,
-                    efl_net_server_simple_inner_class_set(efl_added, EFL_NET_SERVER_UNIX_CLASS));
-#else
-   /* TODO: maybe start a TCP using locahost:12345?
-    * Right now eina_debug_monitor is only for AF_UNIX, so not an issue.
-    */
-   fprintf(stderr, "ERROR: your platform doesn't support Efl.Net.Server.Unix\n");
+                       c = _client_find_by_fd(events[i].data.fd);
+                       if (c)
+                          {
+                             int size;
+                             unsigned char *buffer;
+                             size = _data_receive(c, &buffer);
+                             // if not negative - we have a real message
+                             if (size > 0)
+                               {
+                                  if(!_dispatch(c, buffer, size))
+                                    {
+                                       // something we don't understand
+                                       fprintf(stderr, "Dispatch: unknown command");
+                                    }
+                                  free(buffer);
+                               }
+                             else
+                               {
+                                  // major failure on debug daemon control fd - get out of here.
+                                  //   else goto fail;
+                                  close(events[i].data.fd);
+                                  //TODO if its not main session we will tell the main_loop
+                                  //that it disconneted
+                               }
+                          }
+                    }
+               }
+          }
+#if 0
+        else
+          {
+             if (poll_time && poll_timer_cb)
+               {
+                  if (!poll_timer_cb()) poll_time = 0;
+               }
+          }
 #endif
-   if (!server)
-     {
-        fprintf(stderr, "ERROR: could not create communication server\n");
-        retval = EXIT_FAILURE;
-        goto end;
      }
+#endif
+}
 
-   efl_event_callback_add(server, EFL_NET_SERVER_EVENT_CLIENT_ADD, _client_add, NULL);
-   efl_event_callback_add(server, EFL_NET_SERVER_EVENT_ERROR, _error, NULL);
+static const char *
+_socket_home_get()
+{
+   // get possible debug daemon socket directory base
+   const char *dir = getenv("XDG_RUNTIME_DIR");
+   if (!dir) dir = eina_environment_home_get();
+   if (!dir) dir = eina_environment_tmp_get();
+   return dir;
+}
 
-#ifdef EFL_NET_SERVER_UNIX_CLASS
-   {
-      Eo *inner_server = efl_net_server_simple_inner_server_get(server);
-      efl_net_server_unix_unlink_before_bind_set(inner_server, EINA_TRUE);
-      efl_net_server_unix_leading_directories_create_set(inner_server, EINA_TRUE, 0700);
-   }
+#ifndef _WIN32
+#define LENGTH_OF_SOCKADDR_UN(s) \
+   (strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
+static int
+_local_listening_socket_create(const char *path)
+{
+   struct sockaddr_un socket_unix;
+   int socket_unix_len, curstate = 0;
+   // create the socket
+   int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+   if (fd < 0) goto err;
+   // set the socket to close when we exec things so they don't inherit it
+   if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto err;
+   // set up some socket options on addr re-use
+   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate,
+                  sizeof(curstate)) < 0)
+     goto err;
+   // sa that it's a unix socket and where the path is
+   socket_unix.sun_family = AF_UNIX;
+   strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path) - 1);
+   socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
+   unlink(socket_unix.sun_path);
+   if (bind(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
+     {
+        perror("ERROR on binding");
+        goto err;
+     }
+   listen(fd, 5);
+   return fd;
+err:
+   if (fd >= 0) close(fd);
+   return -1;
+}
 #endif
 
-   err = efl_net_server_serve(server, path);
-   if (err)
+static Eina_Bool
+_server_launch()
+{
+#ifndef _WIN32
+   char buf[4096];
+   struct epoll_event event = {0};
+   mode_t mask = 0;
+   const char *socket_home_path = _socket_home_get();
+   char *socket_path = NULL;
+   if (!socket_home_path) return EINA_FALSE;
+   _epfd = epoll_create (MAX_EVENTS);
+   socket_path = strdup(socket_home_path);
+
+   snprintf(buf, sizeof(buf), "%s/%s", socket_path, SERVER_PATH);
+   if (mkdir(buf, S_IRWXU) < 0 && errno != EEXIST)
+     {
+        perror("mkdir SERVER_PATH");
+        goto err;
+     }
+   snprintf(buf, sizeof(buf), "%s/%s/%s", socket_path, SERVER_PATH, SERVER_NAME);
+   if (mkdir(buf, S_IRWXU) < 0 && errno != EEXIST)
      {
-        fprintf(stderr, "ERROR: could not serve '%s': %s\n", path, eina_error_msg_get(err));
-        retval = EXIT_FAILURE;
-        goto end;
+        perror("mkdir SERVER_NAME");
+        goto err;
      }
+   mask = umask(S_IRWXG | S_IRWXO);
+   snprintf(buf, sizeof(buf), "%s/%s/%s/%i", socket_path, SERVER_PATH, SERVER_NAME, SERVER_MASTER_PORT);
+   _listening_master_fd = _local_listening_socket_create(buf);
+   if (_listening_master_fd <= 0) goto err;
+   event.data.fd = _listening_master_fd;
+   event.events = EPOLLIN;
+   epoll_ctl (_epfd, EPOLL_CTL_ADD, _listening_master_fd, &event);
+   snprintf(buf, sizeof(buf), "%s/%s/%s/%i", socket_path, SERVER_PATH, SERVER_NAME, SERVER_SLAVE_PORT);
+   _listening_slave_fd = _local_listening_socket_create(buf);
+   if (_listening_slave_fd <= 0) goto err;
+   event.data.fd = _listening_slave_fd;
+   event.events = EPOLLIN;
+   epoll_ctl (_epfd, EPOLL_CTL_ADD, _listening_slave_fd, &event);
+   umask(mask);
+   return EINA_TRUE;
+err:
+   if (mask) umask(mask);
+   if (_listening_master_fd >= 0) close(_listening_master_fd);
+   _listening_master_fd = -1;
+   if (_listening_slave_fd >= 0) close(_listening_slave_fd);
+   _listening_slave_fd = -1;
+   free(socket_path);
+#endif
+   return EINA_FALSE;
+}
+
+int
+main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
+{
+   eina_debug_disable();
+   eina_init();
+   ecore_init();
 
-   ecore_main_loop_begin();
+   _string_to_opcode_hash = eina_hash_string_superfast_new(NULL);
+   _opcode_register("daemon/opcode/register", EINA_DEBUG_OPCODE_REGISTER, _opcode_register_cb);
+   _opcode_register("daemon/greet", EINA_DEBUG_OPCODE_HELLO, _hello_cb);
+   _clients_stat_register_opcode = _opcode_register("daemon/observer/client/register", EINA_DEBUG_OPCODE_INVALID, _cl_stat_obs_register_cb);
+   _slave_added_opcode = _opcode_register("daemon/observer/slave_added", EINA_DEBUG_OPCODE_INVALID, NULL);
+   _slave_deleted_opcode = _opcode_register("daemon/observer/slave_deleted", EINA_DEBUG_OPCODE_INVALID, NULL);
+   _cid_from_pid_opcode = _opcode_register("daemon/info/cid_from_pid", EINA_DEBUG_OPCODE_INVALID, _cid_get_cb);
+   _test_loop_opcode = _opcode_register("daemon/test/loop", EINA_DEBUG_OPCODE_INVALID, _data_test_cb);
 
- end:
-   efl_del(server);
-   free(path);
+   _server_launch();
+   _monitor();
 
-   ecore_con_shutdown();
    ecore_shutdown();
    eina_shutdown();
 
-   return retval;
+   return 0;
 }
index 41c1828..ee65911 100644 (file)
@@ -272,6 +272,7 @@ extern "C" {
 #include <eina_slice.h>
 #include <eina_freeq.h>
 #include <eina_slstr.h>
+#include <eina_debug.h>
 
 #undef EAPI
 #define EAPI
index 8578f13..f8c6209 100644 (file)
  * if not, see <http://www.gnu.org/licenses/>.
  */
 
+# ifndef _GNU_SOURCE
+#  define _GNU_SOURCE 1
+# endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/epoll.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 "eina_debug.h"
 #include "eina_types.h"
+#include "eina_list.h"
+#include "eina_mempool.h"
+#include "eina_util.h"
+#include "eina_evlog.h"
+#include "eina_hash.h"
+#include "eina_debug_private.h"
 
-#ifdef EINA_HAVE_DEBUG
+#ifdef __CYGWIN__
+# define LIBEXT ".dll"
+#else
+# define LIBEXT ".so"
+#endif
+
+#define SIG SIGPROF
 
 // 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
@@ -28,6 +62,789 @@ Eina_Spinlock _eina_debug_lock;
 
 // only init once
 static Eina_Bool _inited = EINA_FALSE;
+static char *_my_app_name = NULL;
+
+extern Eina_Bool eina_module_init(void);
+extern Eina_Bool eina_mempool_init(void);
+extern Eina_Bool eina_list_init(void);
+
+extern Eina_Spinlock _eina_debug_thread_lock;
+
+static Eina_Bool _debug_disabled = EINA_FALSE;
+
+/* Local session */
+/* __thread here to allow debuggers to be master and slave by using two different threads */
+static __thread Eina_Debug_Session *_session = NULL;
+static Eina_Debug_Session *_last_local_session = NULL;
+
+/* Opcode used to load a module
+ * needed by the daemon to notify loading success */
+static int _module_init_opcode = EINA_DEBUG_OPCODE_INVALID;
+static int _module_shutdown_opcode = EINA_DEBUG_OPCODE_INVALID;
+static Eina_Hash *_modules_hash = NULL;
+
+static unsigned int _poll_time = 0;
+static Eina_Debug_Timer_Cb _poll_timer_cb = NULL;
+static void *_poll_timer_data = NULL;
+
+static Eina_Semaphore _thread_cmd_ready_sem;
+
+typedef struct
+{
+   int magic; /* Used to certify the validity of the struct */
+   const Eina_Debug_Opcode *ops;
+   Eina_Debug_Opcode_Status_Cb status_cb;
+} _opcode_reply_info;
+
+struct _Eina_Debug_Session
+{
+   Eina_List **cbs; /* Table of callbacks lists indexed by opcode id */
+   Eina_List *opcode_reply_infos;
+   Eina_Debug_Dispatch_Cb dispatch_cb; /* Session dispatcher */
+   int cbs_length; /* cbs table size */
+   int fd_in; /* File descriptor to read */
+   int fd_out; /* File descriptor to write */
+};
+
+static void _opcodes_register_all();
+static void _thread_start(Eina_Debug_Session *session);
+
+EAPI int
+eina_debug_session_send_to_thread(Eina_Debug_Session *session, int dest_id, int thread_id, int op, void *data, int size)
+{
+   Eina_Debug_Packet_Header hdr;
+
+   if (!session) return -1;
+   if (op == EINA_DEBUG_OPCODE_INVALID) return -1;
+   /* Preparation of the packet header */
+   hdr.size = size + sizeof(Eina_Debug_Packet_Header);
+   hdr.opcode = op;
+   hdr.cid = dest_id;
+   hdr.thread_id = thread_id;
+#ifndef _WIN32
+   e_debug("socket: %d / opcode %X / packet size %ld / bytes to send: %d",
+         session->fd_out, op, hdr->size + sizeof(int), total_size);
+   eina_spinlock_take(&_eina_debug_lock);
+   /* Sending header */
+   write(session->fd_out, &hdr, sizeof(hdr));
+   /* Sending payload */
+   if (size) write(session->fd_out, data, size);
+   eina_spinlock_release(&_eina_debug_lock);
+#endif
+   return hdr.size;
+}
+
+EAPI int
+eina_debug_session_send(Eina_Debug_Session *session, int dest, int op, void *data, int size)
+{
+   return eina_debug_session_send_to_thread(session, dest, 0, op, data, size);
+}
+
+static void
+_daemon_greet(Eina_Debug_Session *session)
+{
+   /* say hello to our debug daemon - tell them our PID and protocol
+      version we speak */
+   /* Version + Pid + App name */
+   int size = 8 + (_my_app_name ? strlen(_my_app_name) : 0) + 1;
+   unsigned char *buf = alloca(size);
+   int version = 1; // version of protocol we speak
+   int pid = getpid();
+   memcpy(buf + 0, &version, 4);
+   memcpy(buf + 4, &pid, 4);
+   if (_my_app_name)
+      memcpy(buf + 8, _my_app_name, strlen(_my_app_name) + 1);
+   else
+      buf[8] = '\0';
+   eina_debug_session_send(session, 0, EINA_DEBUG_OPCODE_HELLO, buf, size);
+}
+
+#ifndef _WIN32
+static int
+_packet_receive(unsigned char **buffer)
+{
+   unsigned char *packet_buf = NULL;
+   int rret = -1;
+   int size = 0;
+
+   if (!_session) goto end;
+
+   if (read(_session->fd_in, &size, 4) == 4)
+     {
+        // allocate a buffer for the next bytes to receive
+        packet_buf = malloc(size);
+        if (packet_buf)
+          {
+             int cur_packet_size = 4;
+             memcpy(packet_buf, &size, sizeof(int));
+             /* Receive all the remaining packet bytes */
+             while (cur_packet_size < size)
+               {
+                  rret = read(_session->fd_in, packet_buf + cur_packet_size, size - cur_packet_size);
+                  if (rret <= 0) goto end;
+                  cur_packet_size += rret;
+               }
+             *buffer = packet_buf;
+             rret = cur_packet_size;
+          }
+        else
+          {
+             // we couldn't allocate memory for payloa buffer
+             // internal memory limit error
+             e_debug("Cannot allocate %u bytes for op", (unsigned int)size);
+             goto end;
+          }
+     }
+   else
+     {
+        e_debug("Invalid size read %i != %i", rret, size_sz);
+        goto end;
+     }
+end:
+   if (rret <= 0 && packet_buf) free(packet_buf);
+   return rret;
+}
+#endif
+
+EAPI void
+eina_debug_disable()
+{
+   _debug_disabled = EINA_TRUE;
+}
+
+EAPI void
+eina_debug_session_terminate(Eina_Debug_Session *session)
+{
+   /* FIXME: Maybe just close fd here so the thread terminates its own session by itself */
+   if (!session) return;
+
+   _opcode_reply_info *info = NULL;
+
+   EINA_LIST_FREE(session->opcode_reply_infos, info) free(info);
+
+   free(session->cbs);
+   free(session);
+}
+
+EAPI void
+eina_debug_session_dispatch_override(Eina_Debug_Session *session, Eina_Debug_Dispatch_Cb disp_cb)
+{
+   if (!session) return;
+   if (!disp_cb) disp_cb = eina_debug_dispatch;
+   session->dispatch_cb = disp_cb;
+}
+
+typedef struct {
+     Eina_Module *handle;
+     Eina_Bool (*init)(void);
+     Eina_Bool (*shutdown)(void);
+     int ref;
+} _module_info;
+
+#define _LOAD_SYMBOL(cls_struct, pkg, sym) \
+   do \
+     { \
+        char func_name[1024]; \
+        snprintf(func_name, sizeof(func_name), "%s_debug_" #sym, pkg); \
+        (cls_struct)->sym = eina_module_symbol_get((cls_struct)->handle, func_name); \
+        if (!(cls_struct)->sym) \
+          { \
+             e_debug("Failed loading symbol '%s' from the library.", func_name); \
+             eina_module_free((cls_struct)->handle); \
+             (cls_struct)->handle = NULL; \
+             free((cls_struct)); \
+             return EINA_FALSE; \
+          } \
+     } \
+   while (0)
+
+static Eina_Debug_Error
+_module_init_cb(Eina_Debug_Session *session, int cid, void *buffer, int size)
+{
+   char module_path[1024];
+   _module_info *minfo = NULL;
+   const char *module_name = buffer;
+   char *resp;
+   if (size <= 0) return EINA_DEBUG_ERROR;
+   if (!_modules_hash) _modules_hash = eina_hash_string_small_new(NULL);
+
+   minfo = eina_hash_find(_modules_hash, module_name);
+   if (minfo && minfo->ref)
+     {
+        minfo->ref++;
+        goto end;
+     }
+
+   e_debug("Init module %s", module_name);
+   if (!minfo)
+     {
+        snprintf(module_path, sizeof(module_path), PACKAGE_LIB_DIR "/lib%s_debug"LIBEXT, module_name);
+        minfo = calloc(1, sizeof(*minfo));
+        eina_hash_add(_modules_hash, module_name, minfo);
+     }
+   if (!minfo->handle) minfo->handle = eina_module_new(module_path);
+   if (!minfo->handle || !eina_module_load(minfo->handle))
+     {
+        e_debug("Failed loading debug module %s.", module_name);
+        if (minfo->handle) eina_module_free(minfo->handle);
+        minfo->handle = NULL;
+        goto end;
+     }
+
+   if (!minfo->init) _LOAD_SYMBOL(minfo, module_name, init);
+   if (!minfo->shutdown) _LOAD_SYMBOL(minfo, module_name, shutdown);
+
+   if (minfo->init()) minfo->ref = 1;
+
+end:
+   resp = alloca(size + 1);
+   memcpy(resp, buffer, size);
+   resp[size] = !!(minfo->ref);
+   eina_debug_session_send(session, cid, _module_init_opcode, resp, size+1);
+   return EINA_DEBUG_OK;
+}
+
+static Eina_Debug_Error
+_module_shutdown_cb(Eina_Debug_Session *session, int cid, void *buffer, int size)
+{
+   _module_info *minfo = NULL;
+   const char *module_name = buffer;
+   char *resp;
+   Eina_Bool ret = EINA_TRUE;
+   if (size <= 0 || !_modules_hash) return EINA_DEBUG_ERROR;
+
+   minfo = eina_hash_find(_modules_hash, module_name);
+   if (minfo)
+     {
+        if (!--(minfo->ref))
+          {
+             eina_hash_del(_modules_hash, module_name, minfo);
+             if (minfo->shutdown) ret = minfo->shutdown();
+             if (minfo->handle) eina_module_free(minfo->handle);
+             minfo->handle = NULL;
+             free(minfo);
+          }
+     }
+   resp = alloca(size + 1);
+   memcpy(resp, buffer, size);
+   resp[size] = !!ret;
+   eina_debug_session_send(session, cid, _module_shutdown_opcode, resp, size+1);
+   return EINA_DEBUG_OK;
+}
+
+static const Eina_Debug_Opcode _EINA_DEBUG_MONITOR_OPS[] = {
+       {"module/init", &_module_init_opcode, &_module_init_cb},
+       {"module/shutdown", &_module_shutdown_opcode, &_module_shutdown_cb},
+       {NULL, NULL, NULL}
+};
+
+static void
+_static_opcode_register(Eina_Debug_Session *session,
+      int op_id, Eina_Debug_Cb cb)
+{
+   if(session->cbs_length < op_id + 1)
+     {
+        int i = session->cbs_length;
+        session->cbs_length = op_id + 16;
+        session->cbs = realloc(session->cbs, session->cbs_length * sizeof(Eina_List *));
+        for(; i < session->cbs_length; i++) session->cbs[i] = NULL;
+     }
+   if (cb)
+     {
+        session->cbs[op_id] = eina_list_append(session->cbs[op_id], cb);
+     }
+}
+
+/*
+ * Response of the daemon containing the ids of the requested opcodes.
+ * PTR64 + (opcode id)*
+ */
+static Eina_Debug_Error
+_callbacks_register_cb(Eina_Debug_Session *session, int src_id EINA_UNUSED, void *buffer, int size)
+{
+   _opcode_reply_info *info = NULL, *info2;
+   Eina_List *itr;
+   int *os;
+   unsigned int count, i;
+
+   uint64_t info_64;
+   memcpy(&info_64, buffer, sizeof(uint64_t));
+   info = (_opcode_reply_info *)info_64;
+
+   if (!info) return EINA_DEBUG_ERROR;
+   EINA_LIST_FOREACH(session->opcode_reply_infos, itr, info2)
+     {
+        if (info2 == info)
+          {
+             os = (int *)((unsigned char *)buffer + sizeof(uint64_t));
+             count = (size - sizeof(uint64_t)) / sizeof(int);
+
+             for (i = 0; i < count; i++)
+               {
+                  if (info->ops[i].opcode_id) *(info->ops[i].opcode_id) = os[i];
+                  _static_opcode_register(session, os[i], info->ops[i].cb);
+                  e_debug("Opcode %s -> %d", info->ops[i].opcode_name, os[i]);
+               }
+             if (info->status_cb) info->status_cb(EINA_TRUE);
+             return EINA_DEBUG_OK;
+          }
+     }
+
+   return EINA_DEBUG_ERROR;
+}
+
+static void
+_opcodes_registration_send(Eina_Debug_Session *session,
+      _opcode_reply_info *info)
+{
+   unsigned char *buf;
+
+   int count = 0;
+   int size = sizeof(uint64_t);
+
+   while(info->ops[count].opcode_name)
+     {
+        size += strlen(info->ops[count].opcode_name) + 1;
+        count++;
+     }
+
+   buf = alloca(size);
+
+   uint64_t info_64 = (uint64_t)info;
+   memcpy(buf, &info_64, sizeof(uint64_t));
+   int size_curr = sizeof(uint64_t);
+
+   count = 0;
+   while(info->ops[count].opcode_name)
+     {
+        int len = strlen(info->ops[count].opcode_name) + 1;
+        memcpy(buf + size_curr, info->ops[count].opcode_name, len);
+        size_curr += len;
+        count++;
+     }
+
+   eina_debug_session_send(session, 0, EINA_DEBUG_OPCODE_REGISTER, buf, size);
+}
+
+static void
+_opcodes_register_all(Eina_Debug_Session *session)
+{
+   Eina_List *l;
+   _opcode_reply_info *info = NULL;
+
+   _static_opcode_register(session,
+         EINA_DEBUG_OPCODE_REGISTER, _callbacks_register_cb);
+   EINA_LIST_FOREACH(session->opcode_reply_infos, l, info)
+        _opcodes_registration_send(session, info);;
+}
+
+static void
+_opcodes_unregister_all(Eina_Debug_Session *session)
+{
+   Eina_List *l;
+   int i;
+   _opcode_reply_info *info = NULL;
+
+   if (!session) return;
+   session->cbs_length = 0;
+   for (i = 0; i < session->cbs_length; i++)
+      eina_list_free(session->cbs[i]);
+   free(session->cbs);
+   session->cbs = NULL;
+
+   EINA_LIST_FOREACH(session->opcode_reply_infos, l, info)
+     {
+        const Eina_Debug_Opcode *op = info->ops;
+        while(!op->opcode_name)
+          {
+             if (op->opcode_id) *(op->opcode_id) = EINA_DEBUG_OPCODE_INVALID;
+             op++;
+          }
+        if (info->status_cb) info->status_cb(EINA_FALSE);
+     }
+}
+
+static const char *
+_socket_home_get()
+{
+   // get possible debug daemon socket directory base
+   const char *dir = getenv("XDG_RUNTIME_DIR");
+   if (!dir) dir = eina_environment_home_get();
+   if (!dir) dir = eina_environment_tmp_get();
+   return dir;
+}
+
+#ifndef _WIN32
+#define LENGTH_OF_SOCKADDR_UN(s) \
+   (strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
+#endif
+
+EAPI Eina_Debug_Session *
+eina_debug_local_connect(Eina_Bool is_master)
+{
+#ifndef _WIN32
+   char buf[4096];
+   int fd, socket_unix_len, curstate = 0;
+   struct sockaddr_un socket_unix;
+#endif
+
+   Eina_Debug_Session *session = calloc(1, sizeof(*session));
+   session->dispatch_cb = eina_debug_dispatch;
+   session->fd_out = session->fd_in = -1;
+   // try this socket file - it will likely be:
+   //   ~/.ecore/efl_debug/0
+   // or maybe
+   //   /var/run/UID/.ecore/efl_debug/0
+   // either way a 4k buffer should be ebough ( if it's not we're on an
+   // insane system)
+#ifndef _WIN32
+   snprintf(buf, sizeof(buf), "%s/%s/%s/%i", _socket_home_get(), SERVER_PATH, SERVER_NAME,
+         is_master ? SERVER_MASTER_PORT : SERVER_SLAVE_PORT);
+   // create the socket
+   fd = socket(AF_UNIX, SOCK_STREAM, 0);
+   if (fd < 0) goto err;
+   // set the socket to close when we exec things so they don't inherit it
+   if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto err;
+   // set up some socket options on addr re-use
+   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate,
+                  sizeof(curstate)) < 0)
+     goto err;
+   // sa that it's a unix socket and where the path is
+   socket_unix.sun_family = AF_UNIX;
+   strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path) - 1);
+   socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
+   // actually connect to efl_debugd service
+   if (connect(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
+      goto err;
+   // we succeeded
+   session->fd_out = session->fd_in = fd;
+   // start the monitor thread
+   _thread_start(session);
+
+   _daemon_greet(session);
+   _opcodes_register_all(session);
+   if (!is_master)
+      eina_debug_opcodes_register(session, _EINA_DEBUG_MONITOR_OPS, NULL);
+
+   _last_local_session = session;
+   return session;
+err:
+   // some kind of connection failure here, so close a valid socket and
+   // get out of here
+   if (fd >= 0) close(fd);
+   if (session) free(session);
+#else
+   (void) _session;
+   (void) type;
+#endif
+   return NULL;
+}
+
+EAPI Eina_Bool
+eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data)
+{
+   _poll_time = timeout_ms;
+   _poll_timer_cb = cb;
+   _poll_timer_data = data;
+   return EINA_TRUE;
+}
+
+// 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 *
+_monitor(void *_data)
+{
+#ifndef _WIN32
+#define MAX_EVENTS   4
+   int ret;
+   struct epoll_event event;
+   struct epoll_event events[MAX_EVENTS];
+   int epfd = epoll_create(MAX_EVENTS);
+
+   _session = _data;
+   event.data.fd = _session->fd_in;
+   event.events = EPOLLIN;
+   ret = epoll_ctl(epfd, EPOLL_CTL_ADD, _session->fd_in, &event);
+   if (ret) perror("epoll_ctl/add");
+
+   // set a name for this thread for system debugging
+#ifdef EINA_HAVE_PTHREAD_SETNAME
+# ifndef __linux__
+   pthread_set_name_np
+# else
+   pthread_setname_np
+# endif
+     (pthread_self(), "Edbg-mon");
+#endif
+
+   // sit forever processing commands or timeouts in the debug monitor
+   // thread - this is separate to the rest of the app so it shouldn't
+   // impact the application specifically
+   for (;_session;)
+     {
+        // if we are in a polling mode then set up a timeout and wait for it
+        int timeout = _poll_time ? (int)_poll_time : -1; //in milliseconds
+
+        ret = epoll_wait(epfd, events, MAX_EVENTS, timeout);
+
+        // if the fd for debug daemon says it's alive, process it
+        if (ret)
+          {
+             int i;
+             //check which fd are set/ready for read
+             for (i = 0; i < ret; i++)
+               {
+                  if (events[i].events & EPOLLHUP)
+                    {
+                       _opcodes_unregister_all(_session);
+                       free(_session);
+                       _session = NULL;
+                    }
+                  else if (events[i].events & EPOLLIN)
+                    {
+                       int size;
+                       unsigned char *buffer;
+
+                       size = _packet_receive(&buffer);
+                       // if not negative - we have a real message
+                       if (size > 0)
+                         {
+                            if(!_session->dispatch_cb(_session, buffer))
+                              {
+                                 // something we don't understand
+                                 e_debug("EINA DEBUG ERROR: Unknown command");
+                              }
+                         }
+                       else if (size == 0)
+                         {
+                            // May be due to a response from a script line
+                         }
+                       else
+                         {
+                            // major failure on debug daemon control fd - get out of here.
+                            //   else goto fail;
+                            close(_session->fd_in);
+                            //TODO if its not main _session we will tell the main_loop
+                            //that it disconneted
+                         }
+                    }
+               }
+          }
+        else
+          {
+             if (_poll_time && _poll_timer_cb)
+               {
+                  if (!_poll_timer_cb(_poll_timer_data)) _poll_time = 0;
+               }
+          }
+     }
+#endif
+   return NULL;
+}
+
+// start up the debug monitor if we haven't already
+static void
+_thread_start(Eina_Debug_Session *session)
+{
+   pthread_t monitor_thread;
+   int err;
+   sigset_t oldset, newset;
+
+   sigemptyset(&newset);
+   sigaddset(&newset, SIGPIPE);
+   sigaddset(&newset, SIGALRM);
+   sigaddset(&newset, SIGCHLD);
+   sigaddset(&newset, SIGUSR1);
+   sigaddset(&newset, SIGUSR2);
+   sigaddset(&newset, SIGHUP);
+   sigaddset(&newset, SIGQUIT);
+   sigaddset(&newset, SIGINT);
+   sigaddset(&newset, SIGTERM);
+#ifdef SIGPWR
+   sigaddset(&newset, SIGPWR);
+#endif
+   sigprocmask(SIG_BLOCK, &newset, &oldset);
+
+   err = pthread_create(&monitor_thread, NULL, _monitor, session);
+
+   sigprocmask(SIG_SETMASK, &oldset, NULL);
+   if (err != 0)
+     {
+        e_debug("EINA DEBUG ERROR: Can't create monitor debug thread!");
+        abort();
+     }
+}
+
+/*
+ * Sends to daemon:
+ * - Pointer to ops: returned in the response to determine which opcodes have been added
+ * - List of opcode names seperated by \0
+ */
+EAPI void
+eina_debug_opcodes_register(Eina_Debug_Session *session, const Eina_Debug_Opcode ops[],
+      Eina_Debug_Opcode_Status_Cb status_cb)
+{
+   if (!session) session = _last_local_session;
+   if (!session) return;
+
+   _opcode_reply_info *info = malloc(sizeof(*info));
+   info->ops = ops;
+   info->status_cb = status_cb;
+
+   session->opcode_reply_infos = eina_list_append(
+         session->opcode_reply_infos, info);
+
+   //send only if _session's fd connected, if not -  it will be sent when connected
+   if(session && session->fd_in != -1)
+      _opcodes_registration_send(session, info);
+}
+
+static Eina_Debug_Error
+_self_dispatch(Eina_Debug_Session *session, void *buffer)
+{
+   Eina_Debug_Packet_Header *hdr =  buffer;
+   Eina_List *itr;
+   int opcode = hdr->opcode;
+   Eina_Debug_Cb cb = NULL;
+
+   if (opcode >= session->cbs_length)
+     {
+        e_debug("Invalid opcode %d", opcode);
+        return EINA_DEBUG_ERROR;
+     }
+
+   EINA_LIST_FOREACH(session->cbs[opcode], itr, cb)
+     {
+        if (!cb) continue;
+        Eina_Debug_Error ret = cb(session, hdr->cid,
+              (unsigned char *)buffer + sizeof(*hdr),
+              hdr->size - sizeof(*hdr));
+        if (ret == EINA_DEBUG_ERROR) return ret;
+     }
+   return EINA_DEBUG_OK;
+}
+
+EAPI Eina_Debug_Error
+eina_debug_dispatch(Eina_Debug_Session *session, void *buffer)
+{
+   Eina_Debug_Packet_Header *hdr = buffer;
+   Eina_Debug_Error ret = EINA_DEBUG_OK;
+   if (hdr->thread_id == 0)
+     {
+        ret = _self_dispatch(session, buffer);
+        free(buffer);
+        return ret;
+     }
+   else
+     {
+        int i, nb_calls = 0;
+        eina_spinlock_take(&_eina_debug_thread_lock);
+        for (i = 0; i < _eina_debug_thread_active_num; i++)
+          {
+             _eina_debug_thread_active[i].cmd_buffer = NULL;
+             if (hdr->thread_id == (int)0xFFFFFFFF ||
+                   hdr->thread_id == _eina_debug_thread_active[i].thread_id)
+               {
+                  _eina_debug_thread_active[i].cmd_session = session;
+                  _eina_debug_thread_active[i].cmd_buffer = buffer;
+                  _eina_debug_thread_active[i].cmd_result = EINA_DEBUG_OK;
+                  pthread_kill(_eina_debug_thread_active[i].thread, SIG);
+                  nb_calls++;
+               }
+          }
+        eina_spinlock_release(&_eina_debug_thread_lock);
+        while (nb_calls)
+          {
+             while (nb_calls)
+               {
+                  eina_semaphore_lock(&_thread_cmd_ready_sem);
+                  nb_calls--;
+               }
+             eina_spinlock_take(&_eina_debug_thread_lock);
+             for (i = 0; i < _eina_debug_thread_active_num; i++)
+               {
+                  if (_eina_debug_thread_active[i].cmd_buffer)
+                    {
+                       switch (_eina_debug_thread_active[i].cmd_result)
+                         {
+                          case EINA_DEBUG_OK:
+                               {
+                                  _eina_debug_thread_active[i].cmd_buffer = NULL;
+                                  break;
+                               }
+                          case EINA_DEBUG_ERROR:
+                               {
+                                  _eina_debug_thread_active[i].cmd_buffer = NULL;
+                                  ret = EINA_DEBUG_ERROR;
+                                  break;
+                               }
+                          case EINA_DEBUG_AGAIN:
+                               {
+                                  pthread_kill(_eina_debug_thread_active[i].thread, SIG);
+                                  nb_calls++;
+                                  break;
+                               }
+                          default: break;
+                         }
+                    }
+               }
+             eina_spinlock_release(&_eina_debug_thread_lock);
+          }
+        free(buffer);
+     }
+   return ret;
+}
+
+static void
+_signal_handler(int sig EINA_UNUSED,
+      siginfo_t *si EINA_UNUSED, void *foo EINA_UNUSED)
+{
+   int i, slot = -1;
+   pthread_t self = pthread_self();
+   eina_spinlock_take(&_eina_debug_thread_lock);
+   for (i = 0; i < _eina_debug_thread_active_num; i++)
+     {
+        if (self == _eina_debug_thread_active[i].thread)
+          {
+             slot = i;
+             break;
+          }
+     }
+   eina_spinlock_release(&_eina_debug_thread_lock);
+   if (slot != -1)
+     {
+        _eina_debug_thread_active[slot].cmd_result =
+           _self_dispatch(_eina_debug_thread_active[slot].cmd_session,
+                 _eina_debug_thread_active[slot].cmd_buffer);
+     }
+   eina_semaphore_release(&_thread_cmd_ready_sem, 1);
+}
+
+#ifdef __linux__
+   extern char *__progname;
+#endif
+
+static void
+_signal_init(void)
+{
+   struct sigaction sa;
+
+   // set up signal handler for our profiling signal - eevery thread should
+   // obey this (this is the case on linux - other OSs may vary)
+   sa.sa_sigaction = _signal_handler;
+   sa.sa_flags = SA_RESTART | SA_SIGINFO;
+   sigemptyset(&sa.sa_mask);
+   if (sigaction(SIG, &sa, NULL) != 0)
+     e_debug("EINA DEBUG ERROR: Can't set up sig %i handler!", SIG);
+
+   sa.sa_sigaction = NULL;
+   sa.sa_handler = SIG_IGN;
+   sigemptyset(&sa.sa_mask);
+   sa.sa_flags = 0;
+   if (sigaction(SIGPIPE, &sa, 0) == -1) perror(0);
+}
 
 Eina_Bool
 eina_debug_init(void)
@@ -43,54 +860,46 @@ eina_debug_init(void)
      }
    // mark as initted
    _inited = EINA_TRUE;
+   eina_module_init();
+   eina_mempool_init();
+   eina_list_init();
+   // For Windows support GetModuleFileName can be used
    // set up thread things
    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);
    _eina_debug_thread_add(&self);
-# if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
+#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
    // if we are setuid - don't debug!
    if (getuid() != geteuid()) return EINA_TRUE;
 #endif
-   // if someone uses the EFL_NODEBUG env var - do not do debugging. handy
-   // for when this debug code is buggy itself
-   if (getenv("EFL_NODEBUG")) return EINA_TRUE;
-   // connect to our debug daemon
-   _eina_debug_monitor_service_connect();
-   // if we connected - set up the debug monitor properly
-   if (_eina_debug_monitor_service_fd >= 0)
-     {
-        // say hello to the debug daemon
-        _eina_debug_monitor_service_greet();
-        // set up our profile signal handler
-        _eina_debug_monitor_signal_init();
-        // start the monitor thread
-        _eina_debug_monitor_thread_start();
+   // if someone uses the EFL_NODEBUG env var or disabled debug - do not do
+   // debugging. handy for when this debug code is buggy itself
+
+#ifdef __linux__
+   _my_app_name = __progname;
+#endif
+   if (!getenv("EFL_NODEBUG") && !_debug_disabled)
+     {
+        eina_debug_local_connect(EINA_FALSE);
      }
+   eina_semaphore_new(&_thread_cmd_ready_sem, 0);
+   _signal_init();
+   _eina_debug_cpu_init();
+   _eina_debug_bt_init();
    return EINA_TRUE;
 }
 
 Eina_Bool
 eina_debug_shutdown(void)
 {
+   _eina_debug_bt_shutdown();
+   _eina_debug_cpu_shutdown();
+   eina_semaphore_free(&_thread_cmd_ready_sem);
    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
index cb11565..834927b 100644 (file)
 #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
-
-typedef struct _Eina_Debug_Thread Eina_Debug_Thread;
-
-struct _Eina_Debug_Thread
+# include "eina_config.h"
+# include "eina_list.h"
+
+/**
+ * @page eina_debug_main Eina Debug
+ *
+ * @date 2015 (created)
+ */
+
+/**
+ * @addtogroup Eina_Debug
+ * @{
+ */
+
+#define SERVER_PATH ".edebug"
+#define SERVER_NAME "efl_debug"
+#define SERVER_MASTER_PORT 0
+#define SERVER_SLAVE_PORT 1
+
+typedef enum
 {
-   pthread_t thread;
-#if defined(__clockid_t_defined)
-   struct timespec clok;
-#endif
-   int val;
+   EINA_DEBUG_OK,
+   EINA_DEBUG_ERROR,
+   EINA_DEBUG_AGAIN
+} Eina_Debug_Error;
+
+enum
+{
+   EINA_DEBUG_OPCODE_INVALID = -1, /**< Invalid opcode value */
+   EINA_DEBUG_OPCODE_REGISTER = 0, /**< Opcode used to register other opcodes */
+   EINA_DEBUG_OPCODE_HELLO = 1 /**< Opcode used to send greetings to the daemon */
 };
 
-extern Eina_Spinlock                _eina_debug_lock;
-extern Eina_Spinlock                _eina_debug_thread_lock;
-extern pthread_t                    _eina_debug_thread_mainloop;
-extern Eina_Debug_Thread           *_eina_debug_thread_active;
-extern int                          _eina_debug_thread_active_num;
-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);
-
-int  _eina_debug_monitor_service_send(int fd, const char op[4],
-                                      unsigned char *data, int size);
-void _eina_debug_monitor_service_greet(void);
-int  _eina_debug_monitor_service_read(char *op, unsigned char **data);
-
-#  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
+/**
+ * @typedef Eina_Debug_Session
+ *
+ * A handle used to interact with the debug daemon.
+ * It contains all the information related to this connection and needed
+ * to send/receive/dispatch/...
+ */
+typedef struct _Eina_Debug_Session Eina_Debug_Session;
+
+/**
+ * @typedef Eina_Debug_Cb
+ *
+ * A callback invoked when a specific packet is received.
+ *
+ * @param session the session
+ * @param srcid the source id
+ * @param buffer the packet payload data. It doesn't contain any transport information.
+ * @param size the packet payload size
+ */
+typedef Eina_Debug_Error (*Eina_Debug_Cb)(Eina_Debug_Session *session, int srcid, void *buffer, int size);
+
+/**
+ * @typedef Eina_Debug_Opcode_Status_Cb
+ *
+ * When the opcodes ids are retrieved, this callback is invoked with a true
+ * status.
+ * When a disconnection to the daemon is happening, the opcodes ids are set
+ * as invalid and this callback is invoked with a false status. The upper
+ * layer should not try to send more requests until a new connection is
+ * established.
+ *
+ * @param status EINA_TRUE if opcodes have been received from the daemon, EINA_FALSE otherwise.
+ */
+typedef void (*Eina_Debug_Opcode_Status_Cb)(Eina_Bool status);
+
+/**
+ * @typedef Eina_Debug_Dispatch_Cb
+ *
+ * Dispatcher callback prototype used to override the default dispatcher of a
+ * session.
+ *
+ * @param session the session
+ * @param buffer the packet received
+ *
+ * The given packet is the entire data received, including the header.
+ */
+typedef Eina_Debug_Error (*Eina_Debug_Dispatch_Cb)(Eina_Debug_Session *session, void *buffer);
+
+/**
+ * @typedef Eina_Debug_Timer_Cb
+ *
+ * A callback for a timer
+ */
+typedef Eina_Bool (*Eina_Debug_Timer_Cb)(void *);
+
+/**
+ * @typedef Eina_Debug_Packet_Header
+ *
+ * Header of Eina Debug packet
+ */
+typedef struct
+{
+   int size; /**< Packet size after this element */
+   /**<
+    * During sending, it corresponds to the id of the destination. During reception, it is the id of the source
+    * The daemon is in charge of swapping the id before forwarding the packet to the destination.
+    */
+   int cid;
+   int thread_id;
+   int opcode; /**< Opcode of the packet */
+} Eina_Debug_Packet_Header;
+
+/**
+ * @typedef Eina_Debug_Opcode
+ *
+ * Structure to describe information for an opcode. It is used to register new
+ * opcodes.
+ */
+typedef struct
+{
+   char *opcode_name; /**< Opcode string. On registration, the daemon uses it to calculate an opcode id */
+   int *opcode_id; /**< A pointer to store the opcode id received from the daemon */
+   Eina_Debug_Cb cb; /**< Callback to call when a packet corresponding to the opcode is received */
+} Eina_Debug_Opcode;
+
+/**
+ * @brief Disable debugging
+ *
+ * Useful for applications that don't want debugging. The debug daemon is one
+ * of them.
+ * Need to be invoked before eina_init. Otherwise it can't have any effect.
+ */
+EAPI void eina_debug_disable(void);
+
+/**
+ * @brief Connect to the local daemon
+ *
+ * @param is_master true if the application is a debugger. EINA_FALSE otherwise.
+ *
+ * @return EINA_TRUE on success, EINA_FALSE otherwise.
+ */
+EAPI Eina_Debug_Session *eina_debug_local_connect(Eina_Bool is_master);
+
+/**
+ * @brief Terminate the session
+ *
+ * @param session the session to terminate
+ *
+ */
+EAPI void eina_debug_session_terminate(Eina_Debug_Session *session);
 
+/**
+ * @brief Override the dispatcher of a specific session
+ *
+ * For example, it can be used to forward a packet to the main thread and to
+ * use the default dispatcher there.
+ * All the packets received in this session will use this dispatcher.
+ *
+ * @param session the session
+ * @disp_cb the new dispatcher for the given session
+ */
+EAPI void eina_debug_session_dispatch_override(Eina_Debug_Session *session, Eina_Debug_Dispatch_Cb disp_cb);
+
+/**
+ * @brief Dispatch a given packet according to its header.
+ *
+ * This function checks the header contained into the packet and invokes
+ * the correct callback according to the opcode.
+ * This is the default dispatcher.
+ *
+ * @param session the session
+ * @param buffer the packet
+ *
+ * @return EINA_DEBUG_OK on success, EINA_DEBUG_ERROR if the packet is not as expected.
+ */
+EAPI Eina_Debug_Error eina_debug_dispatch(Eina_Debug_Session *session, void *buffer);
+
+/**
+ * @brief Register opcodes to a session
+ *
+ * This function registers opcodes for the given session. If the session is not
+ * connected, the request is not sent to the daemon. Otherwise, the request for
+ * the opcodes ids is sent.
+ * On the reception from the daemon, status_cb function is invoked to inform
+ * the requester that the opcodes can now be used.
+ */
+EAPI void eina_debug_opcodes_register(Eina_Debug_Session *session,
+      const Eina_Debug_Opcode ops[], Eina_Debug_Opcode_Status_Cb status_cb);
+
+/**
+ * @brief Send a packet to the given destination
+ *
+ * The packet will be treated by the debug thread itself.
+ *
+ * @param session the session to use to send the packet
+ * @param dest_id the destination id to send the packet to
+ * @param op the opcode for this packet
+ * @param data payload to send
+ * @param size payload size
+ *
+ * @return the number of sent bytes
+ */
+EAPI int eina_debug_session_send(Eina_Debug_Session *session, int dest_id, int op, void *data, int size);
+
+/**
+ * @brief Send a packet to the given thread of the given destination
+ *
+ * If the thread is 0x0, the packet will be treated by the debug thread itself.
+ * If the thread is 0xFF..FF, the packet will be broadcasted to all the threads.
+ * Otherwise, the packet will be treated by the specific thread.
+ *
+ * @param session the session to use to send the packet
+ * @param dest_id the destination id to send the packet to
+ * @param thread_id the thread to send the packet to.
+ * @param op the opcode for this packet
+ * @param data payload to send
+ * @param size payload size
+ *
+ * @return the number of sent bytes
+ */
+EAPI int eina_debug_session_send_to_thread(Eina_Debug_Session *session, int dest_id, int thread_id, int op, void *data, int size);
+
+/**
+ * @brief Add a timer
+ *
+ * Needed for polling debug
+ *
+ * @param timeout_ms timeout in ms
+ * @param cb callback to call when the timeout is reached
+ * @param data user data
+ *
+ * @return EINA_TRUE on success, EINA_FALSE otherwise
+ */
+EAPI Eina_Bool eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data);
+
+EAPI int eina_debug_thread_id_get(void);
 #endif
+/**
+ * @}
+ */
index ac04e94..68083e2 100644 (file)
  * if not, see <http://www.gnu.org/licenses/>.
  */
 
+# ifdef HAVE_CONFIG_H
+#  include "config.h"
+# endif
+
+#ifdef HAVE_DLADDR
+# include <dlfcn.h>
+#endif
+#include <libunwind.h>
+
 #include "eina_debug.h"
+#include "eina_debug_private.h"
+
+static Eina_Semaphore _wait_for_bts_sem;
 
-#ifdef EINA_HAVE_DEBUG
+// _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;
+
+/* Used by trace timer */
+static double _trace_t0 = 0.0;
+
+static int _prof_get_op = EINA_DEBUG_OPCODE_INVALID;
 
 void
 _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
@@ -46,4 +67,220 @@ _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
         else fprintf(f, "??\t -\n");
      }
 }
+
+// a backtracer that uses libunwind to do the job
+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;
+
+   // create a context for unwinding
+   unw_getcontext(&uc);
+   // begin our work
+   unw_init_local(&cursor, &uc);
+   // walk up each stack frame until there is no more, storing it
+   for (total = 0; (unw_step(&cursor) > 0) && (total < max); total++)
+     {
+        unw_get_reg(&cursor, UNW_REG_IP, &p);
+        bt[total] = (void *)p;
+     }
+   // return our total backtrace stack size
+   return total;
+}
+
+// this signal handler is called inside each and every thread when the
+// thread gets a signal via pthread_kill(). this causes the thread to
+// stop here inside this handler and "do something" then when this returns
+// resume whatever it was doing like any signal handler
+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;
+
+   // find which slot in the array of threads we have so we store info
+   // in the correct slot for us
+   if (self != _eina_debug_thread_mainloop)
+     {
+        for (i = 0; i < _eina_debug_thread_active_num; i++)
+          {
+             if (self == _eina_debug_thread_active[i].thread)
+               {
+                  slot = i + 1;
+                  goto found;
+               }
+          }
+        // we couldn't find out thread reference! help!
+        e_debug("EINA DEBUG ERROR: can't find thread slot!");
+        eina_semaphore_release(&_wait_for_bts_sem, 1);
+        return;
+     }
+found:
+   // store thread info like what cpu core we are on now (not reliable
+   // but hey - better than nothing), the amount of cpu time total
+   // we have consumed (it's cumulative so subtracing deltas can give
+   // you an average amount of cpu time consumed between now and the
+   // previous time we looked) and also a full backtrace
+   _bt_cpu[slot] = sched_getcpu();
+   pthread_getcpuclockid(self, &cid);
+   clock_gettime(cid, &(_bt_ts[slot]));
+   _bt_buf_len[slot] = _eina_debug_unwind_bt(_bt_buf[slot], EINA_MAX_BT);
+   // now wake up the monitor to let them know we are done collecting our
+   // backtrace info
+   eina_semaphore_release(&_wait_for_bts_sem, 1);
+}
+
+
+// a quick and dirty local time point getter func - not portable
+static inline double
+get_time(void)
+{
+#if defined(__clockid_t_defined)
+   struct timespec t;
+   clock_gettime(CLOCK_MONOTONIC, &t);
+   return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0);
+#else
+   struct timeval timev;
+   gettimeofday(&timev, NULL);
+   return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
 #endif
+}
+
+static void
+_eina_debug_collect_bt(pthread_t pth EINA_UNUSED)
+{
+   // 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);
+}
+
+static Eina_Bool
+_trace_cb(void *data)
+{
+   static Eina_Debug_Packet_Header *hdr = NULL;
+
+   if (!hdr)
+     {
+        hdr = calloc(1, sizeof(*hdr));
+        hdr->size = sizeof(Eina_Debug_Packet_Header);
+        hdr->thread_id = 0xFFFFFFFF;
+        hdr->opcode = _prof_get_op;
+     }
+
+   eina_debug_dispatch(data, (void *)hdr);
+   return EINA_TRUE;
+}
+
+static Eina_Debug_Error
+_prof_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
+{
+   static int bts = 0;
+   int i;
+   if (!_trace_t0) _trace_t0 = get_time();
+   // 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].thread);
+   // 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(&_wait_for_bts_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
+   //// XXX: some debug so we can see the bt's we collect - will go
+   //                  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);
+   //// XXX: some debug just to see how well we perform - will go
+   bts++;
+   if (bts >= 10000)
+     {
+        double t;
+        t = get_time();
+        e_debug("%1.5f bt's per sec", (double)bts / (t - _trace_t0));
+        _trace_t0 = t;
+        bts = 0;
+     }
+   return EINA_DEBUG_OK;
+}
+
+// profiling on with poll time gap as uint payload
+static Eina_Debug_Error
+_prof_on_cb(Eina_Debug_Session *session, int cid EINA_UNUSED, void *buffer, int size)
+{
+   unsigned int time;
+   if (size >= 4)
+     {
+        memcpy(&time, buffer, 4);
+        _trace_t0 = 0.0;
+        eina_debug_timer_add(time, _trace_cb, session);
+     }
+   return EINA_DEBUG_OK;
+}
+
+static Eina_Debug_Error
+_prof_off_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
+{
+   eina_debug_timer_add(0, NULL, NULL);
+   return EINA_DEBUG_OK;
+}
+
+static const Eina_Debug_Opcode _OPS[] = {
+       {"profiler/on", NULL, &_prof_on_cb},
+       {"profiler/off", NULL, &_prof_off_cb},
+       {"profiler/bt_get", &_prof_get_op, &_prof_get_cb},
+       {NULL, NULL, NULL}
+};
+
+Eina_Bool
+_eina_debug_bt_init(void)
+{
+   eina_semaphore_new(&_wait_for_bts_sem, 0);
+   eina_debug_opcodes_register(NULL, _OPS, NULL);
+   return EINA_TRUE;
+}
+
+Eina_Bool
+_eina_debug_bt_shutdown(void)
+{
+   eina_semaphore_free(&_wait_for_bts_sem);
+   return EINA_TRUE;
+}
index 66636e3..081c9f8 100644 (file)
  * if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "eina_debug.h"
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
-#ifdef EINA_HAVE_DEBUG
+#include "eina_debug_private.h"
+
+extern Eina_Spinlock _eina_debug_lock;
 
 static unsigned int _table_num = 0;
 static unsigned int _table_size = 0;
@@ -164,4 +169,3 @@ _eina_debug_file_get(const char *fname)
      }
    return file;
 }
-#endif
index de98746..3662919 100644 (file)
  * if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "eina_debug.h"
+#include <string.h>
 
-#ifdef EINA_HAVE_DEBUG
+#include "eina_debug.h"
 
-# ifdef HAVE_MMAP
-#  include <sys/mman.h>
+# ifdef HAVE_CONFIG_H
+#  include "config.h"
 # endif
 
+#ifdef HAVE_MMAP
+# include <sys/mman.h>
+
 // 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;
@@ -243,4 +245,3 @@ _eina_debug_chunk_strdup(const char *str)
    strcpy(s, str);
    return s;
 }
-#endif
diff --git a/src/lib/eina/eina_debug_cpu.c b/src/lib/eina/eina_debug_cpu.c
new file mode 100644 (file)
index 0000000..e864592
--- /dev/null
@@ -0,0 +1,307 @@
+# ifndef _GNU_SOURCE
+#  define _GNU_SOURCE 1
+# endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "eina_debug.h"
+#include "eina_types.h"
+#include "eina_list.h"
+#include "eina_mempool.h"
+#include "eina_util.h"
+#include "eina_evlog.h"
+#include "eina_debug_private.h"
+
+volatile int           _eina_debug_sysmon_reset = 0;
+volatile int           _eina_debug_sysmon_active = 0;
+volatile int           _eina_debug_evlog_active = 0;
+
+static Eina_Lock       _sysmon_lock;
+
+static Eina_Bool       _sysmon_thread_runs = EINA_FALSE;
+static pthread_t       _sysmon_thread;
+
+// this is a DEDICATED thread tojust collect system info and to have the
+// least impact it can on a cpu core or system. all this does right now
+// is sleep and wait for a command to begin polling for the cpu state.
+// right now that means iterating through cpu's and getting their cpu
+// frequency to match up with event logs.
+static void *
+_sysmon(void *_data EINA_UNUSED)
+{
+   static int cpufreqs[64] = { 0 };
+   int i, fd, freq;
+   char buf[256], path[256];
+   ssize_t red;
+#if defined(__clockid_t_defined)
+   static struct timespec t_last = { 0, 0 };
+   static Eina_Debug_Thread *prev_threads = NULL;
+   static int prev_threads_num = 0;
+   int j, cpu;
+   Eina_Bool prev_threads_redo;
+   clockid_t cid;
+   struct timespec t, t_now;
+   unsigned long long tim_span, tim1, tim2;
+#endif
+
+   // set a name for this thread for system debugging
+#ifdef EINA_HAVE_PTHREAD_SETNAME
+# ifndef __linux__
+   pthread_set_name_np
+# else
+      pthread_setname_np
+# endif
+      (pthread_self(), "Edbg-sys");
+#endif
+   for (;;)
+     {
+        // wait on a mutex that will be locked for as long as this
+        // threead is not meant to go running
+        eina_lock_take(&_sysmon_lock);
+        // if we need to reset as we just started polling system stats...
+        if (_eina_debug_sysmon_reset)
+          {
+             _eina_debug_sysmon_reset = 0;
+             // clear out all the clocks for threads
+#if defined(__clockid_t_defined)
+             // reset the last clock timestamp when we checked to "now"
+             clock_gettime(CLOCK_MONOTONIC, &t);
+             t_last = t;
+             // walk over all threads
+             eina_spinlock_take(&_eina_debug_thread_lock);
+             for (i = 0; i < _eina_debug_thread_active_num; i++)
+               {
+                  // get the correct clock id to use for the target thread
+                  pthread_getcpuclockid
+                     (_eina_debug_thread_active[i].thread, &cid);
+                  // get the clock cpu time accumulation for that threas
+                  clock_gettime(cid, &t);
+                  _eina_debug_thread_active[i].clok = t;
+               }
+             eina_spinlock_release(&_eina_debug_thread_lock);
+#endif
+             // clear all the cpu freq (up to 64 cores) to 0
+             for (i = 0; i < 64; i++) cpufreqs[i] = 0;
+          }
+        eina_lock_release(&_sysmon_lock);
+
+#if defined(__clockid_t_defined)
+        // get the current time
+        clock_gettime(CLOCK_MONOTONIC, &t_now);
+        tim1 = (t_last.tv_sec * 1000000000LL) + t_last.tv_nsec;
+        // the time span between now and last time we checked
+        tim_span = ((t_now.tv_sec * 1000000000LL) + t_now.tv_nsec) - tim1;
+        // if the time span is non-zero we might get sensible results
+        if (tim_span > 0)
+          {
+             prev_threads_redo = EINA_FALSE;
+             eina_spinlock_take(&_eina_debug_thread_lock);
+             // figure out if the list of thread id's has changed since
+             // our last poll. this is imporant as we need to set the
+             // thread cpu usage to 0 for threads that have disappeared
+             if (prev_threads_num != _eina_debug_thread_active_num)
+               prev_threads_redo = EINA_TRUE;
+             else
+               {
+                  // XXX: isolate this out of hot path
+                  for (i = 0; i < _eina_debug_thread_active_num; i++)
+                    {
+                       if (_eina_debug_thread_active[i].thread !=
+                           prev_threads[i].thread)
+                         {
+                            prev_threads_redo = EINA_TRUE;
+                            break;
+                         }
+                    }
+               }
+             for (i = 0; i < _eina_debug_thread_active_num; i++)
+               {
+                  pthread_t thread = _eina_debug_thread_active[i].thread;
+                  // get the clock for the thread and its cpu time usage
+                  pthread_getcpuclockid(thread, &cid);
+                  clock_gettime(cid, &t);
+                  // calculate a long timestamp (64bits)
+                  tim1 = (_eina_debug_thread_active[i].clok.tv_sec * 1000000000LL) +
+                          _eina_debug_thread_active[i].clok.tv_nsec;
+                  // and get the difference in clock time in NS
+                  tim2 = ((t.tv_sec * 1000000000LL) + t.tv_nsec) - tim1;
+                  // and that as percentage of the timespan
+                  cpu = (int)((100 * (int)tim2) / (int)tim_span);
+                  // round to the nearest 10 percent - rough anyway
+                  cpu = ((cpu + 5) / 10) * 10;
+                  if (cpu > 100) cpu = 100;
+                  // if the usage changed since last time we checked...
+                  if (cpu != _eina_debug_thread_active[i].val)
+                    {
+                       // log this change
+                       snprintf(buf, sizeof(buf), "*CPUUSED %llu",
+                                (unsigned long long)thread);
+                       snprintf(path, sizeof(path), "%i", _eina_debug_thread_active[i].val);
+                       eina_evlog(buf, NULL, 0.0, path);
+                       snprintf(path, sizeof(path), "%i", cpu);
+                       eina_evlog(buf, NULL, 0.0, path);
+                       // store the clock time + cpu we got for next poll
+                       _eina_debug_thread_active[i].val = cpu;
+                    }
+                  _eina_debug_thread_active[i].clok = t;
+               }
+             // so threads changed between this poll and last so we need
+             // to redo our mapping/storage of them
+             if (prev_threads_redo)
+               {
+                  prev_threads_redo = EINA_FALSE;
+                  // find any threads from our last run that do not
+                  // exist now in our new list of threads
+                  for (j = 0; j < prev_threads_num; j++)
+                    {
+                       for (i = 0; i < _eina_debug_thread_active_num; i++)
+                         {
+                            if (prev_threads[j].thread ==
+                                _eina_debug_thread_active[i].thread) break;
+                         }
+                       // thread was there before and not now
+                       if (i == _eina_debug_thread_active_num)
+                         {
+                            // log it finishing - ie 0
+                            snprintf(buf, sizeof(buf), "*CPUUSED %llu",
+                                     (unsigned long long)
+                                     prev_threads[i].thread);
+                            eina_evlog(buf, NULL, 0.0, "0");
+                         }
+                    }
+                  // if the thread count changed then allocate a new shadow
+                  // buffer of thread id's etc.
+                  if (prev_threads_num != _eina_debug_thread_active_num)
+                    {
+                       if (prev_threads) free(prev_threads);
+                       prev_threads_num = _eina_debug_thread_active_num;
+                       prev_threads = malloc(prev_threads_num *
+                                             sizeof(Eina_Debug_Thread));
+                    }
+                  // store the thread handles/id's
+                  for (i = 0; i < prev_threads_num; i++)
+                    prev_threads[i].thread =
+                    _eina_debug_thread_active[i].thread;
+               }
+             eina_spinlock_release(&_eina_debug_thread_lock);
+             t_last = t_now;
+          }
+#endif
+        // poll up to 64 cpu cores for cpufreq info to log alongside
+        // the evlog call data
+        for (i = 0; i < 64; i++)
+          {
+             // look at sysfs nodes per cpu
+             snprintf
+               (buf, sizeof(buf),
+                "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq", i);
+             fd = open(buf, O_RDONLY);
+             freq = 0;
+             // if the node is there, read it
+             if (fd >= 0)
+               {
+                  // really low overhead read from cpufreq node (just an int)
+                  red = read(fd, buf, sizeof(buf) - 1);
+                  if (red > 0)
+                    {
+                       // read something - it should be an int with whitespace
+                       buf[red] = 0;
+                       freq = atoi(buf);
+                       // move to mhz
+                       freq = (freq + 500) / 1000;
+                       // round mhz to the nearest 100mhz - to have less noise
+                       freq = ((freq + 50) / 100) * 100;
+                    }
+                  // close the fd so we can freshly poll next time around
+                  close(fd);
+               }
+             // node not there - ran out of cpu's to poll?
+             else break;
+             // if the current frequency changed vs previous poll, then log
+             if (freq != cpufreqs[i])
+               {
+                  snprintf(buf, sizeof(buf), "*CPUFREQ %i", i);
+                  snprintf(path, sizeof(path), "%i", freq);
+                  eina_evlog(buf, NULL, 0.0, path);
+                  cpufreqs[i] = freq;
+               }
+          }
+        usleep(1000); // 1ms sleep
+     }
+   return NULL;
+}
+
+static Eina_Debug_Error
+_cpufreq_on_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
+{
+   if (!_eina_debug_evlog_active)
+     {
+        _eina_debug_evlog_active = 1;
+        eina_evlog_start();
+     }
+   if (!_eina_debug_sysmon_active)
+     {
+        _eina_debug_sysmon_reset = 1;
+        _eina_debug_sysmon_active = 1;
+        eina_lock_release(&_sysmon_lock);
+     }
+   return EINA_DEBUG_OK;
+}
+
+static Eina_Debug_Error
+_cpufreq_off_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
+{
+   if (_eina_debug_sysmon_active)
+     {
+        eina_lock_take(&_sysmon_lock);
+        _eina_debug_sysmon_active = 0;
+     }
+   if (_eina_debug_evlog_active)
+     {
+        eina_evlog_stop();
+        _eina_debug_evlog_active = 0;
+     }
+   return EINA_DEBUG_OK;
+}
+
+static const Eina_Debug_Opcode _OPS[] = {
+       {"cpufreq/on", NULL, &_cpufreq_on_cb},
+       {"cpufreq/off", NULL, &_cpufreq_off_cb},
+       {NULL, NULL, NULL}
+};
+
+Eina_Bool
+_eina_debug_cpu_init(void)
+{
+   // if it's already running - we're good.
+   if (!_sysmon_thread_runs)
+     {
+        int err;
+        eina_lock_new(&_sysmon_lock);
+        eina_lock_take(&_sysmon_lock);
+        err = pthread_create(&_sysmon_thread, NULL, _sysmon, NULL);
+        if (err != 0)
+          {
+             e_debug("EINA DEBUG ERROR: Can't create debug sysmon thread!");
+             abort();
+          }
+        _sysmon_thread_runs = EINA_TRUE;
+     }
+   eina_debug_opcodes_register(NULL, _OPS, NULL);
+   return EINA_TRUE;
+}
+
+Eina_Bool
+_eina_debug_cpu_shutdown(void)
+{
+   return EINA_TRUE;
+}
+
diff --git a/src/lib/eina/eina_debug_monitor.c b/src/lib/eina/eina_debug_monitor.c
deleted file mode 100644 (file)
index b2ae5a4..0000000
+++ /dev/null
@@ -1,708 +0,0 @@
-/* EINA - EFL data type library
- * Copyright (C) 2015 Carsten Haitzler
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library;
- * if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "eina_debug.h"
-#include "eina_types.h"
-#include "eina_evlog.h"
-#include "eina_util.h"
-#include "eina_thread.h"
-#include "eina_file.h"
-#include <signal.h>
-
-#ifdef EINA_HAVE_DEBUG
-
-#define DEBUG_SERVER ".ecore/efl_debug/0"
-
-volatile int           _eina_debug_sysmon_reset = 0;
-volatile int           _eina_debug_evlog_active = 0;
-
-int                    _eina_debug_monitor_service_fd = -1;
-Eina_Semaphore         _eina_debug_monitor_return_sem;
-Eina_Lock              _eina_debug_sysmon_lock;
-
-static Eina_Bool       _monitor_thread_runs = EINA_FALSE;
-static pthread_t       _monitor_thread;
-static pthread_t       _sysmon_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;
-
-// a backtracer that uses libunwind to do the job
-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;
-
-   // create a context for unwinding
-   unw_getcontext(&uc);
-   // begin our work
-   unw_init_local(&cursor, &uc);
-   // walk up each stack frame until there is no more, storing it
-   for (total = 0; (unw_step(&cursor) > 0) && (total < max); total++)
-     {
-        unw_get_reg(&cursor, UNW_REG_IP, &p);
-        bt[total] = (void *)p;
-     }
-   // return our total backtrace stack size
-   return total;
-}
-
-static inline void
-_bt_cpu_set(int slot)
-{
-#if HAVE_SCHED_GETCPU
-   _bt_cpu[slot] = sched_getcpu();
-#else
-   _bt_cpu[slot] = -1;
-#endif
-}
-
-static inline void
-_bt_ts_set(int slot, pthread_t self)
-{
-#if defined(__clockid_t_defined)
-   clockid_t cid;
-   pthread_getcpuclockid(self, &cid);
-   clock_gettime(cid, &(_bt_ts[slot]));
-#else
-   (void) self;
-   memset(&(_bt_ts[slot]), 0, sizeof(struct timespec));
-#endif
-}
-
-// this signal handler is called inside each and every thread when the
-// thread gets a signal via pthread_kill(). this causes the thread to
-// stop here inside this handler and "do something" then when this returns
-// resume whatever it was doing like any signal handler
-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();
-
-   // find which slot in the array of threads we have so we store info
-   // in the correct slot for us
-   if (self != _eina_debug_thread_mainloop)
-     {
-        for (i = 0; i < _eina_debug_thread_active_num; i++)
-          {
-             if (self == _eina_debug_thread_active[i].thread)
-               {
-                  slot = i + 1;
-                  goto found;
-               }
-          }
-        // we couldn't find out thread reference! help!
-        fprintf(stderr, "EINA DEBUG ERROR: can't find thread slot!\n");
-        eina_semaphore_release(&_eina_debug_monitor_return_sem, 1);
-        return;
-     }
-found:
-   // store thread info like what cpu core we are on now (not reliable
-   // but hey - better than nothing), the amount of cpu time total
-   // we have consumed (it's cumulative so subtracing deltas can give
-   // you an average amount of cpu time consumed between now and the
-   // previous time we looked) and also a full backtrace
-   _bt_cpu_set(slot);
-   _bt_ts_set(slot, self);
-   _bt_buf_len[slot] = _eina_debug_unwind_bt(_bt_buf[slot], EINA_MAX_BT);
-   // now wake up the monitor to let them know we are done collecting our
-   // backtrace info
-   eina_semaphore_release(&_eina_debug_monitor_return_sem, 1);
-}
-
-// we shall sue SIGPROF as out signal for pausing threads and having them
-// dump a backtrace for polling based profiling
-#define SIG SIGPROF
-
-// a quick and dirty local time point getter func - not portable
-static inline double
-get_time(void)
-{
-#if defined(__clockid_t_defined)
-   struct timespec t;
-   clock_gettime(CLOCK_MONOTONIC, &t);
-   return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0);
-#else
-   struct timeval timev;
-   gettimeofday(&timev, NULL);
-   return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
-#endif
-}
-
-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 thread tojust collect system info and to have the
-// least impact it can on a cpu core or system. all this does right now
-// is sleep and wait for a command to begin polling for the cpu state.
-// right now that means iterating through cpu's and getting their cpu
-// frequency to match up with event logs.
-static void *
-_eina_debug_sysmon(void *_data EINA_UNUSED)
-{
-   static int cpufreqs[64] = { 0 };
-   int i, fd, freq;
-   char buf[256], path[256];
-   ssize_t red;
-#if defined(__clockid_t_defined)
-   static struct timespec t_last = { 0, 0 };
-   static Eina_Debug_Thread *prev_threads = NULL;
-   static int prev_threads_num = 0;
-   int j, cpu;
-   Eina_Bool prev_threads_redo;
-   clockid_t cid;
-   struct timespec t, t_now;
-   unsigned long long tim_span, tim1, tim2;
-#endif
-
-   // set a name for this thread for system debugging
-#ifdef EINA_HAVE_PTHREAD_SETNAME
-# ifndef __linux__
-   pthread_set_name_np
-# else
-   pthread_setname_np
-# endif
-     (pthread_self(), "Edbg-sys");
-#endif
-   for (;;)
-     {
-        // wait on a mutex that will be locked for as long as this
-        // threead is not meant to go running
-        eina_lock_take(&_eina_debug_sysmon_lock);
-        // if we need to reset as we just started polling system stats...
-        if (_eina_debug_sysmon_reset)
-          {
-             _eina_debug_sysmon_reset = 0;
-             // clear out all the clocks for threads
-#if defined(__clockid_t_defined)
-             // reset the last clock timestamp when we checked to "now"
-             clock_gettime(CLOCK_MONOTONIC, &t);
-             t_last = t;
-             // walk over all threads
-             eina_spinlock_take(&_eina_debug_thread_lock);
-             for (i = 0; i < _eina_debug_thread_active_num; i++)
-               {
-                  // get the correct clock id to use for the target thread
-                  pthread_getcpuclockid
-                    (_eina_debug_thread_active[i].thread, &cid);
-                  // get the clock cpu time accumulation for that threas
-                  clock_gettime(cid, &t);
-                  _eina_debug_thread_active[i].clok = t;
-               }
-             eina_spinlock_release(&_eina_debug_thread_lock);
-#endif
-             // clear all the cpu freq (up to 64 cores) to 0
-             for (i = 0; i < 64; i++) cpufreqs[i] = 0;
-          }
-        eina_lock_release(&_eina_debug_sysmon_lock);
-
-#if defined(__clockid_t_defined)
-        // get the current time
-        clock_gettime(CLOCK_MONOTONIC, &t_now);
-        tim1 = (t_last.tv_sec * 1000000000LL) + t_last.tv_nsec;
-        // the time span between now and last time we checked
-        tim_span = ((t_now.tv_sec * 1000000000LL) + t_now.tv_nsec) - tim1;
-        // if the time span is non-zero we might get sensible results
-        if (tim_span > 0)
-          {
-             prev_threads_redo = EINA_FALSE;
-             eina_spinlock_take(&_eina_debug_thread_lock);
-             // figure out if the list of thread id's has changed since
-             // our last poll. this is imporant as we need to set the
-             // thread cpu usage to 0 for threads that have disappeared
-             if (prev_threads_num != _eina_debug_thread_active_num)
-               prev_threads_redo = EINA_TRUE;
-             else
-               {
-                  // XXX: isolate this out of hot path
-                  for (i = 0; i < _eina_debug_thread_active_num; i++)
-                    {
-                       if (_eina_debug_thread_active[i].thread !=
-                           prev_threads[i].thread)
-                         {
-                            prev_threads_redo = EINA_TRUE;
-                            break;
-                         }
-                    }
-               }
-             for (i = 0; i < _eina_debug_thread_active_num; i++)
-               {
-                  pthread_t thread = _eina_debug_thread_active[i].thread;
-                  // get the clock for the thread and its cpu time usage
-                  pthread_getcpuclockid(thread, &cid);
-                  clock_gettime(cid, &t);
-                  // calculate a long timestamp (64bits)
-                  tim1 = (_eina_debug_thread_active[i].clok.tv_sec * 1000000000LL) +
-                          _eina_debug_thread_active[i].clok.tv_nsec;
-                  // and get the difference in clock time in NS
-                  tim2 = ((t.tv_sec * 1000000000LL) + t.tv_nsec) - tim1;
-                  // and that as percentage of the timespan
-                  cpu = (int)((100 * (int)tim2) / (int)tim_span);
-                  // round to the nearest 10 percent - rough anyway
-                  cpu = ((cpu + 5) / 10) * 10;
-                  if (cpu > 100) cpu = 100;
-                  // if the usage changed since last time we checked...
-                  if (cpu != _eina_debug_thread_active[i].val)
-                    {
-                       // log this change
-                       snprintf(buf, sizeof(buf), "*CPUUSED %llu",
-                                (unsigned long long)thread);
-                       snprintf(path, sizeof(path), "%i", _eina_debug_thread_active[i].val);
-                       eina_evlog(buf, NULL, 0.0, path);
-                       snprintf(path, sizeof(path), "%i", cpu);
-                       eina_evlog(buf, NULL, 0.0, path);
-                       // store the clock time + cpu we got for next poll
-                       _eina_debug_thread_active[i].val = cpu;
-                    }
-                  _eina_debug_thread_active[i].clok = t;
-               }
-             // so threads changed between this poll and last so we need
-             // to redo our mapping/storage of them
-             if (prev_threads_redo)
-               {
-                  prev_threads_redo = EINA_FALSE;
-                  // find any threads from our last run that do not
-                  // exist now in our new list of threads
-                  for (j = 0; j < prev_threads_num; j++)
-                    {
-                       for (i = 0; i < _eina_debug_thread_active_num; i++)
-                         {
-                            if (prev_threads[j].thread ==
-                                _eina_debug_thread_active[i].thread) break;
-                         }
-                       // thread was there before and not now
-                       if (i == _eina_debug_thread_active_num)
-                         {
-                            // log it finishing - ie 0
-                            snprintf(buf, sizeof(buf), "*CPUUSED %llu",
-                                     (unsigned long long)
-                                     prev_threads[i].thread);
-                            eina_evlog(buf, NULL, 0.0, "0");
-                         }
-                    }
-                  // if the thread count changed then allocate a new shadow
-                  // buffer of thread id's etc.
-                  if (prev_threads_num != _eina_debug_thread_active_num)
-                    {
-                       if (prev_threads) free(prev_threads);
-                       prev_threads_num = _eina_debug_thread_active_num;
-                       prev_threads = malloc(prev_threads_num *
-                                             sizeof(Eina_Debug_Thread));
-                    }
-                  // store the thread handles/id's
-                  for (i = 0; i < prev_threads_num; i++)
-                    prev_threads[i].thread =
-                    _eina_debug_thread_active[i].thread;
-               }
-             eina_spinlock_release(&_eina_debug_thread_lock);
-             t_last = t_now;
-          }
-#endif
-        // poll up to 64 cpu cores for cpufreq info to log alongside
-        // the evlog call data
-        for (i = 0; i < 64; i++)
-          {
-             // look at sysfs nodes per cpu
-             snprintf
-               (buf, sizeof(buf),
-                "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq", i);
-             fd = open(buf, O_RDONLY);
-             freq = 0;
-             // if the node is there, read it
-             if (fd >= 0)
-               {
-                  // really low overhead read from cpufreq node (just an int)
-                  red = read(fd, buf, sizeof(buf) - 1);
-                  if (red > 0)
-                    {
-                       // read something - it should be an int with whitespace
-                       buf[red] = 0;
-                       freq = atoi(buf);
-                       // move to mhz
-                       freq = (freq + 500) / 1000;
-                       // round mhz to the nearest 100mhz - to have less noise
-                       freq = ((freq + 50) / 100) * 100;
-                    }
-                  // close the fd so we can freshly poll next time around
-                  close(fd);
-               }
-             // node not there - ran out of cpu's to poll?
-             else break;
-             // if the current frequency changed vs previous poll, then log
-             if (freq != cpufreqs[i])
-               {
-                  snprintf(buf, sizeof(buf), "*CPUFREQ %i", i);
-                  snprintf(path, sizeof(path), "%i", freq);
-                  eina_evlog(buf, NULL, 0.0, path);
-                  cpufreqs[i] = freq;
-               }
-          }
-        usleep(1000); // 1ms sleep
-     }
-   return NULL;
-}
-
-// 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, 0 };
-   // some state for debugging
-   unsigned int poll_time = 1000;
-   Eina_Bool poll_on = EINA_FALSE;
-   Eina_Bool poll_trace = EINA_FALSE;
-   Eina_Bool poll_cpu = EINA_FALSE;
-
-   t0 = get_time();
-   // set a name for this thread for system debugging
-#ifdef EINA_HAVE_PTHREAD_SETNAME
-# ifndef __linux__
-   pthread_set_name_np
-# else
-   pthread_setname_np
-# endif
-     (pthread_self(), "Edbg-mon");
-#endif
-   // sit forever processing commands or timeouts in the debug monitor
-   // thread - this is separate to the rest of the app so it shouldn't
-   // impact the application specifically
-   for (;;)
-     {
-        int i;
-
-        // set up data for select like read fd's
-        FD_ZERO(&rfds);
-        FD_ZERO(&wfds);
-        FD_ZERO(&exfds);
-        // the only fd we care about - out debug daemon connection
-        FD_SET(_eina_debug_monitor_service_fd, &rfds);
-        max_fd = _eina_debug_monitor_service_fd;
-        // if we are in a polling mode then set up a timeout and wait for it
-        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);
-          }
-        // we have no timeout - so wait forever for a message from debugd
-        else ret = select(max_fd + 1, &rfds, &wfds, &exfds, NULL);
-        // if the fd for debug daemon says it's alive, process it
-        if ((ret == 1) && (FD_ISSET(_eina_debug_monitor_service_fd, &rfds)))
-          {
-             // collect a single op on the debug daemon control fd
-             char op[5];
-             int size;
-             unsigned char *data;
-
-             // get the opcode and stor in op - guarantee its 0 byte terminated
-             data = NULL;
-             size = _eina_debug_monitor_service_read(op, &data);
-             // if not negative - we have a real message
-             if (size >= 0)
-               {
-                  // profiling on with poll time gap as uint payload
-                  if (!strcmp(op, "PLON"))
-                    {
-                       if (size >= 4) memcpy(&poll_time, data, 4);
-                       poll_on = EINA_TRUE;
-                       poll_trace = EINA_TRUE;
-                    }
-                  // profiling off with no payload
-                  else if (!strcmp(op, "PLOF"))
-                    {
-                       poll_time = 1000;
-                       poll_on = EINA_FALSE;
-                       poll_trace = EINA_FALSE;
-                    }
-                  // enable evlog
-                  else if (!strcmp(op, "EVON"))
-                    {
-                       if (!_eina_debug_evlog_active)
-                         {
-                            _eina_debug_evlog_active = 1;
-                            eina_evlog_start();
-                            _eina_debug_sysmon_reset = 1;
-                            eina_lock_release(&_eina_debug_sysmon_lock);
-                         }
-                    }
-                  // stop evlog
-                  else if (!strcmp(op, "EVOF"))
-                    {
-                       if (_eina_debug_evlog_active)
-                         {
-                            eina_lock_take(&_eina_debug_sysmon_lock);
-                            eina_evlog_stop();
-                            _eina_debug_evlog_active = 0;
-                         }
-                    }
-                  // enable evlog
-                  else if (!strcmp(op, "CPON"))
-                    {
-                       if (size >= 4) memcpy(&poll_time, data, 4);
-                       poll_on = EINA_TRUE;
-                       poll_cpu = EINA_TRUE;
-                    }
-                  // stop evlog
-                  else if (!strcmp(op, "CPOF"))
-                    {
-                       poll_on = EINA_FALSE;
-                       poll_cpu = EINA_FALSE;
-                    }
-                  // fetch the evlog
-                  else if (!strcmp(op, "EVLG"))
-                    {
-                       Eina_Evlog_Buf *evlog = eina_evlog_steal();
-                       if ((evlog) && (evlog->buf))
-                         {
-                            char          tmp[12];
-                            unsigned int *tmpsize  = (unsigned int *)(tmp + 0);
-                            char         *op2 = "EVLG";
-                            unsigned int *overflow = (unsigned int *)(tmp + 8);
-
-                            *tmpsize = (sizeof(tmp) - 4) + evlog->top;
-                            memcpy(tmp + 4, op2, 4);
-                            *overflow = evlog->overflow;
-                            write(_eina_debug_monitor_service_fd,
-                                  tmp, sizeof(tmp));
-                            write(_eina_debug_monitor_service_fd,
-                                  evlog->buf, evlog->top);
-                         }
-                    }
-                  // something we don't understand
-                  else fprintf(stderr,
-                               "EINA DEBUG ERROR: "
-                               "Uunknown command %s\n", op);
-                  free(data);
-               }
-             // major failure on debug daemon control fd - get out of here
-             else goto fail;
-          }
-
-        if (poll_on)
-          {
-             if (poll_trace)
-               {
-                  // 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].thread);
-                  // 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
-//// XXX: some debug so we can see the bt's we collect - will go
-//                  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);
-//// XXX: some debug just to see how well we perform - will go
-                  bts++;
-                  if (bts >= 10000)
-                    {
-                       t = get_time();
-                       fprintf(stderr, "%1.5f bt's per sec\n", (double)bts / (t - t0));
-                       t0 = t;
-                       bts = 0;
-                    }
-               }
-             if (poll_cpu)
-               {
-                  // XXX: opendir /proc/sefl/task
-//                  eina_evlog("*cpustat", NULL, 0.0, cpustat);
-               }
-          }
-     }
-fail:
-   // we failed - get out of here and disconnect to debugd
-   close(_eina_debug_monitor_service_fd);
-   _eina_debug_monitor_service_fd = -1;
-   // sleep forever because there is currently no logic to join this thread
-   for (;;) pause();
-   return NULL;
-}
-
-// start up the debug monitor if we haven't already
-void
-_eina_debug_monitor_thread_start(void)
-{
-   int err;
-   sigset_t oldset, newset;
-
-   // if it's already running - we're good.
-   if (_monitor_thread_runs) return;
-   // create debug monitor thread
-   sigemptyset(&newset);
-   sigaddset(&newset, SIGPIPE);
-   sigaddset(&newset, SIGALRM);
-   sigaddset(&newset, SIGCHLD);
-   sigaddset(&newset, SIGUSR1);
-   sigaddset(&newset, SIGUSR2);
-   sigaddset(&newset, SIGHUP);
-   sigaddset(&newset, SIGQUIT);
-   sigaddset(&newset, SIGINT);
-   sigaddset(&newset, SIGTERM);
-#ifdef SIGPWR
-   sigaddset(&newset, SIGPWR);
-#endif
-   sigprocmask(SIG_BLOCK, &newset, &oldset);
-   eina_lock_new(&_eina_debug_sysmon_lock);
-   eina_lock_take(&_eina_debug_sysmon_lock);
-   err = pthread_create(&_sysmon_thread, NULL, _eina_debug_sysmon, NULL);
-   if (err != 0)
-     {
-        fprintf(stderr, "EINA DEBUG ERROR: Can't create debug thread 1!\n");
-        abort();
-     }
-   err = pthread_create(&_monitor_thread, NULL, _eina_debug_monitor, NULL);
-   sigprocmask(SIG_SETMASK, &oldset, NULL);
-   if (err != 0)
-     {
-        fprintf(stderr, "EINA DEBUG ERROR: Can't create debug thread 2!\n");
-        abort();
-     }
-   _monitor_thread_runs = EINA_TRUE;
-}
-
-void
-_eina_debug_monitor_signal_init(void)
-{
-   struct sigaction sa;
-
-   // set up signal handler for our profiling signal - eevery thread should
-   // obey this (this is the case on linux - other OSs may vary)
-   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(void)
-{
-   static char *dir;
-
-   if (dir) return dir;
-   // get possible debug daemon socket directory base
-#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
-   if (getuid() == geteuid()) dir = getenv("XDG_RUNTIME_DIR");
-#endif
-   if (!dir) dir = (char *)eina_environment_home_get();
-   if (!dir) dir = (char *)eina_environment_tmp_get();
-   dir = strdup(dir);
-   return dir;
-}
-
-// connect to efl_debugd
-void
-_eina_debug_monitor_service_connect(void)
-{
-   char buf[4096];
-   int fd, socket_unix_len, curstate = 0;
-   struct sockaddr_un socket_unix;
-
-   // try this socket file - it will likely be:
-   //   ~/.ecore/efl_debug/0
-   // or maybe
-   //   /var/run/UID/.ecore/efl_debug/0
-   // either way a 4k buffer should be ebough ( if it's not we're on an
-   // insane system)
-   snprintf(buf, sizeof(buf), "%s/%s", _socket_home_get(), DEBUG_SERVER);
-   // create the socket
-   fd = socket(AF_UNIX, SOCK_STREAM, 0);
-   if (fd < 0) goto err;
-   // set the socket to close when we exec things so they don't inherit it
-   if (!eina_file_close_on_exec(fd, EINA_TRUE)) goto err;
-   // set up some socket options on addr re-use
-   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate,
-                  sizeof(curstate)) < 0)
-     goto err;
-   // sa that it's a unix socket and where the path is
-   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);
-   // actually conenct to efl_debugd service
-   if (connect(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
-     goto err;
-   // we succeeded - store fd
-   _eina_debug_monitor_service_fd = fd;
-   return;
-err:
-   // some kind of connection failure here, so close a valid socket and
-   // get out of here
-   if (fd >= 0) close(fd);
-}
-#endif
diff --git a/src/lib/eina/eina_debug_private.h b/src/lib/eina/eina_debug_private.h
new file mode 100644 (file)
index 0000000..2f733b5
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef EINA_DEBUG_PRIVATE_H_
+# define EINA_DEBUG_PRIVATE_H_
+
+# include "eina_config.h"
+# include "eina_lock.h"
+# include "eina_thread.h"
+
+#ifdef DEBUGON
+# define e_debug(fmt, args...) fprintf(stderr, "%d:"__FILE__":%s/%d : " fmt "\n", getpid(), __FUNCTION__, __LINE__, ##args)
+# define e_debug_begin(fmt, args...) fprintf(stderr, "%d:"__FILE__":%s/%d : " fmt "", getpid(), __FUNCTION__, __LINE__, ##args)
+# define e_debug_continue(fmt, args...) fprintf(stderr, fmt, ##args)
+# define e_debug_end() fprintf(stderr, "\n")
+#else
+# define e_debug(x...) do { } while (0)
+# define e_debug_begin(x...) do { } while (0)
+# define e_debug_continue(x...) do { } while (0)
+# define e_debug_end(x...) do { } while (0)
+#endif
+
+typedef struct _Eina_Debug_Session Eina_Debug_Session;
+
+typedef struct _Eina_Debug_Thread Eina_Debug_Thread;
+
+struct _Eina_Debug_Thread
+{
+   pthread_t thread;
+
+   Eina_Debug_Session *cmd_session;
+   void *cmd_buffer;
+   int cmd_result;
+
+#if defined(__clockid_t_defined)
+   struct timespec clok;
+#endif
+   int thread_id;
+   int val;
+};
+
+extern Eina_Spinlock                _eina_debug_lock;
+extern Eina_Spinlock                _eina_debug_thread_lock;
+extern pthread_t                    _eina_debug_thread_mainloop;
+extern Eina_Debug_Thread           *_eina_debug_thread_active;
+extern int                          _eina_debug_thread_active_num;
+
+/* TEMP: should be private to debug thread module */
+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);
+#define EINA_MAX_BT 256
+#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)
+
+Eina_Bool _eina_debug_cpu_init(void);
+Eina_Bool _eina_debug_cpu_shutdown(void);
+
+Eina_Bool _eina_debug_bt_init(void);
+Eina_Bool _eina_debug_bt_shutdown(void);
+#endif
+
diff --git a/src/lib/eina/eina_debug_proto.c b/src/lib/eina/eina_debug_proto.c
deleted file mode 100644 (file)
index 8ac8874..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/* EINA - EFL data type library
- * Copyright (C) 2015 Carsten Haitzler
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library;
- * if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "eina_debug.h"
-
-#ifdef EINA_HAVE_DEBUG
-
-int
-_eina_debug_monitor_service_send(int fd, const char op[4],
-                                 unsigned char *data, int size)
-{
-   // send protocol packet. all protocol is an int for size of packet then
-   // included in that size (so a minimum size of 4) is a 4 byte opcode
-   // (all opcodes are 4 bytes as a string of 4 chars), then the real
-   // message payload as a data blob after that
-   unsigned char *buf = alloca(4 + 4 + size);
-   int newsize = size + 4;
-   memcpy(buf, &newsize, 4);
-   memcpy(buf + 4, op, 4);
-   if (size > 0) memcpy(buf + 8, data, size);
-   return write(fd, buf, newsize + 4);
-}
-
-void
-_eina_debug_monitor_service_greet(void)
-{
-   // say hello to our debug daemon - tell them our PID and protocol
-   // version we speak
-   unsigned char buf[8];
-   int version = 1; // version of protocol we speak
-   int pid = getpid();
-   memcpy(buf +  0, &version, 4);
-   memcpy(buf +  4, &pid, 4);
-   _eina_debug_monitor_service_send(_eina_debug_monitor_service_fd,
-                                    "HELO", buf, sizeof(buf));
-}
-
-int
-_eina_debug_monitor_service_read(char *op, unsigned char **data)
-{
-   unsigned char buf[8];
-   unsigned int size;
-   int rret;
-
-   // read first 8 bytes - payload size (excl size header) with 4 byte
-   // opcode that always has to be there
-   rret = read(_eina_debug_monitor_service_fd, buf, 8);
-   if (rret == 8)
-     {
-        // store size in int - native endianess as it's local
-        memcpy(&size, buf, 4);
-        // min size of course is 4 (just opcode) and we will have a max
-        // size for any packet of 1mb here coming from debug daemon
-        // for sanity
-        if ((size >= 4) && (size <= (1024 * 1024)))
-          {
-             // store 4 byte opcode and guarantee it's 0 byte terminated
-             memcpy(op, buf + 4, 4);
-             op[4] = 0;
-             // subtract space for opcode
-             size -= 4;
-             // if we have no payload - move on
-             if (size == 0) *data = NULL;
-             else
-               {
-                  // allocate a buffer for real payload
-                  *data = malloc(size);
-                  if (*data)
-                    {
-                       // get payload - blocking!!!!
-                       rret = read(_eina_debug_monitor_service_fd, *data, size);
-                       if (rret != (int)size)
-                         {
-                            // we didn't get payload as expected - error on
-                            // comms
-                            fprintf(stderr,
-                                    "EINA DEBUG ERROR: "
-                                    "Invalid Debug opcode read of %i\n", rret);
-                            free(*data);
-                            *data = NULL;
-                            return -1;
-                         }
-                    }
-                  else
-                    {
-                       // we couldn't allocate memory for payloa buffer
-                       // internal memory limit error
-                       fprintf(stderr,
-                               "EINA DEBUG ERROR: "
-                               "Cannot allocate %u bytes for op\n", size);
-                       return -1;
-                    }
-               }
-             // return payload size (< 0 is an error)
-             return size;
-          }
-        else
-          {
-             fprintf(stderr,
-                     "EINA DEBUG ERROR: "
-                     "Invalid opcode size of %u\n", size);
-             return -1;
-          }
-     }
-   fprintf(stderr,
-           "EINA DEBUG ERROR: "
-           "Invalid opcode read %i != 8\n", rret);
-   return -1;
-}
-#endif
index 9f4826a..f04fc19 100644 (file)
@@ -17,8 +17,7 @@
  */
 
 #include "eina_debug.h"
-
-#ifdef EINA_HAVE_DEBUG
+#include "eina_debug_private.h"
 
 // a really simple store of currently known active threads. the mainloop is
 // special and inittied at debug init time - assuming eina inits in the
@@ -32,6 +31,7 @@ Eina_Debug_Thread    *_eina_debug_thread_active = NULL;
 int                   _eina_debug_thread_active_num = 0;
 
 static int            _thread_active_size = 0;
+static int            _thread_id_counter = 1;
 
 // 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
@@ -62,6 +62,7 @@ _eina_debug_thread_add(void *th)
    _eina_debug_thread_active[_eina_debug_thread_active_num].clok.tv_nsec = 0;
    _eina_debug_thread_active[_eina_debug_thread_active_num].val = -1;
 #endif
+   _eina_debug_thread_active[_eina_debug_thread_active_num].thread_id = _thread_id_counter++;
    _eina_debug_thread_active_num++;
    // release our lock cleanly
    eina_spinlock_release(&_eina_debug_thread_lock);
@@ -101,4 +102,17 @@ _eina_debug_thread_mainloop_set(void *th)
    pthread_t *pth = th;
    _eina_debug_thread_mainloop = *pth;
 }
-#endif
+
+EAPI int
+eina_debug_thread_id_get(void)
+{
+   pthread_t self = pthread_self();
+   int i;
+
+   for (i = 0; i < _eina_debug_thread_active_num; i++)
+     {
+        if (self == _eina_debug_thread_active[i].thread)
+           return _eina_debug_thread_active[i].thread_id;
+     }
+   return -1;
+}
index d1b3a4b..1d2183a 100644 (file)
@@ -24,8 +24,6 @@
 #include "eina_evlog.h"
 #include "eina_debug.h"
 
-#ifdef EINA_HAVE_DEBUG
-
 #ifdef HAVE_EVIL
 # include <Evil.h>
 #endif
@@ -35,6 +33,7 @@
 #endif
 
 #include <time.h>
+#include <unistd.h>
 
 # ifdef HAVE_MMAP
 #  include <sys/mman.h>
@@ -54,6 +53,8 @@ static clockid_t _eina_evlog_time_clock_id = -1;
 static double _eina_evlog_time_clock_conversion = 1e-9;
 #endif
 
+static int _evlog_get_opcode = EINA_DEBUG_OPCODE_INVALID;
+
 static inline double
 get_time(void)
 {
@@ -211,6 +212,50 @@ eina_evlog_stop(void)
    eina_spinlock_release(&_evlog_lock);
 }
 
+// get evlog
+static Eina_Debug_Error
+_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
+{
+   Eina_Evlog_Buf *evlog = eina_evlog_steal();
+   int resp_size = 0;
+   unsigned char *resp_buf = NULL;
+
+   if ((evlog) && (evlog->buf))
+     {
+        resp_size = evlog->top + sizeof(evlog->overflow);
+        resp_buf = alloca(resp_size);
+        memcpy(resp_buf, &(evlog->overflow), sizeof(evlog->overflow));
+        memcpy(resp_buf + sizeof(evlog->overflow), evlog->buf, evlog->top);
+     }
+   printf("send evlog size %d\n", resp_size);
+   eina_debug_session_send(session, cid, _evlog_get_opcode, resp_buf, resp_size);
+
+   return EINA_DEBUG_OK;
+}
+
+// enable evlog
+static Eina_Debug_Error
+_start_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
+{
+   eina_evlog_start();
+   return EINA_DEBUG_OK;
+}
+
+// stop evlog
+static Eina_Debug_Error
+_stop_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
+{
+   eina_evlog_stop();
+   return EINA_DEBUG_OK;
+}
+
+static const Eina_Debug_Opcode _EINA_DEBUG_EVLOG_OPS[] = {
+       {"evlog/on", NULL, &_start_cb},
+       {"evlog/off", NULL, &_stop_cb},
+       {"evlog/get", &_evlog_get_opcode, &_get_cb},
+       {NULL, NULL, NULL}
+};
+
 Eina_Bool
 eina_evlog_init(void)
 {
@@ -227,6 +272,7 @@ eina_evlog_init(void)
      }
 #endif
    eina_evlog("+eina_init", NULL, 0.0, NULL);
+   eina_debug_opcodes_register(NULL, _EINA_DEBUG_EVLOG_OPS, NULL);
    return EINA_TRUE;
 }
 
@@ -237,37 +283,3 @@ eina_evlog_shutdown(void)
    eina_spinlock_free(&_evlog_lock);
    return EINA_TRUE;
 }
-#else
-EAPI void
-eina_evlog(const char *event EINA_UNUSED, void *obj EINA_UNUSED, double srctime EINA_UNUSED, const char *detail EINA_UNUSED)
-{
-}
-
-EAPI Eina_Evlog_Buf *
-eina_evlog_steal(void)
-{
-   return NULL;
-}
-
-EAPI void
-eina_evlog_start(void)
-{
-}
-
-EAPI void
-eina_evlog_stop(void)
-{
-}
-
-Eina_Bool
-eina_evlog_init(void)
-{
-   return EINA_TRUE;
-}
-
-Eina_Bool
-eina_evlog_shutdown(void)
-{
-   return EINA_TRUE;
-}
-#endif
index 1c19f0a..14182e3 100644 (file)
 # include <Evil.h>
 #endif
 
-#include "eina_debug.h"
-#ifdef EINA_HAVE_DEBUG
-# define EINA_LOG_BACKTRACE
+#ifdef HAVE_EXECINFO_H
+# include <execinfo.h>
 #endif
 
+#include "eina_debug_private.h"
+#define EINA_LOG_BACKTRACE
+
 #include "eina_config.h"
 #include "eina_private.h"
 #include "eina_inlist.h"
index 2c681fc..a9f39dd 100644 (file)
 /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
 #include "eina_safety_checks.h"
 
-#include "eina_debug.h"
+#include "eina_debug_private.h"
 
 #include <pthread.h>
 #include <errno.h>
 #ifndef _WIN32
 # include <signal.h>
 #endif
+# include <string.h>
 
 #if defined(EINA_HAVE_PTHREAD_AFFINITY) || defined(EINA_HAVE_PTHREAD_SETNAME)
 #ifndef __linux__
@@ -133,9 +134,7 @@ _eina_internal_call(void *context)
 {
    Eina_Thread_Call *c = context;
    void *r;
-#ifdef EINA_HAVE_DEBUG
    pthread_t self;
-#endif
 
    EINA_THREAD_CLEANUP_PUSH(free, c);
 
@@ -143,16 +142,11 @@ _eina_internal_call(void *context)
        c->prio == EINA_THREAD_IDLE)
      eina_sched_prio_drop();
 
-#ifdef EINA_HAVE_DEBUG
    self = pthread_self();
    _eina_debug_thread_add(&self);
    EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &self);
-#endif
    r = c->func((void*) c->data, eina_thread_self());
-#ifdef EINA_HAVE_DEBUG
    EINA_THREAD_CLEANUP_POP(EINA_TRUE);
-#endif
-
    EINA_THREAD_CLEANUP_POP(EINA_TRUE);
 
    return r;