© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
D. StraussGetting Started with Visual Studio 2022https://doi.org/10.1007/978-1-4842-8922-8_3

3. Debugging Your Code

Dirk Strauss1  
(1)
Uitenhage, South Africa
 

Debugging code is probably one of the most essential tasks that a developer performs. Being able to run your application and pause the execution of code midway is a lifesaver. But there is a lot more to debugging than just setting breakpoints and viewing results.

In this chapter, we will be discussing the options available to you as a developer that needs to effectively debug their code. We will be looking at
  • Using breakpoints, conditional breakpoints, breakpoint actions and labels, and exporting breakpoints

  • Using data tips

  • The DebuggerDisplay attribute

  • Diagnostic Tools and Immediate Window

  • Attaching to a running process

  • Remote debugging

Visual Studio gives developers all the required tools in order to effectively debug the code you are experiencing problems with. Without being able to debug your code, it will be virtually impossible to resolve any issues you might be experiencing.

Not being able to effectively debug your application (not knowing how to effectively use the tools you have) is just as bad as not having the tools to debug with in the first place.

Working with Breakpoints

If you are familiar with debugging in Visual Studio, this chapter might seem like old hat for you. Stick around, there might be sections discussed here that you didn’t know about.

If you are new to Visual Studio, the concept of debugging in Visual Studio is when you run your application with the debugger attached. Debugging allows you to step through the code and view the values stored in variables. More importantly, you can see how those values change.

Setting a Breakpoint

The most basic task of debugging is setting a breakpoint. Breakpoints mark the lines of code that you want Visual Studio to pause at, allowing you to take a closer look at what the code is doing at that particular point in time. To place a breakpoint in code, click the margin to the left of the line of code you want to inspect as seen in Figure 3-1.

A snapshot of the programming code in which if condition for validating the password is highlighted.

Figure 3-1

Setting a breakpoint

This line of code is contained in the ValidateLogin() method. The method is called when the user clicks the login button. Press F5 or click Debug ➤ Start Debugging to run your application. You can also just click the Start button as shown in Figure 3-2.

A snapshot denotes the toolbar which contains the Search bar and buttons, including the Start button for executing the programming code.

Figure 3-2

The Start button

After you start debugging, and a breakpoint is hit, the debug toolbar in Visual Studio changes as seen in Figure 3-3.

A snapshot of debug toolbar, after a breakpoint is hit, the Start button changes to Continue.

Figure 3-3

Debug toolbar when breakpoint hit

The Start button now changes to display Continue. Remember, at this point, your code execution is paused in Visual Studio at the breakpoint you set earlier.

In order to step through your code, you can click the step buttons as displayed in Figure 3-4.

A snapshot denotes a set of 3 step buttons on the tool bar. The buttons are Step into, Step Over, and Step Out.

Figure 3-4

Step buttons

From left to right, these buttons are as follows:
  • Step Into (F11)

  • Step Over (F10)

  • Step Out (Shift+F11)

When you step into a method, you jump to the point in the editor where that method’s code is. If you do not want to step into the method, you can click the Step Over button or press F10 to carry on with the next line of code. If you are inside a method and want to step out and continue debugging the calling code, click the Step Out button or press Shift+F11.

Step into Specific

Imagine that we need a method that generates a waybill number based on specific business rules. Then, when the application starts, the text box field is auto populated with the generated waybill number.

The code used to generate the random waybill functionality is listed in Listing 3-1.
private string GenerateWaybill(string partA, int rndNum) => $"{partA}-{rndNum}-{DateTime.Now.Year}-{DateTime.Now.Month}";
private string WBPartA() => "acme-";
private int WBPartB(int min, int max)
{
    var rngCrypto = new RNGCryptoServiceProvider();
    var bf = new byte[4];
    rngCrypto.GetBytes(bf);
    var result = BitConverter.ToInt32(bf, 0);
    return new Random(result).Next(min, max);
}
Listing 3-1

Waybill Generation Code

In the form load of the tracking application, we then make a call to the GenerateWaybill() method and pass it the other two methods WBPartA() and WBPartB() as parameters as seen in Listing 3-2.
private void Form1_Load(object sender, EventArgs e)
{
    var frmLogin = new Login();
    _ = frmLogin.ShowDialog();
    txtWaybill.Text = GenerateWaybill(WBPartA(), WBPartB(100,2000));
}
Listing 3-2

Form Load

If you had placed a breakpoint on the line of code that contains the GenerateWaybill() method and step into the methods by pressing F11, you would first step into method WBPartA(), then into method WBPartB(), and lastly into the GenerateWaybill() method.

Did you know that you can choose which method to step into? When the breakpoint is hit, hold down Alt+Shift+F11 and Visual Studio will pop up a menu for you to choose from as seen in Figure 3-5.

A snapshot denotes executing a programming code and a set of code is highlighted in the popup menu.

Figure 3-5

Step into specific

Simply select the method you want to step into and off you go.

Run to Click

When you start debugging and you hit a breakpoint, you can jump around quickly within the code by clicking the Run to Click button. While in the debugger, hover your mouse over a line of code as seen in Figure 3-6, and click the Run to Click button that pops up.

A Snapshot depicts programming code in which the Run button is denoted with the caption of Run execution here.

Figure 3-6

Run to Click

This will advance the debugger to the line of code where you clicked, allowing you to continue stepping through the code from the new location. Quite handy if you do not want to be pressing F10 a gazillion times.

