Merge Eina Debug feature

Eina Debug is a layer aimed to bring a way to debug EFL applications by
providing a transport channel between a debug tool and the applications
of a device.

In order to be interrupted by EFL core as less as possible, the communication
is done in a separated thread.
The Ecore loop is not used there, as well as the Ecore helpers (socket...).

Debugging operations can be registered easily by any layer of the application.

To use it:
- Launch efl_debugd
- Launch the application to debug
- Launch Clouseau (the new version will be pushed soon) or another debug tool
supporting Eina Debug
This commit is contained in:
Daniel Zaoui 2017-06-05 09:10:03 +03:00
commit b0bb79a3f0
24 changed files with 2943 additions and 1840 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,9 @@ lib/eina/eina_debug.c \
lib/eina/eina_debug_bt.c \
lib/eina/eina_debug_bt_file.c \
lib/eina/eina_debug_chunk.c \
lib/eina/eina_debug_monitor.c \
lib/eina/eina_debug_proto.c \
lib/eina/eina_debug_thread.c \
lib/eina/eina_debug_cpu.c \
lib/eina/eina_debug_timer.c \
lib/eina/eina_error.c \
lib/eina/eina_evlog.c \
lib/eina/eina_file_common.h \
@ -171,7 +172,6 @@ lib/eina/eina_util.c \
lib/eina/eina_value.c \
lib/eina/eina_value_util.c \
lib/eina/eina_xattr.c \
lib/eina/eina_debug.h \
lib/eina/eina_private.h \
lib/eina/eina_share_common.h \
lib/eina/eina_strbuf_common.h \

View File

@ -16,269 +16,262 @@
* if not, see <http://www.gnu.org/licenses/>.
*/
#define DECLARE_OPS
#include "efl_debug_common.h"
#include <Eina.h>
#include <Ecore.h>
static Eo *dialer;
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
static Eina_List *waiting;
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define SWAP_64(x) x
#define SWAP_32(x) x
#define SWAP_16(x) x
#else
#define SWAP_64(x) eina_swap64(x)
#define SWAP_32(x) eina_swap32(x)
#define SWAP_16(x) eina_swap16(x)
#endif
static int retval = EXIT_SUCCESS;
static void
_process_reply(void *data EINA_UNUSED, const char op[static 4], const Eina_Slice payload)
{
if (IS_OP(CLST))
{
int mypid = getpid();
size_t offset;
waiting = eina_list_remove(waiting, OP_CLST);
for (offset = 0; offset + sizeof(int) <= payload.len; offset += sizeof(int))
{
int p;
memcpy(&p, payload.bytes + offset, sizeof(int));
if (p == mypid) continue;
if (p > 0) printf("%i\n", p);
}
}
else
{
fprintf(stderr, "ERROR: unexpected server reply: %.4s\n", op);
retval = EXIT_FAILURE;
}
if (!waiting) ecore_main_loop_quit();
#define EXTRACT(_buf, pval, sz) \
{ \
memcpy(pval, _buf, sz); \
_buf += sz; \
}
#define _EVLOG_INTERVAL 0.2
static void
_on_data(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
{
if (!received_data(dialer, _process_reply, NULL))
{
retval = EXIT_FAILURE;
ecore_main_loop_quit();
}
}
static int _evlog_max_times = 0;
static Ecore_Timer *_evlog_fetch_timer = NULL;
static FILE *_evlog_file = NULL;
static int _cl_stat_reg_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _cid_from_pid_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _prof_on_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _prof_off_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _cpufreq_on_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _cpufreq_off_opcode = EINA_DEBUG_OPCODE_INVALID;
static int _evlog_get_opcode = EINA_DEBUG_OPCODE_INVALID;
static Eina_Debug_Session *_session = NULL;
static int _cid = 0;
static int my_argc = 0;
static char **my_argv = NULL;
static Eina_Bool
_command_send(const char op[static 4], const void *data, unsigned int len)
_evlog_get_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
{
if (!send_data(dialer, op, data, len))
static int received_times = 0;
unsigned char *d = buffer;
unsigned int *overflow = (unsigned int *)(d + 0);
unsigned char *p = d + 4;
unsigned int blocksize = size - 4;
if(++received_times <= _evlog_max_times)
{
retval = EXIT_FAILURE;
return EINA_FALSE;
if ((_evlog_file) && (blocksize > 0))
{
unsigned int header[3];
header[0] = 0xffee211;
header[1] = SWAP_32(blocksize);
header[2] = *overflow;
if (fwrite(header, 1, 12, _evlog_file) < 12 ||
fwrite(p, 1, blocksize, _evlog_file) < blocksize)
printf("Error writing bytes to evlog file\n");
}
}
if(received_times == _evlog_max_times)
{
printf("Received last evlog response\n");
if (_evlog_file) fclose(_evlog_file);
_evlog_file = NULL;
ecore_main_loop_quit();
}
return EINA_TRUE;
}
#define command_send(op, data, len) _command_send(OP_ ## op, data, len)
static void
_write_finished(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
static Eina_Bool
_cb_evlog(void *data EINA_UNUSED)
{
if (!waiting) ecore_main_loop_quit();
static int sent_times = 0;
Eina_Bool ret = ECORE_CALLBACK_RENEW;
if(++sent_times <= _evlog_max_times)
eina_debug_session_send(_session, _cid, _evlog_get_opcode, NULL, 0);
if(sent_times == _evlog_max_times)
{
eina_debug_session_send(_session, _cid, _cpufreq_off_opcode, NULL, 0);
ecore_timer_del(_evlog_fetch_timer);
_evlog_fetch_timer = NULL;
ret = ECORE_CALLBACK_CANCEL;
}
return ret;
}
static void
_finished(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
static Eina_Bool
_cid_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer, int size EINA_UNUSED)
{
ecore_main_loop_quit();
_cid = *(int *)buffer;
const char *op_str = my_argv[1];
Eina_Bool quit = EINA_TRUE;
if ((!strcmp(op_str, "pon")) && (3 <= (my_argc - 1)))
{
int freq = SWAP_32(atoi(my_argv[3]));
eina_debug_session_send(_session, _cid, _prof_on_opcode, &freq, sizeof(int));
}
else if (!strcmp(op_str, "poff"))
eina_debug_session_send(_session, _cid, _prof_off_opcode, NULL, 0);
else if (!strcmp(op_str, "evlogon") && (3 <= (my_argc - 1)))
{
double max_time;
sscanf(my_argv[3], "%lf", &max_time);
_evlog_max_times = max_time > 0 ? (max_time/_EVLOG_INTERVAL+1) : 1;
eina_debug_session_send(_session, 0, _cl_stat_reg_opcode, NULL, 0);
printf("Evlog request will be sent %d times\n", _evlog_max_times);
eina_debug_session_send(_session, _cid, _cpufreq_on_opcode, NULL, 0);
/* Creating the evlog file and setting the timer */
char path[4096];
int pid = atoi(my_argv[2]);
snprintf(path, sizeof(path), "%s/efl_debug_evlog-%ld.log",
getenv("HOME"), (long)pid);
_evlog_file = fopen(path, "wb");
_evlog_fetch_timer = ecore_timer_add(_EVLOG_INTERVAL, _cb_evlog, NULL);
quit = EINA_FALSE;
}
else if (!strcmp(op_str, "evlogoff"))
eina_debug_session_send(_session, _cid, _cpufreq_off_opcode, NULL, 0);
if(quit)
ecore_main_loop_quit();
return EINA_TRUE;
}
static void
_error(void *data EINA_UNUSED, const Efl_Event *event)
static Eina_Bool
_clients_info_added_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
{
Eina_Error *perr = event->info;
fprintf(stderr, "ERROR: error communicating to %s: %s\n",
efl_net_dialer_address_dial_get(dialer),
eina_error_msg_get(*perr));
retval = EXIT_FAILURE;
ecore_main_loop_quit();
char *buf = buffer;
while(size)
{
int cid, pid, len;
EXTRACT(buf, &cid, sizeof(int));
EXTRACT(buf, &pid, sizeof(int));
cid = SWAP_32(cid);
pid = SWAP_32(pid);
/* We dont need client notifications on evlog */
if(!_evlog_fetch_timer)
printf("Added: CID: %d - PID: %d - Name: %s\n", cid, pid, buf);
len = strlen(buf) + 1;
buf += len;
size -= (2 * sizeof(int) + len);
}
return EINA_TRUE;
}
int
main(int argc, char **argv)
static Eina_Bool
_clients_info_deleted_cb(Eina_Debug_Session *session EINA_UNUSED, int src EINA_UNUSED, void *buffer, int size)
{
Eo *loop;
char *path;
Eina_Error err;
int i;
if (argc < 2)
char *buf = buffer;
while(size)
{
fprintf(stderr, "ERROR: missing argument.\n");
return EXIT_FAILURE;
}
for (i = 1; i < argc; i++)
{
if ((strcmp(argv[i], "-h") != 0) &&
(strcmp(argv[i], "--help") != 0))
continue;
int cid;
EXTRACT(buf, &cid, sizeof(int));
cid = SWAP_32(cid);
size -= sizeof(int);
printf("Usage:\n"
"\n"
"\t%s <command> [arguments]\n"
"\n"
"where <command> is one of:\n"
"\tlist list connected process (pid)\n"
"\tpon <pid> <freq> enable profiling for <pid> at frequency <freq> in microseconds.\n"
"\tpoff <pid> disable profiling for <pid>\n"
"\tevlogon <pid> start logging events to ~/efl_debug_evlog-<pid>.log\n"
"\tevlogoff <pid> stop logging events from <pid>\n",
argv[0]);
return EXIT_SUCCESS;
}
ecore_app_no_system_modules();
eina_init();
ecore_init();
ecore_con_init();
path = ecore_con_local_path_new(EINA_FALSE, "efl_debug", 0);
if (!path)
{
fprintf(stderr, "ERROR: could not get local communication path\n");
retval = EXIT_FAILURE;
goto end;
}
loop = ecore_main_loop_get();
#ifdef EFL_NET_DIALER_UNIX_CLASS
dialer = efl_add(EFL_NET_DIALER_SIMPLE_CLASS, loop,
efl_net_dialer_simple_inner_class_set(efl_added, EFL_NET_DIALER_UNIX_CLASS));
#elif defined(EFL_NET_DIALER_WINDOWS_CLASS)
dialer = efl_add(EFL_NET_DIALER_SIMPLE_CLASS, loop,
efl_net_dialer_simple_inner_class_set(efl_added, EFL_NET_DIALER_WINDOWS_CLASS));
#else
/* TODO: maybe start a TCP using locahost:12345?
* Right now eina_debug_monitor is only for AF_UNIX, so not an issue.
*/
fprintf(stderr, "ERROR: your platform doesn't support Efl.Net.Dialer.*\n");
#endif
if (!dialer)
{
fprintf(stderr, "ERROR: could not create communication dialer\n");
retval = EXIT_FAILURE;
goto end;
}
efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_ERROR, _error, NULL);
efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_SLICE_CHANGED, _on_data, NULL);
efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_WRITE_FINISHED, _write_finished, NULL);
efl_event_callback_add(dialer, EFL_IO_BUFFERED_STREAM_EVENT_FINISHED, _finished, NULL);
for (i = 1; i < argc; i++)
{
const char *cmd = argv[i];
if (strcmp(cmd, "list") == 0)
/* If client deleted dont send anymore evlog requests */
if(_evlog_fetch_timer)
{
if (!command_send(LIST, NULL, 0))
goto end;
waiting = eina_list_append(waiting, OP_CLST);
}
else if (strcmp(cmd, "pon") == 0)
{
if (i + 2 >= argc)
if(_cid == cid)
{
fprintf(stderr, "ERROR: missing argument: pon <pid> <freq>\n");
retval = EXIT_FAILURE;
goto end;
}
else
{
int data[2] = {atoi(argv[i + 1]), atoi(argv[1 + 2])};
if (!command_send(PLON, data, sizeof(data)))
goto end;
i += 2;
}
}
else if (strcmp(cmd, "poff") == 0)
{
if (i + 1 >= argc)
{
fprintf(stderr, "ERROR: missing argument: poff <pid>\n");
retval = EXIT_FAILURE;
goto end;
}
else
{
int data[1] = {atoi(argv[i + 1])};
if (!command_send(PLOF, data, sizeof(data)))
goto end;
i++;
}
}
else if (strcmp(cmd, "evlogon") == 0)
{
if (i + 1 >= argc)
{
fprintf(stderr, "ERROR: missing argument: evlogon <pid>\n");
retval = EXIT_FAILURE;
goto end;
}
else
{
int data[1] = {atoi(argv[i + 1])};
if (!command_send(EVON, data, sizeof(data)))
goto end;
i++;
}
}
else if (strcmp(cmd, "evlogoff") == 0)
{
if (i + 1 >= argc)
{
fprintf(stderr, "ERROR: missing argument: evlogoff <pid>\n");
retval = EXIT_FAILURE;
goto end;
}
else
{
int data[1] = {atoi(argv[i + 1])};
if (!command_send(EVOF, data, sizeof(data)))
goto end;
i++;
printf("Evlog debugged App closed (CID: %d), stopping evlog\n", cid);
ecore_timer_del(_evlog_fetch_timer);
_evlog_fetch_timer = NULL;
fclose(_evlog_file);
_evlog_file = NULL;
ecore_main_loop_quit();
}
}
else
{
fprintf(stderr, "ERROR: unknown command: %s\n", argv[i]);
retval = EXIT_FAILURE;
goto end;
}
printf("Deleted: CID: %d\n", cid);
}
efl_io_buffered_stream_eos_mark(dialer);
return EINA_TRUE;
}
err = efl_net_dialer_dial(dialer, path);
if (err)
static void
_ecore_thread_dispatcher(void *data)
{
eina_debug_dispatch(_session, data);
}
Eina_Bool
_disp_cb(Eina_Debug_Session *session EINA_UNUSED, void *buffer)
{
ecore_main_loop_thread_safe_call_async(_ecore_thread_dispatcher, buffer);
return EINA_TRUE;
}
static void
_args_handle(void *data EINA_UNUSED, Eina_Bool flag)
{
if (!flag) exit(0);
eina_debug_session_dispatch_override(_session, _disp_cb);;
const char *op_str = my_argv[1];
if (op_str && !strcmp(op_str, "list"))
{
fprintf(stderr, "ERROR: could not connect '%s': %s\n", path, eina_error_msg_get(err));
retval = EXIT_FAILURE;
goto end;
eina_debug_session_send(_session, 0, _cl_stat_reg_opcode, NULL, 0);
}
else if (2 <= my_argc - 1)
{
int pid = atoi(my_argv[2]);
eina_debug_session_send(_session, 0, _cid_from_pid_opcode, &pid, sizeof(int));
}
}
EINA_DEBUG_OPCODES_ARRAY_DEFINE(ops,
{"Daemon/Client/register_observer", &_cl_stat_reg_opcode, NULL},
{"Daemon/Client/added", NULL, &_clients_info_added_cb},
{"Daemon/Client/deleted", NULL, &_clients_info_deleted_cb},
{"Daemon/Client/cid_from_pid", &_cid_from_pid_opcode, &_cid_get_cb},
{"Profiler/on", &_prof_on_opcode, NULL},
{"Profiler/off", &_prof_off_opcode, NULL},
{"CPU/Freq/on", &_cpufreq_on_opcode, NULL},
{"CPU/Freq/off", &_cpufreq_off_opcode, NULL},
{"EvLog/get", &_evlog_get_opcode, _evlog_get_cb},
{NULL, NULL, NULL}
);
int
main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
{
eina_init();
ecore_init();
my_argc = argc;
my_argv = argv;
_session = eina_debug_local_connect(EINA_TRUE);
if (!_session)
{
fprintf(stderr, "ERROR: Cannot connect to debug daemon.\n");
return -1;
}
eina_debug_opcodes_register(_session, ops(), _args_handle, NULL);
ecore_main_loop_begin();
end:
eina_list_free(waiting);
efl_del(dialer);
free(path);
ecore_con_shutdown();
ecore_shutdown();
eina_shutdown();
(void) OP_HELO;
(void) OP_EVLG;
return retval;
return 0;
}

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

