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
picodbg_getLevelColor(int level)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
picodbg_setTextAttr(FILE * stream,int attr)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
picodbg_setTextAttr(FILE * stream,int attr)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
picodbg_setTextAttr(FILE * stream,int attr)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
picodbg_fileTitle(const char * file)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
picodbg_logToStream(int level,int donewline,const char * context,const char * msg)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
picodbg_initialize(int level)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
picodbg_terminate()274 void picodbg_terminate()
275 {
276 if (logFile != NULL) {
277 fclose(logFile);
278 }
279
280 logLevel = 0;
281 logFile = NULL;
282 }
283
284
picodbg_setLogLevel(int level)285 void picodbg_setLogLevel(int level)
286 {
287 PICODBG_ASSERT_RANGE(level, 0, PICODBG_LOG_LEVEL_TRACE);
288 logLevel = level;
289 }
290
291
picodbg_setLogFilterFN(const char * name)292 void picodbg_setLogFilterFN(const char *name)
293 {
294 strcpy(logFilterFN, name);
295 }
296
297
picodbg_setLogFile(const char * name)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
picodbg_enableColors(int flag)312 void picodbg_enableColors(int flag)
313 {
314 optColor = (flag != 0);
315 }
316
317
picodbg_setOutputFormat(unsigned int format)318 void picodbg_setOutputFormat(unsigned int format)
319 {
320 logFormat = format;
321 }
322
323
picodbg_varargs(const char * format,...)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
picodbg_log(int level,int donewline,const char * file,int line,const char * func,const char * msg)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
picodbg_log_msg(int level,const char * file,const char * msg)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
picodbg_assert(const char * file,int line,const char * func,const char * expr)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