Tuesday, April 16, 2024 04:10

Table of contents >> Debugging And Error Handling > Breakpoints and local variables

Breakpoints and local variables

Breakpoints are arguably the most used feature in the process of debugging. As their name suggests, they are literally a point where the execution of your program will break, or more precisely, will pause. Whenever the execution of a program is in this paused state, which isn’t design time, but it’s neither runtime (you could say it is an intermediary between these two states), we say that the program is in debug time, or debugging mode.

So, why do we need to pause the execution of our programs? Why do we need to get into this debugging mode? Well, first of all, by setting a breakpoint at a certain line of code, we can check whether that line is executed or not, like, for instance, in an If-Else statement, to see which condition is met. Let’s actually do that! Let’s consider the following code:

The easiest way to create a breakpoint is by clicking on the vertical left bar of main screen of Visual Studio, to the left of the line you want to set a breakpoint to. When you click it with the mouse, you should see a red circle appearing on it, like this:

Setting a breakpoint in Visual Studio

Another way of doing this is by placing the cursor on the line where you want to set up your breakpoint and going to the Debug menu and choosing the Toggle Breakpoint option, or by simply pressing F9 on your keyboard. To disable a breakpoint, you can simply click on the red circle with your mouse, or press F9 again while the cursor is on the specified line of code.

Run your program, like you already did countless times. You will notice that nothing new happens. The output looks like this:


The reason why nothing happened is because, obviously, if we initialized our string variable as “Tuesday”, when we check to see if it’s “Monday”, that condition will equal to False, and the code of that condition will not be run. Click on the breakpoint again, to delete it, and instead, place a breakpoint here:

Run the program again.

Oops! Something strange happened. The console window was created, but it immediately minimized, and Visual Studio showed us the code editor again. Additionally, our green Start button (the one that looks like a “Play”) now says “Continue”, our new breakpoint now seems to have a yellow arrow on it, while the line of code is also highlighted with a yellow background:

Visual Studio breakpoint hit

If we go to the console window again, we can also see that it is empty. Nothing was printed. And it is also not responding to our interactions, no matter how many keys we press. This means that the Console.Read(); part is not doing it’s job! And why didn’t it print “Today is not the first day of the week!”?

The fact that the red circle has a yellow arrow on it indicates that that’s the point where the execution was paused, or, as programmers say, “where it hit the breakpoint”. In simple words, since dayOfWeek was initialized as “Tuesday”, when we checked it to see if its value is “Monday”, that condition returned False, and the execution was transferred to the Else part. And since we placed a breakpoint at that part of the code, when the execution was transferred to it, the breakpoint was hit and it paused the execution of the program. We are now in debugging mode. Since the phrase was not yet printed on the screen, this also means that when a breakpoint is hit, the yellow arrow indicates where the execution paused, in other words, what’s the next code to be executed when the program execution resumes. This is the reason why the phrase was not printed on the console, that instruction will be the first following when the execution resumes. Also, the reason why Console.Read(); was not executed is similar: that part of the code was not yet reached, it is yet to be executed.

The fact that our program is in a paused state also explains why the console window doesn’t respond to our interactions.

So, what can we do in this debugging state? OK, we saw that we can test whether a certain line of code is executed or not. But, that’s it?

No. Just hover with your mouse cursor over the dayOfWeek variable:

Visual Studio will display a small popup with the name of our variable, and its current value. That is really a good thing! We can see what the values of our variables are, in real time! Hover the cursor of your mouse over the equality check operator, ==. You will see this:

conditional check at debug time in Visual Studio

Another beautiful thing! Visual Studio lets us know the result of our conditional checks! It indicates that the condition dayOfWeek == "Monday" was False when the execution reached it, and that explains why the Else part was executed.

So, only for these, and breakpoints would be awesome! But, wait! There’s more to them! Set another breakpoint on the line Console.Read();. After that, click on the Continue button (the one that used to be the Start one). You will see this:

The execution now resumed, but it immediately paused again at the next line, because it encountered our second breakpoint. This demonstrates once more, if it was still necessary, that execution of programs is linear, from top to bottom. Anyway, this is not the reason why I told you to perform these new steps. Go to the console window again and you will see that now it looks like this:

But trying to press any key would still be in ignored. The line that was printed appeared because when we clicked on Continue, Visual Studio executed the next codes that were following after the point where it was paused, until the new breakpoint paused it. In other words, it executed the Console.WriteLine("Today is is not the first day of the week!"); line.

Press Continue again, and you will see the console window restoring to front and none of our breakpoints with yellow arrows on them, while the Continue button reverted to being grayed out. All these indicate that we are back in runtime mode. If you want, you can check this by pressing any key, followed by Enter. Console.Read(); line should now do its job and the window will close. Stop the execution.

