Home | History | Annotate | Download | only in lib
      1 /*
      2  * Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 /**
     17  * @file picodbg.c
     18  *
     19  * Provides functions and macros to debug the Pico system and to trace
     20  * the execution of its code.
     21  *
     22  * Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
     23  * All rights reserved.
     24  *
     25  * History:
     26  * - 2009-04-20 -- initial version
     27  */
     28 
     29 #ifdef __cplusplus
     30 extern "C" {
     31 #endif
     32 #if 0
     33 }
     34 #endif
     35 
     36 
     37 #if defined(PICO_DEBUG)
     38 
     39 /* Two variants of colored console output are implemented:
     40    COLOR_MODE_WINDOWS
     41       uses the Windows API function SetConsoleTextAttribute
     42    COLOR_MODE_ANSI
     43       uses ANSI escape codes */
     44 #if defined(_WIN32)
     45 #define COLOR_MODE_WINDOWS
     46 #else
     47 #define COLOR_MODE_ANSI
     48 #endif
     49 
     50 
     51 #include <stdio.h>
     52 #include <stdlib.h>
     53 
     54 #include <stdarg.h>
     55 #include <string.h>
     56 
     57 #include "picodbg.h"
     58 
     59 
     60 /* Maximum length of a formatted tracing message */
     61 #define MAX_MESSAGE_LEN         999
     62 
     63 /* Maximum length of contextual information */
     64 #define MAX_CONTEXT_LEN         499
     65 
     66 /* Maximum length of filename filter */
     67 #define MAX_FILTERFN_LEN         16
     68 
     69 /* Delimiter used in debug messages */
     70 #define MSG_DELIM               "|"
     71 
     72 /* Standard output file for debug messages */
     73 #define STDDBG                  stdout /* or stderr */
     74 
     75 /* Default setup */
     76 #define PICODBG_DEFAULT_LEVEL   PICODBG_LOG_LEVEL_WARN
     77 #define PICODBG_DEFAULT_FILTERFN   ""
     78 #define PICODBG_DEFAULT_FORMAT  \
     79     (PICODBG_SHOW_LEVEL | PICODBG_SHOW_SRCNAME | PICODBG_SHOW_FUNCTION)
     80 #define PICODBG_DEFAULT_COLOR   1
     81 
     82 
     83 /* Current log level */
     84 static int logLevel = PICODBG_DEFAULT_LEVEL;
     85 
     86 /* Current log filter (filename) */
     87 static char logFilterFN[MAX_FILTERFN_LEN + 1];
     88 
     89 /* Current log file or NULL if no log file is set */
     90 static FILE *logFile = NULL;
     91 
     92 /* Current output format */
     93 static int logFormat = PICODBG_DEFAULT_FORMAT;
     94 
     95 /* Color mode for console output (0 : disable colors, != 0 : enable colors */
     96 static int optColor = 0;
     97 
     98 /* Buffer for context information */
     99 static char ctxbuf[MAX_CONTEXT_LEN + 1];
    100 
    101 /* Buffer to format tracing messages */
    102 static char msgbuf[MAX_MESSAGE_LEN + 1];
    103 
    104 
    105 /* *** Support for colored text output to console *****/
    106 
    107 
    108 /* Console text colors */
    109 enum color_t {
    110     /* order matches Windows color codes */
    111     ColorBlack,
    112     ColorBlue,
    113     ColorGreen,
    114     ColorCyan,
    115     ColorRed,
    116     ColorPurple,
    117     ColorBrown,
    118     ColorLightGray,
    119     ColorDarkGray,
    120     ColorLightBlue,
    121     ColorLightGreen,
    122     ColorLightCyan,
    123     ColorLightRed,
    124     ColorLightPurple,
    125     ColorYellow,
    126     ColorWhite
    127 };
    128 
    129 
    130 static enum color_t picodbg_getLevelColor(int level)
    131 {
    132     switch (level) {
    133         case PICODBG_LOG_LEVEL_ERROR: return ColorLightRed;
    134         case PICODBG_LOG_LEVEL_WARN : return ColorYellow;
    135         case PICODBG_LOG_LEVEL_INFO : return ColorGreen;
    136         case PICODBG_LOG_LEVEL_DEBUG: return ColorLightGray;
    137         case PICODBG_LOG_LEVEL_TRACE: return ColorDarkGray;
    138     }
    139     return ColorWhite;
    140 }
    141 
    142 
    143 #if defined(COLOR_MODE_WINDOWS)
    144 
    145 #define WIN32_LEAN_AND_MEAN
    146 #include <windows.h>
    147 
    148 static int picodbg_setTextAttr(FILE *stream, int attr)
    149 {
    150     HANDLE hConsole;
    151 
    152     if (stream == stdout) {
    153         hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    154     } else if (stream == stderr) {
    155         hConsole = GetStdHandle(STD_ERROR_HANDLE);
    156     } else {
    157         hConsole = INVALID_HANDLE_VALUE;
    158     }
    159 
    160     if (hConsole != INVALID_HANDLE_VALUE) {
    161         /* do nothing if console output is redirected to a file */
    162         if (GetFileType(hConsole) == FILE_TYPE_CHAR) {
    163             CONSOLE_SCREEN_BUFFER_INFO csbi;
    164             GetConsoleScreenBufferInfo(hConsole, &csbi);
    165             SetConsoleTextAttribute(hConsole, (WORD) attr);
    166             return (int) csbi.wAttributes;
    167         }
    168     }
    169 
    170     return 0;
    171 }
    172 
    173 #elif defined(COLOR_MODE_ANSI)
    174 
    175 static int picodbg_setTextAttr(FILE *stream, int attr)
    176 {
    177     const char *c = "";
    178 
    179     if (attr == -1) {
    180         c = "0";
    181     } else switch (attr) {
    182         case ColorBlack:       c = "0;30"; break;
    183         case ColorRed:         c = "0;31"; break;
    184         case ColorGreen:       c = "0;32"; break;
    185         case ColorBrown:       c = "0;33"; break;
    186         case ColorBlue:        c = "0;34"; break;
    187         case ColorPurple:      c = "0;35"; break;
    188         case ColorCyan:        c = "0;36"; break;
    189         case ColorLightGray:   c = "0;37"; break;
    190         case ColorDarkGray:    c = "1;30"; break;
    191         case ColorLightRed:    c = "1;31"; break;
    192         case ColorLightGreen:  c = "1;32"; break;
    193         case ColorYellow:      c = "1;33"; break;
    194         case ColorLightBlue:   c = "1;34"; break;
    195         case ColorLightPurple: c = "1;35"; break;
    196         case ColorLightCyan:   c = "1;36"; break;
    197         case ColorWhite:       c = "1;37"; break;
    198     }
    199 
    200     fprintf(stream, "\x1b[%sm", c);
    201     return -1;
    202 }
    203 
    204 #else
    205 
    206 static int picodbg_setTextAttr(FILE *stream, int attr)
    207 {
    208     /* avoid 'unreferenced formal parameter' */
    209     (void) stream;
    210     (void) attr;
    211     return 0;
    212 }
    213 
    214 #endif
    215 
    216 
    217 /* *** Auxiliary routines *****/
    218 
    219 
    220 static const char *picodbg_fileTitle(const char *file)
    221 {
    222     const char *name = file, *str = file;
    223 
    224     /* try to extract file name without path in a platform independent
    225        way, i.e., skip all chars preceding path separator chars like
    226        '/' (Unix, MacOSX), '\' (Windows, DOS), and ':' (MacOS9) */
    227     while (*str) {
    228         if ((*str == '\\') || (*str == '/') || (*str == ':')) {
    229             name = str + 1;
    230         }
    231         str++;
    232     }
    233 
    234     return name;
    235 }
    236 
    237 
    238 static void picodbg_logToStream(int level, int donewline,
    239                                 const char *context, const char *msg)
    240 {
    241     int oldAttr = 0;
    242 
    243     if (optColor) {
    244         oldAttr = picodbg_setTextAttr(STDDBG, picodbg_getLevelColor(level));
    245     }
    246 
    247     fprintf(STDDBG, "%s%s", context, msg);
    248     if (donewline) fprintf(STDDBG, "\n");
    249     if (logFile != NULL) {
    250         fprintf(logFile, "%s%s", context, msg);
    251         if (donewline) fprintf(logFile, "\n");
    252     }
    253 
    254     if (optColor) {
    255         picodbg_setTextAttr(STDDBG, oldAttr);
    256     }
    257 }
    258 
    259 
    260 /* *** Exported routines *****/
    261 
    262 
    263 void picodbg_initialize(int level)
    264 {
    265     logLevel  = level;
    266     strcpy(logFilterFN, PICODBG_DEFAULT_FILTERFN);
    267     logFile   = NULL;
    268     logFormat = PICODBG_DEFAULT_FORMAT;
    269     optColor  = PICODBG_DEFAULT_COLOR;
    270     PICODBG_ASSERT_RANGE(level, 0, PICODBG_LOG_LEVEL_TRACE);
    271 }
    272 
    273 
    274 void picodbg_terminate()
    275 {
    276     if (logFile != NULL) {
    277         fclose(logFile);
    278     }
    279 
    280     logLevel = 0;
    281     logFile  = NULL;
    282 }
    283 
    284 
    285 void picodbg_setLogLevel(int level)
    286 {
    287     PICODBG_ASSERT_RANGE(level, 0, PICODBG_LOG_LEVEL_TRACE);
    288     logLevel = level;
    289 }
    290 
    291 
    292 void picodbg_setLogFilterFN(const char *name)
    293 {
    294     strcpy(logFilterFN, name);
    295 }
    296 
    297 
    298 void picodbg_setLogFile(const char *name)
    299 {
    300     if (logFile != NULL) {
    301         fclose(logFile);
    302     }
    303 
    304     if ((name != NULL) && (strlen(name) > 0)) {
    305         logFile = fopen(name, "wt");
    306     } else {
    307         logFile = NULL;
    308     }
    309 }
    310 
    311 
    312 void picodbg_enableColors(int flag)
    313 {
    314     optColor = (flag != 0);
    315 }
    316 
    317 
    318 void picodbg_setOutputFormat(unsigned int format)
    319 {
    320     logFormat = format;
    321 }
    322 
    323 
    324 const char *picodbg_varargs(const char *format, ...)
    325 {
    326     int len;
    327 
    328     va_list argptr;
    329     va_start(argptr, format);
    330 
    331     len = vsprintf(msgbuf, format, argptr);
    332     PICODBG_ASSERT_RANGE(len, 0, MAX_MESSAGE_LEN);
    333 
    334     return msgbuf;
    335 }
    336 
    337 
    338 void picodbg_log(int level, int donewline, const char *file, int line,
    339                  const char *func, const char *msg)
    340 {
    341     char cb[MAX_CONTEXT_LEN + 1];
    342 
    343     PICODBG_ASSERT_RANGE(level, 0, PICODBG_LOG_LEVEL_TRACE);
    344 
    345     if ((level <= logLevel) &&
    346         ((strlen(logFilterFN) == 0) || !strcmp(logFilterFN, picodbg_fileTitle(file)))) {
    347         /* compose output format string */
    348         strcpy(ctxbuf, "*** ");
    349         if (logFormat & PICODBG_SHOW_LEVEL) {
    350             switch (level) {
    351                 case PICODBG_LOG_LEVEL_ERROR:
    352                     strcat(ctxbuf, "error" MSG_DELIM);
    353                     break;
    354                 case PICODBG_LOG_LEVEL_WARN:
    355                     strcat(ctxbuf, "warn " MSG_DELIM);
    356                     break;
    357                 case PICODBG_LOG_LEVEL_INFO:
    358                     strcat(ctxbuf, "info " MSG_DELIM);
    359                     break;
    360                 case PICODBG_LOG_LEVEL_DEBUG:
    361                     strcat(ctxbuf, "debug" MSG_DELIM);
    362                     break;
    363                 case PICODBG_LOG_LEVEL_TRACE:
    364                     strcat(ctxbuf, "trace" MSG_DELIM);
    365                     break;
    366                 default:
    367                     break;
    368             }
    369         }
    370         if (logFormat & PICODBG_SHOW_DATE) {
    371             /* nyi */
    372         }
    373         if (logFormat & PICODBG_SHOW_TIME) {
    374             /* nyi */
    375         }
    376         if (logFormat & PICODBG_SHOW_SRCNAME) {
    377             sprintf(cb, "%-10s", picodbg_fileTitle(file));
    378             strcat(ctxbuf, cb);
    379             if (logFormat & PICODBG_SHOW_SRCLINE) {
    380                 sprintf(cb, "(%d)", line);
    381                 strcat(ctxbuf, cb);
    382             }
    383             strcat(ctxbuf, MSG_DELIM);
    384         }
    385         if (logFormat & PICODBG_SHOW_FUNCTION) {
    386             if (strlen(func) > 0) {
    387                 sprintf(cb, "%-18s", func);
    388                 strcat(ctxbuf, cb);
    389                 strcat(ctxbuf, MSG_DELIM);
    390             }
    391         }
    392 
    393         picodbg_logToStream(level, donewline, ctxbuf, msg);
    394     }
    395 }
    396 
    397 
    398 void picodbg_log_msg(int level, const char *file, const char *msg)
    399 {
    400     PICODBG_ASSERT_RANGE(level, 0, PICODBG_LOG_LEVEL_TRACE);
    401 
    402     if ((level <= logLevel) &&
    403         ((strlen(logFilterFN) == 0) || !strcmp(logFilterFN, picodbg_fileTitle(file)))) {
    404         picodbg_logToStream(level, 0, "", msg);
    405     }
    406 }
    407 
    408 
    409 void picodbg_assert(const char *file, int line, const char *func, const char *expr)
    410 {
    411     if (strlen(func) > 0) {
    412         fprintf(STDDBG, "assertion failed: %s, file %s, function %s, line %d",
    413             expr, picodbg_fileTitle(file), func, line);
    414     } else {
    415         fprintf(STDDBG, "assertion failed: %s, file %s, line %d",
    416             expr, picodbg_fileTitle(file), line);
    417     }
    418     picodbg_terminate();
    419     abort();
    420 }
    421 
    422 
    423 #else
    424 
    425 /* To prevent warning about "translation unit is empty" when
    426    diagnostic output is disabled. */
    427 static void picodbg_dummy(void) {
    428     picodbg_dummy();
    429 }
    430 
    431 #endif /* defined(PICO_DEBUG) */
    432 
    433 #ifdef __cplusplus
    434 }
    435 #endif
    436 
    437 
    438 /* end */
    439