Debugging starts with understanding the problem and gathering error messages, stack traces, and logs before touching source code. Advanced techniques follow for memory analysis and concurrency debugging, where leaks, race conditions, and deadlocks surface. Tools range from GDB and Visual Studio debuggers to JProfiler, YourKit, and the Eclipse Memory Analyzer for heap dumps. Best practices favor a systematic approach, isolation, documentation, and prevention.
https://viblo.asia/p/debugging-trong-lap-trinh-chien-luoc-cong-cu-va-thuc-tien-tot-nhat-2oKLn1dXJQO
Foundational Debugging Principles
-
Understand the problem before starting:
- Don't rush into analyzing the source code
- Understand clearly what is going wrong
- Identify the expected behavior vs. the actual behavior
-
Gather detailed information:
- Error messages
- Stack traces, to see the execution flow
- Logs from the system and application
- Context around the error (conditions, input data)
-
Reproduce the bug consistently:
- You need to be able to reproduce the bug to diagnose it easily
- It lets you verify whether a fix actually works
- If you can't reproduce it, it is very hard to fix and verify
-
Compare actual behavior with requirements:
- Identify the deviation between actual behavior and the software requirements
- Understand the specification and expected behavior
- Distinguish between a bug and a feature request
Advanced Debugging Techniques
Memory Analysis
-
Purpose:
- Detect memory leaks
- Detect memory corruption
- Identify objects that consume too much memory
-
Tools and techniques:
- Memory profilers: tools that analyze memory usage
- Heap dump analysis: analyze a snapshot of heap memory
- Track allocation and deallocation:
- Identify objects that are not reclaimed (garbage collected)
- Detect circular references
- Find objects holding memory unnecessarily
Concurrency Debugging
-
Common problems:
- Race conditions: the result depends on execution order
- Deadlocks: threads wait on each other indefinitely
- Data races: multiple threads access the same data without synchronization
-
Tools and techniques:
- ThreadSanitizer (TSan): detects data races in C/C++
- Helgrind: a Valgrind tool for detecting race conditions and deadlocks
- Careful logging:
- Log thread activities
- Track execution order
- Record the state of shared resources
- Assertions:
- Check data consistency
- Detect unexpected conditions
- Validate invariants in code
Debugging Tools
Debuggers
-
GDB (GNU Debugger):
- Debugger for C/C++ and many other languages
- Supports breakpoints, step-through, variable inspection
- Command-line interface, powerful but with a learning curve
-
Visual Studio Debugger:
- Debugger integrated into Visual Studio
- Supports many languages (.NET, C++, Python, JavaScript)
- Friendly, easy-to-use GUI
- Supports remote debugging and multi-threaded debugging
Profilers
-
JProfiler:
- Java profiler with a GUI
- Analyzes CPU, memory, threads
- Supports heap walker and thread analysis
-
YourKit:
- Java and .NET profiler
- Analyzes performance and memory
- Supports CPU profiling, memory profiling, thread profiling
Memory Analysis Tools
- Eclipse Memory Analyzer Tool (MAT):
- Analyzes heap dumps
- Detects memory leaks
- Visualizes memory usage
- Supports many heap dump formats
Choosing the Right Tool
-
Consider:
- The programming language in use
- The development environment (IDE, OS)
- The type of problem to debug (memory, concurrency, performance)
- Learning curve and usability
-
Principles:
- Use tools that fit your language and environment
- Combine multiple tools for a complete picture
- Invest time in learning the important tools
Best Practices
-
Systematic approach:
- Don't guess; follow a systematic process
- Record the steps you tried and their results
- Rule out causes logically
-
Isolation:
- Isolate the problem and identify the code that causes the bug
- Use unit tests to isolate functionality
- Create a minimal reproducible case
-
Documentation:
- Record the debugging process
- Document workarounds and solutions
- Share knowledge with the team
-
Prevention:
- Write code that is easy to debug (logging, error handling)
- Use assertions and validations
- Do code reviews to catch problems early
- Use automated testing to catch bugs early
See also: Case Study Quick Win Optimization · Kafka DLQ And Retry