July 08, 2021
Nikolay Kostov (Nikolay.IT)
Topics covered:
- Introduction to Debugging
- Visual Studio Debugger
- Breakpoints
- Data Inspection
- Threads and Stacks
- Finding a Defect
Video (in Bulgarian)
Presentation Content
What is Debugging?
- The process of locating and fixing or bypassing bugs (errors) in computer program code
- To debug a program:
- Start with a problem and stable source state
- Isolate the source of the problem
- Fix it and send the fix to the source control
- Create regression test
- Debugging tools (called debuggers) help identify coding errors
Debugging vs. Testing
- Testing
- A means of initial detection of errors
- Debugging
- A means of diagnosing and correcting the root causes of errors that have already been detected
Importance of Debugging
- Perfect code is an illusion
- There is no non-trivial software without bugs
- There are factors that are out of our control
- Debugging can viewed as one big decision tree
- Individual nodes represent theories
- Leaf nodes represent possible root causes
- You should be able to debug code that is written years ago
- $60 Billion per year in economic losses due to software defects (in USA only)
How to Avoid Bugs
- Reduce number of code lines
- Write less and write smarter. Keep it simple.
- Reduce complexity / Good code separation
- Reduce number of possible code paths
- Understand the size of the input & output spaces (be aware of the corner cases)
- Unit test (TDD) with almost 100% test coverage
- Always assume your code is not working
- Prove it otherwise!
- Always check the code you write
- Experience matters / Read, learn, code
Visual Studio Debugger
- Visual Studio IDE gives us a lot of tools to debug your application
- Adding breakpoints
- Visualize the program flow
- Control the flow of execution
- Data tips
- Watch variables
- Debugging multithreaded programs
- and many more…
How To Debug a Process
- Starting a process under the Visual Studio debugger
- Attaching to an already running process
- Without a solution loaded you can still debug
- Useful when solution isn’t readily available
- Debug menu -> Attach to Process
Debugging a Solution
- Debug menu, Start Debugging item
- Easier access to the source code and symbols since its loaded in the solution
- Certain differences exist in comparison to debugging an already running process
- Hosting for ASP.NET application
- VS uses a replacement of the real IIS
Debug Windows
- Debug Windows are the means to introspect on the state of a process
- Opens a new window with the selected information in it
- Window categories
- Data inspection
- Threading
- Accessible from menu
Debugging Toolbar
- Convenient shortcut to common debugging tasks
- Step into
- Step over
- Continue
- Break
- Breakpoints
- Customizable to fit your needs
- Add and/or remove buttons
Controlling Execution
- By default, an app will run uninterrupted (and stop on exception or breakpoint)
- Debugging is all about looking at the state of the process
- Controlling execution allows:
- Pausing execution
- Resuming execution
- Stepping through the application in smaller chunks
- In the case of IntelliTrace (recording steps), allows backward and forward stepping
IntelliTrace
- IntelliTrace operates in the background, records what you are doing during debugging
- You can easily get a past state of your application from the IntelliTrace
- You can navigate your code with any part and see what’s happened
- To navigate, just click any of the events that you want to explore
Options and Settings
- Visual Studio offers quite a few knobs and tweaks in the debugging experience
- Options and settings is available via Debug -> Options and Settings
- Examples of Options and Settings
- Enable just my code (ignore other code)
- Enable .NET framework source stepping
- Source server support
- Symbols (line numbers, variable names)
- Much more…
Breakpoints
- Ability to stop execution based on certain criteria is key when debugging
- When a function is hit
- When data changes
- When a specific thread hits a function
- much more
- Visual Studio debugger has a huge feature set when it comes to breakpoints
Visual Studio Breakpoints
- Stops execution at a specific instruction (line of code)
- Can be set using Debug->Toggle breakpoint
- F9 shortcut
- Clicking on the left most side of the source code window
- By default, the breakpoint will hit every time execution reaches the line of the code
- Additional capabilities: condition, hit count, value changed, when hit, filters
Managing Breakpoints
- Managed in the breakpoint window
- Adding breakpoints
- Removing or disabling breakpoints
- Labeling or grouping breakpoints
- Export/import breakpoints
Breakpoint Filters
- Allows you to excerpt even more control of when a breakpoint hits
- Examples of customization
- Machine name
- Process ID
- Process name
- Thread ID
- Thread name
- Multiple can be combined using
&, ||, !
Data Inspection
- Debugging is all about data inspection
- What are the local variables?
- What is in memory?
- What is the code flow?
- In general - What is the state of the process right now and how did it get there?
- As such, the ease of data inspection is key to quick resolution of problems
Visual Studio Data Inspection
- Visual Studio offers great data inspection features
- Watch windows
- Autos and Locals
- Memory and Registers
- Data Tips
- Immediate window
Watch Window
- Allows you to inspect various states of your application
- Several different kinds of “predefined” watch windows
- “Custom” watch windows also possible
- Contains only variables that you choose to add
- Right click on the variable and select “Add to Watch”
Autos and Locals
- Locals watch window contains the local variables for the specific stack frame
- Debug -> Windows -> Locals
- Displays: name of the variable, value and type
- Allows drill down into objects by clicking on the + sign in the tree control
- Autos lets the debugger decide which variables to show in the window
- Loosely based on the current and previous statement
Memory and Registers
- Memory window can be used to inspect process wide memory
- Address field can be a raw pointer or an expression
- Drag and drop a variable from the source window
- Number of columns displayed can be configured
- Data format can be configured
- Registers window can be used to inspect processor registers
Data Tips
- Provides information about variables
- Variables must be within scope of current execution
- Place mouse pointer over any variable
- Variables can be expanded by using the + sign
- Pinning the data tip causes it to always stay open
- Comments can be added to data tips
- Data tips support drag and drop
- Importing and exporting data tips
Immediate Window
- Useful when debugging due to the expansive expressions that can be executed
- To output the value of a variable <name of variable>
- To set values, use <name of variable>=<value>
- To call a method, use <name of variable>.<method>(arguments)
- Similar to regular code
- Supports Intellisense
Threads
- Fundamental unit of code execution
- Commonly, more than one thread
- .NET, always more than one thread
- Each thread has a memory area associated with it known as a stack used to
- Store local variables
- Store frame specific information
- Memory area employs last in first out semantics
Threads Window
- Contains an overview of thread activity in the process
- Includes basic information in a per thread basis
- Thread ID’s
- Category
- Name
- Location
- Priority
Callstacks
- A threads stack is commonly referred to as a callstack
- Visual Studio shows the elements of a callstack
- Local variables
- Method frames
Finding a Defect
- Stabilize the error
- Locate the source of the error
- Gather the data
- Analyze the data and form hypothesis
- Determine how to prove or disprove the hypothesis
- Prove or disprove the hypothesis by 2c
- Fix the defect
- Test the fix
- Look for similar errors
Tips for Finding Defects
- Use all available data
- Refine the test cases
- Check unit tests
- Use available tools
- Reproduce the error several different ways
- Generate more data to generate more hypotheses
- Use the results of negative tests
- Brainstorm for possible hypotheses
- Narrow the suspicious region of the code
- Be suspicious of classes and routines that have had defects before
- Check code that’s changed recently
- Expand the suspicious region of the code
- Integrate incrementally
- Check for common defects
- Talk to someone else about the problem
- Take a break from the problem
Fixing a Defect
- Understand the problem before you fix it
- Understand the program, not just the problem
- Confirm the defect diagnosis
- Relax
- Save the original source code
- Fix the problem not the symptom
- Make one change at a time
- Add a unit test that expose the defect
- Look for similar defects
Psychological Considerations
- Your ego tells you that your code is good and doesn’t have a defect even when you’ve seen that it has one.
- How “Psychological Set” Contributes to Debugging Blindness