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@
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 \
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 \
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 \
* 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;
}
+++ /dev/null
-/* 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;
-}
+++ /dev/null
-/* 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
* 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;
}
#include <eina_slice.h>
#include <eina_freeq.h>
#include <eina_slstr.h>
+#include <eina_debug.h>
#undef EAPI
#define EAPI
* 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
// 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)
}
// 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
#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
+/**
+ * @}
+ */
* 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)
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;
+}
* 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;
}
return file;
}
-#endif
* 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;
strcpy(s, str);
return s;
}
-#endif
--- /dev/null
+# 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;
+}
+
+++ /dev/null
-/* 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
--- /dev/null
+#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
+
+++ /dev/null
-/* 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
*/
#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
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
_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);
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;
+}
#include "eina_evlog.h"
#include "eina_debug.h"
-#ifdef EINA_HAVE_DEBUG
-
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#endif
#include <time.h>
+#include <unistd.h>
# ifdef HAVE_MMAP
# include <sys/mman.h>
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)
{
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)
{
}
#endif
eina_evlog("+eina_init", NULL, 0.0, NULL);
+ eina_debug_opcodes_register(NULL, _EINA_DEBUG_EVLOG_OPS, NULL);
return EINA_TRUE;
}
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
# 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"
/* 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__
{
Eina_Thread_Call *c = context;
void *r;
-#ifdef EINA_HAVE_DEBUG
pthread_t self;
-#endif
EINA_THREAD_CLEANUP_PUSH(free, c);
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;