extended debug facilities for output
* Output strings with unprintable characters either as a table or as continuing string of characters; the byte value of unprintable characters (`isprint(<character>, <locale>) == false`) is shown. * Output readable string of `key` -> `value` pairs with an enumerated type for the key and union type for the value.
This commit is contained in:
parent
d01917c82d
commit
fd800dbca8
10
Debug.cpp
10
Debug.cpp
|
@ -22,7 +22,13 @@
|
||||||
|
|
||||||
namespace krikkel
|
namespace krikkel
|
||||||
{
|
{
|
||||||
|
using std::string;
|
||||||
|
using std::fstream;
|
||||||
using std::filesystem::is_directory;
|
using std::filesystem::is_directory;
|
||||||
|
using std::endl;
|
||||||
|
using std::time;
|
||||||
|
using std::localtime;
|
||||||
|
using std::strftime;
|
||||||
|
|
||||||
Debug::Debug()
|
Debug::Debug()
|
||||||
{
|
{
|
||||||
|
@ -30,7 +36,7 @@ namespace krikkel
|
||||||
if(is_directory("sandbox"))
|
if(is_directory("sandbox"))
|
||||||
directory = "./sandbox";
|
directory = "./sandbox";
|
||||||
logFile = fstream(directory + "/debug.log", fstream::out | fstream::app);//| fstream::trunc);
|
logFile = fstream(directory + "/debug.log", fstream::out | fstream::app);//| fstream::trunc);
|
||||||
logFile << endl << endl << "New instance of class Debug." << endl;
|
logFile << endl << endl << endl << endl << "New instance of class Debug." << endl;
|
||||||
logFile << getUname();
|
logFile << getUname();
|
||||||
logFile.flush();
|
logFile.flush();
|
||||||
}
|
}
|
||||||
|
@ -50,7 +56,7 @@ namespace krikkel
|
||||||
|
|
||||||
string Debug::getTimestamp()
|
string Debug::getTimestamp()
|
||||||
{
|
{
|
||||||
time_t now = std::time(NULL);
|
time_t now = time(NULL);
|
||||||
tm *localNow = localtime(&now);
|
tm *localNow = localtime(&now);
|
||||||
static char formattedLocalNow[32];
|
static char formattedLocalNow[32];
|
||||||
strftime(formattedLocalNow, 32, "%c", localNow);
|
strftime(formattedLocalNow, 32, "%c", localNow);
|
||||||
|
|
93
Debug.hpp
93
Debug.hpp
|
@ -1,6 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* @brief Writes messages to `debug.log` if `NDEBUG` is not defined.
|
* @brief Writes messages to `debug.log` if `NDEBUG` is not defined.
|
||||||
* @author Christian Burger (christian@krikkel.de)
|
* @author Christian Burger (christian@krikkel.de)
|
||||||
|
* @todo refactor macros
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __DEBUG_H__
|
#ifndef __DEBUG_H__
|
||||||
|
@ -11,30 +12,24 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
namespace krikkel
|
namespace krikkel
|
||||||
{
|
{
|
||||||
using std::fstream;
|
|
||||||
using std::string;
|
|
||||||
using std::endl;
|
|
||||||
using std::strftime;
|
|
||||||
using std::localtime;
|
|
||||||
using std::time;
|
|
||||||
|
|
||||||
class Debug
|
class Debug
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
fstream logFile;
|
std::fstream logFile;
|
||||||
static const int MAX_NUMBER_OF_SYSCALLS = 1024;
|
static const int MAX_NUMBER_OF_SYSCALLS = 1024;
|
||||||
static const char * syscalls[MAX_NUMBER_OF_SYSCALLS];
|
static const char * syscalls[MAX_NUMBER_OF_SYSCALLS];
|
||||||
|
|
||||||
Debug();
|
Debug();
|
||||||
string getTimestamp();
|
std::string getTimestamp();
|
||||||
string getUname();
|
std::string getUname();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void log(string message, string fileName, int lineNo
|
void log(std::string message, std::string fileName, int lineNo
|
||||||
, string functionName);
|
, std::string functionName);
|
||||||
static Debug *getInstance();
|
static Debug *getInstance();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -42,10 +37,84 @@ namespace krikkel
|
||||||
#define __debug_log(message) krikkel::Debug::getInstance()\
|
#define __debug_log(message) krikkel::Debug::getInstance()\
|
||||||
->log(message, __FILE__, __LINE__, __func__)
|
->log(message, __FILE__, __LINE__, __func__)
|
||||||
|
|
||||||
|
|
||||||
|
/// @todo need an explanation on why and how to use this
|
||||||
|
/// this is the complete opposite of self-explanatory
|
||||||
|
#define __debug_stringify_switch_begin(closureName, __prop, __value) \
|
||||||
|
auto __debug_stringify__##closureName = [__prop, __value]() -> std::string \
|
||||||
|
{ \
|
||||||
|
std::string propertyName, valueString; \
|
||||||
|
switch(__prop) \
|
||||||
|
{
|
||||||
|
#define __debug_stringify_switch_case(caseTestValue) \
|
||||||
|
case caseTestValue: \
|
||||||
|
propertyName = (propertyName.empty() ? #caseTestValue : propertyName)
|
||||||
|
#define __debug_stringify_switch_case_end_bool(value) \
|
||||||
|
valueString = (value ? "true" : "false"); \
|
||||||
|
break
|
||||||
|
#define __debug_stringify_switch_case_end_string(value) \
|
||||||
|
valueString = "\"" + std::string(value) + "\""; \
|
||||||
|
break
|
||||||
|
#define __debug_stringify_switch_case_end_number(value) \
|
||||||
|
valueString = std::to_string(value); \
|
||||||
|
break;
|
||||||
|
#define __debug_stringify_switch_end(__prop) \
|
||||||
|
default: \
|
||||||
|
propertyName = "default case triggerd for " + std::to_string(__prop); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
return propertyName + " = " + valueString; \
|
||||||
|
}
|
||||||
|
#define __debug_stringify_get_string(closureName) __debug_stringify__##closureName()
|
||||||
|
|
||||||
|
#define __debug_bytes_printable_table(unprintable, numberOfBytes) ([](const char *bytes, size_t length) \
|
||||||
|
{ \
|
||||||
|
std::locale loc; \
|
||||||
|
size_t index, targetIndex = 0; \
|
||||||
|
size_t targetLength = (length / 16 + 1) * (3 + 3 * 16 + 1) + 1; \
|
||||||
|
char targetBytes[targetLength]; \
|
||||||
|
for(index = 0; index < length; index++) \
|
||||||
|
{ \
|
||||||
|
if(std::isprint(bytes[index], loc)) \
|
||||||
|
targetIndex += snprintf(targetBytes + targetIndex, targetLength - targetIndex, index % 16 == 0 ? " | %c" : " %c", bytes[index]); \
|
||||||
|
else \
|
||||||
|
targetIndex += snprintf(targetBytes + targetIndex, targetLength - targetIndex, index % 16 == 0 ? " | %02x" : " %02x", bytes[index]); \
|
||||||
|
if(index % 16 == 15) \
|
||||||
|
targetIndex += snprintf(targetBytes + targetIndex, targetLength - targetIndex, "\n"); \
|
||||||
|
} \
|
||||||
|
if(index % 16) \
|
||||||
|
snprintf(targetBytes + targetIndex, targetLength - targetIndex, "\n"); \
|
||||||
|
return std::string(targetBytes); \
|
||||||
|
} \
|
||||||
|
)(unprintable, numberOfBytes)
|
||||||
|
|
||||||
|
#define __debug_bytes_printable(bytes, length) ([](std::string unprintable, size_t numberOfBytes) -> std::string \
|
||||||
|
{ \
|
||||||
|
std::locale loc; \
|
||||||
|
std::string result; \
|
||||||
|
for(char character : unprintable) \
|
||||||
|
{ \
|
||||||
|
if(!numberOfBytes--) \
|
||||||
|
break; \
|
||||||
|
if(std::isprint(character, loc) && character != '<') \
|
||||||
|
result += character; \
|
||||||
|
else \
|
||||||
|
result += "<" + std::to_string(character) + ">"; \
|
||||||
|
} \
|
||||||
|
return result; \
|
||||||
|
})(bytes, length)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
/// @todo check what's the business with this "((void)0)" instead of empty macro
|
/// @todo check what's the business with this "((void)0)" instead of empty macro
|
||||||
#define __debug_log(message)
|
#define __debug_log(message)
|
||||||
|
#define __debug_stringify_switch_begin(closureName, __prop, __value)
|
||||||
|
#define __debug_stringify_switch_case(caseTestValue)
|
||||||
|
#define __debug_stringify_switch_case_end_bool(value)
|
||||||
|
#define __debug_stringify_switch_case_end_string(value)
|
||||||
|
#define __debug_stringify_switch_case_end_number(value)
|
||||||
|
#define __debug_stringify_switch_end()
|
||||||
|
#define __debug_bytes_printable(bytes)
|
||||||
|
|
||||||
#endif /* NDEBUG */
|
#endif /* NDEBUG */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue