First patch of the Eina Debug layer rewriting

Eina Debug is a new layer aimed for EFL debugging. It offers scalability
by allowing registration of operations not specific to EFL core.

The protocol is simple and the APIs try to provide as much
functionalities/freedom as possible.
This commit is contained in:
Daniel Zaoui 2016-11-27 07:48:10 +02:00
parent e53b77d6ce
commit 5f268ec26a
20 changed files with 2463 additions and 1776 deletions

View File

@ -166,18 +166,12 @@ bin_PROGRAMS += \
bin/efl/efl_debugd \
bin/efl/efl_debug
bin_efl_efl_debugd_SOURCES = \
bin/efl/efl_debugd.c \
bin/efl/efl_debug_common.c \
bin/efl/efl_debug_common.h
bin_efl_efl_debugd_SOURCES = bin/efl/efl_debugd.c
bin_efl_efl_debugd_CPPFLAGS = -I$(top_builddir)/src/bin/efl @EINA_CFLAGS@ @ECORE_CFLAGS@ @ECORE_CON_CFLAGS@
bin_efl_efl_debugd_LDADD = @EFL_LIBS@ @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
bin_efl_efl_debugd_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
bin_efl_efl_debug_SOURCES = \
bin/efl/efl_debug.c \
bin/efl/efl_debug_common.c \
bin/efl/efl_debug_common.h
bin_efl_efl_debug_SOURCES = bin/efl/efl_debug.c
bin_efl_efl_debug_CPPFLAGS = -I$(top_builddir)/src/bin/efl @EINA_CFLAGS@ @ECORE_CFLAGS@ @ECORE_CON_CFLAGS@
bin_efl_efl_debug_LDADD = @EFL_LIBS@ @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
bin_efl_efl_debug_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@

View File

@ -12,6 +12,7 @@ installed_einaheadersdir = $(includedir)/eina-@VMAJ@/eina
dist_installed_einaheaders_DATA = \
lib/eina/eina_safety_checks.h \
lib/eina/eina_error.h \
lib/eina/eina_debug.h \
lib/eina/eina_log.h \
lib/eina/eina_inline_log.x \
lib/eina/eina_fp.h \
@ -123,9 +124,8 @@ lib/eina/eina_debug.c \
lib/eina/eina_debug_bt.c \
lib/eina/eina_debug_bt_file.c \
lib/eina/eina_debug_chunk.c \
lib/eina/eina_debug_monitor.c \
lib/eina/eina_debug_proto.c \
lib/eina/eina_debug_thread.c \
lib/eina/eina_debug_cpu.c \
lib/eina/eina_error.c \
lib/eina/eina_evlog.c \
lib/eina/eina_file_common.h \
@ -171,7 +171,6 @@ lib/eina/eina_util.c \
lib/eina/eina_value.c \
lib/eina/eina_value_util.c \
lib/eina/eina_xattr.c \
lib/eina/eina_debug.h \
lib/eina/eina_private.h \
lib/eina/eina_share_common.h \
lib/eina/eina_strbuf_common.h \

View File

