left-icon

WPF Debugging and Performance Succinctly®
by Alessandro Del Sole

Previous
Chapter

of
A
A
A

CHAPTER 2

Stepping Through Code

Stepping Through Code


The debugger is one of the most powerful tools in the Visual Studio development experience. For instance, it allows us to execute single lines of code or small sets of lines and investigate the behavior of code along with referenced variables. Debugging code starts in the code editor, so this chapter will address how to work with breakpoints, data tips, and special commands that execute a limited set of lines of code per time. What you will learn in this chapter applies to any kind of application you build with Visual Studio, but here we will work with a WPF application.

Introducing breakpoints and data tips

A breakpoint causes the execution of the application to halt at the point where it is placed. When the application breaks because of a breakpoint, Visual Studio enters into break mode. In break mode, you can investigate the value of local variables, you can execute code in steps, you can evaluate expressions with debugging windows, and you can resume the execution when ready. Placing one or more breakpoints in your code is easy. In fact, you can either press F9 on the line of code you want to debug or you can click the leftmost column in the code editor window. You can also select Debug, Toggle Breakpoint. When you add a breakpoint, the line of code is highlighted in red. For example, in the sample application created in Chapter 1, add a breakpoint on the following line inside the BrowseButton_Click event handler:

this.FileNameBox.Text = openDialog.FileName;

The line will be highlighted in red, as shown in Figure 4.

Adding Breakpoints

Figure 4: Adding Breakpoints

Notice how the scroll bar in the code editor reports small red squares to help you find breakpoints in your code. Now, press F5 to start debugging the application. When running, click Browse and select any file on your PC. When Visual Studio encounters the breakpoint, it breaks the execution by entering the break mode, and it highlights in yellow the line that is currently debugged before that line is executed (see Figure 5). If you hover over the openDialog.FileName object, you will see a small tooltip that shows its current value (see Figure 5). If you do the same over the this.FileNameBox.Text property, the tooltip will show that it has no value yet, which is correct because that line of code has not been executed. Such tooltips go under the name of Data Tips. Now you have many options: you can step through the code with one of the debugging commands (described shortly), such as Step Into (F11), or you can resume the application execution with F5.

Investigating Variables’ Value with Data Tips

Figure 5: Investigating Variables’ Value with Data Tips

Run-to-click in Visual Studio 2017

Before Visual Studio 2017, you had to introduce temporary breakpoints to continue the execution from a breakpoint to a certain point in your code. Visual Studio 2017 took a step forward and introduced a new feature called Run to Click. When the debugger enters in break mode and you hover over a line of code, a green glyph appears near the line. This glyph represents the Run to Click button. If you click it, your code will be executed to that line, without the need of temporary breakpoints. Actually, the line of Run to Click is highlighted and not executed, exactly as would happen if a breakpoint was set on that line. You can find a more detailed explanation in my Visual Studio 2017 Succinctly e-book.

Intentionally and unintentionally breaking the application execution

Breakpoints allow you to halt the application execution intentionally at a certain point, so that you can investigate the behavior of your code. You can also intentionally break the application execution in code, using the System.Diagnostics.Debugger class and its Break method as follows:

System.Diagnostics.Debugger.Break();

When this line of code is encountered, the debugger enters the break mode. Of course, there are situations when the application execution breaks unintentionally—this is typically the case with unhandled runtime errors.

Tip: The Debugger class provides a communication channel with the debugger. The IsAttached property returns true if an instance of the debugger is attached to the process; the Log method allows sending a message to the debugger; the Launch method allows launching and attaching the debugger to the application process.

Removing breakpoints

Removing breakpoints is as easy as adding them. You can remove a breakpoint using F9, or by clicking the leftmost column in the code editor, or by selecting Debug, Toggle Breakpoint on the breakpoint’s current line. You can remove all the breakpoints by pressing Ctrl+Alt+F9 or by selecting Debug, Delete All Breakpoints.

Understanding runtime errors

Runtime errors are unpredictable. They happen while the application is running, and they are often caused by programming errors that cannot be detected at compile time. As an example, suppose you give users the option to specify a file name, but then the file is not found on disk. In real-world scenarios, you would certainly implement try..catch blocks, predict as many errors as possible, and handle those errors the proper way. However, while debugging, you might encounter an unhandled error. For instance, run the sample application again, but instead of selecting a file name with the Browse button, enter a file name that does not exist in the text box, then click Open. At this point, the application execution will break because of an unhandled error, and Visual Studio will enter the break mode, highlighting in yellow the line of code that caused the exception (see Figure 6).

Runtime Error Causes Application Execution to Break

Figure 6: Runtime Error Causes Application Execution to Break

In this case, the highlighted line of code is searching for a file that does not exist, but an appropriate try..catch block has not been supplied, therefore the debugger broke at this point. If you fall into this situation, you can see the exception details window (the grey pop-up), which not only shows information about the error, such as the bad file name (a FileNotFoundException in this case), but also allows you to get details by clicking the View Detail hyperlink in the Actions area. This will open the View Details window (see Figure 7), where you can get information about the stack trace and which will allow you to investigate the method calls stack. It will also open the InnerException object, which is useful if the current exception has been caused by another exception. Also, you will get exception-specific information, such as the FileName property value, which is exposed by the FileNotFoundException class.