File diff suppressed because it is too large Load Diff

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,74 @@
* if not, see <http://www.gnu.org/licenses/>.
*/
# ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
# endif
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_SYS_EPOLL_H
# include <sys/epoll.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UN_H
# include <sys/un.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#include <fcntl.h>
#ifdef _WIN32
# include <winsock2.h>
# include <Evil.h>
#endif
#include "eina_debug.h"
#include "eina_types.h"
#include "eina_list.h"
#include "eina_mempool.h"
#include "eina_util.h"
#include "eina_evlog.h"
#include "eina_hash.h"
#include "eina_stringshare.h"
#include "eina_debug_private.h"
#ifdef EINA_HAVE_DEBUG
#if defined(__CYGWIN__) || defined (_WIN32)
# define LIBEXT ".dll"
#else
# define LIBEXT ".so"
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define SWAP_64(x) x
#define SWAP_32(x) x
#define SWAP_16(x) x
#else
#define SWAP_64(x) eina_swap64(x)
#define SWAP_32(x) eina_swap32(x)
#define SWAP_16(x) eina_swap16(x)
#endif
// yes - a global debug spinlock. i expect contention to be low for now, and
// when needed we can split this up into mroe locks to reduce contention when
@ -29,6 +93,580 @@ Eina_Spinlock _eina_debug_lock;
// only init once
static Eina_Bool _inited = EINA_FALSE;
#ifdef __linux__
extern char *__progname;
#endif
extern Eina_Bool eina_module_init(void);
extern Eina_Bool eina_mempool_init(void);
extern Eina_Bool eina_list_init(void);
extern Eina_Spinlock _eina_debug_thread_lock;
static Eina_Bool _debug_disabled = EINA_FALSE;
/* Local session */
/* __thread here to allow debuggers to be master and slave by using two different threads */
static Eina_Debug_Session *_last_local_session = NULL;
typedef struct
{
const Eina_Debug_Opcode *ops;
Eina_Debug_Opcode_Status_Cb status_cb;
void *status_data;
} _opcode_reply_info;
struct _Eina_Debug_Session
{
Eina_List **cbs; /* Table of callbacks lists indexed by opcode id */
Eina_List *opcode_reply_infos;
Eina_Debug_Dispatch_Cb dispatch_cb; /* Session dispatcher */
void *data; /* User data */
int cbs_length; /* cbs table size */
int fd; /* File descriptor */
};
#ifndef _WIN32
static void _opcodes_register_all(Eina_Debug_Session *session);
#endif
static void _thread_start(Eina_Debug_Session *session);
EAPI int
eina_debug_session_send(Eina_Debug_Session *session, int dest, int op, void *data, int size)
{
Eina_Debug_Packet_Header hdr;
if (!session) return -1;
if (op == EINA_DEBUG_OPCODE_INVALID) return -1;
/* Preparation of the packet header */
hdr.size = SWAP_32(size + sizeof(Eina_Debug_Packet_Header));
hdr.opcode = SWAP_32(op);
hdr.cid = SWAP_32(dest);
e_debug("socket: %d / opcode %X / bytes to send: %d",
session->fd, op, size + sizeof(*hdr));
#ifndef _WIN32
eina_spinlock_take(&_eina_debug_lock);
/* Sending header */
write(session->fd, &hdr, sizeof(hdr));
/* Sending payload */
if (size) write(session->fd, data, size);
eina_spinlock_release(&_eina_debug_lock);
#else
(void)data;
#endif
return size;
}
#ifndef _WIN32
static void
_daemon_greet(Eina_Debug_Session *session)
{
/* say hello to our debug daemon - tell them our PID and protocol
version we speak */
/* Version + Pid + App name */
#ifdef __linux__
char *app_name = __progname;
#else
char *app_name = NULL;
#endif
int size = 8 + (app_name ? strlen(app_name) : 0) + 1;
unsigned char *buf = alloca(size);
int version = SWAP_32(1); // version of protocol we speak
int pid = getpid();
pid = SWAP_32(pid);
memcpy(buf + 0, &version, 4);
memcpy(buf + 4, &pid, 4);
if (app_name)
memcpy(buf + 8, app_name, strlen(app_name) + 1);
else
buf[8] = '\0';
eina_debug_session_send(session, 0, EINA_DEBUG_OPCODE_HELLO, buf, size);
}
static int
_packet_receive(Eina_Debug_Session *session, unsigned char **buffer)
{
unsigned char *packet_buf = NULL;
int rret = -1;
unsigned int size = 0;
if (!session) goto end;
if ((rret = read(session->fd, &size, 4)) == 4)
{
size = SWAP_32(size);
if (size > EINA_DEBUG_MAX_PACKET_SIZE)
{
e_debug("Packet too big: %d. The maximum allowed is %d", size, EINA_DEBUG_MAX_PACKET_SIZE);
rret = -1;
goto end;
}
e_debug("Begin to receive a packet of %d bytes", size);
// allocate a buffer for the next bytes to receive
packet_buf = malloc(size);
if (packet_buf)
{
unsigned int cur_packet_size = 4;
memcpy(packet_buf, &size, 4);
/* Receive all the remaining packet bytes */
while (cur_packet_size < size)
{
rret = read(session->fd, packet_buf + cur_packet_size, size - cur_packet_size);
if (rret <= 0)
{
e_debug("Error on read: %d", rret);
perror("Read");
goto end;
}
cur_packet_size += rret;
}
*buffer = packet_buf;
rret = cur_packet_size;
e_debug("Received a packet of %d bytes", cur_packet_size);
}
else
{
// we couldn't allocate memory for payload buffer
// internal memory limit error
e_debug("Cannot allocate %u bytes for op", size);
goto end;
}
}
else
{
e_debug("Invalid size read %i != %i", rret, size_sz);
goto end;
}
end:
if (rret <= 0 && packet_buf) free(packet_buf);
return rret;
}
#endif
EAPI void
eina_debug_disable()
{
_debug_disabled = EINA_TRUE;
}
EAPI void
eina_debug_session_terminate(Eina_Debug_Session *session)
{
/* Close fd here so the thread terminates its own session by itself */
if (!session) return;
close(session->fd);
}
EAPI void
eina_debug_session_dispatch_override(Eina_Debug_Session *session, Eina_Debug_Dispatch_Cb disp_cb)
{
if (!session) return;
if (!disp_cb) disp_cb = eina_debug_dispatch;
session->dispatch_cb = disp_cb;
}
EAPI Eina_Debug_Dispatch_Cb
eina_debug_session_dispatch_get(Eina_Debug_Session *session)
{
if (session) return session->dispatch_cb;
else return NULL;
}
#ifndef _WIN32
static void
_static_opcode_register(Eina_Debug_Session *session,
int op_id, Eina_Debug_Cb cb)
{
if(session->cbs_length < op_id + 1)
{
int i = session->cbs_length;
session->cbs_length = op_id + 16;
session->cbs = realloc(session->cbs, session->cbs_length * sizeof(Eina_List *));
for(; i < session->cbs_length; i++) session->cbs[i] = NULL;
}
if (cb)
{
session->cbs[op_id] = eina_list_append(session->cbs[op_id], cb);
}
}
/*
* Response of the daemon containing the ids of the requested opcodes.
* PTR64 + (opcode id)*
*/
static Eina_Bool
_callbacks_register_cb(Eina_Debug_Session *session, int src_id EINA_UNUSED, void *buffer, int size)
{
_opcode_reply_info *info = NULL, *info2;
Eina_List *itr;
int *os;
unsigned int count, i;
uint64_t info_64;
memcpy(&info_64, buffer, sizeof(uint64_t));
info_64 = SWAP_64(info_64);
info = (_opcode_reply_info *)info_64;
if (!info) return EINA_FALSE;
EINA_LIST_FOREACH(session->opcode_reply_infos, itr, info2)
{
if (info2 == info)
{
os = (int *)((unsigned char *)buffer + sizeof(uint64_t));
count = (size - sizeof(uint64_t)) / sizeof(int);
for (i = 0; i < count; i++)
{
int op = SWAP_32(os[i]);
if (info->ops[i].opcode_id) *(info->ops[i].opcode_id) = op;
_static_opcode_register(session, op, info->ops[i].cb);
e_debug("Opcode %s -> %d", info->ops[i].opcode_name, op);
}
if (info->status_cb) info->status_cb(info->status_data, EINA_TRUE);
return EINA_TRUE;
}
}
return EINA_FALSE;
}
#endif
static void
_opcodes_registration_send(Eina_Debug_Session *session,
_opcode_reply_info *info)
{
unsigned char *buf;
int count = 0;
int size = sizeof(uint64_t);
while (info->ops[count].opcode_name)
{
size += strlen(info->ops[count].opcode_name) + 1;
count++;
}
buf = malloc(size);
uint64_t info_64 = (uint64_t)(uintptr_t)info;
info_64 = SWAP_64(info_64);
memcpy(buf, &info_64, sizeof(uint64_t));
int size_curr = sizeof(uint64_t);
count = 0;
while (info->ops[count].opcode_name)
{
int len = strlen(info->ops[count].opcode_name) + 1;
memcpy(buf + size_curr, info->ops[count].opcode_name, len);
size_curr += len;
count++;
}
eina_debug_session_send(session, 0, EINA_DEBUG_OPCODE_REGISTER, buf, size);
free(buf);
}
#ifndef _WIN32
static void
_opcodes_register_all(Eina_Debug_Session *session)
{
Eina_List *l;
_opcode_reply_info *info = NULL;
_static_opcode_register(session,
EINA_DEBUG_OPCODE_REGISTER, _callbacks_register_cb);
EINA_LIST_FOREACH(session->opcode_reply_infos, l, info)
_opcodes_registration_send(session, info);;
}
static void
_opcodes_unregister_all(Eina_Debug_Session *session)
{
Eina_List *l;
int i;
_opcode_reply_info *info = NULL;
if (!session) return;
session->cbs_length = 0;
for (i = 0; i < session->cbs_length; i++)
eina_list_free(session->cbs[i]);
free(session->cbs);
session->cbs = NULL;
EINA_LIST_FOREACH(session->opcode_reply_infos, l, info)
{
const Eina_Debug_Opcode *op = info->ops;
while(!op->opcode_name)
{
if (op->opcode_id) *(op->opcode_id) = EINA_DEBUG_OPCODE_INVALID;
op++;
}
if (info->status_cb) info->status_cb(info->status_data, EINA_FALSE);
}
}
static const char *
_socket_home_get()
{
// get possible debug daemon socket directory base
const char *dir = getenv("XDG_RUNTIME_DIR");
if (!dir) dir = eina_environment_home_get();
if (!dir) dir = eina_environment_tmp_get();
return dir;
}
#define LENGTH_OF_SOCKADDR_UN(s) \
(strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
#endif
static Eina_Debug_Session *
_session_create(int fd)
{
Eina_Debug_Session *session = calloc(1, sizeof(*session));
session->dispatch_cb = eina_debug_dispatch;
session->fd = fd;
// start the monitor thread
_thread_start(session);
return session;
}
EAPI Eina_Debug_Session *
eina_debug_remote_connect(int port)
{
int fd;
struct sockaddr_in server;
//Create socket
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) goto err;
// set the socket to close when we exec things so they don't inherit it
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto err;
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr.s_addr);
server.sin_port = htons(port);
if (connect(fd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("connect failed. Error");
goto err;
}
return _session_create(fd);
err:
// some kind of connection failure here, so close a valid socket and
// get out of here
if (fd >= 0) close(fd);
return NULL;
}
EAPI Eina_Debug_Session *
eina_debug_local_connect(Eina_Bool is_master)
{
#ifndef _WIN32
char buf[4096];
int fd, socket_unix_len, curstate = 0;
struct sockaddr_un socket_unix;
if (is_master) return eina_debug_remote_connect(REMOTE_SERVER_PORT);
// try this socket file - it will likely be:
// ~/.ecore/efl_debug/0
// or maybe
// /var/run/UID/.ecore/efl_debug/0
snprintf(buf, sizeof(buf), "%s/%s/%s/%i", _socket_home_get(),
LOCAL_SERVER_PATH, LOCAL_SERVER_NAME, LOCAL_SERVER_PORT);
// create the socket
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) goto err;
// set the socket to close when we exec things so they don't inherit it
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto err;
// set up some socket options on addr re-use
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate,
sizeof(curstate)) < 0)
goto err;
// sa that it's a unix socket and where the path is
socket_unix.sun_family = AF_UNIX;
strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path) - 1);
socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
// actually connect to efl_debugd service
if (connect(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
goto err;
_last_local_session = _session_create(fd);
return _last_local_session;
err:
// some kind of connection failure here, so close a valid socket and
// get out of here
if (fd >= 0) close(fd);
#else
(void) is_master;
#endif
return NULL;
}
// this is a DEDICATED debug thread to monitor the application so it works
// even if the mainloop is blocked or the app otherwise deadlocked in some
// way. this is an alternative to using external debuggers so we can get
// users or developers to get useful information about an app at all times
#ifndef _WIN32
static void *
_monitor(void *_data)
{
Eina_Debug_Session *session = _data;
_daemon_greet(session);
_opcodes_register_all(session);
// set a name for this thread for system debugging
#ifdef EINA_HAVE_PTHREAD_SETNAME
# ifndef __linux__
pthread_set_name_np
# else
pthread_setname_np
# endif
(pthread_self(), "Edbg-mon");
#endif
// sit forever processing commands or timeouts in the debug monitor
// thread - this is separate to the rest of the app so it shouldn't
// impact the application specifically
for (;session;)
{
unsigned char *buffer;
int size;
size = _packet_receive(session, &buffer);
// if not negative - we have a real message
if (size > 0)
{
Eina_Debug_Packet_Header *hdr = (Eina_Debug_Packet_Header *)buffer;
hdr->cid = SWAP_32(hdr->cid);
hdr->opcode = SWAP_32(hdr->opcode);
if (EINA_TRUE != session->dispatch_cb(session, buffer))
{
// something we don't understand
e_debug("EINA DEBUG ERROR: Unknown command");
}
/* Free the buffer only if the default dispatcher is used */
if (session->dispatch_cb == eina_debug_dispatch)
free(buffer);
}
else
{
close(session->fd);
_opcodes_unregister_all(session);
free(session);
session = NULL;
}
}
return NULL;
}
#endif
// start up the debug monitor if we haven't already
static void
_thread_start(Eina_Debug_Session *session)
{
#ifndef _WIN32
pthread_t monitor_thread;
int err;
sigset_t oldset, newset;
sigemptyset(&newset);
sigaddset(&newset, SIGPIPE);
sigaddset(&newset, SIGALRM);
sigaddset(&newset, SIGCHLD);
sigaddset(&newset, SIGUSR1);
sigaddset(&newset, SIGUSR2);
sigaddset(&newset, SIGHUP);
sigaddset(&newset, SIGQUIT);
sigaddset(&newset, SIGINT);
sigaddset(&newset, SIGTERM);
#ifdef SIGPWR
sigaddset(&newset, SIGPWR);
#endif
sigprocmask(SIG_BLOCK, &newset, &oldset);
err = pthread_create(&monitor_thread, NULL, _monitor, session);
sigprocmask(SIG_SETMASK, &oldset, NULL);
if (err != 0)
{
e_debug("EINA DEBUG ERROR: Can't create monitor debug thread!");
abort();
}
#else
(void)session;
#endif
}
/*
* Sends to daemon:
* - Pointer to ops: returned in the response to determine which opcodes have been added
* - List of opcode names separated by \0
*/
EAPI void
eina_debug_opcodes_register(Eina_Debug_Session *session, const Eina_Debug_Opcode ops[],
Eina_Debug_Opcode_Status_Cb status_cb, void *data)
{
if (!session) session = _last_local_session;
if (!session) return;
_opcode_reply_info *info = malloc(sizeof(*info));
info->ops = ops;
info->status_cb = status_cb;
info->status_data = data;
session->opcode_reply_infos = eina_list_append(
session->opcode_reply_infos, info);
/* Send only if session's fd connected.
* Otherwise, it will be sent when connected */
if(session && session->fd!= -1)
_opcodes_registration_send(session, info);
}
EAPI Eina_Bool
eina_debug_dispatch(Eina_Debug_Session *session, void *buffer)
{
Eina_Debug_Packet_Header *hdr = buffer;
Eina_List *itr;
int opcode = hdr->opcode;
Eina_Debug_Cb cb = NULL;
if (opcode >= session->cbs_length)
{
e_debug("Invalid opcode %d", opcode);
return EINA_FALSE;
}
EINA_LIST_FOREACH(session->cbs[opcode], itr, cb)
{
if (!cb) continue;
Eina_Bool ret = cb(session, hdr->cid,
(unsigned char *)buffer + sizeof(*hdr),
hdr->size - sizeof(*hdr));
if (ret == EINA_FALSE) return ret;
}
return EINA_TRUE;
}
EAPI void
eina_debug_session_data_set(Eina_Debug_Session *session, void *data)
{
if (session) session->data = data;
}
EAPI void *
eina_debug_session_data_get(Eina_Debug_Session *session)
{
if (session) return session->data;
else return NULL;
}
Eina_Bool
eina_debug_init(void)
{
@ -43,54 +681,42 @@ eina_debug_init(void)
}
// mark as initted
_inited = EINA_TRUE;
eina_module_init();
eina_mempool_init();
eina_list_init();
// For Windows support GetModuleFileName can be used
// set up thread things
eina_spinlock_new(&_eina_debug_lock);
eina_spinlock_new(&_eina_debug_thread_lock);
eina_semaphore_new(&_eina_debug_monitor_return_sem, 0);
self = pthread_self();
_eina_debug_thread_mainloop_set(&self);
_eina_debug_thread_add(&self);
# if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
// if we are setuid - don't debug!
if (getuid() != geteuid()) return EINA_TRUE;
#endif
// if someone uses the EFL_NODEBUG env var - do not do debugging. handy
// for when this debug code is buggy itself
if (getenv("EFL_NODEBUG")) return EINA_TRUE;
// connect to our debug daemon
_eina_debug_monitor_service_connect();
// if we connected - set up the debug monitor properly
if (_eina_debug_monitor_service_fd >= 0)
// if someone uses the EFL_NODEBUG env var or disabled debug - do not do
// debugging. handy for when this debug code is buggy itself
if (!getenv("EFL_NODEBUG") && !_debug_disabled)
{
// say hello to the debug daemon
_eina_debug_monitor_service_greet();
// set up our profile signal handler
_eina_debug_monitor_signal_init();
// start the monitor thread
_eina_debug_monitor_thread_start();
eina_debug_local_connect(EINA_FALSE);
}
_eina_debug_cpu_init();
_eina_debug_bt_init();
_eina_debug_timer_init();
return EINA_TRUE;
}
Eina_Bool
eina_debug_shutdown(void)
{
_eina_debug_timer_shutdown();
_eina_debug_bt_shutdown();
_eina_debug_cpu_shutdown();
eina_spinlock_take(&_eina_debug_thread_lock);
// yes - we never free on shutdown - this is because the monitor thread
// never exits. this is not a leak - we intend to never free up any
// resources here because they are allocated once only ever.
return EINA_TRUE;
}
#else
Eina_Bool
eina_debug_init(void)
{
return EINA_TRUE;
}
Eina_Bool
eina_debug_shutdown(void)
{
return EINA_TRUE;
}
#endif

