Home AMX User Forum NetLinx Studio

Debugging

A question came up about debugging in a different thread and I feel that it needs it's own thread. The question was one about the effectiveness of the debugger and how it should be used. I am not sure about the Programmer I class, but debugging was only briefly touched in Programming II. Here is how I use some of the debugging tools and I look forward to other thoughts or suggestions on debugging.

First I use breakpoints to see how the program flows in certain areas. If I have a new function or call that doesn't seem to be doing what I want, I'll put a break at the top of the function and wait to see if the function even gets called. One thing that I have found is the netlinx program is sort of picky about where a break is placed. It doesn't break if the break is placed on a blank line above the code that executes (which makes sense). Next, I'll add the affected variables to the watch list so that as I step through, I can see what is going on. Sometimes I have to add some temporary variables just to make it easier to do this.
I then push a button (or whatever is needed) to trigger the code block to execute. If I don't see a break happen, I figure out what I screwed up in the logic that calls the function. If a break occurs where I want it, I will use the Single Step option to step through the code in question and then try to find out where the program and my thought process differ. If I get to the end of the block of code and feel I need another step through it, I hit the RUN button... sometimes I have to hit the run button a few times to get the program going again for some reason but it eventually gets running again. Then I just repeat the process until I figure out what I did wrong.

The other debug tool I use more and more is the device notification feature. This diagnostic tool let's you see what is being sent/received by all devices. This has helped me tremendously in figuring out what is happening with a few RS232 devices. It also helps me catch those times when I forget to do and ITOA() in a string that is being sent.

Lastly, I occasionally use the emulate a device to send strings to the processor to test out those odd bits of code that are there for either error handling or rarely used events. Such as to report a hardware failure or maybe a lamp burned out or ... just to test out the code without having to constantly break $500 projector bulbs ;)

I am sure there are debugging features that I haven't even discovered yet so I am eagerly awaiting any other advice some of the more experienced programmers can offer.

Jeff

