/********************************************************************************** * Copyright (C) 2020 Dominik Egert * * This file is the lib_debug demonstration file. * * Lisence applied: Free, no license applied to this file. * * Author Dominik Egert / Freie Netze UG. ********************************************************************************** * * Version: 1.01 * State: public * * File information * ================ * */ #property version "1.01" /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Additional options for LIB_DEBUG // // LIB_DEBUG_MQLAPI_TRACE // // MQL API function call tracing support. This will log calls to MQL built-in functions. // "LIB_DEBUG_MQLAPI_TRACE" will enable this feature. You will get trace output, if you have // LIB_DEBUG built-in tracing for the particular function enabled. - See example below on how to // integrate tracing of a function. // // Functions for which tracing is activated, see description below, will now // also log calls to MQL-API functions. The error code and return type will be logged. // // // !!! IMPORTANT NOTE !!! // // MQL-API-Function tracing is incompatible with the standard library from MQL. // You will receive compiler errors, if activated and using standard-library. // // If you wish to do so anyways, move forward to "Custom Integration" below // to learn how you could do it anyways. // // // LIMITATION when using LIB_DEBUG_MQLAPI_TRACE // // If you are callling an MQL function on global scope, you must do so before // including lib_debug.mqh. // // Example: // // // Global MQL function calls must happen before "#include lib_debug" // const string files = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\files\\"; // const uint ticks = GetTickCount(); // // // Include lib_debug // #define LIB_MQLAPI_TRACE // #include // // These global calls will not be tracked. // ///////////////////////////////////////// // // Include the library // #define LIB_DEBUG #define LIB_DEBUG_MQLAPI_TRACE #include /////////////////////////////////////////////////////////////////////// // // In OnInit you can see a simple usage of the debugging macros. // //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { ////////////////////////////////////////////////////////////// // // Simple output macros // // Some initial debug message DBG_MSG("Example how to use lib_debug.mqh as project integrated version."); // Output details about current program // Available in debugging and runtime printf("%s", DBG_STR_EX45_FILEINFO); // Keep this message also in runtime builds DBG_MSG_PERSIST("Persistent message in debug and runtime (Runtime is without debug details)"); // Return return(INIT_SUCCEEDED); } /////////////////////////////////////////////////////////////////////// // // In OnDeinit we will use full tracing of the function call. // // First we define some helpers for compile time code // inclusion selection. This will make it easy to enable/disable // function tracing as needed. // // To do this, first a macro is defined which will be used as a // switch (defined = trace function) (not defined = disable tracing) // // We can collect these "switches" at top of file for a better overview // Also we could wrap them into a condition, so that they will only be // defined when debugging is enabled. // // Example: // #ifdef LIB_DEBUG // #define DBG_TRACE_EXPERT_MAIN_ONDEINIT // // #endif // En/Disable function tracing by commenting following macro: #define DBG_TRACE_EXPERT_MAIN_ONDEINIT // Now we define the helper macros which will be used inside // the functions body. //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function debug trace code #ifdef DBG_TRACE_EXPERT_MAIN_ONDEINIT #undef DBG_TRACE_EXPERT_MAIN_ONDEINIT #define DBG_TRACE_EXPERT_MAIN_ONDEINIT(x) x #define DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN DBG_MSG_TRACE_RETURN #else #define DBG_TRACE_EXPERT_MAIN_ONDEINIT(x) #define DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN DBG_MSG_NOTRACE_RETURN #endif ///////////////////////////////////// void OnDeinit(const int reason) { ///////////////////////////////////// // // TOP of function body // // Here the tracing code gets included, this is mandatory for tracing to work. // The switching macro can also be used to include additional // debugging code, if any is required, see example below. DBG_TRACE_EXPERT_MAIN_ONDEINIT( DBG_MSG_TRACE_BEGIN; // Additional details can be added here, as required. // For example to trace the inputs to this function, we can add DBG_MSG_VAR(reason); // Or whatever we would like to add. ); // Additionally the macro "PERF_COUNTER_BEGIN" must be added. // It is mandatory to complete the "return" macro, irrespective of it // being used or not. (Any insertions will be removed for runtime builds, // if "LIB_PERF_PROFILING" is not defined) PERF_COUNTER_BEGIN; // // This concludes the functions head // control code required for tracing. // If tracing is disabled for this function, // all additional code will be stripped at // compile time. // // Now follows the actual functions body: ///////////////////////////////////// // Local init int some_int_val = 3; string some_string = "I dont know"; ///////////////////////////////////// // // As an example, here the local variables // are printed, but only if tracing is enbaled. // In contrast to the usage of macros in OnInit, where // the macros were not wrapped in the // Trace-Enable-Disable macro. // DBG_TRACE_EXPERT_MAIN_ONDEINIT( DBG_MSG_VAR(some_int_val); DBG_MSG_VAR(some_string); ); // Destroy timer EventKillTimer(); ///////////////////////////////////// // // EXITING/RETURNING from the fucntions body. // // A return statement is mandatory, no matter the functions signature. // This will inform the debug library about the functions ending/exiting. // // The special function "OnDeinit" will auto-resolve the deinit reason // and show the state of the _StopFlag/IsStopped() variable. // This is automatically inserted and needs no extra care taking. // DBG_TRACE_EXPERT_MAIN_ONDEINIT_RETURN; } /////////////////////////////////////////////////////////////////////// // // In OnTick we will use full tracing of the function call as // shown with OnDeinit, and we will integrate global performance // counters as well. This will give output of the runtime used by // this function. // // Since the tracing has already been exlained, here is a // stripped version, showing the raw requirements for a full trace. // // Short outline. // - First define the enable/disable macros. // - Then include the head required inside the body. // - Use the return macro at all exit points of the function. // // To enable tracing for this function, same procedure as // with OnDeinit applies. // Again, this could be collected at top of file for better oversight. // Turn on tracing #define DBG_TRACE_EXPERT_MAIN_ONTICK //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function debug trace code #ifdef DBG_TRACE_EXPERT_MAIN_ONTICK #undef DBG_TRACE_EXPERT_MAIN_ONTICK #define DBG_TRACE_EXPERT_MAIN_ONTICK(x) x #define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN DBG_MSG_TRACE_RETURN #else #define DBG_TRACE_EXPERT_MAIN_ONTICK(x) #define DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN DBG_MSG_NOTRACE_RETURN #endif ///////////////////////////////////// void OnTick() { DBG_TRACE_EXPERT_MAIN_ONTICK( DBG_MSG_TRACE_BEGIN; ); PERF_COUNTER_BEGIN; // Local init int some_var = MathRand(); DBG_MSG_VAR(some_var); // Some random check operation if((some_var % 2) == 0) { call_sub_function(); call_sub2_function(); call_obj_function(); } // Some eventual exit at annother point inside the functions body if((some_var % 2048) == 0) { DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN; } // Return DBG_TRACE_EXPERT_MAIN_ONTICK_RETURN; } /////////////////////////////////////////////////////////////////////// // // In call_sub_function we will use full tracing of the function call. // // This example shows the difference for a function with // a return value. // // Take notice of the used macros. - It is required to use a // different return macro for this type of function call. // // Instead of using the macro "DBG_MSG_TRACE_RETURN" and "DBG_MSG_NOTRACE_RETURN" // here the macros "DBG_MSG_TRACE_RETURN_VAR(x)" and "DBG_MSG_NOTRACE_RETURN_VAR(x)" // are used. // // For use of objects of type "struct", "class" or "interface", a separate // return value macro must be used. - (This implementation is new in MQL5) // // Returning objects from functions, use following macros: // "DBG_MSG_TRACE_RETURN_OBJ(x)" and "DBG_MSG_NOTRACE_RETURN_OBJ(x)" // // For use with pointers as return value, either macro can be used, preferably use // "DBG_MSG_TRACE_RETURN_VAR(x)". // // IMPORTANT NOTICE: // Calling the macro with the parameter "NULL" requires!!! a cast operation. // (This functions return value is "int", "const int" or "int" doesnt make a difference) // // Example: // DBG_MSG_TRACE_RETURN_VAR((int)NULL); // // This cast operation can be included in the macro definition itself, like this: // #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR((int)x) // #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x) // // or inside the function using the custom defined macro: // DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN((int)NULL); // // See second function example below. // Here the cast operation is inside the functions body. // // NOTE on CAST-OPeration: // The runtime macro does not require the cast operation. // You should include the cast-operation as shown in second example. If it is included // in the functions body, it will be also part of the runtime-code and not being stripped // from your code by the library. // // Again the enbale trace macro needs to be defined, so tracing // is enabled. // Enable tracing of this function #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION //+------------------------------------------------------------------+ //| Example function with return value | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function debug trace code #ifdef DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION #undef DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION(x) x #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR(x) #else #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION(x) #define DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x) #endif ///////////////////////////////////// const int call_sub_function() { DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION( DBG_MSG_TRACE_BEGIN; ); PERF_COUNTER_BEGIN; // Do some stuff if(MathRand() == 5) { DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN(5); } // Return DBG_TRACE_EXPERT_MAIN_CALL_SUB_FUNCTION_RETURN((int)NULL); } // Enable tracing of this function #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION //+------------------------------------------------------------------+ //| Example function with return value | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function debug trace code #ifdef DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION #undef DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION(x) x #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR((int)x) #else #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION(x) #define DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x) #endif ///////////////////////////////////// const int call_sub2_function() { DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION( DBG_MSG_TRACE_BEGIN; ); PERF_COUNTER_BEGIN; // Do some stuff if(MathRand() == 12) { DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(12); } // Return DBG_TRACE_EXPERT_MAIN_CALL_SUB2_FUNCTION_RETURN(NULL); } // A local class class CObj { public: int test; CObj() : test(NULL) { }; CObj(const CObj& p_in) { test = p_in.test; printf("%s", "COPY"); }; }; // Enable tracing of this function #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION //+------------------------------------------------------------------+ //| Example function with return object | //+------------------------------------------------------------------+ ///////////////////////////////////// // Function debug trace code #ifdef DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION #undef DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION(x) x #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(x) DBG_MSG_TRACE_RETURN_VAR(x) #else #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION(x) #define DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(x) DBG_MSG_NOTRACE_RETURN_VAR(x) #endif ///////////////////////////////////// CObj call_obj_function() { DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION( DBG_MSG_TRACE_BEGIN; ); PERF_COUNTER_BEGIN; // Local init CObj test_obj; CObj* test_ptr = new CObj(); Print("1"); DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION( DBG_MSG_VAR(1); DBG_MSG_VAR(test_obj); DBG_MSG_VAR(test_ptr); ); // Do some stuff if(MathRand() == 12) { DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(test_obj); } // Return DBG_TRACE_EXPERT_MAIN_CALL_OBJ_FUNCTION_RETURN(test_obj); }