View File

@ -19,103 +19,294 @@
#ifndef EINA_DEBUG_H_
# define EINA_DEBUG_H_
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include "eina_config.h"
# include "eina_list.h"
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <unistd.h>
# if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && defined(HAVE_DLADDR) && defined(HAVE_UNWIND)
# include <execinfo.h>
# ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
# endif
# include <errno.h>
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <dlfcn.h>
# include <sys/select.h>
# include <sys/time.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <pthread.h>
# include <signal.h>
# include <time.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/socket.h>
# include <sys/un.h>
# include <fcntl.h>
# include <libunwind.h>
# include "eina_config.h"
# include "eina_private.h"
# include "eina_inlist.h"
# include "eina_lock.h"
# include "eina_thread.h"
# include "eina_convert.h"
# include "eina_strbuf.h"
# include "eina_safety_checks.h"
# include "eina_log.h"
# include "eina_inline_private.h"
# define EINA_HAVE_DEBUG 1
# define EINA_MAX_BT 256
typedef struct _Eina_Debug_Thread Eina_Debug_Thread;
struct _Eina_Debug_Thread
{
pthread_t thread;
#if defined(__clockid_t_defined)
struct timespec clok;
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
int val;
#ifdef EFL_BETA_API_SUPPORT
/**
* @page eina_debug_main Eina Debug
*
* @date 2015 (created)
*/
/**
* @addtogroup Eina_Debug
* @{
*/
enum
{
EINA_DEBUG_OPCODE_INVALID = -1, /**< Invalid opcode value */
EINA_DEBUG_OPCODE_REGISTER = 0, /**< Opcode used to register other opcodes */
EINA_DEBUG_OPCODE_HELLO = 1 /**< Opcode used to send greetings to the daemon */
};
extern Eina_Spinlock _eina_debug_lock;
extern Eina_Spinlock _eina_debug_thread_lock;
extern pthread_t _eina_debug_thread_mainloop;
extern Eina_Debug_Thread *_eina_debug_thread_active;
extern int _eina_debug_thread_active_num;
extern Eina_Semaphore _eina_debug_monitor_return_sem;
extern int _eina_debug_monitor_service_fd;
/**
* @typedef Eina_Debug_Session
*
* A handle used to interact with the debug daemon.
* It contains all the information related to this connection and needed
* to send/receive/dispatch/...
*/
typedef struct _Eina_Debug_Session Eina_Debug_Session;
void _eina_debug_thread_add(void *th);
void _eina_debug_thread_del(void *th);
void _eina_debug_thread_mainloop_set(void *th);
/**
* @typedef Eina_Debug_Cb
*
* A callback invoked when a specific packet is received.
*
* @param session the session
* @param srcid the source id
* @param buffer the packet payload data. It doesn't contain any transport information.
* @param size the packet payload size
*
* return true on success, false if the connection seems compromised
*/
typedef Eina_Bool (*Eina_Debug_Cb)(Eina_Debug_Session *session, int srcid, void *buffer, int size);
void *_eina_debug_chunk_push(int size);
void *_eina_debug_chunk_realloc(int size);
char *_eina_debug_chunk_strdup(const char *str);
void *_eina_debug_chunk_tmp_push(int size);
void _eina_debug_chunk_tmp_reset(void);
/**
* @typedef Eina_Debug_Opcode_Status_Cb
*
* When the opcodes ids are retrieved, this callback is invoked with a true
* status.
* When a disconnection to the daemon is happening, the opcodes ids are set
* as invalid and this callback is invoked with a false status. The upper
* layer should not try to send more requests until a new connection is
* established.
*
* @param data data pointer given when registering opcodes
* @param status EINA_TRUE if opcodes have been received from the daemon, EINA_FALSE otherwise.
*/
typedef void (*Eina_Debug_Opcode_Status_Cb)(void *data, Eina_Bool status);
const char *_eina_debug_file_get(const char *fname);
/**
* @typedef Eina_Debug_Dispatch_Cb
*
* Dispatcher callback prototype used to override the default dispatcher of a
* session.
*
* @param session the session
* @param buffer the packet received
*
* The given packet is the entire data received, including the header.
*
* return the return result of the invoked callback
*/
typedef Eina_Bool (*Eina_Debug_Dispatch_Cb)(Eina_Debug_Session *session, void *buffer);
void _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen);
/**
* @typedef Eina_Debug_Timer_Cb
*
* A callback for a timer
*/
typedef Eina_Bool (*Eina_Debug_Timer_Cb)(void *);
void _eina_debug_monitor_thread_start(void);
void _eina_debug_monitor_signal_init(void);
void _eina_debug_monitor_service_connect(void);
/**
* @typedef Eina_Debug_Timer
*/
typedef struct _Eina_Debug_Timer Eina_Debug_Timer;
int _eina_debug_monitor_service_send(int fd, const char op[4],
unsigned char *data, int size);
void _eina_debug_monitor_service_greet(void);
int _eina_debug_monitor_service_read(char *op, unsigned char **data);
/**
* @typedef Eina_Debug_Packet_Header
*
* Header of Eina Debug packet
*/
typedef struct
{
unsigned int size; /**< Packet size including this element */
/**<
* During sending, it corresponds to the id of the destination. During reception, it is the id of the source
* The daemon is in charge of swapping the id before forwarding the packet to the destination.
*/
int cid;
int opcode; /**< Opcode of the packet */
} Eina_Debug_Packet_Header;
# define EINA_BT(file) \
do { \
void *bt[EINA_MAX_BT]; \
int btlen = backtrace((void **)bt, EINA_MAX_BT); \
_eina_debug_dump_fhandle_bt(file, bt, btlen); \
} while (0)
# else
# define EINA_BT(file) do { } while (0)
# endif
/**
* Helper for creating global opcodes arrays.
* The problem is on windows where you can't declare a static array with
* external symbols in it, because the addresses are only known at runtime.
*/
#define EINA_DEBUG_OPCODES_ARRAY_DEFINE(Name, ...) \
static Eina_Debug_Opcode * \
Name(void) \
{ \
Eina_Debug_Opcode tmp[] = { __VA_ARGS__ }; \
static Eina_Debug_Opcode internal[EINA_C_ARRAY_LENGTH(tmp) + 1] = \
{ { 0, 0, 0 } }; \
if (internal[0].opcode_name == NULL) \
{ \
memcpy(internal, tmp, sizeof(tmp)); \
} \
return internal; \
}
/**
* @typedef Eina_Debug_Opcode
*
* Structure to describe information for an opcode. It is used to register new
* opcodes.
*/
typedef struct
{
char *opcode_name; /**< Opcode string. On registration, the daemon uses it to calculate an opcode id */
int *opcode_id; /**< A pointer to store the opcode id received from the daemon */
Eina_Debug_Cb cb; /**< Callback to call when a packet corresponding to the opcode is received */
} Eina_Debug_Opcode;
/**
* @brief Disable debugging
*
* Useful for applications that don't want debugging. The debug daemon is one
* of them.
* Need to be invoked before eina_init. Otherwise it won't have any effect.
*/
EAPI void eina_debug_disable(void);
/**
* @brief Connect to the local daemon
*
* @param is_master true if the application is a debugger. EINA_FALSE otherwise.
*
* @return the session on success or NULL otherwise
*/
EAPI Eina_Debug_Session *eina_debug_local_connect(Eina_Bool is_master);
/**
* @brief Connect to remote daemon
*
* This function connects to localhost:port.
*
* @param port the port to connect to
*
* @return the session on success or NULL otherwise
*/
EAPI Eina_Debug_Session *eina_debug_remote_connect(int port);
/**
* @brief Terminate the session
*
* @param session the session to terminate
*
*/
EAPI void eina_debug_session_terminate(Eina_Debug_Session *session);
/**
* @brief Override the dispatcher of a specific session
*
* For example, it can be used to forward a packet to the main thread and to
* use the default dispatcher there.
* All the packets received in this session will use this dispatcher.
*
* @param session the session
* @disp_cb the new dispatcher for the given session
*/
EAPI void eina_debug_session_dispatch_override(Eina_Debug_Session *session, Eina_Debug_Dispatch_Cb disp_cb);
/**
* @brief Get the dispatcher of a specific session
*
* @param session the session
*
* @return the session dispatcher
*/
EAPI Eina_Debug_Dispatch_Cb eina_debug_session_dispatch_get(Eina_Debug_Session *session);
/**
* @brief Dispatch a given packet according to its header.
*
* This function checks the header contained into the packet and invokes
* the correct callback according to the opcode.
* This is the default dispatcher.
*
* @param session the session
* @param buffer the packet
*
* return true on success, false if the connection seems compromised
*/
EAPI Eina_Bool eina_debug_dispatch(Eina_Debug_Session *session, void *buffer);
/**
* @brief Set data to a session
*
* @param session the session
* @param data the data to set
*
*/
EAPI void eina_debug_session_data_set(Eina_Debug_Session *session, void *data);
/**
* @brief Get the data attached to a session
*
* @param session the session
*
* @return the data of the session
*/
EAPI void *eina_debug_session_data_get(Eina_Debug_Session *session);
/**
* @brief Register opcodes to a session
*
* This function registers opcodes for the given session. If the session is not
* connected, the request is not sent to the daemon. Otherwise, the request for
* the opcodes ids is sent.
* On the reception from the daemon, status_cb function is invoked to inform
* the requester that the opcodes can now be used.
*
* @param session the session
* @param ops the operations to register
* @param status_cb a function to call when the opcodes are received
* @param status_data the data to give to status_cb
*/
EAPI void eina_debug_opcodes_register(Eina_Debug_Session *session,
const Eina_Debug_Opcode ops[],
Eina_Debug_Opcode_Status_Cb status_cb, void *status_data);
/**
* @brief Send a packet to the given destination
*
* The packet will be treated by the debug thread itself.
*
* @param session the session to use to send the packet
* @param dest_id the destination id to send the packet to
* @param op the opcode for this packet
* @param data payload to send
* @param size payload size
*
* @return the number of sent bytes
*/
EAPI int eina_debug_session_send(Eina_Debug_Session *session, int dest_id, int op, void *data, int size);
/**
* @brief Add a timer
*
* @param timeout_ms timeout in ms
* @param cb callback to call when the timeout is reached
* @param data user data
*
* @return the timer handle, NULL on error
*/
EAPI Eina_Debug_Timer *eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data);
/**
* @brief Delete a timer
*
* @param timer the timer to delete
*
* If the timer reaches the end and has not be renewed, trying to delete it will lead to a crash, as
* it has already been deleted internally.
*/
EAPI void eina_debug_timer_del(Eina_Debug_Timer *timer);
/**
* @}
*/
#endif
#endif

