◎ Debugging Tools Overview¶
◎ Knowing Which Tool to Reach For¶
A programmer who knows only one debugging tool will reach for it in every situation — whether it is appropriate or not. A programmer who knows the landscape of available tools can match the right tool to the problem at hand.
This page surveys the categories of tools available to a working developer. It is not exhaustive — the tooling ecosystem is large and changes over time — but it covers the categories that matter most in everyday development and the specific examples most relevant to the environments used in this guide.
Tools support techniques — they do not replace thinking
No tool finds bugs on its own. A debugger that shows you the wrong value does not tell you why the value is wrong. A profiler that shows slow code does not tell you how to fix it. Tools make evidence visible. Reasoning turns evidence into understanding.
◎ I. IDE Debuggers¶
An integrated debugger is built into the development environment and operates directly on your source code. It is the primary debugging tool for the vast majority of development work.
All major IDEs provide the same core set of capabilities — the names and keyboard shortcuts differ, but the underlying operations are identical. See IDE-Specific Notes for the specific controls in each environment.
| Capability | What it does |
|---|---|
| Line breakpoints | Pause execution at a specific line |
| Conditional breakpoints | Pause only when a condition is true |
| Method breakpoints | Pause at entry or exit of a specific method |
| Exception breakpoints | Pause when a specific exception type is thrown |
| Step over | Advance one line, stay in the current method |
| Step into | Enter the called method |
| Step out | Finish the current method, return to caller |
| Continue | Run to the next breakpoint |
| Run to cursor | Run to wherever the cursor is placed |
| Variable inspection | See all variables in scope at the current position |
| Watch expressions | Monitor custom expressions throughout a session |
| Call stack navigation | View and navigate the full execution path |
| Expression evaluation | Evaluate any expression in the current context |
Primary use cases: Single-developer investigation of bugs in local development. All levels of debugger — foundational through strategic.
◎ II. Standalone Debuggers¶
Standalone debuggers operate independently of an IDE. They are primarily used for lower-level debugging — systems programming, compiled binaries, and environments where an IDE is not available or appropriate.
| Tool | Language / Platform | Notes |
|---|---|---|
| GDB (GNU Debugger) | C, C++, and other compiled languages | Command-line interface; widely available on Unix/Linux systems; supports remote debugging |
| LLDB | C, C++, Swift, Objective-C | Modern alternative to GDB; default debugger in Xcode; also available independently |
| WinDbg | Windows (kernel and user mode) | Used for advanced Windows debugging including crash dump analysis |
| rr | C, C++ on Linux | Time-travel debugging — records execution and allows replaying backward; powerful for intermittent bugs |
When standalone debuggers are relevant
For most students and junior developers working in Java, Kotlin, C#, JavaScript, or TypeScript, an IDE debugger is sufficient. Standalone debuggers become relevant when working with C or C++ at a systems level, or when investigating crash dumps from production systems. They are worth being aware of — and the concepts (breakpoints, stepping, stack inspection) transfer directly from IDE debuggers.
◎ III. Logging Utilities¶
In production environments, an interactive debugger is rarely available. Logs are the primary — and sometimes only — evidence of what happened.
Development Logging¶
During development, logging libraries provide structured output with severity levels, timestamps, and context. These are the tools that replace System.out.println() in production code.
| Platform | Library | Notes |
|---|---|---|
| Java | SLF4J + Logback, Log4j2 | SLF4J provides a standard API; Logback and Log4j2 are implementations |
| C# / .NET | Microsoft.Extensions.Logging, NLog, Serilog | ILogger abstraction built into .NET; Serilog supports structured logging |
| Kotlin / Android | android.util.Log |
Built in; filter by tag and level in Logcat |
| JavaScript / Node.js | Winston, Pino, Morgan (HTTP) | Winston is general-purpose; Pino prioritises performance |
| TypeScript | Winston, Pino (same as Node.js) | Type definitions available for both |
| Python | logging (standard library) |
Built in; configurable levels, formatters, and handlers |
Production Log Management¶
When applications are deployed, logs from multiple instances, services, and time periods must be collected, stored, and searched.
| Tool / Stack | Purpose |
|---|---|
| ELK Stack (Elasticsearch, Logstash, Kibana) | Collect, transform, store, and visualise logs; widely used in enterprise environments |
| Loki + Grafana | Lightweight log aggregation designed for cloud-native environments |
| Splunk | Enterprise log analysis and monitoring; powerful search and alerting |
| Datadog | Full observability platform; logs, metrics, traces, and dashboards |
| AWS CloudWatch | Log management for applications running on AWS infrastructure |
Production logging is not debugging logging
Debug logs are temporary — written during investigation and removed after. Production logs are permanent — structured, levelled, and maintained as part of the application's architecture. The distinction matters: debug output left in production code creates noise, potentially exposes sensitive information, and signals that investigation output was not cleaned up.
◎ IV. Profilers¶
A profiler measures the runtime behaviour of a program — how much time each method takes, how much memory is allocated, where CPU cycles are spent. Profilers are used when the bug is not a crash or wrong output, but performance: the program is correct but too slow, too memory-hungry, or too resource-intensive.
| Tool | Platform | What it measures |
|---|---|---|
| Android Studio Profiler | Android | CPU, memory, network, energy — all in one tool |
| Visual Studio Diagnostic Tools | C# / .NET | CPU usage, memory, and performance events |
| IntelliJ IDEA Profiler | Java / Kotlin (JVM) | CPU sampling, memory allocation, thread activity |
| YourKit | Java, .NET | Detailed CPU and memory profiling; commercial tool |
| Chrome DevTools (Performance tab) | JavaScript | JavaScript execution timeline, rendering, memory |
| Node.js built-in profiler | Node.js | CPU profiling via --prof flag |
| Valgrind | C, C++ | Memory error detection — leaks, invalid reads/writes |
| Instruments (Xcode) | iOS, macOS | CPU, memory, energy, and disk profiling |
When to use a profiler:
- The application is slower than expected for a given input size
- Memory usage grows over time without an obvious cause (suspected memory leak)
- A specific operation takes disproportionately long compared to others
- The application uses more battery or CPU than expected on mobile
Profile before optimising
The most common mistake in performance debugging is optimising the wrong thing. Without a profiler, it is very difficult to know where the actual bottleneck is — and programmers' intuition about which code is slow is frequently wrong. Profile first, then optimise the thing the profiler identifies.
◎ V. Static Analysis Tools¶
Static analysis examines source code without running it. It catches a specific class of bugs — those that can be identified by analysing the code's structure, types, and patterns — before any test or execution.
What static analysis finds:
- Potential null dereferences
- Unused variables, methods, or imports
- Unreachable code
- Common security vulnerabilities (injection patterns, hardcoded credentials)
- Type errors (in dynamically typed languages with type checking enabled)
- Complexity warnings (methods that are too long or too deeply nested)
| Tool | Language | Category |
|---|---|---|
| Android Lint | Kotlin / Java (Android) | IDE-integrated; runs automatically during build |
| Detekt | Kotlin | Static analysis and code style |
| SpotBugs | Java | Finds likely bugs by pattern matching |
| SonarQube | Multi-language | Enterprise-grade code quality platform |
| ESLint | JavaScript / TypeScript | Linting and style enforcement |
TypeScript compiler (tsc --strict) |
TypeScript | Type checking as static analysis |
| Pylint / mypy | Python | Linting and optional type checking |
| Checkstyle | Java | Style and formatting rules |
Integrate static analysis into your workflow
Static analysis is most valuable when it runs automatically — on every save, every build, or every pull request — rather than only when you remember to run it manually. Most IDE plugins for the tools above provide real-time feedback as you type.
◎ VI. Network and Protocol Tools¶
For applications that communicate over a network — web applications, mobile apps, APIs, microservices — bugs sometimes originate in the communication layer: the request was malformed, the response was not what was expected, or a connection timed out silently.
| Tool | Purpose |
|---|---|
| Browser DevTools (Network tab) | Inspect HTTP requests and responses in real time; available in all major browsers |
| Postman | Send HTTP requests manually to test API endpoints independently of your application code |
| curl | Command-line HTTP client; useful for scripted API testing and CI environments |
| Wireshark | Capture and inspect raw network traffic at the packet level |
| tcpdump | Command-line packet capture on Unix/Linux systems |
| Charles Proxy / mitmproxy | HTTP/HTTPS proxy that captures and displays traffic from mobile apps and browsers |
When network tools matter:
- An API call returns an unexpected result and you cannot tell whether the request was sent correctly
- A mobile app behaves differently on a real device than on an emulator
- SSL/TLS errors occur and the certificate chain needs to be inspected
- A timeout or connection failure occurs and you need to know whether a request reached the server
◎ VII. Version Control as a Debugging Tool¶
Version control systems — primarily Git — are debugging tools in their own right. They provide a timestamped record of every change, making it possible to identify when a bug was introduced and what changed at that point.
The primary debugging use of Git is git bisect. Full coverage of this technique and other version-control debugging practices is in Professional Debugging Practices.
Continue to: Finding Help →
← Back to: Section Index