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