Delete the breakpoints by going to the Debug menu and choosing Delete All Breakpoints. You can always use this option to clear up your numerous breakpoints, but be aware that it will delete all the breakpoints in the entire project. So, pay attention, if you have some set in other files.

Now, set a new breakpoint at the first line inside the block of our Main() method. Start the program again. The execution will be paused again, and we will enter the debug mode once more:

Hover the cursor of your mouse over the dayOfWeek variable again. Visual Studio will indicate to us that its value is null:

We already know now that this happen because the line where we assign a value to that variable is not yet executed, since the breakpoint paused the execution before it got a chance to execute that line. We now know that that’s where the execution reached, and that’s the next line that will be executed when the execution resumes. We can see that our variable is null also in the Locals tab at the bottom, as I explained in the previous lesson:

Now, to advance the execution just one more line, we know that we could set up a new breakpoint on the next line. But if we want to pause the execution line by line for 10 lines, this will soon become annoying, setting and deleting breakpoints all the time. Instead, now go to the Debug menu and chose Step Into, or press F11 on your keyboard. The same thing can be achieved by clicking on this button on the Visual Studio top toolbar:

What you will notice is the fact that the yellow arrow over our breakpoint will now jump to the next line automatically:

Visual Studio Step Into

If we hover our dayOfWeek variable again, we will now see that it has the “Tuesday” value assigned. This means that the execution resumed, executed the line where the value received its value, and then paused again, advancing a single line. No need to manually set a breakpoint on the next line, when we can execute next lines one at a time, using this button.

Hit the Step Into button again:

If you expected it to advance on the next line, you were partially wrong. Only partially because it indeed jumped on the next line, but on the next line to be executed according to the logic of our program. Since the line that was executed was a conditional check, it calculated its result, and advanced the execution to the first line that followed according to that conditional results. Since we checked if dayOfWeek is “Monday”, but it was “Tuesday”, the execution jumped on the Else block, not the If one. With this, we realize that we can use this Step Into button to follow the execution path of our programs, see which lines are executed, which are ignored, etc.

Now, click on the yellow arrow on the left vertical bar, the one that indicates where the execution is paused and drag it to the line where our breakpoint is set. Hit the Step Into button again. You can observe that we can actually move the execution to whatever line we want (inside the current method, that is!), including backwards to the beginning (running the lines again) or even in places that would normally be ignored by the compiler. Yes! We can even drag it to the block of the If condition, even if the condition would return False and the Else block would be normally executed:

You wouldn’t normally need to do this, but there are cases where you want to test how some codes that would not be hit under the current conditions behave. Who knows? Finally, you can even drag the arrow downwards, making the compiler skip executing some of the lines. We can even drag it to the ending curly bracket of the Main() method, skipping the execution of all the code left inside its block! This feature could prove to be very useful in certain situations. I only need to think of one scenario where this could be critically important: if you were working in a nuclear power plant and you were debugging the codes that would trigger the shut down of the core, would you really shut it down when the execution reaches that part? No, we only need to test that the conditions that trigger the shut down are met and the execution actually gets there. After that, we can simply skip the code that would really shut it off.

Drag and drop the execution yellow arrow to the first line of the Main() method block again. Hit Step Into. Hover the dayOfWeek variable again. It’s value will be “Tuesday”, as expected. However, click on the popup that shows its value. It should suddenly enter in editing mode:

On the fly edit of variable values in debug mode

Change the value to “Monday” and press Enter. It will come as a surprise to you, but even if you assigned the “Tuesday” value to your variable in your code, if you hover it now with the mouse, it has the value “Monday”. So, wow, Visual Studio even lets us change the values of our variables on the fly, while the execution is paused, without modifying our codes. Like this, we can very easily check what the computations are when our variables have different values. If we hit Step Into now, we will see that the condition evaluation is quite different:

The If block was executed now, because we changed the value of our variable to “Monday”, and now the conditional check returns True, thus executing that particular block of code.

As you can notice, there are a few other buttons near the Step Into one, namely: Show Next Statement, Step Into, Step Over, Step Out. Let’s analyze a bit deeper what they all do.

Show Next Statement is quite simple. It highlights the next statement that will be executed when the execution resumes.

For Step Into, Step Over and Step Out, let’s change our code a bit:

Place a new breakpoint at the first line of code inside Main’s block, and start the execution. Press Step Into once, and you will see the execution advance to the next line of code, which is what you would expect. Now, press Step Into again, and you will notice something different, but logical: since this is the line where we call MyFirstMethod(), the execution is transferred to this new method, and the debugger follows that path. To highlight the fact that the code left the Main method because it needed to go “deeper”, inside the body of the method it was calling, you will also notice that the method call now has a dark gray background:

Debugger Step Into

If we continue to press the Step Into button, the current executing line will advance until it will reach the call to the second method, where, again, it will be transferred:

Keep pressing Step Into and you will witness the execution advancing through it’s block, then returning to the first method:

Keep pressing it more, and you will see the execution finally returning to the code inside Main() method, where it were before the method call:

Good! Now we know that using Step Into will transfer the debugger current executing line to whatever place the logic of the program flows, even if this means going inside other methods, classes, project files, etc. We can always use this to follow a strict line of execution of the code, where we can see the exact path it took from start to end. However, in our particular example, maybe we only want to debug the codes inside our Main() method, and we are not at all interested in the codes inside first and second methods. Here, we can use Step Over button. Restart the program’s execution and advance it until the call to the first method follows:

We do know that if we were to press Step Into, the execution would now be transferred to MyFirstMethod(), so, instead, press Step Over. You will see the debugger advancing one line, still inside Main() method:

Visual Studio Step Over

So, indeed, it stepped over the codes inside the MyFirstMethod(), and consequently MySecondMethod(), which was called by it. However, be very aware of the fact that this does NOT mean those calls were not executed! MyFirstMethod() was indeed called, and printed its messages on the console, then calling method MySecondMethod(), which printed its message on the console too, etc, etc, but when both these method’s blocks were executed, the execution returned to Main(), where it paused again, at the next line. So, Step Over doesn’t imply that it will skip the execution of the method that the code is calling, but it simply executes it without pause until the execution returns to the first line after the initial method call. To demonstrate this, we can take a look at the console screen, where we will see the text printed inside these two methods:

Finally, let’s see what Step Out button does. Restart the execution of the program, and use Step Into to advance the executed line until you are inside MyFirstMethod(), like this:

Visual Studio Step Out

Now, instead of pressing Step Into once again, which would transfer the execution to the MySeconMethod() block, due to the call to this method, press Step Out button:

Visual Studio Step Out

and see that the execution was transferred back to the Main() method. In other words, if we used Step Into to go one level deeper inside a function call, if we pressed Step Out while inside its body, the execution will be returned to the point where that method was called. As a conclusion, using a combination of Step Into and Step Out can help us peek inside the methods that are called in our codes, and quickly return to where it were, if it was not what we were looking for.

To conclude this lesson, let’s take a look at the menu that appears when we right click a breakpoint:

Breakpoint context menu

We can already imagine what Delete and Disable Breakpoint do. They are self descriptive. The last options, Export, is also intuitive: it lets us export breakpoints as XML files which can later be imported, to have the same breakpoints set up again.

But, let’s talk about the remaining three options: Conditions, Actions and Labels. First, let’s change our code a bit:

As you can see, we only have a For loop which counts from 0 to 99 and displays the current value at the console. Set a breakpoint at the line where we print the value on the console. Right click on the breakpoint and choose Conditions. You will be presented with a new window:

Visual Studio Conditional Breakpoint

Now, inside the text field, type i == 15, then press Enter. You can then close the popup window. Your breakpoint should now have a white cross on it:

Visual Studio Advanced Breakpoint

This indicates that the breakpoint is an advanced breakpoint, in this case, one that will be hit only when the condition we specified will be hit. Run the program. The execution will stop at our breakpoint, and if we hover with the mouse cursor over the variable i, or if we go to the Locals tab, we will see that the current value of i is 15, just as we specified as condition for the breakpoint. You should already see how this could be useful to us, to be able to set out breakpoints that will pause the execution of our programs only when certain condition we want to specify are met or not met, if a certain variable’s value changes, and a lot of other options that you should check out for yourselves.

Similarly, Actions would allow us to perform an action, and notice that we can combine Conditions and Actions together, for more control.

The last point of interest for us in the context menu of a breakpoint is Labels. Right click the breakpoint’s red circle and choose Edit Labels from the menu. In the new window that appears, type a descriptive message for what the breakpoint does:

Visual Studio Breakpoint Label

Now, by going to the Debug menu, Windows submenu and clicking on Breakpoints, we will get a list of all the breakpoints inside our application, meaning, in all its files. Double clicking on a breakpoint will get us to the line of code where that breakpoint is set. The interesting thing to notice is that here we can also see the labels we set to our breakpoints:

Another important thing is the fact that we can actually filter our breakpoints by labels, and this could be crucial when we have a lot of breakpoints set throughout our entire application (remember that we can set the same label for multiple breakpoints). Here is an example where we only display breakpoints that have their label set to “Bug 3135”:

Visual Studio Breakpoint Filter

With this, we conclude the lesson about breakpoints. In the next lesson we will learn about the Stack and the Heap memory areas, what they are and how they can help us.

Tags: ,

Leave a Reply

Follow the white rabbit