Raised This Month: $51 Target: $400
 12% 

[TUT] How to use an efficient debug system, which does not overload the final code


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
addons_zz
Veteran Member
Join Date: Aug 2015
Location: Dreams, zz
Old 01-13-2016 , 18:33   [TUT] How to use an efficient debug system, which does not overload the final code
Reply With Quote #1

[Tutorial]
How to use an efficient debug system,
which do not overload your final code


Contents' Table

Introduction | go to top

Recently when developing plugins, was found a way to do a debug system that you do not need to delete after you configure it. This means that, if in the future you need to re-use you debug system, you do not need to re-create it, because it was never deleted. And was never deleted because you do not need to delete it, it is invisible to you final code and the Amx Mod X compiler itself, but not for its preprocessor.

First, here are some links to you get inside to what means to debug code, or debugging:
  1. Debug code
  2. Debugging
  3. Good programming habits
  4. [TUT] Code Styling & Good Programming Habits
  5. [INFO] Pawn compiler constants
  6. PAWN Pre-Processor - Introduction - Part 1/7
  7. [HOW TO] Debug your plugins
  8. [TUT] Compiling errors
  9. Boston University - Debugging
  10. Scripting Help (Suggested) Posting Guidelines
  11. How to be a zero-bug programmer?
  12. C preprocessor
  13. How to fix bugs, step by step
  14. Finding Bugs in your Projects
  15. A Gentle Introduction to Programming
  16. Known Runtime/Compiler PAWN Bugs


Points
| go to top
  1. This debugging system is controlled by a preprocessor directive called 'IS_DEBUG_ENABLED'. This is how we declare it to our needs:
    Code:
    /** This is to view internal program data while execution. See the function 'debugMesssageLogger(...)'  * and the variable 'g_debug_level' for more information. Default value: 0  - which is disabled.  */ #define IS_DEBUG_ENABLED 1
  2. Once we declare it with the value 1, the preprocessor will understand that the debug system is active, then it will allow our Amx Mod X compiler to see our debugging code, i.e., compile it to our final binary file '.amxx'. But if the directive 'IS_DEBUG_ENABLED' is declared with the value 0, the preprocessor will understand that the debug system is inactive, then it will not allow our Amx Mod X compiler to see its code, i.e., compile it to our final binary file '.amxx'.

    Now we define what will be our debug logger, that is controlled by our directive 'IS_DEBUG_ENABLED':
    Code:
    #if IS_DEBUG_ENABLED > 0     #define DEBUG_LOGGER(%1) debugMesssageLogger( %1 ); /**  * ( 0 ) 0 disabled all debug.  * ( 1 ) 1 displays basic debug messages.  */ new g_debug_level = 1; #else     #define DEBUG_LOGGER(%1) #endif
  3. If the preprocessor directive 'IS_DEBUG_ENABLED' is declared as '1', i.e., active, we define that the preprocessor function DEBUG_LOGGER is to be recognized as our real debugger function 'debugMesssageLogger'. But if it is disabled, all debug functions at our code, will be recognized by the compiler as just a simple empty statement, which means that is like they never existed. See:
    1. The choice for allow semicolons on Macros

    Now let us see the printer function 'debugMesssageLogger', mentioned above, with outputs the debug messages to the console:
    Code:
    #define LONG_STRING 256 /**  * Write debug messages to server's console accordantly to the global variable g_debug_level.  *  * @param mode the debug mode level, see the variable 'g_debug_level' for the levels.  * @param message[] the text formatting rules to display.  * @param any the variable number of formatting parameters.  */ stock debugMesssageLogger( mode, message[], any: ... ) {     if( mode & g_debug_level )     {         static formated_message[ LONG_STRING ];                 vformat( formated_message, charsmax( formated_message ), message, 3 );                 server_print( "%s", formated_message         );         client_print(       0, print_console,             "%s", formated_message );     } }

Gathering Everthing | go to top

So, gathering every thing mentioned until now, we get all we need to build are debug system. He it is the full code:
Code:
#include <amxmodx> #include <amxmisc> #define LONG_STRING 256 /** This is to view internal program data while execution. See the function 'debugMesssageLogger(...)'  * and the variable 'g_debug_level' for more information. Default value: 0  - which is disabled.  */ #define IS_DEBUG_ENABLED 1 #if IS_DEBUG_ENABLED > 0     #define DEBUG_LOGGER(%1) debugMesssageLogger( %1 ); /**  * ( 0 ) 0 disabled all debug.  * ( 1 ) 1 displays basic debug messages.  * ( 10 ) 2 displays leaving functions messages.  * ( 100 ) 4 displays do_debug() messages.  * ( 111 ) 7 displays all debug levels.  */ new g_debug_level = 7; /**  * Write debug messages to server's console accordantly to the global variable g_debug_level.  *  * @param mode the debug mode level, see the variable 'g_debug_level' for the levels.  * @param message[] the text formatting rules to display.  * @param any the variable number of formatting parameters.  */ stock debugMesssageLogger( mode, message[], any: ... ) {     if( mode & g_debug_level )     {         static formated_message[ LONG_STRING ];                 vformat( formated_message, charsmax( formated_message ), message, 3 );                 server_print( "%s", formated_message         );         client_print(       0, print_console,             "%s", formated_message );     } } #else     #define DEBUG_LOGGER(%1) #endif /*  * Called just after server activation.  */ public plugin_init() {     DEBUG_LOGGER( 1"^n^nI am ENTERING on plugin_init()" )     register_plugin( "Cool Plugin", "1.0", "Addons zz" );     register_concmd( "amx_next", "do_debug" );     do_debug();     DEBUG_LOGGER( 2"I am LEAVING on plugin_init()^n" ) } public do_debug() {     new hour;     new minute;     new second;         time( hour, minute, second );     DEBUG_LOGGER( 3"^nI am doing debug, the current time is: %d:%d:%d^n", \             hour, minute, second ) }
This would out put this at the server/client console:
Code:
I am ENTERING on plugin_init()

