From 5f268ec26a76b130a78ab182981dde372951f075 Mon Sep 17 00:00:00 2001 From: Daniel Zaoui Date: Sun, 27 Nov 2016 07:48:10 +0200 Subject: [PATCH] First patch of the Eina Debug layer rewriting Eina Debug is a new layer aimed for EFL debugging. It offers scalability by allowing registration of operations not specific to EFL core. The protocol is simple and the APIs try to provide as much functionalities/freedom as possible. --- src/Makefile_Efl.am | 10 +- src/Makefile_Eina.am | 5 +- src/bin/efl/efl_debug.c | 417 +++++++------- src/bin/efl/efl_debug_common.c | 87 --- src/bin/efl/efl_debug_common.h | 55 -- src/bin/efl/efl_debugd.c | 897 +++++++++++++++++------------- src/lib/eina/Eina.h | 1 + src/lib/eina/eina_debug.c | 867 ++++++++++++++++++++++++++++- src/lib/eina/eina_debug.h | 301 +++++++--- src/lib/eina/eina_debug_bt.c | 241 +++++++- src/lib/eina/eina_debug_bt_file.c | 10 +- src/lib/eina/eina_debug_chunk.c | 13 +- src/lib/eina/eina_debug_cpu.c | 307 ++++++++++ src/lib/eina/eina_debug_monitor.c | 708 ----------------------- src/lib/eina/eina_debug_private.h | 73 +++ src/lib/eina/eina_debug_proto.c | 125 ----- src/lib/eina/eina_debug_thread.c | 20 +- src/lib/eina/eina_evlog.c | 84 +-- src/lib/eina/eina_log.c | 8 +- src/lib/eina/eina_thread.c | 10 +- 20 files changed, 2463 insertions(+), 1776 deletions(-) delete mode 100644 src/bin/efl/efl_debug_common.c delete mode 100644 src/bin/efl/efl_debug_common.h create mode 100644 src/lib/eina/eina_debug_cpu.c delete mode 100644 src/lib/eina/eina_debug_monitor.c create mode 100644 src/lib/eina/eina_debug_private.h delete mode 100644 src/lib/eina/eina_debug_proto.c diff --git a/src/Makefile_Efl.am b/src/Makefile_Efl.am index 554e31ab70..6fda23e101 100644 --- a/src/Makefile_Efl.am +++ b/src/Makefile_Efl.am @@ -166,18 +166,12 @@ bin_PROGRAMS += \ bin/efl/efl_debugd \ bin/efl/efl_debug -bin_efl_efl_debugd_SOURCES = \ -bin/efl/efl_debugd.c \ -bin/efl/efl_debug_common.c \ -bin/efl/efl_debug_common.h +bin_efl_efl_debugd_SOURCES = bin/efl/efl_debugd.c bin_efl_efl_debugd_CPPFLAGS = -I$(top_builddir)/src/bin/efl @EINA_CFLAGS@ @ECORE_CFLAGS@ @ECORE_CON_CFLAGS@ bin_efl_efl_debugd_LDADD = @EFL_LIBS@ @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@ bin_efl_efl_debugd_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@ -bin_efl_efl_debug_SOURCES = \ -bin/efl/efl_debug.c \ -bin/efl/efl_debug_common.c \ -bin/efl/efl_debug_common.h +bin_efl_efl_debug_SOURCES = bin/efl/efl_debug.c bin_efl_efl_debug_CPPFLAGS = -I$(top_builddir)/src/bin/efl @EINA_CFLAGS@ @ECORE_CFLAGS@ @ECORE_CON_CFLAGS@ bin_efl_efl_debug_LDADD = @EFL_LIBS@ @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@ bin_efl_efl_debug_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@ diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am index b99d286a88..60981fb541 100644 --- a/src/Makefile_Eina.am +++ b/src/Makefile_Eina.am @@ -12,6 +12,7 @@ installed_einaheadersdir = $(includedir)/eina-@VMAJ@/eina dist_installed_einaheaders_DATA = \ lib/eina/eina_safety_checks.h \ lib/eina/eina_error.h \ +lib/eina/eina_debug.h \ lib/eina/eina_log.h \ lib/eina/eina_inline_log.x \ lib/eina/eina_fp.h \ @@ -123,9 +124,8 @@ lib/eina/eina_debug.c \ lib/eina/eina_debug_bt.c \ lib/eina/eina_debug_bt_file.c \ lib/eina/eina_debug_chunk.c \ -lib/eina/eina_debug_monitor.c \ -lib/eina/eina_debug_proto.c \ lib/eina/eina_debug_thread.c \ +lib/eina/eina_debug_cpu.c \ lib/eina/eina_error.c \ lib/eina/eina_evlog.c \ lib/eina/eina_file_common.h \ @@ -171,7 +171,6 @@ lib/eina/eina_util.c \ lib/eina/eina_value.c \ lib/eina/eina_value_util.c \ lib/eina/eina_xattr.c \ -lib/eina/eina_debug.h \ lib/eina/eina_private.h \ lib/eina/eina_share_common.h \ lib/eina/eina_strbuf_common.h \ diff --git a/src/bin/efl/efl_debug.c b/src/bin/efl/efl_debug.c index 28cdb39d24..7876a08c18 100644 --- a/src/bin/efl/efl_debug.c +++ b/src/bin/efl/efl_debug.c @@ -16,269 +16,250 @@ * if not, see . */ -#define DECLARE_OPS -#include "efl_debug_common.h" +#include +#include -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) +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; + +static Eina_Debug_Session *_session = NULL; + +static int _cid = 0; + +static int my_argc = 0; +static char **my_argv = NULL; + +static Eina_Debug_Error +_evlog_get_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size) { - if (IS_OP(CLST)) + 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) { - int mypid = getpid(); - size_t offset; - - waiting = eina_list_remove(waiting, OP_CLST); - - for (offset = 0; offset + sizeof(int) <= payload.len; offset += sizeof(int)) + if ((_evlog_file) && (blocksize > 0)) { - int p; + unsigned int header[3]; - memcpy(&p, payload.bytes + offset, sizeof(int)); - - if (p == mypid) continue; - if (p > 0) printf("%i\n", p); + 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 - { - fprintf(stderr, "ERROR: unexpected server reply: %.4s\n", op); - retval = EXIT_FAILURE; - } - if (!waiting) ecore_main_loop_quit(); -} - -static void -_on_data(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED) -{ - if (!received_data(dialer, _process_reply, NULL)) + if(received_times == _evlog_max_times) { - retval = EXIT_FAILURE; + printf("Received last evlog response\n"); + if (_evlog_file) fclose(_evlog_file); + _evlog_file = NULL; ecore_main_loop_quit(); } + + return EINA_DEBUG_OK; } static Eina_Bool -_command_send(const char op[static 4], const void *data, unsigned int len) +_cb_evlog(void *data EINA_UNUSED) { - if (!send_data(dialer, op, data, len)) + 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; - return EINA_FALSE; + 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 EINA_TRUE; + return ret; } -#define command_send(op, data, len) _command_send(OP_ ## op, data, len) - -static void -_write_finished(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED) +static Eina_Debug_Error +_cid_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer, int size EINA_UNUSED) { - if (!waiting) ecore_main_loop_quit(); + _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))) + { + 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); + + if(quit) + ecore_main_loop_quit(); + + return EINA_DEBUG_OK; } -static void -_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) { - 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 -_error(void *data EINA_UNUSED, const Efl_Event *event) +static Eina_Debug_Error +_clients_info_deleted_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size) { - Eina_Error *perr = event->info; - - 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(); -} - -int -main(int argc, char **argv) -{ - Eo *loop; - char *path; - Eina_Error err; - int i; - - if (argc < 2) + char *buf = buffer; + while(size) { - fprintf(stderr, "ERROR: missing argument.\n"); - return EXIT_FAILURE; - } - for (i = 1; i < argc; i++) - { - if ((strcmp(argv[i], "-h") != 0) && - (strcmp(argv[i], "--help") != 0)) - continue; + int cid; + EXTRACT(buf, &cid, sizeof(int)); + size -= sizeof(int); - printf("Usage:\n" - "\n" - "\t%s [arguments]\n" - "\n" - "where is one of:\n" - "\tlist list connected process (pid)\n" - "\tpon enable profiling for at frequency in microseconds.\n" - "\tpoff disable profiling for \n" - "\tevlogon start logging events to ~/efl_debug_evlog-.log\n" - "\tevlogoff stop logging events from \n", - argv[0]); - - return EXIT_SUCCESS; - } - - ecore_app_no_system_modules(); - - 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 client deleted dont send anymore evlog requests */ + if(_evlog_fetch_timer) { - 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) + if(_cid == cid) { - fprintf(stderr, "ERROR: missing argument: pon \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 \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 \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 \n"); - retval = EXIT_FAILURE; - goto end; - } - else - { - int data[1] = {atoi(argv[i + 1])}; - if (!command_send(EVOF, data, sizeof(data))) - goto end; - i++; + 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 - { - fprintf(stderr, "ERROR: unknown command: %s\n", argv[i]); - retval = EXIT_FAILURE; - goto end; - } + printf("Deleted: CID: %d\n", cid); } - efl_io_buffered_stream_eos_mark(dialer); + return EINA_DEBUG_OK; +} - err = efl_net_dialer_dial(dialer, path); - if (err) +static void +_ecore_thread_dispatcher(void *data) +{ + eina_debug_dispatch(_session, data); +} + +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; +} + +static void +_args_handle(Eina_Bool flag) +{ + if (!flag) exit(0); + eina_debug_session_dispatch_override(_session, _disp_cb);; + + const char *op_str = my_argv[1]; + if (op_str && !strcmp(op_str, "list")) { - fprintf(stderr, "ERROR: could not connect '%s': %s\n", path, eina_error_msg_get(err)); - retval = EXIT_FAILURE; - goto end; + eina_debug_session_send(_session, 0, _cl_stat_reg_opcode, NULL, 0); } + else if (2 <= my_argc - 1) + { + int pid = atoi(my_argv[2]); + eina_debug_session_send(_session, 0, _cid_from_pid_opcode, &pid, sizeof(int)); + } +} + +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(); + + my_argc = argc; + my_argv = argv; + + _session = eina_debug_local_connect(EINA_TRUE); + if (!_session) + { + fprintf(stderr, "ERROR: Cannot connect to debug daemon.\n"); + return -1; + } + eina_debug_opcodes_register(_session, ops, _args_handle); ecore_main_loop_begin(); - end: - eina_list_free(waiting); - efl_del(dialer); - free(path); - - ecore_con_shutdown(); ecore_shutdown(); eina_shutdown(); - (void) OP_HELO; - (void) OP_EVLG; - - return retval; + return 0; } diff --git a/src/bin/efl/efl_debug_common.c b/src/bin/efl/efl_debug_common.c deleted file mode 100644 index f37f17f16b..0000000000 --- a/src/bin/efl/efl_debug_common.c +++ /dev/null @@ -1,87 +0,0 @@ -/* EINA - EFL data type library - * Copyright (C) 2015 Carsten Haitzler - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; - * if not, see . - */ - -#include "efl_debug_common.h" - -Eina_Bool -received_data(Eo *sock, void (*handle)(void *data, const char op[static 4], const Eina_Slice payload), const void *data) -{ - Eina_Slice slice, payload; - Efl_Debug_Message_Header msgheader; - - slice = efl_io_buffered_stream_slice_get(sock); - if (slice.len < sizeof(msgheader)) - return EINA_TRUE; - - memcpy(&msgheader, slice.mem, sizeof(msgheader)); - if (msgheader.size < 4) /* must contain at last 4 byte opcode */ - { - fprintf(stderr, "ERROR: invalid message header, size=%u\n", msgheader.size); - return EINA_FALSE; - } - - if (msgheader.size + 4 > slice.len) - return EINA_TRUE; - - payload.bytes = slice.bytes + sizeof(msgheader); - payload.len = msgheader.size - 4; - - handle((void *)data, msgheader.op, payload); - - efl_io_buffered_stream_discard(sock, sizeof(msgheader) + payload.len); - return EINA_TRUE; -} - -Eina_Bool -send_data(Eo *sock, const char op[static 4], const void *data, unsigned int len) -{ - Eina_Error err; - Efl_Debug_Message_Header msghdr = { - .size = 4 + len, - }; - Eina_Slice s, r; - - memcpy(msghdr.op, op, 4); - - s.mem = &msghdr; - s.len = sizeof(msghdr); - - err = efl_io_writer_write(sock, &s, &r); - if (err || r.len) goto end; - - if (!len) goto end; - - s.mem = data; - s.len = len; - err = efl_io_writer_write(sock, &s, &r); - - end: - if (err) - { - fprintf(stderr, "ERROR: could not queue message '%.4s': %s\n", op, eina_error_msg_get(err)); - return EINA_FALSE; - } - - if (r.len) - { - fprintf(stderr, "ERROR: could not queue message '%.4s': out of memory\n", op); - return EINA_FALSE; - } - - return EINA_TRUE; -} diff --git a/src/bin/efl/efl_debug_common.h b/src/bin/efl/efl_debug_common.h deleted file mode 100644 index 6a0ba0b416..0000000000 --- a/src/bin/efl/efl_debug_common.h +++ /dev/null @@ -1,55 +0,0 @@ -/* EINA - EFL data type library - * Copyright (C) 2015 Carsten Haitzler - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; - * if not, see . - */ - -#ifndef EFL_DEBUG_COMMON_H -#define EFL_DEBUG_COMMON_H 1 - -#define EFL_BETA_API_SUPPORT 1 -#define EFL_EO_API_SUPPORT 1 - -#include -#include -#include -#include -#include -#include -#include - -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 diff --git a/src/bin/efl/efl_debugd.c b/src/bin/efl/efl_debugd.c index dfe175ce51..9da2648723 100644 --- a/src/bin/efl/efl_debugd.c +++ b/src/bin/efl/efl_debugd.c @@ -16,355 +16,129 @@ * if not, see . */ -#define DECLARE_OPS -#include "efl_debug_common.h" +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 +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; + +typedef struct +{ + int opcode; + Eina_Stringshare *opcode_string; + Opcode_Cb cb; +} Opcode_Information; + +#define MAX_OPCODES 1000 +Opcode_Information *_opcodes[MAX_OPCODES]; + +/* epoll stuff */ +#ifndef _WIN32 +static int _epfd = -1, _listening_master_fd = -1, _listening_slave_fd = -1; #endif -#define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) - -#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__) - -#ifdef WRN -# undef WRN -#endif -#define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__) - -#ifdef CRI -# undef CRI -#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; - - if (pid <= 0) return NULL; - EINA_LIST_FOREACH(clients, l, c) - { - if (c->pid == pid) return c; - } - - WRN("no client pid=%d", pid); + EINA_LIST_FOREACH(_clients, l, c) + if (c->cid == cid) return c; return NULL; } -static Eina_Bool -_cb_evlog(void *data) +static Client * +_client_find_by_pid(int pid) { - Client *c = data; - send_cli(c, EVLG, NULL, 0); - return EINA_TRUE; -} - -static void -_process_command(void *data, const char op[static 4], const Eina_Slice payload) -{ - Client *c = data; - Client *c2; + Client *c; Eina_List *l; + EINA_LIST_FOREACH(_clients, l, c) + if (c->pid == pid) return c; + return NULL; +} - DBG("client %p (%p) [pid:%d] op=%.4s payload=%zd", c, c->client, c->pid, op, payload.len); +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; +} - if (IS_OP(HELO)) - { - 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); - } - } - else if (IS_OP(LIST)) - { - 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); - } - } - else if (IS_OP(PLON)) - { - 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)); - } - } - } - else if (IS_OP(PLOF)) - { - if (payload.len < sizeof(int)) - fprintf(stderr, "INFO: client %p [pid: %d] sent invalid PLOF\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 PLOF %d: no such client\n", c, (int)c->pid, pid); - } - 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); - } - } - } - else if (IS_OP(EVON)) - { - 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); - } - } - } - } - else if (IS_OP(EVOF)) - { - if (payload.len < sizeof(int)) - fprintf(stderr, "INFO: client %p [pid: %d] sent invalid EVOF\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 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; - } - } - } - 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]; - - header[0] = 0xffee211; - header[1] = blocksize; - memcpy(header + 2, payload.mem, sizeof(int)); - - 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; - - send_cli(c, EVOF, NULL, 0); - if (c->evlog_fetch_timer) - { - ecore_timer_del(c->evlog_fetch_timer); - c->evlog_fetch_timer = NULL; - } - } - } - } - } +static int +_send(Client *dest, int opcode, void *payload, int payload_size) +{ + 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 -_client_data(void *data, const Efl_Event *event) +_client_del(Client *c) { - Client *c = data; - if (!received_data(event->object, _process_command, 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; - } -} + Client *c2; + if (!c) return; + Eina_List *itr; -static void -_client_error(void *data, const Efl_Event *event) -{ - 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)); -} - -static void -_client_eos(void *data, const Efl_Event *event EINA_UNUSED) -{ - 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); -} - -static void -_client_write_finished(void *data, const Efl_Event *event EINA_UNUSED) -{ - 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)); -} - -static void -_client_read_finished(void *data, const Efl_Event *event EINA_UNUSED) -{ - 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)); -} - -static Efl_Callback_Array_Item *_client_cbs(void); - -static void -_client_finished(void *data, const Efl_Event *event EINA_UNUSED) -{ - Client *c = data; - - clients = eina_list_remove(clients, c); + _clients = eina_list_remove(_clients, c); if (c->evlog_fetch_timer) { ecore_timer_del(c->evlog_fetch_timer); @@ -375,111 +149,438 @@ _client_finished(void *data, const Efl_Event *event EINA_UNUSED) 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); + + /* Don't update the observers if the client is a master */ + if (c->is_master) return; + + EINA_LIST_FOREACH(_clients, itr, c2) + { + if (c2->cl_stat_obs) _send(c2, _slave_deleted_opcode, &c->cid, sizeof(int)); + } 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) +static Eina_Bool +_dispatch(Client *src, void *buffer, int size) { - Client *c = calloc(1, sizeof(Client)); + Eina_Debug_Packet_Header *hdr = (Eina_Debug_Packet_Header *)buffer; + if (hdr->cid) + { + /* If the client id is given, we forward */ + Client *dest = _client_find_by_cid(hdr->cid); + if (dest) + { + if (dest->is_master != src->is_master) + { + hdr->cid = src->cid; + send(dest->fd, buffer, size, 0); + } + else + { + /* + * 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 + { + 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)); + } + return EINA_TRUE; +} - 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); +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) + { + op_info = calloc(1, sizeof(*op_info)); + if (op_id == EINA_DEBUG_OPCODE_INVALID) + { + do + { + 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; + } + printf("Register %s -> opcode %d\n", op_name, op_info->opcode); + return op_info->opcode; +} + +static Eina_Bool +_hello_cb(Client *c, void *buffer, int size) +{ + Eina_List *itr; + char *buf = (char *)buffer, *tmp; + + EXTRACT(buf, &c->version, 4); + EXTRACT(buf, &c->pid, 4); + size -= 8; + + c->cid = _free_cid++; + if (size > 1) + { + c->app_name = eina_stringshare_add_length(buf, size); + } + 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) + { + if (c->cl_stat_obs) _send(c, _slave_added_opcode, buf, size); + } + return EINA_TRUE; +} + +static Eina_Bool +_cid_get_cb(Client *src, void *buffer, int size EINA_UNUSED) +{ + 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 Eina_Bool +_data_test_cb(Client *src, void *buffer, int size) +{ + printf("Data test: loop packet of %d bytes\n", size); + _send(src, _test_loop_opcode, buffer, size); + return EINA_TRUE; +} + +static Eina_Bool +_cl_stat_obs_register_cb(Client *src, void *buffer, int size) +{ + 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 Eina_Bool +_opcode_register_cb(Client *src, void *buffer, int size) +{ + char *buf = (char *)buffer; + char *ops_buf = buf; + int ops_size = size; + + ops_buf += sizeof(uint64_t); + ops_size -= sizeof(uint64_t); + int *opcodes = (int *)ops_buf; + + while (ops_size > 0) + { + int len = strlen(ops_buf) + 1; + *opcodes++ = _opcode_register(ops_buf, EINA_DEBUG_OPCODE_INVALID, NULL); + ops_buf += len; + ops_size -= len; + } + + _send(src, EINA_DEBUG_OPCODE_REGISTER, buf, (char *)opcodes - (char *)buf); + + return EINA_TRUE; +} + +static int +_data_receive(Client *c, unsigned char **buffer) +{ + unsigned char *recv_buf = NULL; + int rret; + int size = 0; + + if (!c) return -1; + + rret = recv(c->fd, &size, sizeof(int), MSG_PEEK); + + 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; } static void -_error(void *data EINA_UNUSED, const Efl_Event *event) +_monitor() { - 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; +#ifndef _WIN32 +#define MAX_EVENTS 1000 + int ret = 0; + struct epoll_event events[MAX_EVENTS]; + Client *c; + + // sit forever processing commands or timeouts + for (; ret != -1;) + { + ret = epoll_wait (_epfd, events, MAX_EVENTS, -1); + + // 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; + } + + 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 + } +#endif +} + +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)) +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 + +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) + { + 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) { - Eo *loop; - char *path; - Eina_Error err; - - ecore_app_no_system_modules(); - + eina_debug_disable(); eina_init(); ecore_init(); - ecore_con_init(); - _log_dom = eina_log_domain_register("efl_debugd", EINA_COLOR_CYAN); + _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); - 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; - } + _server_launch(); + _monitor(); - loop = ecore_main_loop_get(); - -#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"); -#endif - if (!server) - { - fprintf(stderr, "ERROR: could not create communication server\n"); - retval = EXIT_FAILURE; - goto end; - } - - 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); - -#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); - } -#endif - - err = efl_net_server_serve(server, path); - if (err) - { - fprintf(stderr, "ERROR: could not serve '%s': %s\n", path, eina_error_msg_get(err)); - retval = EXIT_FAILURE; - goto end; - } - - ecore_main_loop_begin(); - - end: - efl_del(server); - free(path); - - ecore_con_shutdown(); ecore_shutdown(); eina_shutdown(); - return retval; + return 0; } diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h index 41c182842b..ee659117ea 100644 --- a/src/lib/eina/Eina.h +++ b/src/lib/eina/Eina.h @@ -272,6 +272,7 @@ extern "C" { #include #include #include +#include #undef EAPI #define EAPI diff --git a/src/lib/eina/eina_debug.c b/src/lib/eina/eina_debug.c index 8578f13883..f8c62095e5 100644 --- a/src/lib/eina/eina_debug.c +++ b/src/lib/eina/eina_debug.c @@ -16,10 +16,44 @@ * if not, see . */ +# ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +# endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "eina_debug.h" #include "eina_types.h" +#include "eina_list.h" +#include "eina_mempool.h" +#include "eina_util.h" +#include "eina_evlog.h" +#include "eina_hash.h" +#include "eina_debug_private.h" -#ifdef EINA_HAVE_DEBUG +#ifdef __CYGWIN__ +# define LIBEXT ".dll" +#else +# define LIBEXT ".so" +#endif + +#define SIG SIGPROF // yes - a global debug spinlock. i expect contention to be low for now, and // when needed we can split this up into mroe locks to reduce contention when @@ -28,6 +62,789 @@ Eina_Spinlock _eina_debug_lock; // only init once static Eina_Bool _inited = EINA_FALSE; +static char *_my_app_name = NULL; + +extern Eina_Bool eina_module_init(void); +extern Eina_Bool eina_mempool_init(void); +extern Eina_Bool eina_list_init(void); + +extern Eina_Spinlock _eina_debug_thread_lock; + +static Eina_Bool _debug_disabled = EINA_FALSE; + +/* Local session */ +/* __thread here to allow debuggers to be master and slave by using two different threads */ +static __thread Eina_Debug_Session *_session = NULL; +static Eina_Debug_Session *_last_local_session = NULL; + +/* Opcode used to load a module + * needed by the daemon to notify loading success */ +static int _module_init_opcode = EINA_DEBUG_OPCODE_INVALID; +static int _module_shutdown_opcode = EINA_DEBUG_OPCODE_INVALID; +static Eina_Hash *_modules_hash = NULL; + +static unsigned int _poll_time = 0; +static Eina_Debug_Timer_Cb _poll_timer_cb = NULL; +static void *_poll_timer_data = NULL; + +static Eina_Semaphore _thread_cmd_ready_sem; + +typedef struct +{ + int magic; /* Used to certify the validity of the struct */ + const Eina_Debug_Opcode *ops; + Eina_Debug_Opcode_Status_Cb status_cb; +} _opcode_reply_info; + +struct _Eina_Debug_Session +{ + Eina_List **cbs; /* Table of callbacks lists indexed by opcode id */ + Eina_List *opcode_reply_infos; + Eina_Debug_Dispatch_Cb dispatch_cb; /* Session dispatcher */ + int cbs_length; /* cbs table size */ + int fd_in; /* File descriptor to read */ + int fd_out; /* File descriptor to write */ +}; + +static void _opcodes_register_all(); +static void _thread_start(Eina_Debug_Session *session); + +EAPI int +eina_debug_session_send_to_thread(Eina_Debug_Session *session, int dest_id, int thread_id, int op, void *data, int size) +{ + Eina_Debug_Packet_Header hdr; + + if (!session) return -1; + if (op == EINA_DEBUG_OPCODE_INVALID) return -1; + /* Preparation of the packet header */ + hdr.size = size + sizeof(Eina_Debug_Packet_Header); + hdr.opcode = op; + hdr.cid = dest_id; + hdr.thread_id = thread_id; +#ifndef _WIN32 + e_debug("socket: %d / opcode %X / packet size %ld / bytes to send: %d", + session->fd_out, op, hdr->size + sizeof(int), total_size); + eina_spinlock_take(&_eina_debug_lock); + /* Sending header */ + write(session->fd_out, &hdr, sizeof(hdr)); + /* Sending payload */ + if (size) write(session->fd_out, data, size); + eina_spinlock_release(&_eina_debug_lock); +#endif + return hdr.size; +} + +EAPI int +eina_debug_session_send(Eina_Debug_Session *session, int dest, int op, void *data, int size) +{ + return eina_debug_session_send_to_thread(session, dest, 0, op, data, size); +} + +static void +_daemon_greet(Eina_Debug_Session *session) +{ + /* say hello to our debug daemon - tell them our PID and protocol + version we speak */ + /* Version + Pid + App name */ + int size = 8 + (_my_app_name ? strlen(_my_app_name) : 0) + 1; + unsigned char *buf = alloca(size); + int version = 1; // version of protocol we speak + int pid = getpid(); + memcpy(buf + 0, &version, 4); + memcpy(buf + 4, &pid, 4); + if (_my_app_name) + memcpy(buf + 8, _my_app_name, strlen(_my_app_name) + 1); + else + buf[8] = '\0'; + eina_debug_session_send(session, 0, EINA_DEBUG_OPCODE_HELLO, buf, size); +} + +#ifndef _WIN32 +static int +_packet_receive(unsigned char **buffer) +{ + unsigned char *packet_buf = NULL; + int rret = -1; + int size = 0; + + if (!_session) goto end; + + if (read(_session->fd_in, &size, 4) == 4) + { + // allocate a buffer for the next bytes to receive + packet_buf = malloc(size); + if (packet_buf) + { + int cur_packet_size = 4; + memcpy(packet_buf, &size, sizeof(int)); + /* Receive all the remaining packet bytes */ + while (cur_packet_size < size) + { + rret = read(_session->fd_in, packet_buf + cur_packet_size, size - cur_packet_size); + if (rret <= 0) goto end; + cur_packet_size += rret; + } + *buffer = packet_buf; + rret = cur_packet_size; + } + else + { + // we couldn't allocate memory for payloa buffer + // internal memory limit error + e_debug("Cannot allocate %u bytes for op", (unsigned int)size); + goto end; + } + } + else + { + e_debug("Invalid size read %i != %i", rret, size_sz); + goto end; + } +end: + if (rret <= 0 && packet_buf) free(packet_buf); + return rret; +} +#endif + +EAPI void +eina_debug_disable() +{ + _debug_disabled = EINA_TRUE; +} + +EAPI void +eina_debug_session_terminate(Eina_Debug_Session *session) +{ + /* FIXME: Maybe just close fd here so the thread terminates its own session by itself */ + if (!session) return; + + _opcode_reply_info *info = NULL; + + EINA_LIST_FREE(session->opcode_reply_infos, info) free(info); + + free(session->cbs); + free(session); +} + +EAPI void +eina_debug_session_dispatch_override(Eina_Debug_Session *session, Eina_Debug_Dispatch_Cb disp_cb) +{ + if (!session) return; + if (!disp_cb) disp_cb = eina_debug_dispatch; + session->dispatch_cb = disp_cb; +} + +typedef struct { + Eina_Module *handle; + Eina_Bool (*init)(void); + Eina_Bool (*shutdown)(void); + int ref; +} _module_info; + +#define _LOAD_SYMBOL(cls_struct, pkg, sym) \ + do \ + { \ + char func_name[1024]; \ + snprintf(func_name, sizeof(func_name), "%s_debug_" #sym, pkg); \ + (cls_struct)->sym = eina_module_symbol_get((cls_struct)->handle, func_name); \ + if (!(cls_struct)->sym) \ + { \ + e_debug("Failed loading symbol '%s' from the library.", func_name); \ + eina_module_free((cls_struct)->handle); \ + (cls_struct)->handle = NULL; \ + free((cls_struct)); \ + return EINA_FALSE; \ + } \ + } \ + while (0) + +static Eina_Debug_Error +_module_init_cb(Eina_Debug_Session *session, int cid, void *buffer, int size) +{ + char module_path[1024]; + _module_info *minfo = NULL; + const char *module_name = buffer; + char *resp; + if (size <= 0) return EINA_DEBUG_ERROR; + if (!_modules_hash) _modules_hash = eina_hash_string_small_new(NULL); + + minfo = eina_hash_find(_modules_hash, module_name); + if (minfo && minfo->ref) + { + minfo->ref++; + goto end; + } + + e_debug("Init module %s", module_name); + if (!minfo) + { + snprintf(module_path, sizeof(module_path), PACKAGE_LIB_DIR "/lib%s_debug"LIBEXT, module_name); + minfo = calloc(1, sizeof(*minfo)); + eina_hash_add(_modules_hash, module_name, minfo); + } + if (!minfo->handle) minfo->handle = eina_module_new(module_path); + if (!minfo->handle || !eina_module_load(minfo->handle)) + { + e_debug("Failed loading debug module %s.", module_name); + if (minfo->handle) eina_module_free(minfo->handle); + minfo->handle = NULL; + goto end; + } + + if (!minfo->init) _LOAD_SYMBOL(minfo, module_name, init); + if (!minfo->shutdown) _LOAD_SYMBOL(minfo, module_name, shutdown); + + if (minfo->init()) minfo->ref = 1; + +end: + resp = alloca(size + 1); + memcpy(resp, buffer, size); + resp[size] = !!(minfo->ref); + eina_debug_session_send(session, cid, _module_init_opcode, resp, size+1); + return EINA_DEBUG_OK; +} + +static Eina_Debug_Error +_module_shutdown_cb(Eina_Debug_Session *session, int cid, void *buffer, int size) +{ + _module_info *minfo = NULL; + const char *module_name = buffer; + char *resp; + Eina_Bool ret = EINA_TRUE; + if (size <= 0 || !_modules_hash) return EINA_DEBUG_ERROR; + + minfo = eina_hash_find(_modules_hash, module_name); + if (minfo) + { + if (!--(minfo->ref)) + { + eina_hash_del(_modules_hash, module_name, minfo); + if (minfo->shutdown) ret = minfo->shutdown(); + if (minfo->handle) eina_module_free(minfo->handle); + minfo->handle = NULL; + free(minfo); + } + } + resp = alloca(size + 1); + memcpy(resp, buffer, size); + resp[size] = !!ret; + eina_debug_session_send(session, cid, _module_shutdown_opcode, resp, size+1); + return EINA_DEBUG_OK; +} + +static const Eina_Debug_Opcode _EINA_DEBUG_MONITOR_OPS[] = { + {"module/init", &_module_init_opcode, &_module_init_cb}, + {"module/shutdown", &_module_shutdown_opcode, &_module_shutdown_cb}, + {NULL, NULL, NULL} +}; + +static void +_static_opcode_register(Eina_Debug_Session *session, + int op_id, Eina_Debug_Cb cb) +{ + if(session->cbs_length < op_id + 1) + { + int i = session->cbs_length; + session->cbs_length = op_id + 16; + session->cbs = realloc(session->cbs, session->cbs_length * sizeof(Eina_List *)); + for(; i < session->cbs_length; i++) session->cbs[i] = NULL; + } + if (cb) + { + session->cbs[op_id] = eina_list_append(session->cbs[op_id], cb); + } +} + +/* + * Response of the daemon containing the ids of the requested opcodes. + * PTR64 + (opcode id)* + */ +static Eina_Debug_Error +_callbacks_register_cb(Eina_Debug_Session *session, int src_id EINA_UNUSED, void *buffer, int size) +{ + _opcode_reply_info *info = NULL, *info2; + Eina_List *itr; + int *os; + unsigned int count, i; + + uint64_t info_64; + memcpy(&info_64, buffer, sizeof(uint64_t)); + info = (_opcode_reply_info *)info_64; + + if (!info) return EINA_DEBUG_ERROR; + EINA_LIST_FOREACH(session->opcode_reply_infos, itr, info2) + { + if (info2 == info) + { + os = (int *)((unsigned char *)buffer + sizeof(uint64_t)); + count = (size - sizeof(uint64_t)) / sizeof(int); + + for (i = 0; i < count; i++) + { + if (info->ops[i].opcode_id) *(info->ops[i].opcode_id) = os[i]; + _static_opcode_register(session, os[i], info->ops[i].cb); + e_debug("Opcode %s -> %d", info->ops[i].opcode_name, os[i]); + } + if (info->status_cb) info->status_cb(EINA_TRUE); + return EINA_DEBUG_OK; + } + } + + return EINA_DEBUG_ERROR; +} + +static void +_opcodes_registration_send(Eina_Debug_Session *session, + _opcode_reply_info *info) +{ + unsigned char *buf; + + int count = 0; + int size = sizeof(uint64_t); + + while(info->ops[count].opcode_name) + { + size += strlen(info->ops[count].opcode_name) + 1; + count++; + } + + buf = alloca(size); + + uint64_t info_64 = (uint64_t)info; + memcpy(buf, &info_64, sizeof(uint64_t)); + int size_curr = sizeof(uint64_t); + + count = 0; + while(info->ops[count].opcode_name) + { + int len = strlen(info->ops[count].opcode_name) + 1; + memcpy(buf + size_curr, info->ops[count].opcode_name, len); + size_curr += len; + count++; + } + + eina_debug_session_send(session, 0, EINA_DEBUG_OPCODE_REGISTER, buf, size); +} + +static void +_opcodes_register_all(Eina_Debug_Session *session) +{ + Eina_List *l; + _opcode_reply_info *info = NULL; + + _static_opcode_register(session, + EINA_DEBUG_OPCODE_REGISTER, _callbacks_register_cb); + EINA_LIST_FOREACH(session->opcode_reply_infos, l, info) + _opcodes_registration_send(session, info);; +} + +static void +_opcodes_unregister_all(Eina_Debug_Session *session) +{ + Eina_List *l; + int i; + _opcode_reply_info *info = NULL; + + if (!session) return; + session->cbs_length = 0; + for (i = 0; i < session->cbs_length; i++) + eina_list_free(session->cbs[i]); + free(session->cbs); + session->cbs = NULL; + + EINA_LIST_FOREACH(session->opcode_reply_infos, l, info) + { + const Eina_Debug_Opcode *op = info->ops; + while(!op->opcode_name) + { + if (op->opcode_id) *(op->opcode_id) = EINA_DEBUG_OPCODE_INVALID; + op++; + } + if (info->status_cb) info->status_cb(EINA_FALSE); + } +} + +static const char * +_socket_home_get() +{ + // get possible debug daemon socket directory base + const char *dir = getenv("XDG_RUNTIME_DIR"); + if (!dir) dir = eina_environment_home_get(); + if (!dir) dir = eina_environment_tmp_get(); + return dir; +} + +#ifndef _WIN32 +#define LENGTH_OF_SOCKADDR_UN(s) \ + (strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path)) +#endif + +EAPI Eina_Debug_Session * +eina_debug_local_connect(Eina_Bool is_master) +{ +#ifndef _WIN32 + char buf[4096]; + int fd, socket_unix_len, curstate = 0; + struct sockaddr_un socket_unix; +#endif + + Eina_Debug_Session *session = calloc(1, sizeof(*session)); + session->dispatch_cb = eina_debug_dispatch; + session->fd_out = session->fd_in = -1; + // try this socket file - it will likely be: + // ~/.ecore/efl_debug/0 + // or maybe + // /var/run/UID/.ecore/efl_debug/0 + // either way a 4k buffer should be ebough ( if it's not we're on an + // insane system) +#ifndef _WIN32 + snprintf(buf, sizeof(buf), "%s/%s/%s/%i", _socket_home_get(), SERVER_PATH, SERVER_NAME, + is_master ? SERVER_MASTER_PORT : SERVER_SLAVE_PORT); + // create the socket + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) goto err; + // set the socket to close when we exec things so they don't inherit it + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto err; + // set up some socket options on addr re-use + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate, + sizeof(curstate)) < 0) + goto err; + // sa that it's a unix socket and where the path is + socket_unix.sun_family = AF_UNIX; + strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path) - 1); + socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix); + // actually connect to efl_debugd service + if (connect(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) + goto err; + // we succeeded + session->fd_out = session->fd_in = fd; + // start the monitor thread + _thread_start(session); + + _daemon_greet(session); + _opcodes_register_all(session); + if (!is_master) + eina_debug_opcodes_register(session, _EINA_DEBUG_MONITOR_OPS, NULL); + + _last_local_session = session; + return session; +err: + // some kind of connection failure here, so close a valid socket and + // get out of here + if (fd >= 0) close(fd); + if (session) free(session); +#else + (void) _session; + (void) type; +#endif + return NULL; +} + +EAPI Eina_Bool +eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data) +{ + _poll_time = timeout_ms; + _poll_timer_cb = cb; + _poll_timer_data = data; + return EINA_TRUE; +} + +// this is a DEDICATED debug thread to monitor the application so it works +// even if the mainloop is blocked or the app otherwise deadlocked in some +// way. this is an alternative to using external debuggers so we can get +// users or developers to get useful information about an app at all times +static void * +_monitor(void *_data) +{ +#ifndef _WIN32 +#define MAX_EVENTS 4 + int ret; + struct epoll_event event; + struct epoll_event events[MAX_EVENTS]; + int epfd = epoll_create(MAX_EVENTS); + + _session = _data; + event.data.fd = _session->fd_in; + event.events = EPOLLIN; + ret = epoll_ctl(epfd, EPOLL_CTL_ADD, _session->fd_in, &event); + if (ret) perror("epoll_ctl/add"); + + // set a name for this thread for system debugging +#ifdef EINA_HAVE_PTHREAD_SETNAME +# ifndef __linux__ + pthread_set_name_np +# else + pthread_setname_np +# endif + (pthread_self(), "Edbg-mon"); +#endif + + // sit forever processing commands or timeouts in the debug monitor + // thread - this is separate to the rest of the app so it shouldn't + // impact the application specifically + for (;_session;) + { + // if we are in a polling mode then set up a timeout and wait for it + int timeout = _poll_time ? (int)_poll_time : -1; //in milliseconds + + ret = epoll_wait(epfd, events, MAX_EVENTS, timeout); + + // if the fd for debug daemon says it's alive, process it + if (ret) + { + int i; + //check which fd are set/ready for read + for (i = 0; i < ret; i++) + { + if (events[i].events & EPOLLHUP) + { + _opcodes_unregister_all(_session); + free(_session); + _session = NULL; + } + else if (events[i].events & EPOLLIN) + { + int size; + unsigned char *buffer; + + size = _packet_receive(&buffer); + // if not negative - we have a real message + if (size > 0) + { + if(!_session->dispatch_cb(_session, buffer)) + { + // something we don't understand + e_debug("EINA DEBUG ERROR: Unknown command"); + } + } + else if (size == 0) + { + // May be due to a response from a script line + } + else + { + // major failure on debug daemon control fd - get out of here. + // else goto fail; + close(_session->fd_in); + //TODO if its not main _session we will tell the main_loop + //that it disconneted + } + } + } + } + else + { + if (_poll_time && _poll_timer_cb) + { + if (!_poll_timer_cb(_poll_timer_data)) _poll_time = 0; + } + } + } +#endif + return NULL; +} + +// start up the debug monitor if we haven't already +static void +_thread_start(Eina_Debug_Session *session) +{ + pthread_t monitor_thread; + int err; + sigset_t oldset, newset; + + sigemptyset(&newset); + sigaddset(&newset, SIGPIPE); + sigaddset(&newset, SIGALRM); + sigaddset(&newset, SIGCHLD); + sigaddset(&newset, SIGUSR1); + sigaddset(&newset, SIGUSR2); + sigaddset(&newset, SIGHUP); + sigaddset(&newset, SIGQUIT); + sigaddset(&newset, SIGINT); + sigaddset(&newset, SIGTERM); +#ifdef SIGPWR + sigaddset(&newset, SIGPWR); +#endif + sigprocmask(SIG_BLOCK, &newset, &oldset); + + err = pthread_create(&monitor_thread, NULL, _monitor, session); + + sigprocmask(SIG_SETMASK, &oldset, NULL); + if (err != 0) + { + e_debug("EINA DEBUG ERROR: Can't create monitor debug thread!"); + abort(); + } +} + +/* + * Sends to daemon: + * - Pointer to ops: returned in the response to determine which opcodes have been added + * - List of opcode names seperated by \0 + */ +EAPI void +eina_debug_opcodes_register(Eina_Debug_Session *session, const Eina_Debug_Opcode ops[], + Eina_Debug_Opcode_Status_Cb status_cb) +{ + if (!session) session = _last_local_session; + if (!session) return; + + _opcode_reply_info *info = malloc(sizeof(*info)); + info->ops = ops; + info->status_cb = status_cb; + + session->opcode_reply_infos = eina_list_append( + session->opcode_reply_infos, info); + + //send only if _session's fd connected, if not - it will be sent when connected + if(session && session->fd_in != -1) + _opcodes_registration_send(session, info); +} + +static Eina_Debug_Error +_self_dispatch(Eina_Debug_Session *session, void *buffer) +{ + Eina_Debug_Packet_Header *hdr = buffer; + Eina_List *itr; + int opcode = hdr->opcode; + Eina_Debug_Cb cb = NULL; + + if (opcode >= session->cbs_length) + { + e_debug("Invalid opcode %d", opcode); + return EINA_DEBUG_ERROR; + } + + EINA_LIST_FOREACH(session->cbs[opcode], itr, cb) + { + if (!cb) continue; + Eina_Debug_Error ret = cb(session, hdr->cid, + (unsigned char *)buffer + sizeof(*hdr), + hdr->size - sizeof(*hdr)); + if (ret == EINA_DEBUG_ERROR) return ret; + } + return EINA_DEBUG_OK; +} + +EAPI Eina_Debug_Error +eina_debug_dispatch(Eina_Debug_Session *session, void *buffer) +{ + Eina_Debug_Packet_Header *hdr = buffer; + Eina_Debug_Error ret = EINA_DEBUG_OK; + if (hdr->thread_id == 0) + { + ret = _self_dispatch(session, buffer); + free(buffer); + return ret; + } + else + { + int i, nb_calls = 0; + eina_spinlock_take(&_eina_debug_thread_lock); + for (i = 0; i < _eina_debug_thread_active_num; i++) + { + _eina_debug_thread_active[i].cmd_buffer = NULL; + if (hdr->thread_id == (int)0xFFFFFFFF || + hdr->thread_id == _eina_debug_thread_active[i].thread_id) + { + _eina_debug_thread_active[i].cmd_session = session; + _eina_debug_thread_active[i].cmd_buffer = buffer; + _eina_debug_thread_active[i].cmd_result = EINA_DEBUG_OK; + pthread_kill(_eina_debug_thread_active[i].thread, SIG); + nb_calls++; + } + } + eina_spinlock_release(&_eina_debug_thread_lock); + while (nb_calls) + { + while (nb_calls) + { + eina_semaphore_lock(&_thread_cmd_ready_sem); + nb_calls--; + } + eina_spinlock_take(&_eina_debug_thread_lock); + for (i = 0; i < _eina_debug_thread_active_num; i++) + { + if (_eina_debug_thread_active[i].cmd_buffer) + { + switch (_eina_debug_thread_active[i].cmd_result) + { + case EINA_DEBUG_OK: + { + _eina_debug_thread_active[i].cmd_buffer = NULL; + break; + } + case EINA_DEBUG_ERROR: + { + _eina_debug_thread_active[i].cmd_buffer = NULL; + ret = EINA_DEBUG_ERROR; + break; + } + case EINA_DEBUG_AGAIN: + { + pthread_kill(_eina_debug_thread_active[i].thread, SIG); + nb_calls++; + break; + } + default: break; + } + } + } + eina_spinlock_release(&_eina_debug_thread_lock); + } + free(buffer); + } + return ret; +} + +static void +_signal_handler(int sig EINA_UNUSED, + siginfo_t *si EINA_UNUSED, void *foo EINA_UNUSED) +{ + int i, slot = -1; + pthread_t self = pthread_self(); + eina_spinlock_take(&_eina_debug_thread_lock); + for (i = 0; i < _eina_debug_thread_active_num; i++) + { + if (self == _eina_debug_thread_active[i].thread) + { + slot = i; + break; + } + } + eina_spinlock_release(&_eina_debug_thread_lock); + if (slot != -1) + { + _eina_debug_thread_active[slot].cmd_result = + _self_dispatch(_eina_debug_thread_active[slot].cmd_session, + _eina_debug_thread_active[slot].cmd_buffer); + } + eina_semaphore_release(&_thread_cmd_ready_sem, 1); +} + +#ifdef __linux__ + extern char *__progname; +#endif + +static void +_signal_init(void) +{ + struct sigaction sa; + + // set up signal handler for our profiling signal - eevery thread should + // obey this (this is the case on linux - other OSs may vary) + sa.sa_sigaction = _signal_handler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + if (sigaction(SIG, &sa, NULL) != 0) + e_debug("EINA DEBUG ERROR: Can't set up sig %i handler!", SIG); + + sa.sa_sigaction = NULL; + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGPIPE, &sa, 0) == -1) perror(0); +} Eina_Bool eina_debug_init(void) @@ -43,54 +860,46 @@ eina_debug_init(void) } // mark as initted _inited = EINA_TRUE; + eina_module_init(); + eina_mempool_init(); + eina_list_init(); + // For Windows support GetModuleFileName can be used // set up thread things eina_spinlock_new(&_eina_debug_lock); eina_spinlock_new(&_eina_debug_thread_lock); - eina_semaphore_new(&_eina_debug_monitor_return_sem, 0); self = pthread_self(); _eina_debug_thread_mainloop_set(&self); _eina_debug_thread_add(&self); -# if defined(HAVE_GETUID) && defined(HAVE_GETEUID) +#if defined(HAVE_GETUID) && defined(HAVE_GETEUID) // if we are setuid - don't debug! if (getuid() != geteuid()) return EINA_TRUE; #endif - // if someone uses the EFL_NODEBUG env var - do not do debugging. handy - // for when this debug code is buggy itself - if (getenv("EFL_NODEBUG")) return EINA_TRUE; - // connect to our debug daemon - _eina_debug_monitor_service_connect(); - // if we connected - set up the debug monitor properly - if (_eina_debug_monitor_service_fd >= 0) + // 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) { - // 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(); + 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 diff --git a/src/lib/eina/eina_debug.h b/src/lib/eina/eina_debug.h index cb1156587f..834927b1de 100644 --- a/src/lib/eina/eina_debug.h +++ b/src/lib/eina/eina_debug.h @@ -19,103 +19,236 @@ #ifndef EINA_DEBUG_H_ # define EINA_DEBUG_H_ -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif +# include "eina_config.h" +# include "eina_list.h" -# include -# include -# include -# include -# if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && defined(HAVE_DLADDR) && defined(HAVE_UNWIND) -# include -# ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -# endif -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include +/** + * @page eina_debug_main Eina Debug + * + * @date 2015 (created) + */ -# 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" +/** + * @addtogroup Eina_Debug + * @{ + */ -# define EINA_HAVE_DEBUG 1 +#define SERVER_PATH ".edebug" +#define SERVER_NAME "efl_debug" +#define SERVER_MASTER_PORT 0 +#define SERVER_SLAVE_PORT 1 -# define EINA_MAX_BT 256 - -typedef struct _Eina_Debug_Thread Eina_Debug_Thread; - -struct _Eina_Debug_Thread +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; +/** + * @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; -void _eina_debug_thread_add(void *th); -void _eina_debug_thread_del(void *th); -void _eina_debug_thread_mainloop_set(void *th); +/** + * @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); -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); +/** + * @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); -const char *_eina_debug_file_get(const char *fname); +/** + * @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); -void _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen); +/** + * @typedef Eina_Debug_Timer_Cb + * + * A callback for a timer + */ +typedef Eina_Bool (*Eina_Debug_Timer_Cb)(void *); -void _eina_debug_monitor_thread_start(void); -void _eina_debug_monitor_signal_init(void); -void _eina_debug_monitor_service_connect(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; -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); +/** + * @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; -# 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 +/** + * @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 +/** + * @} + */ diff --git a/src/lib/eina/eina_debug_bt.c b/src/lib/eina/eina_debug_bt.c index ac04e942bf..68083e271b 100644 --- a/src/lib/eina/eina_debug_bt.c +++ b/src/lib/eina/eina_debug_bt.c @@ -16,9 +16,30 @@ * if not, see . */ -#include "eina_debug.h" +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif -#ifdef EINA_HAVE_DEBUG +#ifdef HAVE_DLADDR +# include +#endif +#include + +#include "eina_debug.h" +#include "eina_debug_private.h" + +static Eina_Semaphore _wait_for_bts_sem; + +// _bt_buf[0] is always for mainloop, 1 + is for extra threads +static void ***_bt_buf; +static int *_bt_buf_len; +static struct timespec *_bt_ts; +static int *_bt_cpu; + +/* Used by trace timer */ +static double _trace_t0 = 0.0; + +static int _prof_get_op = EINA_DEBUG_OPCODE_INVALID; void _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen) @@ -46,4 +67,220 @@ _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen) else fprintf(f, "??\t -\n"); } } + +// a backtracer that uses libunwind to do the job +static inline int +_eina_debug_unwind_bt(void **bt, int max) +{ + unw_cursor_t cursor; + unw_context_t uc; + unw_word_t p; + int total; + + // create a context for unwinding + unw_getcontext(&uc); + // begin our work + unw_init_local(&cursor, &uc); + // walk up each stack frame until there is no more, storing it + for (total = 0; (unw_step(&cursor) > 0) && (total < max); total++) + { + unw_get_reg(&cursor, UNW_REG_IP, &p); + bt[total] = (void *)p; + } + // return our total backtrace stack size + return total; +} + +// this signal handler is called inside each and every thread when the +// thread gets a signal via pthread_kill(). this causes the thread to +// stop here inside this handler and "do something" then when this returns +// resume whatever it was doing like any signal handler +static void +_eina_debug_signal(int sig EINA_UNUSED, + siginfo_t *si EINA_UNUSED, + void *foo EINA_UNUSED) +{ + int i, slot = 0; + pthread_t self = pthread_self(); + clockid_t cid; + + // find which slot in the array of threads we have so we store info + // in the correct slot for us + if (self != _eina_debug_thread_mainloop) + { + for (i = 0; i < _eina_debug_thread_active_num; i++) + { + if (self == _eina_debug_thread_active[i].thread) + { + slot = i + 1; + goto found; + } + } + // we couldn't find out thread reference! help! + e_debug("EINA DEBUG ERROR: can't find thread slot!"); + eina_semaphore_release(&_wait_for_bts_sem, 1); + return; + } +found: + // store thread info like what cpu core we are on now (not reliable + // but hey - better than nothing), the amount of cpu time total + // we have consumed (it's cumulative so subtracing deltas can give + // you an average amount of cpu time consumed between now and the + // previous time we looked) and also a full backtrace + _bt_cpu[slot] = sched_getcpu(); + pthread_getcpuclockid(self, &cid); + clock_gettime(cid, &(_bt_ts[slot])); + _bt_buf_len[slot] = _eina_debug_unwind_bt(_bt_buf[slot], EINA_MAX_BT); + // now wake up the monitor to let them know we are done collecting our + // backtrace info + eina_semaphore_release(&_wait_for_bts_sem, 1); +} + + +// a quick and dirty local time point getter func - not portable +static inline double +get_time(void) +{ +#if defined(__clockid_t_defined) + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0); +#else + struct timeval timev; + gettimeofday(&timev, NULL); + return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0); #endif +} + +static void +_eina_debug_collect_bt(pthread_t pth EINA_UNUSED) +{ + // this async signals the thread to switch to the deebug signal handler + // and collect a backtrace and other info from inside the thread + //pthread_kill(pth, SIG); +} + +static Eina_Bool +_trace_cb(void *data) +{ + static Eina_Debug_Packet_Header *hdr = NULL; + + if (!hdr) + { + hdr = calloc(1, sizeof(*hdr)); + hdr->size = sizeof(Eina_Debug_Packet_Header); + hdr->thread_id = 0xFFFFFFFF; + hdr->opcode = _prof_get_op; + } + + eina_debug_dispatch(data, (void *)hdr); + return EINA_TRUE; +} + +static Eina_Debug_Error +_prof_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED) +{ + static int bts = 0; + int i; + if (!_trace_t0) _trace_t0 = get_time(); + // take a lock on grabbing thread debug info like backtraces + eina_spinlock_take(&_eina_debug_thread_lock); + // reset our "stack" of memory se use to dump thread info into + _eina_debug_chunk_tmp_reset(); + // get an array of pointers for the backtrace array for main + th + _bt_buf = _eina_debug_chunk_tmp_push + ((1 + _eina_debug_thread_active_num) * sizeof(void *)); + if (!_bt_buf) goto err; + // get an array of pointers for the timespec array for mainloop + th + _bt_ts = _eina_debug_chunk_tmp_push + ((1 + _eina_debug_thread_active_num) * sizeof(struct timespec)); + if (!_bt_ts) goto err; + // get an array of pointers for the cpuid array for mainloop + th + _bt_cpu = _eina_debug_chunk_tmp_push + ((1 + _eina_debug_thread_active_num) * sizeof(int)); + if (!_bt_cpu) goto err; + // now get an array of void pts for mainloop bt + _bt_buf[0] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *)); + if (!_bt_buf[0]) goto err; + // get an array of void ptrs for each thread we know about for bt + for (i = 0; i < _eina_debug_thread_active_num; i++) + { + _bt_buf[i + 1] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *)); + if (!_bt_buf[i + 1]) goto err; + } + // get an array of ints to stor the bt len for mainloop + threads + _bt_buf_len = _eina_debug_chunk_tmp_push + ((1 + _eina_debug_thread_active_num) * sizeof(int)); + // collect bt from the mainloop - always there + _eina_debug_collect_bt(_eina_debug_thread_mainloop); + // now collect per thread + for (i = 0; i < _eina_debug_thread_active_num; i++) + _eina_debug_collect_bt(_eina_debug_thread_active[i].thread); + // we're done probing. now collec all the "i'm done" msgs on the + // semaphore for every thread + mainloop + for (i = 0; i < (_eina_debug_thread_active_num + 1); i++) + eina_semaphore_lock(&_wait_for_bts_sem); + // we now have gotten all the data from all threadd + mainloop. + // we can process it now as we see fit, so release thread lock + //// XXX: some debug so we can see the bt's we collect - will go + // for (i = 0; i < (_eina_debug_thread_active_num + 1); i++) + // { + // _eina_debug_dump_fhandle_bt(stderr, _bt_buf[i], _bt_buf_len[i]); + // } +err: + eina_spinlock_release(&_eina_debug_thread_lock); + //// XXX: some debug just to see how well we perform - will go + bts++; + if (bts >= 10000) + { + double t; + t = get_time(); + e_debug("%1.5f bt's per sec", (double)bts / (t - _trace_t0)); + _trace_t0 = t; + bts = 0; + } + return EINA_DEBUG_OK; +} + +// profiling on with poll time gap as uint payload +static Eina_Debug_Error +_prof_on_cb(Eina_Debug_Session *session, int cid EINA_UNUSED, void *buffer, int size) +{ + unsigned int time; + if (size >= 4) + { + memcpy(&time, buffer, 4); + _trace_t0 = 0.0; + eina_debug_timer_add(time, _trace_cb, session); + } + return EINA_DEBUG_OK; +} + +static Eina_Debug_Error +_prof_off_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED) +{ + eina_debug_timer_add(0, NULL, NULL); + return EINA_DEBUG_OK; +} + +static const Eina_Debug_Opcode _OPS[] = { + {"profiler/on", NULL, &_prof_on_cb}, + {"profiler/off", NULL, &_prof_off_cb}, + {"profiler/bt_get", &_prof_get_op, &_prof_get_cb}, + {NULL, NULL, NULL} +}; + +Eina_Bool +_eina_debug_bt_init(void) +{ + eina_semaphore_new(&_wait_for_bts_sem, 0); + eina_debug_opcodes_register(NULL, _OPS, NULL); + return EINA_TRUE; +} + +Eina_Bool +_eina_debug_bt_shutdown(void) +{ + eina_semaphore_free(&_wait_for_bts_sem); + return EINA_TRUE; +} diff --git a/src/lib/eina/eina_debug_bt_file.c b/src/lib/eina/eina_debug_bt_file.c index 66636e3567..081c9f8336 100644 --- a/src/lib/eina/eina_debug_bt_file.c +++ b/src/lib/eina/eina_debug_bt_file.c @@ -16,9 +16,14 @@ * if not, see . */ -#include "eina_debug.h" +#include +#include +#include +#include -#ifdef EINA_HAVE_DEBUG +#include "eina_debug_private.h" + +extern Eina_Spinlock _eina_debug_lock; static unsigned int _table_num = 0; static unsigned int _table_size = 0; @@ -164,4 +169,3 @@ _eina_debug_file_get(const char *fname) } return file; } -#endif diff --git a/src/lib/eina/eina_debug_chunk.c b/src/lib/eina/eina_debug_chunk.c index de987462e1..36629198a0 100644 --- a/src/lib/eina/eina_debug_chunk.c +++ b/src/lib/eina/eina_debug_chunk.c @@ -16,21 +16,23 @@ * if not, see . */ +#include + #include "eina_debug.h" -#ifdef EINA_HAVE_DEBUG - -# ifdef HAVE_MMAP -# include +# ifdef HAVE_CONFIG_H +# include "config.h" # endif +#ifdef HAVE_MMAP +# include + // custom memory allocators to avoid malloc/free during backtrace handling // just in case we're inside some signal handler due to mem corruption and // are inside a malloc/free lock and thus would deadlock ourselves if we // allocated memory, so implement scratch space just big enough for what we // need and then some via either a static 8k+4k buffer pair or via a growable // mmaped mem chunk pair -# ifdef HAVE_MMAP // implement using mmap so we can grow if needed - unlikelt though static unsigned char *chunk1 = NULL; static unsigned char *chunk2 = NULL; @@ -243,4 +245,3 @@ _eina_debug_chunk_strdup(const char *str) strcpy(s, str); return s; } -#endif diff --git a/src/lib/eina/eina_debug_cpu.c b/src/lib/eina/eina_debug_cpu.c new file mode 100644 index 0000000000..e864592001 --- /dev/null +++ b/src/lib/eina/eina_debug_cpu.c @@ -0,0 +1,307 @@ +# ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +# endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eina_debug.h" +#include "eina_types.h" +#include "eina_list.h" +#include "eina_mempool.h" +#include "eina_util.h" +#include "eina_evlog.h" +#include "eina_debug_private.h" + +volatile int _eina_debug_sysmon_reset = 0; +volatile int _eina_debug_sysmon_active = 0; +volatile int _eina_debug_evlog_active = 0; + +static Eina_Lock _sysmon_lock; + +static Eina_Bool _sysmon_thread_runs = EINA_FALSE; +static pthread_t _sysmon_thread; + +// this is a DEDICATED thread tojust collect system info and to have the +// least impact it can on a cpu core or system. all this does right now +// is sleep and wait for a command to begin polling for the cpu state. +// right now that means iterating through cpu's and getting their cpu +// frequency to match up with event logs. +static void * +_sysmon(void *_data EINA_UNUSED) +{ + static int cpufreqs[64] = { 0 }; + int i, fd, freq; + char buf[256], path[256]; + ssize_t red; +#if defined(__clockid_t_defined) + static struct timespec t_last = { 0, 0 }; + static Eina_Debug_Thread *prev_threads = NULL; + static int prev_threads_num = 0; + int j, cpu; + Eina_Bool prev_threads_redo; + clockid_t cid; + struct timespec t, t_now; + unsigned long long tim_span, tim1, tim2; +#endif + + // set a name for this thread for system debugging +#ifdef EINA_HAVE_PTHREAD_SETNAME +# ifndef __linux__ + pthread_set_name_np +# else + pthread_setname_np +# endif + (pthread_self(), "Edbg-sys"); +#endif + for (;;) + { + // wait on a mutex that will be locked for as long as this + // threead is not meant to go running + eina_lock_take(&_sysmon_lock); + // if we need to reset as we just started polling system stats... + if (_eina_debug_sysmon_reset) + { + _eina_debug_sysmon_reset = 0; + // clear out all the clocks for threads +#if defined(__clockid_t_defined) + // reset the last clock timestamp when we checked to "now" + clock_gettime(CLOCK_MONOTONIC, &t); + t_last = t; + // walk over all threads + eina_spinlock_take(&_eina_debug_thread_lock); + for (i = 0; i < _eina_debug_thread_active_num; i++) + { + // get the correct clock id to use for the target thread + pthread_getcpuclockid + (_eina_debug_thread_active[i].thread, &cid); + // get the clock cpu time accumulation for that threas + clock_gettime(cid, &t); + _eina_debug_thread_active[i].clok = t; + } + eina_spinlock_release(&_eina_debug_thread_lock); +#endif + // clear all the cpu freq (up to 64 cores) to 0 + for (i = 0; i < 64; i++) cpufreqs[i] = 0; + } + eina_lock_release(&_sysmon_lock); + +#if defined(__clockid_t_defined) + // get the current time + clock_gettime(CLOCK_MONOTONIC, &t_now); + tim1 = (t_last.tv_sec * 1000000000LL) + t_last.tv_nsec; + // the time span between now and last time we checked + tim_span = ((t_now.tv_sec * 1000000000LL) + t_now.tv_nsec) - tim1; + // if the time span is non-zero we might get sensible results + if (tim_span > 0) + { + prev_threads_redo = EINA_FALSE; + eina_spinlock_take(&_eina_debug_thread_lock); + // figure out if the list of thread id's has changed since + // our last poll. this is imporant as we need to set the + // thread cpu usage to 0 for threads that have disappeared + if (prev_threads_num != _eina_debug_thread_active_num) + prev_threads_redo = EINA_TRUE; + else + { + // XXX: isolate this out of hot path + for (i = 0; i < _eina_debug_thread_active_num; i++) + { + if (_eina_debug_thread_active[i].thread != + prev_threads[i].thread) + { + prev_threads_redo = EINA_TRUE; + break; + } + } + } + for (i = 0; i < _eina_debug_thread_active_num; i++) + { + pthread_t thread = _eina_debug_thread_active[i].thread; + // get the clock for the thread and its cpu time usage + pthread_getcpuclockid(thread, &cid); + clock_gettime(cid, &t); + // calculate a long timestamp (64bits) + tim1 = (_eina_debug_thread_active[i].clok.tv_sec * 1000000000LL) + + _eina_debug_thread_active[i].clok.tv_nsec; + // and get the difference in clock time in NS + tim2 = ((t.tv_sec * 1000000000LL) + t.tv_nsec) - tim1; + // and that as percentage of the timespan + cpu = (int)((100 * (int)tim2) / (int)tim_span); + // round to the nearest 10 percent - rough anyway + cpu = ((cpu + 5) / 10) * 10; + if (cpu > 100) cpu = 100; + // if the usage changed since last time we checked... + if (cpu != _eina_debug_thread_active[i].val) + { + // log this change + snprintf(buf, sizeof(buf), "*CPUUSED %llu", + (unsigned long long)thread); + snprintf(path, sizeof(path), "%i", _eina_debug_thread_active[i].val); + eina_evlog(buf, NULL, 0.0, path); + snprintf(path, sizeof(path), "%i", cpu); + eina_evlog(buf, NULL, 0.0, path); + // store the clock time + cpu we got for next poll + _eina_debug_thread_active[i].val = cpu; + } + _eina_debug_thread_active[i].clok = t; + } + // so threads changed between this poll and last so we need + // to redo our mapping/storage of them + if (prev_threads_redo) + { + prev_threads_redo = EINA_FALSE; + // find any threads from our last run that do not + // exist now in our new list of threads + for (j = 0; j < prev_threads_num; j++) + { + for (i = 0; i < _eina_debug_thread_active_num; i++) + { + if (prev_threads[j].thread == + _eina_debug_thread_active[i].thread) break; + } + // thread was there before and not now + if (i == _eina_debug_thread_active_num) + { + // log it finishing - ie 0 + snprintf(buf, sizeof(buf), "*CPUUSED %llu", + (unsigned long long) + prev_threads[i].thread); + eina_evlog(buf, NULL, 0.0, "0"); + } + } + // if the thread count changed then allocate a new shadow + // buffer of thread id's etc. + if (prev_threads_num != _eina_debug_thread_active_num) + { + if (prev_threads) free(prev_threads); + prev_threads_num = _eina_debug_thread_active_num; + prev_threads = malloc(prev_threads_num * + sizeof(Eina_Debug_Thread)); + } + // store the thread handles/id's + for (i = 0; i < prev_threads_num; i++) + prev_threads[i].thread = + _eina_debug_thread_active[i].thread; + } + eina_spinlock_release(&_eina_debug_thread_lock); + t_last = t_now; + } +#endif + // poll up to 64 cpu cores for cpufreq info to log alongside + // the evlog call data + for (i = 0; i < 64; i++) + { + // look at sysfs nodes per cpu + snprintf + (buf, sizeof(buf), + "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq", i); + fd = open(buf, O_RDONLY); + freq = 0; + // if the node is there, read it + if (fd >= 0) + { + // really low overhead read from cpufreq node (just an int) + red = read(fd, buf, sizeof(buf) - 1); + if (red > 0) + { + // read something - it should be an int with whitespace + buf[red] = 0; + freq = atoi(buf); + // move to mhz + freq = (freq + 500) / 1000; + // round mhz to the nearest 100mhz - to have less noise + freq = ((freq + 50) / 100) * 100; + } + // close the fd so we can freshly poll next time around + close(fd); + } + // node not there - ran out of cpu's to poll? + else break; + // if the current frequency changed vs previous poll, then log + if (freq != cpufreqs[i]) + { + snprintf(buf, sizeof(buf), "*CPUFREQ %i", i); + snprintf(path, sizeof(path), "%i", freq); + eina_evlog(buf, NULL, 0.0, path); + cpufreqs[i] = freq; + } + } + usleep(1000); // 1ms sleep + } + return NULL; +} + +static Eina_Debug_Error +_cpufreq_on_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED) +{ + if (!_eina_debug_evlog_active) + { + _eina_debug_evlog_active = 1; + eina_evlog_start(); + } + if (!_eina_debug_sysmon_active) + { + _eina_debug_sysmon_reset = 1; + _eina_debug_sysmon_active = 1; + eina_lock_release(&_sysmon_lock); + } + return EINA_DEBUG_OK; +} + +static Eina_Debug_Error +_cpufreq_off_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED) +{ + if (_eina_debug_sysmon_active) + { + eina_lock_take(&_sysmon_lock); + _eina_debug_sysmon_active = 0; + } + if (_eina_debug_evlog_active) + { + eina_evlog_stop(); + _eina_debug_evlog_active = 0; + } + return EINA_DEBUG_OK; +} + +static const Eina_Debug_Opcode _OPS[] = { + {"cpufreq/on", NULL, &_cpufreq_on_cb}, + {"cpufreq/off", NULL, &_cpufreq_off_cb}, + {NULL, NULL, NULL} +}; + +Eina_Bool +_eina_debug_cpu_init(void) +{ + // if it's already running - we're good. + if (!_sysmon_thread_runs) + { + int err; + eina_lock_new(&_sysmon_lock); + eina_lock_take(&_sysmon_lock); + err = pthread_create(&_sysmon_thread, NULL, _sysmon, NULL); + if (err != 0) + { + e_debug("EINA DEBUG ERROR: Can't create debug sysmon thread!"); + abort(); + } + _sysmon_thread_runs = EINA_TRUE; + } + eina_debug_opcodes_register(NULL, _OPS, NULL); + return EINA_TRUE; +} + +Eina_Bool +_eina_debug_cpu_shutdown(void) +{ + return EINA_TRUE; +} + diff --git a/src/lib/eina/eina_debug_monitor.c b/src/lib/eina/eina_debug_monitor.c deleted file mode 100644 index b2ae5a47f7..0000000000 --- a/src/lib/eina/eina_debug_monitor.c +++ /dev/null @@ -1,708 +0,0 @@ -/* EINA - EFL data type library - * Copyright (C) 2015 Carsten Haitzler - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; - * if not, see . - */ - -#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 - -#ifdef EINA_HAVE_DEBUG - -#define DEBUG_SERVER ".ecore/efl_debug/0" - -volatile int _eina_debug_sysmon_reset = 0; -volatile int _eina_debug_evlog_active = 0; - -int _eina_debug_monitor_service_fd = -1; -Eina_Semaphore _eina_debug_monitor_return_sem; -Eina_Lock _eina_debug_sysmon_lock; - -static Eina_Bool _monitor_thread_runs = EINA_FALSE; -static pthread_t _monitor_thread; -static pthread_t _sysmon_thread; - -// _bt_buf[0] is always for mainloop, 1 + is for extra threads -static void ***_bt_buf; -static int *_bt_buf_len; -static struct timespec *_bt_ts; -static int *_bt_cpu; - -// a backtracer that uses libunwind to do the job -static inline int -_eina_debug_unwind_bt(void **bt, int max) -{ - unw_cursor_t cursor; - unw_context_t uc; - unw_word_t p; - int total; - - // create a context for unwinding - unw_getcontext(&uc); - // begin our work - unw_init_local(&cursor, &uc); - // walk up each stack frame until there is no more, storing it - for (total = 0; (unw_step(&cursor) > 0) && (total < max); total++) - { - unw_get_reg(&cursor, UNW_REG_IP, &p); - bt[total] = (void *)p; - } - // return our total backtrace stack size - return total; -} - -static inline void -_bt_cpu_set(int slot) -{ -#if HAVE_SCHED_GETCPU - _bt_cpu[slot] = sched_getcpu(); -#else - _bt_cpu[slot] = -1; -#endif -} - -static inline void -_bt_ts_set(int slot, pthread_t self) -{ -#if defined(__clockid_t_defined) - clockid_t cid; - pthread_getcpuclockid(self, &cid); - clock_gettime(cid, &(_bt_ts[slot])); -#else - (void) self; - memset(&(_bt_ts[slot]), 0, sizeof(struct timespec)); -#endif -} - -// this signal handler is called inside each and every thread when the -// thread gets a signal via pthread_kill(). this causes the thread to -// stop here inside this handler and "do something" then when this returns -// resume whatever it was doing like any signal handler -static void -_eina_debug_signal(int sig EINA_UNUSED, - siginfo_t *si EINA_UNUSED, - void *foo EINA_UNUSED) -{ - int i, slot = 0; - pthread_t self = pthread_self(); - - // find which slot in the array of threads we have so we store info - // in the correct slot for us - if (self != _eina_debug_thread_mainloop) - { - for (i = 0; i < _eina_debug_thread_active_num; i++) - { - if (self == _eina_debug_thread_active[i].thread) - { - slot = i + 1; - goto found; - } - } - // we couldn't find out thread reference! help! - fprintf(stderr, "EINA DEBUG ERROR: can't find thread slot!\n"); - eina_semaphore_release(&_eina_debug_monitor_return_sem, 1); - return; - } -found: - // store thread info like what cpu core we are on now (not reliable - // but hey - better than nothing), the amount of cpu time total - // we have consumed (it's cumulative so subtracing deltas can give - // you an average amount of cpu time consumed between now and the - // previous time we looked) and also a full backtrace - _bt_cpu_set(slot); - _bt_ts_set(slot, self); - _bt_buf_len[slot] = _eina_debug_unwind_bt(_bt_buf[slot], EINA_MAX_BT); - // now wake up the monitor to let them know we are done collecting our - // backtrace info - eina_semaphore_release(&_eina_debug_monitor_return_sem, 1); -} - -// we shall sue SIGPROF as out signal for pausing threads and having them -// dump a backtrace for polling based profiling -#define SIG SIGPROF - -// a quick and dirty local time point getter func - not portable -static inline double -get_time(void) -{ -#if defined(__clockid_t_defined) - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); - return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0); -#else - struct timeval timev; - gettimeofday(&timev, NULL); - return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0); -#endif -} - -static void -_eina_debug_collect_bt(pthread_t pth) -{ - // this async signals the thread to switch to the deebug signal handler - // and collect a backtrace and other info from inside the thread - pthread_kill(pth, SIG); -} - -// this is a DEDICATED thread tojust collect system info and to have the -// least impact it can on a cpu core or system. all this does right now -// is sleep and wait for a command to begin polling for the cpu state. -// right now that means iterating through cpu's and getting their cpu -// frequency to match up with event logs. -static void * -_eina_debug_sysmon(void *_data EINA_UNUSED) -{ - static int cpufreqs[64] = { 0 }; - int i, fd, freq; - char buf[256], path[256]; - ssize_t red; -#if defined(__clockid_t_defined) - static struct timespec t_last = { 0, 0 }; - static Eina_Debug_Thread *prev_threads = NULL; - static int prev_threads_num = 0; - int j, cpu; - Eina_Bool prev_threads_redo; - clockid_t cid; - struct timespec t, t_now; - unsigned long long tim_span, tim1, tim2; -#endif - - // set a name for this thread for system debugging -#ifdef EINA_HAVE_PTHREAD_SETNAME -# ifndef __linux__ - pthread_set_name_np -# else - pthread_setname_np -# endif - (pthread_self(), "Edbg-sys"); -#endif - for (;;) - { - // wait on a mutex that will be locked for as long as this - // threead is not meant to go running - eina_lock_take(&_eina_debug_sysmon_lock); - // if we need to reset as we just started polling system stats... - if (_eina_debug_sysmon_reset) - { - _eina_debug_sysmon_reset = 0; - // clear out all the clocks for threads -#if defined(__clockid_t_defined) - // reset the last clock timestamp when we checked to "now" - clock_gettime(CLOCK_MONOTONIC, &t); - t_last = t; - // walk over all threads - eina_spinlock_take(&_eina_debug_thread_lock); - for (i = 0; i < _eina_debug_thread_active_num; i++) - { - // get the correct clock id to use for the target thread - pthread_getcpuclockid - (_eina_debug_thread_active[i].thread, &cid); - // get the clock cpu time accumulation for that threas - clock_gettime(cid, &t); - _eina_debug_thread_active[i].clok = t; - } - eina_spinlock_release(&_eina_debug_thread_lock); -#endif - // clear all the cpu freq (up to 64 cores) to 0 - for (i = 0; i < 64; i++) cpufreqs[i] = 0; - } - eina_lock_release(&_eina_debug_sysmon_lock); - -#if defined(__clockid_t_defined) - // get the current time - clock_gettime(CLOCK_MONOTONIC, &t_now); - tim1 = (t_last.tv_sec * 1000000000LL) + t_last.tv_nsec; - // the time span between now and last time we checked - tim_span = ((t_now.tv_sec * 1000000000LL) + t_now.tv_nsec) - tim1; - // if the time span is non-zero we might get sensible results - if (tim_span > 0) - { - prev_threads_redo = EINA_FALSE; - eina_spinlock_take(&_eina_debug_thread_lock); - // figure out if the list of thread id's has changed since - // our last poll. this is imporant as we need to set the - // thread cpu usage to 0 for threads that have disappeared - if (prev_threads_num != _eina_debug_thread_active_num) - prev_threads_redo = EINA_TRUE; - else - { - // XXX: isolate this out of hot path - for (i = 0; i < _eina_debug_thread_active_num; i++) - { - if (_eina_debug_thread_active[i].thread != - prev_threads[i].thread) - { - prev_threads_redo = EINA_TRUE; - break; - } - } - } - for (i = 0; i < _eina_debug_thread_active_num; i++) - { - pthread_t thread = _eina_debug_thread_active[i].thread; - // get the clock for the thread and its cpu time usage - pthread_getcpuclockid(thread, &cid); - clock_gettime(cid, &t); - // calculate a long timestamp (64bits) - tim1 = (_eina_debug_thread_active[i].clok.tv_sec * 1000000000LL) + - _eina_debug_thread_active[i].clok.tv_nsec; - // and get the difference in clock time in NS - tim2 = ((t.tv_sec * 1000000000LL) + t.tv_nsec) - tim1; - // and that as percentage of the timespan - cpu = (int)((100 * (int)tim2) / (int)tim_span); - // round to the nearest 10 percent - rough anyway - cpu = ((cpu + 5) / 10) * 10; - if (cpu > 100) cpu = 100; - // if the usage changed since last time we checked... - if (cpu != _eina_debug_thread_active[i].val) - { - // log this change - snprintf(buf, sizeof(buf), "*CPUUSED %llu", - (unsigned long long)thread); - snprintf(path, sizeof(path), "%i", _eina_debug_thread_active[i].val); - eina_evlog(buf, NULL, 0.0, path); - snprintf(path, sizeof(path), "%i", cpu); - eina_evlog(buf, NULL, 0.0, path); - // store the clock time + cpu we got for next poll - _eina_debug_thread_active[i].val = cpu; - } - _eina_debug_thread_active[i].clok = t; - } - // so threads changed between this poll and last so we need - // to redo our mapping/storage of them - if (prev_threads_redo) - { - prev_threads_redo = EINA_FALSE; - // find any threads from our last run that do not - // exist now in our new list of threads - for (j = 0; j < prev_threads_num; j++) - { - for (i = 0; i < _eina_debug_thread_active_num; i++) - { - if (prev_threads[j].thread == - _eina_debug_thread_active[i].thread) break; - } - // thread was there before and not now - if (i == _eina_debug_thread_active_num) - { - // log it finishing - ie 0 - snprintf(buf, sizeof(buf), "*CPUUSED %llu", - (unsigned long long) - prev_threads[i].thread); - eina_evlog(buf, NULL, 0.0, "0"); - } - } - // if the thread count changed then allocate a new shadow - // buffer of thread id's etc. - if (prev_threads_num != _eina_debug_thread_active_num) - { - if (prev_threads) free(prev_threads); - prev_threads_num = _eina_debug_thread_active_num; - prev_threads = malloc(prev_threads_num * - sizeof(Eina_Debug_Thread)); - } - // store the thread handles/id's - for (i = 0; i < prev_threads_num; i++) - prev_threads[i].thread = - _eina_debug_thread_active[i].thread; - } - eina_spinlock_release(&_eina_debug_thread_lock); - t_last = t_now; - } -#endif - // poll up to 64 cpu cores for cpufreq info to log alongside - // the evlog call data - for (i = 0; i < 64; i++) - { - // look at sysfs nodes per cpu - snprintf - (buf, sizeof(buf), - "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq", i); - fd = open(buf, O_RDONLY); - freq = 0; - // if the node is there, read it - if (fd >= 0) - { - // really low overhead read from cpufreq node (just an int) - red = read(fd, buf, sizeof(buf) - 1); - if (red > 0) - { - // read something - it should be an int with whitespace - buf[red] = 0; - freq = atoi(buf); - // move to mhz - freq = (freq + 500) / 1000; - // round mhz to the nearest 100mhz - to have less noise - freq = ((freq + 50) / 100) * 100; - } - // close the fd so we can freshly poll next time around - close(fd); - } - // node not there - ran out of cpu's to poll? - else break; - // if the current frequency changed vs previous poll, then log - if (freq != cpufreqs[i]) - { - snprintf(buf, sizeof(buf), "*CPUFREQ %i", i); - snprintf(path, sizeof(path), "%i", freq); - eina_evlog(buf, NULL, 0.0, path); - cpufreqs[i] = freq; - } - } - usleep(1000); // 1ms sleep - } - return NULL; -} - -// this is a DEDICATED debug thread to monitor the application so it works -// even if the mainloop is blocked or the app otherwise deadlocked in some -// way. this is an alternative to using external debuggers so we can get -// users or developers to get useful information about an app at all times -static void * -_eina_debug_monitor(void *_data EINA_UNUSED) -{ - int bts = 0, ret, max_fd; - double t0, t; - fd_set rfds, wfds, exfds; - struct timeval tv = { 0, 0 }; - // some state for debugging - unsigned int poll_time = 1000; - Eina_Bool poll_on = EINA_FALSE; - Eina_Bool poll_trace = EINA_FALSE; - Eina_Bool poll_cpu = EINA_FALSE; - - t0 = get_time(); - // set a name for this thread for system debugging -#ifdef EINA_HAVE_PTHREAD_SETNAME -# ifndef __linux__ - pthread_set_name_np -# else - pthread_setname_np -# endif - (pthread_self(), "Edbg-mon"); -#endif - // sit forever processing commands or timeouts in the debug monitor - // thread - this is separate to the rest of the app so it shouldn't - // impact the application specifically - for (;;) - { - int i; - - // set up data for select like read fd's - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&exfds); - // the only fd we care about - out debug daemon connection - FD_SET(_eina_debug_monitor_service_fd, &rfds); - max_fd = _eina_debug_monitor_service_fd; - // if we are in a polling mode then set up a timeout and wait for it - if (poll_on) - { - if ((tv.tv_sec == 0) && (tv.tv_usec == 0)) - { - tv.tv_sec = 0; - tv.tv_usec = poll_time; - } - ret = select(max_fd + 1, &rfds, &wfds, &exfds, &tv); - } - // we have no timeout - so wait forever for a message from debugd - else ret = select(max_fd + 1, &rfds, &wfds, &exfds, NULL); - // if the fd for debug daemon says it's alive, process it - if ((ret == 1) && (FD_ISSET(_eina_debug_monitor_service_fd, &rfds))) - { - // collect a single op on the debug daemon control fd - char op[5]; - int size; - unsigned char *data; - - // get the opcode and stor in op - guarantee its 0 byte terminated - data = NULL; - size = _eina_debug_monitor_service_read(op, &data); - // if not negative - we have a real message - if (size >= 0) - { - // profiling on with poll time gap as uint payload - if (!strcmp(op, "PLON")) - { - if (size >= 4) memcpy(&poll_time, data, 4); - poll_on = EINA_TRUE; - poll_trace = EINA_TRUE; - } - // profiling off with no payload - else if (!strcmp(op, "PLOF")) - { - poll_time = 1000; - poll_on = EINA_FALSE; - poll_trace = EINA_FALSE; - } - // enable evlog - else if (!strcmp(op, "EVON")) - { - if (!_eina_debug_evlog_active) - { - _eina_debug_evlog_active = 1; - eina_evlog_start(); - _eina_debug_sysmon_reset = 1; - eina_lock_release(&_eina_debug_sysmon_lock); - } - } - // stop evlog - else if (!strcmp(op, "EVOF")) - { - if (_eina_debug_evlog_active) - { - eina_lock_take(&_eina_debug_sysmon_lock); - eina_evlog_stop(); - _eina_debug_evlog_active = 0; - } - } - // enable evlog - else if (!strcmp(op, "CPON")) - { - if (size >= 4) memcpy(&poll_time, data, 4); - poll_on = EINA_TRUE; - poll_cpu = EINA_TRUE; - } - // stop evlog - else if (!strcmp(op, "CPOF")) - { - poll_on = EINA_FALSE; - poll_cpu = EINA_FALSE; - } - // fetch the evlog - else if (!strcmp(op, "EVLG")) - { - Eina_Evlog_Buf *evlog = eina_evlog_steal(); - if ((evlog) && (evlog->buf)) - { - char tmp[12]; - unsigned int *tmpsize = (unsigned int *)(tmp + 0); - char *op2 = "EVLG"; - unsigned int *overflow = (unsigned int *)(tmp + 8); - - *tmpsize = (sizeof(tmp) - 4) + evlog->top; - memcpy(tmp + 4, op2, 4); - *overflow = evlog->overflow; - write(_eina_debug_monitor_service_fd, - tmp, sizeof(tmp)); - write(_eina_debug_monitor_service_fd, - evlog->buf, evlog->top); - } - } - // something we don't understand - else fprintf(stderr, - "EINA DEBUG ERROR: " - "Uunknown command %s\n", op); - free(data); - } - // major failure on debug daemon control fd - get out of here - else goto fail; - } - - if (poll_on) - { - if (poll_trace) - { - // take a lock on grabbing thread debug info like backtraces - eina_spinlock_take(&_eina_debug_thread_lock); - // reset our "stack" of memory se use to dump thread info into - _eina_debug_chunk_tmp_reset(); - // get an array of pointers for the backtrace array for main + th - _bt_buf = _eina_debug_chunk_tmp_push - ((1 + _eina_debug_thread_active_num) * sizeof(void *)); - if (!_bt_buf) goto err; - // get an array of pointers for the timespec array for mainloop + th - _bt_ts = _eina_debug_chunk_tmp_push - ((1 + _eina_debug_thread_active_num) * sizeof(struct timespec)); - if (!_bt_ts) goto err; - // get an array of pointers for the cpuid array for mainloop + th - _bt_cpu = _eina_debug_chunk_tmp_push - ((1 + _eina_debug_thread_active_num) * sizeof(int)); - if (!_bt_cpu) goto err; - // now get an array of void pts for mainloop bt - _bt_buf[0] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *)); - if (!_bt_buf[0]) goto err; - // get an array of void ptrs for each thread we know about for bt - for (i = 0; i < _eina_debug_thread_active_num; i++) - { - _bt_buf[i + 1] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *)); - if (!_bt_buf[i + 1]) goto err; - } - // get an array of ints to stor the bt len for mainloop + threads - _bt_buf_len = _eina_debug_chunk_tmp_push - ((1 + _eina_debug_thread_active_num) * sizeof(int)); - // collect bt from the mainloop - always there - _eina_debug_collect_bt(_eina_debug_thread_mainloop); - // now collect per thread - for (i = 0; i < _eina_debug_thread_active_num; i++) - _eina_debug_collect_bt(_eina_debug_thread_active[i].thread); - // we're done probing. now collec all the "i'm done" msgs on the - // semaphore for every thread + mainloop - for (i = 0; i < (_eina_debug_thread_active_num + 1); i++) - eina_semaphore_lock(&_eina_debug_monitor_return_sem); - // we now have gotten all the data from all threadd + mainloop. - // we can process it now as we see fit, so release thread lock -//// XXX: some debug so we can see the bt's we collect - will go -// for (i = 0; i < (_eina_debug_thread_active_num + 1); i++) -// { -// _eina_debug_dump_fhandle_bt(stderr, _bt_buf[i], _bt_buf_len[i]); -// } -err: - eina_spinlock_release(&_eina_debug_thread_lock); -//// XXX: some debug just to see how well we perform - will go - bts++; - if (bts >= 10000) - { - t = get_time(); - fprintf(stderr, "%1.5f bt's per sec\n", (double)bts / (t - t0)); - t0 = t; - bts = 0; - } - } - if (poll_cpu) - { - // XXX: opendir /proc/sefl/task -// eina_evlog("*cpustat", NULL, 0.0, cpustat); - } - } - } -fail: - // we failed - get out of here and disconnect to debugd - close(_eina_debug_monitor_service_fd); - _eina_debug_monitor_service_fd = -1; - // sleep forever because there is currently no logic to join this thread - for (;;) pause(); - return NULL; -} - -// start up the debug monitor if we haven't already -void -_eina_debug_monitor_thread_start(void) -{ - int err; - sigset_t oldset, newset; - - // if it's already running - we're good. - if (_monitor_thread_runs) return; - // create debug monitor thread - sigemptyset(&newset); - sigaddset(&newset, SIGPIPE); - sigaddset(&newset, SIGALRM); - sigaddset(&newset, SIGCHLD); - sigaddset(&newset, SIGUSR1); - sigaddset(&newset, SIGUSR2); - sigaddset(&newset, SIGHUP); - sigaddset(&newset, SIGQUIT); - sigaddset(&newset, SIGINT); - sigaddset(&newset, SIGTERM); -#ifdef SIGPWR - sigaddset(&newset, SIGPWR); -#endif - sigprocmask(SIG_BLOCK, &newset, &oldset); - eina_lock_new(&_eina_debug_sysmon_lock); - eina_lock_take(&_eina_debug_sysmon_lock); - err = pthread_create(&_sysmon_thread, NULL, _eina_debug_sysmon, NULL); - if (err != 0) - { - fprintf(stderr, "EINA DEBUG ERROR: Can't create debug thread 1!\n"); - abort(); - } - err = pthread_create(&_monitor_thread, NULL, _eina_debug_monitor, NULL); - sigprocmask(SIG_SETMASK, &oldset, NULL); - if (err != 0) - { - fprintf(stderr, "EINA DEBUG ERROR: Can't create debug thread 2!\n"); - abort(); - } - _monitor_thread_runs = EINA_TRUE; -} - -void -_eina_debug_monitor_signal_init(void) -{ - struct sigaction sa; - - // set up signal handler for our profiling signal - eevery thread should - // obey this (this is the case on linux - other OSs may vary) - sa.sa_sigaction = _eina_debug_signal; - sa.sa_flags = SA_RESTART | SA_SIGINFO; - sigemptyset(&sa.sa_mask); - if (sigaction(SIG, &sa, NULL) != 0) - fprintf(stderr, "EINA DEBUG ERROR: Can't set up sig %i handler!\n", SIG); -} - -static const char * -_socket_home_get(void) -{ - static char *dir; - - if (dir) return dir; - // get possible debug daemon socket directory base -#if defined(HAVE_GETUID) && defined(HAVE_GETEUID) - if (getuid() == geteuid()) dir = getenv("XDG_RUNTIME_DIR"); -#endif - if (!dir) dir = (char *)eina_environment_home_get(); - if (!dir) dir = (char *)eina_environment_tmp_get(); - dir = strdup(dir); - return dir; -} - -// connect to efl_debugd -void -_eina_debug_monitor_service_connect(void) -{ - char buf[4096]; - int fd, socket_unix_len, curstate = 0; - struct sockaddr_un socket_unix; - - // try this socket file - it will likely be: - // ~/.ecore/efl_debug/0 - // or maybe - // /var/run/UID/.ecore/efl_debug/0 - // either way a 4k buffer should be ebough ( if it's not we're on an - // insane system) - snprintf(buf, sizeof(buf), "%s/%s", _socket_home_get(), DEBUG_SERVER); - // create the socket - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) goto err; - // set the socket to close when we exec things so they don't inherit it - if (!eina_file_close_on_exec(fd, EINA_TRUE)) goto err; - // set up some socket options on addr re-use - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate, - sizeof(curstate)) < 0) - goto err; - // sa that it's a unix socket and where the path is - socket_unix.sun_family = AF_UNIX; - strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path)); -#define LENGTH_OF_SOCKADDR_UN(s) \ - (strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path)) - socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix); - // actually conenct to efl_debugd service - if (connect(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) - goto err; - // we succeeded - store fd - _eina_debug_monitor_service_fd = fd; - return; -err: - // some kind of connection failure here, so close a valid socket and - // get out of here - if (fd >= 0) close(fd); -} -#endif diff --git a/src/lib/eina/eina_debug_private.h b/src/lib/eina/eina_debug_private.h new file mode 100644 index 0000000000..2f733b5f11 --- /dev/null +++ b/src/lib/eina/eina_debug_private.h @@ -0,0 +1,73 @@ +#ifndef EINA_DEBUG_PRIVATE_H_ +# define EINA_DEBUG_PRIVATE_H_ + +# include "eina_config.h" +# include "eina_lock.h" +# include "eina_thread.h" + +#ifdef DEBUGON +# define e_debug(fmt, args...) fprintf(stderr, "%d:"__FILE__":%s/%d : " fmt "\n", getpid(), __FUNCTION__, __LINE__, ##args) +# define e_debug_begin(fmt, args...) fprintf(stderr, "%d:"__FILE__":%s/%d : " fmt "", getpid(), __FUNCTION__, __LINE__, ##args) +# define e_debug_continue(fmt, args...) fprintf(stderr, fmt, ##args) +# define e_debug_end() fprintf(stderr, "\n") +#else +# define e_debug(x...) do { } while (0) +# define e_debug_begin(x...) do { } while (0) +# define e_debug_continue(x...) do { } while (0) +# define e_debug_end(x...) do { } while (0) +#endif + +typedef struct _Eina_Debug_Session Eina_Debug_Session; + +typedef struct _Eina_Debug_Thread Eina_Debug_Thread; + +struct _Eina_Debug_Thread +{ + pthread_t thread; + + Eina_Debug_Session *cmd_session; + void *cmd_buffer; + int cmd_result; + +#if defined(__clockid_t_defined) + struct timespec clok; +#endif + int thread_id; + int val; +}; + +extern Eina_Spinlock _eina_debug_lock; +extern Eina_Spinlock _eina_debug_thread_lock; +extern pthread_t _eina_debug_thread_mainloop; +extern Eina_Debug_Thread *_eina_debug_thread_active; +extern int _eina_debug_thread_active_num; + +/* TEMP: should be private to debug thread module */ +void _eina_debug_thread_add(void *th); +void _eina_debug_thread_del(void *th); +void _eina_debug_thread_mainloop_set(void *th); + +void *_eina_debug_chunk_push(int size); +void *_eina_debug_chunk_realloc(int size); +char *_eina_debug_chunk_strdup(const char *str); +void *_eina_debug_chunk_tmp_push(int size); +void _eina_debug_chunk_tmp_reset(void); + +const char *_eina_debug_file_get(const char *fname); + +void _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen); +#define EINA_MAX_BT 256 +#define EINA_BT(file) \ + do { \ + void *bt[EINA_MAX_BT]; \ + int btlen = backtrace((void **)bt, EINA_MAX_BT); \ + _eina_debug_dump_fhandle_bt(file, bt, btlen); \ + } while (0) + +Eina_Bool _eina_debug_cpu_init(void); +Eina_Bool _eina_debug_cpu_shutdown(void); + +Eina_Bool _eina_debug_bt_init(void); +Eina_Bool _eina_debug_bt_shutdown(void); +#endif + diff --git a/src/lib/eina/eina_debug_proto.c b/src/lib/eina/eina_debug_proto.c deleted file mode 100644 index 8ac88744d6..0000000000 --- a/src/lib/eina/eina_debug_proto.c +++ /dev/null @@ -1,125 +0,0 @@ -/* EINA - EFL data type library - * Copyright (C) 2015 Carsten Haitzler - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; - * if not, see . - */ - -#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 diff --git a/src/lib/eina/eina_debug_thread.c b/src/lib/eina/eina_debug_thread.c index 9f4826aea1..f04fc19362 100644 --- a/src/lib/eina/eina_debug_thread.c +++ b/src/lib/eina/eina_debug_thread.c @@ -17,8 +17,7 @@ */ #include "eina_debug.h" - -#ifdef EINA_HAVE_DEBUG +#include "eina_debug_private.h" // a really simple store of currently known active threads. the mainloop is // special and inittied at debug init time - assuming eina inits in the @@ -32,6 +31,7 @@ Eina_Debug_Thread *_eina_debug_thread_active = NULL; int _eina_debug_thread_active_num = 0; static int _thread_active_size = 0; +static int _thread_id_counter = 1; // add a thread id to our tracking array - very simple. add to end, and // if array to small, reallocate it to be bigger by 16 slots AND double that @@ -62,6 +62,7 @@ _eina_debug_thread_add(void *th) _eina_debug_thread_active[_eina_debug_thread_active_num].clok.tv_nsec = 0; _eina_debug_thread_active[_eina_debug_thread_active_num].val = -1; #endif + _eina_debug_thread_active[_eina_debug_thread_active_num].thread_id = _thread_id_counter++; _eina_debug_thread_active_num++; // release our lock cleanly eina_spinlock_release(&_eina_debug_thread_lock); @@ -101,4 +102,17 @@ _eina_debug_thread_mainloop_set(void *th) pthread_t *pth = th; _eina_debug_thread_mainloop = *pth; } -#endif + +EAPI int +eina_debug_thread_id_get(void) +{ + pthread_t self = pthread_self(); + int i; + + for (i = 0; i < _eina_debug_thread_active_num; i++) + { + if (self == _eina_debug_thread_active[i].thread) + return _eina_debug_thread_active[i].thread_id; + } + return -1; +} diff --git a/src/lib/eina/eina_evlog.c b/src/lib/eina/eina_evlog.c index d1b3a4be3f..1d2183ad55 100644 --- a/src/lib/eina/eina_evlog.c +++ b/src/lib/eina/eina_evlog.c @@ -24,8 +24,6 @@ #include "eina_evlog.h" #include "eina_debug.h" -#ifdef EINA_HAVE_DEBUG - #ifdef HAVE_EVIL # include #endif @@ -35,6 +33,7 @@ #endif #include +#include # ifdef HAVE_MMAP # include @@ -54,6 +53,8 @@ static clockid_t _eina_evlog_time_clock_id = -1; static double _eina_evlog_time_clock_conversion = 1e-9; #endif +static int _evlog_get_opcode = EINA_DEBUG_OPCODE_INVALID; + static inline double get_time(void) { @@ -211,6 +212,50 @@ eina_evlog_stop(void) eina_spinlock_release(&_evlog_lock); } +// get evlog +static Eina_Debug_Error +_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED) +{ + Eina_Evlog_Buf *evlog = eina_evlog_steal(); + int resp_size = 0; + unsigned char *resp_buf = NULL; + + if ((evlog) && (evlog->buf)) + { + resp_size = evlog->top + sizeof(evlog->overflow); + resp_buf = alloca(resp_size); + memcpy(resp_buf, &(evlog->overflow), sizeof(evlog->overflow)); + memcpy(resp_buf + sizeof(evlog->overflow), evlog->buf, evlog->top); + } + printf("send evlog size %d\n", resp_size); + eina_debug_session_send(session, cid, _evlog_get_opcode, resp_buf, resp_size); + + return EINA_DEBUG_OK; +} + +// enable evlog +static Eina_Debug_Error +_start_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED) +{ + eina_evlog_start(); + return EINA_DEBUG_OK; +} + +// stop evlog +static Eina_Debug_Error +_stop_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED) +{ + eina_evlog_stop(); + return EINA_DEBUG_OK; +} + +static const Eina_Debug_Opcode _EINA_DEBUG_EVLOG_OPS[] = { + {"evlog/on", NULL, &_start_cb}, + {"evlog/off", NULL, &_stop_cb}, + {"evlog/get", &_evlog_get_opcode, &_get_cb}, + {NULL, NULL, NULL} +}; + Eina_Bool eina_evlog_init(void) { @@ -227,6 +272,7 @@ eina_evlog_init(void) } #endif eina_evlog("+eina_init", NULL, 0.0, NULL); + eina_debug_opcodes_register(NULL, _EINA_DEBUG_EVLOG_OPS, NULL); return EINA_TRUE; } @@ -237,37 +283,3 @@ eina_evlog_shutdown(void) eina_spinlock_free(&_evlog_lock); return EINA_TRUE; } -#else -EAPI void -eina_evlog(const char *event EINA_UNUSED, void *obj EINA_UNUSED, double srctime EINA_UNUSED, const char *detail EINA_UNUSED) -{ -} - -EAPI Eina_Evlog_Buf * -eina_evlog_steal(void) -{ - return NULL; -} - -EAPI void -eina_evlog_start(void) -{ -} - -EAPI void -eina_evlog_stop(void) -{ -} - -Eina_Bool -eina_evlog_init(void) -{ - return EINA_TRUE; -} - -Eina_Bool -eina_evlog_shutdown(void) -{ - return EINA_TRUE; -} -#endif diff --git a/src/lib/eina/eina_log.c b/src/lib/eina/eina_log.c index 1c19f0a8e3..14182e3ede 100644 --- a/src/lib/eina/eina_log.c +++ b/src/lib/eina/eina_log.c @@ -37,11 +37,13 @@ # include #endif -#include "eina_debug.h" -#ifdef EINA_HAVE_DEBUG -# define EINA_LOG_BACKTRACE +#ifdef HAVE_EXECINFO_H +# include #endif +#include "eina_debug_private.h" +#define EINA_LOG_BACKTRACE + #include "eina_config.h" #include "eina_private.h" #include "eina_inlist.h" diff --git a/src/lib/eina/eina_thread.c b/src/lib/eina/eina_thread.c index 2c681fce25..a9f39dd2f7 100644 --- a/src/lib/eina/eina_thread.c +++ b/src/lib/eina/eina_thread.c @@ -31,13 +31,14 @@ /* 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 #include #ifndef _WIN32 # include #endif +# include #if defined(EINA_HAVE_PTHREAD_AFFINITY) || defined(EINA_HAVE_PTHREAD_SETNAME) #ifndef __linux__ @@ -133,9 +134,7 @@ _eina_internal_call(void *context) { Eina_Thread_Call *c = context; void *r; -#ifdef EINA_HAVE_DEBUG pthread_t self; -#endif EINA_THREAD_CLEANUP_PUSH(free, c); @@ -143,16 +142,11 @@ _eina_internal_call(void *context) c->prio == EINA_THREAD_IDLE) eina_sched_prio_drop(); -#ifdef EINA_HAVE_DEBUG self = pthread_self(); _eina_debug_thread_add(&self); EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &self); -#endif r = c->func((void*) c->data, eina_thread_self()); -#ifdef EINA_HAVE_DEBUG EINA_THREAD_CLEANUP_POP(EINA_TRUE); -#endif - EINA_THREAD_CLEANUP_POP(EINA_TRUE); return r;