Run to Cursor

Run to Cursor works similarly to Run to Click. The difference is that with Run to Cursor you are not debugging. With the debugger stopped, you can right-click a line of code and click Run to Cursor from the context menu as seen in Figure 3-7.

A snapshot of the context menu which includes a list of options and Run To Cursor is indicated by an arrow.

Figure 3-7

Run to Cursor

Doing this will start the debugger and set a temporary breakpoint on the line you right-clicked. This is useful for quickly setting a breakpoint and starting the debugger at the same time. When you reach the breakpoint, you can continue debugging as normal.

Be aware though that you will be hitting any other breakpoints set before the temporary breakpoint first. So, you will need to keep on pressing F5 until you reach the line of code you set the temporary breakpoint on.

Force Run to Cursor

Visual Studio 2022 will now allow you to skip any breakpoints between the line of code your breakpoint is on and the line of code that you want to debug. This is done with Force Run to Cursor on the context menu which can be seen in Figure 3-7. You can also easily access this while debugging as seen in Figure 3-8.

A snapshot depicts the execution of Programming code in which force run to cursor button is highlighted with an arrow mark and labeled as Hover mouse and shift plus click.

Figure 3-8

Force Run to Cursor

Here, we have a breakpoint at the top of the method that is currently hit, and the debugger is paused. I want to run the cursor to the return statement, but there are two breakpoints between where my debugger is and where I want to be. To skip the breakpoints in the middle, hover the mouse over the line that you want to move to next, and hold down the Shift key. The Run to execution button with the single arrow (as seen in Figure 3-6) now changes to the Force run execution to here button with a double arrow as seen in Figure 3-8. This will now advance the debugger to the line of code you forced the debugger to, skipping all the breakpoints in between. This is especially convenient since I do not have to remove my breakpoints, nor do I have to keep on pressing F5 for each breakpoint hit.

Conditional Breakpoints and Actions

Sometimes, you need to use a condition to catch a bug. Let’s say that you are in a for loop, and the bug seems to be data related. The erroneous data only seems to enter the loop after several hundred iterations. If you set a regular breakpoint, you will be pressing F10 until your keyboard stops working.

This is a perfect use case for using conditional breakpoints. You can now tell the debugger to break when a specific condition is true. To set a conditional breakpoint, right-click the breakpoint, and click Conditions from the context menu as seen in Figure 3-9.

A snapshot of the breakpoint context menu with six specific options. The option, Conditions is highlighted, which is used to set a conditional breakpoint.

Figure 3-9

Breakpoint context menu

You can now select a conditional expression and select to break if this condition is true or when changed as seen in Figure 3-10.

A snapshot of the breakpoint settings window to set a conditional expression to break if the conditional expression is true.

Figure 3-10

Conditional expression

We will discuss Actions shortly.

You can also select to break when the Hit Count is equal to, a multiple of, or greater or equal to a value you set as seen in Figure 3-11.

A snapshot of the breakpoint settings window, where conditions for Hit Count can be set. The conditions to break are, when hit point equals to, is a multiple of, or greater or equal to a value.

Figure 3-11

Hit Count condition

The last condition you can set on a conditional breakpoint is a Filter as seen in Figure 3-12.

A snapshot breakpoint settings window that can be used to set conditions for Filter. You can choose to break if the value of Filter is true.

Figure 3-12

Filter condition

You will have noticed the Actions checkbox from the Breakpoint Settings. You will also see the Actions menu on the context menu in Figure 3-9. Here, you can add an expression to log to the Output Window using specific keywords that are accessed using the $ symbol.

The special keywords are as follows:
  • $ADDRESS – Current instruction

  • $CALLER – Previous function name

  • $CALLSTACK – Call stack

  • $FILEPOS – The current file and line position

  • $FUNCTION – Current function name

  • $PID – Process ID

  • $PNAME – Process name

  • $TICK – Milliseconds elapsed since the system was started, up to 49.7 days

  • $TID – Thread ID

  • $TNAME – Thread name

You can now use these special keywords to write an entry to the Output Window. You can include the value of a variable by placing it between curly braces (think of interpolated strings). Listing 3-3 shows an example of an expression that uses the $FUNCTION keyword.
The value of the counter = {iCount} in $FUNCTION
Listing 3-3

Action Expression

Placing this breakpoint action in the constructor of the Login() form of the ShipmentLocator application will be indicated by a diamond instead of a circle as seen in Figure 3-13.

A snapshot depicts the Programming code. Line 14 is the breakpoint action in the constructor of the login form of the shipment locator application and is indicated by a diamond symbol.

Figure 3-13

Breakpoint action

When you run your application, you will see the expression output in the Output Window as seen in Figure 3-14.

A snapshot of the output window of the programming code that displays the expression output. The Action will be displayed output window without pausing the code.

Figure 3-14

Action expression in Output Window

This is great for debugging because if you don’t select a condition, the Action will be displayed in the Output Window without hitting the breakpoint and pausing the code. The breakpoint action can be seen in Figure 3-15.

A snapshot of the breakpoint settings window. If a condition is not selected, the Action will log a message to the output window without pausing the code. To pause the code, uncheck the continue execution box.

Figure 3-15

The breakpoint action

If you want to pause the code execution, then you need to uncheck the Continue execution checkbox.

Temporary Breakpoints

