blob: c7074ca660b9b004739d3dbe2fbacb02e413b53f [file] [log] [blame]
// basedebug.cpp - Base Debug (BD) run-time debugging.
/*******************************************************************************
* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
// See basedebug.hh for interface details.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdarg.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <sys/time.h>
#include <semaphore.h>
#include "basedebug.hh"
#include "basedebugtype.hh"
#include "basefatal.hh"
namespace base
{
// Colour codes:
//-static const char * ANSI_RESET = "\u001B[0m";
//static const char * ANSI_DIM_BLACK = "\u001B[2;30m";
//static const char * ANSI_DIM_RED = "\u001B[2;31m";
//static const char * ANSI_DIM_GREEN = "\u001B[2;32m";
//static const char * ANSI_DIM_YELLOW = "\u001B[2;33m";
//-static const char * ANSI_DIM_BLUE = "\u001B[2;34m";
//static const char * ANSI_DIM_PURPLE = "\u001B[2;35m";
//static const char * ANSI_DIM_CYAN = "\u001B[2;36m";
//static const char * ANSI_BRIGHT_BLACK = "\u001B[1;30m";
//-static const char * ANSI_BRIGHT_RED = "\u001B[1;31m";
//static const char * ANSI_BRIGHT_GREEN = "\u001B[1;32m";
//static const char * ANSI_BRIGHT_YELLOW = "\u001B[1;33m";
//-static const char * ANSI_BRIGHT_BLUE = "\u001B[1;34m";
//static const char * ANSI_BRIGHT_PURPLE = "\u001B[1;35m";
//static const char * ANSI_BRIGHT_CYAN = "\u001B[1;36m";
//static const char * ANSI_WHITE = "\u001B[37m";
// To output BD type as string.
const char *debugMessageTypePrefix[BD_MAXPLUS1] = BD_PREFIX_STRING;
// ---- RUN-TIME ENABLE OF BD MESSAGES HERE ----
// Un-comment the lines that correspond to the types you want enabled.
long int debugEnableMessageType = 0
// | (1 << BD_INJECT)
// | (1 << BD_MSG)
// | (1 << BD_SIGNAL)
// | (1 << BD_SIGNALREF)
// | (1 << BD_SIGNALINIT)
// | (1 << BD_TIMER)
// | (1 << BD_CONTROLLER)
// | (1 << BD_NOTIFY)
// | (1 << BD_SERIALIZE)
// | (1 << BD_LOCK)
;
// ---- RUN-TIME ENABLE OF BD MESSAGE COMPONENTS HERE ----
// Set to true to enable that part of the BD message output.
bool debugEnable = false; // Overall enable - if this is false, no debug output is produced regardless of all other settings.
bool debugEnableColor = true; // Enable color escape-sequences for output text.
bool debugEnableTypeName = true;
bool debugEnableTime = true;
bool debugEnableThreadId = false;
bool debugEnableFilename = false;
bool debugEnableLineNumber = false;
bool debugEnableMethod = false;
bool debugEnableUserMessage = true;
// Use a semaphore to serialize debug log messages.
class bdsem {
public:
bdsem( int value ) // Must explicitly set initial value.
{
if (sem_init(&sem, 0/*threads only*/, value) < 0)
{
FATAL_ERRNO("sem_init");
}
}
~bdsem()
{
if (sem_destroy(&sem) < 0)
{
FATAL_ERRNO("sem_destroy");
}
}
// Wait forever for one to be available.
void Wait(void)
{
if (sem_wait(&sem) < 0)
{
FATAL_ERRNO("sem_wait");
}
}
// Give one back.
void Post(void)
{
if (sem_post(&sem) < 0)
{
FATAL_ERRNO("sem_wait");
}
}
private:
bdsem();
sem_t sem;
};
// Convenience class for serializing debug log output.
class bdguard {
public:
bdguard( bdsem * * m );
~bdguard() { sem->Post(); }
private:
bdsem * sem;
};
bdguard::bdguard( bdsem * * m )
{
if (!(*m))
{
(*m) = new bdsem(1);
}
(*m)->Wait();
sem = (*m);
}
bdsem * bdprintflock;
// BD debug printf - called from BDEBUG macros.
void debugPrintf( int bdtype, const char * file, int line, const char * method, const char * fmt, ...)
{
static bool outputprefix = true;
static pthread_t lastthread = 0;
outputprefix |= (lastthread != pthread_self());
lastthread = pthread_self();
if (debugEnable && (bdtype < BD_MAXPLUS1) && (debugEnableMessageType & (1 << bdtype)))
{
va_list ap;
va_start(ap, fmt);
bdguard guard(&bdprintflock);
debugColourBrightBlue();
if (debugEnableTime && outputprefix)
{
struct timeval now;
gettimeofday(&now, NULL);
struct tm from_localtime;
const time_t seconds = now.tv_sec;
localtime_r( &seconds, &from_localtime );
char tmbuf[64];
size_t length = strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d:%H:%M:%S", &from_localtime);
snprintf(&tmbuf[length], sizeof(tmbuf)-length, ".%03ld", now.tv_usec/1000);
if (length <= 0)
{
strcpy(tmbuf, "?");
}
printf("%s:", tmbuf);
}
if (debugEnableTypeName && outputprefix)
{
printf("%s:", debugMessageTypePrefix[bdtype]);
}
if (debugEnableThreadId && outputprefix)
{
#if 0
// Not universally available.
char name[80]; // TODO: remove magic #.
if (pthread_getname_np(pthread_self(), name, sizeof(name)) != 0)
strcpy(name, "?");
printf("%s:", name);
#else
// Use this if pthread_getname_np/pthread_setname_np taken out.
printf("%ld:", pthread_self());
#endif
}
if (debugEnableFilename && outputprefix)
{
printf("%s:", file);
}
if (debugEnableLineNumber && outputprefix)
{
printf("line %d:", line);
}
if (debugEnableMethod && outputprefix)
{
printf("%s:", method);
}
if (debugEnableUserMessage)
{
vprintf(fmt, ap);
}
size_t len;
if ((len = strlen(fmt)) > 0)
{
outputprefix = (fmt[len-1] == '\n');
}
va_end(ap);
debugColourReset();
fflush(stdout);
}
}
// See UMLRTMain.hh
long int debugGetEnabledTypes()
{
return debugEnableMessageType;
}
// See UMLRTMain.hh
void debugEnableSet( bool enabled )
{
debugEnable = enabled;
}
// See UMLRTMain.hh
void debugEnableColorSet( bool enabled )
{
debugEnableColor = enabled;
}
// See UMLRTMain.hh
bool debugEnableTypeMaskNameSet( const char * name, bool enable )
{
long int index = -1;
bool ok = true;
// Special name 'all'
if (!strcasecmp(name, "ALL"))
{
for (int i = 1; (i < BD_MAXPLUS1) && ok; ++i)
{
ok = debugEnableTypeMaskNameSet(debugMessageTypePrefix[i], enable);
}
}
else
{
for (int i = 0; (i < BD_MAXPLUS1) && (index == -1); ++i)
{
if (!strcasecmp(name, debugMessageTypePrefix[i]))
{
index = i;
}
}
if (index == -1)
{
printf("ERROR: debug-type '%s' not found.\n", name);
ok = false;
}
else if (enable)
{
debugEnableMessageType |= (1 << index);
}
else
{
debugEnableMessageType &= ~(1 << index);
}
}
return ok;
}
// See UMLRTMain.hh
bool debugEnableTypeMaskSpecSet( char * typeMaskSpec, bool enable )
{
bool ok = true;
bool oneset = false;
char * next;
char * str = typeMaskSpec;
while (ok)
{
// str non-NULL the first time, get the first token.
char * token = strtok_r(str, " ", &next);
// str set NULL, to use 'next' for subsequent calls.
str = NULL;
if (token == NULL)
{
break;
}
oneset = true;
// Got a debug-type name - enable or disable it.
ok = debugEnableTypeMaskNameSet(token, enable);
}
if (ok && oneset)
{
debugEnableSet(true);
}
return ok;
}
// See UMLRTMain.hh
void debugEnableTypeNameDisplaySet( bool enabled )
{
debugEnableTypeName = enabled;
if (enabled)
{
debugEnableSet(true);
}
}
// See UMLRTMain.hh
void debugEnableTimeDisplaySet( bool enabled )
{
debugEnableTime = enabled;
if (enabled)
{
debugEnableSet(true);
}
}
// See UMLRTMain.hh
void debugEnableThreadDisplaySet( bool enabled )
{
debugEnableThreadId = enabled;
if (enabled)
{
debugEnableSet(true);
}
}
// See UMLRTMain.hh
void debugEnableFilenameDisplaySet( bool enabled )
{
debugEnableFilename = enabled;
if (enabled)
{
debugEnableSet(true);
}
}
// See UMLRTMain.hh
void debugEnableLineNumberDisplaySet( bool enabled )
{
debugEnableLineNumber = enabled;
if (enabled)
{
debugEnableSet(true);
}
}
// See UMLRTMain.hh
void debugEnableMethodDisplaySet( bool enabled )
{
debugEnableMethod = enabled;
if (enabled)
{
debugEnableSet(true);
}
}
// See UMLRTMain.hh
void debugEnableUserMsgDisplaySet( bool enabled )
{
debugEnableUserMessage = enabled;
if (enabled)
{
debugEnableSet(true);
}
}
// See UMLRTMain.hh
bool debugTypeEnabled( long int type )
{
return ((debugEnableMessageType & (1 << type)) != 0);
}
// See UMLRTMain.hh
void debugLogData( long int type, uint8_t * data, size_t size)
{
if (base::debugTypeEnabled(type) && (size != 0))
{
int extra = size & 0x7;
for (size_t i = 0; i < ((size / 8) + (extra != 0 ? 1 : 0)); ++i)
{
char buf[(8*6) + 2];
buf[0] = '\0';
char * p = buf;
int count = 0;
for (size_t j = 0; (j < 8) && (((i*8) + j) < size); ++j, ++count)
{
p += snprintf(p, sizeof(buf)-(p-buf), "0x%02X ", data[(i*8) + j]);
}
for (size_t j = count; j < 8; ++j)
{
p += snprintf(p, sizeof(buf)-(p-buf), " ");
}
*(p++) = ' ';
for (size_t j = 0; (j < 8) && ((j + (i*8)) < size); ++j)
{
char c = data[(i*8) + j];
if ((c >= 0x20) && (c <= 0x7F))
{
*(p++) = c;
}
else
{
*(p++) = '.';
}
}
*p = '\0';
BDEBUG(type, "%s\n", buf);
}
}
}
// See UMLRTMain.hh
void debugOptionSummary()
{
debugColourBrightBlue();
printf("Overall debug log enable (option 'debug/D') : %s\n", debugEnable ? "enabled" : "disabled");
printf("Debug message types (option 'debugtype/T')\n");
for (int i = 1; i < BD_MAXPLUS1; ++i)
{
printf(" %13s : %s\n", debugMessageTypePrefix[i], ((debugEnableMessageType & (1 << i)) != 0) ? "enabled" : "disabled");
}
printf("Debug log message components displayed:\n");
printf(" time-stamp (option 'debugtime/S') : %s\n", debugEnableTime ? "displayed" : "suppressed" );
printf(" type name (option 'debugname/N') : %s\n", debugEnableTypeName ? "displayed" : "suppressed" );
printf(" thread-id (option 'debugthread/n') : %s\n", debugEnableThreadId ? "displayed" : "suppressed" );
printf(" file-name (option 'debugfile/F') : %s\n", debugEnableFilename ? "displayed" : "suppressed" );
printf(" line-number (option 'debugline/L') : %s\n", debugEnableLineNumber ? "displayed" : "suppressed" );
printf(" method name (option 'debugmethod/M') : %s\n", debugEnableMethod ? "displayed" : "suppressed" );
printf(" user message (option 'debugmsg/m') : %s\n", debugEnableUserMessage ? "displayed" : "suppressed" );
debugColourReset();
}
// See UMLRTMain.hh
void debugTypeSummary()
{
for (int i = 1; i < BD_MAXPLUS1; ++i)
{
printf("%s ", debugMessageTypePrefix[i]);
}
}
// See basedebug.hh
void debugColourReset()
{
if (debugEnableColor)
{
//printf("%s", ANSI_RESET);
}
}
void debugColourBrightBlue()
{
if (debugEnableColor)
{
//printf("%s", ANSI_BRIGHT_BLUE);
}
}
void debugColourBrightRed()
{
if (debugEnableColor)
{
//printf("%s", ANSI_BRIGHT_RED);
}
}
void debugColourDimBlue()
{
if (debugEnableColor)
{
//printf("%s", ANSI_DIM_BLUE);
}
}
// See basedebug.hh for documentation.
void swerr( const char * file, int line, const char * method, const char * fmt, ...)
{
if (debugEnable && (debugEnableMessageType & (1 << BD_SWERR)))
{
va_list ap;
va_start(ap, fmt);
bdguard guard(&bdprintflock);
debugColourBrightRed();
printf("SWERR:%s:%d:%s:%ld:", file, line, method, pthread_self());
vprintf(fmt, ap);
printf("\n");
va_end(ap);
debugColourReset();
}
}
// See basedebug.hh for documentation.
void swerrErrno( const char * file, int line, const char * method, int errno_, const char * api)
{
if (debugEnable && (debugEnableMessageType & (1 << BD_SWERR)))
{
bdguard guard(&bdprintflock);
debugColourBrightRed();
printf("SWERR:%s:%d:%s:%ld:%s:errno(%d):%s\n", file, line, method, pthread_self(), api, errno_, strerror(errno_));
debugColourReset();
}
}
}