Loren on the Art of MATLAB

Turn ideas into MATLAB

Stop If 6

Posted by Loren Shure,

Today's guest post comes from Sean de Wolski, one of my fellow Application Engineers. You might recognize him from MATLAB answers and the pick of the week blog!

Have you ever wanted MATLAB to pause in debug mode when a certain condition is met? Perhaps when a specific problem-file out of a directory is read or on an iteration of a for-loop that yields an unexpected result? Perhaps that for-loop has to iterate a few dozen or hundred times before the problem iteration occurs and you don't want to walk through step by step in debug mode?

In this post, we're going to survey a few different ways to handle this.

Contents

Keyboard

I was talking with a MATLAB user recently about the merits of the keyboard function of which I'm not a big fan. Keyboard pulls you into debug mode as soon as it's encountered. They claimed to like it for difficult debugging when certain criteria are met.

Here's a simple example scenario. CM is the cameraman image:

imshow(CM)

This is a simple algorithm to show how many pixels are brighter than each pixel value in the uint8 range of [0 255].

xg = zeros(1, 256);
for ii = 0:255
    xg(ii+1) = sum(CM(:)>ii);
end

plot(0:255, xg)
ylabel('Pixels Greater Than X')
axis tight

Somewhere around iteration 125 appears to be giving me problems; I'd expect the curve to be positive through to 255.

I want to see what's happening on iteration 125 and the subsequent ones. One way would be to put a break point on the calculation line, hit run, and then hit the continue button 125 times hoping I don't get in a rhythm and accidentally skip it. This has some obvious shortcomings in that it doesn't scale and is time consuming.

The approach with keyboard would look like this:

xg = zeros(1, 256);
for ii = 0:255
    if ii == 125
        keyboard
    end
end

Now on the 125th iteration, we enter the if-statement and can step in debug mode into the problem area. This is pretty straight-forward and is a safe, decent solution. However, it requires changing the code in order to debug it and will require changing it again once we've fixed the bug.

DBSTOP ...

What if we could just stop if? Well you can! dbstop, the function underlying breakpoints, supports a wide range of conditional breakpoints.

These breakpoints can be set from the breakpoints menu on the editor tab or with dbstop directly. Here's the same code encapsulated in a function called badForLoop.

function badForLoop(CM)
xg = zeros(1, 256);
for ii = 0:255
    xg(ii+1) = sum(CM(:)>ii); 
end

plot(0:255, xg)
ylabel('Pixels Greater Than X')
axis tight
end

To set it from the breakpoints menu, select "Set Condition" while the cursor is on the line you'd like to stop on.

Then enter your condition; any valid MATLAB code will work here.

To set the same breakpoint programmatically:

dbstop in badForLoop at 4 if (ii==125)
badForLoop(CM);

Testing and Clean Up