Comments

  • Good thread here as I find debugging in NetLinx some what tedious. One trick I have learned and adopted as practice is that it is a lot easier to debug the flow of a program by using lots of SEND_STRING 0 cmds. Basically this just sends a message back to the master which it then prints out in it's messages. For instance

    SWITCH(X)
    {
    CASE 1:
    {
    //BLAH BLAH CODE CODE
    SEND_STRING 0, "'CASE 1 TRIPPED ON SWITCH(X)'"
    }
    CASE 2:
    {
    //BLAH BLAH CODE CODE
    SEND_STRING 0 ,"'CASE 2 TRIPPED ON SWITCH(X)'"
    }
    DEFAULT:
    {
    SEND_STRING 0 ,"'SWITCH(X) DEFAULTED X=',ITOA(X)"
    }
    }

    This always help me get little bugs taken care of. That way you can know what the case is tripped, and if it isn't what the vaule of x was when it defaulted.

    Note: to see these messages either enable NetLinx Diagnostics or telnet into the master and type MSG ON.

    Have fun!
  • To extend that SEND_STRING 0,"'blah,blah,blah'" thought. If you evaluate a "debug" conditional variable you have the ability to easily enable and disable the debug information by changing the value of the variable in the watch variable window. You could extend it even farther via system discriminating debugging.

    DEFINE_VARAIBLE
    INTEGER nAudioDebug = 1
    INTEGER nVIdeoDebug = 1

    PUSH[someDevice,someAudioFunctionButton]
    {
    PUSH:
    {
    IF(nAudioDebug) SEND_STRING 0,'blah,blah,blah'
    }
    }

    PUSH[someDevice,someVideoFunctionButton]
    {
    PUSH:
    {
    IF(nVideoDebug) SEND_STRING 0,'blah,blah,blah'
    }
    }
  • jeffacojeffaco Posts: 121
    I've done this exact thing with my SYSLOG module (freely available for use on SourceForge).

    Syslog can do any of the following:

    1. Log console messages conditionally (you can change how things are logged via the debugger) via SEND_STRING 0,

    2. Log syslog messages (to a syslog server) conditionally. Syslog servers are installed on all UNIX systems, and decent freebie syslog servers are available for Windows (kiwi for one),

    3. Make use of a "Flight Recorder". If your program crashes the NetLinx master (yeah, I've been there), then SEND_STRING 0 won't work anymore because those messages aren't sent instantly, and the NetLinx master may crash before you get those.

    Messages logged can be rated "Emergency", "Alert Immediate", "Critical", "Warning", "Notice", "Info", and "Debug". You can tell syslog, on the fly, to log debug messages and higher, or just warnings and higher, etc. Messages generally have a "component" associated with it, and each component has separate settings for how it logs to syslog.

    Here's a link if anyone's interested:

    http://cvs.sourceforge.net/viewcvs.py/netlinx-modules/NetLinx-Modules/SyslogMod/

    All my modules use it. If you don't want it, just pass 0:0:0 on to the module for the syslog port, and it's disabled. So the "cost" is one parameter to the module (if you don't want it), but a tremendously valuable tool if you do want it.
  • I typically do use Irvine_Kyle method using Jeff's SYSLOG module and using #define for conditional execution of debug messages instead of variables as icraigie suggests.

    //#define MODULE_DEBUG_L1
    #define MODULE_DEBUG_L2

    PUSH[someDevice,someAudioFunctionButton]
    {
    PUSH:
    {
    #if_defined MODULE_DEBUG_L1
    Log('blah,blah,blah');
    #end_if
    }
    }

    where Log is a function that calls SYSLOG. The advantage of syslog is that you can have the log on another machine, with a trace... The disadvantage is that during the NetLinx boot(DEFINE_START), you're piling tons of messages and that can crash the master...

    The advantage of variables is that you can change them dynamically, but you bear the cost of evaluation. #define forces you to recompile for a change, but then for the useful life of the program (normally much longer than its "being-debugged" life) you do not have the performance hit.

    With all that, the debugger is very rarely used...

    Fred
  • DHawthorneDHawthorne Posts: 4,584
    Funny, I have never used breakpoints, and only rarely the system notifictions. If I have a castastrophic failure, I'll comment out the section I suspect is causing trouble and gradually include more code until I isolate the problem.

    But the primary tools I use are watch variables and SEND_STRING 0. I put a conditional in, as icraigie suggests, so the notifications aren't continually clogging the message stream. Between those two tools I can nearly always find any issues that creep up. Debug mode on an Axcent3 was kind of flaky, and tended to slow the entire system to a crawl, but in NetLinx it's much more robust. With it up I can watch key values and in conjunction with the SEND_STRING 0 messages telling me where in code I am, and what event have just occured, I can then see what key values are. i might add, I tend to use a lot of tracking variables to determine what state components of the system are in, and debug mode is most useful for making sure those are updating correctly. Variables that change frequently and quickly you pretty much have to output on a SEND_STRING or you will miss crucial changes.
  • One word of caution using the variable watcher. It can cause problems if you are looking at large device or struct arrays. A programmer from AMX told us that the variable watcher uses large chunks of memory and that with computers with less memory you can start seeing really really quirky stuff. I've witnessed this myself for instance watching a dev arrays.
  • DHawthorneDHawthorne Posts: 4,584
    Originally posted by Irvine_Kyle
    One word of caution using the variable watcher. It can cause problems if you are looking at large device or struct arrays. A programmer from AMX told us that the variable watcher uses large chunks of memory and that with computers with less memory you can start seeing really really quirky stuff. I've witnessed this myself for instance watching a dev arrays.
    Yes, I've learned to be careful with it. Some things can't be watched as well (Stack vars for example, if defined within a block, or parameters of a module as referenced by the module itself - you have to use the variable passed to it insstead).
Sign In or Register to comment.