summaryrefslogtreecommitdiffstats
path: root/lib/picodbg.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/picodbg.c')
-rw-r--r--lib/picodbg.c438
1 files changed, 438 insertions, 0 deletions
diff --git a/lib/picodbg.c b/lib/picodbg.c
new file mode 100644
index 0000000..8fa753b
--- /dev/null
+++ b/lib/picodbg.c
@@ -0,0 +1,438 @@
+/*
+ * 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 */