View File

@ -16,13 +16,45 @@
* if not, see <http://www.gnu.org/licenses/>.
*/
#include "eina_debug.h"
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
#ifdef EINA_HAVE_DEBUG
#ifdef HAVE_DLADDR
# ifdef _WIN32
# include <Evil.h>
# else
# include <dlfcn.h>
# endif
#endif
#ifdef HAVE_UNWIND
#include <libunwind.h>
#endif
#include "eina_debug.h"
#include "eina_debug_private.h"
#ifndef _WIN32
# define SIG SIGPROF
#endif
static Eina_Semaphore _wait_for_bts_sem;
// _bt_buf[0] is always for mainloop, 1 + is for extra threads
static void ***_bt_buf;
static int *_bt_buf_len;
static struct timespec *_bt_ts;
static int *_bt_cpu;
/* Used by trace timer */
static double _trace_t0 = 0.0;
static Eina_Debug_Timer *_timer = NULL;
void
_eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
{
#ifndef _WIN32
int i;
Dl_info info;
const char *file;
@ -32,7 +64,7 @@ _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
{
file = NULL;
offset = base = 0;
// we have little choice but to hgope/assume dladdr() doesn't alloc
// we have little choice but to hope/assume dladdr() doesn't alloc
// anything here
if ((dladdr(bt[i], &info)) && (info.dli_fname[0]))
{
@ -45,5 +77,231 @@ _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
if (file) fprintf(f, "%s\t 0x%llx 0x%llx\n", file, offset, base);
else fprintf(f, "??\t -\n");
}
#else
(void)f;
(void)bt;
(void)btlen;
#endif
}
// a backtracer that uses libunwind to do the job
static inline int
_eina_debug_unwind_bt(void **bt, int max)
{
#ifdef HAVE_UNWIND
unw_cursor_t cursor;
unw_context_t uc;
unw_word_t p;
int total;
// create a context for unwinding
unw_getcontext(&uc);
// begin our work
unw_init_local(&cursor, &uc);
// walk up each stack frame until there is no more, storing it
for (total = 0; (unw_step(&cursor) > 0) && (total < max); total++)
{
unw_get_reg(&cursor, UNW_REG_IP, &p);
bt[total] = (void *)p;
}
// return our total backtrace stack size
return total;
#else
(void)bt;
(void)max;
return 0;
#endif
}
// a quick and dirty local time point getter func - not portable
static inline double
get_time(void)
{
#if defined(__clockid_t_defined)
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0);
#else
struct timeval timev;
gettimeofday(&timev, NULL);
return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
#endif
}
#ifndef _WIN32
static void
_signal_handler(int sig EINA_UNUSED,
siginfo_t *si EINA_UNUSED, void *foo EINA_UNUSED)
{
int i, slot = 0;
pthread_t self = pthread_self();
clockid_t cid;
// find which slot in the array of threads we have so we store info
// in the correct slot for us
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
if (self == _eina_debug_thread_active[i].thread)
{
slot = i;
goto found;
}
}
// we couldn't find out thread reference! help!
e_debug("EINA DEBUG ERROR: can't find thread slot!");
eina_semaphore_release(&_wait_for_bts_sem, 1);
return;
found:
// store thread info like what cpu core we are on now (not reliable
// but hey - better than nothing), the amount of cpu time total
// we have consumed (it's cumulative so subtracing deltas can give
// you an average amount of cpu time consumed between now and the
// previous time we looked) and also a full backtrace
_bt_cpu[slot] = sched_getcpu();
pthread_getcpuclockid(self, &cid);
clock_gettime(cid, &(_bt_ts[slot]));
_bt_buf_len[slot] = _eina_debug_unwind_bt(_bt_buf[slot], EINA_MAX_BT);
// now wake up the monitor to let them know we are done collecting our
// backtrace info
eina_semaphore_release(&_wait_for_bts_sem, 1);
}
#endif
static void
_signal_init(void)
{
#ifndef _WIN32
struct sigaction sa;
// set up signal handler for our profiling signal - eevery thread should
// obey this (this is the case on linux - other OSs may vary)
sa.sa_sigaction = _signal_handler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(SIG, &sa, NULL) != 0)
e_debug("EINA DEBUG ERROR: Can't set up sig %i handler!", SIG);
sa.sa_sigaction = NULL;
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGPIPE, &sa, 0) == -1) perror(0);
#endif
}
static void
_collect_bt(pthread_t pth)
{
// this async signals the thread to switch to the deebug signal handler
// and collect a backtrace and other info from inside the thread
#ifndef _WIN32
pthread_kill(pth, SIG);
#endif
}
static Eina_Bool
_trace_cb(void *data EINA_UNUSED)
{
static int bts = 0;
int i;
if (!_trace_t0) _trace_t0 = get_time();
// take a lock on grabbing thread debug info like backtraces
eina_spinlock_take(&_eina_debug_thread_lock);
// reset our "stack" of memory se use to dump thread info into
_eina_debug_chunk_tmp_reset();
// get an array of pointers for the backtrace array for main + th
_bt_buf = _eina_debug_chunk_tmp_push
((_eina_debug_thread_active_num) * sizeof(void *));
if (!_bt_buf) goto err;
// get an array of pointers for the timespec array for mainloop + th
_bt_ts = _eina_debug_chunk_tmp_push
((_eina_debug_thread_active_num) * sizeof(struct timespec));
if (!_bt_ts) goto err;
// get an array of pointers for the cpuid array for mainloop + th
_bt_cpu = _eina_debug_chunk_tmp_push
((_eina_debug_thread_active_num) * sizeof(int));
if (!_bt_cpu) goto err;
// get an array of void ptrs for each thread we know about for bt
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
_bt_buf[i] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *));
if (!_bt_buf[i]) goto err;
}
// get an array of ints to stor the bt len for mainloop + threads
_bt_buf_len = _eina_debug_chunk_tmp_push
((_eina_debug_thread_active_num) * sizeof(int));
// now collect per thread
for (i = 0; i < _eina_debug_thread_active_num; i++)
_collect_bt(_eina_debug_thread_active[i].thread);
// we're done probing. now collec all the "i'm done" msgs on the
// semaphore for every thread + mainloop
for (i = 0; i < (_eina_debug_thread_active_num); i++)
eina_semaphore_lock(&_wait_for_bts_sem);
// we now have gotten all the data from all threads
// we can process it now as we see fit, so release thread lock
for (i = 0; i < _eina_debug_thread_active_num; i++)
{
_eina_debug_dump_fhandle_bt(stderr, _bt_buf[i], _bt_buf_len[i]);
}
err:
eina_spinlock_release(&_eina_debug_thread_lock);
//// XXX: some debug just to see how well we perform - will go
bts++;
if (bts >= 10000)
{
double t;
t = get_time();
e_debug("%1.5f bt's per sec", (double)bts / (t - _trace_t0));
_trace_t0 = t;
bts = 0;
}
return EINA_TRUE;
}
// profiling on with poll time gap as uint payload
static Eina_Bool
_prof_on_cb(Eina_Debug_Session *session, int cid EINA_UNUSED, void *buffer, int size)
{
unsigned int time;
if (size >= 4)
{
memcpy(&time, buffer, 4);
_trace_t0 = 0.0;
if (_timer) eina_debug_timer_del(_timer);
_timer = eina_debug_timer_add(time, _trace_cb, session);
}
return EINA_TRUE;
}
static Eina_Bool
_prof_off_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
eina_debug_timer_del(_timer);
_timer = NULL;
return EINA_TRUE;
}
EINA_DEBUG_OPCODES_ARRAY_DEFINE(_OPS,
{"Profiler/on", NULL, &_prof_on_cb},
{"Profiler/off", NULL, &_prof_off_cb},
{NULL, NULL, NULL}
);
Eina_Bool
_eina_debug_bt_init(void)
{
_signal_init();
eina_semaphore_new(&_wait_for_bts_sem, 0);
eina_debug_opcodes_register(NULL, _OPS(), NULL, NULL);
return EINA_TRUE;
}
Eina_Bool
_eina_debug_bt_shutdown(void)
{
eina_semaphore_free(&_wait_for_bts_sem);
return EINA_TRUE;
}

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_Bool
_cpufreq_on_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
if (!_eina_debug_evlog_active)
{
_eina_debug_evlog_active = 1;
eina_evlog_start();
}
if (!_eina_debug_sysmon_active)
{
_eina_debug_sysmon_reset = 1;
_eina_debug_sysmon_active = 1;
eina_lock_release(&_sysmon_lock);
}
return EINA_TRUE;
}
static Eina_Bool
_cpufreq_off_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
if (_eina_debug_sysmon_active)
{
eina_lock_take(&_sysmon_lock);
_eina_debug_sysmon_active = 0;
}
if (_eina_debug_evlog_active)
{
eina_evlog_stop();
_eina_debug_evlog_active = 0;
}
return EINA_TRUE;
}
EINA_DEBUG_OPCODES_ARRAY_DEFINE(_OPS,
{"CPU/Freq/on", NULL, &_cpufreq_on_cb},
{"CPU/Freq/off", NULL, &_cpufreq_off_cb},
{NULL, NULL, NULL}
);
Eina_Bool
_eina_debug_cpu_init(void)
{
// if it's already running - we're good.
if (!_sysmon_thread_runs)
{
int err;
eina_lock_new(&_sysmon_lock);
eina_lock_take(&_sysmon_lock);
err = pthread_create(&_sysmon_thread, NULL, _sysmon, NULL);
if (err != 0)
{
e_debug("EINA DEBUG ERROR: Can't create debug sysmon thread!");
abort();
}
_sysmon_thread_runs = EINA_TRUE;
}
eina_debug_opcodes_register(NULL, _OPS(), NULL, NULL);
return EINA_TRUE;
}
Eina_Bool
_eina_debug_cpu_shutdown(void)
{
return EINA_TRUE;
}

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

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,4 @@ _eina_debug_thread_mainloop_set(void *th)
pthread_t *pth = th;
_eina_debug_thread_mainloop = *pth;
}
#endif

