re/src/dbg/dbg.c
2011-10-26 07:08:26 +00:00

328 lines
5.2 KiB
C

/**
* @file dbg.c Debug printing
*
* Copyright (C) 2010 Creytiv.com
*/
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_PTHREAD
#include <stdlib.h>
#include <pthread.h>
#endif
#include <time.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_list.h>
#include <re_tmr.h>
#define DEBUG_MODULE "dbg"
#define DEBUG_LEVEL 0
#include <re_dbg.h>
/** Debug configuration */
static struct {
uint64_t tick; /**< Init ticks */
int level; /**< Current debug level */
enum dbg_flags flags; /**< Debug flags */
dbg_print_h *ph; /**< Optional print handler */
void *arg; /**< Handler argument */
FILE *f; /**< Logfile */
#ifdef HAVE_PTHREAD
pthread_mutex_t mutex; /**< Thread locking */
#endif
} dbg = {
0,
DBG_INFO,
DBG_ANSI,
NULL,
NULL,
NULL,
#ifdef HAVE_PTHREAD
PTHREAD_MUTEX_INITIALIZER,
#endif
};
#ifdef HAVE_PTHREAD
static inline void dbg_lock(void)
{
pthread_mutex_lock(&dbg.mutex);
}
static inline void dbg_unlock(void)
{
pthread_mutex_unlock(&dbg.mutex);
}
#else
#define dbg_lock() /**< Stub */
#define dbg_unlock() /**< Stub */
#endif
/**
* Initialise debug printing
*
* @param level Debug level
* @param flags Debug flags
*/
void dbg_init(int level, enum dbg_flags flags)
{
dbg.tick = tmr_jiffies();
dbg.level = level;
dbg.flags = flags;
}
/**
* Close debugging
*/
void dbg_close(void)
{
if (dbg.f) {
(void)fclose(dbg.f);
dbg.f = NULL;
}
}
/**
* Set debug logfile
*
* @param name Name of the logfile, NULL to close
*
* @return 0 if success, otherwise errorcode
*/
int dbg_logfile_set(const char *name)
{
time_t t;
dbg_close();
if (!name)
return 0;
dbg.f = fopen(name, "a+");
if (!dbg.f)
return errno;
(void)time(&t);
(void)re_fprintf(dbg.f, "\n===== Log Started: %s", ctime(&t));
(void)fflush(dbg.f);
return 0;
}
/**
* Set optional debug print handler
*
* @param ph Print handler
* @param arg Handler argument
*/
void dbg_handler_set(dbg_print_h *ph, void *arg)
{
dbg.ph = ph;
dbg.arg = arg;
}
/* NOTE: This function should not allocate memory */
static void dbg_vprintf(int level, const char *fmt, va_list ap)
{
if (level > dbg.level)
return;
/* Print handler? */
if (dbg.ph)
return;
dbg_lock();
if (dbg.flags & DBG_ANSI) {
switch (level) {
case DBG_WARNING:
(void)re_fprintf(stderr, "\x1b[31m"); /* Red */
break;
case DBG_NOTICE:
(void)re_fprintf(stderr, "\x1b[33m"); /* Yellow */
break;
case DBG_INFO:
(void)re_fprintf(stderr, "\x1b[32m"); /* Green */
break;
default:
break;
}
}
if (dbg.flags & DBG_TIME) {
const uint64_t ticks = tmr_jiffies();
if (0 == dbg.tick)
dbg.tick = tmr_jiffies();
(void)re_fprintf(stderr, "[%09llu] ", ticks - dbg.tick);
}
(void)re_vfprintf(stderr, fmt, ap);
if (dbg.flags & DBG_ANSI && level < DBG_DEBUG)
(void)re_fprintf(stderr, "\x1b[;m");
dbg_unlock();
}
/* Formatted output to print handler and/or logfile */
static void dbg_fmt_vprintf(int level, const char *fmt, va_list ap)
{
char buf[256];
int len;
if (level > dbg.level)
return;
if (!dbg.ph && !dbg.f)
return;
dbg_lock();
len = re_vsnprintf(buf, sizeof(buf), fmt, ap);
if (len <= 0)
goto out;
/* Print handler? */
if (dbg.ph) {
dbg.ph(level, buf, len, dbg.arg);
}
/* Output to file */
if (dbg.f) {
if (fwrite(buf, 1, len, dbg.f) > 0)
(void)fflush(dbg.f);
}
out:
dbg_unlock();
}
/**
* Print a formatted debug message
*
* @param level Debug level
* @param fmt Formatted string
*/
void dbg_printf(int level, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
dbg_vprintf(level, fmt, ap);
va_end(ap);
va_start(ap, fmt);
dbg_fmt_vprintf(level, fmt, ap);
va_end(ap);
}
/**
* Print a formatted debug message to /dev/null
*
* @param fmt Formatted string
*/
void dbg_noprintf(const char *fmt, ...)
{
(void)fmt;
}
/**
* Print a formatted warning message
*
* @param fmt Formatted string
*/
void dbg_warning(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
dbg_vprintf(DBG_WARNING, fmt, ap);
va_end(ap);
va_start(ap, fmt);
dbg_fmt_vprintf(DBG_WARNING, fmt, ap);
va_end(ap);
}
/**
* Print a formatted notice message
*
* @param fmt Formatted string
*/
void dbg_notice(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
dbg_vprintf(DBG_NOTICE, fmt, ap);
va_end(ap);
va_start(ap, fmt);
dbg_fmt_vprintf(DBG_NOTICE, fmt, ap);
va_end(ap);
}
/**
* Print a formatted info message
*
* @param fmt Formatted string
*/
void dbg_info(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
dbg_vprintf(DBG_INFO, fmt, ap);
va_end(ap);
va_start(ap, fmt);
dbg_fmt_vprintf(DBG_INFO, fmt, ap);
va_end(ap);
}
/**
* Get the name of the debug level
*
* @param level Debug level
*
* @return String with debug level name
*/
const char *dbg_level_str(int level)
{
switch (level) {
case DBG_EMERG: return "EMERGENCY";
case DBG_ALERT: return "ALERT";
case DBG_CRIT: return "CRITICAL";
case DBG_ERR: return "ERROR";
case DBG_WARNING: return "WARNING";
case DBG_NOTICE: return "NOTICE";
case DBG_INFO: return "INFO";
case DBG_DEBUG: return "DEBUG";
default: return "???";
}
}