forked from enlightenment/efl
313 lines
7.8 KiB
C
313 lines
7.8 KiB
C
/* 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/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_VALGRIND
|
|
# include <valgrind.h>
|
|
# include <memcheck.h>
|
|
#endif
|
|
|
|
#include "Eina.h"
|
|
#include "eina_evlog.h"
|
|
#include "eina_debug.h"
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
# include <mach/mach_time.h>
|
|
#endif
|
|
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#ifdef _WIN32
|
|
# include <evil_private.h> /* mmap */
|
|
#else
|
|
# 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;
|
|
static int _evlog_go = 0;
|
|
|
|
static Eina_Evlog_Buf *buf; // current event log we are writing events to
|
|
static Eina_Evlog_Buf buffers[2]; // double-buffer our event log buffers
|
|
|
|
#if defined (HAVE_CLOCK_GETTIME)
|
|
static clockid_t _eina_evlog_time_clock_id = -1;
|
|
#elif defined(__APPLE__) && defined(__MACH__)
|
|
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)
|
|
{
|
|
#if defined (HAVE_CLOCK_GETTIME)
|
|
struct timespec t;
|
|
|
|
if (EINA_UNLIKELY(clock_gettime(_eina_evlog_time_clock_id, &t)))
|
|
{
|
|
struct timeval timev;
|
|
gettimeofday(&timev, NULL);
|
|
return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
|
|
}
|
|
return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0);
|
|
#elif defined(_WIN32)
|
|
return evil_time_get();
|
|
#elif defined(__APPLE__) && defined(__MACH__)
|
|
return _eina_evlog_time_clock_conversion * (double)mach_absolute_time();
|
|
#else
|
|
struct timeval timev;
|
|
gettimeofday(&timev, NULL);
|
|
return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
alloc_buf(Eina_Evlog_Buf *b, unsigned int size)
|
|
{
|
|
if (b->buf) return;
|
|
b->size = size;
|
|
b->top = 0;
|
|
#ifdef HAVE_MMAP
|
|
# ifdef HAVE_VALGRIND
|
|
if (RUNNING_ON_VALGRIND) b->buf = malloc(size);
|
|
else
|
|
# endif
|
|
{
|
|
b->buf = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
|
MAP_PRIVATE | MAP_ANON, -1, 0);
|
|
if (b->buf == MAP_FAILED) b->buf = NULL;
|
|
}
|
|
#else
|
|
b->buf = malloc(size);
|
|
#endif
|
|
b->overflow = 0;
|
|
}
|
|
|
|
static void
|
|
free_buf(Eina_Evlog_Buf *b)
|
|
{
|
|
if (!b->buf) return;
|
|
#ifdef HAVE_MMAP
|
|
# ifdef HAVE_VALGRIND
|
|
if (RUNNING_ON_VALGRIND) free(b->buf);
|
|
else
|
|
# endif
|
|
munmap(b->buf, b->size);
|
|
#else
|
|
free(b->buf);
|
|
#endif
|
|
b->buf = NULL;
|
|
b->size = 0;
|
|
b->top = 0;
|
|
}
|
|
|
|
static inline void *
|
|
push_buf(Eina_Evlog_Buf *b, unsigned int size)
|
|
{
|
|
void *ptr;
|
|
|
|
if (b->size < size) abort();
|
|
if ((b->top + size) > (b->size))
|
|
{
|
|
b->overflow++;
|
|
b->top = 0;
|
|
}
|
|
ptr = (b->buf + b->top);
|
|
b->top += size;
|
|
return ptr;
|
|
}
|
|
|
|
EAPI void
|
|
eina_evlog(const char *event, void *obj, double srctime, const char *detail)
|
|
{
|
|
Eina_Evlog_Item *item;
|
|
char *strings;
|
|
double now;
|
|
int size;
|
|
unsigned short detail_offset, event_size;
|
|
|
|
if (!_evlog_go) return;
|
|
now = get_time();
|
|
event_size = strlen(event) + 1;
|
|
size = sizeof(Eina_Evlog_Item) + event_size;
|
|
detail_offset = 0;
|
|
if (detail)
|
|
{
|
|
detail_offset = size;
|
|
size += strlen(detail) + 1;
|
|
}
|
|
size = sizeof(double) * ((size + sizeof(double) - 1)
|
|
/ sizeof(double));
|
|
eina_spinlock_take(&_evlog_lock);
|
|
strings = push_buf(buf, size);
|
|
item = (Eina_Evlog_Item *)strings;
|
|
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);
|
|
}
|
|
|
|
EAPI Eina_Evlog_Buf *
|
|
eina_evlog_steal(void)
|
|
{
|
|
Eina_Evlog_Buf *stolen = NULL;
|
|
|
|
eina_spinlock_take(&_evlog_lock);
|
|
if (buf == &(buffers[0]))
|
|
{
|
|
buf = &(buffers[1]);
|
|
buf->top = 0;
|
|
buf->overflow = 0;
|
|
stolen = &(buffers[0]);
|
|
}
|
|
else
|
|
{
|
|
buf = &(buffers[0]);
|
|
buf->top = 0;
|
|
buf->overflow = 0;
|
|
stolen = &(buffers[1]);
|
|
}
|
|
eina_spinlock_release(&_evlog_lock);
|
|
return stolen;
|
|
}
|
|
|
|
EAPI void
|
|
eina_evlog_start(void)
|
|
{
|
|
eina_spinlock_take(&_evlog_lock);
|
|
_evlog_go++;
|
|
if (_evlog_go == 1)
|
|
{
|
|
// alloc 2 buffers for spinning around in
|
|
alloc_buf(&(buffers[0]), EVLOG_BUF_SIZE);
|
|
alloc_buf(&(buffers[1]), EVLOG_BUF_SIZE);
|
|
}
|
|
eina_spinlock_release(&_evlog_lock);
|
|
}
|
|
|
|
EAPI void
|
|
eina_evlog_stop(void)
|
|
{
|
|
eina_spinlock_take(&_evlog_lock);
|
|
_evlog_go--;
|
|
if (_evlog_go == 0)
|
|
{
|
|
free_buf(&(buffers[0]));
|
|
free_buf(&(buffers[1]));
|
|
}
|
|
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)
|
|
{
|
|
eina_spinlock_new(&_evlog_lock);
|
|
buf = &(buffers[0]);
|
|
#if defined (HAVE_CLOCK_GETTIME)
|
|
{
|
|
struct timespec t;
|
|
|
|
if (!clock_gettime(CLOCK_MONOTONIC, &t))
|
|
_eina_evlog_time_clock_id = CLOCK_MONOTONIC;
|
|
else
|
|
_eina_evlog_time_clock_id = CLOCK_REALTIME;
|
|
}
|
|
#endif
|
|
eina_evlog("+eina_init", NULL, 0.0, NULL);
|
|
eina_debug_opcodes_register(NULL, _EINA_DEBUG_EVLOG_OPS(), NULL, NULL);
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
Eina_Bool
|
|
eina_evlog_shutdown(void)
|
|
{
|
|
// yes - we don't free tyhe evlog buffers. they may be in used by debug th
|
|
eina_spinlock_free(&_evlog_lock);
|
|
return EINA_TRUE;
|
|
}
|