/** * @brief Writes messages to `debug.log` if `NDEBUG` is not defined. * @author Christian Burger (christian@krikkel.de) * @todo refactor macros */ #ifndef __DEBUG_H__ #define __DEBUG_H__ #ifndef NDEBUG #include #include #include #include namespace krikkel { class Debug { private: std::fstream logFile; static const int MAX_NUMBER_OF_SYSCALLS = 1024; static const char * syscalls[MAX_NUMBER_OF_SYSCALLS]; Debug(); std::string getTimestamp(); std::string getUname(); std::string peekData(pid_t shellPid, void *data, size_t length); std::string formatToAddress(unsigned long long address); public: void log(std::string message, std::string fileName, int lineNo , std::string functionName); void logPtraceSysCall(pid_t shellPid, uint16_t sysCallId, bool returnedFromSysCall, unsigned long long result, unsigned long long firstArgument, unsigned long long secondArgument, unsigned long long thirdArgument, unsigned long long fourthArgument, std::string fileName, int lineNo, std::string functionName); static Debug *getInstance(); }; } #define __debug_log(message) \ krikkel::Debug::getInstance()->log(message, __FILE__, __LINE__, __func__) #define __debug_log_ptrace_syscall(shellPid, sysCallId, returnedFromSysCall, result, firstArgument, secondArgument, thirdArgument, fourthArgument) \ krikkel::Debug::getInstance()->logPtraceSysCall(shellPid, sysCallId, returnedFromSysCall, result, firstArgument, secondArgument, thirdArgument, fourthArgument, __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); \ [[fallthrough]] #define __debug_stringify_switch_last_case_bool(caseTestValue, value) \ case caseTestValue: \ propertyName = (propertyName.empty() ? #caseTestValue : propertyName); \ valueString = (value ? "true" : "false"); \ break #define __debug_stringify_switch_last_case_string(caseTestValue, value) \ case caseTestValue: \ propertyName = (propertyName.empty() ? #caseTestValue : propertyName); \ valueString = "\"" + std::string(value) + "\""; \ break #define __debug_stringify_switch_last_case_number(caseTestValue, value) \ case caseTestValue: \ propertyName = (propertyName.empty() ? #caseTestValue : propertyName); \ 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_make_bytes_printable_table(__bytes) ([](std::string bytes) -> std::string \ { \ std::locale loc; \ std::string result = "\n"; \ int index; \ for(index = 0; index < bytes.length(); ++index) \ { \ static const uint8_t TRANSLATION_BUFFER_SIZE = 4; \ char translationBuffer[TRANSLATION_BUFFER_SIZE]; \ if(std::isprint(bytes[index], loc)) \ { \ translationBuffer[0] = translationBuffer[1] = ' '; \ translationBuffer[2] = bytes[index]; \ translationBuffer[3] = '\0'; \ } \ else \ std::snprintf(translationBuffer, TRANSLATION_BUFFER_SIZE, " %02x", bytes[index]); \ result += (index % 16 == 0 ? " | " : " "); \ result += translationBuffer; \ if(index % 16 == 15) \ result += '\n'; \ } \ if(index % 16) \ result += '\n'; \ return result; \ } \ )(__bytes) #define __debug_make_bytes_printable(__bytes) ([](std::string bytes) -> std::string \ { \ std::locale loc; \ std::string result; \ for(char character : bytes) \ { \ if(std::isprint(character, loc) && character != '<') \ result += character; \ else \ result += '<' + std::to_string((unsigned char) character) + '>'; \ } \ return result; \ })(__bytes) #else /// @todo check what's the business with this "((void)0)" instead of empty macro #define __debug_log(message) #define __debug_stringify_switch_begin(closureName, __prop, __value) #define __debug_stringify_switch_case(caseTestValue) #define __debug_stringify_switch_last_case_bool(caseTestValue, value) #define __debug_stringify_switch_last_case_string(caseTestValue, value) #define __debug_stringify_switch_last_case_number(caseTestValue, value) #define __debug_stringify_switch_end(__prop) #define __debug_make_bytes_printable_table(__bytes) #define __debug_make_bytes_printable(__bytes) #endif /* NDEBUG */ #endif // __DEBUG_H__