/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set et sw=2 ts=2: */ /*************************************************************************** * debug.c * * Thu Nov 1 13:38:47 CET 2012 * Copyright 2012 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of Debug Module. * * Debug Module is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Debug Module 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Debug Module; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "debug.h" #include #include #include #include #include #include #include #include #ifdef WITH_DBG_MUTEX #include #endif struct { unsigned int flags; #ifdef WITH_DBG_MUTEX pthread_mutex_t mutex; #endif int fd; int file_fd; } dbg_config = { .flags = DBG_FLAG_DEFAULT, .fd = -1, .file_fd = -1 }; static void dbg_mutex_init() { #ifdef WITH_DBG_MUTEX if(dbg_config.flags & DBG_FLAG_USE_MUTEX) { pthread_mutex_init(&dbg_config.mutex, NULL); } #endif } static void dbg_mutex_lock() { #ifdef WITH_DBG_MUTEX if(dbg_config.flags & DBG_FLAG_USE_MUTEX) { pthread_mutex_lock(&dbg_config.mutex); } #endif } static void dbg_mutex_unlock() { #ifdef WITH_DBG_MUTEX if(dbg_config.flags & DBG_FLAG_USE_MUTEX) { pthread_mutex_unlock(&dbg_config.mutex); } #endif } static void dbg_mutex_close() { #ifdef WITH_DBG_MUTEX if(dbg_config.flags & DBG_FLAG_USE_MUTEX) { // Make sure we don't destroy the mutex while another thread is using it. dbg_mutex_lock(); dbg_mutex_unlock(); pthread_mutex_destroy(&dbg_config.mutex); } #endif } dbg_status_t dbg_init(unsigned int flags, ...) { dbg_status_t status = DBG_STATUS_OK; dbg_config.flags = flags; int end = 0; va_list vl; va_start(vl, flags); while(!end) { int option = va_arg(vl, int); switch(option) { case DBG_OPTION_END: end = 1; break; case DBG_OPTION_FD: dbg_config.fd = va_arg(vl, int); break; case DBG_OPTION_FILENAME: if(dbg_config.flags & DBG_FLAG_OUTPUT_TO_FILE) { const char *filename = (const char*)va_arg(vl, char*); dbg_config.file_fd = open(filename, O_CREAT | O_RDWR, 0777); } break; default: status = DBG_STATUS_UNKNOWN_OPTION; printf("option: %x\n", option); goto err; } } dbg_mutex_init(); err: va_end(vl); return status; } void dbg_close() { if(dbg_config.flags & DBG_FLAG_OUTPUT_TO_FILE) { if(dbg_config.file_fd != -1) close(dbg_config.file_fd); } dbg_mutex_close(); } static const char * const debug_class_str[] = { "fixme", "err", "warn", "info", "debug" }; static int dbg_create_header(char *hdr, size_t size) { time_t rawtime = time(NULL); struct tm t; localtime_r(&rawtime, &t); return snprintf(hdr, size, "%d-%02d-%02d %02d:%02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); } static int dbg_output_fd(int fd, const char *msg) { if(fd == -1) return 1; char hdr[32]; dbg_create_header(hdr, sizeof(hdr)); write(fd, hdr, strlen(hdr)); write(fd, " ", 1); write(fd, msg, strlen(msg)); if(msg[strlen(msg) - 1] != '\n') write(fd, "\n", 1); return 0; } int __debug(const char *func, const int line, const enum __debug_class cl, const char *ch, const char *fmt, ...) { int result = 0; dbg_mutex_lock(); // // Generate message // char buf[1024]; int sz = snprintf(buf, sizeof(buf), "%s:%s:%s:%d ", debug_class_str[(unsigned)cl], ch, func, line); va_list va; va_start(va, fmt); sz += vsnprintf(buf + sz, sizeof(buf) - sz, fmt, va); va_end(va); // // Send message to output // if(dbg_config.flags & DBG_FLAG_OUTPUT_TO_STDOUT) { dbg_output_fd(STDOUT_FILENO, buf); } if(dbg_config.flags & DBG_FLAG_OUTPUT_TO_STDERR) { dbg_output_fd(STDERR_FILENO, buf); } if(dbg_config.flags & DBG_FLAG_OUTPUT_TO_FD) { dbg_output_fd(dbg_config.fd, buf); } if(dbg_config.flags & DBG_FLAG_OUTPUT_TO_FILE) { dbg_output_fd(dbg_config.file_fd, buf); } done: dbg_mutex_unlock(); return result; } #if 0 #include #include #include //#include #include #include //#include "mutex.h" //static Mutex mutex; static unsigned int gettid() { return 0;//(unsigned int)pthread_self(); } static FILE *logfp = stderr; #define NELEM(x) (sizeof(x)/sizeof((x)[0])) struct __debug_channel { char name[32]; unsigned flags; }; static const char * const debug_class_str[] = { "fixme", "err", "warn", "info", "debug" }; #define __DEBUG_CHANNEL_MAX 256 static struct __debug_channel debug_channel[__DEBUG_CHANNEL_MAX]; static unsigned n_debug_channel = 0; static unsigned debug_flags = (1 << __class_err) | (1 << __class_fixme); static int __debug_enabled(const enum __debug_class cl, const char *ch) { unsigned i; for(i = 0; i < n_debug_channel; i++) { if(!strcmp(ch, debug_channel[i].name)) { return (debug_channel[i].flags & (1 << cl)) != 0; } } return debug_flags & (1 << cl); } int __debug(const char *func, const int line, const enum __debug_class cl, const char *ch, const char *fmt, ...) { time_t rawtime = time(NULL); struct tm *t = localtime(&rawtime); // MutexAutolock m(mutex); int ret = 0; if(__debug_enabled(cl, ch)) { if((unsigned)cl < NELEM(debug_class_str)) ret += fprintf(logfp, "%d-%02d-%02d %02d:%02d:%02d %u %s:%s:%s:%d ", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, gettid(), debug_class_str[(unsigned)cl], ch, func, line); if(fmt) { va_list va; va_start(va, fmt); ret += vfprintf(logfp, fmt, va); va_end(va); } } if(ret){ fflush(logfp); } return ret; } void debug_init(FILE *fp) { // mutex.name = "debug"; // MutexAutolock m(mutex); logfp = fp; } /* * fmt := [set[,set]*]* * set := [+-]channel * | class[+-]channel * | [+-]all */ void debug_parse(const char *fmt) { // MutexAutolock m(mutex); char *s; char *next; char *opt; if(!(s = strdup(fmt))) return; for(opt = s; opt; opt = next) { int set = 0; int clr = 0; unsigned i; if((next = strchr(opt, ','))) *next++ = '\0'; char *p = opt + strcspn(opt, "+-"); if(!*p) p = opt; // All chars -> a channel name if(p > opt) { // we have a class for(i = 0; i < NELEM(debug_class_str); i++) { int n = strlen(debug_class_str[i]); if(n != (p - opt)) continue; if(!memcmp(opt, debug_class_str[i], n)) { // Found the class if(*p == '+') set = 1 << i; else clr = 1 << i; break; } } if(i == NELEM(debug_class_str)) continue; } else { if(*p == '-') clr = ~0; else set = ~0; } if(*p == '+' || *p == '-') p++; if(!*p) continue; if(!strcmp("all", p)) { debug_flags = (debug_flags & ~clr) | set; } else { if(strlen(p) >= sizeof(debug_channel[0].name)) continue; for(i = 0; i < n_debug_channel; i++) { if(!strcmp(p, debug_channel[i].name)) { debug_channel[i].flags = (debug_channel[i].flags & ~clr) | set; break; } } if(i == n_debug_channel && n_debug_channel < __DEBUG_CHANNEL_MAX) { strcpy(debug_channel[i].name, p); debug_channel[i].flags = (debug_flags & ~clr) | set; n_debug_channel++; } } } free(s); } #endif