Debugging 2
School of Computing Lancaster
SCC.111 Software Development - Lecture 9: Debugging II
Instructors
Joe Finney
Nigel Davies
Hansi Hettiarachchi
Adrian Friday
Lecture Overview
Exploration of strategies for finding logical and programming errors in code (debugging).
Previous week’s focus: dry running.
Introduction of two additional strategies for isolating problems.
Worked examples to illustrate concepts.
Debugging Overview
Debugging is likened to detective work; finding issues in code can often feel like solving a mystery.
A critical goal: isolate the problem to facilitate fixing it.
Importance of Runtime Errors
Runtime errors are problematic and need resolution.
Developers must aim to address these errors proactively.
Strategies for Debugging
Approach 2: Testing Hypotheses to Isolate Faults
Focus: Validate assumptions about code behavior.
Example Hypotheses to Test:
A variable should hold a specific value at a particular point in the source file.
A loop that is expected to exit does not.
The else clause in an if-then-else statement is the one being executed erroneously.
A certain function call receives the correct parameters and returns the anticipated result.
Testing Hypotheses Using printf
Utilizing the
printffunction serves as a tool for debugging, allowing programmers to output variable states and track execution flow, helping in validating hypotheses about code execution.
Consideration of Code Complexity
As code complexity increases, finding issues can become more challenging.
Introducing concepts like runtime debugging can greatly aid in dealing with complicated scenarios.
Approach 3: Runtime Debugging
Purpose: Designed for uncovering more elusive and complex faults within code.
Usage scenarios include:
Addressing memory corruption issues, timing or concurrency bugs, and other subtle problems.
Typical Functionality of Runtime Debuggers
Execution control and observation capabilities include:
Breakpoints: Set at specific lines or conditions to interrupt execution to check the current state.
Single Stepping: Step into or over functions one at a time to observe the flow of execution closely.
Inspecting/Changing Variables: Ability to examine variable values and modify them if necessary.
Watchpoints: Monitor changes to state variables, increasing visibility into how data changes over time.
Recommendations on When to Use Each Approach
Dry Running: Typically effective for manageable sized code units. Should be a habitual practice when reading code.
Printing Statements: Using debugging statements like
printfcan significantly help in isolating problems, particularly when dealing with smaller chunks of code. Debug print statements act as a method for code to convey its behavior.Error Logs: Commonly utilized for tracking behavior in production systems, essential for identifying trends in errors or unexpected behavior.
Debuggers: Best suited for more complex scenarios involving confusing or unexpected runtime errors, as well as for post-crash dump analysis where deep insights into faults can be gathered.
Example Issues Addressed by Debuggers:
Memory errors (often referred to as ‘splat bugs’).
Concurrency issues (where processes affect one another unexpectedly).
Data-dependent faults (where the behavior depends on the data being processed).
Summary
Understanding the concept of debugging is crucial for developers.
Key skills to develop include:
Formulating and testing hypotheses regarding code execution.
Applying debugging statements (like
printf) to systematically isolate problems.Familiarity with software debuggers, which are indispensable for identifying hard-to-find errors and conducting code forensics.