Discover MakerZone

MATLAB and Simulink resources for Arduino, LEGO, and Raspberry Pi

Learn more

Discover what MATLAB® can do for your career.

Opportunities for recent engineering grads.

Apply Today

Debug code invoked by timer

Asked by per isakson on 3 Mar 2013

Debugging code, which is invoked by a timer, is more of a challenge than it should be. The other day I expressed my frustration in What frustrates you about MATLAB?. Now, I'll try to be more constructive.

Wanted: Tips, tricks and tools to help me debug more efficiently. (Below, I present some of mine.)

My code: I have a code that is invoked by a timer on a regular basis. The code consist of a dozen classes and a handful of functions. Both objects and some functions have state. Below, I have included some small functions to illustrate my problems.

My development environment: R2012a (64bit) on Windows 7. I use a unit testing framework with a graphical interface and write some code in debug mode. The framework is based on MUNIT, by Brad Phelan.

Some problems:

  • Matlab's error messages are very generic and include neither function name nor line number
  • "Cannot clear classes". The old state must be cleared before each test. I often restart the unit testing tool to be able to clear old state.
  • Break-points are cleared by clear all and I often forget to reset them before executing the code.

Some examples:

The function, invoked_by_timer, as value of TimerFcn illustrates the error message of Matlab.

    >> debug_invoked_by_timer()
    Error while evaluating TimerFcn for timer 'my_timer' 
    Index exceeds matrix dimensions.

The function, invoked_by_timer_catch, as value of TimerFcn illustrates that it is possible to improve the error message. Daniels comment to my frustration triggered me to do this experiment.

    >> debug_invoked_by_timer()
    Error while evaluating TimerFcn for timer 'my_timer' 
    Error: Index exceeds matrix dimensions.
    In invoked_by_timer.index_out_of_bounds (line: 7)
    In invoked_by_timer.invoked_by_timer (line: 2)
    In invoked_by_timer_catch.invoked_by_timer_catch (line: 3)
    In timercb.timercb (line: 31)
    In timercb.timercb (line: 14)
    In wait.wait (line: 51)
    In debug_invoked_by_timer.debug_invoked_by_timer (line: 17)

TraceHistory is a singleton, which keeps its state after debug_invoked_by_timer has finished. It displays which functions have been called.

    >> log = TraceHistory.Instance;
    >> disp(log)
    --- tracer4m ---
    invoked_by_timer_catch
        invoked_by_timer
            index_out_of_bounds

( log.clearHistory and log.setup can be done outside the function, debug_invoked_by_timer).

where

    function    debug_invoked_by_timer()
        log = TraceHistory.Instance;
        log.clearHistory            
        log.setup( {'h:\m\PiaX\Experiments\debug\timer\invoked_by_timer.m'} )  
        tmr = timer('Name'      , 'my_timer'        ...
            ,   'TimerFcn'      , @invoked_by_timer_catch ...
    ...     ,   'TimerFcn'      , @invoked_by_timer ...
            ,   'BusyMode'      , 'drop'            ...
            ,   'ExecutionMode' , 'fixedRate'       ...
            ,   'Period'        ,  1                ...
            ,   'StartDelay'    ,  1                ...     
            ,   'TasksToExecute',  1           ...
            ); 
        start( tmr )
        wait ( tmr )
    end

and

    function    invoked_by_timer_catch( tmr, evnt )
        try
            invoked_by_timer( tmr, evnt ) 
        catch me
            str = exception2str( me );
            error( str )
        end
    end

and

    function    invoked_by_timer( tmr, evnt )
        index_out_of_bounds()
        variable_not_used( tmr, evnt ) 
    end
    function    index_out_of_bounds()
        a = 17;    
        b = a(2);
        variable_not_used( a, b ) 
    end

and

    function    str = exception2str( me )       
        str = sprintf( 'Error: %s\n', me.message );  
        for s = transpose( me.stack )
           str = [ str, hyperlink_row_( s ) ];   %#ok<AGROW>
        end
    end
    function    str = hyperlink_row_( s )
        [ ~, filename ] = fileparts( s.file );
         str = sprintf                                                      ...
            (   [ 'In <a href="matlab:matlab.desktop.editor.openAndGoToLine'...
                , '(''%s'',%i);">%s.%s (line: %i)</a>\n' ]                  ...
            ,   s.file, s.line, filename, s.name, s.line                    );
    end

and

    function    variable_not_used( varargin )       
    %variable_not_used - helps keep the code analyzer box green  
    end

Persistent break-point: The function, dbs, sets a break-point on the following line. I found it useful.

    function dbs
    % dbs provides a persistent break-point; debug, dbstop
    %   Based on the FEX contribution kdb 
    %   By Romesh Abeysuriya 15-11-12
        stk = dbstack('-completenames');
        dbstop( 'in', stk(2).file, 'at', num2str( stk(2).line+1 ) )
    end

.

(Why do I use the timer in the first place? A while-loop with a computed pause would most likely have been good enough and easier to work with.)

See also: Can't find where the warning msg is from

2 Comments

Daniel on 10 Mar 2013

Is there an advantage of exception2str over getReport?

per isakson on 10 Mar 2013

No. I was too quick making exception2str. However, now I think my output is easier to read ;). I failed to enter ">" or "/" between the file and the function name.

per isakson

Products

No products are associated with this question.

1 Answer

Answer by Daniel on 6 Mar 2013
Accepted answer

My solution, that I alluded to in the original thread, looks almost identical to your invoke_by_timer_cacth, except I use the getReport method of the MException class instead. I also have defined a generic callback wrapper that I use for all callbacks that includes the try-catch.

The clear classes and clear all issues are separate and I have no insights.

0 Comments

Daniel

Contact us