View File

@ -0,0 +1,206 @@
/* EINA - EFL data type library
* Copyright (C) 2017 Carsten Haitzler
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library;
* if not, see <http://www.gnu.org/licenses/>.
*/
# ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
# endif
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_SYS_EPOLL_H
# include <sys/epoll.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include "eina_debug.h"
#include "eina_debug_private.h"
static Eina_Spinlock _lock;
struct _Eina_Debug_Timer
{
unsigned int rel_time;
unsigned int timeout;
Eina_Debug_Timer_Cb cb;
void *data;
};
static Eina_List *_timers = NULL;
static Eina_Bool _thread_runs = EINA_FALSE;
static Eina_Bool _exit_required = EINA_FALSE;
static pthread_t _thread;
static int pipeToThread[2];
static void
_timer_append(Eina_Debug_Timer *t)
{
Eina_Debug_Timer *t2;
Eina_List *itr;
unsigned int prev_time = 0;
char c = '\0';
EINA_LIST_FOREACH(_timers, itr, t2)
{
if (t2->timeout > t->timeout) goto end;
prev_time = t2->timeout;
}
t2 = NULL;
end:
t->rel_time = t->timeout - prev_time;
if (!t2) _timers = eina_list_append(_timers, t);
else _timers = eina_list_prepend_relative(_timers, t, t2);
write(pipeToThread[1], &c, 1);
}
static void *
_monitor(void *_data EINA_UNUSED)
{
#ifndef _WIN32
#define MAX_EVENTS 4
struct epoll_event event;
struct epoll_event events[MAX_EVENTS];
int epfd = epoll_create(MAX_EVENTS), ret;
event.data.fd = pipeToThread[0];
event.events = EPOLLIN;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, event.data.fd, &event);
if (ret) perror("epoll_ctl/add");
#ifdef EINA_HAVE_PTHREAD_SETNAME
# ifndef __linux__
pthread_set_name_np
# else
pthread_setname_np
# endif
(pthread_self(), "Edbg-tim");
#endif
for (;!_exit_required;)
{
int timeout = -1; //in milliseconds
eina_spinlock_take(&_lock);
if (_timers)
{
Eina_Debug_Timer *t = eina_list_data_get(_timers);
timeout = t->timeout;
}
eina_spinlock_release(&_lock);
ret = epoll_wait(epfd, events, MAX_EVENTS, timeout);
if (_exit_required) continue;
/* Some timer has been add/removed or we need to exit */
if (ret)
{
char c;
if (read(pipeToThread[0], &c, 1) != 1) _exit_required = EINA_TRUE;
}
else
{
Eina_List *itr, *itr2, *renew = NULL;
Eina_Debug_Timer *t;
eina_spinlock_take(&_lock);
EINA_LIST_FOREACH_SAFE(_timers, itr, itr2, t)
{
if (itr == _timers || t->rel_time == 0)
{
_timers = eina_list_remove(_timers, t);
if (t->cb(t->data)) renew = eina_list_append(renew, t);
else free(t);
}
}
EINA_LIST_FREE(renew, t) _timer_append(t);
eina_spinlock_release(&_lock);
}
}
#endif
_thread_runs = EINA_FALSE;
close(pipeToThread[0]);
close(pipeToThread[1]);
return NULL;
}
EAPI Eina_Debug_Timer *
eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data)
{
if (!cb || !timeout_ms) return NULL;
Eina_Debug_Timer *t = calloc(1, sizeof(*t));
t->cb = cb;
t->data = data;
t->timeout = timeout_ms;
eina_spinlock_take(&_lock);
_timer_append(t);
if (!_thread_runs)
{
int err = pthread_create(&_thread, NULL, _monitor, NULL);
if (err != 0)
{
e_debug("EINA DEBUG ERROR: Can't create debug timer thread!");
abort();
}
_thread_runs = EINA_TRUE;
}
eina_spinlock_release(&_lock);
return t;
}
EAPI void
eina_debug_timer_del(Eina_Debug_Timer *t)
{
eina_spinlock_take(&_lock);
Eina_List *itr = eina_list_data_find_list(_timers, t);
if (itr)
{
_timers = eina_list_remove_list(_timers, itr);
free(t);
}
eina_spinlock_release(&_lock);
}
Eina_Bool
_eina_debug_timer_init(void)
{
eina_spinlock_new(&_lock);
#ifndef _WIN32
pipe(pipeToThread);
#endif
return EINA_TRUE;
}
Eina_Bool
_eina_debug_timer_shutdown(void)
{
char c = '\0';
_exit_required = EINA_TRUE;
write(pipeToThread[1], &c, 1);
eina_spinlock_free(&_lock);
return EINA_TRUE;
}

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,11 +33,24 @@
#endif
#include <time.h>
#include <unistd.h>
# ifdef HAVE_MMAP
# include <sys/mman.h>
# endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define SWAP_64(x) x
#define SWAP_32(x) x
#define SWAP_16(x) x
#define SWAP_DBL(x) x
#else
#define SWAP_64(x) eina_swap64(x)
#define SWAP_32(x) eina_swap32(x)
#define SWAP_16(x) eina_swap16(x)
#define SWAP_DBL(x) SWAP_64(x)
#endif
# define EVLOG_BUF_SIZE (4 * (1024 * 1024))
static Eina_Spinlock _evlog_lock;
@ -54,6 +65,8 @@ static clockid_t _eina_evlog_time_clock_id = -1;
static double _eina_evlog_time_clock_conversion = 1e-9;
#endif
static int _evlog_get_opcode = EINA_DEBUG_OPCODE_INVALID;
static inline double
get_time(void)
{
@ -148,13 +161,13 @@ eina_evlog(const char *event, void *obj, double srctime, const char *detail)
eina_spinlock_take(&_evlog_lock);
strings = push_buf(buf, size);
item = (Eina_Evlog_Item *)strings;
item->tim = now;
item->srctim = srctime;
item->thread = (unsigned long long)(uintptr_t)pthread_self();
item->obj = (unsigned long long)(uintptr_t)obj;
item->event_offset = sizeof(Eina_Evlog_Item);
item->detail_offset = detail_offset;
item->event_next = size;
item->tim = SWAP_DBL(now);
item->srctim = SWAP_DBL(srctime);
item->thread = SWAP_64((unsigned long long)(uintptr_t)pthread_self());
item->obj = SWAP_64((unsigned long long)(uintptr_t)obj);
item->event_offset = SWAP_16(sizeof(Eina_Evlog_Item));
item->detail_offset = SWAP_16(detail_offset);
item->event_next = SWAP_16(size);
strcpy(strings + sizeof(Eina_Evlog_Item), event);
if (detail_offset > 0) strcpy(strings + detail_offset, detail);
eina_spinlock_release(&_evlog_lock);
@ -211,6 +224,52 @@ eina_evlog_stop(void)
eina_spinlock_release(&_evlog_lock);
}
// get evlog
static Eina_Bool
_get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
Eina_Evlog_Buf *evlog = eina_evlog_steal();
int resp_size = 0;
unsigned char *resp_buf = NULL;
if ((evlog) && (evlog->buf))
{
int ovf = SWAP_32(evlog->overflow);
resp_size = evlog->top + sizeof(evlog->overflow);
resp_buf = malloc(resp_size);
memcpy(resp_buf, &ovf, sizeof(ovf));
memcpy(resp_buf + sizeof(evlog->overflow), evlog->buf, evlog->top);
}
printf("send evlog size %d\n", resp_size);
eina_debug_session_send(session, cid, _evlog_get_opcode, resp_buf, resp_size);
free(resp_buf);
return EINA_TRUE;
}
// enable evlog
static Eina_Bool
_start_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
eina_evlog_start();
return EINA_TRUE;
}
// stop evlog
static Eina_Bool
_stop_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
{
eina_evlog_stop();
return EINA_TRUE;
}
EINA_DEBUG_OPCODES_ARRAY_DEFINE(_EINA_DEBUG_EVLOG_OPS,
{"EvLog/on", NULL, &_start_cb},
{"EvLog/off", NULL, &_stop_cb},
{"EvLog/get", &_evlog_get_opcode, &_get_cb},
{NULL, NULL, NULL}
);
Eina_Bool
eina_evlog_init(void)
{
@ -227,6 +286,7 @@ eina_evlog_init(void)
}
#endif
eina_evlog("+eina_init", NULL, 0.0, NULL);
eina_debug_opcodes_register(NULL, _EINA_DEBUG_EVLOG_OPS(), NULL, NULL);
return EINA_TRUE;
}
@ -237,37 +297,3 @@ eina_evlog_shutdown(void)
eina_spinlock_free(&_evlog_lock);
return EINA_TRUE;
}
#else
EAPI void
eina_evlog(const char *event EINA_UNUSED, void *obj EINA_UNUSED, double srctime EINA_UNUSED, const char *detail EINA_UNUSED)
{
}
EAPI Eina_Evlog_Buf *
eina_evlog_steal(void)
{
return NULL;
}
EAPI void
eina_evlog_start(void)
{
}
EAPI void
eina_evlog_stop(void)
{
}
Eina_Bool
eina_evlog_init(void)
{
return EINA_TRUE;
}
Eina_Bool
eina_evlog_shutdown(void)
{
return EINA_TRUE;
}
#endif

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;