If you think you have a fix, you can then disable the breakpoint without clearing it by either clicking on the yellow breakpoint itself (an X will appear over it indicating it's disabled). If the problem is resolved, clicking the breakpoint again will clear it or you can reenable it by right clicking. This can be done from the menu as well and clearing can be done with dbclear.

The analogous workflow with keyboard would be to highlight the selection and comment it out to "disable" and then delete to clear.

The fix for this toy problem was that the image was stored as an int8 with a range [-128 127] rather than a uint8 with range [0 255].

Bonus

Additionally, dbstop has some predefined conditions to stop on including on errors, warnings, or if nans or infs are encountered. If you haven't discovered dbstop if error yet, I strongly suggest giving it a try. When an error occurs, it stops and you can view the state of the world as it was when the error occurred.

Comments

Do you have any debugging war stories where one of these tricks could've helped? What about other uses for the keyboard command? Let us know here.


Get the MATLAB code

Published with MATLAB® R2017a

Note

Comments are closed.

6 CommentsOldest to Newest

Dan K replied on : 1 of 6

Hi Loren,
In the past it has seemed to me that using a conditional dbstop can exact an extreme amount of overhead relative to the unaltered code. (I’ve not found this using the dbstop if error), but when setting a custom condition on a loop that runs many, many cycles can slow down the execution to the point where it wasn’t feasible to use it. Is there some interference between dbstop conditions and the JIT compiler, perhaps? In those cases, I’ve found that the code runs much, much faster by using an approach that looks like your keyboard approach. Am I missing something?

Here’s an example:

N = 10e5;
testCond = 0.99*N;
tic
for i = 1:N
% Just create something to do so it doesn't optimize away
j = i+1;
if j==(testCond-1)
break
end
end
toc

tic
for i = 1:N
% Just create something to do so it doesn't optimize away
j = i+1;
if j==testCond
keyboard;
end
if j==(testCond-1)
break
end
end
toc

I put a conditional breakpoint on the line that says: j = i+1; in the first case, testing for j==testCond.
The break statements are so that I can time it without actually tripping the debug condition.
The times for the two cases were:
Elapsed time is 34.506428 seconds.
Elapsed time is 0.004920 seconds.

Is there an approach to using dbstop that doesn’t pay this penalty?

Thanks,
Dan

Dan K replied on : 2 of 6

Slight addition to that earlier comment. I pay an even worse penalty if the breakpoint is disabled, but not if it is deleted.
Enabled:
Elapsed time is 34.412000 seconds.
Elapsed time is 0.004994 seconds.

Disabled:
Elapsed time is 51.132263 seconds.
Elapsed time is 0.004941 seconds.

Deleted:
Elapsed time is 0.004422 seconds.
Elapsed time is 0.004628 seconds.

BTW: This is all using R2017a.

Dan

Eric replied on : 3 of 6

I’ll admit to using the keyboard command. Sometimes it’s useful to define a boolean DEBUG variable to perform several analyses in a piece of code that are only needed for debugging purposes. For instance, one may want to perform intermediate analyses and generate plots to help understand how to tune an algorithm. In that case I’ll wrap the debug code in “if DEBUG” statements and include a “keyboard” call at the end of the last one.

Yair Altman replied on : 4 of 6

I often write Matlab program that include multiple branches that I wish to debug in run-time. I am often frustrated by several limitations of the dbstop mechanism:

I can manually place the breakpoints in the relevant code locations but I need to do it again and again for each Matlab session because breakpoints (like bookmarks) are not persisted across Matlab sessions. Since code-folding is persisted I see no reason why breakpoints/bookmarks should not be. In fact, I think that persisting breakpoints (and to a lesser extent bookmarks) would be more useful than persisted code-folding.
To avoid the need to remember to manually set breakpoints in each Matlab session, I often include dbstop commands in my code. For example:

try
   doSomething();
catch err
   dbstop myProgram 123   % line 123 is the next line number in myProgram.m
   disp(err)
end

The problem with this is that I need to modify the m-file name and line number whenever I make any change to the file. This is ok for small m-files but entirely unmanageable for large complex files (which is where such constructs are most needed).

We can use dbstack to extract the current line number and then use the functional interface of dbstop, for example: s = dbstack; dbstop(mfilename, num2str(s(1).line+1)); but this looks a bit convoluted. Ages ago BASIC had the ability to specify “Next” as the target line, and it would be very useful to have something similar in Matlab: dbstop next %or: dbstop('next')This looks much cleaner and more readable, don’t you agree?
We cannot modify the code (or strictly speaking, the m-files that exist on the current dbstack) when we’re stopped at a breakpoint. It would be very useful to lift this limitation so that if we see a bug as a result of the breakpoint debugging, we could fix the code, have Matlab recompile the code, and enable a dbcont from that point onward. Most major debuggers/IDEs in recent decades allow this… It is very frustrating to be forced to restart a long Matlab program run, just to be able to retest the program after a small fix.

Sean de Wolski replied on : 5 of 6

@Dan: I was not aware of the performance loss but would guess it’s because they’re EVALing the condition on each line. I’ve reported it to development.

@Eric: That’s a fair use case. I usually use sections for this and cherry pick which sections to run based on what I need.

@Yair: dbstop nextline would be very helpful; I reported this to development along with both of our votes for persistent bookmarks. I’ve rarely used bookmarks because of this limitation.

It is possible, with a couple extra steps, to make breakpoints persist across sessions.

b = dbstatus;
save(fullfile(prefdir, 'breakpoints.mat'), 'b');

In the next session

s = load(fullfile(prefdir, 'breakpoints.mat'));
dbstop(s.b);

These could then be added to startup.m and finish.m. Of course, it would make sense to have this as a preference.