There might be times when you only want a breakpoint hit once and never again. A scenario could exist where you need to check the change of a variable in several places, but once this is confirmed to be working, you do not need to check the variable again. Instead of having to add several breakpoints, and having to remove them again afterward, Visual Studio 2022 allows you to set a temporary breakpoint. Right-click the line of code where you want to set the temporary breakpoint, and from the Breakpoint menu item, select Insert Temporary Breakpoint as seen in Figure 3-16.

A snapshot illustrates the menu bar that appears when you right click the line of code. The first value Breakpoint is selected, and it leads to a submenu. From the submenu, the Insert Temporary Breakpoint option is selected.

Figure 3-16

Insert Temporary Breakpoint

You can also hold down Shift+Alt+F9, T to do the same thing.

Dependent Breakpoints

If you look at Figure 3-16 again, you will notice an option to insert a dependent breakpoint. A dependent breakpoint is a fantastic addition to Visual Studio because it is a breakpoint that will only pause the debugger when another breakpoint is hit on which it has been marked as a dependent.

As seen in Figure 3-17, you can click the drop-down menu which will show you a list of other breakpoints to choose from. After selecting a dependent breakpoint, the debugger will only pause when the breakpoint you selected from the drop-down is hit.

A snapshot of the drop down menu with a list of other breakpoints. The list consists of Conditions, Actions, Remove breakpoint once hit, and Only enable when the following breakpoint is hit.

Figure 3-17

Insert a dependent breakpoint

Dragging Breakpoints

You can also drag breakpoints to a different line of code. To do this, click and hold on the breakpoint and start dragging your mouse. You can now move it to another line.

Manage Breakpoints with Labels

As you continue debugging your application, you will be setting many breakpoints throughout the code. Different developers have different ways of debugging. Personally, I add and remove breakpoints as needed, but some developers might end up with a lot of set breakpoints as seen in Figure 3-18.

A snapshot of a set of programming codes. Lines 17, 24, 27, 31, 32, 33, and 34 are highlighted. Many breakpoints are set during debugging the application.

Figure 3-18

Many breakpoints set

This is where the Breakpoints window comes in handy. Think of it as mission control for managing complex debugging sessions. This is especially helpful in large solutions where you might have many breakpoints set at various code files throughout your solution.

The Breakpoints window allows developers to manage the breakpoints that they have set by allowing them to search, sort, filter, enable, disable, and delete breakpoints. The Breakpoints window also allows developers to specify conditional breakpoints and actions.

To open the Breakpoints window, click the Debug menu, Windows, and then Breakpoints. You can also press Ctrl+D, Ctrl+B. The Breakpoints window will now be displayed as seen in Figure 3-19.

A snapshot of the breakpoints window. It lists the line number of the breakpoints in the code.

Figure 3-19

Breakpoints window

Compare the line numbers of the breakpoints listed in Figure 3-19 with the breakpoints displayed in Figure 3-18. You will see that this accurately reflects the breakpoints displayed in the Breakpoints window.

The only problem with this window is that it doesn’t help you much in the way of managing your breakpoints. At the moment, the only information displayed in the Breakpoints window is the class name and the line number.

This is where breakpoint labels are very beneficial. To set a breakpoint label, right-click a breakpoint, and click Edit labels from the context menu as seen in Figure 3-20.

A snapshot of the context menu that is displayed when you right click a breakpoint. It lists six options, click Edit Labels to set a breakpoint label.

Figure 3-20

Edit breakpoint labels

The Edit breakpoint labels window is then displayed as seen in Figure 3-21.

A snapshot of the action window titled edit breakpoint labels, used to add a new breakpoint label. It has a text box labeled, type a new label, a text area labeled, or choose among existing labels, and buttons for OK and cancel.

Figure 3-21

Add a new breakpoint label

A snapshot of the breakpoints window. It displays the list of breakpoint name, labels, and Hit Count.

Figure 3-22

Breakpoints window with labels set

You can type in a new label or choose from any of the existing labels available. If you swing back to the Breakpoints window, you will see that these labels are displayed (Figure 3-22), making the identification and management of your breakpoints much easier.

You are in a better position now with the breakpoint labels set to manage your breakpoints more effectively.

Exporting Breakpoints

If you would like to save the current state and location of the breakpoints you have set, Visual Studio allows you to export and import breakpoints. This will create an XML file with the exported breakpoints that you can then share with a colleague.

I foresee the use of Visual Studio Live Share replacing the need to share breakpoints with a colleague just for the sake of aiding in debugging an application. There are, however, other situations I can see exporting breakpoints as being beneficial.

To export your breakpoints, you can right-click a breakpoint and click Export from the context menu, or you can click the export button in the Breakpoints window. You can also import breakpoints from the Breakpoints window by clicking the export or import button as highlighted in Figure 3-23.

A snapshot denotes the breakpoints window. The buttons for export and import are highlighted.

Figure 3-23

Import or export breakpoints

I’m not too convinced that the icons used on the import and export buttons are indicative of importing and exporting something, but that is just my personal opinion.

Using DataTips

DataTips in Visual Studio allows developers to view information about variables during a debug session. You can only view DataTips in break mode, and DataTips only work with variables that are currently in scope.

This means that before you can see a DataTip, you are going to have to debug your code. Place a breakpoint somewhere in your code and start debugging. When you hit the breakpoint that you have set, you can hover your mouse cursor over a variable. The DataTip now appears showing the name of the variable and the value it currently holds. You can also pin this DataTip as seen in Figure 3-24.