@ -16,269 +16,250 @@
* if not, see <http://www.gnu.org/licenses/>.
*/
#define DECLARE_OPS
#include "efl_debug_common.h"
#include <Eina.h>
#include <Ecore.h>
static Eo *dialer;
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
static Eina_List *waiting;
#define EXTRACT(_buf, pval, sz) \
{ \
memcpy(pval, _buf, sz); \
_buf += sz; \
}
#define _EVLOG_INTERVAL 0.2
static int retval = EXIT_SUCCESS;
static int _evlog_max_times = 0;
static Ecore_Timer *_evlog_fetch_timer = NULL;
static FILE *_evlog_file = NULL;
static void
_process_reply(void *data EINA_UNUSED, const char op[static 4], const Eina_Slice payload)
static int _cl_stat_reg_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _cid_from_pid_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _prof_on_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _prof_off_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _cpufreq_on_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _cpufreq_off_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _evlog_get_opcode = EINA_DEBUG_OPCODE_INVALID;
static Eina_Debug_Session *_session = NULL;
static int _cid = 0;
static int my_argc = 0;
static char **my_argv = NULL;
static Eina_Debug_Error
_evlog_get_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
{
if (IS_OP(CLST))
static int received_times = 0;
unsigned char *d = buffer;
unsigned int *overflow = (unsigned int *)(d + 0);
unsigned char *p = d + 4;
unsigned int blocksize = size - 4;
if(++received_times <= _evlog_max_times)
{
int mypid = getpid();
size_t offset;
waiting = eina_list_remove(waiting, OP_CLST);
for (offset = 0; offset + sizeof(int) <= payload.len; offset += sizeof(int))
if ((_evlog_file) && (blocksize > 0))
{
int p;
unsigned int header[3];
memcpy(&p, payload.bytes + offset, sizeof(int));
if (p == mypid) continue;
if (p > 0) printf("%i\n", p);
header[0] = 0xffee211;
header[1] = blocksize;
header[2] = *overflow;
if (fwrite(header, 1, 12, _evlog_file) < 12 ||
fwrite(p, 1, blocksize, _evlog_file) < blocksize)
printf("Error writing bytes to evlog file\n");
}
}
else
{
fprintf(stderr, "ERROR: unexpected server reply: %.4s\n", op);
retval = EXIT_FAILURE;
}
if (!waiting) ecore_main_loop_quit();
}
static void
_on_data(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
{
if (!received_data(dialer, _process_reply, NULL))
if(received_times == _evlog_max_times)
{
retval = EXIT_FAILURE;
printf("Received last evlog response\n");
if (_evlog_file) fclose(_evlog_file);
_evlog_file = NULL;
ecore_main_loop_quit();
}
return EINA_DEBUG_OK;
}
static Eina_Bool
_command_send(const char op[static 4], const void *data, unsigned int len)
_cb_evlog(void *data EINA_UNUSED)
{
if (!send_data(dialer, op, data, len))
static int sent_times = 0;
Eina_Bool ret = ECORE_CALLBACK_RENEW;
if(++sent_times <= _evlog_max_times)
eina_debug_session_send(_session, _cid, _evlog_get_opcode, NULL, 0);
if(sent_times == _evlog_max_times)
{
retval = EXIT_FAILURE;
return EINA_FALSE;
eina_debug_session_send(_session, _cid, _cpufreq_off_opcode, NULL, 0);
ecore_timer_del(_evlog_fetch_timer);
_evlog_fetch_timer = NULL;
ret = ECORE_CALLBACK_CANCEL;
}
return EINA_TRUE;
return ret;
}
#define command_send(op, data, len) _command_send(OP_ ## op, data, len)
static void
_write_finished(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
static Eina_Debug_Error
_cid_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer, int size EINA_UNUSED)
{
if (!waiting) ecore_main_loop_quit();
_cid = *(int *)buffer;
const char *op_str = my_argv[1];
Eina_Bool quit = EINA_TRUE;
if ((!strcmp(op_str, "pon")) && (3 <= (my_argc - 1)))
{
int freq = atoi(my_argv[3]);
eina_debug_session_send(_session, _cid, _prof_on_opcode, &freq, sizeof(int));
}
else if (!strcmp(op_str, "poff"))
eina_debug_session_send(_session, _cid, _prof_off_opcode, NULL, 0);
else if (!strcmp(op_str, "evlogon") && (3 <= (my_argc - 1)))
{
double max_time;
sscanf(my_argv[3], "%lf", &max_time);
_evlog_max_times = max_time > 0 ? (max_time/_EVLOG_INTERVAL+1) : 1;
eina_debug_session_send(_session, 0, _cl_stat_reg_opcode, NULL, 0);
printf("Evlog request will be sent %d times\n", _evlog_max_times);
eina_debug_session_send(_session, _cid, _cpufreq_on_opcode, NULL, 0);
/* Creating the evlog file and setting the timer */
char path[4096];
int pid = atoi(my_argv[2]);
snprintf(path, sizeof(path), "%s/efl_debug_evlog-%ld.log",
getenv("HOME"), (long)pid);
_evlog_file = fopen(path, "wb");
_evlog_fetch_timer = ecore_timer_add(_EVLOG_INTERVAL, _cb_evlog, NULL);
quit = EINA_FALSE;
}
else if (!strcmp(op_str, "evlogoff"))
eina_debug_session_send(_session, _cid, _cpufreq_off_opcode, NULL, 0);
if(quit)
ecore_main_loop_quit();
return EINA_DEBUG_OK;
}
static void
_finished(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
static Eina_Debug_Error
_clients_info_added_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
{
ecore_main_loop_quit();
char *buf = buffer;
while(size)
{
int cid, pid, len;
EXTRACT(buf, &cid, sizeof(int));
EXTRACT(buf, &pid, sizeof(int));
/* We dont need client notifications on evlog */
if(!_evlog_fetch_timer)
printf("Added: CID: %d - PID: %d - Name: %s\n", cid, pid, buf);
len = strlen(buf) + 1;
buf += len;
size -= (2 * sizeof(int) + len);
}
return EINA_DEBUG_OK;
}
static void
_error(void *data EINA_UNUSED, const Efl_Event *event)
static Eina_Debug_Error
_clients_info_deleted_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
{
Eina_Error *perr = event->info;
fprintf(stderr, "ERROR: error communicating to %s: %s\n",
efl_net_dialer_address_dial_get(dialer),
eina_error_msg_get(*perr));
retval = EXIT_FAILURE;
ecore_main_loop_quit();
}
int
main(int argc, char **argv)
{
Eo *loop;
char *path;
Eina_Error err;
int i;
if (argc < 2)
char *buf = buffer;
while(size)
{
fprintf(stderr, "ERROR: missing argument.\n");
return EXIT_FAILURE;
}
for (i = 1; i < argc; i++)
{
if ((strcmp(argv[i], "-h") != 0) &&
(strcmp(argv[i], "--help") != 0))
continue;
int cid;
EXTRACT(buf, &cid, sizeof(int));
size -= sizeof(int);
printf("Usage:\n"
"\n"
"\t%s <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_DEBUG_OK;
}
err = efl_net_dialer_dial(dialer, path);
if (err)
static void
_ecore_thread_dispatcher(void *data)
{
eina_debug_dispatch(_session, data);
}
Eina_Debug_Error
_disp_cb(Eina_Debug_Session *session EINA_UNUSED, void *buffer)
{
ecore_main_loop_thread_safe_call_async(_ecore_thread_dispatcher, buffer);
return EINA_DEBUG_OK;
}
static void
_args_handle(Eina_Bool flag)
{
if (!flag) exit(0);
eina_debug_session_dispatch_override(_session, _disp_cb);;
const char *op_str = my_argv[1];
if (op_str && !strcmp(op_str, "list"))
{
fprintf(stderr, "ERROR: could not connect '%s': %s\n", path, eina_error_msg_get(err));
retval = EXIT_FAILURE;
goto end;
eina_debug_session_send(_session, 0, _cl_stat_reg_opcode, NULL, 0);
}
else if (2 <= my_argc - 1)
{
int pid = atoi(my_argv[2]);
eina_debug_session_send(_session, 0, _cid_from_pid_opcode, &pid, sizeof(int));
}
}
static const Eina_Debug_Opcode ops[] =
{
{"daemon/observer/client/register", &_cl_stat_reg_opcode, NULL},
{"daemon/observer/slave_added", NULL, &_clients_info_added_cb},
{"daemon/observer/slave_deleted", NULL, &_clients_info_deleted_cb},
{"daemon/info/cid_from_pid", &_cid_from_pid_opcode, &_cid_get_cb},
{"profiler/on", &_prof_on_opcode, NULL},
{"profiler/off", &_prof_off_opcode, NULL},
{"cpufreq/on", &_cpufreq_on_opcode, NULL},
{"cpufreq/off", &_cpufreq_off_opcode, NULL},
{"evlog/get", &_evlog_get_opcode, _evlog_get_cb},
{NULL, NULL, NULL}
};
int
main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
{
eina_init();
ecore_init();
my_argc = argc;
my_argv = argv;
_session = eina_debug_local_connect(EINA_TRUE);
if (!_session)
{
fprintf(stderr, "ERROR: Cannot connect to debug daemon.\n");
return -1;
}
eina_debug_opcodes_register(_session, ops, _args_handle);
ecore_main_loop_begin();
end:
eina_list_free(waiting);
efl_del(dialer);
free(path);
ecore_con_shutdown();
ecore_shutdown();
eina_shutdown();
(void) OP_HELO;
(void) OP_EVLG;
return retval;
return 0;
}

View File

@ -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;
}

View File

@ -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

View File

@ -16,355 +16,129 @@
* if not, see <http://www.gnu.org/licenses/>.
*/
#define DECLARE_OPS
#include "efl_debug_common.h"
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <Eina.h>
#include <Ecore.h>
#define STORE(_buf, pval, sz) \
{ \
memcpy(_buf, pval, sz); \
_buf += sz; \
}
#define EXTRACT(_buf, pval, sz) \
{ \
memcpy(pval, _buf, sz); \
_buf += sz; \
}
typedef struct _Client Client;
struct _Client
{
Eo *client;
unsigned char *buf;
unsigned int buf_size;
Eina_Stringshare *app_name;
Ecore_Timer *evlog_fetch_timer;
int evlog_on;
FILE *evlog_file;
int version;
int fd;
int cid;
pid_t pid;
Eina_Bool cl_stat_obs : 1;
Eina_Bool is_master : 1;
};
static Eo *server;
static Eina_List *_clients = NULL;
static Eina_List *clients = NULL;
typedef Eina_Bool (*Opcode_Cb)(Client *client, void *buffer, int size);
static int retval;
static Eina_Hash *_string_to_opcode_hash = NULL;
static int _log_dom = -1;
static int _free_cid = 1;
#ifdef ERR
# undef ERR
static int _clients_stat_register_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _slave_added_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _slave_deleted_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _cid_from_pid_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _test_loop_opcode = EINA_DEBUG_OPCODE_INVALID;
typedef struct
{
int opcode;
Eina_Stringshare *opcode_string;
Opcode_Cb cb;
} Opcode_Information;
#define MAX_OPCODES 1000
Opcode_Information *_opcodes[MAX_OPCODES];
/* epoll stuff */
#ifndef _WIN32
static int _epfd = -1, _listening_master_fd = -1, _listening_slave_fd = -1;
#endif
#define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
#ifdef DBG
# undef DBG
#endif
#define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
#ifdef INF
# undef INF
#endif
#define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
#ifdef WRN
# undef WRN
#endif
#define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
#ifdef CRI
# undef CRI
#endif
#define CRI(...) EINA_LOG_DOM_CRIT(_log_dom, __VA_ARGS__)
#define send_cli(cl, op, data, len) \
do \
{ \
if (!send_data(cl->client, OP_ ## op, data, len)) \
{ \
if (!efl_io_closer_closed_get(cl->client)) \
efl_io_closer_close(cl->client); \
} \
} \
while (0)
static Client *
_client_pid_find(int pid)
_client_find_by_cid(int cid)
{
Client *c;
Eina_List *l;
if (pid <= 0) return NULL;
EINA_LIST_FOREACH(clients, l, c)
{
if (c->pid == pid) return c;
}
WRN("no client pid=%d", pid);
EINA_LIST_FOREACH(_clients, l, c)
if (c->cid == cid) return c;
return NULL;
}
static Eina_Bool
_cb_evlog(void *data)
static Client *
_client_find_by_pid(int pid)
{
Client *c = data;
send_cli(c, EVLG, NULL, 0);
return EINA_TRUE;
}
static void
_process_command(void *data, const char op[static 4], const Eina_Slice payload)
{
Client *c = data;
Client *c2;
Client *c;
Eina_List *l;
EINA_LIST_FOREACH(_clients, l, c)
if (c->pid == pid) return c;
return NULL;
}
DBG("client %p (%p) [pid:%d] op=%.4s payload=%zd", c, c->client, c->pid, op, payload.len);
static Client *
_client_find_by_fd(int fd)
{
Eina_List *itr;
Client *c;
EINA_LIST_FOREACH(_clients, itr, c)
if (c->fd == fd) return c;
return NULL;
}
if (IS_OP(HELO))
{
if (payload.len < sizeof(int) * 2)
{
fprintf(stderr, "INFO: client %p [pid: %d] sent invalid HELO\n", c, (int)c->pid);
if (!efl_io_closer_closed_get(c->client))
efl_io_closer_close(c->client);
}
else
{
memcpy(&c->version, payload.bytes, sizeof(int));
memcpy(&c->pid, payload.bytes + sizeof(int), sizeof(int));
INF("client %p (%p) HELO version=%d, pid=%d", c, c->client, c->version, c->pid);
}
}
else if (IS_OP(LIST))
{
int n = eina_list_count(clients);
unsigned int *pids = malloc(n * sizeof(int));
if (pids)
{
int i = 0;
EINA_LIST_FOREACH(clients, l, c2)
{
if (c2->pid == 0) continue; /* no HELO yet */
pids[i] = c2->pid;
i++;
}
send_cli(c, CLST, pids, i * sizeof(int));
free(pids);
}
}
else if (IS_OP(PLON))
{
if (payload.len < sizeof(int) * 2)
fprintf(stderr, "INFO: client %p [pid: %d] sent invalid PLON\n", c, (int)c->pid);
else
{
int pid;
unsigned int freq;
memcpy(&pid, payload.bytes, sizeof(int));
memcpy(&freq, payload.bytes + sizeof(int), sizeof(int));
c2 = _client_pid_find(pid);
if (!c2)
{
fprintf(stderr, "INFO: client %p [pid: %d] sent PLON %d: no such client\n", c, (int)c->pid, pid);
}
else
{
DBG("client %p (%p) [pid:%d] requested PLON on %p (%p) [pid:%d]",
c, c->client, c->pid,
c2, c2->client, c2->pid);
send_cli(c2, PLON, &freq, sizeof(freq));
}
}
}
else if (IS_OP(PLOF))
{
if (payload.len < sizeof(int))
fprintf(stderr, "INFO: client %p [pid: %d] sent invalid PLOF\n", c, (int)c->pid);
else
{
int pid;
memcpy(&pid, payload.bytes, sizeof(int));
c2 = _client_pid_find(pid);
if (!c2)
{
fprintf(stderr, "INFO: client %p [pid: %d] sent PLOF %d: no such client\n", c, (int)c->pid, pid);
}
else
{
DBG("client %p (%p) [pid:%d] requested PLOF on %p (%p) [pid:%d]",
c, c->client, c->pid,
c2, c2->client, c2->pid);
send_cli(c2, PLOF, NULL, 0);
}
}
}
else if (IS_OP(EVON))
{
if (payload.len < sizeof(int))
fprintf(stderr, "INFO: client %p [pid: %d] sent invalid EVON\n", c, (int)c->pid);
else
{
int pid;
memcpy(&pid, payload.bytes, sizeof(int));
c2 = _client_pid_find(pid);
if (!c2)
{
fprintf(stderr, "INFO: client %p [pid: %d] sent EVON %d: no such client\n", c, (int)c->pid, pid);
}
else
{
c2->evlog_on++;
DBG("client %p (%p) [pid:%d] requested EVON (%d) on %p (%p) [pid:%d]",
c, c->client, c->pid,
c2->evlog_on,
c2, c2->client, c2->pid);
if (c2->evlog_on == 1)
{
char buf[4096];
send_cli(c2, EVON, NULL, 0);
c2->evlog_fetch_timer = ecore_timer_add(0.2, _cb_evlog, c2);
snprintf(buf, sizeof(buf), "%s/efl_debug_evlog-%d.log",
getenv("HOME"), c2->pid);
c2->evlog_file = fopen(buf, "wb");
DBG("client %p (%p) [pid:%d] logging to %s [%p]",
c2, c2->client, c2->pid, buf, c2->evlog_file);
}
}
}
}
else if (IS_OP(EVOF))
{
if (payload.len < sizeof(int))
fprintf(stderr, "INFO: client %p [pid: %d] sent invalid EVOF\n", c, (int)c->pid);
else
{
int pid;
memcpy(&pid, payload.bytes, sizeof(int));
c2 = _client_pid_find(pid);
if (!c2)
{
fprintf(stderr, "INFO: client %p [pid: %d] sent EVOF %d: no such client\n", c, (int)c->pid, pid);
}
else
{
c2->evlog_on--;
DBG("client %p (%p) [pid:%d] requested EVOF (%d) on %p (%p) [pid:%d]",
c, c->client, c->pid,
c2->evlog_on,
c2, c2->client, c2->pid);
if (c2->evlog_on == 0)
{
send_cli(c2, EVOF, NULL, 0);
if (c2->evlog_fetch_timer)
{
ecore_timer_del(c2->evlog_fetch_timer);
c2->evlog_fetch_timer = NULL;
}
if (c2->evlog_file)
{
DBG("client %p (%p) [pid:%d] finished logged to %p",
c2, c2->client, c2->pid, c2->evlog_file);
fclose(c2->evlog_file);
c2->evlog_file = NULL;
}
}
else if (c2->evlog_on < 0)
c2->evlog_on = 0;
}
}
}
else if (IS_OP(EVLG))
{
if (payload.len < sizeof(int))
fprintf(stderr, "INFO: client %p [pid: %d] sent invalid EVLG\n", c, (int)c->pid);
else if (!c->evlog_file)
fprintf(stderr, "INFO: client %p [pid: %d] no matching EVON\n", c, (int)c->pid);
else
{
unsigned int blocksize = payload.len - sizeof(int);
if (blocksize > 0)
{
unsigned int header[3];
header[0] = 0xffee211;
header[1] = blocksize;
memcpy(header + 2, payload.mem, sizeof(int));
if ((fwrite(header, 12, 1, c->evlog_file) != 1) ||
(fwrite(payload.bytes + sizeof(int), blocksize, 1, c->evlog_file) != 1))
{
fprintf(stderr, "INFO: failed to write log file for client %p [pid: %d]\n", c, (int)c->pid);
fclose(c->evlog_file);
c->evlog_file = NULL;
c->evlog_on = 0;
send_cli(c, EVOF, NULL, 0);
if (c->evlog_fetch_timer)
{
ecore_timer_del(c->evlog_fetch_timer);
c->evlog_fetch_timer = NULL;
}
}
}
}
}
static int
_send(Client *dest, int opcode, void *payload, int payload_size)
{
int size = sizeof(Eina_Debug_Packet_Header) + payload_size;
char *buf = alloca(size);
Eina_Debug_Packet_Header *hdr = (Eina_Debug_Packet_Header *)buf;
hdr->size = size;
hdr->cid = 0;
hdr->thread_id = 0;
hdr->opcode = opcode;
memcpy(buf + sizeof(Eina_Debug_Packet_Header), payload, payload_size);
//printf("%d bytes sent (opcode %s) to %s fd %d\n", size, _opcodes[opcode]->opcode_string, dest->app_name, dest->fd);
return send(dest->fd, buf, size, 0);
}
static void
_client_data(void *data, const Efl_Event *event)
_client_del(Client *c)
{
Client *c = data;
if (!received_data(event->object, _process_command, c))
{
fprintf(stderr, "INFO: client %p [pid: %d] sent invalid data\n", c, (int)c->pid);
if (!efl_io_closer_closed_get(event->object))
efl_io_closer_close(event->object);
return;
}
}
Client *c2;
if (!c) return;
Eina_List *itr;
static void
_client_error(void *data, const Efl_Event *event)
{
Client *c = data;
Eina_Error *perr = event->info;
WRN("client %p [pid: %d] error: %s",
c, (int)c->pid, eina_error_msg_get(*perr));
fprintf(stderr, "INFO: client %p [pid: %d] error: %s\n",
c, (int)c->pid, eina_error_msg_get(*perr));
}
static void
_client_eos(void *data, const Efl_Event *event EINA_UNUSED)
{
Client *c = data;
DBG("client %p (%p) [pid: %d] closed, pending read %zu, write %zu",
c, c->client, (int)c->pid,
efl_io_buffered_stream_pending_read_get(c->client),
efl_io_buffered_stream_pending_write_get(c->client));
efl_io_closer_close(c->client);
}
static void
_client_write_finished(void *data, const Efl_Event *event EINA_UNUSED)
{
Client *c = data;
DBG("client %p (%p) [pid: %d] finished writing, pending read %zu",
c, c->client, (int)c->pid, efl_io_buffered_stream_pending_read_get(c->client));
}
static void
_client_read_finished(void *data, const Efl_Event *event EINA_UNUSED)
{
Client *c = data;
DBG("client %p (%p) [pid: %d] finished reading, pending write %zu",
c, c->client, (int)c->pid, efl_io_buffered_stream_pending_write_get(c->client));
}
static Efl_Callback_Array_Item *_client_cbs(void);
static void
_client_finished(void *data, const Efl_Event *event EINA_UNUSED)
{
Client *c = data;
clients = eina_list_remove(clients, c);
_clients = eina_list_remove(_clients, c);
if (c->evlog_fetch_timer)
{
ecore_timer_del(c->evlog_fetch_timer);
@ -375,111 +149,438 @@ _client_finished(void *data, const Efl_Event *event EINA_UNUSED)
fclose(c->evlog_file);
c->evlog_file = NULL;
}
efl_event_callback_array_del(c->client, _client_cbs(), c);
INF("finished client %p (%p) [pid:%d]", c, c->client, c->pid);
efl_unref(c->client);
/* Don't update the observers if the client is a master */
if (c->is_master) return;
EINA_LIST_FOREACH(_clients, itr, c2)
{
if (c2->cl_stat_obs) _send(c2, _slave_deleted_opcode, &c->cid, sizeof(int));
}
free(c);
}
EFL_CALLBACKS_ARRAY_DEFINE(_client_cbs,
{ EFL_IO_READER_EVENT_EOS, _client_eos },
{ EFL_IO_BUFFERED_STREAM_EVENT_ERROR, _client_error },
{ EFL_IO_BUFFERED_STREAM_EVENT_READ_FINISHED, _client_read_finished },
{ EFL_IO_BUFFERED_STREAM_EVENT_WRITE_FINISHED, _client_write_finished },
{ EFL_IO_BUFFERED_STREAM_EVENT_FINISHED, _client_finished },
{ EFL_IO_BUFFERED_STREAM_EVENT_SLICE_CHANGED, _client_data });
static void
_client_add(void *data EINA_UNUSED, const Efl_Event *event)
static Eina_Bool
_dispatch(Client *src, void *buffer, int size)
{
Client *c = calloc(1, sizeof(Client));
Eina_Debug_Packet_Header *hdr = (Eina_Debug_Packet_Header *)buffer;
if (hdr->cid)
{
/* If the client id is given, we forward */
Client *dest = _client_find_by_cid(hdr->cid);
if (dest)
{
if (dest->is_master != src->is_master)
{
hdr->cid = src->cid;
send(dest->fd, buffer, size, 0);
}
else
{
/*
* Packets Master -> Master or Slave -> Slave are forbidden
* Only Master <-> Slave packets are allowed.
*/
printf("Packet from %d to %d: denied (same type)\n", hdr->cid, dest->cid);
}
}
}
else
{
printf("Invoke %s\n", _opcodes[hdr->opcode]->opcode_string);
if (_opcodes[hdr->opcode]->cb)
return _opcodes[hdr->opcode]->cb(src,
(char *)buffer + sizeof(Eina_Debug_Packet_Header), size - sizeof(Eina_Debug_Packet_Header));
}
return EINA_TRUE;
}
EINA_SAFETY_ON_NULL_RETURN(c);
c->client = efl_ref(event->info);
clients = eina_list_append(clients, c);
efl_event_callback_array_add(c->client, _client_cbs(), c);
INF("server %p new client %p (%p)", event->object, c, c->client);
static int
_opcode_register(const char *op_name, int op_id, Opcode_Cb cb)
{
static int free_opcode = 0;
Opcode_Information *op_info = eina_hash_find(_string_to_opcode_hash, op_name);
if (!op_info)
{
op_info = calloc(1, sizeof(*op_info));
if (op_id == EINA_DEBUG_OPCODE_INVALID)
{
do
{
free_opcode = (free_opcode + 1) % MAX_OPCODES;
op_id = free_opcode;
}
while(_opcodes[op_id]);
}
op_info->opcode = op_id;
op_info->opcode_string = eina_stringshare_add(op_name);
op_info->cb = cb;
eina_hash_add(_string_to_opcode_hash, op_name, op_info);
_opcodes[op_id] = op_info;
}
printf("Register %s -> opcode %d\n", op_name, op_info->opcode);
return op_info->opcode;
}
static Eina_Bool
_hello_cb(Client *c, void *buffer, int size)
{
Eina_List *itr;
char *buf = (char *)buffer, *tmp;
EXTRACT(buf, &c->version, 4);
EXTRACT(buf, &c->pid, 4);
size -= 8;
c->cid = _free_cid++;
if (size > 1)
{
c->app_name = eina_stringshare_add_length(buf, size);
}
printf("Connection from %s: pid %d - name %s\n",
c->is_master ? "Master" : "Slave",
c->pid, c->app_name);
if (c->is_master) return EINA_TRUE;
/* Update the observers */
size = 2 * sizeof(int) + (c->app_name ? strlen(c->app_name) : 0) + 1; /* cid + pid + name + \0 */
buf = alloca(size);
tmp = buf;
STORE(tmp, &c->cid, sizeof(int));
STORE(tmp, &c->pid, sizeof(int));
if (c->app_name)
{
STORE(tmp, c->app_name, strlen(c->app_name) + 1);
}
else
{
char end = '\0';
STORE(tmp, &end, 1);
}
EINA_LIST_FOREACH(_clients, itr, c)
{
if (c->cl_stat_obs) _send(c, _slave_added_opcode, buf, size);
}
return EINA_TRUE;
}
static Eina_Bool
_cid_get_cb(Client *src, void *buffer, int size EINA_UNUSED)
{
int pid = *(int *)buffer;
Client *c = _client_find_by_pid(pid);
int cid = c ? c->cid : 0;
_send(src, _cid_from_pid_opcode, &cid, sizeof(int));
return EINA_TRUE;
}
static Eina_Bool
_data_test_cb(Client *src, void *buffer, int size)
{
printf("Data test: loop packet of %d bytes\n", size);
_send(src, _test_loop_opcode, buffer, size);
return EINA_TRUE;
}
static Eina_Bool
_cl_stat_obs_register_cb(Client *src, void *buffer, int size)
{
Client *c;
if (!src) return EINA_FALSE;
if (!src->is_master) return EINA_FALSE;
if (!src->cl_stat_obs)
{
Eina_List *itr;
src->cl_stat_obs = EINA_TRUE;
size = 0;
EINA_LIST_FOREACH(_clients, itr, c)
{
char *tmp;
if (c->is_master) continue;
size = 2 * sizeof(int) + (c->app_name ? strlen(c->app_name) : 0) + 1;
buffer = alloca(size);
tmp = buffer;
STORE(tmp, &c->cid, sizeof(int));
STORE(tmp, &c->pid, sizeof(int));
if (c->app_name)
{
STORE(tmp, c->app_name, strlen(c->app_name) + 1);
}
else
{
char end = '\0';
STORE(tmp, &end, 1);
}
_send(src, _slave_added_opcode, buffer, size);
}
}
return EINA_TRUE;
}
static Eina_Bool
_opcode_register_cb(Client *src, void *buffer, int size)
{
char *buf = (char *)buffer;
char *ops_buf = buf;
int ops_size = size;
ops_buf += sizeof(uint64_t);
ops_size -= sizeof(uint64_t);
int *opcodes = (int *)ops_buf;
while (ops_size > 0)
{
int len = strlen(ops_buf) + 1;
*opcodes++ = _opcode_register(ops_buf, EINA_DEBUG_OPCODE_INVALID, NULL);
ops_buf += len;
ops_size -= len;
}
_send(src, EINA_DEBUG_OPCODE_REGISTER, buf, (char *)opcodes - (char *)buf);
return EINA_TRUE;
}
static int
_data_receive(Client *c, unsigned char **buffer)
{
unsigned char *recv_buf = NULL;
int rret;
int size = 0;
if (!c) return -1;
rret = recv(c->fd, &size, sizeof(int), MSG_PEEK);
if (rret == sizeof(int))
{
int cur_packet_size = 0;
// allocate a buffer for the next bytes to receive
recv_buf = malloc(size);
if (!recv_buf) goto error;
while (cur_packet_size < size)
{
rret = recv(c->fd, recv_buf + cur_packet_size, size - cur_packet_size, 0);
if (rret <= 0) goto error;
cur_packet_size += rret;
}
}
if (buffer) *buffer = recv_buf;
//printf("%d bytes received from client %s fd %d\n", size, c->app_name, c->fd);
return size;
error:
if (rret == -1) perror("Read from socket");
if (recv_buf) free(recv_buf);
return -1;
}
static void
_error(void *data EINA_UNUSED, const Efl_Event *event)
_monitor()
{
Eina_Error *perr = event->info;
ERR("server %p error: %s", event->object, eina_error_msg_get(*perr));
fprintf(stderr, "ERROR: %s\n", eina_error_msg_get(*perr));
ecore_main_loop_quit();
retval = EXIT_FAILURE;
#ifndef _WIN32
#define MAX_EVENTS 1000
int ret = 0;
struct epoll_event events[MAX_EVENTS];
Client *c;
// sit forever processing commands or timeouts
for (; ret != -1;)
{
ret = epoll_wait (_epfd, events, MAX_EVENTS, -1);
// if the fd for debug daemon says it's alive, process it
if (ret > 0)
{
int i;
//check which fd are set/ready for read
for (i = 0; i < ret; i++)
{
if (events[i].events & EPOLLHUP)
{
c = _client_find_by_fd(events[i].data.fd);
close(events[i].data.fd);
if (c) _client_del(c);
}
if (events[i].events & EPOLLIN)
{
// Someone wants to connect
if(events[i].data.fd == _listening_master_fd || events[i].data.fd == _listening_slave_fd)
{
int new_fd = accept(events[i].data.fd, NULL, NULL);
if (new_fd < 0) perror("Accept");
else
{
struct epoll_event event;
c = calloc(1, sizeof(*c));
c->fd = new_fd;
c->is_master = (events[i].data.fd == _listening_master_fd);
_clients = eina_list_append(_clients, c);
event.data.fd = new_fd;
event.events = EPOLLIN;
epoll_ctl (_epfd, EPOLL_CTL_ADD, new_fd, &event);
}
continue;
}
c = _client_find_by_fd(events[i].data.fd);
if (c)
{
int size;
unsigned char *buffer;
size = _data_receive(c, &buffer);
// if not negative - we have a real message
if (size > 0)
{
if(!_dispatch(c, buffer, size))
{
// something we don't understand
fprintf(stderr, "Dispatch: unknown command");
}
free(buffer);
}
else
{
// major failure on debug daemon control fd - get out of here.
// else goto fail;
close(events[i].data.fd);
//TODO if its not main session we will tell the main_loop
//that it disconneted
}
}
}
}
}
#if 0
else
{
if (poll_time && poll_timer_cb)
{
if (!poll_timer_cb()) poll_time = 0;
}
}
#endif
}
#endif
}
static const char *
_socket_home_get()
{
// get possible debug daemon socket directory base
const char *dir = getenv("XDG_RUNTIME_DIR");
if (!dir) dir = eina_environment_home_get();
if (!dir) dir = eina_environment_tmp_get();
return dir;
}
#ifndef _WIN32
#define LENGTH_OF_SOCKADDR_UN(s) \
(strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
static int
_local_listening_socket_create(const char *path)
{
struct sockaddr_un socket_unix;
int socket_unix_len, curstate = 0;
// create the socket
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) goto err;
// set the socket to close when we exec things so they don't inherit it
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto err;
// set up some socket options on addr re-use
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate,
sizeof(curstate)) < 0)
goto err;
// sa that it's a unix socket and where the path is
socket_unix.sun_family = AF_UNIX;
strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path) - 1);
socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
unlink(socket_unix.sun_path);
if (bind(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
{
perror("ERROR on binding");
goto err;
}
listen(fd, 5);
return fd;
err:
if (fd >= 0) close(fd);
return -1;
}
#endif
static Eina_Bool
_server_launch()
{
#ifndef _WIN32
char buf[4096];
struct epoll_event event = {0};
mode_t mask = 0;
const char *socket_home_path = _socket_home_get();
char *socket_path = NULL;
if (!socket_home_path) return EINA_FALSE;
_epfd = epoll_create (MAX_EVENTS);
socket_path = strdup(socket_home_path);
snprintf(buf, sizeof(buf), "%s/%s", socket_path, SERVER_PATH);
if (mkdir(buf, S_IRWXU) < 0 && errno != EEXIST)
{
perror("mkdir SERVER_PATH");
goto err;
}
snprintf(buf, sizeof(buf), "%s/%s/%s", socket_path, SERVER_PATH, SERVER_NAME);
if (mkdir(buf, S_IRWXU) < 0 && errno != EEXIST)
{
perror("mkdir SERVER_NAME");
goto err;
}
mask = umask(S_IRWXG | S_IRWXO);
snprintf(buf, sizeof(buf), "%s/%s/%s/%i", socket_path, SERVER_PATH, SERVER_NAME, SERVER_MASTER_PORT);
_listening_master_fd = _local_listening_socket_create(buf);
if (_listening_master_fd <= 0) goto err;
event.data.fd = _listening_master_fd;
event.events = EPOLLIN;
epoll_ctl (_epfd, EPOLL_CTL_ADD, _listening_master_fd, &event);
snprintf(buf, sizeof(buf), "%s/%s/%s/%i", socket_path, SERVER_PATH, SERVER_NAME, SERVER_SLAVE_PORT);
_listening_slave_fd = _local_listening_socket_create(buf);
if (_listening_slave_fd <= 0) goto err;
event.data.fd = _listening_slave_fd;
event.events = EPOLLIN;
epoll_ctl (_epfd, EPOLL_CTL_ADD, _listening_slave_fd, &event);
umask(mask);
return EINA_TRUE;
err:
if (mask) umask(mask);
if (_listening_master_fd >= 0) close(_listening_master_fd);
_listening_master_fd = -1;
if (_listening_slave_fd >= 0) close(_listening_slave_fd);
_listening_slave_fd = -1;
free(socket_path);
#endif
return EINA_FALSE;
}
int
main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
{
Eo *loop;
char *path;
Eina_Error err;
ecore_app_no_system_modules();
eina_debug_disable();
eina_init();
ecore_init();
ecore_con_init();
_log_dom = eina_log_domain_register("efl_debugd", EINA_COLOR_CYAN);
_string_to_opcode_hash = eina_hash_string_superfast_new(NULL);
_opcode_register("daemon/opcode/register", EINA_DEBUG_OPCODE_REGISTER, _opcode_register_cb);
_opcode_register("daemon/greet", EINA_DEBUG_OPCODE_HELLO, _hello_cb);
_clients_stat_register_opcode = _opcode_register("daemon/observer/client/register", EINA_DEBUG_OPCODE_INVALID, _cl_stat_obs_register_cb);
_slave_added_opcode = _opcode_register("daemon/observer/slave_added", EINA_DEBUG_OPCODE_INVALID, NULL);
_slave_deleted_opcode = _opcode_register("daemon/observer/slave_deleted", EINA_DEBUG_OPCODE_INVALID, NULL);
_cid_from_pid_opcode = _opcode_register("daemon/info/cid_from_pid", EINA_DEBUG_OPCODE_INVALID, _cid_get_cb);
_test_loop_opcode = _opcode_register("daemon/test/loop", EINA_DEBUG_OPCODE_INVALID, _data_test_cb);
path = ecore_con_local_path_new(EINA_FALSE, "efl_debug", 0);
if (!path)
{
fprintf(stderr, "ERROR: could not get local communication path\n");
retval = EXIT_FAILURE;
goto end;
}
_server_launch();
_monitor();
loop = ecore_main_loop_get();
#ifdef EFL_NET_SERVER_UNIX_CLASS
server = efl_add(EFL_NET_SERVER_SIMPLE_CLASS, loop,
efl_net_server_simple_inner_class_set(efl_added, EFL_NET_SERVER_UNIX_CLASS));
#else
/* TODO: maybe start a TCP using locahost:12345?
* Right now eina_debug_monitor is only for AF_UNIX, so not an issue.
*/
fprintf(stderr, "ERROR: your platform doesn't support Efl.Net.Server.Unix\n");
#endif
if (!server)
{
fprintf(stderr, "ERROR: could not create communication server\n");
retval = EXIT_FAILURE;
goto end;
}
efl_event_callback_add(server, EFL_NET_SERVER_EVENT_CLIENT_ADD, _client_add, NULL);
efl_event_callback_add(server, EFL_NET_SERVER_EVENT_ERROR, _error, NULL);
#ifdef EFL_NET_SERVER_UNIX_CLASS
{
Eo *inner_server = efl_net_server_simple_inner_server_get(server);
efl_net_server_unix_unlink_before_bind_set(inner_server, EINA_TRUE);
efl_net_server_unix_leading_directories_create_set(inner_server, EINA_TRUE, 0700);
}
#endif
err = efl_net_server_serve(server, path);
if (err)
{
fprintf(stderr, "ERROR: could not serve '%s': %s\n", path, eina_error_msg_get(err));
retval = EXIT_FAILURE;
goto end;
}
ecore_main_loop_begin();
end:
efl_del(server);
free(path);
ecore_con_shutdown();
ecore_shutdown();
eina_shutdown();
return retval;
return 0;
}

View File

@ -272,6 +272,7 @@ extern "C" {
#include <eina_slice.h>
#include <eina_freeq.h>
#include <eina_slstr.h>
#include <eina_debug.h>
#undef EAPI
#define EAPI

View File

@ -16,10 +16,44 @@
* if not, see <http://www.gnu.org/licenses/>.
*/
# ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
# endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include "eina_debug.h"
#include "eina_types.h"
#include "eina_list.h"
#include "eina_mempool.h"
#include "eina_util.h"
#include "eina_evlog.h"
#include "eina_hash.h"
#include "eina_debug_private.h"
#ifdef EINA_HAVE_DEBUG
#ifdef __CYGWIN__
# define LIBEXT ".dll"
#else
# define LIBEXT ".so"
#endif
#define SIG SIGPROF
// yes - a global debug spinlock. i expect contention to be low for now, and
// when needed we can split this up into mroe locks to reduce contention when
@ -28,6 +62,789 @@ Eina_Spinlock _eina_debug_lock;
// only init once
static Eina_Bool _inited = EINA_FALSE;
static char *_my_app_name = NULL;
extern Eina_Bool eina_module_init(void);
extern Eina_Bool eina_mempool_init(void);
extern Eina_Bool eina_list_init(void);
extern Eina_Spinlock _eina_debug_thread_lock;
static Eina_Bool _debug_disabled = EINA_FALSE;
/* Local session */
/* __thread here to allow debuggers to be master and slave by using two different threads */
static __thread Eina_Debug_Session *_session = NULL;
static Eina_Debug_Session *_last_local_session = NULL;
/* Opcode used to load a module
* needed by the daemon to notify loading success */
static int _module_init_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _module_shutdown_opcode = EINA_DEBUG_OPCODE_INVALID;
static Eina_Hash *_modules_hash = NULL;
static unsigned int _poll_time = 0;
static Eina_Debug_Timer_Cb _poll_timer_cb = NULL;
static void *_poll_timer_data = NULL;
static Eina_Semaphore _thread_cmd_ready_sem;
typedef struct
{
int magic; /* Used to certify the validity of the struct */
const Eina_Debug_Opcode *ops;
Eina_Debug_Opcode_Status_Cb status_cb;
} _opcode_reply_info;
struct _Eina_Debug_Session
{
Eina_List **cbs; /* Table of callbacks lists indexed by opcode id */
Eina_List *opcode_reply_infos;
Eina_Debug_Dispatch_Cb dispatch_cb; /* Session dispatcher */
int cbs_length; /* cbs table size */
int fd_in; /* File descriptor to read */
int fd_out; /* File descriptor to write */
};
static void _opcodes_register_all();
static void _thread_start(Eina_Debug_Session *session);
EAPI int
eina_debug_session_send_to_thread(Eina_Debug_Session *session, int dest_id, int thread_id, int op, void *data, int size)
{
Eina_Debug_Packet_Header hdr;
if (!session) return -1;
if (op == EINA_DEBUG_OPCODE_INVALID) return -1;
/* Preparation of the packet header */
hdr.size = size + sizeof(Eina_Debug_Packet_Header);
hdr.opcode = op;
hdr.cid = dest_id;
hdr.thread_id = thread_id;
#ifndef _WIN32
e_debug("socket: %d / opcode %X / packet size %ld / bytes to send: %d",
session->fd_out, op, hdr->size + sizeof(int), total_size);
eina_spinlock_take(&_eina_debug_lock);
/* Sending header */
write(session->fd_out, &hdr, sizeof(hdr));
/* Sending payload */
if (size) write(session->fd_out, data, size);
eina_spinlock_release(&_eina_debug_lock);
#endif
return hdr.size;
}
EAPI int
eina_debug_session_send(Eina_Debug_Session *session, int dest, int op, void *data, int size)
{
return eina_debug_session_send_to_thread(session, dest, 0, op, data, size);
}
static void
_daemon_greet(Eina_Debug_Session *session)
{
/* say hello to our debug daemon - tell them our PID and protocol
version we speak */
/* Version + Pid + App name */
int size = 8 + (_my_app_name ? strlen(_my_app_name) : 0) + 1;
unsigned char *buf = alloca(size);
int version = 1; // version of protocol we speak
int pid = getpid();
memcpy(buf + 0, &version, 4);
memcpy(buf + 4, &pid, 4);
if (_my_app_name)
memcpy(buf + 8, _my_app_name, strlen(_my_app_name) + 1);
else
buf[8] = '\0';
eina_debug_session_send(session, 0, EINA_DEBUG_OPCODE_HELLO, buf, size);
}
#ifndef _WIN32
static int
_packet_receive(unsigned char **buffer)
{
unsigned char *packet_buf = NULL;
int rret = -1;
int size = 0;
if (!_session) goto end;
if (read(_session->fd_in, &size, 4) == 4)
{
// allocate a buffer for the next bytes to receive
packet_buf = malloc(size);
if (packet_buf)
{
int cur_packet_size = 4;
memcpy(packet_buf, &size, sizeof(int));
/* Receive all the remaining packet bytes */
while (cur_packet_size < size)
{
rret = read(_session->fd_in, packet_buf + cur_packet_size, size - cur_packet_size);
if (rret <= 0) goto end;
cur_packet_size += rret;
}
*buffer = packet_buf;
rret = cur_packet_size;
}
else
{
// we couldn't allocate memory for payloa buffer
// internal memory limit error
e_debug("Cannot allocate %u bytes for op", (unsigned int)size);
goto end;
}
}
else
{
e_debug("Invalid size read %i != %i", rret, size_sz);
goto end;
}
end:
if (rret <= 0 && packet_buf) free(packet_buf);
return rret;
}
#endif
EAPI void
eina_debug_disable()
{
_debug_disabled = EINA_TRUE;
}
EAPI void
eina_debug_session_terminate(Eina_Debug_Session *session)
{
/* FIXME: Maybe just close fd here so the thread terminates its own session by itself */
if (!session) return;
_opcode_reply_info *info = NULL;
EINA_LIST_FREE(session->opcode_reply_infos, info) free(info);
free(session->cbs);
free(session);
}
EAPI void
eina_debug_session_dispatch_override(Eina_Debug_Session *session, Eina_Debug_Dispatch_Cb disp_cb)
{
if (!session) return;
if (!disp_cb) disp_cb = eina_debug_dispatch;
session->dispatch_cb = disp_cb;
}
typedef struct {
Eina_Module *handle;
Eina_Bool (*init)(void);
Eina_Bool (*shutdown)(void);
int ref;
} _module_info;
#define _LOAD_SYMBOL(cls_struct, pkg, sym) \
do \
{ \
char func_name[1024]; \
snprintf(func_name, sizeof(func_name), "%s_debug_" #sym, pkg); \
(cls_struct)->sym = eina_module_symbol_get((cls_struct)->handle, func_name); \
if (!(cls_struct)->sym) \
{ \
e_debug("Failed loading symbol '%s' from the library.", func_name); \
eina_module_free((cls_struct)->handle); \
(cls_struct)->handle = NULL; \
free((cls_struct)); \
return EINA_FALSE; \
} \
} \
while (0)
static Eina_Debug_Error
_module_init_cb(Eina_Debug_Session *session, int cid, void *buffer, int size)
{
char module_path[1024];
_module_info *minfo = NULL;
const char *module_name = buffer;
char *resp;
if (size <= 0) return EINA_DEBUG_ERROR;
if (!_modules_hash) _modules_hash = eina_hash_string_small_new(NULL);
minfo = eina_hash_find(_modules_hash, module_name);
if (minfo && minfo->ref)
{
minfo->ref++;
goto end;
}
e_debug("Init module %s", module_name);
if (!minfo)
{
snprintf(module_path, sizeof(module_path), PACKAGE_LIB_DIR "/lib%s_debug"LIBEXT, module_name);
minfo = calloc(1, sizeof(*minfo));
eina_hash_add(_modules_hash, module_name, minfo);
}
if (!minfo->handle) minfo->handle = eina_module_new(module_path);
if (!minfo->handle || !eina_module_load(minfo->handle))
{
e_debug("Failed loading debug module %s.", module_name);
if (minfo->handle) eina_module_free(minfo->handle);
minfo->handle = NULL;
goto end;
}
if (!minfo->init) _LOAD_SYMBOL(minfo, module_name, init);
if (!minfo->shutdown) _LOAD_SYMBOL(minfo, module_name, shutdown);
if (minfo->init()) minfo->ref = 1;
end:
resp = alloca(size + 1);
memcpy(resp, buffer, size);
resp[size] = !!(minfo->ref);
eina_debug_session_send(session, cid, _module_init_opcode, resp, size+1);
return EINA_DEBUG_OK;
}
static Eina_Debug_Error
_module_shutdown_cb(Eina_Debug_Session *session, int cid, void *buffer, int size)
{
_module_info *minfo = NULL;
const char *module_name = buffer;
char *resp;
Eina_Bool ret = EINA_TRUE;
if (size <= 0 || !_modules_hash) return EINA_DEBUG_ERROR;
minfo = eina_hash_find(_modules_hash, module_name);
if (minfo)
{
if (!--(minfo->ref))
{
eina_hash_del(_modules_hash, module_name, minfo);
if (minfo->shutdown) ret = minfo->shutdown();
if (minfo->handle) eina_module_free(minfo->handle);
minfo->handle = NULL;
free(minfo);
}
}
resp = alloca(size + 1);
memcpy(resp, buffer, size);
resp[size] = !!ret;
eina_debug_session_send(session, cid, _module_shutdown_opcode, resp, size+1);
return EINA_DEBUG_OK;
}
static const Eina_Debug_Opcode _EINA_DEBUG_MONITOR_OPS[] = {
{"module/init", &_module_init_opcode, &_module_init_cb},
{"module/shutdown", &_module_shutdown_opcode, &_module_shutdown_cb},
{NULL, NULL, NULL}
};
static void
_static_opcode_register(Eina_Debug_Session *session,
int op_id, Eina_Debug_Cb cb)
{
if(session->cbs_length < op_id + 1)
{
int i = session->cbs_length;
session->cbs_length = op_id + 16;
session->cbs = realloc(session->cbs, session->cbs_length * sizeof(Eina_List *));
for(; i < session->cbs_length; i++) session->cbs[i] = NULL;
}
if (cb)
{
session->cbs[op_id] = eina_list_append(session->cbs[op_id], cb);
}
}
/*
* Response of the daemon containing the ids of the requested opcodes.
* PTR64 + (opcode id)*
*/
static Eina_Debug_Error
_callbacks_register_cb(Eina_Debug_Session *session, int src_id EINA_UNUSED, void *buffer, int size)
{
_opcode_reply_info *info = NULL, *info2;
Eina_List *itr;
int *os;
unsigned int count, i;
uint64_t info_64;
memcpy(&info_64, buffer, sizeof(uint64_t));
info = (_opcode_reply_info *)info_64;
if (!info) return EINA_DEBUG_ERROR;
EINA_LIST_FOREACH(session->opcode_reply_infos, itr, info2)
{
if (info2 == info)
{
os = (int *)((unsigned char *)buffer + sizeof(uint64_t));
count = (size - sizeof(uint64_t)) / sizeof(int);
for (i = 0; i < count; i++)
{
if (info->ops[i].opcode_id) *(info->ops[i].opcode_id) = os[i];
_static_opcode_register(session, os[i], info->ops[i].cb);
e_debug("Opcode %s -> %d", info->ops[i].opcode_name, os[i]);
}
if (info->status_cb) info->status_cb(EINA_TRUE);
return EINA_DEBUG_OK;
}
}
return EINA_DEBUG_ERROR;
}
static void
_opcodes_registration_send(Eina_Debug_Session *session,
_opcode_reply_info *info)
{
unsigned char *buf;
int count = 0;
int size = sizeof(uint64_t);
while(info->ops[count].opcode_name)
{
size += strlen(info->ops[count].opcode_name) + 1;
count++;
}
buf = alloca(size);
uint64_t info_64 = (uint64_t)info;
memcpy(buf, &info_64, sizeof(uint64_t));
int size_curr = sizeof(uint64_t);
count = 0;
while(info->ops[count].opcode_name)
{
int len = strlen(info->ops[count].opcode_name) + 1;
memcpy(buf + size_curr, info->ops[count].opcode_name, len);
size_curr += len;
count++;
}
eina_debug_session_send(session, 0, EINA_DEBUG_OPCODE_REGISTER, buf, size);
}
static void
_opcodes_register_all(Eina_Debug_Session *session)
{
Eina_List *l;
_opcode_reply_info *info = NULL;
_static_opcode_register(session,
EINA_DEBUG_OPCODE_REGISTER, _callbacks_register_cb);
EINA_LIST_FOREACH(session->opcode_reply_infos, l, info)
_opcodes_registration_send(session, info);;
}
static void
_opcodes_unregister_all(Eina_Debug_Session *session)
{
Eina_List *l;
int i;
_opcode_reply_info *info = NULL;
if (!session) return;
session->cbs_length = 0;
for (i = 0; i < session->cbs_length; i++)
eina_list_free(session->cbs[i]);
free(session->cbs);
session->cbs = NULL;
EINA_LIST_FOREACH(session->opcode_reply_infos, l, info)
{
const Eina_Debug_Opcode *op = info->ops;
while(!op->opcode_name)
{
if (op->opcode_id) *(op->opcode_id) = EINA_DEBUG_OPCODE_INVALID;
op++;
}
if (info->status_cb) info->status_cb(EINA_FALSE);
}
}
static const char *
_socket_home_get()
{
// get possible debug daemon socket directory base
const char *dir = getenv("XDG_RUNTIME_DIR");
if (!dir) dir = eina_environment_home_get();
if (!dir) dir = eina_environment_tmp_get();
return dir;
}
#ifndef _WIN32
#define LENGTH_OF_SOCKADDR_UN(s) \
(strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
#endif
EAPI Eina_Debug_Session *
eina_debug_local_connect(Eina_Bool is_master)
{
#ifndef _WIN32
char buf[4096];
int fd, socket_unix_len, curstate = 0;
struct sockaddr_un socket_unix;
#endif
Eina_Debug_Session *session = calloc(1, sizeof(*session));
session->dispatch_cb = eina_debug_dispatch;
session->fd_out = session->fd_in = -1;
// try this socket file - it will likely be:
// ~/.ecore/efl_debug/0
// or maybe
// /var/run/UID/.ecore/efl_debug/0
// either way a 4k buffer should be ebough ( if it's not we're on an
// insane system)
#ifndef _WIN32
snprintf(buf, sizeof(buf), "%s/%s/%s/%i", _socket_home_get(), SERVER_PATH, SERVER_NAME,
is_master ? SERVER_MASTER_PORT : SERVER_SLAVE_PORT);
// create the socket
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) goto err;
// set the socket to close when we exec things so they don't inherit it
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto err;
// set up some socket options on addr re-use
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate,
sizeof(curstate)) < 0)
goto err;
// sa that it's a unix socket and where the path is
socket_unix.sun_family = AF_UNIX;
strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path) - 1);
socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
// actually connect to efl_debugd service
if (connect(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
goto err;
// we succeeded
session->fd_out = session->fd_in = fd;
// start the monitor thread
_thread_start(session);
_daemon_greet(session);
_opcodes_register_all(session);
if (!is_master)
eina_debug_opcodes_register(session, _EINA_DEBUG_MONITOR_OPS, NULL);
_last_local_session = session;
return session;
err:
// some kind of connection failure here, so close a valid socket and
// get out of here
if (fd >= 0) close(fd);
if (session) free(session);
#else
(void) _session;
(void) type;
#endif
return NULL;
}
EAPI Eina_Bool
eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data)
{
_poll_time = timeout_ms;
_poll_timer_cb = cb;
_poll_timer_data = data;
return EINA_TRUE;
}
// this is a DEDICATED debug thread to monitor the application so it works
// even if the mainloop is blocked or the app otherwise deadlocked in some
// way. this is an alternative to using external debuggers so we can get
// users or developers to get useful information about an app at all times
static void *
_monitor(void *_data)
{
#ifndef _WIN32
#define MAX_EVENTS 4
int ret;
struct epoll_event event;
struct epoll_event events[MAX_EVENTS];
int epfd = epoll_create(MAX_EVENTS);
_session = _data;
event.data.fd = _session->fd_in;
event.events = EPOLLIN;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, _session->fd_in, &event);
if (ret) perror("epoll_ctl/add");
// set a name for this thread for system debugging
#ifdef EINA_HAVE_PTHREAD_SETNAME
# ifndef __linux__
pthread_set_name_np
# else
pthread_setname_np
# endif
(pthread_self(), "Edbg-mon");
#endif
// sit forever processing commands or timeouts in the debug monitor
// thread - this is separate to the rest of the app so it shouldn't
// impact the application specifically
for (;_session;)
{
// if we are in a polling mode then set up a timeout and wait for it
int timeout = _poll_time ? (int)_poll_time : -1; //in milliseconds
ret = epoll_wait(epfd, events, MAX_EVENTS, timeout);
// if the fd for debug daemon says it's alive, process it
if (ret)
{
int i;
//check which fd are set/ready for read
for (i = 0; i < ret; i++)
{
if (events[i].events & EPOLLHUP)
{
_opcodes_unregister_all(_session);
free(_session);
_session = NULL;
}
else if (events[i].events & EPOLLIN)
{
int size;
unsigned char *buffer;
size = _packet_receive(&buffer);
// if not negative - we have a real message
if (size > 0)
{
if(!_session->dispatch_cb(_session, buffer))
{
// something we don't understand
e_debug("EINA DEBUG ERROR: Unknown command");
}
}
else if (size == 0)
{
// May be due to a response from a script line
}
else
{
// major failure on debug daemon control fd - get out of here.
// else goto fail;
close(_session->fd_in);
//TODO if its not main _session we will tell the main_loop
//that it disconneted
}
}
}
}
else
{
if (_poll_time && _poll_timer_cb)
{
if (!_poll_timer_cb(_poll_timer_data)) _poll_time = 0;
}
}
}
#endif
return NULL;
}
// start up the debug monitor if we haven't already
static void
_thread_start(Eina_Debug_Session *session)
{
pthread_t monitor_thread;
int err;
sigset_t oldset, newset;
sigemptyset(&newset);
sigaddset(&newset, SIGPIPE);
sigaddset(&newset, SIGALRM);
sigaddset(&newset, SIGCHLD);
sigaddset(&newset, SIGUSR1);
sigaddset(&newset, SIGUSR2);
sigaddset(&newset, SIGHUP);
sigaddset(&newset, SIGQUIT);
sigaddset(&newset, SIGINT);
sigaddset(&newset, SIGTERM);
#ifdef SIGPWR
sigaddset(&newset, SIGPWR);
#endif
sigprocmask(SIG_BLOCK, &newset, &oldset);
err = pthread_create(&monitor_thread, NULL, _monitor, session);
sigprocmask(SIG_SETMASK, &oldset, NULL);
if (err != 0)
{
e_debug("EINA DEBUG ERROR: Can't create monitor debug thread!");
abort();
}
}
/*
* Sends to daemon:
* - Pointer to ops: returned in the response to determine which opcodes have been added
* - List of opcode names seperated by \0
*/
EAPI void
eina_debug_opcodes_register(Eina_Debug_Session *session, const Eina_Debug_Opcode ops[],
Eina_Debug_Opcode_Status_Cb status_cb)
{
if (!session) session = _last_local_session;
if (!session) return;
_opcode_reply_info *info = malloc(sizeof(*info));
info->ops = ops;
info->status_cb = status_cb;
session->opcode_reply_infos = eina_list_append(
session->opcode_reply_infos, info);
//send only if _session's fd connected, if not - it will be sent when connected
if(session && session->fd_in != -1)
_opcodes_registration_send(session, info);
}
static Eina_Debug_Error
_self_dispatch(Eina_Debug_Session *session, void *buffer)
{
Eina_Debug_Packet_Header *hdr = buffer;
Eina_List *itr;
int opcode = hdr->opcode;
Eina_Debug_Cb cb = NULL;
if (opcode >= session->cbs_length)
{
e_debug("Invalid opcode %d", opcode);
return EINA_DEBUG_ERROR;
}
EINA_LIST_FOREACH(session->cbs[opcode], itr, cb)
{
if (!cb) continue;
Eina_Debug_Error ret = cb(session, hdr->cid,
(unsigned char *)buffer + sizeof(*hdr),
hdr->size - sizeof(*hdr));
if (ret == EINA_DEBUG_ERROR) return ret;
}
return EINA_DEBUG_OK;
}
EAPI Eina_Debug_Error
eina_debug_dispatch(Eina_Debug_Session *session, void *buffer)
{
Eina_Debug_Packet_Header *hdr = buffer;
Eina_Debug_Error ret = EINA_DEBUG_OK;
if (hdr->thread_id == 0)
{
ret = _self_dispatch(session, buffer);
free(buffer);
return ret;
}
else
{
int i, nb_calls = 0;
eina_spinlock_take(&_eina_debug_thread_lock);
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
_eina_debug_thread_active[i].cmd_buffer = NULL;
if (hdr->thread_id == (int)0xFFFFFFFF ||
hdr->thread_id == _eina_debug_thread_active[i].thread_id)
{
_eina_debug_thread_active[i].cmd_session = session;
_eina_debug_thread_active[i].cmd_buffer = buffer;
_eina_debug_thread_active[i].cmd_result = EINA_DEBUG_OK;
pthread_kill(_eina_debug_thread_active[i].thread, SIG);
nb_calls++;
}
}
eina_spinlock_release(&_eina_debug_thread_lock);
while (nb_calls)
{
while (nb_calls)
{
eina_semaphore_lock(&_thread_cmd_ready_sem);
nb_calls--;
}
eina_spinlock_take(&_eina_debug_thread_lock);
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
if (_eina_debug_thread_active[i].cmd_buffer)
{
switch (_eina_debug_thread_active[i].cmd_result)
{
case EINA_DEBUG_OK:
{
_eina_debug_thread_active[i].cmd_buffer = NULL;
break;
}
case EINA_DEBUG_ERROR:
{
_eina_debug_thread_active[i].cmd_buffer = NULL;
ret = EINA_DEBUG_ERROR;
break;
}
case EINA_DEBUG_AGAIN:
{
pthread_kill(_eina_debug_thread_active[i].thread, SIG);
nb_calls++;
break;
}
default: break;
}
}
}
eina_spinlock_release(&_eina_debug_thread_lock);
}
free(buffer);
}
return ret;
}
static void
_signal_handler(int sig EINA_UNUSED,
siginfo_t *si EINA_UNUSED, void *foo EINA_UNUSED)
{
int i, slot = -1;
pthread_t self = pthread_self();
eina_spinlock_take(&_eina_debug_thread_lock);
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
if (self == _eina_debug_thread_active[i].thread)
{
slot = i;
break;
}
}
eina_spinlock_release(&_eina_debug_thread_lock);
if (slot != -1)
{
_eina_debug_thread_active[slot].cmd_result =
_self_dispatch(_eina_debug_thread_active[slot].cmd_session,
_eina_debug_thread_active[slot].cmd_buffer);
}
eina_semaphore_release(&_thread_cmd_ready_sem, 1);
}
#ifdef __linux__
extern char *__progname;
#endif
static void
_signal_init(void)
{
struct sigaction sa;
// set up signal handler for our profiling signal - eevery thread should
// obey this (this is the case on linux - other OSs may vary)
sa.sa_sigaction = _signal_handler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(SIG, &sa, NULL) != 0)
e_debug("EINA DEBUG ERROR: Can't set up sig %i handler!", SIG);
sa.sa_sigaction = NULL;
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGPIPE, &sa, 0) == -1) perror(0);
}
Eina_Bool
eina_debug_init(void)
@ -43,54 +860,46 @@ eina_debug_init(void)
}
// mark as initted
_inited = EINA_TRUE;
eina_module_init();
eina_mempool_init();
eina_list_init();
// For Windows support GetModuleFileName can be used
// set up thread things
eina_spinlock_new(&_eina_debug_lock);
eina_spinlock_new(&_eina_debug_thread_lock);
eina_semaphore_new(&_eina_debug_monitor_return_sem, 0);
self = pthread_self();
_eina_debug_thread_mainloop_set(&self);
_eina_debug_thread_add(&self);
# if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
// if we are setuid - don't debug!
if (getuid() != geteuid()) return EINA_TRUE;
#endif
// if someone uses the EFL_NODEBUG env var - do not do debugging. handy
// for when this debug code is buggy itself
if (getenv("EFL_NODEBUG")) return EINA_TRUE;
// connect to our debug daemon
_eina_debug_monitor_service_connect();
// if we connected - set up the debug monitor properly
if (_eina_debug_monitor_service_fd >= 0)
// if someone uses the EFL_NODEBUG env var or disabled debug - do not do
// debugging. handy for when this debug code is buggy itself
#ifdef __linux__
_my_app_name = __progname;
#endif
if (!getenv("EFL_NODEBUG") && !_debug_disabled)
{
// say hello to the debug daemon
_eina_debug_monitor_service_greet();
// set up our profile signal handler
_eina_debug_monitor_signal_init();
// start the monitor thread
_eina_debug_monitor_thread_start();
eina_debug_local_connect(EINA_FALSE);
}
eina_semaphore_new(&_thread_cmd_ready_sem, 0);
_signal_init();
_eina_debug_cpu_init();
_eina_debug_bt_init();
return EINA_TRUE;
}
Eina_Bool
eina_debug_shutdown(void)
{
_eina_debug_bt_shutdown();
_eina_debug_cpu_shutdown();
eina_semaphore_free(&_thread_cmd_ready_sem);
eina_spinlock_take(&_eina_debug_thread_lock);
// yes - we never free on shutdown - this is because the monitor thread
// never exits. this is not a leak - we intend to never free up any
// resources here because they are allocated once only ever.
return EINA_TRUE;
}
#else
Eina_Bool
eina_debug_init(void)
{
return EINA_TRUE;
}
Eina_Bool
eina_debug_shutdown(void)
{
return EINA_TRUE;
}
#endif