View File

@ -237,16 +237,38 @@ static struct {
void (*shutdown)(void);
Eina_Bool (*app_connect)(const char *appname);
Eina_Bool is_init;
} _clouseau_old_info;
static struct {
Eina_Module *handle;
Eina_Bool (*init)(void);
Eina_Bool (*shutdown)(void);
Eina_Bool is_init;
} _clouseau_info;
#define _CLOUSEAU_OLD_LOAD_SYMBOL(cls_struct, sym) \
do \
{ \
if ((cls_struct).handle) \
(cls_struct).sym = eina_module_symbol_get((cls_struct).handle, "clouseau_" #sym); \
if (!(cls_struct).sym) \
{ \
WRN("Failed loading symbol '%s' from the clouseau library.", "clouseau_" #sym); \
if ((cls_struct).handle) eina_module_free((cls_struct).handle); \
(cls_struct).handle = NULL; \
} \
} \
while (0)
#define _CLOUSEAU_LOAD_SYMBOL(cls_struct, sym) \
do \
{ \
(cls_struct).sym = eina_module_symbol_get((cls_struct).handle, "clouseau_" #sym); \
if ((cls_struct).handle) \
(cls_struct).sym = eina_module_symbol_get((cls_struct).handle, "clouseau_debug_" #sym); \
if (!(cls_struct).sym) \
{ \
WRN("Failed loading symbol '%s' from the clouseau library.", "clouseau_" #sym); \
eina_module_free((cls_struct).handle); \
WRN("Failed loading symbol '%s' from the clouseau library.", "clouseau_debug_" #sym); \
if ((cls_struct).handle) eina_module_free((cls_struct).handle); \
(cls_struct).handle = NULL; \
return EINA_FALSE; \
} \
@ -256,21 +278,32 @@ static struct {
static void
_elm_clouseau_unload()
{
if (!_clouseau_info.is_init)
return;
if (_clouseau_info.shutdown)
if (_clouseau_old_info.is_init)
{
_clouseau_info.shutdown();
if (_clouseau_old_info.shutdown)
{
_clouseau_old_info.shutdown();
}
if (_clouseau_old_info.handle)
{
eina_module_free(_clouseau_old_info.handle);
_clouseau_old_info.handle = NULL;
}
_clouseau_old_info.is_init = EINA_FALSE;
}
if (_clouseau_info.handle)
if (_clouseau_info.is_init)
{
eina_module_free(_clouseau_info.handle);
_clouseau_info.handle = NULL;
if (_clouseau_info.shutdown)
{
_clouseau_info.shutdown();
}
if (_clouseau_info.handle)
{
eina_module_free(_clouseau_info.handle);
_clouseau_info.handle = NULL;
}
_clouseau_info.is_init = EINA_FALSE;
}
_clouseau_info.is_init = EINA_FALSE;
}
Eina_Bool
@ -282,30 +315,53 @@ _elm_clouseau_reload()
return EINA_TRUE;
}
if (_clouseau_info.is_init)
return EINA_TRUE;
_clouseau_info.handle = eina_module_new(
PACKAGE_LIB_DIR "/libclouseau" LIBEXT);
if (!_clouseau_info.handle || !eina_module_load(_clouseau_info.handle))
if (!_clouseau_old_info.is_init)
{
WRN("Failed loading the clouseau library.");
if (_clouseau_info.handle) eina_module_free(_clouseau_info.handle);
_clouseau_info.handle = NULL;
return EINA_FALSE;
_clouseau_old_info.handle = eina_module_new(
PACKAGE_LIB_DIR "/libclouseau" LIBEXT);
if (!_clouseau_old_info.handle || !eina_module_load(_clouseau_old_info.handle))
{
WRN("Failed loading the clouseau_old library.");
if (_clouseau_old_info.handle) eina_module_free(_clouseau_old_info.handle);
_clouseau_old_info.handle = NULL;
}
_CLOUSEAU_OLD_LOAD_SYMBOL(_clouseau_old_info, init);
_CLOUSEAU_OLD_LOAD_SYMBOL(_clouseau_old_info, shutdown);
_CLOUSEAU_OLD_LOAD_SYMBOL(_clouseau_old_info, app_connect);
if (_clouseau_old_info.handle)
{
_clouseau_old_info.init();
if (!_clouseau_old_info.app_connect(elm_app_name_get()))
{
ERR("Failed connecting to the clouseau server.");
}
_clouseau_old_info.is_init = EINA_TRUE;
}
}
_CLOUSEAU_LOAD_SYMBOL(_clouseau_info, init);
_CLOUSEAU_LOAD_SYMBOL(_clouseau_info, shutdown);
_CLOUSEAU_LOAD_SYMBOL(_clouseau_info, app_connect);
_clouseau_info.init();
if (!_clouseau_info.app_connect(elm_app_name_get()))
if (!_clouseau_info.is_init)
{
ERR("Failed connecting to the clouseau server.");
}
_clouseau_info.handle = eina_module_new(
PACKAGE_LIB_DIR "/libclouseau_debug" LIBEXT);
if (!_clouseau_info.handle || !eina_module_load(_clouseau_info.handle))
{
WRN("Failed loading the clouseau library.");
if (_clouseau_info.handle) eina_module_free(_clouseau_info.handle);
_clouseau_info.handle = NULL;
return EINA_FALSE;
}
_clouseau_info.is_init = EINA_TRUE;
_CLOUSEAU_LOAD_SYMBOL(_clouseau_info, init);
_CLOUSEAU_LOAD_SYMBOL(_clouseau_info, shutdown);
if (_clouseau_info.handle)
{
_clouseau_info.init();
_clouseau_info.is_init = EINA_TRUE;
}
}
return EINA_TRUE;
}

View File

@ -1770,6 +1770,33 @@ efl_replace(Eo **storage, Eo *new_obj)
#include "efl_future.h"
/**
* @addtogroup Eo_Iterators Eo iterators
* @{
*/
/**
* @brief Get an iterator on the Eo classes
*
* You can use this function to walk over the Eo classes.
*
* @return an iterator on success, NULL otherwise
*/
EAPI Eina_Iterator *eo_classes_iterator_new(void);
/**
* @brief Get an iterator on the Eo objects
*
* You can use this function to walk over the Eo objects.
*
* @return an iterator on success, NULL otherwise
*/
EAPI Eina_Iterator *eo_objects_iterator_new(void);
/**
* @}
*/
/**
* @}
*/

View File

@ -2934,3 +2934,124 @@ _eo_log_obj_shutdown(void)
eina_inarray_flush(&_eo_log_objs_no_debug);
}
#endif
typedef struct
{
Eina_Iterator iterator;
unsigned int cur_kl_id;
} _Eo_Classes_Iterator;
static Eina_Bool
_eo_classes_iterator_next(Eina_Iterator *it, void **data)
{
_Eo_Classes_Iterator *eo_it = (_Eo_Classes_Iterator *)it;
if (eo_it->cur_kl_id == _eo_classes_last_id) return EINA_FALSE;
*data = _eo_class_id_get(_eo_classes[eo_it->cur_kl_id]);
eo_it->cur_kl_id++;
return EINA_TRUE;
}
static void
_eo_classes_iterator_free(Eina_Iterator *it)
{
EINA_MAGIC_SET(it, EINA_MAGIC_NONE);
free(it);
}
EAPI Eina_Iterator *
eo_classes_iterator_new(void)
{
_Eo_Classes_Iterator *it;
it = calloc(1, sizeof (*it));
if (!it) return NULL;
it->iterator.version = EINA_ITERATOR_VERSION;
it->iterator.next = _eo_classes_iterator_next;
it->iterator.free = _eo_classes_iterator_free;
EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
return (Eina_Iterator *)it;
}
typedef struct
{
Eina_Iterator iterator;
Eo_Id_Table_Data *tdata;
Table_Index mid_table_id;
Table_Index table_id;
Table_Index entry_id;
} _Eo_Objects_Iterator;
static Eina_Bool
_eo_objects_iterator_next(Eina_Iterator *it, void **data)
{
Table_Index mid_table_id, table_id, entry_id;
Eo_Id_Table_Data *tdata;
_Eo_Objects_Iterator *eo_it = (_Eo_Objects_Iterator *)it;
if (!eo_it->tdata) return EINA_FALSE;
tdata = eo_it->tdata;
mid_table_id = eo_it->mid_table_id;
table_id = eo_it->table_id;
entry_id = eo_it->entry_id;
while (mid_table_id < MAX_MID_TABLE_ID)
{
if (tdata->eo_ids_tables[mid_table_id])
{
while (table_id < MAX_TABLE_ID)
{
if (TABLE_FROM_IDS)
{
while (entry_id < MAX_ENTRY_ID)
{
_Eo_Id_Entry *entry = &(TABLE_FROM_IDS->entries[entry_id]);
if (entry->active)
{
Eo *obj = _eo_header_id_get((Eo_Header *) entry->ptr);
*data = obj;
eo_it->mid_table_id = mid_table_id;
eo_it->table_id = table_id;
eo_it->entry_id = entry_id + 1;
return EINA_TRUE;
}
entry_id++;
}
entry_id = 0;
}
table_id++;
}
table_id = 0;
}
mid_table_id++;
}
return EINA_FALSE;
}
static void
_eo_objects_iterator_free(Eina_Iterator *it)
{
EINA_MAGIC_SET(it, EINA_MAGIC_NONE);
free(it);
}
EAPI Eina_Iterator *
eo_objects_iterator_new(void)
{
_Eo_Objects_Iterator *it;
Eo_Id_Table_Data *tdata = _eo_table_data_table_get(_eo_table_data_get(), EFL_ID_DOMAIN_MAIN);
if (!tdata) return NULL;
it = calloc(1, sizeof (*it));
if (!it) return NULL;
it->tdata = tdata;
it->iterator.version = EINA_ITERATOR_VERSION;
it->iterator.next = _eo_objects_iterator_next;
it->iterator.free = _eo_objects_iterator_free;
EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
return (Eina_Iterator *)it;
}