A snapshot illustrates a set of programming codes. Datatip highlights the name of the variable, and it indicates the value, comment, and pinned, with arrow marks.

Figure 3-24

Debugger DataTip

When you pin a DataTip, a pin will appear in the gutter next to the line number. You can now move this DataTip around to another position on the screen. If you look below the pin icon on the DataTip, you will see a “double down arrow” icon. If you click this, you can add a comment to your DataTip as seen in Figure 3-25.

A snapshot of the programming code. It explains the addition of a comment to the DataTip by clicking the double down arrow below the pin icon on the DataTip.

Figure 3-25

DataTip comment

DataTips also allow you to edit the value of the variable, as long as the value isn’t read-only. To do this, simply select the value in the DataTip and enter a new value. Then press the Enter key to save the new value.

Visualizing Complex Data Types

DataTips also allow you to visualize complex data in a more meaningful way. To illustrate this, we will need to write a little bit of code. We are going to create a class, then create a list of that class, and then create a data table from that list that we will view in the DataTip. I have just created a small Console Application. Start by creating the class in Listing 3-4.
public class Subject
{
    public int SubjectCode { get; set; }
    public string SubjectDescription { get; set; }
}
Listing 3-4

The Subject Class

We are going to create a list of the Subject class. Before we do this, however, we need to write the code that is going to create a DataTable of the values in List<Subject>. This code is illustrated in Listing 3-5.
static DataTable ConvertListToDataTable<T>(List<T> list)
{
    var table = new DataTable();
    var properties = typeof(T).GetProperties();
    foreach (var prop in properties)
    {
        _ = table.Columns.Add(prop.Name);
    }
    foreach (var item in list)
    {
        var row = table.NewRow();
        foreach (var property in properties)
        {
            var name = property.Name;
            var value = property.GetValue(item, null);
            row[name] = value;
        }
        table.Rows.Add(row);
    }
    return table;
}
Listing 3-5

Convert List to DataTable

In the Main method, add the code in Listing 3-6. We will then place a breakpoint on the call to the ConvertListToDataTable() method and step over that so that we can inspect the table variable’s DataTip.
static void Main(string[] args)
{
    var lstSubjects = new List<Subject>();
    for (var i = 0; i <= 5; i++)
    {
        var sub = new Subject();
        sub.SubjectCode = i;
        sub.SubjectDescription = $"Subject-{i}";
        lstSubjects.Add(sub);
    }
    var table = ConvertListToDataTable<Subject>(lstSubjects);
}
Listing 3-6

Create the List<Subject> and the DataTable

When you hover over the table variable, you will see that the DataTip displays a magnifying glass icon as seen in Figure 3-26.

A snapshot of the magnifying glass icon which is displayed when you hover over the table variable.

Figure 3-26

The table variable DataTip

If you click the magnifying glass icon, you will see the contents of the table variable displayed in a nice graphical way as seen in Figure 3-27.

A snapshot of the contents of the table variable displayed when clicked on the magnifying glass.

Figure 3-27

DataTable Visualizer

The magnifying glass icon tells us that one or more visualizers are available for the variable, in this example, the DataTable Visualizer.

Bonus Tip

If you are feeling adventurous, pin the DataTip that is displayed when hovering over the table variable, and right-click the pinned DataTip. You can now copy the value, copy the expression, add a new expression, and remove the expression previously added. Go ahead and add the expression in Listing 3-7.
table.Rows.Count
Listing 3-7

Add a DataTip Expression

This is great if you forgot to add a variable watch or just want to see some additional info regarding the variable in the DataTip.

Using the Watch Window

The Watch window allows us to keep track of the value of one or more variables and also allows us to see how these variable values change as one steps through the code.

You can easily add a variable to the Watch window by right-clicking the variable and selecting Add Watch from the context menu. Doing this with the table variable in the previous section will add it to the Watch 1 window as illustrated in Figure 3-28.

A snapshot of the watch window. It records the name, value, and type of the variable in a table format. The variable named, table is selected.

Figure 3-28

The Watch 1 window

Here, you can open the visualizer by clicking the magnifying glass icon or expanding the table variable to view the other properties of the object. I use the Watch window often as it is a convenient way to keep track of several variables at once.

The DebuggerDisplay Attribute

In the previous section, we discussed how to add a variable to the Watch window in Visual Studio. We saw that we can view the value of a variable or variables easily from this single window.

Using the same code we wrote in the previous section, add the variable called lstSubjects to the Watch window, and expand the variable. You will see the values of the lstSubjects variable listed as {VisualStudioDebugging.Subject} in the Value column as seen in Figure 3-29.

A snapshot of the watch window. It records the name, value, and type of the variable in a table format. The variable named, l s t subjects are selected.

Figure 3-29

The lstSubjects variable values

To view the values of each item in the list, we need to expand the list item (Figure 3-30) and inspect the values.

A snapshot of the watch window after adding the Debugger Display attribute. It records the name, value, and type of the l s t variable in a table format.

Figure 3-30

View list item values

This will quickly become rather tedious, especially when you are dealing with a rather large list, and you are looking for a specific value.

This is where the DebuggerDisplay attribute comes into play. We are going to modify the Subject class.

Ensure that you add the statement using System.Diagnostics to your code file.

Modify your Subject class as in Listing 3-8.
[DebuggerDisplay("Code: {SubjectCode, nq}, Subject: {SubjectDescription, nq}")]
public class Subject
{
    public int SubjectCode { get; set; }
    public string SubjectDescription { get; set; }
}
Listing 3-8