View File

@ -19,103 +19,236 @@
#ifndef EINA_DEBUG_H_
# define EINA_DEBUG_H_
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include "eina_config.h"
# include "eina_list.h"
# include <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>
/**
* @page eina_debug_main Eina Debug
*
* @date 2015 (created)
*/
# include "eina_config.h"
# include "eina_private.h"
# include "eina_inlist.h"
# include "eina_lock.h"
# include "eina_thread.h"
# include "eina_convert.h"
# include "eina_strbuf.h"
# include "eina_safety_checks.h"
# include "eina_log.h"
# include "eina_inline_private.h"
/**
* @addtogroup Eina_Debug
* @{
*/
# define EINA_HAVE_DEBUG 1
#define SERVER_PATH ".edebug"
#define SERVER_NAME "efl_debug"
#define SERVER_MASTER_PORT 0
#define SERVER_SLAVE_PORT 1
# define EINA_MAX_BT 256
typedef struct _Eina_Debug_Thread Eina_Debug_Thread;
struct _Eina_Debug_Thread
typedef enum
{
pthread_t thread;
#if defined(__clockid_t_defined)
struct timespec clok;
#endif
int val;
EINA_DEBUG_OK,
EINA_DEBUG_ERROR,
EINA_DEBUG_AGAIN
} Eina_Debug_Error;
enum
{
EINA_DEBUG_OPCODE_INVALID = -1, /**< Invalid opcode value */
EINA_DEBUG_OPCODE_REGISTER = 0, /**< Opcode used to register other opcodes */
EINA_DEBUG_OPCODE_HELLO = 1 /**< Opcode used to send greetings to the daemon */
};
extern Eina_Spinlock _eina_debug_lock;
extern Eina_Spinlock _eina_debug_thread_lock;
extern pthread_t _eina_debug_thread_mainloop;
extern Eina_Debug_Thread *_eina_debug_thread_active;
extern int _eina_debug_thread_active_num;
extern Eina_Semaphore _eina_debug_monitor_return_sem;
extern int _eina_debug_monitor_service_fd;
/**
* @typedef Eina_Debug_Session
*
* A handle used to interact with the debug daemon.
* It contains all the information related to this connection and needed
* to send/receive/dispatch/...
*/
typedef struct _Eina_Debug_Session Eina_Debug_Session;
void _eina_debug_thread_add(void *th);
void _eina_debug_thread_del(void *th);
void _eina_debug_thread_mainloop_set(void *th);
/**
* @typedef Eina_Debug_Cb
*
* A callback invoked when a specific packet is received.
*
* @param session the session
* @param srcid the source id
* @param buffer the packet payload data. It doesn't contain any transport information.
* @param size the packet payload size
*/
typedef Eina_Debug_Error (*Eina_Debug_Cb)(Eina_Debug_Session *session, int srcid, void *buffer, int size);
void *_eina_debug_chunk_push(int size);
void *_eina_debug_chunk_realloc(int size);
char *_eina_debug_chunk_strdup(const char *str);
void *_eina_debug_chunk_tmp_push(int size);
void _eina_debug_chunk_tmp_reset(void);
/**
* @typedef Eina_Debug_Opcode_Status_Cb
*
* When the opcodes ids are retrieved, this callback is invoked with a true
* status.
* When a disconnection to the daemon is happening, the opcodes ids are set
* as invalid and this callback is invoked with a false status. The upper
* layer should not try to send more requests until a new connection is
* established.
*
* @param status EINA_TRUE if opcodes have been received from the daemon, EINA_FALSE otherwise.
*/
typedef void (*Eina_Debug_Opcode_Status_Cb)(Eina_Bool status);
const char *_eina_debug_file_get(const char *fname);
/**
* @typedef Eina_Debug_Dispatch_Cb
*
* Dispatcher callback prototype used to override the default dispatcher of a
* session.
*
* @param session the session
* @param buffer the packet received
*
* The given packet is the entire data received, including the header.
*/
typedef Eina_Debug_Error (*Eina_Debug_Dispatch_Cb)(Eina_Debug_Session *session, void *buffer);
void _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen);
/**
* @typedef Eina_Debug_Timer_Cb
*
* A callback for a timer
*/
typedef Eina_Bool (*Eina_Debug_Timer_Cb)(void *);
void _eina_debug_monitor_thread_start(void);
void _eina_debug_monitor_signal_init(void);
void _eina_debug_monitor_service_connect(void);
/**
* @typedef Eina_Debug_Packet_Header
*
* Header of Eina Debug packet
*/
typedef struct
{
int size; /**< Packet size after this element */
/**<
* During sending, it corresponds to the id of the destination. During reception, it is the id of the source
* The daemon is in charge of swapping the id before forwarding the packet to the destination.
*/
int cid;
int thread_id;
int opcode; /**< Opcode of the packet */
} Eina_Debug_Packet_Header;
int _eina_debug_monitor_service_send(int fd, const char op[4],
unsigned char *data, int size);
void _eina_debug_monitor_service_greet(void);
int _eina_debug_monitor_service_read(char *op, unsigned char **data);
/**
* @typedef Eina_Debug_Opcode
*
* Structure to describe information for an opcode. It is used to register new
* opcodes.
*/
typedef struct
{
char *opcode_name; /**< Opcode string. On registration, the daemon uses it to calculate an opcode id */
int *opcode_id; /**< A pointer to store the opcode id received from the daemon */
Eina_Debug_Cb cb; /**< Callback to call when a packet corresponding to the opcode is received */
} Eina_Debug_Opcode;
# define EINA_BT(file) \
do { \
void *bt[EINA_MAX_BT]; \
int btlen = backtrace((void **)bt, EINA_MAX_BT); \
_eina_debug_dump_fhandle_bt(file, bt, btlen); \
} while (0)
# else
# define EINA_BT(file) do { } while (0)
# endif
/**
* @brief Disable debugging
*
* Useful for applications that don't want debugging. The debug daemon is one
* of them.
* Need to be invoked before eina_init. Otherwise it can't have any effect.
*/
EAPI void eina_debug_disable(void);
/**
* @brief Connect to the local daemon
*
* @param is_master true if the application is a debugger. EINA_FALSE otherwise.
*
* @return EINA_TRUE on success, EINA_FALSE otherwise.
*/
EAPI Eina_Debug_Session *eina_debug_local_connect(Eina_Bool is_master);
/**
* @brief Terminate the session
*
* @param session the session to terminate
*
*/
EAPI void eina_debug_session_terminate(Eina_Debug_Session *session);
/**
* @brief Override the dispatcher of a specific session
*
* For example, it can be used to forward a packet to the main thread and to
* use the default dispatcher there.
* All the packets received in this session will use this dispatcher.
*
* @param session the session
* @disp_cb the new dispatcher for the given session
*/
EAPI void eina_debug_session_dispatch_override(Eina_Debug_Session *session, Eina_Debug_Dispatch_Cb disp_cb);
/**
* @brief Dispatch a given packet according to its header.
*
* This function checks the header contained into the packet and invokes
* the correct callback according to the opcode.
* This is the default dispatcher.
*
* @param session the session
* @param buffer the packet
*
* @return EINA_DEBUG_OK on success, EINA_DEBUG_ERROR if the packet is not as expected.
*/
EAPI Eina_Debug_Error eina_debug_dispatch(Eina_Debug_Session *session, void *buffer);
/**
* @brief Register opcodes to a session
*
* This function registers opcodes for the given session. If the session is not
* connected, the request is not sent to the daemon. Otherwise, the request for
* the opcodes ids is sent.
* On the reception from the daemon, status_cb function is invoked to inform
* the requester that the opcodes can now be used.
*/
EAPI void eina_debug_opcodes_register(Eina_Debug_Session *session,
const Eina_Debug_Opcode ops[], Eina_Debug_Opcode_Status_Cb status_cb);
/**
* @brief Send a packet to the given destination
*
* The packet will be treated by the debug thread itself.
*
* @param session the session to use to send the packet
* @param dest_id the destination id to send the packet to
* @param op the opcode for this packet
* @param data payload to send
* @param size payload size
*
* @return the number of sent bytes
*/
EAPI int eina_debug_session_send(Eina_Debug_Session *session, int dest_id, int op, void *data, int size);
/**
* @brief Send a packet to the given thread of the given destination
*
* If the thread is 0x0, the packet will be treated by the debug thread itself.
* If the thread is 0xFF..FF, the packet will be broadcasted to all the threads.
* Otherwise, the packet will be treated by the specific thread.
*
* @param session the session to use to send the packet
* @param dest_id the destination id to send the packet to
* @param thread_id the thread to send the packet to.
* @param op the opcode for this packet
* @param data payload to send
* @param size payload size
*
* @return the number of sent bytes
*/
EAPI int eina_debug_session_send_to_thread(Eina_Debug_Session *session, int dest_id, int thread_id, int op, void *data, int size);
/**
* @brief Add a timer
*
* Needed for polling debug
*
* @param timeout_ms timeout in ms
* @param cb callback to call when the timeout is reached
* @param data user data
*
* @return EINA_TRUE on success, EINA_FALSE otherwise
*/
EAPI Eina_Bool eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data);
EAPI int eina_debug_thread_id_get(void);
#endif
/**
* @}
*/

