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.
Gentoo
Christian Burger 2022-04-08 21:00:10 +02:00
parent d01917c82d
commit fd800dbca8
2 changed files with 89 additions and 14 deletions

View File

@ -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);

View File

@ -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 */