Modified Subject Class

Start debugging your code again and have a look at your Watch window after adding the DebuggerDisplay attribute. Your item values are more readable as seen in Figure 3-31.

A snapshot of the watch window after adding the debugger display attribute, the variable name l s t subjects is selected and its value is exhibited as count equal to 6.

Figure 3-31

The lstSubjects variable values with DebuggerDisplay

The use of “nq” in the DebuggerDisplay attribute will remove the quotes when the final value is displayed. The “nq” means “no quotes.”

Evaluate Functions Without Side Effects

While debugging an application, we probably do not want the state of the application to change because of an expression we are evaluating. It is, unfortunately, a fact that evaluating some expressions might cause side effects.

To illustrate this, we will need to write some more code. We will be creating a class called Student that contains a List of Subject as seen in Listing 3-9.
public class Student
{
    private List<Subject> _subjectList;
    public Student() { }
    public Student(List<Subject> subjects) => _subjectList = subjects;
    public bool HasSubjects() => _subjectList != null;
    public List<Subject> StudentSubjects
    {
        get
        {
            if (_subjectList == null)
            {
                _subjectList = new List<Subject>();
            }
            return _subjectList;
        }
    }
}
Listing 3-9

The Student Class

In this class, we have a HasSubjects() method that simply returns a Boolean indicating if the Student class contains a list of subjects. We also have a property called StudentSubjects that returns the list of subjects. If the list of subjects is null, it creates a new instance of List<Subject>.

It is here that the side effect is caused. If the HasSubjects() method returns false, calling the StudentSubjects property will change the value of HasSubjects().

This is better illustrated in the following screenshots. Create a new instance of Student and place a breakpoint right after that line of code (Figure 3-32).

A snapshot of a set of programming codes where a new instance is created for Student and a breakpoint is placed right after Student.

Figure 3-32

Place a breakpoint after Student

If we now use the Watch window to look at the value returned by the HasSubjects() method, we will see that it returns false (Figure 3-33).

A snapshot of the watch window. It records the name, value, and type of the variable in a table format. It displays that the value returned by the student dot Has Subject open and closed parenthesis method, is false and type bool.

Figure 3-33

HasSubjects() method returns false

When we call the StudentSubjects property, we see this side effect come into play in Figure 3-34. As soon as this property is called, the value of the HasSubjects() method changes.

This means that the state of our Student class has changed because of an expression that we ran in the Watch window.

This can cause all sorts of issues further down the debugging path, and sometimes the change might be so subtle that you don’t even notice it. You could end up chasing “bugs” that never really were bugs to begin with.

A snapshot of the watch window, with the Student subjects, value, and type. The value of Has Subjects open and close parenthesis method has changed due to a change in Student class.

Figure 3-34

HasSubjects() method value has changed

To prevent any side effects from an expression, just add nse to the end of the expression as seen in Figure 3-35.

A snapshot of the watch window. It records the name, value, and type of the variable in a table format, n s e is added to the end of the expression to prevent any side effects from an expression.

Figure 3-35

Adding nse to the expression to evaluate

This time, the value of the HasSubjects() method remains the same, which means that the state of your class remains unchanged. As you have probably guessed by now, the nse added after the expression stands for “No Side Effects.”

Format Specifiers

Format specifiers allow you to control the format in which a value is displayed in the Watch window. Format specifiers can also be used in the Immediate and Command window. Using a format specifier is as easy as entering the variable expression and typing a comma followed by the format specifier you want to use. The following are the C# format specifiers for the Visual Studio debugger.

ac

Force evaluation of an expression decimal integer

d

Decimal integer

dynamic

Displays the specified object using a Dynamic View

h

Hexadecimal integer

nq

String with no quotes

nse

Evaluates expressions without side effects where “nse” means “No Side Effects”

hidden

Displays all public and nonpublic members

raw

Displays item as it appears in the raw node. Valid on proxy objects only

results

Used with a variable that implements IEnumerable or IEnumerable<T>. Displays only members that contain the query result

You will recall that we used the “nq” format specifier with the DebuggerDisplay attribute discussed in a previous section.

Diagnostic Tools

Visual Studio gives developers access to performance measurement and profiling tools. The performance of your application should, therefore, be high on your priority list. An application that suffers from significant performance issues is as good as broken (especially from an end user’s perspective).

Visual Studio Diagnostic Tools might be enabled by default. If not, enable Diagnostic Tools by going to the Tools menu and clicking Options, Debugging, and then General. Ensure that Enable Diagnostic Tools while debugging is checked as seen in Figure 3-36.

A snapshot of a window. It displays the options menu wherein debugging is expanded and the first value General is selected. On expanding General, enable diagnostic tools while debugging is highlighted.

Figure 3-36

Enable Diagnostic Tools

This will ensure that the Diagnostic Tools window opens automatically when you start debugging. When we start debugging our ShipmentLocator Windows Forms application, the Diagnostic Tools window will be displayed as seen in Figure 3-37.

A snapshot of diagnostic tools. It depicts the values for diagnostics sessions, events, process memory, and C P U.

Figure 3-37

Diagnostic Tools

With our Windows Forms application, you can use Diagnostic Tools to monitor memory or CPU usage as seen in Figure 3-38.

A snapshot depicts the window of debugging tools. Select Tools option is selected and the following sub options are expanded. 1. C P U usage, 2. Memory usage, 3. Settings.

