© Dirk Strauss 2020
D. StraussGetting Started with Visual Studio 2019https://doi.org/10.1007/978-1-4842-5449-3_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.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig1_HTML.jpg
Figure 3-1

Setting a breakpoint

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

Yes

No

PerfTips

Yes

Yes for XAML

Yes

Performance Explorer

Yes

No

Yes

IntelliTrace

VS Enterprise only

VS Enterprise only

VS Enterprise only

Network Usage

No

Yes

No

HTML UI Responsiveness

No

Yes for HTML

No

JavaScript Memory

No

Yes for HTML

No

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.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig2_HTML.jpg
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.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig3_HTML.jpg
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.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig4_HTML.jpg
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.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig5_HTML.jpg
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.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig6_HTML.jpg
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 in a similar fashion to Run to Click. The difference being 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.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig7_HTML.jpg
Figure 3-7

Run to Cursor

Doing this will start the debugger and set a temporary breakpoint on the line you right-clicked on. 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.

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-8.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig8_HTML.jpg
Figure 3-8

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-9.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig9_HTML.jpg
Figure 3-9

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-10.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig10_HTML.jpg
Figure 3-10

Hit Count Condition

The last condition you can set on a conditional breakpoint is a Filter as seen in Figure 3-11.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig11_HTML.jpg
Figure 3-11

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-8. 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-12.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig12_HTML.jpg
Figure 3-12

Breakpoint Action

When you run your application, you will see the expression output in the Output Window as seen in Figure 3-13.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig13_HTML.jpg
Figure 3-13

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-14.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig14_HTML.jpg
Figure 3-14

The Breakpoint Action

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

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-15.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig15_HTML.jpg
Figure 3-15

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-16.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig16_HTML.jpg
Figure 3-16

Breakpoints window

Compare the line numbers of the breakpoints listed in Figure 3-16 with the breakpoints displayed in Figure 3-15. 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-17.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig17_HTML.jpg
Figure 3-17

Edit Breakpoint Labels

The Edit breakpoint labels window is then displayed as seen in Figure 3-18.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig18_HTML.jpg
Figure 3-18

Add a new breakpoint label

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, making the identification and management of your breakpoints much easier.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig19_HTML.jpg
Figure 3-19

Breakpoints window with Labels Set

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-20.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig20_HTML.jpg
Figure 3-20

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 are able to 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-21.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig21_HTML.jpg
Figure 3-21

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 are able to add a comment to your DataTip as seen in Figure 3-22.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig22_HTML.jpg
Figure 3-22

DataTip Comment

DataTips also allow you to edit the value of the variable, as long as the value isn’t a read-only value. 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 allows 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 off 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-23.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig23_HTML.jpg
Figure 3-23

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-24.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig24_HTML.jpg
Figure 3-24

DataTable Visualizer

The magnifying glass icon tells us that one or more visualizers are available for the particular 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 following 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-25.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig25_HTML.jpg
Figure 3-25

The Watch 1 window

Here you can open the visualizer by clicking the magnifying glass icon or expand the table variable to view the other properties of the object. I use the Watch window often as it is a really 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-26.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig26_HTML.jpg
Figure 3-26

The lstSubjects Variable Values

To view the values of each item in the list, we need to expand the list item (Figure 3-27) and inspect the values.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig27_HTML.jpg
Figure 3-27

View list items 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-28.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig28_HTML.jpg
Figure 3-28

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-29).
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig29_HTML.jpg
Figure 3-29

Place 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-30).
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig30_HTML.jpg
Figure 3-30

HasSubjects() method returns false

When we call the StudentSubjects property, we see this side effect come into play in Figure 3-31. 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.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig31_HTML.jpg
Figure 3-31

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-32.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig32_HTML.jpg
Figure 3-32

Adding nse to 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-33.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig33_HTML.jpg
Figure 3-33

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-34.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig34_HTML.jpg
Figure 3-34

