| /** |
| * @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 "???"; |
| } |
| } |