I am doing debug, the current time is: 19:55:12

I am LEAVING on plugin_init()

Examples | go to top
  1. This function below integrates the mapchooser Galileo:
    Code:
    stock client_print_color_internal( player_id, message[], any: ... ) {     new formated_message[ COLOR_MESSAGE ];         if( g_is_color_chat_supported )     { #if AMXX_VERSION_NUM < 183         if( player_id )         {             vformat( formated_message, charsmax( formated_message ), message, 3 );             DEBUG_LOGGER( 64, "( in ) Player_Id: %d, Chat printed: %s", player_id, formated_message )                         PRINT_COLORED_MESSAGE( player_id, formated_message )         }         else         {             new players_array[ 32 ];             new players_number;                         get_players( players_array, players_number, "ch" );                         // Figure out if at least 1 player is connected             // so we don't execute useless code             if( !players_number )             {                 DEBUG_LOGGER( 64, "!players_number. players_number = %d", players_number )                 return;             }                         new player_id;             new string_index;             new argument_index;             new multi_lingual_constants_number;                         new params_number                     = numargs();             new Array:multi_lingual_indexes_array = ArrayCreate();                         DEBUG_LOGGER( 64, "players_number: %d, params_number: %d", players_number, params_number )                         if( params_number >= 4 ) // ML can be used             {                 for( argument_index = 2; argument_index < params_number; argument_index++ )                 {                     DEBUG_LOGGER( 64, "argument_index: %d, getarg(argument_index): %d / %s", \                             argument_index, getarg( argument_index ), getarg( argument_index ) )                                         // retrieve original param value and check if it's LANG_PLAYER value                     if( getarg( argument_index ) == LANG_PLAYER )                     {                         string_index = 0;                                                 // as LANG_PLAYER == -1, check if next param string is a registered language translation                         while( ( formated_message[ string_index ] =                                      getarg( argument_index + 1, string_index++ ) ) )                         {                         }                         formated_message[ string_index ] = 0;                                                 DEBUG_LOGGER( 64, "Player_Id: %d, formated_message: %s, \                                 GetLangTransKey( formated_message ) != TransKey_Bad: %d", \                                 player_id, formated_message, \                                 GetLangTransKey( formated_message ) != TransKey_Bad )                                                 DEBUG_LOGGER( 64, "(multi_lingual_constants_number: %d, string_index: %d", \                                 multi_lingual_constants_number, string_index )                                                 if( GetLangTransKey( formated_message ) != TransKey_Bad )                         {                             // Store that argument as LANG_PLAYER so we can alter it later                             ArrayPushCell( multi_lingual_indexes_array, argument_index++ );                                                         // Update ML array, so we'll know 1st if ML is used,                             // 2nd how many arguments we have to change                             multi_lingual_constants_number++;                         }                                                 DEBUG_LOGGER( 64, "argument_index (after ArrayPushCell): %d", argument_index )                     }                 }             }                         DEBUG_LOGGER( 64, "(multi_lingual_constants_number: %d", multi_lingual_constants_number )                         for( --players_number; players_number >= 0; players_number-- )             {                 player_id = players_array[ players_number ];                                 if( multi_lingual_constants_number )                 {                     for( argument_index = 0; argument_index < multi_lingual_constants_number; argument_index++ )                     {                         // Set all LANG_PLAYER args to player index ( = player_id )                         // so we can format the text for that specific player                         setarg( ArrayGetCell( multi_lingual_indexes_array, argument_index ), _, player_id );                                                 DEBUG_LOGGER( 64, "(argument_index: %d, player_id: %d, \                                 ArrayGetCell( multi_lingual_indexes_array, argument_index ): %d", \                                 argument_index, player_id, \                                 ArrayGetCell( multi_lingual_indexes_array, argument_index ) )                     }                     vformat( formated_message, charsmax( formated_message ), message, 3 );                 }                                 DEBUG_LOGGER( 64, "( in ) Player_Id: %d, Chat printed: %s", player_id, formated_message )                 PRINT_COLORED_MESSAGE( player_id, formated_message )             }                         ArrayDestroy( multi_lingual_indexes_array );         } #else         vformat( formated_message, charsmax( formated_message ), message, 3 );         DEBUG_LOGGER( 64, "( in ) Player_Id: %d, Chat printed: %s", player_id, formated_message )                 client_print_color( player_id, print_team_default, formated_message ); #endif     }     else     {         vformat( formated_message, charsmax( formated_message ), message, 3 );         DEBUG_LOGGER( 64, "( in ) Player_Id: %d, Chat printed: %s", player_id, formated_message )                 REMOVE_COLOR_TAGS( formated_message )         client_print( player_id, print_chat, formated_message );     }     DEBUG_LOGGER( 64, "( out ) Player_Id: %d, Chat printed: %s", player_id, formated_message ) }
    Here is the output example:
    Code:
    players_number: 1, params_number: 4 argument_index: 2, getarg(argument_index): -1 / Player_Id: 0, formated_message: GAL_CHANGE_TIMEEXPIRED, GetLangTransKey( formated_message ) != TransKey_Bad = 1 (multi_lingual_constants_number: 0, string_index: 23 argument_index: 3 (multi_lingual_constants_number: 1 (argument_index: 0, player_id: 4, ArrayGetCell( multi_lingual_indexes_array, argument_index ): 2 ( in ) Player_Id: 4, Chat printed: O Limite de Tempo expirou. ( out ) Player_Id: 0, Chat printed: O Limite de Tempo expirou.
    At that code you can see the use of the 'DEBUG_LOGGER' directive. Here below are the currently active debug levels at that plugin (here you can see its whole code):
    Code:
    #if IS_DEBUG_ENABLED > 0     #define DEBUG_LOGGER(%1) debugMesssageLogger( %1 ); /**  * ( 0 ) 0 disabled all debug.  * ( 1 ) 1 displays basic debug messages.  * ( 10 ) 2 displays players disconnect, total number, multiple time limits changes and restores.  * ( 100 ) 4 displays maps events, choices, votes, nominations, and the calls to 'map_populateList'.  * ( ... ) 8 displays vote_loadChoices( ) and actions at vote_startDirector.  * ( ... ) 16 displays messages related to RunOff voting.  * ( ... ) 32 displays messages related to the rounds end map voting.  * ( ... ) 64 displays messages related 'client_print_color_internal'.  * ( ... ) 128 execute the test units and print their out put results.  * ( 11111111 ) 255 displays all debug logs levels at server console.  */ new g_debug_level 251 #else     #define DEBUG_LOGGER(%1) #endif

  2. One tip, is that you some times need to print something that is really big and do not fit to any debug levels you already have, or you do not want to create a debug level just for it. So you declare is as debug level 1 to see it, a during the time you need, and when you do not need it anymore, change its level to 0. That level means that it will never be showed at any level, just when you go to it, and change it to an accept level as 1. For exemple, when was needed to know if all my maps was being loaded and how are they being loaded from the map directory, at the moment the nomination menu was showed. Hence, to do that was just created a debug logger with level 1 while it was needed it, and later, it was placed it to the level 0, because there are a lot of maps at the map directory, and as each one of them are being printed in one line, have it enabled at any debug level is annoying. This is the code:
    Code:
    stock nomination_menu( player_id ) {     // assume there'll be more than one match ( because we're lazy ) and starting building the match menu     if( g_nominationMatchesMenu[ player_id ] )     {         menu_destroy( g_nominationMatchesMenu[ player_id ] );     }         g_nominationMatchesMenu[ player_id ] = menu_create( "Nominate Map", "nomination_handleMatchChoice" );         // gather all maps that match the nomination     new mapIdx;         new info[ 1 ];     new choice[ 64 ];     new nominationMap[ 32 ];     new disabledReason[ 16 ];         for( mapIdx = 0; mapIdx < g_nominationMapCnt; mapIdx++ )     {         ArrayGetString( g_nominationMap, mapIdx, nominationMap, charsmax( nominationMap ) );                 info[ 0 ] = mapIdx;                 // in most cases, the map will be available for selection, so assume that's the case here         disabledReason[ 0 ] = '^0';                 if( nomination_getPlayer( mapIdx ) ) // disable if the map has already been nominated         {             formatex( disabledReason, charsmax( disabledReason ), "%L", player_id,                     "GAL_MATCH_NOMINATED" );         }         else if( map_isTooRecent( nominationMap ) ) // disable if the map is too recent         {             formatex( disabledReason, charsmax( disabledReason ), "%L", player_id,                     "GAL_MATCH_TOORECENT" );         }         else if( equal( g_currentMap, nominationMap ) ) // disable if the map is the current map         {             formatex( disabledReason, charsmax( disabledReason ), "%L", player_id,                     "GAL_MATCH_CURRENTMAP" );         }                 formatex( choice, charsmax( choice ), "%s %s", nominationMap, disabledReason );                 menu_additem( g_nominationMatchesMenu[ player_id ], choice, info,                 ( disabledReason[ 0 ] == '^0' ? 0 : ( 1 << 26 ) ) );                 DEBUG_LOGGER( 0, "( nomination_menu ) choice: %s, info[0]: %d", choice, info[ 0 ] )     }         menu_display( player_id, g_nominationMatchesMenu[ player_id ] ); }
    Here is the output example, when the debug level other than 0, i.e., is activated:
    Code:
    ( nomination_menu ) choice: !!!sex_movie , info[0]: 0 ( nomination_menu ) choice: !!water_iceworld , info[0]: 1 ( nomination_menu ) choice: !!_STTZ_!! , info[0]: 2 ( nomination_menu ) choice: !!~sex_movie , info[0]: 3 ( nomination_menu ) choice: !3 , info[0]: 4 ( nomination_menu ) choice: !vip_truck , info[0]: 5 ... ... ... ( nomination_menu ) choice: zm_toxic_house2 , info[0]: 802 ( nomination_menu ) choice: zm_uknow , info[0]: 803 ( nomination_menu ) choice: zm_uknow_last_hope , info[0]: 804 ( nomination_menu ) choice: zm_umbrella_v1 , info[0]: 805 ( nomination_menu ) choice: zm_zombattack , info[0]: 806 ( nomination_menu ) choice: zm_zombies , info[0]: 807 ( cmd_say ) equali( arg1, 'nom', 3 ): 1, equali( arg1[ strlen( arg1 ) - 4 ], 'menu' ): 1 [V]iper*S| Addons zz : nomenu L 01/11/2016 - 16:38:32: "[V]iper*S| Addons zz<1><STEAM_ID_LAN><CT>" say "nomenu"

  3. You can also protect a whole code block, then sometimes just the 'DEBUG_LOGGER' directives are not enough. This is an example using the IS_DEBUG_ENABLED protection:
    Code:
    /**  * Loads the config file "voting_list.ini" and all mods stored there.  */ public load_votingList() {     new currentLine         [ LONG_STRING ];     new currentLine_splited [ SHORT_STRING ];     new mod_name            [ SHORT_STRING ];     new mod_shortName_string[ SHORT_STRING ];     new unusedLast_string   [ SHORT_STRING ];         new votingList_filePointer = fopen( g_votingList_filePath, "rt" );         while( !feof( votingList_filePointer ) )     {         fgets( votingList_filePointer, currentLine, charsmax( currentLine ) );         trim( currentLine );                 // skip commentaries while reading file         if( !currentLine[ 0 ]             || currentLine[ 0 ] == ';'             || ( currentLine[ 0 ] == '/'                  && currentLine[ 1 ] == '/' ) )         {             continue;         }                 if( currentLine[ 0 ] == '[' )         {             g_modCounter++                         // remove line delimiters [ and ]             replace_all( currentLine, charsmax( currentLine ), "[", "" );             replace_all( currentLine, charsmax( currentLine ), "]", "" );                         // broke the current config line, in modname ( mod_name ), modtag ( mod_shortName_string )             strtok( currentLine, mod_name, charsmax( mod_name ), currentLine_splited, charsmax( currentLine_splited ), ':', 0 );             strtok( currentLine_splited, mod_shortName_string, charsmax( mod_shortName_string ), unusedLast_string,                     charsmax( unusedLast_string ), ':', 0 );                         // stores at memory the modname and the modShortName             formatex( g_mod_names[ g_modCounter ], sizeof( g_mod_names[] ) - 1, "%s", mod_name );             formatex( g_mod_shortNames[ g_modCounter ], sizeof( g_mod_shortNames[] ) - 1, "%s", mod_shortName_string );                         DEBUG_LOGGER( 1, "[AMX MOD Loaded] %d - %s",  g_modCounter - 2, g_mod_names[ g_modCounter ] )                 #if IS_DEBUG_ENABLED > 0             if( g_debug_level & 2 )             {                 new mapcycle_filePath       [ SHORT_STRING ];                 new config_filePath         [ SHORT_STRING ];                 new plugin_filePath         [ SHORT_STRING ];                 new message_filePath        [ SHORT_STRING ];                 new messageResource_filePath[ SHORT_STRING ];                 new lateConfig_filePath     [ SHORT_STRING ];                                 mapcycle_pathCoder( mod_shortName_string, mapcycle_filePath, charsmax( mapcycle_filePath ) );                 config_pathCoder( mod_shortName_string, config_filePath, charsmax( config_filePath ) );                 plugin_pathCoder( mod_shortName_string, plugin_filePath, charsmax( plugin_filePath ) );                 message_pathCoder( mod_shortName_string, message_filePath, charsmax( message_filePath ) );                                 messageResource_pathCoder( mod_shortName_string, messageResource_filePath,                         charsmax( messageResource_filePath ) );                                 lateConfig_pathCoder( mod_shortName_string, lateConfig_filePath, charsmax( lateConfig_filePath ) );                                 server_print( "[AMX MOD Loaded] %s", mod_shortName_string );                 server_print( "[AMX MOD Loaded] %s", mapcycle_filePath );                 server_print( "[AMX MOD Loaded] %s", plugin_filePath );                 server_print( "[AMX MOD Loaded] %s", config_filePath );                 server_print( "[AMX MOD Loaded] %s", message_filePath );                 server_print( "[AMX MOD Loaded] %s", lateConfig_filePath );                 server_print( "[AMX MOD Loaded] %s^n", messageResource_filePath );             }         #endif         }     }     fclose( votingList_filePointer ) }
    Here is the output example:
    Code:
    [AMX MOD Loaded] 1 - CS-DM (DeathMatch) [AMX MOD Loaded] csdm [AMX MOD Loaded] mapcycles/csdm.txt [AMX MOD Loaded] addons/amxmodx/configs/multimod/plugins/csdm.ini [AMX MOD Loaded] addons/amxmodx/configs/multimod/cfg/csdm.cfg [AMX MOD Loaded] addons/amxmodx/configs/multimod/msg/csdm.cfg [AMX MOD Loaded] addons/amxmodx/configs/multimod/latecfg/csdm.cfg [AMX MOD Loaded] addons/amxmodx/configs/multimod/csdm.cfg ... ... ... [AMX MOD Loaded] 13 - Zombie Mod with AmmoPacks [AMX MOD Loaded] zp50Ammo [AMX MOD Loaded] mapcycles/zp50Ammo.txt [AMX MOD Loaded] addons/amxmodx/configs/multimod/plugins/zp50Ammo.ini [AMX MOD Loaded] addons/amxmodx/configs/multimod/cfg/zp50Ammo.cfg [AMX MOD Loaded] addons/amxmodx/configs/multimod/msg/zp50Ammo.cfg [AMX MOD Loaded] addons/amxmodx/configs/multimod/latecfg/zp50Ammo.cfg [AMX MOD Loaded] addons/amxmodx/configs/multimod/zp50Ammo.cfg

Final Results | go to top

Now, let show that the 'DEBUG_LOGGER' directives are really working and doing the job to keep the final compiled binary '.amxx' clean of debug statements. Here is a table containing at a relation between the mapchooser Galileo with its 'IS_DEBUG_ENABLED' directive activated, and deactivated.

Compilations data comparisons at the Amx Mod X "1.8.3-dev+4964"
Code:
Plugin name:   IS_DEBUG_ENABLED 0     IS_DEBUG_ENABLED 1       Diferences Header size:           3.532 bytes           3.860 bytes       + 328 bytes Code size:            70.936 bytes          87.104 bytes     +16.168 bytes Data size:            42.680 bytes          67.996 bytes     +25.316 bytes Stack/heap size:      16.384 bytes          16.384 bytes        +0.0 bytes Total requirements:  133.532 bytes         175.344 bytes     +41.812 bytes Compilation Time:       0,47 sec              0,55 sec
Source data
Spoiler

Here you can find plugins filled with this debugging system, for more examples:
  1. https://github.com/addonszz/Galileo/blob/develop/scripting/galileo.sma
  2. https://github.com/addonszz/Multi-Mod_Manager/blob/develop/scripting/multimod_manager.sma

Credits | go to top
  1. Craxor: You can't define a macross with '//'
__________________
Plugin: Sublime Text - ITE , Galileo
Multi-Mod: Manager / Plugin / Server

Support me on Patreon, Ko-fi, Liberapay or Open Collective

Last edited by addons_zz; 05-27-2017 at 10:37. Reason: Added new tutorial
addons_zz is offline
addons_zz
Veteran Member
Join Date: Aug 2015
Location: Dreams, zz
Old 01-13-2016 , 18:41   Re: [TUT] How to use a 100% efficient debug system, which do not overload your final
Reply With Quote #2

Notes | go to top
  1. Every time you break lines on the preprocessor functions 'DEBUG_LOGGER' or any other, you must end its like using a inverted bar '\', without any spaces after it. Example:
    Code:
        DEBUG_LOGGER( 3"^nI am doing debug, the current time is: \             %d:%d:%d^n", \             hour, minute, second )
  2. It is a convention to write the preprocessor function and variables using upper case letter, to differ from the rest of the code, as they are not ordinary code. They are not ordinary code, because they are expanded by the preprocessor, which happens before the compiler, generate your final code. Example:
    Spoiler

  3. If you have enabled the semicolon using '#pragma semicolon 1', you cannot put a semicolon on the preprocessor macro 'DEBUG_LOGGER(...)', because they already got one at its declaration. Example:
    Code:
    #if IS_DEBUG_ENABLED > 0 #define DEBUG_LOGGER(%1) debugMesssageLogger( %1 ); ... some code #pragma semicolon 1 ... some code /*  * Called just after server activation.  */ public plugin_init() {     DEBUG_LOGGER( 1"^n^nI am ENTERING on plugin_init()" )     register_plugin( "Cool Plugin", "1.0", "Addons zz" );     register_concmd( "amx_next", "do_debug" );     do_debug();     DEBUG_LOGGER( 2"I am LEAVING on plugin_init()^n" ) }
  4. When you run the plugin, its executes/run/works/do things, you need to know how it is doings things, what function is called before what, why it is being called, and why it is not being called. To do that, to accomplish that, to put a lot of server_prints, and put more server_prints, until you get to know all information that do you need to understand what the plugin is doing, when you are using it, this is called debug. See [TUT] How to use an efficient debug system, which do not overload your final code, but it use, what I could call 'advanced concepts'. However, on the thread beginning and contents, you can find various others tutorials and links to understand things.

    You cannot forget also to read the Amx Mod X documentation, to get to know what your functions do, and why they do. And the tutorials to know more about how things get done. But if some documentation is missing or you do not understand, search, search, search, search, the forums, until you find some thing as example or a plugin which uses that function where you maybe can know what is does when you see what plugin code running. But if all that isn't enough, open the AMX Mod X Source Code and start learning by its functions implementations what does they do. And even better, contribute to the community, once you know it, to do a pull request to add the missing or bad documentation to the official AMX Mod X Source Code. Moreover, much more better is if you find bugs, fix them instead of just complaining or opening a bug/issue report.

    Also, when you get a plugin and modifies it, do not remove thing on the fly unless you are sure they are useless. And to know if they are useless, to debug the code, to read the Amx Mod X documentation, to read the tutorials and search, search, search, search.

  5. Programming in movies vs real life

    http://imgur.com/Kgz562w


  6. So Adele, what do you think about programmer?



Frequently Asked Questions - FAQ | go to top
  1. Code have no bug man! Successfully compiled.
    • Debug is not just to find bugs, it is also to understand what the plugin is doing, and how it is doing.
    • And there two bug types.

  2. If I want to put this here, is it a problem?
    Code:
    public client_disconnect(id) {     DEBUG_LOGGER( 1"^n^nI am ENTERING on plugin_init()" )         if((id == g_iChallenged) || (id == g_iChallenger))     {         g_bInChallenge = false;         g_bProtect = false;     } }
    Yes, because that function is not plugin_init(), it is: client_disconnect(id). Example:
    Code:
    public client_disconnect(id) {     DEBUG_LOGGER( 1, "^n^nI am ENTERING on client_disconnect(id)" )         if((id == g_iChallenged) || (id == g_iChallenger))     {         g_bInChallenge = false;         g_bProtect = false;     } }
    And actually it is now much more better. You can also show more information. Example:
    Code:
    public client_disconnect(id) {     DEBUG_LOGGER( 1, "^n^nI am ENTERING on client_disconnect(id), the client_id is %d, ^n\             the challenged id is %d, the chalanger id is %d, the variable g_bInChallenge is %d, ^n\             the variable g_bProtect is %d^n", \             id, g_iChallenged, g_iChallenger, g_bInChallenge, g_bProtect )                 if( ( id == g_iChallenged )           || (id == g_iChallenger) )     {         g_bInChallenge = false;         g_bProtect = false;     } }

  3. Only for public?
    Code:
    stock vec_null( Float:vec[3] ) {     DEBUG_LOGGER( 2"I am LEAVING on vec_null()^n" )         if( !vec[0]         && !vec[1]         && !vec[2] )     {         return 1;     }         return 0; }
    This is not good because you are not LEAVING, you are ENTERING, put is at the function end before the return statement. Then it is fine, because you are actually LEAVING. Example:
    Code:
    stock vec_null( Float:vec[3] ) {     DEBUG_LOGGER( 1"I am ENTERING on vec_null()^n" )         if( !vec[0]         && !vec[1]         && !vec[2] )     {         DEBUG_LOGGER( 2"I am LEAVING on vec_null() returning 1^n" )         return 1;     }         DEBUG_LOGGER( 2"I am LEAVING on vec_null() returning 0^n" )     return 0; }

C/C++ Language Implementation | go to top

Introduction links:
  1. Variadic Macros
  2. When to use extern in C++
  3. C Storage Classes
  4. C Programming/Preprocessor
  5. Yale University - C Macros
  6. what is the function of "extern" in C++
  7. Undefining and Redefining Macros
  8. Overloading Macro on Number of Arguments
  9. References & the Copy-Constructor
  10. Macro Align on Linux Kernel

Put this on your header file and define and initialize the variable 'g_debug_level' on somewhere else. Just inicialize the variable 'g_debug_level' at one of them.

The header file 'debug_functions.h':
Code:
#define IS_DEBUG_ENABLED 1 #if IS_DEBUG_ENABLED > 0 /**  * ( 0 ) 0 disabled all debug.  * ( 1 ) 1 displays basic debug messages.  * ( 10 ) 2 displays PAWN specific stuff.  * ( 11 ) 3 displays all debug levels.  */ extern int g_debug_level; /**  * This print to the standard output, the debug information passed accordingly to the global  * constant g_debug_level.  *  * @param mode the debug mode level, see the directive 'g_debug_level' for the levels.  * @param message[] the text formatting rules to display. Example: "Hi today is %s-%s"  * @param any the variable number of formatting parameters. Example: day_name, the_time  */ #define DEBUG_LOGGER(mode,...) if( mode & g_debug_level ) \ { \     fprintf( stderr, __VA_ARGS__ ); \ } #else     #define DEBUG_LOGGER(...) //     #endif
The C/C++ file which initializes 'g_debug_level':
Code:
#include "debug_functions.h" #if IS_DEBUG_ENABLED > 0     int g_debug_level = 15; #endif int main(int argc, char *argv[]) {      DEBUG_LOGGER( 1, "Doing debug" ); }
Any C/C++ file which just use the variable 'g_debug_level', examples:
  • Example 1:
    Code:
    #include "debug_functions.h" int main(int argc, char *argv[]) {      DEBUG_LOGGER( 1, "Doing debug" ); }
  • Example 2:
    Code:
    #include "debug_functions.h" int main(int argc, char *argv[]) { #if IS_DEBUG_ENABLED > 1     if( g_debug_level & 8 )     {         static int counter = 0;         DEBUG_LOGGER( 1, " ( main ) current call count: %d", counter++ );     } #endif }
__________________
Plugin: Sublime Text - ITE , Galileo
Multi-Mod: Manager / Plugin / Server

Support me on Patreon, Ko-fi, Liberapay or Open Collective

Last edited by addons_zz; 05-27-2017 at 10:45. Reason: adjusted the source code formatting
addons_zz is offline
GuskiS
Veteran Member
Join Date: Aug 2007
Location: Latvia
Old 01-16-2016 , 11:28   Re: [TUT] How to use a 100% efficient debug system, which do not overload your final
Reply With Quote #3

You can nest that stock function under #if IS_DEBUG_ENABLED > 0 itself.
Also, it would be better to use letters for printing options because then you can combine multiple loggers at the same time, but not all of them if needed.
__________________
Finished mods:
Trouble in Terrorist Town
MurderMod
The Hidden
Cowboys vs Indians
JailBreak Supreme
Survival Madness

Last edited by GuskiS; 01-16-2016 at 11:28.
GuskiS is offline
addons_zz
Veteran Member
Join Date: Aug 2015
Location: Dreams, zz
Old 01-16-2016 , 13:42   Re: [TUT] How to use a 100% efficient debug system, which do not overload your final
Reply With Quote #4

Yes, but as long as the debug is disabled and you do not use it anywhere it is not compiled. However, putting it that, can save someone mistake to use it directly without the DEBUG_LOGGER directive and help to keep the code clean grouping everything related together. Thanks, updated.

Quote:
Originally Posted by GuskiS View Post
to use letters for printing options
Now, I do not understand about using letters, as you said. Can you give an example about using letters?
The advantage of using letters I see, would be name the levels. Example:
Code:
#if IS_DEBUG_ENABLED > 0     #define DISABLED_LEVEL 0     #define BASIC_LEVEL 1     #define COLOR_CHAT_LEVEL 2     #define ROUND_START_LEVEL 4     #define DEBUG_LOGGER(%1) debugMesssageLogger( %1 ) /**  * DISABLED_LEVEL - disabled all debug.  * BASIC_LEVEL - displays basic debug messages.  * COLOR_CHAT_LEVEL - displays players disconnect, total number, multiple time limits changes and restores.  * ROUND_START_LEVEL - displays maps events, choices, votes, nominations, and the calls to 'map_populateList'.  */ new g_debug_level ROUND_START_LEVEL + BASIC_LEVEL #else     #define DEBUG_LOGGER(%1) // #endif ... public some_function() {     ...     DEBUG_LOGGER( COLOR_CHAT_LEVEL, ... )     ...     DEBUG_LOGGER( ROUND_START_LEVEL, ... ) }
And which as I actually see is very good for code reading clearness. But I will let the original post as numbers and let here as instruction to use letters for code clearness. I let it here to let people choose which one they prefer most.
I particularly prefer using numbers, as easily locate myself with the levels, and I like to let more space for the message when I register the debug level. Example:
Code:
    DEBUG_LOGGER( 1, ... ) // let much more space for the message then     ...     DEBUG_LOGGER( ROUND_START_LEVEL, ... ) //this
But thinking better, I can use a solution based on 'chars' to solve that size problem. Example:
Code:
#if IS_DEBUG_ENABLED > 0     #define DEBUG_LOGGER(%1) debugMesssageLogger( %1 ) /**  * "" - disabled all debug.  * "b" - displays basic debug messages.  * "c" - displays players disconnect, total number, multiple time limits changes and restores.  * "d" - displays maps events, choices, votes, nominations, and the calls to 'map_populateList'.  * "bcd" - displays all debug levels on server console.  */ new g_debug_level[] = "bcd" stock debugMesssageLogger( mode[], message[], any: ... ) {     for( new i = 0; i < sizeof(g_debug_level); i++ )     {         for( new j = 0; j < strlen(mode); j++ )         {             if( g_debug_level[i] == mode[j] )             {                 static formated_message[ LONG_STRING ]                                 vformat( formated_message, charsmax( formated_message ), message, 3 )                                 server_print( "%s", formated_message         )                 client_print(       0, print_console,             "%s", formated_message )             }         }     } } #else     #define DEBUG_LOGGER(%1) // #endif ... public some_function() {     ...     DEBUG_LOGGER( "a", ... )     ...     DEBUG_LOGGER( "bd", ... ) }
Well now, we have three options to choose, so anyone can suilt yourself.

Quote:
Originally Posted by GuskiS View Post
you can combine multiple loggers at the same time, but not all of them if needed.
You already can print any, how much levels you want to using numbers. Example:

If you register DEBUG_LOGGER( 8, ...), and set 'g_debug_level = 7', it will only show levels 1, 2, and 4, but not 8.
If you register DEBUG_LOGGER( 2, ...), and set 'g_debug_level = 5', it will only show levels 1, and 4, but not 2.

But if you register DEBUG_LOGGER( 5, ...), you are registering it at levels 1 and 4 at the same time, then if you set 'g_debug_level = 6', it will only show levels 2, and 4, then DEBUG_LOGGER( 5, ...) will be showed, because we are using level 4, and it is registered at that level 1 and 4 using 5.

Learn more about these levels system reading:
  1. Bitwise operation
  2. Logical conjunction
  3. Bitwise operations in C
  4. Bitwise Operators in C
__________________
Plugin: Sublime Text - ITE , Galileo
Multi-Mod: Manager / Plugin / Server

Support me on Patreon, Ko-fi, Liberapay or Open Collective

Last edited by addons_zz; 01-16-2016 at 16:27. Reason: spelling fix
addons_zz is offline
GuskiS
Veteran Member
Join Date: Aug 2007
Location: Latvia
Old 01-17-2016 , 05:34   Re: [TUT] How to use a 100% efficient debug system, which do not overload your final
Reply With Quote #5

What I meant was to use chars = letters ^__^
But if you use chars you need to set those letters only on g_debug_level variable. On DEBUG_LOGGER you still can assign it with numbers or better case - with defined preprocessor variable, like you showed in replied example. But I see a problem there, if IS_DEBUG_ENABLED is not enabled, compiler won't compile code because DISABLED_LEVEL and others are not defined. What I suggest is, create an include and put everything in it, leaving those DISABLED_LEVEL outside that if case.
__________________
Finished mods:
Trouble in Terrorist Town
MurderMod
The Hidden
Cowboys vs Indians
JailBreak Supreme
Survival Madness
GuskiS is offline
addons_zz
Veteran Member
Join Date: Aug 2015
Location: Dreams, zz
Old 01-17-2016 , 10:46   Re: [TUT] How to use a 100% efficient debug system, which do not overload your final
Reply With Quote #6

Quote:
Originally Posted by GuskiS View Post
But I see a problem there, if IS_DEBUG_ENABLED is not enabled, compiler won't compile code because DISABLED_LEVEL and others are not defined.
If I understand you correctly, it will compile because the use of 'DISABLED_LEVEL' and others are at/used at the directive 'DEBUG_LOGGER( DISABLED_LEVEL + ... others, "Debug Message" )', at all that will be replaced by '//', if debug 'IS_DEBUG_ENABLED' is disabled. Example: Using 'IS_DEBUG_ENABLED' as 0, this:
Code:
#define LONG_STRING 256 #define IS_DEBUG_ENABLED 0 #if IS_DEBUG_ENABLED > 0     #define DISABLED_LEVEL 0     #define BASIC_LEVEL 1     #define COLOR_CHAT_LEVEL 2     #define ROUND_START_LEVEL 4     #define DEBUG_LOGGER(%1) debugMesssageLogger( %1 ) new g_debug_level = 7 #else     #define DEBUG_LOGGER(%1) // #endif public plugin_init() {     DEBUG_LOGGER( BASIC_LEVEL,  "^n^nI am ENTERING on plugin_init()" )     register_plugin( "Cool Plugin", "1.0", "Addons zz" )     register_concmd( "amx_next", "do_debug" )     do_debug()     DEBUG_LOGGER( COLOR_CHAT_LEVEL,  "I am LEAVING on plugin_init()^n" ) }
Became this:
Code:
#define LONG_STRING 256 #define IS_DEBUG_ENABLED 0 #define DEBUG_LOGGER(%1) // public plugin_init() {     DEBUG_LOGGER( ROUND_START_LEVEL,  "^n^nI am ENTERING on plugin_init()" )     register_plugin( "Cool Plugin", "1.0", "Addons zz" )     register_concmd( "amx_next", "do_debug" )     do_debug()     DEBUG_LOGGER( DISABLED_LEVEL,  "I am LEAVING on plugin_init()^n" ) }
And later this, which is compiled by the Amx Mod X compiler:
Code:
public plugin_init() {     //     register_plugin( "Cool Plugin", "1.0", "Addons zz" )     register_concmd( "amx_next", "do_debug" )     do_debug()     // }
__________________
Plugin: Sublime Text - ITE , Galileo
Multi-Mod: Manager / Plugin / Server

Support me on Patreon, Ko-fi, Liberapay or Open Collective

Last edited by addons_zz; 01-17-2016 at 10:53. Reason: spelling fix
addons_zz is offline
GuskiS
Veteran Member
Join Date: Aug 2007
Location: Latvia
Old 01-17-2016 , 14:45   Re: [TUT] How to use a 100% efficient debug system, which do not overload your final
Reply With Quote #7

Compile this:
PHP Code:
#include <amxmodx>

// #define ROUND_START_LEVEL 5

public plugin_init()
{
  
register_plugin("Test plugin""1.0""GuskiS");
  
log_amx("some variable %d"ROUND_START_LEVEL);

__________________
Finished mods:
Trouble in Terrorist Town
MurderMod
The Hidden
Cowboys vs Indians
JailBreak Supreme
Survival Madness
GuskiS is offline
addons_zz
Veteran Member
Join Date: Aug 2015
Location: Dreams, zz
Old 01-17-2016 , 15:15   Re: [TUT] How to use a 100% efficient debug system, which do not overload your final
Reply With Quote #8

Quote:
Originally Posted by GuskiS View Post
Compile this:
PHP Code:
#include <amxmodx>

// #define ROUND_START_LEVEL 5

public plugin_init()
{
  
register_plugin("Test plugin""1.0""GuskiS");
  
log_amx("some variable %d"ROUND_START_LEVEL);

I was just telling you that exactly this NEVER happens here. Because you only use 'ROUND_START_LEVEL' inside 'DEBUG_LOGGER(...)', which forbidden your example to happens.

So, I am not telling to use 'ROUND_START_LEVEL' anywhere, if it is not at/but only inside 'DEBUG_LOGGER(...)', because both 'ROUND_START_LEVEL' and 'DEBUG_LOGGER' are protected by 'IS_DEBUG_ENABLED'.

But if you want to use 'ROUND_START_LEVEL' elsewhere than 'DEBUG_LOGGER(...)', you must do as you said or your example comes true.

Update:
I attached a compiling example. There, the 'IS_DEBUG_ENABLED' is set to 0 and 'ROUND_START_LEVEL' is defined within 'IS_DEBUG_ENABLED > 0' and used inside the 'DEBUG_LOGGER(...)' directives.
Spoiler
Attached Files
File Type: sma Get Plugin or Get Source (preprocessor_constants.sma - 645 views - 2.0 KB)
__________________
Plugin: Sublime Text - ITE , Galileo
Multi-Mod: Manager / Plugin / Server

Support me on Patreon, Ko-fi, Liberapay or Open Collective

Last edited by addons_zz; 01-18-2016 at 20:51. Reason: spelling fixes
addons_zz is offline
GuskiS
Veteran Member
Join Date: Aug 2007
Location: Latvia
Old 01-19-2016 , 05:37   Re: [TUT] How to use a 100% efficient debug system, which do not overload your final
Reply With Quote #9

Yes. Sorry, I though that compiler works other way - from inside the () and only then evaluate outside.
__________________
Finished mods:
Trouble in Terrorist Town
MurderMod
The Hidden
Cowboys vs Indians
JailBreak Supreme
Survival Madness
GuskiS is offline
addons_zz
Veteran Member
Join Date: Aug 2015
Location: Dreams, zz
Old 01-19-2016 , 08:00   Re: [TUT] How to use a 100% efficient debug system, which do not overload your final
Reply With Quote #10

Quote:
Originally Posted by GuskiS View Post
Yes. Sorry, I though that compiler works other way - from inside the () and only then evaluate outside.
No problems, but its not by inside, it is by order. Observe, when it is not 'IS_DEBUG_ENABLED > 0', we define 'DEBUG_LOGGER(...)' as //, then on the next step, it will not found the 'ROUND_START_LEVEL' and others because they were vanished.
Code:
... #else     #define DEBUG_LOGGER(%1) // #endif ... later public plugin_init() {     //     register_plugin( "Cool Plugin", "1.0", "Addons zz" )     register_concmd( "amx_next", "do_debug" )     do_debug()     // }
__________________
Plugin: Sublime Text - ITE , Galileo
Multi-Mod: Manager / Plugin / Server

Support me on Patreon, Ko-fi, Liberapay or Open Collective

Last edited by addons_zz; 01-19-2016 at 08:07. Reason: spelling fix
addons_zz is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 08:52.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode