#ifndef LIB_DBG_DEBUG_MACROS_MQH_INCLUDED #define LIB_DBG_DEBUG_MACROS_MQH_INCLUDED #property version "5.10" /********************************************************************************** * Copyright (C) 2020 Dominik Egert * * This file is the debugger main user macros include file. * * Lisence applied: GPLv2 * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Author Dominik Egert / Freie Netze UG. ********************************************************************************** * * Version: 5.10 * State: public * * File information * ================ * */ //*********************************************************************************************************************************************************/ // BEGIN Debugging macros // ///////////////////////////////////////////////////////////////////////////////////////////////////// // // Debugger helpers // #ifndef LIB_DEBUG_INCLUDE_STUB // Stringify macro helper #define DBG_TO_STRING(x) LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)("%s", __DBG_STRINGIFY_MACRO(x)); // Custom register enum resolver macro #ifdef __MQL5__ #define DBG_ENUM_RESOLVER(x) namespace dbg_lib { const string LIB_DBG_NAMESPACE_DEF(dbg_lib, var_out)(const string name, x val, const int shift = 0, const string prefix = "", const int offset = 0, const bool hex = DBG_HEX_OUTPUT) { return(LIB_DBG_NAMESPACE_DEF(dbg_lib, enum_to_string)(name, val, shift, prefix, offset, hex)); }; } #else #define DBG_ENUM_RESOLVER(x) const string LIB_DBG_NAMESPACE_DEF(dbg_lib, var_out)(const string name, x val, const int shift = 0, const string prefix = "", const int offset = 0, const bool hex = DBG_HEX_OUTPUT) { return(LIB_DBG_NAMESPACE_DEF(dbg_lib, enum_to_string)(name, val, shift, prefix, offset, hex)); } #endif // Debug to string functions #define DBG_STR(x) LIB_DBG_NAMESPACE(dbg_lib, dbg_context_msg)(DBG_OUTPUT_PREFIX, __FILE__, __FUNCTION__, __LINE__, x) #define DBG_STR_IF(c, x) ((c) ? LIB_DBG_NAMESPACE(dbg_lib, dbg_context_msg)(DBG_OUTPUT_PREFIX, __FILE__, __FUNCTION__, __LINE__, LIB_DBG_NAMESPACE(dbg_lib, dbg_StringFormat)("$$DBG_IF(%s)$$ :--> %s", #c, x)) : NULL) #define DBG_STR_VAR(x) LIB_DBG_NAMESPACE(dbg_lib, dbg_context_msg)(DBG_OUTPUT_PREFIX, __FILE__, __FUNCTION__, __LINE__, LIB_DBG_NAMESPACE(dbg_lib, var_out)(__DBG_STRINGIFY_MACRO((x)), (x)), "=", DBG_MSG_FORMAT_RIGHT_COLUMN_SPACER) #define DBG_STR_VAR_IF(c, x) ((c) ? LIB_DBG_NAMESPACE(dbg_lib, dbg_context_msg)(DBG_OUTPUT_PREFIX, __FILE__, __FUNCTION__, __LINE__, LIB_DBG_NAMESPACE(dbg_lib, dbg_StringFormat)("$$DBG_IF(%s)$$ :--> %s", #c, LIB_DBG_NAMESPACE(dbg_lib, var_out)(__DBG_STRINGIFY_MACRO((x)), (x))), "=", DBG_MSG_FORMAT_RIGHT_COLUMN_SPACER) : NULL) #define DBG_STR_PERSIST(x) DBG_STR(x) #define DBG_STR_BITS(x) LIB_DBG_NAMESPACE(dbg_lib, dbg_context_msg)(DBG_OUTPUT_PREFIX, __FILE__, __FUNCTION__, __LINE__, LIB_DBG_NAMESPACE(dbg_lib, var_out)(__DBG_STRINGIFY_MACRO((x)), (x), 0, "", 0, false), "=", DBG_MSG_FORMAT_RIGHT_COLUMN_SPACER) #define DBG_STR_ERRCODE(x) LIB_DBG_NAMESPACE(dbg_lib, dbg_context_msg)(DBG_OUTPUT_PREFIX, __FILE__, __FUNCTION__, __LINE__, LIB_DBG_NAMESPACE(dbg_lib, var_out)(__DBG_STRINGIFY_MACRO((x)), LIB_DBG_NAMESPACE(dbg_lib, dbg_errcode_to_name)(x)), "=", DBG_MSG_FORMAT_RIGHT_COLUMN_SPACER) // Debug std out #define DBG_MSG(x) LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)(DBG_STR(x)); #define DBG_MSG_IF(c, x) { if(c) { LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)(DBG_STR_IF(c, x)); } } #define DBG_MSG_VAR(x) LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)(DBG_STR_VAR(x)); #define DBG_MSG_VAR_IF(c, x) { if(c) { LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)(DBG_STR_VAR_IF(c, x)); } } #define DBG_MSG_PERSIST(x) DBG_MSG(x) #define DBG_MSG_BITS(x) LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)(DBG_STR_BITS(x)) #define DBG_MSG_ERRCODE(x) LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)(DBG_STR_ERRCODE(x), true) // Var dump macro #define DBG_MSG_LISTDUMP(x, y) LIB_DBG_NAMESPACE(dbg_lib, list_dump_arrays)(x, #x, y, #y, __FILE__, __FUNCTION__, __LINE__, DBG_MSG_VAR_ARRAY_LIMIT(x)) // Tracing inside ()-condition statements #define DBG_MSG_EVAL(x) (LIB_DBG_NAMESPACE(dbg_lib, dbg_eval_return)(__FILE__, __FUNCTION__, __LINE__, __DBG_STRINGIFY_MACRO(x), x)) #define DBG_MSG_EVAL_IF(c, x) (LIB_DBG_NAMESPACE(dbg_lib, dbg_eval_return)(__FILE__, __FUNCTION__, __LINE__, __DBG_STRINGIFY_MACRO(x), x, c)) #define DBG_MSG_EVAL_CMNT(m, x) (LIB_DBG_NAMESPACE(dbg_lib, dbg_eval_return)(__FILE__, __FUNCTION__, __LINE__, __DBG_STRINGIFY_MACRO(x), x, true, m)) #define DBG_MSG_EVAL_IF_CMNT(c, m, x) (LIB_DBG_NAMESPACE(dbg_lib, dbg_eval_return)(__FILE__, __FUNCTION__, __LINE__, __DBG_STRINGIFY_MACRO(x), x, c, m)) // Array check macro #define DBG_MSG_ARRAY_OUT_OF_RANGE(x, y) { if((y < NULL) || (y >= LIB_DBG_NAMESPACE(dbg_lib, dbg_ArraySize)(x))) { DBG_MSG(LIB_DBG_NAMESPACE(dbg_lib, dbg_StringFormat)("%s{%i] :--> ARRAY OUT OF RANGE", __DBG_STRINGIFY_MACRO(x), y)); DBG_MSG_VAR(x); } } // Program uninit reason tracing #define DBG_SET_UNINIT_REASON(x) LIB_DBG_NAMESPACE(dbg_lib, dbg_uninit_reason) = x; #define DBG_MSG_UNINIT_RESOLVER if(_StopFlag) { DBG_MSG_VAR(_StopFlag); } LIB_DBG_NAMESPACE(dbg_lib, uninit_text)(__FUNCTION__, -1); // Include-File loader tracing #ifdef __MQL5__ #define DBG_FILE_VARNAME(x) dbg_file_loader_##x #define DBG_MSG_TRACE_FILE_LOADER static bool DBG_FILE_VARNAME(__COUNTER__) = LIB_DBG_NAMESPACE(dbg_lib, dbg_print_file_trace)(__FILE__); #else #define DBG_MSG_TRACE_FILE_LOADER #endif #endif // MQL-API function tracer macro #ifndef DBG_MSG_MQLFUNC #define DBG_MSG_MQLFUNC(x, id) dbg_mql_api_call_tracer.trace_call_details(__LINE__, id, __FILE__, __FUNCTION__, #x, DBG_MQLAPI_VERSION).x #endif // ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// // // Assert support // // Generic assert definition #define DBG_GENERIC_ASSERT(condition, message) { if(!(condition)) { string msg = LIB_DBG_NAMESPACE(dbg_lib, dbg_StringFormat)(DBG_OUTPUT_STRING, ((#condition == "") ? "" : #condition + " "), DBG_CODE_LOCATION_STRING, message); // Call modal dialog and div/zero! #define DBG_ASSERT(condition, message) DBG_GENERIC_ASSERT(condition, message) LIB_DBG_NAMESPACE(dbg_lib, dbg_Alert)(DBG_ASSERT_MSG_TXT + " " + msg); DBG_CRASH_CODE; } } // Print out information #define DBG_ASSERT_LOG(condition, message) DBG_GENERIC_ASSERT(condition, message) LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)("%s%s %s", DBG_OUTPUT_PREFIX + DBG_OUTPUT_PREFIX, DBG_ASSERT_MSG_TXT, msg); } } // Print info and abort execution by return; #define DBG_ASSERT_RETURN(condition, message) DBG_GENERIC_ASSERT(condition, message) LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)("%s%s %s", DBG_OUTPUT_PREFIX + DBG_OUTPUT_PREFIX, DBG_ASSERT_MSG_TXT, msg); return; } } // Print info and abort execution by return; #define DBG_ASSERT_RETURN_VAR(condition, message, return_value) DBG_GENERIC_ASSERT(condition, message) LIB_DBG_NAMESPACE(dbg_lib, dbg_printf)("%s%s %s", DBG_OUTPUT_PREFIX + DBG_OUTPUT_PREFIX, DBG_ASSERT_MSG_TXT, msg); return(return_value); } } // ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// // // Software inherited brakepoints // // Debugging macros #define DBG_SOFT_BREAKPOINT { LIB_DBG_NAMESPACE(dbg_lib, BreakPoint)(__LINE__, __FILE__); if(IS_DEBUG_MODE) { DebugBreak(); } else { DBG_SLEEP_SECONDS(DBG_SOFT_BKP_TIMEOUT); } } #define DBG_SOFT_BREAKPOINT_TS(timestamp) { LIB_DBG_NAMESPACE(dbg_lib, BreakPoint)(__LINE__, __FILE__, LIB_DBG_NAMESPACE(dbg_lib, dbg_StringToTime)(timestamp)); DBG_SOFT_BREAKPOINT } #define DBG_SOFT_BREAKPOINT_CONDITION(x) { if((x) && (LIB_DBG_NAMESPACE(dbg_lib, BreakPoint)(__LINE__, __FILE__, LIB_DBG_NAMESPACE(dbg_lib, dbg_TimeCurrent)() + DBG_SOFT_BKP_TIMEOUT))) { DBG_SOFT_BREAKPOINT; } } #define DBG_SOFT_BREAKPOINT_EXEC_TIME(x) { DBG_SOFT_BREAKPOINT_CONDITION(LIB_DBG_NAMESPACE(dbg_lib, ExecTime)(x)); LIB_DBG_NAMESPACE(dbg_lib, ExecTime)(NULL); } // Conditional break debugging macros #define DBG_BREAK_CALL_COUNTER(x) if(dbg_lib_func_call_count == x) { DebugBreak(); } #define DBG_BREAK_IF(x) if(x) { DebugBreak(); } #ifdef __MQL5__ #define DBG_BREAK_CONDITION_CREATE(x, y) namespace dbg_lib { static bool dbg_cond_ID_state_##x = false; static bool dbg_cond_ID_enabled_##x = y; } #define DBG_BREAK_CONDITION_ACTIVATE(x, y) do { dbg_lib::dbg_cond_ID_state_##x |= (y); } while(false) #define DBG_BREAK_CONDITION_DEACTIVATE(x, y) do { dbg_lib::dbg_cond_ID_state_##x = !(y); } while(false) #define DBG_BREAK_CONDITION_ON_ID(x) do { if(dbg_lib::dbg_cond_ID_state_##x && dbg_lib::dbg_cond_ID_enabled_##x) { DebugBreak(); } } while(false) #define DBG_BREAK_CONDITION_ON_ID_RESET(x) do { if(dbg_lib::dbg_cond_ID_state_##x && dbg_lib::dbg_cond_ID_enabled_##x) { dbg_lib::dbg_cond_ID_state_##x = false; DebugBreak(); } } while(false) #else #define DBG_BREAK_CONDITION_CREATE(x, y) static bool dbg_cond_ID_state_##x = false; static bool dbg_cond_ID_enabled_##x = y; #define DBG_BREAK_CONDITION_ACTIVATE(x, y) dbg_cond_ID_state_##x |= (y); #define DBG_BREAK_CONDITION_DEACTIVATE(x, y) dbg_cond_ID_state_##x = !(y); #define DBG_BREAK_CONDITION_ON_ID(x) { if(dbg_cond_ID_state_##x && dbg_cond_ID_enabled_##x) { DebugBreak(); } } #define DBG_BREAK_CONDITION_ON_ID_RESET(x) { if(dbg_cond_ID_state_##x && dbg_cond_ID_enabled_##x) { dbg_cond_ID_state_##x = false; DebugBreak(); } } #endif // Array out of range stop macro #define DBG_BREAK_ARRAY_OUT_OF_RANGE(x, y) { if((LIB_DBG_NAMESPACE(dbg_lib, dbg_ArraySize)(x) <= (y)) || ((y) < NULL)) { DBG_MSG_VARDUMP(x); DBG_MSG_VAR(typename_raw(x)); DBG_MSG_VAR(LIB_DBG_NAMESPACE(dbg_lib, dbg_ArraySize)(x)); DBG_MSG_VAR((y)); DebugBreak(); } } /* #ifdef __MQL5__ #define DBG_BREAK_ARRAY_OUT_OF_RANGE(x, y) { const int arr_size = LIB_DBG_NAMESPACE(dbg_lib, dbg_ArraySize)(x); if((arr_size <= y) || (y < NULL)) { LIB_DBG_NAMESPACE(dbg_lib, dbg_ArrayPrint)(x, _Digits, (arr_size < 5) ? NULL : (arr_size - 5), NULL); DBG_MSG_VAR(typename_raw(x)); DBG_MSG_VAR(LIB_DBG_NAMESPACE(dbg_lib, dbg_ArraySize)(x)); DBG_MSG_VAR(y); DebugBreak(); } } #else #define DBG_BREAK_ARRAY_OUT_OF_RANGE(x, y) { if((LIB_DBG_NAMESPACE(dbg_lib, dbg_ArraySize)(x) <= y) || (y < NULL)) { DBG_MSG_VARDUMP(x); DBG_MSG_VAR(typename_raw(x)); DBG_MSG_VAR(LIB_DBG_NAMESPACE(dbg_lib, dbg_ArraySize)(x)); DBG_MSG_VAR(y); DebugBreak(); } } #endif */ // ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// // // Loop tracing // // Init loop trace #define DBG_TRACE_LOOP_BEGIN_ID(x) int _dbg_loop_start##x = 0x00; int _dbg_loop_finish##x = 0x00; ulong _dbg_loop_runtime##x = LIB_DBG_NAMESPACE(dbg_lib, dbg_GetMicrosecondCount)(); #define DBG_TRACE_LOOP_BEGIN DBG_TRACE_LOOP_BEGIN_ID(__FUNCTION__) // Count loop heads #define DBG_TRACE_LOOP_START_ID(x) _dbg_loop_start##x++; #define DBG_TRACE_LOOP_START DBG_TRACE_LOOP_START_ID(__FUNCTION__) // Count loop footers #define DBG_TRACE_LOOP_FINISH_ID(x) _dbg_loop_finish##x++; #define DBG_TRACE_LOOP_FINISH DBG_TRACE_LOOP_FINISH_ID(__FUNCTION__) // Print loop stats #define DBG_TRACE_LOOP_END_ID(x) DBG_MSG(LIB_DBG_NAMESPACE(dbg_lib, dbg_StringFormat)("Iterations begin: %i; Iterations end: %i; Runtime: %i microseconds.", _dbg_loop_start##x, _dbg_loop_finish##x, (LIB_DBG_NAMESPACE(dbg_lib, dbg_GetMicrosecondCount)() - _dbg_loop_runtime##x))); #define DBG_TRACE_LOOP_END DBG_TRACE_LOOP_END_ID(__FUNCTION__) // ///////////////////////////////////////////////////////////////////////////////////////////////////// // // END Debugging support //*********************************************************************************************************************************************************/ #endif // LIB_DBG_DEBUG_MACROS_MQH_INCLUDED