438 lines
10 KiB
C
438 lines
10 KiB
C
/*
|
|
* Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* @file picodbg.c
|
|
*
|
|
* Provides functions and macros to debug the Pico system and to trace
|
|
* the execution of its code.
|
|
*
|
|
* Copyright (C) 2008-2009 SVOX AG, Baslerstr. 30, 8048 Zuerich, Switzerland
|
|
* All rights reserved.
|
|
*
|
|
* History:
|
|
* - 2009-04-20 -- initial version
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
#if 0
|
|
}
|
|
#endif
|
|
|
|
|
|
#if defined(PICO_DEBUG)
|
|
|
|
/* Two variants of colored console output are implemented:
|
|
COLOR_MODE_WINDOWS
|
|
uses the Windows API function SetConsoleTextAttribute
|
|
COLOR_MODE_ANSI
|
|
uses ANSI escape codes */
|
|
#if defined(_WIN32)
|
|
#define COLOR_MODE_WINDOWS
|
|
#else
|
|
#define COLOR_MODE_ANSI
|
|
#endif
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
#include "picodbg.h"
|
|
|
|
|
|
/* Maximum length of a formatted tracing message */
|
|
#define MAX_MESSAGE_LEN 999
|
|
|
|
/* Maximum length of contextual information */
|
|
#define MAX_CONTEXT_LEN 499
|
|
|
|
/* Maximum length of filename filter */
|
|
#define MAX_FILTERFN_LEN 16
|
|
|
|
/* Delimiter used in debug messages */
|
|
#define MSG_DELIM "|"
|
|
|
|
/* Standard output file for debug messages */
|
|
#define STDDBG stdout /* or stderr */
|
|
|
|
/* Default setup */
|
|
#define PICODBG_DEFAULT_LEVEL PICODBG_LOG_LEVEL_WARN
|
|
#define PICODBG_DEFAULT_FILTERFN ""
|
|
#define PICODBG_DEFAULT_FORMAT \
|
|
(PICODBG_SHOW_LEVEL | PICODBG_SHOW_SRCNAME | PICODBG_SHOW_FUNCTION)
|
|
#define PICODBG_DEFAULT_COLOR 1
|
|
|
|
|
|
/* Current log level */
|
|
static int logLevel = PICODBG_DEFAULT_LEVEL;
|
|
|
|
/* Current log filter (filename) */
|
|
static char logFilterFN[MAX_FILTERFN_LEN + 1];
|
|
|
|
/* Current log file or NULL if no log file is set */
|
|
static FILE *logFile = NULL;
|
|
|
|
/* Current output format */
|
|
static int logFormat = PICODBG_DEFAULT_FORMAT;
|
|
|
|
/* Color mode for console output (0 : disable colors, != 0 : enable colors */
|
|
static int optColor = 0;
|
|
|
|
/* Buffer for context information */
|
|
static char ctxbuf[MAX_CONTEXT_LEN + 1];
|
|
|
|
/* Buffer to format tracing messages */
|
|
static char msgbuf[MAX_MESSAGE_LEN + 1];
|
|
|
|
|
|
/* *** Support for colored text output to console *****/
|
|
|
|
|
|
/* Console text colors */
|
|
enum color_t {
|
|
/* order matches Windows color codes */
|
|
ColorBlack,
|
|
ColorBlue,
|
|
ColorGreen,
|
|
ColorCyan,
|
|
ColorRed,
|
|
ColorPurple,
|
|
ColorBrown,
|
|
ColorLightGray,
|
|
ColorDarkGray,
|
|
ColorLightBlue,
|
|
ColorLightGreen,
|
|
ColorLightCyan,
|
|
ColorLightRed,
|
|
ColorLightPurple,
|
|
ColorYellow,
|
|
ColorWhite
|
|
};
|
|
|
|
|
|
static enum color_t picodbg_getLevelColor(int level)
|
|
{
|
|
switch (level) {
|
|
case PICODBG_LOG_LEVEL_ERROR: return ColorLightRed;
|
|
case PICODBG_LOG_LEVEL_WARN : return ColorYellow;
|
|
case PICODBG_LOG_LEVEL_INFO : return ColorGreen;
|
|
case PICODBG_LOG_LEVEL_DEBUG: return ColorLightGray;
|
|
case PICODBG_LOG_LEVEL_TRACE: return ColorDarkGray;
|
|
}
|
|
return ColorWhite;
|
|
}
|
|
|
|
|
|
#if defined(COLOR_MODE_WINDOWS)
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
static int picodbg_setTextAttr(FILE *stream, int attr)
|
|
{
|
|
HANDLE hConsole;
|
|
|
|
if (stream == stdout) {
|
|
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
} else if (stream == stderr) {
|
|
hConsole = GetStdHandle(STD_ERROR_HANDLE);
|
|
} else {
|
|
hConsole = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (hConsole != INVALID_HANDLE_VALUE) {
|
|
/* do nothing if console output is redirected to a file */
|
|
if (GetFileType(hConsole) == FILE_TYPE_CHAR) {
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
|
SetConsoleTextAttribute(hConsole, (WORD) attr);
|
|
return (int) csbi.wAttributes;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(COLOR_MODE_ANSI)
|
|
|
|
static int picodbg_setTextAttr(FILE *stream, int attr)
|
|
{
|
|
const char *c = "";
|
|
|
|
if (attr == -1) {
|
|
c = "0";
|
|
} else switch (attr) {
|
|
case ColorBlack: c = "0;30"; break;
|
|
case ColorRed: c = "0;31"; break;
|
|
case ColorGreen: c = "0;32"; break;
|
|
case ColorBrown: c = "0;33"; break;
|
|
case ColorBlue: c = "0;34"; break;
|
|
case ColorPurple: c = "0;35"; break;
|
|
case ColorCyan: c = "0;36"; break;
|
|
case ColorLightGray: c = "0;37"; break;
|
|
case ColorDarkGray: c = "1;30"; break;
|
|
case ColorLightRed: c = "1;31"; break;
|
|
case ColorLightGreen: c = "1;32"; break;
|
|
case ColorYellow: c = "1;33"; break;
|
|
case ColorLightBlue: c = "1;34"; break;
|
|
case ColorLightPurple: c = "1;35"; break;
|
|
case ColorLightCyan: c = "1;36"; break;
|
|
case ColorWhite: c = "1;37"; break;
|
|
}
|
|
|
|
fprintf(stream, "\x1b[%sm", c);
|
|
return -1;
|
|
}
|
|
|
|
#else
|
|
|
|
static int picodbg_setTextAttr(FILE *stream, int attr)
|
|
{
|
|
/* avoid 'unreferenced formal parameter' */
|
|
(void) stream;
|
|
(void) attr;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/* *** Auxiliary routines *****/
|
|
|
|
|
|
static const char *picodbg_fileTitle(const char *file)
|
|
{
|
|
const char *name = file, *str = file;
|
|
|
|
/* try to extract file name without path in a platform independent
|
|
way, i.e., skip all chars preceding path separator chars like
|
|
'/' (Unix, MacOSX), '\' (Windows, DOS), and ':' (MacOS9) */
|
|
while (*str) {
|
|
if ((*str == '\\') || (*str == '/') || (*str == ':')) {
|
|
name = str + 1;
|
|
}
|
|
str++;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
static void picodbg_logToStream(int level, int donewline,
|
|
const char *context, const char *msg)
|
|
{
|
|
int oldAttr = 0;
|
|
|
|
if (optColor) {
|
|
oldAttr = picodbg_setTextAttr(STDDBG, picodbg_getLevelColor(level));
|
|
}
|
|
|
|
fprintf(STDDBG, "%s%s", context, msg);
|
|
if (donewline) fprintf(STDDBG, "\n");
|
|
if (logFile != NULL) {
|
|
fprintf(logFile, "%s%s", context, msg);
|
|
if (donewline) fprintf(logFile, "\n");
|
|
}
|
|
|
|
if (optColor) {
|
|
picodbg_setTextAttr(STDDBG, oldAttr);
|
|
}
|
|
}
|
|
|
|
|
|
/* *** Exported routines *****/
|
|
|
|
|
|
void picodbg_initialize(int level)
|
|
{
|
|
logLevel = level;
|
|
strcpy(logFilterFN, PICODBG_DEFAULT_FILTERFN);
|
|
logFile = NULL;
|
|
logFormat = PICODBG_DEFAULT_FORMAT;
|
|
optColor = PICODBG_DEFAULT_COLOR;
|
|
PICODBG_ASSERT_RANGE(level, 0, PICODBG_LOG_LEVEL_TRACE);
|
|
}
|
|
|
|
|
|
void picodbg_terminate()
|
|
{
|
|
if (logFile != NULL) {
|
|
fclose(logFile);
|
|
}
|
|
|
|
logLevel = 0;
|
|
logFile = NULL;
|
|
}
|
|
|
|
|
|
void picodbg_setLogLevel(int level)
|
|
{
|
|
PICODBG_ASSERT_RANGE(level, 0, PICODBG_LOG_LEVEL_TRACE);
|
|
logLevel = level;
|
|
}
|
|
|
|
|
|
void picodbg_setLogFilterFN(const char *name)
|
|
{
|
|
strcpy(logFilterFN, name);
|
|
}
|
|
|
|
|
|
void picodbg_setLogFile(const char *name)
|
|
{
|
|
if (logFile != NULL) {
|
|
fclose(logFile);
|
|
}
|
|
|
|
if ((name != NULL) && (strlen(name) > 0)) {
|
|
logFile = fopen(name, "wt");
|
|
} else {
|
|
logFile = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void picodbg_enableColors(int flag)
|
|
{
|
|
optColor = (flag != 0);
|
|
}
|
|
|
|
|
|
void picodbg_setOutputFormat(unsigned int format)
|
|
{
|
|
logFormat = format;
|
|
}
|
|
|
|
|
|
const char *picodbg_varargs(const char *format, ...)
|
|
{
|
|
int len;
|
|
|
|
va_list argptr;
|
|
va_start(argptr, format);
|
|
|
|
len = vsprintf(msgbuf, format, argptr);
|
|
PICODBG_ASSERT_RANGE(len, 0, MAX_MESSAGE_LEN);
|
|
|
|
return msgbuf;
|
|
}
|
|
|
|
|
|
void picodbg_log(int level, int donewline, const char *file, int line,
|
|
const char *func, const char *msg)
|
|
{
|
|
char cb[MAX_CONTEXT_LEN + 1];
|
|
|
|
PICODBG_ASSERT_RANGE(level, 0, PICODBG_LOG_LEVEL_TRACE);
|
|
|
|
if ((level <= logLevel) &&
|
|
((strlen(logFilterFN) == 0) || !strcmp(logFilterFN, picodbg_fileTitle(file)))) {
|
|
/* compose output format string */
|
|
strcpy(ctxbuf, "*** ");
|
|
if (logFormat & PICODBG_SHOW_LEVEL) {
|
|
switch (level) {
|
|
case PICODBG_LOG_LEVEL_ERROR:
|
|
strcat(ctxbuf, "error" MSG_DELIM);
|
|
break;
|
|
case PICODBG_LOG_LEVEL_WARN:
|
|
strcat(ctxbuf, "warn " MSG_DELIM);
|
|
break;
|
|
case PICODBG_LOG_LEVEL_INFO:
|
|
strcat(ctxbuf, "info " MSG_DELIM);
|
|
break;
|
|
case PICODBG_LOG_LEVEL_DEBUG:
|
|
strcat(ctxbuf, "debug" MSG_DELIM);
|
|
break;
|
|
case PICODBG_LOG_LEVEL_TRACE:
|
|
strcat(ctxbuf, "trace" MSG_DELIM);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (logFormat & PICODBG_SHOW_DATE) {
|
|
/* nyi */
|
|
}
|
|
if (logFormat & PICODBG_SHOW_TIME) {
|
|
/* nyi */
|
|
}
|
|
if (logFormat & PICODBG_SHOW_SRCNAME) {
|
|
sprintf(cb, "%-10s", picodbg_fileTitle(file));
|
|
strcat(ctxbuf, cb);
|
|
if (logFormat & PICODBG_SHOW_SRCLINE) {
|
|
sprintf(cb, "(%d)", line);
|
|
strcat(ctxbuf, cb);
|
|
}
|
|
strcat(ctxbuf, MSG_DELIM);
|
|
}
|
|
if (logFormat & PICODBG_SHOW_FUNCTION) {
|
|
if (strlen(func) > 0) {
|
|
sprintf(cb, "%-18s", func);
|
|
strcat(ctxbuf, cb);
|
|
strcat(ctxbuf, MSG_DELIM);
|
|
}
|
|
}
|
|
|
|
picodbg_logToStream(level, donewline, ctxbuf, msg);
|
|
}
|
|
}
|
|
|
|
|
|
void picodbg_log_msg(int level, const char *file, const char *msg)
|
|
{
|
|
PICODBG_ASSERT_RANGE(level, 0, PICODBG_LOG_LEVEL_TRACE);
|
|
|
|
if ((level <= logLevel) &&
|
|
((strlen(logFilterFN) == 0) || !strcmp(logFilterFN, picodbg_fileTitle(file)))) {
|
|
picodbg_logToStream(level, 0, "", msg);
|
|
}
|
|
}
|
|
|
|
|
|
void picodbg_assert(const char *file, int line, const char *func, const char *expr)
|
|
{
|
|
if (strlen(func) > 0) {
|
|
fprintf(STDDBG, "assertion failed: %s, file %s, function %s, line %d",
|
|
expr, picodbg_fileTitle(file), func, line);
|
|
} else {
|
|
fprintf(STDDBG, "assertion failed: %s, file %s, line %d",
|
|
expr, picodbg_fileTitle(file), line);
|
|
}
|
|
picodbg_terminate();
|
|
abort();
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
/* To prevent warning about "translation unit is empty" when
|
|
diagnostic output is disabled. */
|
|
static void picodbg_dummy(void) {
|
|
picodbg_dummy();
|
|
}
|
|
|
|
#endif /* defined(PICO_DEBUG) */
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
|
|
/* end */
|