Figure 3-38

Select what to analyze

As you debug your application, you can see the CPU usage, memory usage, and other performance-related information.

CPU Usage

A great place to start your performance analysis is the CPU Usage tab. Place two breakpoints in your Form1_Load at the start and end of the function (Figure 3-39) and start debugging.

A snapshot depicts a set of programming codes. Lines 14 and 18 are highlighted.

Figure 3-39

Setting breakpoints to analyze CPU usage

When the debugger reaches the second breakpoint, you will be able to view the summary profiling data for the debug session (as seen in Figure 3-40) at that point. To view detailed profiling data for the region of code you analyzed, click the Open details link under the CPU Usage tab.

A snapshot depicts the results window after debugging. Record C P U Profile option is selected. A pie chart on the right depicts the top five categories. The window depicts the results based on top insights, top functions, and hot path.

Figure 3-40

CPU usage analysis results

As seen in Figure 3-41, you can view the functions by changing the Current View to Functions in the drop-down.

A snapshot depicts a window. C P U usage option is selected. Current view is filtered by functions. The functions along with their names and the total C P U percent are listed below.

Figure 3-41

The Current View filtered by functions

If any of the functions in the CPU Usage pane seem to be problematic, double-click the function and then change the Current View to Caller/Callee to view a more detailed three-pane view of the analysis. As seen in Figure 3-42, the left pane will contain the calling function, the middle will contain the selected function, and any called functions will be displayed in the right pane.

A snapshot depicts the window after debugging. The C P U usage tab is selected and the following are displayed in a three pane view. 1. Calling functions. 2. Current functions. 3. Called functions.

Figure 3-42

Butterfly view of BtnLogin_Click

In the Current Function pane, you will see the Function Body section which details the time spent in the function body. As seen in Figure 3-43, you will also see the code represented for this function.

A snapshot depicts the programming code which executes the login function validation. Line 19 is selected and it executes a function inside an if statement.

Figure 3-43

The code for the function

Because this excludes the calling and called functions, you get a better understanding of the function you are evaluating and can determine if it is the performance bottleneck or not.

Memory Usage

Visual Studio Diagnostic Tools allows developers to see what the change in memory usage is. This is done by taking snapshots. When you start debugging, place a breakpoint on a method you suspect is causing a memory issue. Then you step over the method and place another breakpoint. An increase is indicated with a red up arrow as seen in Figure 3-44.

This is often the best way to analyze memory issues. Two snapshots will give you a nice diff and allow you to see exactly what has changed.

A snapshot depicts the window of memory usage. Take snapshot is selected and the attribute values are displayed in the table with the following headers: 1. I D. 2. Time. 3. Objects. 4. Heap size.

Figure 3-44

Memory usage snapshots

You can also compare two snapshots by clicking one of the links in the memory usage snapshots (Figure 3-44) and viewing the comparison in the snapshot window that opens (Figure 3-45). By selecting a snapshot in the Compare to drop-down list, you can see what has changed.

A snapshot of the detailed description of a selected object. It lists the content in a table with 7 columns labeled, object type, count difference, size difference, inclusive size difference, count, size in bytes, and inclusive size in bytes.

Figure 3-45

Comparing snapshots

The Events View

As you step through your application, the Events view will show you the different events that happen during your debug session. This can be setting a breakpoint or stepping through code. It also shows you the duration of the event as seen in Figure 3-46.

A snapshot of the table view of the events tab. The memory usage tab is selected. It displays the content in four columns labeled, event, time, duration, and thread.

Figure 3-46

The Events view

This means that as you step through your code, the Events tab will display the time the code took to run from the previous step operation to the next. You can also see the same events displayed as PerfTips in the Visual Studio code editor as seen in Figure 3-47.

A snapshot of a programming code in which the link to open the diagnostic tools window is indicated. Lines 25 and 28 are highlighted.

Figure 3-47

PerfTips in the code editor

IntelliTrace events are available in this tab if you have Visual Studio Enterprise.

For a comparison of the Visual Studio 2022 Editions, head on over to https://visualstudio.microsoft.com/vs/compare/ and see what each edition has to offer.

PerfTips allows developers to quickly identify potential issues in your code.

The Right Tool for the Right Project Type

Table 3-1 shows which tools Visual Studio offers and the project types that can make use of these tools.
Table 3-1

Performance Tools for Project Types

Performance Tool

Windows Desktop

UWP

ASP.NET/ASP.NET Core

CPU Usage

Yes

Yes

Yes

Memory Usage

Yes

Yes

Yes

GPU Usage

Yes

Yes

No

Application Timeline

Yes (XAML)

Yes

No

PerfTips

Yes

Yes

Yes

Performance Explorer

No

No

No

IntelliTrace

.NET with VS Enterprise only

.NET with VS Enterprise only

.NET with VS Enterprise only

Events viewer

Yes

Yes

Yes

.NET Async

Yes (.NET only)

Yes

Yes

.NET Counters

Yes (.NET Core only)

No

Yes (ASP.NET Core only)

Database

Yes (.NET Core only)

No

Yes (ASP.NET Core only)

.NET Object Allocation

Yes (.NET only)

Yes

Yes

Immediate Window

The Immediate Window in Visual Studio allows you to debug and evaluate expressions, execute statements, and print the values of variables. If you don’t see the Immediate Window, go to the Debug menu, and select Windows, and click Immediate or hold down Ctrl+D, Ctrl+I.

Let’s place a breakpoint in one of our for loops as seen in Figure 3-48.

A snapshot of a programming code. It executes the conditioning loop. A breakpoint is placed in 16 and 22 and they are highlighted.

Figure 3-48

Breakpoint hit

Opening up the Immediate Window and typing in sub.SubjectDescription will display its value as seen in Figure 3-49.

You can also use ? sub.SubjectDescription to view the value of the variable.

A snapshot of a window titled, immediate window. Line 1. Sub dot subject description. Line 2. Subject dot 0.

Figure 3-49

Immediate Window

If you had entered sub.SubjectDescription = "Math", you would be updating the value from “Subject-0” to “Math” as seen in Figure 3-50.

A snapshot of a programming code. It executes the conditioning loops. The functional line is highlighted and the attributes are listed below.

Figure 3-50

Variable value changed

You can also execute a function at design time (i.e., while not debugging) using the Immediate Window. Add the code in Listing 3-10 to your project.
static string DisplayMessage()
{
    return "Hello World";
}
Listing 3-10

DisplayMessage Function

In the Immediate Window, type ?DisplayMessage() and press Enter. The Immediate Window will run the function and return the result as seen in Figure 3-51.

A snapshot of a window titled, immediate window. It displays 2 lines of code. Line 1. Question mark display message open and close bracket. Line 2. Hello world in double quotes.

Figure 3-51

Execute the DisplayMessage function

Any breakpoints contained in the function will break the execution at the breakpoint. Use the debugger to examine the program state.

Attaching to a Running Process

Attaching to a process allows the Visual Studio Debugger to attach to a running process on the local machine or a remote computer. With the process you want to debug already running, select Debug and click Attach to Process as seen in Figure 3-52. You can also hold down Ctrl+Alt+P to open the Attach to Process window.

A snapshot of a menu of Debug. Attach to process option is selected from the menu.

Figure 3-52

Attach to Process

The Attach to Process window is then displayed (Figure 3-53). The connection type must be set to Default, and the connection target must be set to your local machine name.

A snapshot of an action window tilted, attach to process. It records the following contents, 1. connection type. 2. Connection target. 3. Available processes.

Figure 3-53

Attach to Process

The available processes list allows you to select the process you want to attach to. You can quickly find the process you want by typing the name in the filter process text box.

You can, for example, attach to the w3wp.exe process to debug a web application running on IIS. To debug a C#, VB.NET, or C++ application on the local machine, you can use the Attach to Process by selecting the <appname>.exe from the available processes list (where <appname> is the name of your application).

Attach to a Remote Process

To debug a process running on a remote computer, select Debug and click Attach to Process menu, or hold down Ctrl+Alt+P to open the Attach to Process window. This time, select the remote computer name in the Connection target by selecting it from the drop-down list or typing the name in the connection target text box and pressing Enter.

If you are unable to connect to the remote computer using the computer name, use the IP and port address.

Remote Debugger Port Assignments

The port assignments for the Visual Studio Remote Debugger are as follows:
  • Visual Studio 2022: 4026

  • Visual Studio 2019: 4024

  • Visual Studio 2017: 4022

  • Visual Studio 2015: 4020

  • Visual Studio 2013: 4018

  • Visual Studio 2012: 4016

The port assigned to the Remote Debugger is incremented by two for each release of Visual Studio.

Reattaching to a Process

Starting with Visual Studio 2017, you can quickly reattach to a process you previously attached to. To do this, you can click the Debug menu and select Reattach to Process or hold down Shift+Alt+P. The debugger will try to attach to the last process you attached by matching the previous process ID to the list of running processes. If that fails, it tries to attach to a process by matching the name. If neither is successful, the Attach to Process window is displayed and lets you select the correct process. The option to reattach to a process you previously attached will only be available if you had previously attached to it.

Remote Debugging

Sometimes, you need to debug an application that has already been deployed to a different computer. Visual Studio allows you to do this via remote debugging. To start, you need to download and install remote tools for Visual Studio 2022 on the remote computer.

Remote tools for Visual Studio 2022 enables app deployment, remote debugging, testing, profiling, and unit testing on computers that don’t have Visual Studio 2022 installed.

System Requirements

The supported operating systems for the remote computer must be one of the following:
  • Windows 11

  • Windows 10 (not phone)

  • Windows 8 or 8.1 (not phone)

  • Windows 7 SP 1

  • Windows Server 2016

  • Windows Server 2012 or Windows Server 2012 R2

  • Windows Server 2008 SP 2, Windows Server 2008 R2 Service Pack 1

The supported hardware configurations to enable remote debugging are detailed in the following list:
  • 1.6 GHz or faster processor

  • 1 GB of RAM (1.5 GB if running on a VM)

  • 1 GB of available hard disk space

  • 5400 RPM hard drive

  • DirectX 9-capable video card running at 1024 x 768 or higher display resolution

The remote computer and your local machine (the machine containing Visual Studio) must both be connected over a network, workgroup, or homegroup. The two machines can also be connected directly via an Ethernet cable.

Take note that trying to debug two computers connected through a proxy is not supported.

It is also not recommended to debug via a dial-up connection (do those still exist?) or over the Internet across geographical locations. The high latency or low bandwidth will make debugging unacceptably slow.

Download and Install Remote Tools

Connect to the remote machine and download and install the correct version of the remote tools required for your version of Visual Studio. The link to download the remote tools compatible with all versions of Visual Studio 2022 is https://visualstudio.microsoft.com/downloads#remote-tools-for-visual-studio-2022.