Diagnostic Tools

With our Windows Forms application, you can use Diagnostic Tools to monitor memory or CPU usage as seen in Figure 3-35.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig35_HTML.jpg
Figure 3-35

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-36), and start debugging.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig36_HTML.jpg
Figure 3-36

Setting breakpoints to analyze CPU Usage

When the debugger reaches the second breakpoint, you will have detailed profiling data for the region of code you analyzed as seen in Figure 3-37.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig37_HTML.jpg
Figure 3-37

CPU Usage Analysis Results

If any of the functions in the CPU Usage pane seem to be problematic, double-click the function to view a more detailed three-pane view of the analysis. As seen in Figure 3-38, 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.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig38_HTML.jpg
Figure 3-38

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. 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-39.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig39_HTML.jpg
Figure 3-39

Memory Usage Snapshots

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.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig40_HTML.jpg
Figure 3-40

Comparing Snapshots

You can also compare two snapshots by clicking one of the links in the Memory Usage snapshots (Figure 3-39) and viewing the comparison in the snapshot window that opens up (Figure 3-40). By selecting a snapshot in the Compare to drop-down list, you are able to see what has changed.

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-41.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig41_HTML.jpg
Figure 3-41

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-42.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig42_HTML.jpg
Figure 3-42

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 2019 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

The following table shows which tool Visual Studio offers and the project types that can make use of these tools.

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-43.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig43_HTML.jpg
Figure 3-43

Breakpoint hit

Opening up the Immediate Window and typing in sub.SubjectDescription will display its value as seen in Figure 3-44. You can also use ? sub.SubjectDescription to view the value of the variable.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig44_HTML.jpg
Figure 3-44

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-45.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig45_HTML.jpg
Figure 3-45

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-46.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig46_HTML.jpg
Figure 3-46

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-47. You can also hold down Ctrl+Alt+P to open the Attach to Process window.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig47_HTML.jpg
Figure 3-47

Attach to Process

The Attach to Process window is then displayed (Figure 3-48). The connection type must be set to Default, and the connection target must be set to your local machine name.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig48_HTML.jpg
Figure 3-48

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 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 2 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 to my 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.

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 2019 on the remote computer.

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

System Requirements

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

  • Windows 8 or 8.1

  • 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 2019 is https://visualstudio.microsoft.com/downloads#remote-tools-for-visual-studio-2019.

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-49).
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig49_HTML.jpg
Figure 3-49

Remote tools for Visual Studio 2019

Running Remote Tools

After the installation has completed on the remote machine, run the Remote Tools 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. I did not encounter this window, but if you do, 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.

When the configuration issues have been resolved, click the Configure remote debugging button in the dialog box to open the Remote Debugger window as seen in Figure 3-50.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig50_HTML.jpg
Figure 3-50

Visual Studio 2019 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-50, you can see that the server is named DESKTOP-H1MDEFE:4024 where 4024 is the port assignment for Visual Studio 2019. 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 up as seen in Figure 3-51.
../images/487681_1_En_3_Chapter/487681_1_En_3_Fig51_HTML.jpg
Figure 3-51

Project properties page

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

    Click the Debug tab, and check the Use remote machine checkbox, and enter the remote machine name and port noted earlier. In our example, this is DESKTOP-H1MDEFE:4024.

     
  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 exactly 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 exact 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 should state that it is waiting for new connections.

     
  7. 7.
    On your local machine, start debugging your application and if prompted to 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-52).
    ../images/487681_1_En_3_Chapter/487681_1_En_3_Fig52_HTML.jpg
    Figure 3-52

    Remote Debug Session Connected

     
  8. 8.
    After a few seconds, you will see your application’s main window displayed on the remote machine (Figure 3-53). Yep, breakfast is the most important meal of the day.
    ../images/487681_1_En_3_Chapter/487681_1_En_3_Fig53_HTML.jpg
    Figure 3-53

    Application main screen

     
  9. 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.

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

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