forked from enlightenment/efl
Merge Eina Debug feature
Eina Debug is a layer aimed to bring a way to debug EFL applications by providing a transport channel between a debug tool and the applications of a device. In order to be interrupted by EFL core as less as possible, the communication is done in a separated thread. The Ecore loop is not used there, as well as the Ecore helpers (socket...). Debugging operations can be registered easily by any layer of the application. To use it: - Launch efl_debugd - Launch the application to debug - Launch Clouseau (the new version will be pushed soon) or another debug tool supporting Eina Debug
This commit is contained in:
commit
b0bb79a3f0
|
@ -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@
|
||||
|
|
|
@ -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,9 @@ 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_debug_timer.c \
|
||||
lib/eina/eina_error.c \
|
||||
lib/eina/eina_evlog.c \
|
||||
lib/eina/eina_file_common.h \
|
||||
|
@ -171,7 +172,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 \
|
||||
|
|
|
@ -16,269 +16,262 @@
|
|||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define DECLARE_OPS
|
||||
#include "efl_debug_common.h"
|
||||
#include <Eina.h>
|
||||
#include <Ecore.h>
|
||||
|
||||
static Eo *dialer;
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
static Eina_List *waiting;
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define SWAP_64(x) x
|
||||
#define SWAP_32(x) x
|
||||
#define SWAP_16(x) x
|
||||
#else
|
||||
#define SWAP_64(x) eina_swap64(x)
|
||||
#define SWAP_32(x) eina_swap32(x)
|
||||
#define SWAP_16(x) eina_swap16(x)
|
||||
#endif
|
||||
|
||||
static int retval = EXIT_SUCCESS;
|
||||
|
||||
static void
|
||||
_process_reply(void *data EINA_UNUSED, const char op[static 4], const Eina_Slice payload)
|
||||
{
|
||||
if (IS_OP(CLST))
|
||||
{
|
||||
int mypid = getpid();
|
||||
size_t offset;
|
||||
|
||||
waiting = eina_list_remove(waiting, OP_CLST);
|
||||
|
||||
for (offset = 0; offset + sizeof(int) <= payload.len; offset += sizeof(int))
|
||||
{
|
||||
int p;
|
||||
|
||||
memcpy(&p, payload.bytes + offset, sizeof(int));
|
||||
|
||||
if (p == mypid) continue;
|
||||
if (p > 0) printf("%i\n", p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: unexpected server reply: %.4s\n", op);
|
||||
retval = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!waiting) ecore_main_loop_quit();
|
||||
#define EXTRACT(_buf, pval, sz) \
|
||||
{ \
|
||||
memcpy(pval, _buf, sz); \
|
||||
_buf += sz; \
|
||||
}
|
||||
#define _EVLOG_INTERVAL 0.2
|
||||
|
||||
static void
|
||||
_on_data(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
|
||||
{
|
||||
if (!received_data(dialer, _process_reply, NULL))
|
||||
{
|
||||
retval = EXIT_FAILURE;
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
}
|
||||
static int _evlog_max_times = 0;
|
||||
static Ecore_Timer *_evlog_fetch_timer = NULL;
|
||||
static FILE *_evlog_file = NULL;
|
||||
|
||||
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_Bool
|
||||
_command_send(const char op[static 4], const void *data, unsigned int len)
|
||||
_evlog_get_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
|
||||
{
|
||||
if (!send_data(dialer, op, data, len))
|
||||
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)
|
||||
{
|
||||
retval = EXIT_FAILURE;
|
||||
return EINA_FALSE;
|
||||
if ((_evlog_file) && (blocksize > 0))
|
||||
{
|
||||
unsigned int header[3];
|
||||
|
||||
header[0] = 0xffee211;
|
||||
header[1] = SWAP_32(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");
|
||||
}
|
||||
}
|
||||
|
||||
if(received_times == _evlog_max_times)
|
||||
{
|
||||
printf("Received last evlog response\n");
|
||||
if (_evlog_file) fclose(_evlog_file);
|
||||
_evlog_file = NULL;
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
#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_Bool
|
||||
_cb_evlog(void *data EINA_UNUSED)
|
||||
{
|
||||
if (!waiting) ecore_main_loop_quit();
|
||||
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)
|
||||
{
|
||||
eina_debug_session_send(_session, _cid, _cpufreq_off_opcode, NULL, 0);
|
||||
ecore_timer_del(_evlog_fetch_timer);
|
||||
_evlog_fetch_timer = NULL;
|
||||
ret = ECORE_CALLBACK_CANCEL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
_finished(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
|
||||
static Eina_Bool
|
||||
_cid_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer, int size EINA_UNUSED)
|
||||
{
|
||||
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 = SWAP_32(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_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_error(void *data EINA_UNUSED, const Efl_Event *event)
|
||||
static Eina_Bool
|
||||
_clients_info_added_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();
|
||||
char *buf = buffer;
|
||||
while(size)
|
||||
{
|
||||
int cid, pid, len;
|
||||
EXTRACT(buf, &cid, sizeof(int));
|
||||
EXTRACT(buf, &pid, sizeof(int));
|
||||
cid = SWAP_32(cid);
|
||||
pid = SWAP_32(pid);
|
||||
/* 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_TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
static Eina_Bool
|
||||
_clients_info_deleted_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
|
||||
{
|
||||
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));
|
||||
cid = SWAP_32(cid);
|
||||
size -= sizeof(int);
|
||||
|
||||
printf("Usage:\n"
|
||||
"\n"
|
||||
"\t%s <command> [arguments]\n"
|
||||
"\n"
|
||||
"where <command> is one of:\n"
|
||||
"\tlist list connected process (pid)\n"
|
||||
"\tpon <pid> <freq> enable profiling for <pid> at frequency <freq> in microseconds.\n"
|
||||
"\tpoff <pid> disable profiling for <pid>\n"
|
||||
"\tevlogon <pid> start logging events to ~/efl_debug_evlog-<pid>.log\n"
|
||||
"\tevlogoff <pid> stop logging events from <pid>\n",
|
||||
argv[0]);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
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 <pid> <freq>\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
int data[2] = {atoi(argv[i + 1]), atoi(argv[1 + 2])};
|
||||
if (!command_send(PLON, data, sizeof(data)))
|
||||
goto end;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
else if (strcmp(cmd, "poff") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
fprintf(stderr, "ERROR: missing argument: poff <pid>\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
int data[1] = {atoi(argv[i + 1])};
|
||||
if (!command_send(PLOF, data, sizeof(data)))
|
||||
goto end;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (strcmp(cmd, "evlogon") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
fprintf(stderr, "ERROR: missing argument: evlogon <pid>\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
int data[1] = {atoi(argv[i + 1])};
|
||||
if (!command_send(EVON, data, sizeof(data)))
|
||||
goto end;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (strcmp(cmd, "evlogoff") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
fprintf(stderr, "ERROR: missing argument: evlogoff <pid>\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
int data[1] = {atoi(argv[i + 1])};
|
||||
if (!command_send(EVOF, data, sizeof(data)))
|
||||
goto end;
|
||||
i++;
|
||||
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_TRUE;
|
||||
}
|
||||
|
||||
err = efl_net_dialer_dial(dialer, path);
|
||||
if (err)
|
||||
static void
|
||||
_ecore_thread_dispatcher(void *data)
|
||||
{
|
||||
eina_debug_dispatch(_session, data);
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
_disp_cb(Eina_Debug_Session *session EINA_UNUSED, void *buffer)
|
||||
{
|
||||
ecore_main_loop_thread_safe_call_async(_ecore_thread_dispatcher, buffer);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_args_handle(void *data EINA_UNUSED, 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));
|
||||
}
|
||||
}
|
||||
|
||||
EINA_DEBUG_OPCODES_ARRAY_DEFINE(ops,
|
||||
{"Daemon/Client/register_observer", &_cl_stat_reg_opcode, NULL},
|
||||
{"Daemon/Client/added", NULL, &_clients_info_added_cb},
|
||||
{"Daemon/Client/deleted", NULL, &_clients_info_deleted_cb},
|
||||
{"Daemon/Client/cid_from_pid", &_cid_from_pid_opcode, &_cid_get_cb},
|
||||
{"Profiler/on", &_prof_on_opcode, NULL},
|
||||
{"Profiler/off", &_prof_off_opcode, NULL},
|
||||
{"CPU/Freq/on", &_cpufreq_on_opcode, NULL},
|
||||
{"CPU/Freq/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, NULL);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2015 Carsten Haitzler
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library;
|
||||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "efl_debug_common.h"
|
||||
|
||||
Eina_Bool
|
||||
received_data(Eo *sock, void (*handle)(void *data, const char op[static 4], const Eina_Slice payload), const void *data)
|
||||
{
|
||||
Eina_Slice slice, payload;
|
||||
Efl_Debug_Message_Header msgheader;
|
||||
|
||||
slice = efl_io_buffered_stream_slice_get(sock);
|
||||
if (slice.len < sizeof(msgheader))
|
||||
return EINA_TRUE;
|
||||
|
||||
memcpy(&msgheader, slice.mem, sizeof(msgheader));
|
||||
if (msgheader.size < 4) /* must contain at last 4 byte opcode */
|
||||
{
|
||||
fprintf(stderr, "ERROR: invalid message header, size=%u\n", msgheader.size);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
if (msgheader.size + 4 > slice.len)
|
||||
return EINA_TRUE;
|
||||
|
||||
payload.bytes = slice.bytes + sizeof(msgheader);
|
||||
payload.len = msgheader.size - 4;
|
||||
|
||||
handle((void *)data, msgheader.op, payload);
|
||||
|
||||
efl_io_buffered_stream_discard(sock, sizeof(msgheader) + payload.len);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
send_data(Eo *sock, const char op[static 4], const void *data, unsigned int len)
|
||||
{
|
||||
Eina_Error err;
|
||||
Efl_Debug_Message_Header msghdr = {
|
||||
.size = 4 + len,
|
||||
};
|
||||
Eina_Slice s, r;
|
||||
|
||||
memcpy(msghdr.op, op, 4);
|
||||
|
||||
s.mem = &msghdr;
|
||||
s.len = sizeof(msghdr);
|
||||
|
||||
err = efl_io_writer_write(sock, &s, &r);
|
||||
if (err || r.len) goto end;
|
||||
|
||||
if (!len) goto end;
|
||||
|
||||
s.mem = data;
|
||||
s.len = len;
|
||||
err = efl_io_writer_write(sock, &s, &r);
|
||||
|
||||
end:
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not queue message '%.4s': %s\n", op, eina_error_msg_get(err));
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
if (r.len)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not queue message '%.4s': out of memory\n", op);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2015 Carsten Haitzler
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library;
|
||||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EFL_DEBUG_COMMON_H
|
||||
#define EFL_DEBUG_COMMON_H 1
|
||||
|
||||
#define EFL_BETA_API_SUPPORT 1
|
||||
#define EFL_EO_API_SUPPORT 1
|
||||
|
||||
#include <Eina.h>
|
||||
#include <Ecore.h>
|
||||
#include <Ecore_Con.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct _Efl_Debug_Message_Header {
|
||||
unsigned int size;
|
||||
char op[4];
|
||||
} Efl_Debug_Message_Header;
|
||||
|
||||
#define IS_OP(x) memcmp(op, OP_ ## x, 4) == 0
|
||||
|
||||
#define DECLARE_OP(x) static char OP_ ## x[4] = #x
|
||||
#ifdef DECLARE_OPS
|
||||
DECLARE_OP(LIST);
|
||||
DECLARE_OP(CLST);
|
||||
DECLARE_OP(PLON);
|
||||
DECLARE_OP(PLOF);
|
||||
DECLARE_OP(EVON);
|
||||
DECLARE_OP(EVOF);
|
||||
DECLARE_OP(EVLG);
|
||||
DECLARE_OP(HELO);
|
||||
#endif
|
||||
|
||||
Eina_Bool send_data(Eo *sock, const char op[static 4], const void *data, unsigned int len);
|
||||
Eina_Bool received_data(Eo *sock, void (*handle)(void *data, const char op[static 4], const Eina_Slice payload), const void *data);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -272,6 +272,7 @@ extern "C" {
|
|||
#include <eina_slice.h>
|
||||
#include <eina_freeq.h>
|
||||
#include <eina_slstr.h>
|
||||
#include <eina_debug.h>
|
||||
|
||||
#undef EAPI
|
||||
#define EAPI
|
||||
|
|
|
@ -16,10 +16,74 @@
|
|||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
# ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
# endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_SYS_EPOLL_H
|
||||
# include <sys/epoll.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
# include <sys/socket.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
# include <sys/un.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
# include <netinet/in.h>
|
||||
#endif
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <winsock2.h>
|
||||
# include <Evil.h>
|
||||
#endif
|
||||
|
||||
#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_stringshare.h"
|
||||
#include "eina_debug_private.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
#if defined(__CYGWIN__) || defined (_WIN32)
|
||||
# define LIBEXT ".dll"
|
||||
#else
|
||||
# define LIBEXT ".so"
|
||||
#endif
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define SWAP_64(x) x
|
||||
#define SWAP_32(x) x
|
||||
#define SWAP_16(x) x
|
||||
#else
|
||||
#define SWAP_64(x) eina_swap64(x)
|
||||
#define SWAP_32(x) eina_swap32(x)
|
||||
#define SWAP_16(x) eina_swap16(x)
|
||||
#endif
|
||||
|
||||
// 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
|
||||
|
@ -29,6 +93,580 @@ Eina_Spinlock _eina_debug_lock;
|
|||
// only init once
|
||||
static Eina_Bool _inited = EINA_FALSE;
|
||||
|
||||
#ifdef __linux__
|
||||
extern char *__progname;
|
||||
#endif
|
||||
|
||||
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 Eina_Debug_Session *_last_local_session = NULL;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const Eina_Debug_Opcode *ops;
|
||||
Eina_Debug_Opcode_Status_Cb status_cb;
|
||||
void *status_data;
|
||||
} _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 */
|
||||
void *data; /* User data */
|
||||
int cbs_length; /* cbs table size */
|
||||
int fd; /* File descriptor */
|
||||
};
|
||||
|
||||
#ifndef _WIN32
|
||||
static void _opcodes_register_all(Eina_Debug_Session *session);
|
||||
#endif
|
||||
static void _thread_start(Eina_Debug_Session *session);
|
||||
|
||||
EAPI int
|
||||
eina_debug_session_send(Eina_Debug_Session *session, int dest, 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 = SWAP_32(size + sizeof(Eina_Debug_Packet_Header));
|
||||
hdr.opcode = SWAP_32(op);
|
||||
hdr.cid = SWAP_32(dest);
|
||||
e_debug("socket: %d / opcode %X / bytes to send: %d",
|
||||
session->fd, op, size + sizeof(*hdr));
|
||||
#ifndef _WIN32
|
||||
eina_spinlock_take(&_eina_debug_lock);
|
||||
/* Sending header */
|
||||
write(session->fd, &hdr, sizeof(hdr));
|
||||
/* Sending payload */
|
||||
if (size) write(session->fd, data, size);
|
||||
eina_spinlock_release(&_eina_debug_lock);
|
||||
#else
|
||||
(void)data;
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
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 */
|
||||
#ifdef __linux__
|
||||
char *app_name = __progname;
|
||||
#else
|
||||
char *app_name = NULL;
|
||||
#endif
|
||||
int size = 8 + (app_name ? strlen(app_name) : 0) + 1;
|
||||
unsigned char *buf = alloca(size);
|
||||
int version = SWAP_32(1); // version of protocol we speak
|
||||
int pid = getpid();
|
||||
pid = SWAP_32(pid);
|
||||
memcpy(buf + 0, &version, 4);
|
||||
memcpy(buf + 4, &pid, 4);
|
||||
if (app_name)
|
||||
memcpy(buf + 8, app_name, strlen(app_name) + 1);
|
||||
else
|
||||
buf[8] = '\0';
|
||||
eina_debug_session_send(session, 0, EINA_DEBUG_OPCODE_HELLO, buf, size);
|
||||
}
|
||||
|
||||
static int
|
||||
_packet_receive(Eina_Debug_Session *session, unsigned char **buffer)
|
||||
{
|
||||
unsigned char *packet_buf = NULL;
|
||||
int rret = -1;
|
||||
unsigned int size = 0;
|
||||
|
||||
if (!session) goto end;
|
||||
|
||||
if ((rret = read(session->fd, &size, 4)) == 4)
|
||||
{
|
||||
size = SWAP_32(size);
|
||||
if (size > EINA_DEBUG_MAX_PACKET_SIZE)
|
||||
{
|
||||
e_debug("Packet too big: %d. The maximum allowed is %d", size, EINA_DEBUG_MAX_PACKET_SIZE);
|
||||
rret = -1;
|
||||
goto end;
|
||||
}
|
||||
e_debug("Begin to receive a packet of %d bytes", size);
|
||||
// allocate a buffer for the next bytes to receive
|
||||
packet_buf = malloc(size);
|
||||
if (packet_buf)
|
||||
{
|
||||
unsigned int cur_packet_size = 4;
|
||||
memcpy(packet_buf, &size, 4);
|
||||
/* Receive all the remaining packet bytes */
|
||||
while (cur_packet_size < size)
|
||||
{
|
||||
rret = read(session->fd, packet_buf + cur_packet_size, size - cur_packet_size);
|
||||
if (rret <= 0)
|
||||
{
|
||||
e_debug("Error on read: %d", rret);
|
||||
perror("Read");
|
||||
goto end;
|
||||
}
|
||||
cur_packet_size += rret;
|
||||
}
|
||||
*buffer = packet_buf;
|
||||
rret = cur_packet_size;
|
||||
e_debug("Received a packet of %d bytes", cur_packet_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we couldn't allocate memory for payload buffer
|
||||
// internal memory limit error
|
||||
e_debug("Cannot allocate %u bytes for op", 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)
|
||||
{
|
||||
/* Close fd here so the thread terminates its own session by itself */
|
||||
if (!session) return;
|
||||
close(session->fd);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
EAPI Eina_Debug_Dispatch_Cb
|
||||
eina_debug_session_dispatch_get(Eina_Debug_Session *session)
|
||||
{
|
||||
if (session) return session->dispatch_cb;
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
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_Bool
|
||||
_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_64 = SWAP_64(info_64);
|
||||
info = (_opcode_reply_info *)info_64;
|
||||
|
||||
if (!info) return EINA_FALSE;
|
||||
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++)
|
||||
{
|
||||
int op = SWAP_32(os[i]);
|
||||
if (info->ops[i].opcode_id) *(info->ops[i].opcode_id) = op;
|
||||
_static_opcode_register(session, op, info->ops[i].cb);
|
||||
e_debug("Opcode %s -> %d", info->ops[i].opcode_name, op);
|
||||
}
|
||||
if (info->status_cb) info->status_cb(info->status_data, EINA_TRUE);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return EINA_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
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 = malloc(size);
|
||||
|
||||
uint64_t info_64 = (uint64_t)(uintptr_t)info;
|
||||
info_64 = SWAP_64(info_64);
|
||||
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);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
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(info->status_data, 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;
|
||||
}
|
||||
|
||||
#define LENGTH_OF_SOCKADDR_UN(s) \
|
||||
(strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
|
||||
#endif
|
||||
|
||||
static Eina_Debug_Session *
|
||||
_session_create(int fd)
|
||||
{
|
||||
Eina_Debug_Session *session = calloc(1, sizeof(*session));
|
||||
session->dispatch_cb = eina_debug_dispatch;
|
||||
session->fd = fd;
|
||||
// start the monitor thread
|
||||
_thread_start(session);
|
||||
return session;
|
||||
}
|
||||
|
||||
EAPI Eina_Debug_Session *
|
||||
eina_debug_remote_connect(int port)
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_in server;
|
||||
|
||||
//Create socket
|
||||
fd = socket(AF_INET, 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;
|
||||
|
||||
//Prepare the sockaddr_in structure
|
||||
server.sin_family = AF_INET;
|
||||
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr.s_addr);
|
||||
server.sin_port = htons(port);
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&server, sizeof(server)) < 0)
|
||||
{
|
||||
perror("connect failed. Error");
|
||||
goto err;
|
||||
}
|
||||
return _session_create(fd);
|
||||
err:
|
||||
// some kind of connection failure here, so close a valid socket and
|
||||
// get out of here
|
||||
if (fd >= 0) close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (is_master) return eina_debug_remote_connect(REMOTE_SERVER_PORT);
|
||||
|
||||
// try this socket file - it will likely be:
|
||||
// ~/.ecore/efl_debug/0
|
||||
// or maybe
|
||||
// /var/run/UID/.ecore/efl_debug/0
|
||||
snprintf(buf, sizeof(buf), "%s/%s/%s/%i", _socket_home_get(),
|
||||
LOCAL_SERVER_PATH, LOCAL_SERVER_NAME, LOCAL_SERVER_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;
|
||||
|
||||
_last_local_session = _session_create(fd);
|
||||
|
||||
return _last_local_session;
|
||||
err:
|
||||
// some kind of connection failure here, so close a valid socket and
|
||||
// get out of here
|
||||
if (fd >= 0) close(fd);
|
||||
#else
|
||||
(void) is_master;
|
||||
#endif
|
||||
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
|
||||
|
||||
#ifndef _WIN32
|
||||
static void *
|
||||
_monitor(void *_data)
|
||||
{
|
||||
Eina_Debug_Session *session = _data;
|
||||
|
||||
_daemon_greet(session);
|
||||
_opcodes_register_all(session);
|
||||
|
||||
// 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;)
|
||||
{
|
||||
unsigned char *buffer;
|
||||
int size;
|
||||
|
||||
size = _packet_receive(session, &buffer);
|
||||
// if not negative - we have a real message
|
||||
if (size > 0)
|
||||
{
|
||||
Eina_Debug_Packet_Header *hdr = (Eina_Debug_Packet_Header *)buffer;
|
||||
hdr->cid = SWAP_32(hdr->cid);
|
||||
hdr->opcode = SWAP_32(hdr->opcode);
|
||||
if (EINA_TRUE != session->dispatch_cb(session, buffer))
|
||||
{
|
||||
// something we don't understand
|
||||
e_debug("EINA DEBUG ERROR: Unknown command");
|
||||
}
|
||||
/* Free the buffer only if the default dispatcher is used */
|
||||
if (session->dispatch_cb == eina_debug_dispatch)
|
||||
free(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
close(session->fd);
|
||||
_opcodes_unregister_all(session);
|
||||
free(session);
|
||||
session = NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// start up the debug monitor if we haven't already
|
||||
static void
|
||||
_thread_start(Eina_Debug_Session *session)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
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();
|
||||
}
|
||||
#else
|
||||
(void)session;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends to daemon:
|
||||
* - Pointer to ops: returned in the response to determine which opcodes have been added
|
||||
* - List of opcode names separated by \0
|
||||
*/
|
||||
EAPI void
|
||||
eina_debug_opcodes_register(Eina_Debug_Session *session, const Eina_Debug_Opcode ops[],
|
||||
Eina_Debug_Opcode_Status_Cb status_cb, void *data)
|
||||
{
|
||||
if (!session) session = _last_local_session;
|
||||
if (!session) return;
|
||||
|
||||
_opcode_reply_info *info = malloc(sizeof(*info));
|
||||
info->ops = ops;
|
||||
info->status_cb = status_cb;
|
||||
info->status_data = data;
|
||||
|
||||
session->opcode_reply_infos = eina_list_append(
|
||||
session->opcode_reply_infos, info);
|
||||
|
||||
/* Send only if session's fd connected.
|
||||
* Otherwise, it will be sent when connected */
|
||||
if(session && session->fd!= -1)
|
||||
_opcodes_registration_send(session, info);
|
||||
}
|
||||
|
||||
EAPI Eina_Bool
|
||||
eina_debug_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_FALSE;
|
||||
}
|
||||
|
||||
EINA_LIST_FOREACH(session->cbs[opcode], itr, cb)
|
||||
{
|
||||
if (!cb) continue;
|
||||
Eina_Bool ret = cb(session, hdr->cid,
|
||||
(unsigned char *)buffer + sizeof(*hdr),
|
||||
hdr->size - sizeof(*hdr));
|
||||
if (ret == EINA_FALSE) return ret;
|
||||
}
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
EAPI void
|
||||
eina_debug_session_data_set(Eina_Debug_Session *session, void *data)
|
||||
{
|
||||
if (session) session->data = data;
|
||||
}
|
||||
|
||||
EAPI void *
|
||||
eina_debug_session_data_get(Eina_Debug_Session *session)
|
||||
{
|
||||
if (session) return session->data;
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
eina_debug_init(void)
|
||||
{
|
||||
|
@ -43,54 +681,42 @@ 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
|
||||
|
||||
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_debug_cpu_init();
|
||||
_eina_debug_bt_init();
|
||||
_eina_debug_timer_init();
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
eina_debug_shutdown(void)
|
||||
{
|
||||
_eina_debug_timer_shutdown();
|
||||
_eina_debug_bt_shutdown();
|
||||
_eina_debug_cpu_shutdown();
|
||||
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
|
||||
|
|
|
@ -19,103 +19,294 @@
|
|||
#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 <stdio.h>
|
||||
# include <string.h>
|
||||
# include <stdlib.h>
|
||||
# include <unistd.h>
|
||||
# if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && defined(HAVE_DLADDR) && defined(HAVE_UNWIND)
|
||||
# include <execinfo.h>
|
||||
# ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
# endif
|
||||
# include <errno.h>
|
||||
# include <stdio.h>
|
||||
# include <string.h>
|
||||
# include <unistd.h>
|
||||
# include <dlfcn.h>
|
||||
# include <sys/select.h>
|
||||
# include <sys/time.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# include <pthread.h>
|
||||
# include <signal.h>
|
||||
# include <time.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/un.h>
|
||||
# include <fcntl.h>
|
||||
# include <libunwind.h>
|
||||
|
||||
# include "eina_config.h"
|
||||
# include "eina_private.h"
|
||||
# include "eina_inlist.h"
|
||||
# include "eina_lock.h"
|
||||
# include "eina_thread.h"
|
||||
# include "eina_convert.h"
|
||||
# include "eina_strbuf.h"
|
||||
# include "eina_safety_checks.h"
|
||||
# include "eina_log.h"
|
||||
# include "eina_inline_private.h"
|
||||
|
||||
# define EINA_HAVE_DEBUG 1
|
||||
|
||||
# define EINA_MAX_BT 256
|
||||
|
||||
typedef struct _Eina_Debug_Thread Eina_Debug_Thread;
|
||||
|
||||
struct _Eina_Debug_Thread
|
||||
{
|
||||
pthread_t thread;
|
||||
#if defined(__clockid_t_defined)
|
||||
struct timespec clok;
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
int val;
|
||||
|
||||
#ifdef EFL_BETA_API_SUPPORT
|
||||
|
||||
/**
|
||||
* @page eina_debug_main Eina Debug
|
||||
*
|
||||
* @date 2015 (created)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup Eina_Debug
|
||||
* @{
|
||||
*/
|
||||
|
||||
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
|
||||
*
|
||||
* return true on success, false if the connection seems compromised
|
||||
*/
|
||||
typedef Eina_Bool (*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 data data pointer given when registering opcodes
|
||||
* @param status EINA_TRUE if opcodes have been received from the daemon, EINA_FALSE otherwise.
|
||||
*/
|
||||
typedef void (*Eina_Debug_Opcode_Status_Cb)(void *data, 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.
|
||||
*
|
||||
* return the return result of the invoked callback
|
||||
*/
|
||||
typedef Eina_Bool (*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_Timer
|
||||
*/
|
||||
typedef struct _Eina_Debug_Timer Eina_Debug_Timer;
|
||||
|
||||
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_Packet_Header
|
||||
*
|
||||
* Header of Eina Debug packet
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
unsigned int size; /**< Packet size including 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 opcode; /**< Opcode of the packet */
|
||||
} Eina_Debug_Packet_Header;
|
||||
|
||||
# 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
|
||||
/**
|
||||
* Helper for creating global opcodes arrays.
|
||||
* The problem is on windows where you can't declare a static array with
|
||||
* external symbols in it, because the addresses are only known at runtime.
|
||||
*/
|
||||
#define EINA_DEBUG_OPCODES_ARRAY_DEFINE(Name, ...) \
|
||||
static Eina_Debug_Opcode * \
|
||||
Name(void) \
|
||||
{ \
|
||||
Eina_Debug_Opcode tmp[] = { __VA_ARGS__ }; \
|
||||
static Eina_Debug_Opcode internal[EINA_C_ARRAY_LENGTH(tmp) + 1] = \
|
||||
{ { 0, 0, 0 } }; \
|
||||
if (internal[0].opcode_name == NULL) \
|
||||
{ \
|
||||
memcpy(internal, tmp, sizeof(tmp)); \
|
||||
} \
|
||||
return internal; \
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef Eina_Debug_Opcode
|
||||
*
|
||||
* Structure to describe information for an opcode. It is used to register new
|
||||
* opcodes.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *opcode_name; /**< Opcode string. On registration, the daemon uses it to calculate an opcode id */
|
||||
int *opcode_id; /**< A pointer to store the opcode id received from the daemon */
|
||||
Eina_Debug_Cb cb; /**< Callback to call when a packet corresponding to the opcode is received */
|
||||
} Eina_Debug_Opcode;
|
||||
|
||||
/**
|
||||
* @brief Disable debugging
|
||||
*
|
||||
* Useful for applications that don't want debugging. The debug daemon is one
|
||||
* of them.
|
||||
* Need to be invoked before eina_init. Otherwise it won'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 the session on success or NULL otherwise
|
||||
*/
|
||||
EAPI Eina_Debug_Session *eina_debug_local_connect(Eina_Bool is_master);
|
||||
|
||||
/**
|
||||
* @brief Connect to remote daemon
|
||||
*
|
||||
* This function connects to localhost:port.
|
||||
*
|
||||
* @param port the port to connect to
|
||||
*
|
||||
* @return the session on success or NULL otherwise
|
||||
*/
|
||||
EAPI Eina_Debug_Session *eina_debug_remote_connect(int port);
|
||||
|
||||
/**
|
||||
* @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 Get the dispatcher of a specific session
|
||||
*
|
||||
* @param session the session
|
||||
*
|
||||
* @return the session dispatcher
|
||||
*/
|
||||
EAPI Eina_Debug_Dispatch_Cb eina_debug_session_dispatch_get(Eina_Debug_Session *session);
|
||||
|
||||
/**
|
||||
* @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 true on success, false if the connection seems compromised
|
||||
*/
|
||||
EAPI Eina_Bool eina_debug_dispatch(Eina_Debug_Session *session, void *buffer);
|
||||
|
||||
/**
|
||||
* @brief Set data to a session
|
||||
*
|
||||
* @param session the session
|
||||
* @param data the data to set
|
||||
*
|
||||
*/
|
||||
EAPI void eina_debug_session_data_set(Eina_Debug_Session *session, void *data);
|
||||
|
||||
/**
|
||||
* @brief Get the data attached to a session
|
||||
*
|
||||
* @param session the session
|
||||
*
|
||||
* @return the data of the session
|
||||
*/
|
||||
EAPI void *eina_debug_session_data_get(Eina_Debug_Session *session);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @param session the session
|
||||
* @param ops the operations to register
|
||||
* @param status_cb a function to call when the opcodes are received
|
||||
* @param status_data the data to give to status_cb
|
||||
*/
|
||||
EAPI void eina_debug_opcodes_register(Eina_Debug_Session *session,
|
||||
const Eina_Debug_Opcode ops[],
|
||||
Eina_Debug_Opcode_Status_Cb status_cb, void *status_data);
|
||||
|
||||
/**
|
||||
* @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 Add a timer
|
||||
*
|
||||
* @param timeout_ms timeout in ms
|
||||
* @param cb callback to call when the timeout is reached
|
||||
* @param data user data
|
||||
*
|
||||
* @return the timer handle, NULL on error
|
||||
*/
|
||||
EAPI Eina_Debug_Timer *eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data);
|
||||
|
||||
/**
|
||||
* @brief Delete a timer
|
||||
*
|
||||
* @param timer the timer to delete
|
||||
*
|
||||
* If the timer reaches the end and has not be renewed, trying to delete it will lead to a crash, as
|
||||
* it has already been deleted internally.
|
||||
*/
|
||||
EAPI void eina_debug_timer_del(Eina_Debug_Timer *timer);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,13 +16,45 @@
|
|||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "eina_debug.h"
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
#ifdef HAVE_DLADDR
|
||||
# ifdef _WIN32
|
||||
# include <Evil.h>
|
||||
# else
|
||||
# include <dlfcn.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNWIND
|
||||
#include <libunwind.h>
|
||||
#endif
|
||||
|
||||
#include "eina_debug.h"
|
||||
#include "eina_debug_private.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
# define SIG SIGPROF
|
||||
#endif
|
||||
|
||||
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 Eina_Debug_Timer *_timer = NULL;
|
||||
|
||||
void
|
||||
_eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
int i;
|
||||
Dl_info info;
|
||||
const char *file;
|
||||
|
@ -32,7 +64,7 @@ _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
|
|||
{
|
||||
file = NULL;
|
||||
offset = base = 0;
|
||||
// we have little choice but to hgope/assume dladdr() doesn't alloc
|
||||
// we have little choice but to hope/assume dladdr() doesn't alloc
|
||||
// anything here
|
||||
if ((dladdr(bt[i], &info)) && (info.dli_fname[0]))
|
||||
{
|
||||
|
@ -45,5 +77,231 @@ _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
|
|||
if (file) fprintf(f, "%s\t 0x%llx 0x%llx\n", file, offset, base);
|
||||
else fprintf(f, "??\t -\n");
|
||||
}
|
||||
#else
|
||||
(void)f;
|
||||
(void)bt;
|
||||
(void)btlen;
|
||||
#endif
|
||||
}
|
||||
|
||||
// a backtracer that uses libunwind to do the job
|
||||
static inline int
|
||||
_eina_debug_unwind_bt(void **bt, int max)
|
||||
{
|
||||
#ifdef HAVE_UNWIND
|
||||
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;
|
||||
#else
|
||||
(void)bt;
|
||||
(void)max;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
static void
|
||||
_signal_handler(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
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
if (self == _eina_debug_thread_active[i].thread)
|
||||
{
|
||||
slot = i;
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
_signal_init(void)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
_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
|
||||
#ifndef _WIN32
|
||||
pthread_kill(pth, SIG);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_trace_cb(void *data 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
|
||||
((_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
|
||||
((_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
|
||||
((_eina_debug_thread_active_num) * sizeof(int));
|
||||
if (!_bt_cpu) 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] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *));
|
||||
if (!_bt_buf[i]) goto err;
|
||||
}
|
||||
// get an array of ints to stor the bt len for mainloop + threads
|
||||
_bt_buf_len = _eina_debug_chunk_tmp_push
|
||||
((_eina_debug_thread_active_num) * sizeof(int));
|
||||
// now collect per thread
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
_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); i++)
|
||||
eina_semaphore_lock(&_wait_for_bts_sem);
|
||||
// we now have gotten all the data from all threads
|
||||
// we can process it now as we see fit, so release thread lock
|
||||
for (i = 0; i < _eina_debug_thread_active_num; 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_TRUE;
|
||||
}
|
||||
|
||||
// profiling on with poll time gap as uint payload
|
||||
static Eina_Bool
|
||||
_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;
|
||||
if (_timer) eina_debug_timer_del(_timer);
|
||||
_timer = eina_debug_timer_add(time, _trace_cb, session);
|
||||
}
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_prof_off_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
|
||||
{
|
||||
eina_debug_timer_del(_timer);
|
||||
_timer = NULL;
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
EINA_DEBUG_OPCODES_ARRAY_DEFINE(_OPS,
|
||||
{"Profiler/on", NULL, &_prof_on_cb},
|
||||
{"Profiler/off", NULL, &_prof_off_cb},
|
||||
{NULL, NULL, NULL}
|
||||
);
|
||||
|
||||
Eina_Bool
|
||||
_eina_debug_bt_init(void)
|
||||
{
|
||||
_signal_init();
|
||||
eina_semaphore_new(&_wait_for_bts_sem, 0);
|
||||
eina_debug_opcodes_register(NULL, _OPS(), NULL, NULL);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
_eina_debug_bt_shutdown(void)
|
||||
{
|
||||
eina_semaphore_free(&_wait_for_bts_sem);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,14 @@
|
|||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "eina_debug.h"
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
#include "eina_debug_private.h"
|
||||
|
||||
extern Eina_Spinlock _eina_debug_lock;
|
||||
|
||||
static unsigned int _table_num = 0;
|
||||
static unsigned int _table_size = 0;
|
||||
|
@ -164,4 +169,3 @@ _eina_debug_file_get(const char *fname)
|
|||
}
|
||||
return file;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -16,21 +16,23 @@
|
|||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "eina_debug.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
# ifdef HAVE_MMAP
|
||||
# include <sys/mman.h>
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
# include <sys/mman.h>
|
||||
|
||||
// custom memory allocators to avoid malloc/free during backtrace handling
|
||||
// just in case we're inside some signal handler due to mem corruption and
|
||||
// are inside a malloc/free lock and thus would deadlock ourselves if we
|
||||
// allocated memory, so implement scratch space just big enough for what we
|
||||
// need and then some via either a static 8k+4k buffer pair or via a growable
|
||||
// mmaped mem chunk pair
|
||||
# ifdef HAVE_MMAP
|
||||
// implement using mmap so we can grow if needed - unlikelt though
|
||||
static unsigned char *chunk1 = NULL;
|
||||
static unsigned char *chunk2 = NULL;
|
||||
|
@ -243,4 +245,3 @@ _eina_debug_chunk_strdup(const char *str)
|
|||
strcpy(s, str);
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
# ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
# endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "eina_debug.h"
|
||||
#include "eina_types.h"
|
||||
#include "eina_list.h"
|
||||
#include "eina_mempool.h"
|
||||
#include "eina_util.h"
|
||||
#include "eina_evlog.h"
|
||||
#include "eina_debug_private.h"
|
||||
|
||||
volatile int _eina_debug_sysmon_reset = 0;
|
||||
volatile int _eina_debug_sysmon_active = 0;
|
||||
volatile int _eina_debug_evlog_active = 0;
|
||||
|
||||
static Eina_Lock _sysmon_lock;
|
||||
|
||||
static Eina_Bool _sysmon_thread_runs = EINA_FALSE;
|
||||
static pthread_t _sysmon_thread;
|
||||
|
||||
// this is a DEDICATED thread tojust collect system info and to have the
|
||||
// least impact it can on a cpu core or system. all this does right now
|
||||
// is sleep and wait for a command to begin polling for the cpu state.
|
||||
// right now that means iterating through cpu's and getting their cpu
|
||||
// frequency to match up with event logs.
|
||||
static void *
|
||||
_sysmon(void *_data EINA_UNUSED)
|
||||
{
|
||||
static int cpufreqs[64] = { 0 };
|
||||
int i, fd, freq;
|
||||
char buf[256], path[256];
|
||||
ssize_t red;
|
||||
#if defined(__clockid_t_defined)
|
||||
static struct timespec t_last = { 0, 0 };
|
||||
static Eina_Debug_Thread *prev_threads = NULL;
|
||||
static int prev_threads_num = 0;
|
||||
int j, cpu;
|
||||
Eina_Bool prev_threads_redo;
|
||||
clockid_t cid;
|
||||
struct timespec t, t_now;
|
||||
unsigned long long tim_span, tim1, tim2;
|
||||
#endif
|
||||
|
||||
// set a name for this thread for system debugging
|
||||
#ifdef EINA_HAVE_PTHREAD_SETNAME
|
||||
# ifndef __linux__
|
||||
pthread_set_name_np
|
||||
# else
|
||||
pthread_setname_np
|
||||
# endif
|
||||
(pthread_self(), "Edbg-sys");
|
||||
#endif
|
||||
for (;;)
|
||||
{
|
||||
// wait on a mutex that will be locked for as long as this
|
||||
// threead is not meant to go running
|
||||
eina_lock_take(&_sysmon_lock);
|
||||
// if we need to reset as we just started polling system stats...
|
||||
if (_eina_debug_sysmon_reset)
|
||||
{
|
||||
_eina_debug_sysmon_reset = 0;
|
||||
// clear out all the clocks for threads
|
||||
#if defined(__clockid_t_defined)
|
||||
// reset the last clock timestamp when we checked to "now"
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
t_last = t;
|
||||
// walk over all threads
|
||||
eina_spinlock_take(&_eina_debug_thread_lock);
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
// get the correct clock id to use for the target thread
|
||||
pthread_getcpuclockid
|
||||
(_eina_debug_thread_active[i].thread, &cid);
|
||||
// get the clock cpu time accumulation for that threas
|
||||
clock_gettime(cid, &t);
|
||||
_eina_debug_thread_active[i].clok = t;
|
||||
}
|
||||
eina_spinlock_release(&_eina_debug_thread_lock);
|
||||
#endif
|
||||
// clear all the cpu freq (up to 64 cores) to 0
|
||||
for (i = 0; i < 64; i++) cpufreqs[i] = 0;
|
||||
}
|
||||
eina_lock_release(&_sysmon_lock);
|
||||
|
||||
#if defined(__clockid_t_defined)
|
||||
// get the current time
|
||||
clock_gettime(CLOCK_MONOTONIC, &t_now);
|
||||
tim1 = (t_last.tv_sec * 1000000000LL) + t_last.tv_nsec;
|
||||
// the time span between now and last time we checked
|
||||
tim_span = ((t_now.tv_sec * 1000000000LL) + t_now.tv_nsec) - tim1;
|
||||
// if the time span is non-zero we might get sensible results
|
||||
if (tim_span > 0)
|
||||
{
|
||||
prev_threads_redo = EINA_FALSE;
|
||||
eina_spinlock_take(&_eina_debug_thread_lock);
|
||||
// figure out if the list of thread id's has changed since
|
||||
// our last poll. this is imporant as we need to set the
|
||||
// thread cpu usage to 0 for threads that have disappeared
|
||||
if (prev_threads_num != _eina_debug_thread_active_num)
|
||||
prev_threads_redo = EINA_TRUE;
|
||||
else
|
||||
{
|
||||
// XXX: isolate this out of hot path
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
if (_eina_debug_thread_active[i].thread !=
|
||||
prev_threads[i].thread)
|
||||
{
|
||||
prev_threads_redo = EINA_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
pthread_t thread = _eina_debug_thread_active[i].thread;
|
||||
// get the clock for the thread and its cpu time usage
|
||||
pthread_getcpuclockid(thread, &cid);
|
||||
clock_gettime(cid, &t);
|
||||
// calculate a long timestamp (64bits)
|
||||
tim1 = (_eina_debug_thread_active[i].clok.tv_sec * 1000000000LL) +
|
||||
_eina_debug_thread_active[i].clok.tv_nsec;
|
||||
// and get the difference in clock time in NS
|
||||
tim2 = ((t.tv_sec * 1000000000LL) + t.tv_nsec) - tim1;
|
||||
// and that as percentage of the timespan
|
||||
cpu = (int)((100 * (int)tim2) / (int)tim_span);
|
||||
// round to the nearest 10 percent - rough anyway
|
||||
cpu = ((cpu + 5) / 10) * 10;
|
||||
if (cpu > 100) cpu = 100;
|
||||
// if the usage changed since last time we checked...
|
||||
if (cpu != _eina_debug_thread_active[i].val)
|
||||
{
|
||||
// log this change
|
||||
snprintf(buf, sizeof(buf), "*CPUUSED %llu",
|
||||
(unsigned long long)thread);
|
||||
snprintf(path, sizeof(path), "%i", _eina_debug_thread_active[i].val);
|
||||
eina_evlog(buf, NULL, 0.0, path);
|
||||
snprintf(path, sizeof(path), "%i", cpu);
|
||||
eina_evlog(buf, NULL, 0.0, path);
|
||||
// store the clock time + cpu we got for next poll
|
||||
_eina_debug_thread_active[i].val = cpu;
|
||||
}
|
||||
_eina_debug_thread_active[i].clok = t;
|
||||
}
|
||||
// so threads changed between this poll and last so we need
|
||||
// to redo our mapping/storage of them
|
||||
if (prev_threads_redo)
|
||||
{
|
||||
prev_threads_redo = EINA_FALSE;
|
||||
// find any threads from our last run that do not
|
||||
// exist now in our new list of threads
|
||||
for (j = 0; j < prev_threads_num; j++)
|
||||
{
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
if (prev_threads[j].thread ==
|
||||
_eina_debug_thread_active[i].thread) break;
|
||||
}
|
||||
// thread was there before and not now
|
||||
if (i == _eina_debug_thread_active_num)
|
||||
{
|
||||
// log it finishing - ie 0
|
||||
snprintf(buf, sizeof(buf), "*CPUUSED %llu",
|
||||
(unsigned long long)
|
||||
prev_threads[i].thread);
|
||||
eina_evlog(buf, NULL, 0.0, "0");
|
||||
}
|
||||
}
|
||||
// if the thread count changed then allocate a new shadow
|
||||
// buffer of thread id's etc.
|
||||
if (prev_threads_num != _eina_debug_thread_active_num)
|
||||
{
|
||||
if (prev_threads) free(prev_threads);
|
||||
prev_threads_num = _eina_debug_thread_active_num;
|
||||
prev_threads = malloc(prev_threads_num *
|
||||
sizeof(Eina_Debug_Thread));
|
||||
}
|
||||
// store the thread handles/id's
|
||||
for (i = 0; i < prev_threads_num; i++)
|
||||
prev_threads[i].thread =
|
||||
_eina_debug_thread_active[i].thread;
|
||||
}
|
||||
eina_spinlock_release(&_eina_debug_thread_lock);
|
||||
t_last = t_now;
|
||||
}
|
||||
#endif
|
||||
// poll up to 64 cpu cores for cpufreq info to log alongside
|
||||
// the evlog call data
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
// look at sysfs nodes per cpu
|
||||
snprintf
|
||||
(buf, sizeof(buf),
|
||||
"/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq", i);
|
||||
fd = open(buf, O_RDONLY);
|
||||
freq = 0;
|
||||
// if the node is there, read it
|
||||
if (fd >= 0)
|
||||
{
|
||||
// really low overhead read from cpufreq node (just an int)
|
||||
red = read(fd, buf, sizeof(buf) - 1);
|
||||
if (red > 0)
|
||||
{
|
||||
// read something - it should be an int with whitespace
|
||||
buf[red] = 0;
|
||||
freq = atoi(buf);
|
||||
// move to mhz
|
||||
freq = (freq + 500) / 1000;
|
||||
// round mhz to the nearest 100mhz - to have less noise
|
||||
freq = ((freq + 50) / 100) * 100;
|
||||
}
|
||||
// close the fd so we can freshly poll next time around
|
||||
close(fd);
|
||||
}
|
||||
// node not there - ran out of cpu's to poll?
|
||||
else break;
|
||||
// if the current frequency changed vs previous poll, then log
|
||||
if (freq != cpufreqs[i])
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "*CPUFREQ %i", i);
|
||||
snprintf(path, sizeof(path), "%i", freq);
|
||||
eina_evlog(buf, NULL, 0.0, path);
|
||||
cpufreqs[i] = freq;
|
||||
}
|
||||
}
|
||||
usleep(1000); // 1ms sleep
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_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_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_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_TRUE;
|
||||
}
|
||||
|
||||
EINA_DEBUG_OPCODES_ARRAY_DEFINE(_OPS,
|
||||
{"CPU/Freq/on", NULL, &_cpufreq_on_cb},
|
||||
{"CPU/Freq/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, NULL);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
_eina_debug_cpu_shutdown(void)
|
||||
{
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
|
@ -1,708 +0,0 @@
|
|||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2015 Carsten Haitzler
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library;
|
||||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "eina_debug.h"
|
||||
#include "eina_types.h"
|
||||
#include "eina_evlog.h"
|
||||
#include "eina_util.h"
|
||||
#include "eina_thread.h"
|
||||
#include "eina_file.h"
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
#define DEBUG_SERVER ".ecore/efl_debug/0"
|
||||
|
||||
volatile int _eina_debug_sysmon_reset = 0;
|
||||
volatile int _eina_debug_evlog_active = 0;
|
||||
|
||||
int _eina_debug_monitor_service_fd = -1;
|
||||
Eina_Semaphore _eina_debug_monitor_return_sem;
|
||||
Eina_Lock _eina_debug_sysmon_lock;
|
||||
|
||||
static Eina_Bool _monitor_thread_runs = EINA_FALSE;
|
||||
static pthread_t _monitor_thread;
|
||||
static pthread_t _sysmon_thread;
|
||||
|
||||
// _bt_buf[0] is always for mainloop, 1 + is for extra threads
|
||||
static void ***_bt_buf;
|
||||
static int *_bt_buf_len;
|
||||
static struct timespec *_bt_ts;
|
||||
static int *_bt_cpu;
|
||||
|
||||
// a backtracer that uses libunwind to do the job
|
||||
static inline int
|
||||
_eina_debug_unwind_bt(void **bt, int max)
|
||||
{
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t uc;
|
||||
unw_word_t p;
|
||||
int total;
|
||||
|
||||
// create a context for unwinding
|
||||
unw_getcontext(&uc);
|
||||
// begin our work
|
||||
unw_init_local(&cursor, &uc);
|
||||
// walk up each stack frame until there is no more, storing it
|
||||
for (total = 0; (unw_step(&cursor) > 0) && (total < max); total++)
|
||||
{
|
||||
unw_get_reg(&cursor, UNW_REG_IP, &p);
|
||||
bt[total] = (void *)p;
|
||||
}
|
||||
// return our total backtrace stack size
|
||||
return total;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_bt_cpu_set(int slot)
|
||||
{
|
||||
#if HAVE_SCHED_GETCPU
|
||||
_bt_cpu[slot] = sched_getcpu();
|
||||
#else
|
||||
_bt_cpu[slot] = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
_bt_ts_set(int slot, pthread_t self)
|
||||
{
|
||||
#if defined(__clockid_t_defined)
|
||||
clockid_t cid;
|
||||
pthread_getcpuclockid(self, &cid);
|
||||
clock_gettime(cid, &(_bt_ts[slot]));
|
||||
#else
|
||||
(void) self;
|
||||
memset(&(_bt_ts[slot]), 0, sizeof(struct timespec));
|
||||
#endif
|
||||
}
|
||||
|
||||
// this signal handler is called inside each and every thread when the
|
||||
// thread gets a signal via pthread_kill(). this causes the thread to
|
||||
// stop here inside this handler and "do something" then when this returns
|
||||
// resume whatever it was doing like any signal handler
|
||||
static void
|
||||
_eina_debug_signal(int sig EINA_UNUSED,
|
||||
siginfo_t *si EINA_UNUSED,
|
||||
void *foo EINA_UNUSED)
|
||||
{
|
||||
int i, slot = 0;
|
||||
pthread_t self = pthread_self();
|
||||
|
||||
// find which slot in the array of threads we have so we store info
|
||||
// in the correct slot for us
|
||||
if (self != _eina_debug_thread_mainloop)
|
||||
{
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
if (self == _eina_debug_thread_active[i].thread)
|
||||
{
|
||||
slot = i + 1;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
// we couldn't find out thread reference! help!
|
||||
fprintf(stderr, "EINA DEBUG ERROR: can't find thread slot!\n");
|
||||
eina_semaphore_release(&_eina_debug_monitor_return_sem, 1);
|
||||
return;
|
||||
}
|
||||
found:
|
||||
// store thread info like what cpu core we are on now (not reliable
|
||||
// but hey - better than nothing), the amount of cpu time total
|
||||
// we have consumed (it's cumulative so subtracing deltas can give
|
||||
// you an average amount of cpu time consumed between now and the
|
||||
// previous time we looked) and also a full backtrace
|
||||
_bt_cpu_set(slot);
|
||||
_bt_ts_set(slot, self);
|
||||
_bt_buf_len[slot] = _eina_debug_unwind_bt(_bt_buf[slot], EINA_MAX_BT);
|
||||
// now wake up the monitor to let them know we are done collecting our
|
||||
// backtrace info
|
||||
eina_semaphore_release(&_eina_debug_monitor_return_sem, 1);
|
||||
}
|
||||
|
||||
// we shall sue SIGPROF as out signal for pausing threads and having them
|
||||
// dump a backtrace for polling based profiling
|
||||
#define SIG SIGPROF
|
||||
|
||||
// a quick and dirty local time point getter func - not portable
|
||||
static inline double
|
||||
get_time(void)
|
||||
{
|
||||
#if defined(__clockid_t_defined)
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0);
|
||||
#else
|
||||
struct timeval timev;
|
||||
gettimeofday(&timev, NULL);
|
||||
return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
_eina_debug_collect_bt(pthread_t pth)
|
||||
{
|
||||
// this async signals the thread to switch to the deebug signal handler
|
||||
// and collect a backtrace and other info from inside the thread
|
||||
pthread_kill(pth, SIG);
|
||||
}
|
||||
|
||||
// this is a DEDICATED thread tojust collect system info and to have the
|
||||
// least impact it can on a cpu core or system. all this does right now
|
||||
// is sleep and wait for a command to begin polling for the cpu state.
|
||||
// right now that means iterating through cpu's and getting their cpu
|
||||
// frequency to match up with event logs.
|
||||
static void *
|
||||
_eina_debug_sysmon(void *_data EINA_UNUSED)
|
||||
{
|
||||
static int cpufreqs[64] = { 0 };
|
||||
int i, fd, freq;
|
||||
char buf[256], path[256];
|
||||
ssize_t red;
|
||||
#if defined(__clockid_t_defined)
|
||||
static struct timespec t_last = { 0, 0 };
|
||||
static Eina_Debug_Thread *prev_threads = NULL;
|
||||
static int prev_threads_num = 0;
|
||||
int j, cpu;
|
||||
Eina_Bool prev_threads_redo;
|
||||
clockid_t cid;
|
||||
struct timespec t, t_now;
|
||||
unsigned long long tim_span, tim1, tim2;
|
||||
#endif
|
||||
|
||||
// set a name for this thread for system debugging
|
||||
#ifdef EINA_HAVE_PTHREAD_SETNAME
|
||||
# ifndef __linux__
|
||||
pthread_set_name_np
|
||||
# else
|
||||
pthread_setname_np
|
||||
# endif
|
||||
(pthread_self(), "Edbg-sys");
|
||||
#endif
|
||||
for (;;)
|
||||
{
|
||||
// wait on a mutex that will be locked for as long as this
|
||||
// threead is not meant to go running
|
||||
eina_lock_take(&_eina_debug_sysmon_lock);
|
||||
// if we need to reset as we just started polling system stats...
|
||||
if (_eina_debug_sysmon_reset)
|
||||
{
|
||||
_eina_debug_sysmon_reset = 0;
|
||||
// clear out all the clocks for threads
|
||||
#if defined(__clockid_t_defined)
|
||||
// reset the last clock timestamp when we checked to "now"
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
t_last = t;
|
||||
// walk over all threads
|
||||
eina_spinlock_take(&_eina_debug_thread_lock);
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
// get the correct clock id to use for the target thread
|
||||
pthread_getcpuclockid
|
||||
(_eina_debug_thread_active[i].thread, &cid);
|
||||
// get the clock cpu time accumulation for that threas
|
||||
clock_gettime(cid, &t);
|
||||
_eina_debug_thread_active[i].clok = t;
|
||||
}
|
||||
eina_spinlock_release(&_eina_debug_thread_lock);
|
||||
#endif
|
||||
// clear all the cpu freq (up to 64 cores) to 0
|
||||
for (i = 0; i < 64; i++) cpufreqs[i] = 0;
|
||||
}
|
||||
eina_lock_release(&_eina_debug_sysmon_lock);
|
||||
|
||||
#if defined(__clockid_t_defined)
|
||||
// get the current time
|
||||
clock_gettime(CLOCK_MONOTONIC, &t_now);
|
||||
tim1 = (t_last.tv_sec * 1000000000LL) + t_last.tv_nsec;
|
||||
// the time span between now and last time we checked
|
||||
tim_span = ((t_now.tv_sec * 1000000000LL) + t_now.tv_nsec) - tim1;
|
||||
// if the time span is non-zero we might get sensible results
|
||||
if (tim_span > 0)
|
||||
{
|
||||
prev_threads_redo = EINA_FALSE;
|
||||
eina_spinlock_take(&_eina_debug_thread_lock);
|
||||
// figure out if the list of thread id's has changed since
|
||||
// our last poll. this is imporant as we need to set the
|
||||
// thread cpu usage to 0 for threads that have disappeared
|
||||
if (prev_threads_num != _eina_debug_thread_active_num)
|
||||
prev_threads_redo = EINA_TRUE;
|
||||
else
|
||||
{
|
||||
// XXX: isolate this out of hot path
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
if (_eina_debug_thread_active[i].thread !=
|
||||
prev_threads[i].thread)
|
||||
{
|
||||
prev_threads_redo = EINA_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
pthread_t thread = _eina_debug_thread_active[i].thread;
|
||||
// get the clock for the thread and its cpu time usage
|
||||
pthread_getcpuclockid(thread, &cid);
|
||||
clock_gettime(cid, &t);
|
||||
// calculate a long timestamp (64bits)
|
||||
tim1 = (_eina_debug_thread_active[i].clok.tv_sec * 1000000000LL) +
|
||||
_eina_debug_thread_active[i].clok.tv_nsec;
|
||||
// and get the difference in clock time in NS
|
||||
tim2 = ((t.tv_sec * 1000000000LL) + t.tv_nsec) - tim1;
|
||||
// and that as percentage of the timespan
|
||||
cpu = (int)((100 * (int)tim2) / (int)tim_span);
|
||||
// round to the nearest 10 percent - rough anyway
|
||||
cpu = ((cpu + 5) / 10) * 10;
|
||||
if (cpu > 100) cpu = 100;
|
||||
// if the usage changed since last time we checked...
|
||||
if (cpu != _eina_debug_thread_active[i].val)
|
||||
{
|
||||
// log this change
|
||||
snprintf(buf, sizeof(buf), "*CPUUSED %llu",
|
||||
(unsigned long long)thread);
|
||||
snprintf(path, sizeof(path), "%i", _eina_debug_thread_active[i].val);
|
||||
eina_evlog(buf, NULL, 0.0, path);
|
||||
snprintf(path, sizeof(path), "%i", cpu);
|
||||
eina_evlog(buf, NULL, 0.0, path);
|
||||
// store the clock time + cpu we got for next poll
|
||||
_eina_debug_thread_active[i].val = cpu;
|
||||
}
|
||||
_eina_debug_thread_active[i].clok = t;
|
||||
}
|
||||
// so threads changed between this poll and last so we need
|
||||
// to redo our mapping/storage of them
|
||||
if (prev_threads_redo)
|
||||
{
|
||||
prev_threads_redo = EINA_FALSE;
|
||||
// find any threads from our last run that do not
|
||||
// exist now in our new list of threads
|
||||
for (j = 0; j < prev_threads_num; j++)
|
||||
{
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
if (prev_threads[j].thread ==
|
||||
_eina_debug_thread_active[i].thread) break;
|
||||
}
|
||||
// thread was there before and not now
|
||||
if (i == _eina_debug_thread_active_num)
|
||||
{
|
||||
// log it finishing - ie 0
|
||||
snprintf(buf, sizeof(buf), "*CPUUSED %llu",
|
||||
(unsigned long long)
|
||||
prev_threads[i].thread);
|
||||
eina_evlog(buf, NULL, 0.0, "0");
|
||||
}
|
||||
}
|
||||
// if the thread count changed then allocate a new shadow
|
||||
// buffer of thread id's etc.
|
||||
if (prev_threads_num != _eina_debug_thread_active_num)
|
||||
{
|
||||
if (prev_threads) free(prev_threads);
|
||||
prev_threads_num = _eina_debug_thread_active_num;
|
||||
prev_threads = malloc(prev_threads_num *
|
||||
sizeof(Eina_Debug_Thread));
|
||||
}
|
||||
// store the thread handles/id's
|
||||
for (i = 0; i < prev_threads_num; i++)
|
||||
prev_threads[i].thread =
|
||||
_eina_debug_thread_active[i].thread;
|
||||
}
|
||||
eina_spinlock_release(&_eina_debug_thread_lock);
|
||||
t_last = t_now;
|
||||
}
|
||||
#endif
|
||||
// poll up to 64 cpu cores for cpufreq info to log alongside
|
||||
// the evlog call data
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
// look at sysfs nodes per cpu
|
||||
snprintf
|
||||
(buf, sizeof(buf),
|
||||
"/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq", i);
|
||||
fd = open(buf, O_RDONLY);
|
||||
freq = 0;
|
||||
// if the node is there, read it
|
||||
if (fd >= 0)
|
||||
{
|
||||
// really low overhead read from cpufreq node (just an int)
|
||||
red = read(fd, buf, sizeof(buf) - 1);
|
||||
if (red > 0)
|
||||
{
|
||||
// read something - it should be an int with whitespace
|
||||
buf[red] = 0;
|
||||
freq = atoi(buf);
|
||||
// move to mhz
|
||||
freq = (freq + 500) / 1000;
|
||||
// round mhz to the nearest 100mhz - to have less noise
|
||||
freq = ((freq + 50) / 100) * 100;
|
||||
}
|
||||
// close the fd so we can freshly poll next time around
|
||||
close(fd);
|
||||
}
|
||||
// node not there - ran out of cpu's to poll?
|
||||
else break;
|
||||
// if the current frequency changed vs previous poll, then log
|
||||
if (freq != cpufreqs[i])
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "*CPUFREQ %i", i);
|
||||
snprintf(path, sizeof(path), "%i", freq);
|
||||
eina_evlog(buf, NULL, 0.0, path);
|
||||
cpufreqs[i] = freq;
|
||||
}
|
||||
}
|
||||
usleep(1000); // 1ms sleep
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// this is a DEDICATED debug thread to monitor the application so it works
|
||||
// even if the mainloop is blocked or the app otherwise deadlocked in some
|
||||
// way. this is an alternative to using external debuggers so we can get
|
||||
// users or developers to get useful information about an app at all times
|
||||
static void *
|
||||
_eina_debug_monitor(void *_data EINA_UNUSED)
|
||||
{
|
||||
int bts = 0, ret, max_fd;
|
||||
double t0, t;
|
||||
fd_set rfds, wfds, exfds;
|
||||
struct timeval tv = { 0, 0 };
|
||||
// some state for debugging
|
||||
unsigned int poll_time = 1000;
|
||||
Eina_Bool poll_on = EINA_FALSE;
|
||||
Eina_Bool poll_trace = EINA_FALSE;
|
||||
Eina_Bool poll_cpu = EINA_FALSE;
|
||||
|
||||
t0 = get_time();
|
||||
// set a name for this thread for system debugging
|
||||
#ifdef EINA_HAVE_PTHREAD_SETNAME
|
||||
# ifndef __linux__
|
||||
pthread_set_name_np
|
||||
# else
|
||||
pthread_setname_np
|
||||
# endif
|
||||
(pthread_self(), "Edbg-mon");
|
||||
#endif
|
||||
// sit forever processing commands or timeouts in the debug monitor
|
||||
// thread - this is separate to the rest of the app so it shouldn't
|
||||
// impact the application specifically
|
||||
for (;;)
|
||||
{
|
||||
int i;
|
||||
|
||||
// set up data for select like read fd's
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
FD_ZERO(&exfds);
|
||||
// the only fd we care about - out debug daemon connection
|
||||
FD_SET(_eina_debug_monitor_service_fd, &rfds);
|
||||
max_fd = _eina_debug_monitor_service_fd;
|
||||
// if we are in a polling mode then set up a timeout and wait for it
|
||||
if (poll_on)
|
||||
{
|
||||
if ((tv.tv_sec == 0) && (tv.tv_usec == 0))
|
||||
{
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = poll_time;
|
||||
}
|
||||
ret = select(max_fd + 1, &rfds, &wfds, &exfds, &tv);
|
||||
}
|
||||
// we have no timeout - so wait forever for a message from debugd
|
||||
else ret = select(max_fd + 1, &rfds, &wfds, &exfds, NULL);
|
||||
// if the fd for debug daemon says it's alive, process it
|
||||
if ((ret == 1) && (FD_ISSET(_eina_debug_monitor_service_fd, &rfds)))
|
||||
{
|
||||
// collect a single op on the debug daemon control fd
|
||||
char op[5];
|
||||
int size;
|
||||
unsigned char *data;
|
||||
|
||||
// get the opcode and stor in op - guarantee its 0 byte terminated
|
||||
data = NULL;
|
||||
size = _eina_debug_monitor_service_read(op, &data);
|
||||
// if not negative - we have a real message
|
||||
if (size >= 0)
|
||||
{
|
||||
// profiling on with poll time gap as uint payload
|
||||
if (!strcmp(op, "PLON"))
|
||||
{
|
||||
if (size >= 4) memcpy(&poll_time, data, 4);
|
||||
poll_on = EINA_TRUE;
|
||||
poll_trace = EINA_TRUE;
|
||||
}
|
||||
// profiling off with no payload
|
||||
else if (!strcmp(op, "PLOF"))
|
||||
{
|
||||
poll_time = 1000;
|
||||
poll_on = EINA_FALSE;
|
||||
poll_trace = EINA_FALSE;
|
||||
}
|
||||
// enable evlog
|
||||
else if (!strcmp(op, "EVON"))
|
||||
{
|
||||
if (!_eina_debug_evlog_active)
|
||||
{
|
||||
_eina_debug_evlog_active = 1;
|
||||
eina_evlog_start();
|
||||
_eina_debug_sysmon_reset = 1;
|
||||
eina_lock_release(&_eina_debug_sysmon_lock);
|
||||
}
|
||||
}
|
||||
// stop evlog
|
||||
else if (!strcmp(op, "EVOF"))
|
||||
{
|
||||
if (_eina_debug_evlog_active)
|
||||
{
|
||||
eina_lock_take(&_eina_debug_sysmon_lock);
|
||||
eina_evlog_stop();
|
||||
_eina_debug_evlog_active = 0;
|
||||
}
|
||||
}
|
||||
// enable evlog
|
||||
else if (!strcmp(op, "CPON"))
|
||||
{
|
||||
if (size >= 4) memcpy(&poll_time, data, 4);
|
||||
poll_on = EINA_TRUE;
|
||||
poll_cpu = EINA_TRUE;
|
||||
}
|
||||
// stop evlog
|
||||
else if (!strcmp(op, "CPOF"))
|
||||
{
|
||||
poll_on = EINA_FALSE;
|
||||
poll_cpu = EINA_FALSE;
|
||||
}
|
||||
// fetch the evlog
|
||||
else if (!strcmp(op, "EVLG"))
|
||||
{
|
||||
Eina_Evlog_Buf *evlog = eina_evlog_steal();
|
||||
if ((evlog) && (evlog->buf))
|
||||
{
|
||||
char tmp[12];
|
||||
unsigned int *tmpsize = (unsigned int *)(tmp + 0);
|
||||
char *op2 = "EVLG";
|
||||
unsigned int *overflow = (unsigned int *)(tmp + 8);
|
||||
|
||||
*tmpsize = (sizeof(tmp) - 4) + evlog->top;
|
||||
memcpy(tmp + 4, op2, 4);
|
||||
*overflow = evlog->overflow;
|
||||
write(_eina_debug_monitor_service_fd,
|
||||
tmp, sizeof(tmp));
|
||||
write(_eina_debug_monitor_service_fd,
|
||||
evlog->buf, evlog->top);
|
||||
}
|
||||
}
|
||||
// something we don't understand
|
||||
else fprintf(stderr,
|
||||
"EINA DEBUG ERROR: "
|
||||
"Uunknown command %s\n", op);
|
||||
free(data);
|
||||
}
|
||||
// major failure on debug daemon control fd - get out of here
|
||||
else goto fail;
|
||||
}
|
||||
|
||||
if (poll_on)
|
||||
{
|
||||
if (poll_trace)
|
||||
{
|
||||
// take a lock on grabbing thread debug info like backtraces
|
||||
eina_spinlock_take(&_eina_debug_thread_lock);
|
||||
// reset our "stack" of memory se use to dump thread info into
|
||||
_eina_debug_chunk_tmp_reset();
|
||||
// get an array of pointers for the backtrace array for main + th
|
||||
_bt_buf = _eina_debug_chunk_tmp_push
|
||||
((1 + _eina_debug_thread_active_num) * sizeof(void *));
|
||||
if (!_bt_buf) goto err;
|
||||
// get an array of pointers for the timespec array for mainloop + th
|
||||
_bt_ts = _eina_debug_chunk_tmp_push
|
||||
((1 + _eina_debug_thread_active_num) * sizeof(struct timespec));
|
||||
if (!_bt_ts) goto err;
|
||||
// get an array of pointers for the cpuid array for mainloop + th
|
||||
_bt_cpu = _eina_debug_chunk_tmp_push
|
||||
((1 + _eina_debug_thread_active_num) * sizeof(int));
|
||||
if (!_bt_cpu) goto err;
|
||||
// now get an array of void pts for mainloop bt
|
||||
_bt_buf[0] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *));
|
||||
if (!_bt_buf[0]) goto err;
|
||||
// get an array of void ptrs for each thread we know about for bt
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
{
|
||||
_bt_buf[i + 1] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *));
|
||||
if (!_bt_buf[i + 1]) goto err;
|
||||
}
|
||||
// get an array of ints to stor the bt len for mainloop + threads
|
||||
_bt_buf_len = _eina_debug_chunk_tmp_push
|
||||
((1 + _eina_debug_thread_active_num) * sizeof(int));
|
||||
// collect bt from the mainloop - always there
|
||||
_eina_debug_collect_bt(_eina_debug_thread_mainloop);
|
||||
// now collect per thread
|
||||
for (i = 0; i < _eina_debug_thread_active_num; i++)
|
||||
_eina_debug_collect_bt(_eina_debug_thread_active[i].thread);
|
||||
// we're done probing. now collec all the "i'm done" msgs on the
|
||||
// semaphore for every thread + mainloop
|
||||
for (i = 0; i < (_eina_debug_thread_active_num + 1); i++)
|
||||
eina_semaphore_lock(&_eina_debug_monitor_return_sem);
|
||||
// we now have gotten all the data from all threadd + mainloop.
|
||||
// we can process it now as we see fit, so release thread lock
|
||||
//// XXX: some debug so we can see the bt's we collect - will go
|
||||
// for (i = 0; i < (_eina_debug_thread_active_num + 1); i++)
|
||||
// {
|
||||
// _eina_debug_dump_fhandle_bt(stderr, _bt_buf[i], _bt_buf_len[i]);
|
||||
// }
|
||||
err:
|
||||
eina_spinlock_release(&_eina_debug_thread_lock);
|
||||
//// XXX: some debug just to see how well we perform - will go
|
||||
bts++;
|
||||
if (bts >= 10000)
|
||||
{
|
||||
t = get_time();
|
||||
fprintf(stderr, "%1.5f bt's per sec\n", (double)bts / (t - t0));
|
||||
t0 = t;
|
||||
bts = 0;
|
||||
}
|
||||
}
|
||||
if (poll_cpu)
|
||||
{
|
||||
// XXX: opendir /proc/sefl/task
|
||||
// eina_evlog("*cpustat", NULL, 0.0, cpustat);
|
||||
}
|
||||
}
|
||||
}
|
||||
fail:
|
||||
// we failed - get out of here and disconnect to debugd
|
||||
close(_eina_debug_monitor_service_fd);
|
||||
_eina_debug_monitor_service_fd = -1;
|
||||
// sleep forever because there is currently no logic to join this thread
|
||||
for (;;) pause();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// start up the debug monitor if we haven't already
|
||||
void
|
||||
_eina_debug_monitor_thread_start(void)
|
||||
{
|
||||
int err;
|
||||
sigset_t oldset, newset;
|
||||
|
||||
// if it's already running - we're good.
|
||||
if (_monitor_thread_runs) return;
|
||||
// create debug monitor thread
|
||||
sigemptyset(&newset);
|
||||
sigaddset(&newset, SIGPIPE);
|
||||
sigaddset(&newset, SIGALRM);
|
||||
sigaddset(&newset, SIGCHLD);
|
||||
sigaddset(&newset, SIGUSR1);
|
||||
sigaddset(&newset, SIGUSR2);
|
||||
sigaddset(&newset, SIGHUP);
|
||||
sigaddset(&newset, SIGQUIT);
|
||||
sigaddset(&newset, SIGINT);
|
||||
sigaddset(&newset, SIGTERM);
|
||||
#ifdef SIGPWR
|
||||
sigaddset(&newset, SIGPWR);
|
||||
#endif
|
||||
sigprocmask(SIG_BLOCK, &newset, &oldset);
|
||||
eina_lock_new(&_eina_debug_sysmon_lock);
|
||||
eina_lock_take(&_eina_debug_sysmon_lock);
|
||||
err = pthread_create(&_sysmon_thread, NULL, _eina_debug_sysmon, NULL);
|
||||
if (err != 0)
|
||||
{
|
||||
fprintf(stderr, "EINA DEBUG ERROR: Can't create debug thread 1!\n");
|
||||
abort();
|
||||
}
|
||||
err = pthread_create(&_monitor_thread, NULL, _eina_debug_monitor, NULL);
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
if (err != 0)
|
||||
{
|
||||
fprintf(stderr, "EINA DEBUG ERROR: Can't create debug thread 2!\n");
|
||||
abort();
|
||||
}
|
||||
_monitor_thread_runs = EINA_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
_eina_debug_monitor_signal_init(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
// set up signal handler for our profiling signal - eevery thread should
|
||||
// obey this (this is the case on linux - other OSs may vary)
|
||||
sa.sa_sigaction = _eina_debug_signal;
|
||||
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
if (sigaction(SIG, &sa, NULL) != 0)
|
||||
fprintf(stderr, "EINA DEBUG ERROR: Can't set up sig %i handler!\n", SIG);
|
||||
}
|
||||
|
||||
static const char *
|
||||
_socket_home_get(void)
|
||||
{
|
||||
static char *dir;
|
||||
|
||||
if (dir) return dir;
|
||||
// get possible debug daemon socket directory base
|
||||
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
|
||||
if (getuid() == geteuid()) dir = getenv("XDG_RUNTIME_DIR");
|
||||
#endif
|
||||
if (!dir) dir = (char *)eina_environment_home_get();
|
||||
if (!dir) dir = (char *)eina_environment_tmp_get();
|
||||
dir = strdup(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
// connect to efl_debugd
|
||||
void
|
||||
_eina_debug_monitor_service_connect(void)
|
||||
{
|
||||
char buf[4096];
|
||||
int fd, socket_unix_len, curstate = 0;
|
||||
struct sockaddr_un socket_unix;
|
||||
|
||||
// try this socket file - it will likely be:
|
||||
// ~/.ecore/efl_debug/0
|
||||
// or maybe
|
||||
// /var/run/UID/.ecore/efl_debug/0
|
||||
// either way a 4k buffer should be ebough ( if it's not we're on an
|
||||
// insane system)
|
||||
snprintf(buf, sizeof(buf), "%s/%s", _socket_home_get(), DEBUG_SERVER);
|
||||
// create the socket
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) goto err;
|
||||
// set the socket to close when we exec things so they don't inherit it
|
||||
if (!eina_file_close_on_exec(fd, EINA_TRUE)) goto err;
|
||||
// set up some socket options on addr re-use
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate,
|
||||
sizeof(curstate)) < 0)
|
||||
goto err;
|
||||
// sa that it's a unix socket and where the path is
|
||||
socket_unix.sun_family = AF_UNIX;
|
||||
strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path));
|
||||
#define LENGTH_OF_SOCKADDR_UN(s) \
|
||||
(strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
|
||||
socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
|
||||
// actually conenct to efl_debugd service
|
||||
if (connect(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
|
||||
goto err;
|
||||
// we succeeded - store fd
|
||||
_eina_debug_monitor_service_fd = fd;
|
||||
return;
|
||||
err:
|
||||
// some kind of connection failure here, so close a valid socket and
|
||||
// get out of here
|
||||
if (fd >= 0) close(fd);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,91 @@
|
|||
#ifndef EINA_DEBUG_PRIVATE_H_
|
||||
# define EINA_DEBUG_PRIVATE_H_
|
||||
|
||||
# include "eina_config.h"
|
||||
# include "eina_lock.h"
|
||||
# include "eina_thread.h"
|
||||
|
||||
#define LOCAL_SERVER_PATH ".edebug"
|
||||
#define LOCAL_SERVER_NAME "efl_debug"
|
||||
#define LOCAL_SERVER_PORT 0
|
||||
|
||||
#define REMOTE_SERVER_PORT 6666
|
||||
|
||||
#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
|
||||
|
||||
/* Max packet size */
|
||||
#define EINA_DEBUG_MAX_PACKET_SIZE 10000000
|
||||
|
||||
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
|
||||
#ifdef HAVE_BACKTRACE
|
||||
#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
|
||||
|
||||
|
||||
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);
|
||||
|
||||
Eina_Bool _eina_debug_timer_init(void);
|
||||
Eina_Bool _eina_debug_timer_shutdown(void);
|
||||
#endif
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2015 Carsten Haitzler
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library;
|
||||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "eina_debug.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
int
|
||||
_eina_debug_monitor_service_send(int fd, const char op[4],
|
||||
unsigned char *data, int size)
|
||||
{
|
||||
// send protocol packet. all protocol is an int for size of packet then
|
||||
// included in that size (so a minimum size of 4) is a 4 byte opcode
|
||||
// (all opcodes are 4 bytes as a string of 4 chars), then the real
|
||||
// message payload as a data blob after that
|
||||
unsigned char *buf = alloca(4 + 4 + size);
|
||||
int newsize = size + 4;
|
||||
memcpy(buf, &newsize, 4);
|
||||
memcpy(buf + 4, op, 4);
|
||||
if (size > 0) memcpy(buf + 8, data, size);
|
||||
return write(fd, buf, newsize + 4);
|
||||
}
|
||||
|
||||
void
|
||||
_eina_debug_monitor_service_greet(void)
|
||||
{
|
||||
// say hello to our debug daemon - tell them our PID and protocol
|
||||
// version we speak
|
||||
unsigned char buf[8];
|
||||
int version = 1; // version of protocol we speak
|
||||
int pid = getpid();
|
||||
memcpy(buf + 0, &version, 4);
|
||||
memcpy(buf + 4, &pid, 4);
|
||||
_eina_debug_monitor_service_send(_eina_debug_monitor_service_fd,
|
||||
"HELO", buf, sizeof(buf));
|
||||
}
|
||||
|
||||
int
|
||||
_eina_debug_monitor_service_read(char *op, unsigned char **data)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
unsigned int size;
|
||||
int rret;
|
||||
|
||||
// read first 8 bytes - payload size (excl size header) with 4 byte
|
||||
// opcode that always has to be there
|
||||
rret = read(_eina_debug_monitor_service_fd, buf, 8);
|
||||
if (rret == 8)
|
||||
{
|
||||
// store size in int - native endianess as it's local
|
||||
memcpy(&size, buf, 4);
|
||||
// min size of course is 4 (just opcode) and we will have a max
|
||||
// size for any packet of 1mb here coming from debug daemon
|
||||
// for sanity
|
||||
if ((size >= 4) && (size <= (1024 * 1024)))
|
||||
{
|
||||
// store 4 byte opcode and guarantee it's 0 byte terminated
|
||||
memcpy(op, buf + 4, 4);
|
||||
op[4] = 0;
|
||||
// subtract space for opcode
|
||||
size -= 4;
|
||||
// if we have no payload - move on
|
||||
if (size == 0) *data = NULL;
|
||||
else
|
||||
{
|
||||
// allocate a buffer for real payload
|
||||
*data = malloc(size);
|
||||
if (*data)
|
||||
{
|
||||
// get payload - blocking!!!!
|
||||
rret = read(_eina_debug_monitor_service_fd, *data, size);
|
||||
if (rret != (int)size)
|
||||
{
|
||||
// we didn't get payload as expected - error on
|
||||
// comms
|
||||
fprintf(stderr,
|
||||
"EINA DEBUG ERROR: "
|
||||
"Invalid Debug opcode read of %i\n", rret);
|
||||
free(*data);
|
||||
*data = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we couldn't allocate memory for payloa buffer
|
||||
// internal memory limit error
|
||||
fprintf(stderr,
|
||||
"EINA DEBUG ERROR: "
|
||||
"Cannot allocate %u bytes for op\n", size);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// return payload size (< 0 is an error)
|
||||
return size;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr,
|
||||
"EINA DEBUG ERROR: "
|
||||
"Invalid opcode size of %u\n", size);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
fprintf(stderr,
|
||||
"EINA DEBUG ERROR: "
|
||||
"Invalid opcode read %i != 8\n", rret);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
|
@ -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,4 @@ _eina_debug_thread_mainloop_set(void *th)
|
|||
pthread_t *pth = th;
|
||||
_eina_debug_thread_mainloop = *pth;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2017 Carsten Haitzler
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library;
|
||||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
# ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
# endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_SYS_EPOLL_H
|
||||
# include <sys/epoll.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "eina_debug.h"
|
||||
#include "eina_debug_private.h"
|
||||
|
||||
static Eina_Spinlock _lock;
|
||||
|
||||
struct _Eina_Debug_Timer
|
||||
{
|
||||
unsigned int rel_time;
|
||||
unsigned int timeout;
|
||||
Eina_Debug_Timer_Cb cb;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static Eina_List *_timers = NULL;
|
||||
|
||||
static Eina_Bool _thread_runs = EINA_FALSE;
|
||||
static Eina_Bool _exit_required = EINA_FALSE;
|
||||
static pthread_t _thread;
|
||||
|
||||
static int pipeToThread[2];
|
||||
|
||||
static void
|
||||
_timer_append(Eina_Debug_Timer *t)
|
||||
{
|
||||
Eina_Debug_Timer *t2;
|
||||
Eina_List *itr;
|
||||
unsigned int prev_time = 0;
|
||||
char c = '\0';
|
||||
EINA_LIST_FOREACH(_timers, itr, t2)
|
||||
{
|
||||
if (t2->timeout > t->timeout) goto end;
|
||||
prev_time = t2->timeout;
|
||||
}
|
||||
t2 = NULL;
|
||||
end:
|
||||
t->rel_time = t->timeout - prev_time;
|
||||
if (!t2) _timers = eina_list_append(_timers, t);
|
||||
else _timers = eina_list_prepend_relative(_timers, t, t2);
|
||||
write(pipeToThread[1], &c, 1);
|
||||
}
|
||||
|
||||
static void *
|
||||
_monitor(void *_data EINA_UNUSED)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
#define MAX_EVENTS 4
|
||||
struct epoll_event event;
|
||||
struct epoll_event events[MAX_EVENTS];
|
||||
int epfd = epoll_create(MAX_EVENTS), ret;
|
||||
|
||||
event.data.fd = pipeToThread[0];
|
||||
event.events = EPOLLIN;
|
||||
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, event.data.fd, &event);
|
||||
if (ret) perror("epoll_ctl/add");
|
||||
#ifdef EINA_HAVE_PTHREAD_SETNAME
|
||||
# ifndef __linux__
|
||||
pthread_set_name_np
|
||||
# else
|
||||
pthread_setname_np
|
||||
# endif
|
||||
(pthread_self(), "Edbg-tim");
|
||||
#endif
|
||||
for (;!_exit_required;)
|
||||
{
|
||||
int timeout = -1; //in milliseconds
|
||||
eina_spinlock_take(&_lock);
|
||||
if (_timers)
|
||||
{
|
||||
Eina_Debug_Timer *t = eina_list_data_get(_timers);
|
||||
timeout = t->timeout;
|
||||
}
|
||||
eina_spinlock_release(&_lock);
|
||||
|
||||
ret = epoll_wait(epfd, events, MAX_EVENTS, timeout);
|
||||
if (_exit_required) continue;
|
||||
|
||||
/* Some timer has been add/removed or we need to exit */
|
||||
if (ret)
|
||||
{
|
||||
char c;
|
||||
if (read(pipeToThread[0], &c, 1) != 1) _exit_required = EINA_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Eina_List *itr, *itr2, *renew = NULL;
|
||||
Eina_Debug_Timer *t;
|
||||
eina_spinlock_take(&_lock);
|
||||
EINA_LIST_FOREACH_SAFE(_timers, itr, itr2, t)
|
||||
{
|
||||
if (itr == _timers || t->rel_time == 0)
|
||||
{
|
||||
_timers = eina_list_remove(_timers, t);
|
||||
if (t->cb(t->data)) renew = eina_list_append(renew, t);
|
||||
else free(t);
|
||||
}
|
||||
}
|
||||
EINA_LIST_FREE(renew, t) _timer_append(t);
|
||||
eina_spinlock_release(&_lock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
_thread_runs = EINA_FALSE;
|
||||
close(pipeToThread[0]);
|
||||
close(pipeToThread[1]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EAPI Eina_Debug_Timer *
|
||||
eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data)
|
||||
{
|
||||
if (!cb || !timeout_ms) return NULL;
|
||||
Eina_Debug_Timer *t = calloc(1, sizeof(*t));
|
||||
t->cb = cb;
|
||||
t->data = data;
|
||||
t->timeout = timeout_ms;
|
||||
eina_spinlock_take(&_lock);
|
||||
_timer_append(t);
|
||||
if (!_thread_runs)
|
||||
{
|
||||
int err = pthread_create(&_thread, NULL, _monitor, NULL);
|
||||
if (err != 0)
|
||||
{
|
||||
e_debug("EINA DEBUG ERROR: Can't create debug timer thread!");
|
||||
abort();
|
||||
}
|
||||
_thread_runs = EINA_TRUE;
|
||||
}
|
||||
eina_spinlock_release(&_lock);
|
||||
return t;
|
||||
}
|
||||
|
||||
EAPI void
|
||||
eina_debug_timer_del(Eina_Debug_Timer *t)
|
||||
{
|
||||
eina_spinlock_take(&_lock);
|
||||
Eina_List *itr = eina_list_data_find_list(_timers, t);
|
||||
if (itr)
|
||||
{
|
||||
_timers = eina_list_remove_list(_timers, itr);
|
||||
free(t);
|
||||
}
|
||||
eina_spinlock_release(&_lock);
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
_eina_debug_timer_init(void)
|
||||
{
|
||||
eina_spinlock_new(&_lock);
|
||||
#ifndef _WIN32
|
||||
pipe(pipeToThread);
|
||||
#endif
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
_eina_debug_timer_shutdown(void)
|
||||
{
|
||||
char c = '\0';
|
||||
_exit_required = EINA_TRUE;
|
||||
write(pipeToThread[1], &c, 1);
|
||||
eina_spinlock_free(&_lock);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
|
@ -24,8 +24,6 @@
|
|||
#include "eina_evlog.h"
|
||||
#include "eina_debug.h"
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
|
||||
#ifdef HAVE_EVIL
|
||||
# include <Evil.h>
|
||||
#endif
|
||||
|
@ -35,11 +33,24 @@
|
|||
#endif
|
||||
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
# ifdef HAVE_MMAP
|
||||
# include <sys/mman.h>
|
||||
# endif
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define SWAP_64(x) x
|
||||
#define SWAP_32(x) x
|
||||
#define SWAP_16(x) x
|
||||
#define SWAP_DBL(x) x
|
||||
#else
|
||||
#define SWAP_64(x) eina_swap64(x)
|
||||
#define SWAP_32(x) eina_swap32(x)
|
||||
#define SWAP_16(x) eina_swap16(x)
|
||||
#define SWAP_DBL(x) SWAP_64(x)
|
||||
#endif
|
||||
|
||||
# define EVLOG_BUF_SIZE (4 * (1024 * 1024))
|
||||
|
||||
static Eina_Spinlock _evlog_lock;
|
||||
|
@ -54,6 +65,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)
|
||||
{
|
||||
|
@ -148,13 +161,13 @@ eina_evlog(const char *event, void *obj, double srctime, const char *detail)
|
|||
eina_spinlock_take(&_evlog_lock);
|
||||
strings = push_buf(buf, size);
|
||||
item = (Eina_Evlog_Item *)strings;
|
||||
item->tim = now;
|
||||
item->srctim = srctime;
|
||||
item->thread = (unsigned long long)(uintptr_t)pthread_self();
|
||||
item->obj = (unsigned long long)(uintptr_t)obj;
|
||||
item->event_offset = sizeof(Eina_Evlog_Item);
|
||||
item->detail_offset = detail_offset;
|
||||
item->event_next = size;
|
||||
item->tim = SWAP_DBL(now);
|
||||
item->srctim = SWAP_DBL(srctime);
|
||||
item->thread = SWAP_64((unsigned long long)(uintptr_t)pthread_self());
|
||||
item->obj = SWAP_64((unsigned long long)(uintptr_t)obj);
|
||||
item->event_offset = SWAP_16(sizeof(Eina_Evlog_Item));
|
||||
item->detail_offset = SWAP_16(detail_offset);
|
||||
item->event_next = SWAP_16(size);
|
||||
strcpy(strings + sizeof(Eina_Evlog_Item), event);
|
||||
if (detail_offset > 0) strcpy(strings + detail_offset, detail);
|
||||
eina_spinlock_release(&_evlog_lock);
|
||||
|
@ -211,6 +224,52 @@ eina_evlog_stop(void)
|
|||
eina_spinlock_release(&_evlog_lock);
|
||||
}
|
||||
|
||||
// get evlog
|
||||
static Eina_Bool
|
||||
_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))
|
||||
{
|
||||
int ovf = SWAP_32(evlog->overflow);
|
||||
resp_size = evlog->top + sizeof(evlog->overflow);
|
||||
resp_buf = malloc(resp_size);
|
||||
memcpy(resp_buf, &ovf, sizeof(ovf));
|
||||
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);
|
||||
free(resp_buf);
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
// enable evlog
|
||||
static Eina_Bool
|
||||
_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_TRUE;
|
||||
}
|
||||
|
||||
// stop evlog
|
||||
static Eina_Bool
|
||||
_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_TRUE;
|
||||
}
|
||||
|
||||
EINA_DEBUG_OPCODES_ARRAY_DEFINE(_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 +286,7 @@ eina_evlog_init(void)
|
|||
}
|
||||
#endif
|
||||
eina_evlog("+eina_init", NULL, 0.0, NULL);
|
||||
eina_debug_opcodes_register(NULL, _EINA_DEBUG_EVLOG_OPS(), NULL, NULL);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
|
@ -237,37 +297,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
|
||||
|
|
|
@ -37,11 +37,13 @@
|
|||
# include <Evil.h>
|
||||
#endif
|
||||
|
||||
#include "eina_debug.h"
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
# define EINA_LOG_BACKTRACE
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
# include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#include "eina_debug_private.h"
|
||||
#define EINA_LOG_BACKTRACE
|
||||
|
||||
#include "eina_config.h"
|
||||
#include "eina_private.h"
|
||||
#include "eina_inlist.h"
|
||||
|
|
|
@ -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 <pthread.h>
|
||||
#include <errno.h>
|
||||
#ifndef _WIN32
|
||||
# include <signal.h>
|
||||
#endif
|
||||
# include <string.h>
|
||||
|
||||
#if defined(EINA_HAVE_PTHREAD_AFFINITY) || defined(EINA_HAVE_PTHREAD_SETNAME)
|
||||
#ifndef __linux__
|
||||
|
@ -133,9 +134,7 @@ _eina_internal_call(void *context)
|
|||
{
|
||||
Eina_Thread_Call *c = context;
|
||||
void *r;
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
pthread_t self;
|
||||
#endif
|
||||
|
||||
EINA_THREAD_CLEANUP_PUSH(free, c);
|
||||
|
||||
|
@ -143,16 +142,11 @@ _eina_internal_call(void *context)
|
|||
c->prio == EINA_THREAD_IDLE)
|
||||
eina_sched_prio_drop();
|
||||
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
self = pthread_self();
|
||||
_eina_debug_thread_add(&self);
|
||||
EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &self);
|
||||
#endif
|
||||
r = c->func((void*) c->data, eina_thread_self());
|
||||
#ifdef EINA_HAVE_DEBUG
|
||||
EINA_THREAD_CLEANUP_POP(EINA_TRUE);
|
||||
#endif
|
||||
|
||||
EINA_THREAD_CLEANUP_POP(EINA_TRUE);
|
||||
|
||||
return r;
|
||||
|
|
|
@ -237,16 +237,38 @@ static struct {
|
|||
void (*shutdown)(void);
|
||||
Eina_Bool (*app_connect)(const char *appname);
|
||||
Eina_Bool is_init;
|
||||
} _clouseau_old_info;
|
||||
|
||||
static struct {
|
||||
Eina_Module *handle;
|
||||
Eina_Bool (*init)(void);
|
||||
Eina_Bool (*shutdown)(void);
|
||||
Eina_Bool is_init;
|
||||
} _clouseau_info;
|
||||
|
||||
#define _CLOUSEAU_OLD_LOAD_SYMBOL(cls_struct, sym) \
|
||||
do \
|
||||
{ \
|
||||
if ((cls_struct).handle) \
|
||||
(cls_struct).sym = eina_module_symbol_get((cls_struct).handle, "clouseau_" #sym); \
|
||||
if (!(cls_struct).sym) \
|
||||
{ \
|
||||
WRN("Failed loading symbol '%s' from the clouseau library.", "clouseau_" #sym); \
|
||||
if ((cls_struct).handle) eina_module_free((cls_struct).handle); \
|
||||
(cls_struct).handle = NULL; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define _CLOUSEAU_LOAD_SYMBOL(cls_struct, sym) \
|
||||
do \
|
||||
{ \
|
||||
(cls_struct).sym = eina_module_symbol_get((cls_struct).handle, "clouseau_" #sym); \
|
||||
if ((cls_struct).handle) \
|
||||
(cls_struct).sym = eina_module_symbol_get((cls_struct).handle, "clouseau_debug_" #sym); \
|
||||
if (!(cls_struct).sym) \
|
||||
{ \
|
||||
WRN("Failed loading symbol '%s' from the clouseau library.", "clouseau_" #sym); \
|
||||
eina_module_free((cls_struct).handle); \
|
||||
WRN("Failed loading symbol '%s' from the clouseau library.", "clouseau_debug_" #sym); \
|
||||
if ((cls_struct).handle) eina_module_free((cls_struct).handle); \
|
||||
(cls_struct).handle = NULL; \
|
||||
return EINA_FALSE; \
|
||||
} \
|
||||
|
@ -256,21 +278,32 @@ static struct {
|
|||
static void
|
||||
_elm_clouseau_unload()
|
||||
{
|
||||
if (!_clouseau_info.is_init)
|
||||
return;
|
||||
|
||||
if (_clouseau_info.shutdown)
|
||||
if (_clouseau_old_info.is_init)
|
||||
{
|
||||
_clouseau_info.shutdown();
|
||||
if (_clouseau_old_info.shutdown)
|
||||
{
|
||||
_clouseau_old_info.shutdown();
|
||||
}
|
||||
if (_clouseau_old_info.handle)
|
||||
{
|
||||
eina_module_free(_clouseau_old_info.handle);
|
||||
_clouseau_old_info.handle = NULL;
|
||||
}
|
||||
_clouseau_old_info.is_init = EINA_FALSE;
|
||||
}
|
||||
|
||||
if (_clouseau_info.handle)
|
||||
if (_clouseau_info.is_init)
|
||||
{
|
||||
eina_module_free(_clouseau_info.handle);
|
||||
_clouseau_info.handle = NULL;
|
||||
if (_clouseau_info.shutdown)
|
||||
{
|
||||
_clouseau_info.shutdown();
|
||||
}
|
||||
if (_clouseau_info.handle)
|
||||
{
|
||||
eina_module_free(_clouseau_info.handle);
|
||||
_clouseau_info.handle = NULL;
|
||||
}
|
||||
_clouseau_info.is_init = EINA_FALSE;
|
||||
}
|
||||
|
||||
_clouseau_info.is_init = EINA_FALSE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
|
@ -282,30 +315,53 @@ _elm_clouseau_reload()
|
|||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
if (_clouseau_info.is_init)
|
||||
return EINA_TRUE;
|
||||
|
||||
_clouseau_info.handle = eina_module_new(
|
||||
PACKAGE_LIB_DIR "/libclouseau" LIBEXT);
|
||||
if (!_clouseau_info.handle || !eina_module_load(_clouseau_info.handle))
|
||||
if (!_clouseau_old_info.is_init)
|
||||
{
|
||||
WRN("Failed loading the clouseau library.");
|
||||
if (_clouseau_info.handle) eina_module_free(_clouseau_info.handle);
|
||||
_clouseau_info.handle = NULL;
|
||||
return EINA_FALSE;
|
||||
_clouseau_old_info.handle = eina_module_new(
|
||||
PACKAGE_LIB_DIR "/libclouseau" LIBEXT);
|
||||
if (!_clouseau_old_info.handle || !eina_module_load(_clouseau_old_info.handle))
|
||||
{
|
||||
WRN("Failed loading the clouseau_old library.");
|
||||
if (_clouseau_old_info.handle) eina_module_free(_clouseau_old_info.handle);
|
||||
_clouseau_old_info.handle = NULL;
|
||||
}
|
||||
|
||||
_CLOUSEAU_OLD_LOAD_SYMBOL(_clouseau_old_info, init);
|
||||
_CLOUSEAU_OLD_LOAD_SYMBOL(_clouseau_old_info, shutdown);
|
||||
_CLOUSEAU_OLD_LOAD_SYMBOL(_clouseau_old_info, app_connect);
|
||||
|
||||
if (_clouseau_old_info.handle)
|
||||
{
|
||||
_clouseau_old_info.init();
|
||||
if (!_clouseau_old_info.app_connect(elm_app_name_get()))
|
||||
{
|
||||
ERR("Failed connecting to the clouseau server.");
|
||||
}
|
||||
_clouseau_old_info.is_init = EINA_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
_CLOUSEAU_LOAD_SYMBOL(_clouseau_info, init);
|
||||
_CLOUSEAU_LOAD_SYMBOL(_clouseau_info, shutdown);
|
||||
_CLOUSEAU_LOAD_SYMBOL(_clouseau_info, app_connect);
|
||||
|
||||
_clouseau_info.init();
|
||||
if (!_clouseau_info.app_connect(elm_app_name_get()))
|
||||
if (!_clouseau_info.is_init)
|
||||
{
|
||||
ERR("Failed connecting to the clouseau server.");
|
||||
}
|
||||
_clouseau_info.handle = eina_module_new(
|
||||
PACKAGE_LIB_DIR "/libclouseau_debug" LIBEXT);
|
||||
if (!_clouseau_info.handle || !eina_module_load(_clouseau_info.handle))
|
||||
{
|
||||
WRN("Failed loading the clouseau library.");
|
||||
if (_clouseau_info.handle) eina_module_free(_clouseau_info.handle);
|
||||
_clouseau_info.handle = NULL;
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
_clouseau_info.is_init = EINA_TRUE;
|
||||
_CLOUSEAU_LOAD_SYMBOL(_clouseau_info, init);
|
||||
_CLOUSEAU_LOAD_SYMBOL(_clouseau_info, shutdown);
|
||||
|
||||
if (_clouseau_info.handle)
|
||||
{
|
||||
_clouseau_info.init();
|
||||
_clouseau_info.is_init = EINA_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
|
|
@ -1770,6 +1770,33 @@ efl_replace(Eo **storage, Eo *new_obj)
|
|||
|
||||
#include "efl_future.h"
|
||||
|
||||
/**
|
||||
* @addtogroup Eo_Iterators Eo iterators
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Get an iterator on the Eo classes
|
||||
*
|
||||
* You can use this function to walk over the Eo classes.
|
||||
*
|
||||
* @return an iterator on success, NULL otherwise
|
||||
*/
|
||||
EAPI Eina_Iterator *eo_classes_iterator_new(void);
|
||||
|
||||
/**
|
||||
* @brief Get an iterator on the Eo objects
|
||||
*
|
||||
* You can use this function to walk over the Eo objects.
|
||||
*
|
||||
* @return an iterator on success, NULL otherwise
|
||||
*/
|
||||
EAPI Eina_Iterator *eo_objects_iterator_new(void);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
121
src/lib/eo/eo.c
121
src/lib/eo/eo.c
|
@ -2934,3 +2934,124 @@ _eo_log_obj_shutdown(void)
|
|||
eina_inarray_flush(&_eo_log_objs_no_debug);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Eina_Iterator iterator;
|
||||
unsigned int cur_kl_id;
|
||||
} _Eo_Classes_Iterator;
|
||||
|
||||
static Eina_Bool
|
||||
_eo_classes_iterator_next(Eina_Iterator *it, void **data)
|
||||
{
|
||||
_Eo_Classes_Iterator *eo_it = (_Eo_Classes_Iterator *)it;
|
||||
|
||||
if (eo_it->cur_kl_id == _eo_classes_last_id) return EINA_FALSE;
|
||||
*data = _eo_class_id_get(_eo_classes[eo_it->cur_kl_id]);
|
||||
eo_it->cur_kl_id++;
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_eo_classes_iterator_free(Eina_Iterator *it)
|
||||
{
|
||||
EINA_MAGIC_SET(it, EINA_MAGIC_NONE);
|
||||
free(it);
|
||||
}
|
||||
|
||||
EAPI Eina_Iterator *
|
||||
eo_classes_iterator_new(void)
|
||||
{
|
||||
_Eo_Classes_Iterator *it;
|
||||
|
||||
it = calloc(1, sizeof (*it));
|
||||
if (!it) return NULL;
|
||||
|
||||
it->iterator.version = EINA_ITERATOR_VERSION;
|
||||
it->iterator.next = _eo_classes_iterator_next;
|
||||
it->iterator.free = _eo_classes_iterator_free;
|
||||
EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
|
||||
|
||||
return (Eina_Iterator *)it;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Eina_Iterator iterator;
|
||||
Eo_Id_Table_Data *tdata;
|
||||
Table_Index mid_table_id;
|
||||
Table_Index table_id;
|
||||
Table_Index entry_id;
|
||||
} _Eo_Objects_Iterator;
|
||||
|
||||
static Eina_Bool
|
||||
_eo_objects_iterator_next(Eina_Iterator *it, void **data)
|
||||
{
|
||||
Table_Index mid_table_id, table_id, entry_id;
|
||||
Eo_Id_Table_Data *tdata;
|
||||
_Eo_Objects_Iterator *eo_it = (_Eo_Objects_Iterator *)it;
|
||||
if (!eo_it->tdata) return EINA_FALSE;
|
||||
|
||||
tdata = eo_it->tdata;
|
||||
mid_table_id = eo_it->mid_table_id;
|
||||
table_id = eo_it->table_id;
|
||||
entry_id = eo_it->entry_id;
|
||||
while (mid_table_id < MAX_MID_TABLE_ID)
|
||||
{
|
||||
if (tdata->eo_ids_tables[mid_table_id])
|
||||
{
|
||||
while (table_id < MAX_TABLE_ID)
|
||||
{
|
||||
if (TABLE_FROM_IDS)
|
||||
{
|
||||
while (entry_id < MAX_ENTRY_ID)
|
||||
{
|
||||
_Eo_Id_Entry *entry = &(TABLE_FROM_IDS->entries[entry_id]);
|
||||
if (entry->active)
|
||||
{
|
||||
Eo *obj = _eo_header_id_get((Eo_Header *) entry->ptr);
|
||||
*data = obj;
|
||||
eo_it->mid_table_id = mid_table_id;
|
||||
eo_it->table_id = table_id;
|
||||
eo_it->entry_id = entry_id + 1;
|
||||
return EINA_TRUE;
|
||||
}
|
||||
entry_id++;
|
||||
}
|
||||
entry_id = 0;
|
||||
}
|
||||
table_id++;
|
||||
}
|
||||
table_id = 0;
|
||||
}
|
||||
mid_table_id++;
|
||||
}
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
_eo_objects_iterator_free(Eina_Iterator *it)
|
||||
{
|
||||
EINA_MAGIC_SET(it, EINA_MAGIC_NONE);
|
||||
free(it);
|
||||
}
|
||||
|
||||
EAPI Eina_Iterator *
|
||||
eo_objects_iterator_new(void)
|
||||
{
|
||||
_Eo_Objects_Iterator *it;
|
||||
Eo_Id_Table_Data *tdata = _eo_table_data_table_get(_eo_table_data_get(), EFL_ID_DOMAIN_MAIN);
|
||||
|
||||
if (!tdata) return NULL;
|
||||
|
||||
it = calloc(1, sizeof (*it));
|
||||
if (!it) return NULL;
|
||||
|
||||
it->tdata = tdata;
|
||||
it->iterator.version = EINA_ITERATOR_VERSION;
|
||||
it->iterator.next = _eo_objects_iterator_next;
|
||||
it->iterator.free = _eo_objects_iterator_free;
|
||||
EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
|
||||
|
||||
return (Eina_Iterator *)it;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue