Showing posts with label debug. Show all posts
Showing posts with label debug. Show all posts

Thursday, May 22, 2014

ease debugging: name your threads

hi there,

Whenever I'm debugging multithreaded Ax batch processes via CIL in Visual Studio, I tend to lose the overview of which thread I'm in and what data it actually is processing.

A tip that might help you out is the threads window. You can pull this up in Visual Studio via the the menu: Debug > Windows > Threads. Apparently this option is only available (and visible) while you're actually debugging, so make sure you have an ax32serv.exe process attached to Visual Studio.
That would give you something like this:


Now at least you have an indication (the yellow arrow) which thread you're currently in.

From this point on you get a few options that might come in handy some day:
- Instead of having all threads running, you can focus on one specific thread and freeze the other threads. Just select all the thread you'd like to freeze, and pick 'freeze' from the context menu:

- Once you know which data a specific thread is handling, you may want to give your thread a meaningful name that makes it easier for you to continue debugging: just right-click the thread you want to name and pick the 'rename' option.
The result could then be:
You could take it a step further (or back if you wish) and name your thread at runtime from in X++. If you'd add code like in the example below, you'd see the named thread when debugging CIL in Visual Studio.
The drawback (I found pretty quick) is that at thread can be named only once at runtime. Once a thread is named, you cannot call myThread.set_Name() again (you'd run into an exception). And if you know thread are recycled, the runtime naming of a thread loses most of it's added value in my opinion. The workaround is to rename it during debugging, which then again is possible.

There is probably lots and lots more to say about debugging threaded processes in Visual Studio, the above are the tips & tricks I found the most useful.

Enjoy!


Wednesday, May 14, 2014

debug creation, modification, deletion with doInsert, doUpdate, doDelete

hi all,

Sometimes you just want to find out when a record is created, modified or deleted. The .insert, .update and .delete methods are the place to be, we all figured that out. But it happens some sneaky code uses the .doInsert, doUpdate or .doDelete methods. Your breakpoints in .insert, .update or .delete are silently ignored (as expected), there is no way to xRef on doInsert/doUpdate/doDelete ....  and you're still eager to catch when those insert/update/delete's are happening.
Well there seems an easy trick to cach even those: use the .aosValidateInsert, .aosValidateUpdate and .aosValidateDelete methods!

Enjoy!

Wednesday, September 1, 2010

debugging tip

hi,

With some errors in Ax, I somethimes just don't know where to start debugging.
In those situations I use the call stack as a starting point to give me an idea on where to place breakpoints.

Here's what I do:


1 - create a new static method LAXGetCallStack in the Global class:
// this method returns the X++ call stack, the _offset is to skip entries at the top of the stack


client server static str LAXGetCallStack(int _offset = 0)
{
    #xppTexts
    container   callStack =    xsession::xppCallStack();
    int         k;
    int         lineNumber;
    str         entry;
    str         path;
    ;

    for (k = 1 + _offset ; k <= conlen(callSTack) ; k = k + 2)
    {
        path = conpeek(callStack,k);
        lineNumber = conpeek(callStack,k+1);
        entry += (entry ? #newline :'') + strfmt('-> %1 Line - %2', path, lineNumber);
    }
    return entry;
}


2 - modify the infolog.add method 
a - add this to the declaration section
    boolean     stackTraceActive;
    str         strInfoTxtToDebug;
    #xppTexts


b - add this at the beginning of the method (before switch(loglevel))
    stackTraceActive = true;
    strInfoTxtToDebug = 'debug';
    strInfoTxtToDebug = '*' + strInfoTxtToDebug + '*';

    if (stackTraceActive)
    {
        if (_txt like strInfoTxtToDebug)
        {
            _txt += #newLine + 'STACKTRACE for debugging:';
            _txt += #newLine + LAXGetCallStack(4);
        }
    }

If I get to investigate situations where the only clue is some strange info/warning/error, I use (a specific part of) the text of the infolog as a condition to in the infolog.add method to get the stacktrace an thus the calling objects.

This can be improved by making the stackTraceActive variable a user settable parameter. 
The same goes for the strInfoTxtToDebug variable.