If you are using Visual Studio 2017, for example, download the latest update of remote tools for Visual Studio 2017.

Also, be sure to download the remote tools with the same architecture as the remote computer. This means that even if your app is a 32-bit application, and your remote computer is running a 64-bit operating system, download the 64-bit version of the remote tools.

Install the remote tools and click Install after agreeing to the license terms and conditions (Figure 3-54).

A snapshot of an installation window titled, Remote Tools for Visual Studio 2022. A checkbox labeled, I agree to the license terms and conditions, is placed below. Options, install, and close, buttons are placed below.

Figure 3-54

Remote tools for Visual Studio 2022

Running Remote Tools

After the installation has been completed on the remote machine, run the Remote Debugger application as Administrator if you can. To do this, right-click the Remote Debugger app, and click Run as Administrator.

At this point, you might be presented with a Remote Debugging Configuration dialog box as seen in Figure 3-55.

A snapshot depicts a dialog box titled. Remote Debugging Configuration. It noticed a warning icon and indicates two information icons to configure remote debugging.

Figure 3-55

Remote Debugging Configuration

If you encounter this window, it possibly means that there is a configuration issue that you need to resolve. The Remote Debugging Configuration dialog box will prompt you to correct configuration errors it picks up. Do this by clicking the Configure remote debugging button.

You will then see the Remote Debugger window as seen in Figure 3-56.

A snapshot of a window titled, Visual studio 2022 Remote Debugger. The following headers are displayed: 1. Date and Time. 2. Description. The corresponding values are selected.

Figure 3-56

Visual Studio 2022 Remote Debugger

You are now ready to start remote debugging your application.

Start Remote Debugging

The great thing about the Remote Debugger on the remote computer is that it tells you the server name to connect to. In Figure 3-56, you can see that the server is named 20F4B56E-AB26-4:4026 where 4026 is the port assignment for Visual Studio 2022. Make a note of this server name and port number.

In your application, set a breakpoint somewhere in the code such as in a button click event handler. Now right-click the project in the Solution Explorer and click Properties. The project properties page opens as seen in Figure 3-57.

A snapshot of a properties window. From the left pane, the option debug is selected. Configuration, active. Platform, active. Start action, start options, and debug details are given below.

Figure 3-57

Project properties page

Now, perform the following steps to remotely debug your application:
  1. 1.

    Click the Debug tab, check the Use remote machine checkbox, and enter the remote machine name and port noted earlier. In our example, this is 20F4B56E-AB26-4:4026.

     
  2. 2.

    Make sure that you leave the Working directory text box empty and do not check Enable native code debugging.

     
  3. 3.

    When all this is done, save the properties and build your project.

     
  4. 4.

    You now need to create a folder on the remote computer that is the same path as the Debug folder on your local machine (the Visual Studio machine). For example, the path to the project Debug folder on my local machine is <source path> ShipmentLocatorAppVisualStudioRemoteDebuginDebug. Create this same path on the remote machine.

     
  5. 5.

    Copy the executable that was just created by the build you performed in step 3 to the newly created Debug folder on the remote computer.

    Be aware that any changes to your code or rebuilds to your project will require you to repeat step 5.

     
  6. 6.

    Ensure that the Remote Debugger is running on the remote computer. The description (Figure 3-56) should state that it is waiting for new connections.

     
  7. 7.

    On your local machine, start debugging your application, and if prompted, enter the credentials for the remote machine to log on. Once logged on, you will see that the Remote Debugger on the remote computer displays that the remote debug session is now active (Figure 3-58). A point to note here is that if you trust the network that you are debugging across, and you are having problems logging on, you can specify that no authentication is done. In the project properties, change the Authentication mode from Windows Authentication to No Authentication (Figure 3-57). Then, on the remote machine, click the Remote Debugger and click the Tools menu, and select Options. Here, you can specify that no authentication is done and that any user can debug.

     

A snapshot of a window titled, Visual Studio 2022 Remote Debugger. The date and time, description values are listed. The first entry is selected.

Figure 3-58

Remote debug session connected

  1. 8.

    After a few seconds, you will see your application’s main window displayed on the remote machine (Figure 3-59). Yep, breakfast is the most important meal of the day.

     

A snapshot of a window tilted, Remote debugging. It displays Egg Timer as 3.00. The following buttons are present. 1. Start. 2. Stop.

Figure 3-59

Application main screen

  1. 9.

    On the remote machine, take whatever action is needed to hit the breakpoint you set earlier. I simply set a breakpoint behind the Start button click event handler. When you hit the breakpoint, it will be hit on your local machine (Visual Studio machine).

     

If you need any project resources to debug your application, you will have to include these in your project. The easiest way is to create a project folder in Visual Studio and then add the files to that folder. For each resource you add to the folder, ensure that you set the Copy to Output Directory property to Copy always.

Summary

In this chapter, we saw that Visual Studio provides a rich set of debugging tools for developers. Breakpoints provide a lot of flexibility when you need to pause your application and inspect the state of variables and other objects. Remote debugging allows developers to inspect the state of the application when running on a machine that is not under their direct control. The Diagnostic Tools also allow developers to inspect CPU and memory usage for their applications and help identify bottlenecks. In the next chapter, we will take a closer look at unit testing and how to create and run unit tests, using IntelliTest, and how to measure code coverage in Visual Studio.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.37.56