Getting Details about Problems Causing an Exception at Runtime

Figure 7: Getting Details about Problems Causing an Exception at Runtime

Do not forget to enclose code that might potentially encounter runtime errors inside try..catch blocks, especially in the user interface’s code-behind, in order to avoid these situations and also in order to allow users to make a decision based on the context.

Fixing code at runtime with Edit and Continue

In some situations, you are allowed to change code that has caused an error while in break mode, then resume the application execution. This feature is known as Edit and Continue and, starting from Visual Studio 2015, it has been enhanced to support lambda expressions. You can also change some code by pausing the application execution (e.g., not because of an error) by clicking Pause on the Debug toolbar. In some cases, you will not be able to use Edit and Continue; for example, if your changes will influence the general application behavior, you will need to stop, edit, and restart your code. And, by the way, Visual Studio will tell you if Edit and Continue is available when you are editing your code in break mode.

Stepping through code

Sometimes your code might seem to have no bugs, but at runtime the application will not work as expected. For cases in which you must find subtle bugs, debugging can be a very complicated task. For this reason, the powerful debugger in Visual Studio allows you to step through code, which means executing one line of code, or a limited set of lines of code, and investigating that behavior. You can step through your code using a number of debugging commands, all available in both the Debug menu and with keyboard shortcuts, as will be described in detail in this chapter.

Tip: In most cases, you will step through your code while in break mode. However, two debugging commands, Step Into and Step Over, allow you to start debugging and step through code directly. This is useful if you want to investigate the behavior of your WPF application from startup.

Before continuing, place a breakpoint on any method or event handler in the sample application. This will help you understand how the following debugging commands work.

Step Into and Step Over

Step Into (F11) and Step Over (F10) both execute one instruction at a time. Here is the difference between them: if the instruction to be executed is a method, Step Over does not enter the method body and will complete its execution before going back to the caller. However, Step Into enters a method body and executes one instruction at a time. Step Over is useful when you must debug a method that invokes previously tested and debugged methods that you need not check every time.

Step Out

Step Out (Shift+F11) only works within methods. It executes all the lines of code next to the current line until the method completes. For example, if you place a breakpoint on the if block in the OpenButton_Click event handler in the sample application, the debugger will break on that line. However, when using Step Out, it will execute all the following lines until the event handler completes.

Run To Cursor

With Run To Cursor, you can place the cursor on any line of code, right-click, then select Run To Cursor. This will cause the debugger to execute all the lines until the selected one, then it will break and highlight the current line in yellow. This is useful when you want to debug a specific portion of code without using breakpoints.

Set Next Statement and Show Next Statement

While in break mode, you have the option to set and show the next statement that will be executed after resuming the application or after a breakpoint or stop. You can right-click the next statement you want to execute, thus excluding all the other lines from stepping through code, then select Set Next Statement. By contrast, with Show Next Statement, you can quickly move the cursor onto the next executable statement. This can be particularly useful with long code files where breakpoints are not immediately visible.

Debugging user code only

A WPF application is made of the code you write plus system code. You can decide whether to debug only your code or to debug system code, too. This feature is known as Just My Code, and it is enabled by default, which means the debugger will focus on user code only. In order to change this option and include system code, you must open the debugging options. This can be accomplished by selecting Tools, Options, then by opening the Debugging node in the Options window (see Figure 8).

Changing the Default for Just My Code

Figure 8: Changing the Default for Just My Code

Remember that disabling Just My Code will cause Visual Studio and the debugger to load more symbols and to monitor additional resources, which means doing so might slow your debugging experience. Disable it only when strictly required.

Enabling native code debugging

In case your WPF application invokes unmanaged code (such as the Win32 API), you can enable native code debugging. You can do this by opening the Properties window of a project and selecting the Enable native code debugging check box in the Debug tab. As for disabling Just My Code, enable native code debugging only when strictly required.

Customizing breakpoints

Now that you know how to set breakpoints and step through your code in break mode, it’s time to learn how to get the most out of breakpoints by using some interesting features.

Managing breakpoints with the Breakpoints window

By pressing Ctrl+Alt+B, you enable the Breakpoints window, which is where you can easily manage all the breakpoints in your solution via a convenient user interface (see Figure 9). Simply hover over each button in the toolbar to get a description.

Managing Breakpoints in the Breakpoints Window

Figure 9: Managing Breakpoints in the Breakpoints Window

You can select or unselect the check box for a breakpoint in order to temporarily disable or enable a breakpoint. The toolbar has buttons to add, remove, and even import and export breakpoints. In fact, Visual Studio can store the list of breakpoints into an XML file for later use. You can also click Columns on the toolbar and add additional columns to the window for further details on each breakpoint. Such a window also shows important information, including labels, conditions, and hit counts.

Providing breakpoint labels

