MATLAB: Debug code invoked by timer

debugdiscussionerrortimertimercbtools

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 <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.)

Best 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.