1070 lines
39 KiB
Markdown
1070 lines
39 KiB
Markdown
|
## Implement the Visual Studio Code debugging tools for C#
|
||
|
|
||
|
### Introduction
|
||
|
|
||
|
The faster you discover and identify bugs, the faster you can get your code
|
||
|
stabilized and released. Visual Studio Code supports code debugging for C# and
|
||
|
most other software development languages through the use of Extensions. Once
|
||
|
you've learned to use Visual Studio Code's debug tools, you'll spend less time
|
||
|
wondering why your code stopped working and more time developing great
|
||
|
applications.
|
||
|
|
||
|
Suppose you're using Visual Studio Code to develop a C# console application.
|
||
|
The primary purpose of the application is to process customer data based on
|
||
|
business rules. You develop the application using a small sample data set and
|
||
|
it runs without errors. However, when you run the code using the larger data
|
||
|
set, your code produces some unexpected results. You've read through the code
|
||
|
several times but it's difficult to find the errors in your logic. You've heard
|
||
|
that Visual Studio Code has good debugger tools, but you've never had to use
|
||
|
them. You can't waste any more time reading through the code. You decide that
|
||
|
learning the debugger tools is your best chance for completing the project on
|
||
|
time.
|
||
|
|
||
|
In this module, you learn how to effectively debug C# programs in Visual Studio
|
||
|
Code using breakpoints and other debugging tools, such as resources in the RUN
|
||
|
AND DEBUG view.
|
||
|
|
||
|
By the end of this module, you'll be able to configure and use the Visual Studio
|
||
|
Code debugger tools for C#.
|
||
|
|
||
|
#### Learning objectives
|
||
|
|
||
|
In this module, you will:
|
||
|
|
||
|
- Configure the Visual Studio Code debugger for a C# program.
|
||
|
- Create breakpoints and step through your code to isolate issues.
|
||
|
- Inspect your program state at any execution step.
|
||
|
- Use the call stack to find the source of an exception.
|
||
|
|
||
|
Ultimately, you'll be able to isolate code bugs efficiently using the debugger
|
||
|
tools, and you won't need to rely on `Console.WriteLine` anymore.
|
||
|
|
||
|
---
|
||
|
|
||
|
## Examine the Visual Studio Code debugger interface
|
||
|
|
||
|
The Visual Studio Code user interface provides several ways to configure debug
|
||
|
options and launch debug sessions.
|
||
|
|
||
|
### Debug features in the Visual Studio Code user interface
|
||
|
|
||
|
Visual Studio Code includes several user interface features that will help you
|
||
|
to configure, start, and manage debug sessions:
|
||
|
|
||
|
- Configure and launch the debugger: The Run menu and RUN AND DEBUG view can
|
||
|
both be used to configure and launch debug sessions.
|
||
|
- Examine application state: The RUN AND DEBUG view includes a robust interface
|
||
|
that exposes various aspects of your application state during a debug session.
|
||
|
- Runtime execution control: The Debug toolbar provides high-level runtime
|
||
|
controls during code execution.
|
||
|
|
||
|
> Note
|
||
|
> This Unit introduces you to a lot of debugging tools and terminology. Please
|
||
|
keep in mind that this is your first look at these tools, not your last. You'll
|
||
|
have an opportunity to complete hands-on activities with most of these tools
|
||
|
during this module. Try not to feel overwhelmed by the volume of information
|
||
|
that's presented.
|
||
|
|
||
|
---
|
||
|
|
||
|
### Run menu options
|
||
|
|
||
|
The Visual Studio Code Run menu provides easy access to some common run and
|
||
|
debug commands.
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debuger.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
The Run menu provides menu options that are grouped into six sections.
|
||
|
|
||
|
1. Start and stop applications. This section of the menu includes options for
|
||
|
starting and stopping code execution, with and without the debugger attached.
|
||
|
|
||
|
2. Launch configurations. This section of the menu provides access to examine
|
||
|
or create launch configurations.
|
||
|
|
||
|
3. Runtime control. This section of the menu enables the developer to control
|
||
|
how they want to advance through the code. Controls are enabled when execution
|
||
|
has paused during a debug session.
|
||
|
|
||
|
4. Set Breakpoints. This section of the menu enables the developer to set
|
||
|
breakpoints on code lines. Code execution pauses on Breakpoints during a debug
|
||
|
session.
|
||
|
|
||
|
5. Manage Breakpoints. This section of the menu enables the developer to manage
|
||
|
breakpoints in bulk rather than individually.
|
||
|
|
||
|
6. Install Debuggers. This section of the menu opens the Visual Studio Code
|
||
|
EXTENSIONS view filtered for code debuggers.
|
||
|
|
||
|
### Run and Debug view user interface
|
||
|
|
||
|
The RUN AND DEBUG view provides access to runtime tools that can be invaluable
|
||
|
during the debug process.
|
||
|
|
||
|
1. Run and Debug controls panel. Used to configure and start a debug session.
|
||
|
|
||
|
2. VARIABLES section. Used to view and manage variable state during a debug
|
||
|
session.
|
||
|
|
||
|
3. WATCH section. Used to monitor variables or expressions. For example, you
|
||
|
could configure an expression using one or more variables and watch it to see
|
||
|
when a particular condition is met.
|
||
|
|
||
|
4. CALL STACK section. Used to keep track of the current point of execution
|
||
|
within the running application, starting with the initial point of entry into
|
||
|
the application. The call stack shows which method is currently being executed,
|
||
|
as well as the method or methods in the execution path that led to the current
|
||
|
point of execution (current line of code).
|
||
|
|
||
|
5. BREAKPOINTS section. Displays the current breakpoint settings.
|
||
|
|
||
|
6. Debug toolbar. Used to control code execution during the debug process. This
|
||
|
toolbar is only displayed while the application is running.
|
||
|
|
||
|
7. Current execution step. Used to identify the current execution step by
|
||
|
highlighting it in the Editor. In this case, the current execution step is a
|
||
|
breakpoint (breakpoints are marked with a red dot to the left of the line
|
||
|
number).
|
||
|
|
||
|
8. DEBUG CONSOLE. Used to display messages from the debugger. The DEBUG CONSOLE
|
||
|
panel is the default console for console applications and is able to display
|
||
|
output from `Console.WriteLine()` and related `Console` output methods.
|
||
|
|
||
|
### Controls panel for the Run and Debug view
|
||
|
|
||
|
At the top of the RUN AND DEBUG view, you can find the launch controls:
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debug_panel.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
1. Start debugging. This button (a green arrow) is used to start a debug
|
||
|
session.
|
||
|
|
||
|
2. Launch configurations. This dropdown menu provides access to launch
|
||
|
configurations. The selected option is displayed.
|
||
|
|
||
|
3. Open 'launch.json'. This button (a gear shape) can be used to open the
|
||
|
`launch.json` file, where you can edit the launch configuration if needed.
|
||
|
|
||
|
4. Views and More Actions. This button (an ellipsis) enables you to show/hide
|
||
|
sections of the debug panel as well as the DEBUG CONSOLE panel.
|
||
|
|
||
|
### Debug toolbar
|
||
|
|
||
|
The Debug toolbar provides execution controls while your application is running.
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debug_toolbar.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
1. Pause/Continue. This button can be used to pause execution when the code is
|
||
|
running and Continue when code execution has been paused.
|
||
|
|
||
|
2. Step Over. This button can be used to execute the next method as a single
|
||
|
command without inspecting or following its component steps.
|
||
|
|
||
|
3. Step Into. This button can be used to enter the next method or code line and
|
||
|
observe line-by-line execution steps.
|
||
|
|
||
|
4. Step Out. When inside a method, this button can be used to return to the
|
||
|
earlier execution context by completing all remaining lines of the current
|
||
|
method as though they were a single command.
|
||
|
|
||
|
5. Restart. This button can be used to terminate the current program execution
|
||
|
and start debugging again using the current configuration.
|
||
|
|
||
|
6. Stop. This button can be used to terminate the current program execution.
|
||
|
|
||
|
In addition to six execution controls, the Debug toolbar provides a "handle" on
|
||
|
the left side that enables the developer to reposition the toolbar, and a "More"
|
||
|
dropdown on the right side that enables the developer to disconnect the
|
||
|
debugger.
|
||
|
|
||
|
> Note
|
||
|
> You can use the setting `debug.toolBarLocation` to control the location of
|
||
|
the debug toolbar. It can be floating (the default), docked to the RUN AND
|
||
|
DEBUG view, or hidden. A floating debug toolbar can be dragged horizontally and
|
||
|
down to the Editor area.
|
||
|
|
||
|
### Recap
|
||
|
|
||
|
Here are a few important things to remember from this unit:
|
||
|
|
||
|
The Visual Studio Code user interface can be used to configure, start, and
|
||
|
manage debug sessions. The launch.json file contains the launch configurations
|
||
|
for your application.
|
||
|
|
||
|
- The Run menu provides easy access to common run and debug commands grouped
|
||
|
into six sections.
|
||
|
- The RUN AND DEBUG view provides access to runtime tools, including the Run
|
||
|
and Debug controls panel. The sections of the RUN AND DEBUG view are VARIABLES,
|
||
|
WATCH, CALL STACK, and BREAKPOINTS.
|
||
|
- The Debug toolbar provides execution controls while your application is
|
||
|
running such as pause/continue, step over, step into, step out, restart and
|
||
|
stop.
|
||
|
- The DEBUG CONSOLE is used to display messages from the debugger. The DEBUG
|
||
|
CONSOLE can also display console output from your application.
|
||
|
|
||
|
---
|
||
|
|
||
|
## Exercise - Run code in the debug environment
|
||
|
|
||
|
The Visual Studio Code user interface enables developers to run their code in a
|
||
|
debug environment. Support for debugging is provided by extensions, and for C#
|
||
|
developers, debugger support is provided by the same extension that provides
|
||
|
support for code development and IntelliSense.
|
||
|
|
||
|
### Debugger and application interaction
|
||
|
|
||
|
A code debugger can be used to pause and resume code execution, examine
|
||
|
variable state, and even change the values assigned to variables at runtime.
|
||
|
You may be wondering, how can the debugger control and modify a running
|
||
|
application? The short answer is, the debugger has access to the application's
|
||
|
runtime environment and executable code.
|
||
|
|
||
|
> Note
|
||
|
> Debugger interaction with the runtime environment is an advanced topic. In
|
||
|
addition, understanding how the debugger works behind the scenes isn't a
|
||
|
requirement for using the debugger. However, the following description may
|
||
|
satisfy your curiosity.
|
||
|
|
||
|
The Visual Studio Code debugger for C# uses the .NET Core runtime to launch and
|
||
|
interact with an application. When you start the debugger, it creates a new
|
||
|
instance of the runtime and runs the application within that instance. The
|
||
|
runtime includes an application programming interface (API), which the debugger
|
||
|
uses to attach to the running process (your application).
|
||
|
|
||
|
Once your application is running and the debugger is attached, the debugger
|
||
|
communicates with the running process using the .NET Core runtime's debugging
|
||
|
APIs and a standard debug protocol. The debugger can interact with the process
|
||
|
(the application running within the .NET runtime instance) by setting
|
||
|
breakpoints, stepping through code, and inspecting variables. Visual Studio
|
||
|
Code's debugger interface enables you to navigate the source code, view call
|
||
|
stacks, and evaluate expressions.
|
||
|
|
||
|
The most common way to specify a debug session is a launch configuration in the
|
||
|
launch.json file. This approach is the default option enabled by the debugger
|
||
|
tools. For example, if you create a C# console application and select Start
|
||
|
Debugging from the Run menu, the debugger uses this approach to launch, attach
|
||
|
to, and then interact with your application.
|
||
|
|
||
|
### Create a new code project
|
||
|
|
||
|
The first step in learning the debugger tools is creating a code project that
|
||
|
you can run in the debugger.
|
||
|
|
||
|
- Open a new instance of Visual Studio Code.
|
||
|
|
||
|
- On the File menu, select **Open Folder**.
|
||
|
|
||
|
- On the Open Folder dialog, navigate to your folder.
|
||
|
|
||
|
- On the Open Folder dialog, select New folder.
|
||
|
|
||
|
- Name the new folder **Debug101**, and then select **Select Folder**.
|
||
|
|
||
|
- On the **Terminal** menu, select **New Terminal**.
|
||
|
|
||
|
A .NET CLI command can be used to create a new console app.
|
||
|
|
||
|
- At the TERMINAL panel command prompt, enter the following command:
|
||
|
|
||
|
```pwsh
|
||
|
dotnet new console
|
||
|
```
|
||
|
|
||
|
- Close the TERMINAL panel.
|
||
|
|
||
|
### Examine launch configurations for debugging
|
||
|
|
||
|
Visual Studio Code uses a launch configuration file to specify the application
|
||
|
that runs in the debug environment.
|
||
|
|
||
|
If the Debug101 folder doesn't include a Debug101.sln file, select `Program.cs`,
|
||
|
and then verify that a .sln file is created.
|
||
|
|
||
|
Opening a C# code file prompts the environment to check for project files. The
|
||
|
.sln file is a solution file that is used by Visual Studio to manage projects
|
||
|
and is usually created automatically when you create a new project in Visual
|
||
|
Studio Code. The .sln file is used by the debugger to identify the project that
|
||
|
should be run in the debug environment.
|
||
|
|
||
|
On the View menu, select **Command Palette**.
|
||
|
|
||
|
At the command prompt, enter .net: g and then select **`.NET: Generate Assets for
|
||
|
Build and Debug`**.
|
||
|
|
||
|
Notice the new `.vscode` folder that has been added to your project folder.
|
||
|
|
||
|
The `.vscode` folder contains files that are used to configure the debug
|
||
|
environment.
|
||
|
|
||
|
Expand the `.vscode` folder, and then select the launch.json file.
|
||
|
|
||
|
Take a minute to examine the launch.json file.
|
||
|
|
||
|
The launch configurations file can include multiple configurations. Each
|
||
|
configuration includes a collection of attributes that are used to define that
|
||
|
configuration.
|
||
|
|
||
|
Notice that the `prelaunchTask` attribute specifies a build task.
|
||
|
|
||
|
In the `.vscode` folder, select **`tasks.json`**.
|
||
|
|
||
|
Notice that the tasks.json file contains the *build* task for your code
|
||
|
project.
|
||
|
|
||
|
Close the `launch.json` and `tasks.json` files.
|
||
|
|
||
|
You take a closer look at the launch configuration attributes later in this
|
||
|
module.
|
||
|
|
||
|
### Run your code from the Run menu
|
||
|
|
||
|
The Run menu in Visual Studio Code provides the option to run your code with or
|
||
|
without the debugger.
|
||
|
|
||
|
Open the Program.cs file.
|
||
|
|
||
|
Replace the contents of your Program.cs file with the following code:
|
||
|
|
||
|
```cs
|
||
|
// This code uses a names array and corresponding methods to display
|
||
|
// greeting messages
|
||
|
string[] names = new string[] { "Sophia", "Andrew", "AllGreetings" };
|
||
|
string messageText = "";
|
||
|
|
||
|
foreach (string name in names) {
|
||
|
if (name == "Sophia")
|
||
|
messageText = SophiaMessage();
|
||
|
else if (name == "Andrew")
|
||
|
messageText = AndrewMessage();
|
||
|
else if (name == "AllGreetings")
|
||
|
messageText = SophiaMessage();
|
||
|
messageText = messageText + "\n\r" + AndrewMessage();
|
||
|
Console.WriteLine(messageText + "\n\r");
|
||
|
}
|
||
|
|
||
|
bool pauseCode = true;
|
||
|
while (pauseCode == true);
|
||
|
|
||
|
static string SophiaMessage() {
|
||
|
return "Hello, my name is Sophia.";
|
||
|
}
|
||
|
|
||
|
static string AndrewMessage() {
|
||
|
return "Hi, my name is Andrew. Good to meet you.";
|
||
|
}
|
||
|
```
|
||
|
|
||
|
On the **File** menu, select **Save**.
|
||
|
|
||
|
Open the **Run** menu.
|
||
|
|
||
|
Notice that the **Run** menu provides options for running your code with or
|
||
|
without debugging.
|
||
|
|
||
|
On the **Run** menu, select **Run Without Debugging**
|
||
|
|
||
|
Notice that the DEBUG CONSOLE panel displays console output, and that the
|
||
|
**Debug toolbar** displays execution controls.
|
||
|
|
||
|
The DEBUG CONSOLE panel should be displayed below the code Editor. By default,
|
||
|
the **Debug toolbar** (the small toolbar displaying code execution controls) is
|
||
|
located above the code Editor and horizontally centered on the Visual Studio
|
||
|
Code window.
|
||
|
|
||
|
On the **Debug toolbar**, select **Stop**.
|
||
|
|
||
|
### Start a debug session from the Run menu
|
||
|
|
||
|
The Run menu includes the option to start a debug session.
|
||
|
|
||
|
On the **Run** menu, select **Start Debugging**
|
||
|
|
||
|
Take a minute to review the messages displayed in the DEBUG CONSOLE panel.
|
||
|
|
||
|
The output from your application is the same as when you ran without debugging,
|
||
|
but other messages related to preparing the debug environment are displayed.
|
||
|
|
||
|
Notice the messages about loading .NET resources and your Debug101 application.
|
||
|
|
||
|
The first two messages report loading the .NET Core library and then your
|
||
|
Debug101 application.
|
||
|
|
||
|
```txt
|
||
|
Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\7.0.4\System.Private.CoreLib.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
|
||
|
Loaded 'C:\Users\someuser\Desktop\Debug101\bin\Debug\net7.0\Debug101.dll'. Symbols loaded.
|
||
|
```
|
||
|
|
||
|
The debugger uses a special instance of the .NET runtime to control the
|
||
|
execution of your application and evaluate application state.
|
||
|
|
||
|
On the Debug toolbar, select Stop.
|
||
|
|
||
|
### Run your code from the Run and Debug view
|
||
|
|
||
|
The RUN AND DEBUG view in Visual Studio Code supports a rich debugging
|
||
|
experience.
|
||
|
|
||
|
Switch to the RUN AND DEBUG view.
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debug_01.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
In the RUN AND DEBUG view, select Start Debugging.
|
||
|
|
||
|
The **Start Debugging** button is the green arrow on the control panel at the
|
||
|
top of the view.
|
||
|
|
||
|
Notice that the DEBUG CONSOLE panel shows the same messages about configuring
|
||
|
the debugger that were displayed when starting a debug process from the **Run**
|
||
|
menu.
|
||
|
|
||
|
On the **Debug toolbar**, select **Stop**.
|
||
|
|
||
|
### Examine the output from your application
|
||
|
|
||
|
Before closing the DEBUG CONSOLE panel, take a minute to review the output that
|
||
|
your code produced.
|
||
|
|
||
|
Notice that Andrew's greeting message is repeated unexpectedly.
|
||
|
|
||
|
During the remainder of this module, you'll use the Visual Studio Code debugger
|
||
|
tools to investigate coding issues.
|
||
|
|
||
|
### Recap
|
||
|
|
||
|
Here are a few important things to remember from this unit:
|
||
|
|
||
|
- The Visual Studio Code debugger for C# uses the .NET Core runtime to launch and interact with an application.
|
||
|
- The Visual Studio Code Run menu has options to start an application with and without the debugger attached.
|
||
|
- The Debug toolbar includes a button to Stop a running process.
|
||
|
- The RUN AND DEBUG view includes an option to start debugging an application.
|
||
|
|
||
|
---
|
||
|
|
||
|
## Examine breakpoint configuration options
|
||
|
|
||
|
Debuggers are used to help you to analyze your code and can be used to control
|
||
|
your program's runtime execution. When you start the Visual Studio Code
|
||
|
debugger, it immediately begins executing your code. Because your code executes
|
||
|
in micro-seconds, effective code debugging depends on your ability to pause the
|
||
|
program on any statement within your code. Breakpoints are used to specify
|
||
|
where code execution pauses.
|
||
|
|
||
|
### Set breakpoints
|
||
|
|
||
|
Visual Studio Code provides several ways to configure breakpoints in your code.
|
||
|
For example:
|
||
|
|
||
|
- Code Editor: You can set a breakpoint in the Visual Studio Code Editor by
|
||
|
clicking in the column to the left of a line number.
|
||
|
- Run menu: You can toggle a breakpoint on/off from the Run menu. The current
|
||
|
code line in the Editor specifies where the Toggle Breakpoint action is applied.
|
||
|
|
||
|
When a breakpoint is set, a red circle is displayed to the left of the line
|
||
|
number in the Editor. When you run your code in the debugger, execution pauses
|
||
|
at the breakpoint.
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debug_02.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
### Remove, disable, and enable breakpoints
|
||
|
|
||
|
After setting breakpoints in your application and using them to isolate an
|
||
|
issue, you may want to remove or disable the breakpoints.
|
||
|
|
||
|
To remove a breakpoint, repeat the action used to set a breakpoint. For example,
|
||
|
click the red circle to the left of the line number or use the toggle
|
||
|
breakpoint option on the Run menu.
|
||
|
|
||
|
What if you want to keep a breakpoint location, but you don't want it to
|
||
|
trigger during your next debug session? Visual Studio Code enables you to
|
||
|
"disable" a breakpoint rather than removing it altogether. To disable an active
|
||
|
breakpoint, right-click the red dot to the left of the line number, and then
|
||
|
select Disable Breakpoint from the context menu.
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debug_03.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
When you disable a breakpoint, the red dot to the left of the line number is
|
||
|
changed to a grey dot.
|
||
|
|
||
|
> Note
|
||
|
> The context menu that appears when you right-click a breakpoint also includes
|
||
|
the options to **Remove Breakpoint (Delete)** and **Edit Breakpoint**. The
|
||
|
**Edit Breakpoint** option is examined in the **Conditional breakpoints
|
||
|
Logpoints** section later in this unit.
|
||
|
|
||
|
In addition to managing individual breakpoints in the Editor, the **Run** menu
|
||
|
provides options for performing bulk operations that act on all breakpoints:
|
||
|
|
||
|
- **Enable All Breakpoints**: Use this option to enable all disabled breakpoints.
|
||
|
- **Disable All Breakpoints**: Use this option to disable all breakpoints.
|
||
|
- **Remove All Breakpoints**: Use this option to remove all breakpoints (both
|
||
|
enabled and disabled breakpoints are removed).
|
||
|
|
||
|
### Conditional breakpoints
|
||
|
|
||
|
A conditional breakpoint is a special type of breakpoint that only triggers
|
||
|
when a specified condition is met. For example, you can create a conditional
|
||
|
breakpoint that pauses execution when a variable named `numItems` is greater
|
||
|
than 5.
|
||
|
|
||
|
You've already seen that right-clicking a breakpoint opens a context menu that
|
||
|
includes the **Edit Breakpoint** option. Selecting **Edit Breakpoint** enables
|
||
|
you to change a standard breakpoint into a conditional breakpoint.
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debug_04.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
In addition to editing an existing breakpoint, you can also set a conditional
|
||
|
breakpoint directly. If you right-click (rather than left-click) to set a new
|
||
|
breakpoint, you can choose to create a conditional breakpoint.
|
||
|
|
||
|
When you create a conditional breakpoint, you need to specify an expression
|
||
|
that represents the condition.
|
||
|
|
||
|
Each time the debugger encounters the conditional breakpoint, it evaluates the
|
||
|
expression. If the expression evaluates as `true`, the breakpoint is triggered
|
||
|
and execution pauses. If the expression evaluates as `false`, execution
|
||
|
continues as if there was no breakpoint.
|
||
|
|
||
|
For example, suppose you need to debug some code that's inside the code block
|
||
|
of a `for` loop. You've noticed that the issue you're debugging only occurs
|
||
|
after the loop has completed several iterations. You decide that you want the
|
||
|
breakpoint to trigger once the loop's iteration control variable, `i`, is
|
||
|
greater than three. You create a conditional breakpoint and specify the
|
||
|
expression `i > 3`.
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debug_05.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
|
||
|
When you run your code in the debugger, it skips over your breakpoint until the
|
||
|
iteration when `i > 3` evaluates as true. When `i = 4`, execution pauses on
|
||
|
your conditional breakpoint.
|
||
|
|
||
|
### Support for `Hit Count` breakpoints and `Logpoints`
|
||
|
|
||
|
The C# debugger for Visual Studio Code also supports `Hit Count` breakpoints
|
||
|
and `Logpoints`.
|
||
|
|
||
|
A 'hit count' breakpoint can be used to specify the number of times that a
|
||
|
breakpoint must be encountered before it will 'break' execution. You can
|
||
|
specify a hit count value when creating a new breakpoint (with the Add
|
||
|
Conditional Breakpoint action) or when modifying an existing one (with the Edit
|
||
|
Condition action). In both cases, an inline text box with a dropdown menu opens
|
||
|
where you can enter the hit count value.
|
||
|
|
||
|
A 'Logpoint' is a variant of a breakpoint that does not "break" into the
|
||
|
debugger but instead logs a message to the console. Logpoints are especially
|
||
|
useful for injecting logging while debugging production environments that
|
||
|
cannot be paused or stopped. A Logpoint is represented by a "diamond" shaped
|
||
|
icon rather than a filled circle. Log messages are plain text but can include
|
||
|
expressions to be evaluated within curly braces `{}`
|
||
|
|
||
|
Logpoints can include a conditional 'expression' and/or 'hit count' to further
|
||
|
control when logging messages are generated. For example, you can combine a
|
||
|
Logpoint message of `i = {i}` with Hit Count condition `>4` to generate log
|
||
|
messages as follows:
|
||
|
|
||
|
### Recap
|
||
|
|
||
|
Here are a few important things to remember from this unit:
|
||
|
|
||
|
- Visual Studio Code enables setting breakpoints in the code editor or from the
|
||
|
**Run menu**. Breakpoint code lines are marked with a red dot to the left of the
|
||
|
line number.
|
||
|
- Breakpoints can be removed or disabled using the same options used to set
|
||
|
them. Bulk operations that affect all breakpoints are available on the **Run**
|
||
|
menu.
|
||
|
- Conditional breakpoints can be used to pause execution when a specified
|
||
|
condition is met or when a 'hit count' is reached.
|
||
|
- Logpoints can be used to log information to the terminal without pausing
|
||
|
execution or inserting code.
|
||
|
|
||
|
---
|
||
|
|
||
|
## Exercise
|
||
|
|
||
|
### Set breakpoints
|
||
|
|
||
|
Breakpoints are used during the debug process pause execution. This enables you
|
||
|
to track variables and examine the sequence in which your code is executed.
|
||
|
Breakpoints are a great way to start your debug process.
|
||
|
|
||
|
### Set a breakpoint
|
||
|
|
||
|
Earlier in this module you completed an exercise where you ran an application
|
||
|
in the debugger. The application displayed "greeting messages" in the DEBUG
|
||
|
CONSOLE panel. At the end of the exercise, you noticed that the code repeats
|
||
|
Andrew's greeting in an unexpected way.
|
||
|
|
||
|
In this exercise, you'll use a breakpoint to help you identify the issue.
|
||
|
|
||
|
Ensure that your Program.cs file contains the following code sample:
|
||
|
|
||
|
```cs
|
||
|
//This code uses a names array and corresponding methods to display
|
||
|
//greeting messages
|
||
|
string[] names = new string[] { "Sophia", "Andrew", "AllGreetings" };
|
||
|
|
||
|
string msg_txt = "";
|
||
|
|
||
|
foreach (string name in names) {
|
||
|
if (name == "Sophia") {
|
||
|
msg_txt = sophia_msg();
|
||
|
} else if (name == "Andrew") {
|
||
|
msg_txt = andrew_msg();
|
||
|
} else if { (name == "AllGreetings")
|
||
|
msg_txt = sophia_msg();
|
||
|
msg_txt = msg_txt + "\n\r" + andrew_msg();
|
||
|
}
|
||
|
Console.WriteLine(msg_txt + "\n\r");
|
||
|
}
|
||
|
|
||
|
bool pause_code = true;
|
||
|
while (pause_code == true);
|
||
|
|
||
|
static string sophia_msg() {
|
||
|
return "Hello, my name is Sophia.";
|
||
|
}
|
||
|
|
||
|
static string andrew_msg() {
|
||
|
return "Hi, my name is Andrew. Good to meet you.";
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Use the Visual Studio Code debugger tools to set a breakpoint on the first code
|
||
|
line inside the foreach loop.
|
||
|
|
||
|
> Tip
|
||
|
> One easy option for toggling on/off a breakpoint is to select (left-click)
|
||
|
the area to the left of the line number. Breakpoints can also be set by using
|
||
|
the Run menu and by using keyboard shortcuts.
|
||
|
|
||
|
On the **Run** menu, select **Start Debugging**.
|
||
|
|
||
|
Notice that code execution pauses at the breakpoint, and that the current code
|
||
|
line is highlighted in the Editor.
|
||
|
|
||
|
On the Debug controls toolbar, select Step Into.
|
||
|
|
||
|
You can hover the mouse pointer over the buttons on the **Debug controls**
|
||
|
toolbar to display the button labels.
|
||
|
|
||
|
Notice that code execution advances to the following code line and pauses:
|
||
|
|
||
|
```cs
|
||
|
messageText = SophiaMessage();
|
||
|
```
|
||
|
|
||
|
This code line assigns the return value of the `sophia_msg` method to the
|
||
|
string variable `messageText`.
|
||
|
|
||
|
Take a moment to consider why selecting **Step Into** produced this result.
|
||
|
|
||
|
- The **Step Into** button is used to advance to the next executable statement.
|
||
|
- Since the first element in the `names` array is `Sophia` and the `if`
|
||
|
statement is checking for the name `Sophia`, the expression evaluates to `true`
|
||
|
and code execution moves into the code block of the `if` statement.
|
||
|
|
||
|
On the **Debug controls** toolbar, select **Step Into**.
|
||
|
|
||
|
Notice that code execution advances to the `sophia_msg` method and pauses.
|
||
|
|
||
|
The **Step Into** button has advanced to the next executable code line. The
|
||
|
next executable code line isn't the next line number in the file, it's the next
|
||
|
statement in the execution path. In this case, the next executable statement is
|
||
|
the entry point to the `sophia_msg` method.
|
||
|
|
||
|
On the **Debug controls** toolbar, select Step **Out**.
|
||
|
|
||
|
Notice that code execution returns to the code line that called the
|
||
|
`sophia_msg` method and pauses.
|
||
|
|
||
|
Take a moment to consider why selecting **Step Out** produced this result.
|
||
|
|
||
|
When inside a method, the **Step Out** button completes the remaining lines of
|
||
|
the current method and then returns to the execution context that invoked the
|
||
|
method.
|
||
|
|
||
|
On the **Debug controls** toolbar, select **Step Into**.
|
||
|
|
||
|
Notice that code execution advances to the following code line and pauses:
|
||
|
|
||
|
```cs
|
||
|
messageText = messageText + "\n\r" + AndrewMessage();
|
||
|
```
|
||
|
|
||
|
Take a moment to consider why execution advanced to this code line.
|
||
|
|
||
|
Although the code indentation implies that this code line is part of the code
|
||
|
block for the `else if` statement, it isn't. Using curly braces `{}` to define
|
||
|
the code blocks for this `if - else if` structure would have helped to avoid
|
||
|
this bug. As the code is written, Andrew's message will be added to `msg_txt`
|
||
|
each time the loop iterates.
|
||
|
|
||
|
### Verify your code updates
|
||
|
|
||
|
Once you've isolated an issue in your code, you should update your code and
|
||
|
then verify that the issue has been fixed.
|
||
|
|
||
|
On the **Debug controls** toolbar, select **Stop**.
|
||
|
|
||
|
Take a minute to fix your code logic.
|
||
|
|
||
|
You have a few options for fixing the identified issue in your code. For
|
||
|
example:
|
||
|
|
||
|
- You could keep the existing code lines and add curly braces `{}` to the `if`
|
||
|
structure for each code block.
|
||
|
|
||
|
- You could merge the two code lines that follow the final `else if` statement,
|
||
|
forming a single statement as follows:
|
||
|
|
||
|
```cs
|
||
|
} else if (name == "AllGreetings") {
|
||
|
msg_txt = sophia_msg() + "\n\r" + andrew_msg();
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Either way, your updated code must include the call to `andrew_msg` within the
|
||
|
code block when `name == "AllGreetings"`.
|
||
|
|
||
|
On the **File** menu, select **Save**.
|
||
|
|
||
|
Use the debugger UI tools to clear the breakpoint that you set earlier.
|
||
|
|
||
|
On the **Run** menu, select **Start Debugging**.
|
||
|
|
||
|
Verify that your code now produces the expected results.
|
||
|
|
||
|
```txt
|
||
|
Hello, my name is Sophia.
|
||
|
|
||
|
Hi, my name is Andrew. Good to meet you.
|
||
|
|
||
|
Hello, my name is Sophia.
|
||
|
Hi, my name is Andrew. Good to meet you.
|
||
|
```
|
||
|
|
||
|
On the **Debug controls** toolbar, select **Stop**.
|
||
|
|
||
|
Congratulations! You've successfully used the Visual Studio Code debugger to
|
||
|
help you isolate and correct a logic issue.
|
||
|
|
||
|
### Recap
|
||
|
|
||
|
Here are a few important things to remember from this unit:
|
||
|
|
||
|
- Use breakpoints to pause code execution during a debug session.
|
||
|
- Use **Step Into** from the **Debug controls** toolbar to observe the next
|
||
|
executable code line.
|
||
|
- Use **Step Out** from the **Debug controls** toolbar to advance through the
|
||
|
current method and back to the code line that called the method.
|
||
|
|
||
|
---
|
||
|
|
||
|
## Examine the launch configurations file
|
||
|
|
||
|
You've already seen that Visual Studio Code uses the launch.json file to
|
||
|
configure the debugger. If you're creating a simple C# console application, it's
|
||
|
likely that Visual Studio Code generates a launch.json file that has all of the
|
||
|
information you need to successfully debug your code. However, there are cases
|
||
|
when you need to modify a launch configuration, so it's important to understand
|
||
|
the attributes of a launch configuration.
|
||
|
|
||
|
### Attributes of a launch configuration
|
||
|
|
||
|
The `launch.json` file includes one or more launch `configurations` in the
|
||
|
configurations list. The launch configurations use attributes to support
|
||
|
different debugging scenarios. The following attributes are mandatory for every
|
||
|
launch configuration:
|
||
|
|
||
|
- `name`: The reader-friendly name assigned to the launch configuration.
|
||
|
- `type`: The type of debugger to use for the launch configuration.
|
||
|
- `request`: The request type of the launch configuration.
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"version": "0.2.0",
|
||
|
"configurations": [
|
||
|
{
|
||
|
// Use IntelliSense to find out which attributes exist for C# debugging
|
||
|
// Use hover for the description of the existing attributes
|
||
|
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
|
||
|
"name": ".NET Core Launch (console)",
|
||
|
"type": "coreclr",
|
||
|
"request": "launch",
|
||
|
"preLaunchTask": "build",
|
||
|
// If you have changed target frameworks, make sure to update the program path.
|
||
|
"program": "${workspaceFolder}/bin/Debug/net8.0/ejm_debug.dll",
|
||
|
"args": [],
|
||
|
"cwd": "${workspaceFolder}",
|
||
|
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||
|
"console": "internalConsole",
|
||
|
"stopAtEntry": false
|
||
|
},
|
||
|
{
|
||
|
"name": ".NET Core Attach",
|
||
|
"type": "coreclr",
|
||
|
"request": "attach"
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
```
|
||
|
|
||
|
This section defines some of the attributes you may encounter.
|
||
|
|
||
|
### Name
|
||
|
|
||
|
The `name` attribute specifies the display name for the launch configuration.
|
||
|
The value assigned to `name` appears in the launch configurations dropdown (on
|
||
|
the controls panel at the top of the RUN AND DEBUG view).
|
||
|
|
||
|
### Type
|
||
|
|
||
|
The `type` attribute specifies the type of debugger to use for the launch
|
||
|
configuration. A value of `codeclr` specifies the debugger type for .NET 5+ and
|
||
|
.NET Core applications (including C# applications).
|
||
|
|
||
|
### Request
|
||
|
|
||
|
The `request` attribute specifies the request type for the launch configuration.
|
||
|
Currently, the values `launch` and `attach` are supported.
|
||
|
|
||
|
### PreLaunchTask
|
||
|
|
||
|
The `preLaunchTask` attribute specifies a task to run before debugging your
|
||
|
program. The task itself can be found in the `tasks.json` file, which is in the
|
||
|
`.vscode` folder along with the `launch.json` file. Specifying a prelaunch task
|
||
|
of `build` runs a `dotnet build` command before launching the application.
|
||
|
|
||
|
### Program
|
||
|
|
||
|
The `program` attribute is set to the path of the application dll or .NET Core
|
||
|
host executable to launch.
|
||
|
|
||
|
This property normally takes the form:
|
||
|
`${workspaceFolder}/bin/Debug/<target-framework>/<project-name.dll>`.
|
||
|
|
||
|
Where:
|
||
|
|
||
|
- `<target-framework>` is the framework that the debug project is being built
|
||
|
for. This value is normally found in the project file as the 'TargetFramework'
|
||
|
property.
|
||
|
- `<project-name.dll>` is the name of debugged project's build output dll. This
|
||
|
property is normally the same as the project file name but with a '.dll'
|
||
|
extension.
|
||
|
|
||
|
For example: `${workspaceFolder}/bin/Debug/net7.0/Debug101.dll`
|
||
|
|
||
|
> Note
|
||
|
> The **.dll** extension indicates that this file is a dynamic link library
|
||
|
(dll) file. If your project is named Debug101, a file named **Debug101.dll** is
|
||
|
created when a build task compiles your program using the Program.cs and
|
||
|
Debug101.csproj files. You can find the **Debug101.dll** file in the EXPLORER
|
||
|
view by expanding the "bin" and "Debug" folders, and then opening a folder that
|
||
|
represents the .NET framework used by your code project, such as "net7.0". The
|
||
|
.NET Framework version is specified in your .csproj file.
|
||
|
|
||
|
### Cwd
|
||
|
|
||
|
The `cwd` attribute specifies the working directory of the target process.
|
||
|
|
||
|
### Args
|
||
|
|
||
|
The `args` attribute specifies the arguments that are passed to your program at
|
||
|
launch. There are no arguments by default.
|
||
|
|
||
|
### Console
|
||
|
|
||
|
The `console` attribute specifies the type of console that's used when the
|
||
|
application is launched. The options are `internalConsole`,
|
||
|
`integratedTerminal`, and `externalTerminal`. The default setting is
|
||
|
`internalConsole`. The console types are defined as:
|
||
|
|
||
|
- The `internalConsole` setting corresponds to the DEBUG CONSOLE panel in the
|
||
|
Panels area below the Visual Studio Code Editor.
|
||
|
- The `integratedTerminal` setting corresponds to the OUTPUT panel in the
|
||
|
Panels area below the Visual Studio Code Editor.
|
||
|
- The `externalTerminal` setting corresponds to an external terminal window.
|
||
|
The Command Prompt application that comes with Windows is an example of a
|
||
|
terminal window.
|
||
|
|
||
|
> Important
|
||
|
> The DEBUG CONSOLE panel doesn't support console input. For example, the DEBUG
|
||
|
CONSOLE can't be used if the application includes a `Console.ReadLine()`
|
||
|
statement. When you're working on a C# console application that reads user
|
||
|
input, the `console` setting must be set to either `integratedTerminal` or
|
||
|
`externalTerminal`. Console applications that write to the console, but don't
|
||
|
read input from the console, can use any of the three console settings.
|
||
|
|
||
|
### Stop at Entry
|
||
|
|
||
|
If you need to stop at the entry point of the target, you can optionally set
|
||
|
`stopAtEntry` to be `true`.
|
||
|
|
||
|
### Edit a launch configuration
|
||
|
|
||
|
There are lots of scenarios when you might need to customize the launch
|
||
|
configuration file. Many of those scenarios involve advanced or complex project
|
||
|
scenarios. This module focuses on two simple scenarios when updating the launch
|
||
|
configuration file is required:
|
||
|
|
||
|
- Your C# console application reads input from the console.
|
||
|
- Your project workspace includes more than one application.
|
||
|
|
||
|
### Update the launch configuration to accommodate console input
|
||
|
|
||
|
As you read earlier, the DEBUG CONSOLE panel doesn't support console input. If
|
||
|
you're debugging a console application that relies on user input, you need to
|
||
|
update the `console` attribute in the associated launch configuration.
|
||
|
|
||
|
To edit the `console` attribute:
|
||
|
|
||
|
Open the `launch.json` file in the Visual Studio Code Editor.
|
||
|
|
||
|
Locate the **console** attribute.
|
||
|
|
||
|
Select the colon and assigned value, and then enter a colon character.
|
||
|
|
||
|
Notice that when you overwrite the existing information with a colon, Visual
|
||
|
Studio Code IntelliSense displays the three options in a dropdown list.
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debug_06.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
Select either `integratedTerminal` or `externalTerminal`.
|
||
|
|
||
|
Save the `launch.json` file.
|
||
|
|
||
|
### Update the launch configuration to accommodate multiple applications
|
||
|
|
||
|
If your workspace has only one launchable project, the C# extension will
|
||
|
automatically generate the `launch.json` file. If you have more than one
|
||
|
launchable project, then you need to modify your `launch.json` file manually.
|
||
|
Visual Studio Code generates a `launch.json` file using the basic template that
|
||
|
you can update. In this scenario, you create separate configurations for each
|
||
|
application that you want to debug. Prelaunch tasks, such as a build task, can
|
||
|
be created in the `tasks.json` file.
|
||
|
|
||
|
Suppose that you're working on a coding project that includes several console
|
||
|
applications. The root project folder, **SpecialProjects**, is the workspace
|
||
|
folder that you open in Visual Studio Code when you work on your code. You have
|
||
|
two applications that you're developing, **Project123** and **Project456**.
|
||
|
You use the RUN AND DEBUG view to debug the applications. You want to select
|
||
|
the application that you're debugging from the user interface. You also want
|
||
|
any saved code updates to be compiled prior to attaching the debugger to your
|
||
|
application.
|
||
|
|
||
|
You can achieve the requirements for this scenario by updating the `launch.json`
|
||
|
and `tasks.json` files.
|
||
|
|
||
|
The following screenshot shows the EXPLORER view and the folder structure
|
||
|
containing ***Project123*** and ***Project456***.
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debug_07.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
Notice that the `name`, `preLaunchTask`, and `program` fields are all
|
||
|
configured for a specific application.
|
||
|
|
||
|
The `name` attribute specifies the selectable launch option that's displayed in
|
||
|
the RUN AND DEBUG view user interface, the `program` attribute specifies the
|
||
|
path to your application. The `preLaunchTask` attribute is used to specify the
|
||
|
name of the task that's performed prior to launching the debugger. The
|
||
|
`tasks.json` file contains the named tasks and the information required to
|
||
|
complete the task.
|
||
|
|
||
|
The following example shows how you could configure the tasks.json file. In
|
||
|
this case, the named tasks specify build operations that are specific to the
|
||
|
"Project123" and "Project456" applications. The build task ensures that any
|
||
|
saved edits are compiled and represented in the corresponding .dll file that's
|
||
|
attached to the debugger.
|
||
|
|
||
|
```json
|
||
|
"version": "2.0.0",
|
||
|
"tasks": [
|
||
|
{
|
||
|
"label": "buildProject123",
|
||
|
"command": "dotnet",
|
||
|
"type": "process",
|
||
|
"args": [
|
||
|
"build",
|
||
|
"${workspaceFolder}/Project123/Project123.csproj",
|
||
|
"/property:GenerateFullPaths=true",
|
||
|
"/consoleloggerparameters:NoSummary"
|
||
|
],
|
||
|
"problemMatcher": "$msCompile"
|
||
|
},
|
||
|
{
|
||
|
"label": "buildProject456",
|
||
|
"command": "dotnet",
|
||
|
"type": "process",
|
||
|
"args": [
|
||
|
"build",
|
||
|
"${workspaceFolder}/Project456/Project456.csproj",
|
||
|
"/property:GenerateFullPaths=true",
|
||
|
"/consoleloggerparameters:NoSummary"
|
||
|
],
|
||
|
"problemMatcher": "$msCompile"
|
||
|
}
|
||
|
]
|
||
|
```
|
||
|
|
||
|
With your updates to the launch.json and tasks.json files in place, the RUN AND
|
||
|
DEBUG view displays launch options for debugging either the Project123 or
|
||
|
Project456 application. The following screenshot shows the names of the launch
|
||
|
configurations displayed in the launch configuration dropdown:
|
||
|
|
||
|
<div align="center">
|
||
|
|
||
|
![img](./img/code_oss_debug_08.png)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
### Recap
|
||
|
|
||
|
Here are two important things to remember from this unit:
|
||
|
|
||
|
- Launch configurations are used to specify attributes such as `name`, `type`,
|
||
|
`request`, `preLaunchTask`, `program`, and `console`.
|
||
|
- Developers can edit a launch configuration to accommodate project
|
||
|
requirements.
|
||
|
|
||
|
---
|