View File

@ -16,9 +16,30 @@
* 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
# include <dlfcn.h>
#endif
#include <libunwind.h>
#include "eina_debug.h"
#include "eina_debug_private.h"
static Eina_Semaphore _wait_for_bts_sem;
// _bt_buf[0] is always for mainloop, 1 + is for extra threads
static void ***_bt_buf;
static int *_bt_buf_len;
static struct timespec *_bt_ts;
static int *_bt_cpu;
/* Used by trace timer */
static double _trace_t0 = 0.0;
static int _prof_get_op = EINA_DEBUG_OPCODE_INVALID;
void
_eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
@ -46,4 +67,220 @@ _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
else fprintf(f, "??\t -\n");
}
}
// a backtracer that uses libunwind to do the job
static inline int
_eina_debug_unwind_bt(void **bt, int max)
{
unw_cursor_t cursor;
unw_context_t uc;
unw_word_t p;
int total;
// create a context for unwinding
unw_getcontext(&uc);
// begin our work
unw_init_local(&cursor, &uc);
// walk up each stack frame until there is no more, storing it
for (total = 0; (unw_step(&cursor) > 0) && (total < max); total++)
{
unw_get_reg(&cursor, UNW_REG_IP, &p);
bt[total] = (void *)p;
}
// return our total backtrace stack size
return total;
}
// this signal handler is called inside each and every thread when the
// thread gets a signal via pthread_kill(). this causes the thread to
// stop here inside this handler and "do something" then when this returns
// resume whatever it was doing like any signal handler
static void
_eina_debug_signal(int sig EINA_UNUSED,
siginfo_t *si EINA_UNUSED,
void *foo EINA_UNUSED)
{
int i, slot = 0;
pthread_t self = pthread_self();
clockid_t cid;
// find which slot in the array of threads we have so we store info
// in the correct slot for us
if (self != _eina_debug_thread_mainloop)
{
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
if (self == _eina_debug_thread_active[i].thread)
{
slot = i + 1;
goto found;
}
}
// we couldn't find out thread reference! help!
e_debug("EINA DEBUG ERROR: can't find thread slot!");
eina_semaphore_release(&_wait_for_bts_sem, 1);
return;
}
found:
// store thread info like what cpu core we are on now (not reliable
// but hey - better than nothing), the amount of cpu time total
// we have consumed (it's cumulative so subtracing deltas can give
// you an average amount of cpu time consumed between now and the
// previous time we looked) and also a full backtrace
_bt_cpu[slot] = sched_getcpu();
pthread_getcpuclockid(self, &cid);
clock_gettime(cid, &(_bt_ts[slot]));
_bt_buf_len[slot] = _eina_debug_unwind_bt(_bt_buf[slot], EINA_MAX_BT);
// now wake up the monitor to let them know we are done collecting our
// backtrace info
eina_semaphore_release(&_wait_for_bts_sem, 1);
}
// a quick and dirty local time point getter func - not portable
static inline double
get_time(void)
{
#if defined(__clockid_t_defined)
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0);
#else
struct timeval timev;
gettimeofday(&timev, NULL);
return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
#endif
}
static void
_eina_debug_collect_bt(pthread_t pth EINA_UNUSED)
{
// this async signals the thread to switch to the deebug signal handler
// and collect a backtrace and other info from inside the thread
//pthread_kill(pth, SIG);
}
static Eina_Bool
_trace_cb(void *data)
{
static Eina_Debug_Packet_Header *hdr = NULL;
if (!hdr)
{
hdr = calloc(1, sizeof(*hdr));
hdr->size = sizeof(Eina_Debug_Packet_Header);
hdr->thread_id = 0xFFFFFFFF;
hdr->opcode = _prof_get_op;
}
eina_debug_dispatch(data, (void *)hdr);
return EINA_TRUE;
}
static Eina_Debug_Error
_prof_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
static int bts = 0;
int i;
if (!_trace_t0) _trace_t0 = get_time();
// take a lock on grabbing thread debug info like backtraces
eina_spinlock_take(&_eina_debug_thread_lock);
// reset our "stack" of memory se use to dump thread info into
_eina_debug_chunk_tmp_reset();
// get an array of pointers for the backtrace array for main + th
_bt_buf = _eina_debug_chunk_tmp_push
((1 + _eina_debug_thread_active_num) * sizeof(void *));
if (!_bt_buf) goto err;
// get an array of pointers for the timespec array for mainloop + th
_bt_ts = _eina_debug_chunk_tmp_push
((1 + _eina_debug_thread_active_num) * sizeof(struct timespec));
if (!_bt_ts) goto err;
// get an array of pointers for the cpuid array for mainloop + th
_bt_cpu = _eina_debug_chunk_tmp_push
((1 + _eina_debug_thread_active_num) * sizeof(int));
if (!_bt_cpu) goto err;
// now get an array of void pts for mainloop bt
_bt_buf[0] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *));
if (!_bt_buf[0]) goto err;
// get an array of void ptrs for each thread we know about for bt
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
_bt_buf[i + 1] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *));
if (!_bt_buf[i + 1]) goto err;
}
// get an array of ints to stor the bt len for mainloop + threads
_bt_buf_len = _eina_debug_chunk_tmp_push
((1 + _eina_debug_thread_active_num) * sizeof(int));
// collect bt from the mainloop - always there
_eina_debug_collect_bt(_eina_debug_thread_mainloop);
// now collect per thread
for (i = 0; i < _eina_debug_thread_active_num; i++)
_eina_debug_collect_bt(_eina_debug_thread_active[i].thread);
// we're done probing. now collec all the "i'm done" msgs on the
// semaphore for every thread + mainloop
for (i = 0; i < (_eina_debug_thread_active_num + 1); i++)
eina_semaphore_lock(&_wait_for_bts_sem);
// we now have gotten all the data from all threadd + mainloop.
// we can process it now as we see fit, so release thread lock
//// XXX: some debug so we can see the bt's we collect - will go
// for (i = 0; i < (_eina_debug_thread_active_num + 1); i++)
// {
// _eina_debug_dump_fhandle_bt(stderr, _bt_buf[i], _bt_buf_len[i]);
// }
err:
eina_spinlock_release(&_eina_debug_thread_lock);
//// XXX: some debug just to see how well we perform - will go
bts++;
if (bts >= 10000)
{
double t;
t = get_time();
e_debug("%1.5f bt's per sec", (double)bts / (t - _trace_t0));
_trace_t0 = t;
bts = 0;
}
return EINA_DEBUG_OK;
}
// profiling on with poll time gap as uint payload
static Eina_Debug_Error
_prof_on_cb(Eina_Debug_Session *session, int cid EINA_UNUSED, void *buffer, int size)
{
unsigned int time;
if (size >= 4)
{
memcpy(&time, buffer, 4);
_trace_t0 = 0.0;
eina_debug_timer_add(time, _trace_cb, session);
}
return EINA_DEBUG_OK;
}
static Eina_Debug_Error
_prof_off_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
eina_debug_timer_add(0, NULL, NULL);
return EINA_DEBUG_OK;
}
static const Eina_Debug_Opcode _OPS[] = {
{"profiler/on", NULL, &_prof_on_cb},
{"profiler/off", NULL, &_prof_off_cb},
{"profiler/bt_get", &_prof_get_op, &_prof_get_cb},
{NULL, NULL, NULL}
};
Eina_Bool
_eina_debug_bt_init(void)
{
eina_semaphore_new(&_wait_for_bts_sem, 0);
eina_debug_opcodes_register(NULL, _OPS, NULL);
return EINA_TRUE;
}
Eina_Bool
_eina_debug_bt_shutdown(void)
{
eina_semaphore_free(&_wait_for_bts_sem);
return EINA_TRUE;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,307 @@
# ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
# endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "eina_debug.h"
#include "eina_types.h"
#include "eina_list.h"
#include "eina_mempool.h"
#include "eina_util.h"
#include "eina_evlog.h"
#include "eina_debug_private.h"
volatile int _eina_debug_sysmon_reset = 0;
volatile int _eina_debug_sysmon_active = 0;
volatile int _eina_debug_evlog_active = 0;
static Eina_Lock _sysmon_lock;
static Eina_Bool _sysmon_thread_runs = EINA_FALSE;
static pthread_t _sysmon_thread;
// this is a DEDICATED thread tojust collect system info and to have the
// least impact it can on a cpu core or system. all this does right now
// is sleep and wait for a command to begin polling for the cpu state.
// right now that means iterating through cpu's and getting their cpu
// frequency to match up with event logs.
static void *
_sysmon(void *_data EINA_UNUSED)
{
static int cpufreqs[64] = { 0 };
int i, fd, freq;
char buf[256], path[256];
ssize_t red;
#if defined(__clockid_t_defined)
static struct timespec t_last = { 0, 0 };
static Eina_Debug_Thread *prev_threads = NULL;
static int prev_threads_num = 0;
int j, cpu;
Eina_Bool prev_threads_redo;
clockid_t cid;
struct timespec t, t_now;
unsigned long long tim_span, tim1, tim2;
#endif
// set a name for this thread for system debugging
#ifdef EINA_HAVE_PTHREAD_SETNAME
# ifndef __linux__
pthread_set_name_np
# else
pthread_setname_np
# endif
(pthread_self(), "Edbg-sys");
#endif
for (;;)
{
// wait on a mutex that will be locked for as long as this
// threead is not meant to go running
eina_lock_take(&_sysmon_lock);
// if we need to reset as we just started polling system stats...
if (_eina_debug_sysmon_reset)
{
_eina_debug_sysmon_reset = 0;
// clear out all the clocks for threads
#if defined(__clockid_t_defined)
// reset the last clock timestamp when we checked to "now"
clock_gettime(CLOCK_MONOTONIC, &t);
t_last = t;
// walk over all threads
eina_spinlock_take(&_eina_debug_thread_lock);
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
// get the correct clock id to use for the target thread
pthread_getcpuclockid
(_eina_debug_thread_active[i].thread, &cid);
// get the clock cpu time accumulation for that threas
clock_gettime(cid, &t);
_eina_debug_thread_active[i].clok = t;
}
eina_spinlock_release(&_eina_debug_thread_lock);
#endif
// clear all the cpu freq (up to 64 cores) to 0
for (i = 0; i < 64; i++) cpufreqs[i] = 0;
}
eina_lock_release(&_sysmon_lock);
#if defined(__clockid_t_defined)
// get the current time
clock_gettime(CLOCK_MONOTONIC, &t_now);
tim1 = (t_last.tv_sec * 1000000000LL) + t_last.tv_nsec;
// the time span between now and last time we checked
tim_span = ((t_now.tv_sec * 1000000000LL) + t_now.tv_nsec) - tim1;
// if the time span is non-zero we might get sensible results
if (tim_span > 0)
{
prev_threads_redo = EINA_FALSE;
eina_spinlock_take(&_eina_debug_thread_lock);
// figure out if the list of thread id's has changed since
// our last poll. this is imporant as we need to set the
// thread cpu usage to 0 for threads that have disappeared
if (prev_threads_num != _eina_debug_thread_active_num)
prev_threads_redo = EINA_TRUE;
else
{
// XXX: isolate this out of hot path
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
if (_eina_debug_thread_active[i].thread !=
prev_threads[i].thread)
{
prev_threads_redo = EINA_TRUE;
break;
}
}
}
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
pthread_t thread = _eina_debug_thread_active[i].thread;
// get the clock for the thread and its cpu time usage
pthread_getcpuclockid(thread, &cid);
clock_gettime(cid, &t);
// calculate a long timestamp (64bits)
tim1 = (_eina_debug_thread_active[i].clok.tv_sec * 1000000000LL) +
_eina_debug_thread_active[i].clok.tv_nsec;
// and get the difference in clock time in NS
tim2 = ((t.tv_sec * 1000000000LL) + t.tv_nsec) - tim1;
// and that as percentage of the timespan
cpu = (int)((100 * (int)tim2) / (int)tim_span);
// round to the nearest 10 percent - rough anyway
cpu = ((cpu + 5) / 10) * 10;
if (cpu > 100) cpu = 100;
// if the usage changed since last time we checked...
if (cpu != _eina_debug_thread_active[i].val)
{
// log this change
snprintf(buf, sizeof(buf), "*CPUUSED %llu",
(unsigned long long)thread);
snprintf(path, sizeof(path), "%i", _eina_debug_thread_active[i].val);
eina_evlog(buf, NULL, 0.0, path);
snprintf(path, sizeof(path), "%i", cpu);
eina_evlog(buf, NULL, 0.0, path);
// store the clock time + cpu we got for next poll
_eina_debug_thread_active[i].val = cpu;
}
_eina_debug_thread_active[i].clok = t;
}
// so threads changed between this poll and last so we need
// to redo our mapping/storage of them
if (prev_threads_redo)
{
prev_threads_redo = EINA_FALSE;
// find any threads from our last run that do not
// exist now in our new list of threads
for (j = 0; j < prev_threads_num; j++)
{
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
if (prev_threads[j].thread ==
_eina_debug_thread_active[i].thread) break;
}
// thread was there before and not now
if (i == _eina_debug_thread_active_num)
{
// log it finishing - ie 0
snprintf(buf, sizeof(buf), "*CPUUSED %llu",
(unsigned long long)
prev_threads[i].thread);
eina_evlog(buf, NULL, 0.0, "0");
}
}
// if the thread count changed then allocate a new shadow
// buffer of thread id's etc.
if (prev_threads_num != _eina_debug_thread_active_num)
{
if (prev_threads) free(prev_threads);
prev_threads_num = _eina_debug_thread_active_num;
prev_threads = malloc(prev_threads_num *
sizeof(Eina_Debug_Thread));
}
// store the thread handles/id's
for (i = 0; i < prev_threads_num; i++)
prev_threads[i].thread =
_eina_debug_thread_active[i].thread;
}
eina_spinlock_release(&_eina_debug_thread_lock);
t_last = t_now;
}
#endif
// poll up to 64 cpu cores for cpufreq info to log alongside
// the evlog call data
for (i = 0; i < 64; i++)
{
// look at sysfs nodes per cpu
snprintf
(buf, sizeof(buf),
"/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq", i);
fd = open(buf, O_RDONLY);
freq = 0;
// if the node is there, read it
if (fd >= 0)
{
// really low overhead read from cpufreq node (just an int)
red = read(fd, buf, sizeof(buf) - 1);
if (red > 0)
{
// read something - it should be an int with whitespace
buf[red] = 0;
freq = atoi(buf);
// move to mhz
freq = (freq + 500) / 1000;
// round mhz to the nearest 100mhz - to have less noise
freq = ((freq + 50) / 100) * 100;
}
// close the fd so we can freshly poll next time around
close(fd);
}
// node not there - ran out of cpu's to poll?
else break;
// if the current frequency changed vs previous poll, then log
if (freq != cpufreqs[i])
{
snprintf(buf, sizeof(buf), "*CPUFREQ %i", i);
snprintf(path, sizeof(path), "%i", freq);
eina_evlog(buf, NULL, 0.0, path);
cpufreqs[i] = freq;
}
}
usleep(1000); // 1ms sleep
}
return NULL;
}
static Eina_Debug_Error
_cpufreq_on_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
if (!_eina_debug_evlog_active)
{
_eina_debug_evlog_active = 1;
eina_evlog_start();
}
if (!_eina_debug_sysmon_active)
{
_eina_debug_sysmon_reset = 1;
_eina_debug_sysmon_active = 1;
eina_lock_release(&_sysmon_lock);
}
return EINA_DEBUG_OK;
}
static Eina_Debug_Error
_cpufreq_off_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
if (_eina_debug_sysmon_active)
{
eina_lock_take(&_sysmon_lock);
_eina_debug_sysmon_active = 0;
}
if (_eina_debug_evlog_active)
{
eina_evlog_stop();
_eina_debug_evlog_active = 0;
}
return EINA_DEBUG_OK;
}
static const Eina_Debug_Opcode _OPS[] = {
{"cpufreq/on", NULL, &_cpufreq_on_cb},
{"cpufreq/off", NULL, &_cpufreq_off_cb},
{NULL, NULL, NULL}
};
Eina_Bool
_eina_debug_cpu_init(void)
{
// if it's already running - we're good.
if (!_sysmon_thread_runs)
{
int err;
eina_lock_new(&_sysmon_lock);
eina_lock_take(&_sysmon_lock);
err = pthread_create(&_sysmon_thread, NULL, _sysmon, NULL);
if (err != 0)
{
e_debug("EINA DEBUG ERROR: Can't create debug sysmon thread!");
abort();
}
_sysmon_thread_runs = EINA_TRUE;
}
eina_debug_opcodes_register(NULL, _OPS, NULL);
return EINA_TRUE;
}
Eina_Bool
_eina_debug_cpu_shutdown(void)
{
return EINA_TRUE;
}