Though the Breakpoints window provides a convenient way to work with breakpoints, if you have dozens of breakpoints in your code, it can be very hard to remember what each breakpoint deals with, and viewing the file and line number where the breakpoint is located might not be very helpful. Visual Studio allows adding and editing breakpoint labels—a kind of identifier that will help you categorize, find, and manage breakpoints more easily. In order to provide a label to a breakpoint, you have several options:

  • In the Breakpoints window, right-click a breakpoint, then select Edit labels.
  • In the code editor, right-click the red glyph that identifies a breakpoint, then select Edit labels.

Whichever option you choose, you will see the Edit breakpoint labels window appear. For instance, suppose you have a breakpoint on the OpenFile method definition. In the Edit Breakpoint Labels window, you can type a description as shown in Figure 10.

Assigning a Label to a Breakpoint

Figure 10: Assigning a Label to a Breakpoint

Click Add, then click OK. The new label will be immediately visible in the Breakpoints window, making it easier to remember what a breakpoint is about.

Setting breakpoint conditions

Conditions allow you to specify when the application execution must break as a breakpoint is hit. In order to understand how conditions work, place a breakpoint inside the body of the OpenFile method, thus the return statement. Then right-click the breakpoint’s red icon and select Conditions. At this point, you can specify one or more conditions that will make the execution break when this breakpoint is encountered.

Note: The user interface for adding conditions and actions has changed starting with Visual Studio 2015. Now you have a convenient pop-up that allows you to keep your focus on the active editor window instead of modal dialogs, as with previous editions.

Imagine you want that breakpoint to cause the application execution break only if the supplied string is null. This kind of condition requires an expression to be evaluated; that is, the supplied string is equal to null. The Conditional Expression option allows evaluating conditions that must be true in order to break the application execution when the breakpoint is encountered. In this case, you can specify the condition shown in Figure 11.

Specifying a Breakpoint Condition

Figure 11: Specifying a Breakpoint Condition

It is worth mentioning the availability of IntelliSense, which dramatically simplifies the way you write the expression to be evaluated. Now, let’s talk about other conditions—Hit Count and Filter. Hit Count allows you to debug code from a certain point forward. Suppose you have a for loop with a breakpoint inside but you do not want the application execution to break at every iteration. With Hit Count, you can specify at which iteration the breakpoint must be hit and, consequently, the application execution must break. For example, if you enter Hit Count = 2, the breakpoint will be hit at the second iteration. Possible options are = (equal to), is a multiple of, and >= (greater than or equal to). With Filter, you can break the application execution only if the breakpoint is hit on the specified process, thread, or machine name.

Sending messages to the Output window with Actions

The Actions feature allows you to send log messages to the Output window using a built-in function.

Note: In previous editions of Visual Studio, this feature was known as Trace Points. It is now called Actions and is included in the new user interface for breakpoint settings.

To add an action, right-click the breakpoint’s red icon and select Actions. You will see the same interactive pop-up described for conditions. In the Actions area, you can provide the name of a built-in function writing the $ symbol, then pick up the function name from the contextualized IntelliSense (see Figure 12).

Specifying an Action

Figure 12: Specifying an Action

For example, the $CALLSTACK function will send the call stack information to the Output window. Table 1 shows the supported functions.

Table 1: Supported Functions in the Actions Configuration

Breakpoint Actions

$ADDRESS

The current instruction

$CALLER

The name of the caller function

$CALLSTACK

The method call stack

$FUNCTION

The name of the current function

$PID

The process ID

$PNAME

The process name

$TID

The thread ID

$TNAME

The thread name

Custom expressions are also supported and documented at https://blogs.msdn.microsoft.com/visualstudioalm/2013/10/10/tracepoints/.

Introducing Performance Tips

Visual Studio 2015 introduced an interesting new feature to your debugging experience called Performance Tips (also referred to as PerfTips). With this feature, you can measure how long it takes to execute a section of code in break mode. In order to understand how it works, place two breakpoints in the sample application, one on the line of code that invokes the OpenFile method and assigns its result to the text box, and one on the return statement in the OpenFile method. Now start the application, select an existing file name with Browse, then click Open. At this point, the first breakpoint is hit. Press F5 to resume the execution. When the second breakpoint is hit, you will see a small tooltip near the highlighted line showing the number of milliseconds it took to execute the code between the two breakpoints, as shown in Figure 13.

Measuring Code Execution Time with PerfTips

Figure 13: Measuring Code Execution Time with PerfTips

Note: PerfTips time values are approximate because they include debugging overhead. However, they are useful for establishing a good idea about the behavior of your code.

Chapter summary

Visual Studio 2015 and 2017 provide powerful debugging tools that you can use with any kind of .NET application and that you saw in action against a WPF project. With breakpoints, you highlight the lines of code you want to investigate, then you can step through your code by executing one line or a small set of lines at a time with commands such as Step Into, Step Over, and Step Out. But breakpoints offer more—you can specify conditions and actions to control when the application execution must break and what information you want to log to the Output window. The tools described in this chapter are typically available within the code editor, but the debugging experience in VS 2015 offers much more. In the next chapter, you will work with windows that allow deeper investigations into and evaluations of your code.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.