#ifndef _EVENT_DEBUG_H_
#define _EVENT_DEBUG_H_
#ifdef EVENT_LIB_DEBUG
#include "dhash.h"
#define print(fmt,...) \
PerlIO_printf(PerlIO_stderr(), fmt, __VA_ARGS__)
#define print_(fmt,...) \
{ \
print("________________________________________________________________________\n", NULL);\
print(fmt"\n", __VA_ARGS__); \
print("------------------------------------------------------------------------\n", NULL);\
}
#define ENV(var) (getenv(var) && atoi(getenv(var)))
#define DEBUG_warn(...) \
if (ENV("EVENT_LIB_DEBUG_DESTROY")) \
print_(__VA_ARGS__)
dhash_t EVENTS = { 0, 0, NULL }; /* record pending events */
dhash_t ALLO = { 0, 0, NULL }; /* record allocation and destruction of events */
int EVENT_NEW_COUNT = 0,
SIGNAL_NEW_COUNT = 0,
TIMER_NEW_COUNT = 0;
#define DEBUG_init_count(...) EVENT_NEW_COUNT = SIGNAL_NEW_COUNT = TIMER_NEW_COUNT = 0
#define DEBUG_inc_count(var) var++
void DEBUG_init_pending (pTHX) {
if (ENV("EVENT_LIB_DEBUG_PENDING")) {
dhash_init(&EVENTS);
}
if (ENV("EVENT_LIB_DEBUG_ALLOCS") && ALLO.size == 0) {
dhash_init(&ALLO);
}
}
#define DUMP_one_event(i) \
{ \
print("%i:\n", i); \
print(" flags: %i\n", EVENTS.ary[i].flags); \
print(" event: 0x%p\n", EVENTS.ary[i].ev); \
}
#define DUMP_dhash(ev) \
{ \
if (ENV("EVENT_LIB_DEBUG_DHASH")) {\
int i; \
print("__________\n", NULL);\
print("%s for 0x%p\n", __FUNCTION__, ev); \
print("size:%i count:%i\n", EVENTS.size, EVENTS.count);\
for (i = 0; i < EVENTS.size; i++) \
DUMP_one_event(i); \
print("----------\n", NULL);\
}\
}
void DEBUG_record_event (pTHX_ SV *ev) {
register int i;
struct event_args *args = (struct event_args*)SvIV(SvRV(ev));
dhash_val_t val = EMPTY;
if (!ENV("EVENT_LIB_DEBUG_PENDING")) {
return;
}
DUMP_dhash(args);
/* barf when an event to be added already is in the hash.
* Exception is when in callback to allow something like this:
* sub handler { my $ev; $ev->add }
*/
if (!IN_CALLBACK)
assert(dhash_find(&EVENTS, args) == NULL);
if (dhash_find(&EVENTS, args)) {
print("not adding element 0x%p again\n", args);
goto done;
}
/* prepare the type of event */
if (sv_derived_from(ev, "Event::Lib::timer"))
val.flags |= EV_TIMEOUT;
else if (sv_derived_from(ev, "Event::Lib::signal"))
val.flags |= EV_SIGNAL|EV_PERSIST;
else /* fh-event */
val.flags = args->ev.ev_events;
val.ev = args;
dhash_store(&EVENTS, val);
done:
DUMP_dhash(args);
}
void DEBUG_record_allo (pTHX_ SV *ev) {
register int i;
struct event_args *args = (struct event_args*)SvIV(SvRV(ev));
dhash_val_t val = EMPTY;
if (!ENV("EVENT_LIB_DEBUG_ALLOCS")) {
return;
}
/* barf when an event to be added already is in the hash.
* Exception is when in callback to allow something like this:
* sub handler { my $ev; $ev->add }
*/
if (!IN_CALLBACK)
assert(dhash_find(&ALLO, args) == NULL);
if (dhash_find(&ALLO, args)) {
print("not adding element 0x%p again\n", args);
return;
}
/* prepare the type of event */
if (sv_derived_from(ev, "Event::Lib::timer"))
val.flags |= EV_TIMEOUT;
else if (sv_derived_from(ev, "Event::Lib::signal"))
val.flags |= EV_SIGNAL|EV_PERSIST;
else /* fh-event */
val.flags = args->evtype;
val.ev = args;
dhash_store(&ALLO, val);
}
void DEBUG_delete_event (pTHX_ SV *ev) {
struct event_args *args = (struct event_args*)SvIV(SvRV(ev));
SV *recref;
if (!ENV("EVENT_LIB_DEBUG_PENDING")) {
return;
}
DUMP_dhash(args);
/* an event not yet set has never been added to EVENT
* so don't even try to delete it */
if (!EvEVENT_SET(args) && !dhash_find(&EVENTS, args)) {
print("Attempt to delete non-set event SV = 0x%x with event at 0x%x caught\n", ev, args);
return;
}
assert(dhash_find(&EVENTS, args));
dhash_delete(&EVENTS, args);
DUMP_dhash(args);
}
void DEBUG_delete_allo (pTHX_ SV *ev) {
struct event_args *args = (struct event_args*)SvIV(SvRV(ev));
SV *recref;
if (!ENV("EVENT_LIB_DEBUG_ALLOCS")) {
return;
}
assert(dhash_find(&ALLO, args));
dhash_delete(&ALLO, args);
}
#define DEBUG_get_pending_events(...) \
int i, j; \
if (!ENV("EVENT_LIB_DEBUG_PENDING")) { \
XSRETURN_EMPTY; \
} \
EXTEND(SP, EVENTS.count); \
for (i = 0, j = 0; i <= EVENTS.size; i++) { \
if (!EVENTS.ary[i].ev) \
continue; \
ST(j) = (SV*)EVENTS.ary[i].ev->ev.ev_arg; \
j++; \
} \
XSRETURN(EVENTS.count) \
void DEBUG_dump_pending_events (pTHX) {
register int i;
if (!ENV("EVENT_LIB_DEBUG_PENDING")) {
return;
}
if (EVENTS.count == 0) {
print("No pending events\n", NULL);
return;
}
for (i = 0; i < EVENTS.size; i++) {
int type;
struct event_args *ev;
if (!EVENTS.ary[i].ev)
continue;
ev = EVENTS.ary[i].ev;
type = EVENTS.ary[i].flags;
print("EV = 0x%p\n", ev);
print(" type = ", NULL);
if (type & EV_PERSIST) {
print("EV_PERSIST | ", NULL);
type &= ~EV_PERSIST;
}
if (type & EV_TIMEOUT) print("EV_TIMEOUT", NULL);
else if (type & EV_SIGNAL) print("EV_SIGNAL", NULL);
else if (type & EV_READ) print("EV_READ", NULL);
else if (type & EV_WRITE) print("EV_WRITE", NULL);
else if (type & EV_READ &&
type & EV_WRITE) print("EV_READ | EV_WRITE", NULL);
if (type & 0x20) print(" | EVf_EVENT_TRACED", NULL);
print("\n", NULL);
print(" args = %i\n", ev->num);
print(" refcnt = %i\n", SvREFCNT((SV*)ev->ev.ev_arg));
print(" cback = %s\n", ev->cbname);
if (type & (EV_READ|EV_WRITE))
print(" fhid = %s from %s:%i (fd=%i)\n",
GvNAME((GV*)SvRV(ev->io)),
GvFILE((GV*)SvRV(ev->io)),
GvLINE((GV*)SvRV(ev->io)),
ev->ev.ev_fd);
}
print("----------------------------------------------------------------------------\n", NULL);
print("total: %i events still pending\n", EVENTS.count);
}
void DEBUG_dump_allocated (pTHX) {
register int i;
if (!ENV("EVENT_LIB_DEBUG_ALLOCS")) {
return;
}
if (ALLO.count == 0) {
print("No allocated events\n", NULL);
return;
}
for (i = 0; i < ALLO.size; i++) {
int type;
struct event_args *ev;
if (!ALLO.ary[i].ev)
continue;
ev = ALLO.ary[i].ev;
type = ALLO.ary[i].flags;
print("EV = 0x%p\n", ev);
print(" type = ", NULL);
if (type & EV_PERSIST) {
print("EV_PERSIST | ", NULL);
type &= ~EV_PERSIST;
}
if (type & EV_TIMEOUT) print("EV_TIMEOUT", NULL);
else if (type & EV_SIGNAL) print("EV_SIGNAL", NULL);
else if (type & EV_READ) print("EV_READ", NULL);
else if (type & EV_WRITE) print("EV_WRITE", NULL);
else if (type & EV_READ &&
type & EV_WRITE) print("EV_READ | EV_WRITE", NULL);
if (type & 0x20) print(" | EVf_EVENT_TRACED", NULL);
print("\n", NULL);
print(" args = %i\n", ev->num);
print(" cback = %s\n", ev->cbname);
print(" loc = %s\n", SvPV_nolen(ev->loc));
if (type & (EV_READ|EV_WRITE))
print(" fhid = %s from %s:%i (fd=%i)\n",
GvNAME((GV*)SvRV(ev->io)),
GvFILE((GV*)SvRV(ev->io)),
GvLINE((GV*)SvRV(ev->io)),
ev->ev.ev_fd);
}
print("----------------------------------------------------------------------------\n", NULL);
print("total: %i events still allocated\n", ALLO.count);
}
#define DEBUG_trace(e) \
{ \
if (EvEVENT_TRACED(e)) { \
print("________________________________________________________________________\n", NULL); \
print("EV = 0x%p (%s) (%s)\n (from %s)\n touched at %s (%i)\n (by %s:%d)\n", \
e, e->type, e->cbname, SvPV_nolen(e->loc), __FUNCTION__, __LINE__, \
CopFILE(PL_curcop) ? CopFILE(PL_curcop) : "unknown", \
CopLINE(PL_curcop) ? CopLINE(PL_curcop) : -1); \
if (EvEVENT_SET(e)) { \
sv_dump((SV*)e->ev.ev_arg); \
if (event_initialized(&e->ev) && SvOK((SV*)e->ev.ev_arg)) \
sv_dump((SV*)SvRV((SV*)e->ev.ev_arg)); \
}\
print ("------------------------------------------------------------------------\n", NULL); \
} \
}
void DEBUG_store_location (pTHX_ struct event_args* args) {
New(0, args->cbname, strlen(HvNAME(GvSTASH(CvGV(args->func)))) +
strlen("::") +
strlen(GvNAME(CvGV(args->func))) + 1, char);
sprintf(args->cbname, "%s::%s", HvNAME(GvSTASH(CvGV(args->func))), GvNAME(CvGV(args->func)));
if (CopFILE(PL_curcop) && CopLINE(PL_curcop))
args->loc = newSVpvf("%s:%d", CopFILE(PL_curcop), CopLINE(PL_curcop));
else
args->loc = newSVpv("unknown location", 0);
}
#else
# define DEBUG_init_count(...)
# define DEBUG_inc_count(...)
# define DEBUG_warn(...)
# define DEBUG_enter_callback(...)
# define DEBUG_leave_callback(...)
# define DEBUG_init_pending(...)
# define DEBUG_record_event(...)
# define DEBUG_record_allo(...)
# define DEBUG_delete_event(...)
# define DEBUG_delete_allo(...)
# define DEBUG_get_pending_events(...)
# define DEBUG_dump_pending_events(...)
# define DEBUG_dump_allocated(...)
# define DEBUG_trace(...)
# define DEBUG_store_location(...)
# define EVENT_NEW_COUNT -1
# define SIGNAL_NEW_COUNT -1
# define TIMER_NEW_COUNT -1
#endif
#endif /* _EVENT_DEBUG_H_ */