View File

@ -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

View File

@ -0,0 +1,73 @@
#ifndef EINA_DEBUG_PRIVATE_H_
# define EINA_DEBUG_PRIVATE_H_
# include "eina_config.h"
# include "eina_lock.h"
# include "eina_thread.h"
#ifdef DEBUGON
# define e_debug(fmt, args...) fprintf(stderr, "%d:"__FILE__":%s/%d : " fmt "\n", getpid(), __FUNCTION__, __LINE__, ##args)
# define e_debug_begin(fmt, args...) fprintf(stderr, "%d:"__FILE__":%s/%d : " fmt "", getpid(), __FUNCTION__, __LINE__, ##args)
# define e_debug_continue(fmt, args...) fprintf(stderr, fmt, ##args)
# define e_debug_end() fprintf(stderr, "\n")
#else
# define e_debug(x...) do { } while (0)
# define e_debug_begin(x...) do { } while (0)
# define e_debug_continue(x...) do { } while (0)
# define e_debug_end(x...) do { } while (0)
#endif
typedef struct _Eina_Debug_Session Eina_Debug_Session;
typedef struct _Eina_Debug_Thread Eina_Debug_Thread;
struct _Eina_Debug_Thread
{
pthread_t thread;
Eina_Debug_Session *cmd_session;
void *cmd_buffer;
int cmd_result;
#if defined(__clockid_t_defined)
struct timespec clok;
#endif
int thread_id;
int val;
};
extern Eina_Spinlock _eina_debug_lock;
extern Eina_Spinlock _eina_debug_thread_lock;
extern pthread_t _eina_debug_thread_mainloop;
extern Eina_Debug_Thread *_eina_debug_thread_active;
extern int _eina_debug_thread_active_num;
/* TEMP: should be private to debug thread module */
void _eina_debug_thread_add(void *th);
void _eina_debug_thread_del(void *th);
void _eina_debug_thread_mainloop_set(void *th);
void *_eina_debug_chunk_push(int size);
void *_eina_debug_chunk_realloc(int size);
char *_eina_debug_chunk_strdup(const char *str);
void *_eina_debug_chunk_tmp_push(int size);
void _eina_debug_chunk_tmp_reset(void);
const char *_eina_debug_file_get(const char *fname);
void _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen);
#define EINA_MAX_BT 256
#define EINA_BT(file) \
do { \
void *bt[EINA_MAX_BT]; \
int btlen = backtrace((void **)bt, EINA_MAX_BT); \
_eina_debug_dump_fhandle_bt(file, bt, btlen); \
} while (0)
Eina_Bool _eina_debug_cpu_init(void);
Eina_Bool _eina_debug_cpu_shutdown(void);
Eina_Bool _eina_debug_bt_init(void);
Eina_Bool _eina_debug_bt_shutdown(void);
#endif

