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