#ifndef __UTIL_H #define __UTIL_H #include #include #include #include #include #include #include #include void *xrealloc(void *ptr, size_t size); void *xmalloc(size_t size); typedef void (cleanup_handler) (void * /* context */ , int /* is fatal? */ ); /* set from 0 - 5 depending on what level of verbosity you want */ extern int log_level; /* set up the error globals */ void error_init(void); /* some context for the overall process that appears on each log line */ extern char *log_context; void exit_err(const char *); /* error_set_handler must be a macro not a function due to setjmp stack rules */ #include struct error_handler_context { jmp_buf jmp; cleanup_handler *handler; void *data; }; #define DECLARE_ERROR_CONTEXT(name) \ struct error_handler_context *name = (struct error_handler_context*) \ pthread_getspecific(cleanup_handler_key); /* clean up with the given function & data when error_handler() is invoked, * non-fatal errors will also return here (if that's dangerous, use fatal() * instead of error()). * * error handlers are thread-local, so you need to call this when starting a * new thread. */ extern pthread_key_t cleanup_handler_key; #define error_set_handler(cleanfn, cleandata) \ { \ DECLARE_ERROR_CONTEXT(old); \ struct error_handler_context *context = \ xmalloc(sizeof(struct error_handler_context)); \ context->handler = (cleanfn); \ context->data = (cleandata); \ \ switch (setjmp(context->jmp)) \ { \ case 0: /* setup time */ \ if (old) { free(old); }\ if( EINVAL == pthread_setspecific(cleanup_handler_key, context) ) { \ fprintf(stderr, "Tried to set an error handler at %s:%d" \ " without calling error_init().\n", \ __FILE__, __LINE__ );\ abort();\ }\ break; \ case 1: /* fatal error, terminate process */ \ debug( "Fatal error in thread %p", pthread_self() ); \ abort(); \ /* abort() can't return, so we can't fall through */ \ case 2: /* non-fatal error, call cleanup and terminate thread */ \ debug( "Error in thread %p", pthread_self() ); \ context->handler(context->data, 0); \ pthread_exit((void*) 1);\ /* pthread_exit() can't return, so we can't fall through * */\ default: \ abort(); \ } \ } /* invoke the error handler - longjmps away, don't use directly */ void error_handler(int fatal); /* mylog a line at the given level (0 being most verbose) */ void mylog(int line_level, const char *format, ...); /* Returns the current time, in milliseconds, from CLOCK_MONOTONIC */ uint64_t monotonic_time_ms(void); #define levstr(i) (i==0?'D':(i==1?'I':(i==2?'W':(i==3?'E':'F')))) #define myloglev(level, msg, ...) mylog( level, "%"PRIu64":%c:%d %p %s %s:%d: "msg"\n", monotonic_time_ms(), levstr(level), getpid(),pthread_self(), log_context, __FILE__, __LINE__, ##__VA_ARGS__ ) #ifdef DEBUG #define debug(msg, ...) myloglev(0, msg, ##__VA_ARGS__) #else #define debug(msg, ...) /* no-op */ #endif /* informational message, not expected to be compiled out */ #define info(msg, ...) myloglev(1, msg, ##__VA_ARGS__) /* messages that might indicate a problem */ #define warn(msg, ...) myloglev(2, msg, ##__VA_ARGS__) /* mylog a message and invoke the error handler to recover */ #define error(msg, ...) do { \ myloglev(3, msg, ##__VA_ARGS__); \ error_handler(0); \ } while(0) /* mylog a message and invoke the error handler to kill the current thread */ #define fatal(msg, ...) do { \ myloglev(4, msg, ##__VA_ARGS__); \ error_handler(1); \ exit(1); /* never-reached, this is to make static code analizer happy */ \ } while(0) #define ERROR_IF( test, msg, ... ) do { if ((test)) { error(msg, ##__VA_ARGS__); } } while(0) #define FATAL_IF( test, msg, ... ) do { if ((test)) { fatal(msg, ##__VA_ARGS__); } } while(0) #define ERROR_UNLESS( test, msg, ... ) ERROR_IF( !(test), msg, ##__VA_ARGS__ ) #define FATAL_UNLESS( test, msg, ... ) FATAL_IF( !(test), msg, ##__VA_ARGS__ ) #define ERROR_IF_NULL(value, msg, ...) \ ERROR_IF( NULL == value, msg " (errno=%d, %s)", ##__VA_ARGS__, errno, strerror(errno) ) #define FATAL_IF_NULL(value, msg, ...) \ FATAL_IF( NULL == value, msg " (errno=%d, %s)", ##__VA_ARGS__, errno, strerror(errno) ) #define ERROR_IF_NEGATIVE( value, msg, ... ) ERROR_IF( value < 0, msg, ##__VA_ARGS__ ) #define FATAL_IF_NEGATIVE( value, msg, ... ) FATAL_IF( value < 0, msg, ##__VA_ARGS__ ) #define ERROR_IF_ZERO( value, msg, ... ) ERROR_IF( 0 == value, msg, ##__VA_ARGS__ ) #define FATAL_IF_ZERO( value, msg, ... ) FATAL_IF( 0 == value, msg, ##__VA_ARGS__ ) #define ERROR_UNLESS_NULL(value, msg, ...) \ ERROR_UNLESS( NULL == value, msg " (errno=%d, %s)", ##__VA_ARGS__, errno, strerror(errno) ) #define FATAL_UNLESS_NULL(value, msg, ...) \ FATAL_UNLESS( NULL == value, msg " (errno=%d, %s)", ##__VA_ARGS__, errno, strerror(errno) ) #define ERROR_UNLESS_NEGATIVE( value, msg, ... ) ERROR_UNLESS( value < 0, msg, ##__VA_ARGS__ ) #define FATAL_UNLESS_NEGATIVE( value, msg, ... ) FATAL_UNLESS( value < 0, msg, ##__VA_ARGS__ ) #define ERROR_UNLESS_ZERO( value, msg, ... ) ERROR_UNLESS( 0 == value, msg, ##__VA_ARGS__ ) #define FATAL_UNLESS_ZERO( value, msg, ... ) FATAL_UNLESS( 0 == value, msg, ##__VA_ARGS__ ) #define NULLCHECK(value) FATAL_IF_NULL(value, "BUG: " #value " is null") #define SHOW_ERRNO( msg, ... ) msg ": %s (%i)", ##__VA_ARGS__, ( errno == 0 ? "EOF" : strerror(errno) ), errno #define WARN_IF_NEGATIVE( value, msg, ... ) if ( value < 0 ) { warn( msg, ##__VA_ARGS__ ); } #endif