View File

@ -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

View File

@ -17,8 +17,7 @@
*/
#include "eina_debug.h"
#ifdef EINA_HAVE_DEBUG
#include "eina_debug_private.h"
// a really simple store of currently known active threads. the mainloop is
// special and inittied at debug init time - assuming eina inits in the
@ -32,6 +31,7 @@ Eina_Debug_Thread *_eina_debug_thread_active = NULL;
int _eina_debug_thread_active_num = 0;
static int _thread_active_size = 0;
static int _thread_id_counter = 1;
// add a thread id to our tracking array - very simple. add to end, and
// if array to small, reallocate it to be bigger by 16 slots AND double that
@ -62,6 +62,7 @@ _eina_debug_thread_add(void *th)
_eina_debug_thread_active[_eina_debug_thread_active_num].clok.tv_nsec = 0;
_eina_debug_thread_active[_eina_debug_thread_active_num].val = -1;
#endif
_eina_debug_thread_active[_eina_debug_thread_active_num].thread_id = _thread_id_counter++;
_eina_debug_thread_active_num++;
// release our lock cleanly
eina_spinlock_release(&_eina_debug_thread_lock);
@ -101,4 +102,17 @@ _eina_debug_thread_mainloop_set(void *th)
pthread_t *pth = th;
_eina_debug_thread_mainloop = *pth;
}
#endif
EAPI int
eina_debug_thread_id_get(void)
{
pthread_t self = pthread_self();
int i;
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
if (self == _eina_debug_thread_active[i].thread)
return _eina_debug_thread_active[i].thread_id;
}
return -1;
}

View File

@ -24,8 +24,6 @@
#include "eina_evlog.h"
#include "eina_debug.h"
#ifdef EINA_HAVE_DEBUG
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
@ -35,6 +33,7 @@
#endif
#include <time.h>
#include <unistd.h>
# ifdef HAVE_MMAP
# include <sys/mman.h>
@ -54,6 +53,8 @@ static clockid_t _eina_evlog_time_clock_id = -1;
static double _eina_evlog_time_clock_conversion = 1e-9;
#endif
static int _evlog_get_opcode = EINA_DEBUG_OPCODE_INVALID;
static inline double
get_time(void)
{
@ -211,6 +212,50 @@ eina_evlog_stop(void)
eina_spinlock_release(&_evlog_lock);
}
// get evlog
static Eina_Debug_Error
_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
Eina_Evlog_Buf *evlog = eina_evlog_steal();
int resp_size = 0;
unsigned char *resp_buf = NULL;
if ((evlog) && (evlog->buf))
{
resp_size = evlog->top + sizeof(evlog->overflow);
resp_buf = alloca(resp_size);
memcpy(resp_buf, &(evlog->overflow), sizeof(evlog->overflow));
memcpy(resp_buf + sizeof(evlog->overflow), evlog->buf, evlog->top);
}
printf("send evlog size %d\n", resp_size);
eina_debug_session_send(session, cid, _evlog_get_opcode, resp_buf, resp_size);
return EINA_DEBUG_OK;
}
// enable evlog
static Eina_Debug_Error
_start_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
eina_evlog_start();
return EINA_DEBUG_OK;
}
// stop evlog
static Eina_Debug_Error
_stop_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
eina_evlog_stop();
return EINA_DEBUG_OK;
}
static const Eina_Debug_Opcode _EINA_DEBUG_EVLOG_OPS[] = {
{"evlog/on", NULL, &_start_cb},
{"evlog/off", NULL, &_stop_cb},
{"evlog/get", &_evlog_get_opcode, &_get_cb},
{NULL, NULL, NULL}
};
Eina_Bool
eina_evlog_init(void)
{
@ -227,6 +272,7 @@ eina_evlog_init(void)
}
#endif
eina_evlog("+eina_init", NULL, 0.0, NULL);
eina_debug_opcodes_register(NULL, _EINA_DEBUG_EVLOG_OPS, NULL);
return EINA_TRUE;
}
@ -237,37 +283,3 @@ eina_evlog_shutdown(void)
eina_spinlock_free(&_evlog_lock);
return EINA_TRUE;
}
#else
EAPI void
eina_evlog(const char *event EINA_UNUSED, void *obj EINA_UNUSED, double srctime EINA_UNUSED, const char *detail EINA_UNUSED)
{
}
EAPI Eina_Evlog_Buf *
eina_evlog_steal(void)
{
return NULL;
}
EAPI void
eina_evlog_start(void)
{
}
EAPI void
eina_evlog_stop(void)
{
}
Eina_Bool
eina_evlog_init(void)
{
return EINA_TRUE;
}
Eina_Bool
eina_evlog_shutdown(void)
{
return EINA_TRUE;
}
#endif

View File

@ -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"

View File

@ -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;