tool-specific.md 24 KB

Tool-Specific Debugging Reference

Detailed guides for debugging tools across languages and environments.

Browser DevTools

Elements Panel

Inspect and modify the DOM and CSS in real-time.

Key Operations:
├─ Right-click element → Inspect (jump to element in DOM tree)
├─ Ctrl+Shift+C: Pick element mode (click any element to inspect)
├─ Edit HTML: Double-click tag name, attribute, or text content
├─ Force element state: Right-click → Force state → :hover, :active, :focus
├─ Break on DOM changes: Right-click element → Break on → subtree/attribute/removal
└─ Computed tab: See final computed CSS values and which rule wins

CSS debugging workflow:

1. Inspect the element
2. Check Computed tab for the actual value
3. Look for strikethrough rules (overridden)
4. Check for specificity conflicts
5. Toggle rules on/off with checkboxes
6. Use the color picker, shadow editor, easing editor for visual tweaking
7. Changes persist until page reload (or use Overrides for persistence)

Event listener debugging:

Elements panel → Event Listeners tab
├─ Shows all listeners attached to selected element
├─ Check "Ancestors" to see inherited/delegated listeners
├─ Click filename link to jump to handler source code
├─ "Remove" button to detach a listener for testing
└─ Framework listeners: check "Framework listeners" to unwrap React/Vue

Accessibility inspection:

Elements panel → Accessibility tab
├─ ARIA role and computed properties
├─ Accessible name and description
├─ Keyboard focusability
└─ Color contrast ratio

Console Panel

Essential console methods beyond console.log:

// Structured data display
console.table(arrayOfObjects);               // Tabular format with sorting
console.table(arrayOfObjects, ['name','id']); // Show only specific columns

// Grouping related logs
console.group('Processing order #123');       // Collapsible group (open)
console.groupCollapsed('Details');            // Collapsible group (closed)
console.log('item count:', items.length);
console.groupEnd();
console.groupEnd();

// Timing operations
console.time('fetchUsers');
await fetchUsers();
console.timeEnd('fetchUsers');               // "fetchUsers: 142.3ms"

// Stack traces
console.trace('How did we get here?');       // Prints call stack

// Assertions (only logs on failure)
console.assert(user.age > 0, 'Invalid age:', user.age);

// Counting occurrences
console.count('render');                     // "render: 1", "render: 2", etc.
console.countReset('render');

// Styled output
console.log('%cIMPORTANT', 'color: red; font-size: 20px; font-weight: bold');

// Object with label
console.dir(domElement, { depth: 3 });       // Expandable object tree

Console utilities (only available in DevTools console, not in code):

$0                    // Currently selected element in Elements panel
$_                    // Result of last expression
$('selector')         // document.querySelector shorthand
$$('selector')        // document.querySelectorAll as array
$x('//xpath')         // XPath query

copy(object)          // Copy object as JSON to clipboard
clear()               // Clear the console

// Monitor function calls
monitor(functionName) // Log every call with arguments
unmonitor(functionName)

// Monitor events on an element
monitorEvents($0, 'click')   // Log all click events on selected element
monitorEvents(window, 'resize')
unmonitorEvents($0, 'click')

// Get event listeners for an element
getEventListeners($0)

// Query objects by constructor
queryObjects(Promise)         // Find all Promise instances in heap

Network Panel

Key Features:
├─ Filter by type: XHR, JS, CSS, Img, Media, Font, Doc, WS, Manifest
├─ Filter by text: URL contains, status code, method
├─ Throttling: Simulate slow connections (Slow 3G, Offline, Custom)
├─ Request blocking: Right-click → Block request URL/domain
├─ Replay request: Right-click → Replay XHR (re-sends identical request)
├─ Copy: Right-click → Copy as cURL/fetch/Node.js
└─ HAR export: Right-click → Save all as HAR with content

Debugging API calls:

1. Open Network panel before reproducing the issue
2. Filter by XHR/Fetch to see only API calls
3. Click the failing request:
   - Headers tab: check request method, URL, headers (Authorization, Content-Type)
   - Payload tab: inspect request body
   - Preview tab: formatted response
   - Response tab: raw response
   - Timing tab: DNS lookup, TLS, TTFB, content download breakdown
4. Check for CORS errors:
   - Look for preflight OPTIONS request
   - Check Access-Control-Allow-Origin in response
   - Console will show CORS error message
5. Waterfall: hover over bars to see timing breakdown

Performance Panel

# Recording a performance trace:
1. Open Performance panel
2. Click Record (or Ctrl+E)
3. Perform the action to profile
4. Click Stop
5. Analyze the flame chart

# Key sections:
├─ Summary: pie chart of time spent (Scripting, Rendering, Painting, Idle)
├─ Main: flame chart showing call stack over time
│  ├─ Wide bars = long-running functions
│  ├─ Red corner = long task (>50ms blocks main thread)
│  └─ Click any bar to see source location and timing
├─ Network: requests timeline correlated with execution
├─ Frames: frame rate and dropped frames
└─ Timings: user timing marks and measures

Web Vitals debugging:

// Measure Core Web Vitals in code
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(`${entry.name}: ${entry.startTime.toFixed(0)}ms`);
  }
}).observe({ type: 'largest-contentful-paint', buffered: true });

// Layout shift debugging
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput) {
      console.log('CLS:', entry.value, entry.sources);
    }
  }
}).observe({ type: 'layout-shift', buffered: true });

Memory Panel

Heap snapshot workflow:

Three-Snapshot Technique (finding memory leaks):
1. Take snapshot 1 (baseline)
2. Perform the action suspected of leaking
3. Take snapshot 2
4. Perform the action again
5. Take snapshot 3
6. Select snapshot 3 → "Objects allocated between snapshot 1 and 2"
7. Look for objects that should have been GC'd but weren't

Key views:
├─ Summary: objects grouped by constructor, sorted by retained size
├─ Comparison: diff between two snapshots (delta of object counts)
├─ Containment: object hierarchy from GC roots
└─ Statistics: pie chart of memory by type

Common memory leak patterns:

// Detached DOM nodes (common in SPAs)
// Symptom: "Detached" elements in heap snapshot
// Cause: JS reference to removed DOM node
let cache = [];
document.getElementById('btn').addEventListener('click', () => {
  const el = document.createElement('div');
  document.body.appendChild(el);
  cache.push(el);  // ← Reference keeps element alive even after removal
  document.body.removeChild(el);  // ← Element is "detached" but not GC'd
});

// Fix: remove from cache when element is removed, or use WeakRef

Sources Panel

Breakpoint types:

Line breakpoint:     Click line number gutter
Conditional:         Right-click line → "Add conditional breakpoint"
                     Expression: user.id === 'problem-user'
Logpoint:            Right-click line → "Add logpoint"
                     Expression: 'User:', user.name, 'at', new Date()
DOM breakpoint:      Elements panel → right-click element → Break on
XHR/fetch:           Sources → XHR/fetch Breakpoints → add URL substring
Event listener:      Sources → Event Listener Breakpoints → check events
                     (mouse, keyboard, timer, animation, etc.)
Exception:           Sources → pause on caught/uncaught exceptions toggle

Source maps:

Enable source maps: Settings → Sources → Enable JavaScript/CSS source maps
├─ Maps minified/transpiled code back to original source
├─ Works with TypeScript, Babel, Webpack, etc.
├─ Breakpoints set on original source, not compiled output
└─ Stack traces show original file names and line numbers

Debugging source map issues:
1. Check for //# sourceMappingURL= comment at end of JS file
2. Verify the .map file is accessible (Network panel, check 404)
3. Check source map is valid: JSON.parse(mapContent)

Node.js Debugging

Inspector Protocol

# Start with debugger (pauses at first line)
node --inspect-brk app.js

# Start with debugger (does not pause)
node --inspect app.js

# Custom port
node --inspect=0.0.0.0:9230 app.js

# Then open chrome://inspect in Chrome and click "inspect"

ndb (Enhanced DevTools)

# Install globally
npm install -g ndb

# Debug a script
ndb node app.js

# Debug tests
ndb npm test

# Features over standard DevTools:
# - Child process debugging (workers, clusters)
# - Blackboxing of node_modules by default
# - Edit and run code from within debugger
# - Detect common async issues

clinic.js Suite

# Install
npm install -g clinic

# Detect common performance issues
clinic doctor -- node app.js
# Generates HTML report with recommendations

# Profile async operations (event loop delays)
clinic bubbleprof -- node app.js
# Shows async operation flow diagram

# CPU flame graph
clinic flame -- node app.js
# Interactive flame chart in browser

# Each tool outputs a .clinic/ directory with HTML report
# Open the generated .html file in a browser

0x Flame Graphs

# Install
npm install -g 0x

# Generate flame graph
0x app.js

# With specific arguments
0x -- node --max-old-space-size=4096 app.js

# Output: flamegraph.html
# Wider bars = more time spent in that function
# Search for your code's function names to find hot paths

Node.js Diagnostics

# Trace warnings with stack traces
node --trace-warnings app.js

# Trace deprecations
node --trace-deprecation app.js

# Enable debug logging for specific modules
NODE_DEBUG=http,net node app.js
NODE_DEBUG=stream,fs node app.js

# Heap snapshot on OOM
node --heapsnapshot-near-heap-limit=3 app.js

# Generate diagnostic report
node --report-on-fatalerror app.js
# Or in code: process.report.writeReport()

# Trace garbage collection
node --trace-gc app.js

Python Debugging

pdb Commands

# Launch pdb
python -m pdb script.py

# Or insert breakpoint in code (Python 3.7+)
breakpoint()

# Essential commands:
n (next)          Step over (execute current line, stop at next)
s (step)          Step into (enter function call)
c (continue)      Run until next breakpoint
r (return)        Run until current function returns

p expr            Print expression value
pp expr           Pretty-print expression
l (list)          Show source around current line
ll (longlist)     Show full source of current function
w (where)         Print stack trace (alias: bt)
u (up)            Move up one frame in stack
d (down)          Move down one frame in stack

b 42              Set breakpoint at line 42
b module.py:42    Set breakpoint in specific file
b func            Set breakpoint at function entry
b 42, x > 10     Conditional breakpoint
cl 1              Clear breakpoint number 1
cl                Clear all breakpoints

a (args)          Print arguments of current function
display expr      Display expression value at each stop
undisplay expr    Stop displaying

commands 1        Set commands to run when breakpoint 1 is hit
  p x
  p y
  c
end

interact          Start interactive Python shell with current scope

debugpy (VS Code Remote Debugging)

# In the application code
import debugpy
debugpy.listen(("0.0.0.0", 5678))
print("Waiting for debugger to attach...")
debugpy.wait_for_client()
debugpy.breakpoint()
// VS Code launch.json
{
  "name": "Attach to Remote",
  "type": "debugpy",
  "request": "attach",
  "connect": { "host": "localhost", "port": 5678 }
}

py-spy (Sampling Profiler)

# Install
pip install py-spy

# Profile a running process (no code changes needed)
py-spy top --pid PID

# Generate flame graph
py-spy record -o profile.svg --pid PID

# Profile a command
py-spy record -o profile.svg -- python myapp.py

# Dump all thread stacks (instant, non-invasive)
py-spy dump --pid PID

memray (Memory Profiler)

# Install
pip install memray

# Profile memory usage
memray run script.py

# Generate flame graph of allocations
memray flamegraph output.bin -o memory.html

# Show summary
memray summary output.bin

# Show top allocators
memray stats output.bin

# Live TUI during execution
memray run --live script.py

tracemalloc (Built-in Memory Tracking)

import tracemalloc

tracemalloc.start()

# ... run suspect code ...

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

print("[ Top 10 memory allocations ]")
for stat in top_stats[:10]:
    print(stat)

# Compare two snapshots to find leaks
snapshot1 = tracemalloc.take_snapshot()
# ... run more code ...
snapshot2 = tracemalloc.take_snapshot()
top_stats = snapshot2.compare_to(snapshot1, 'lineno')
for stat in top_stats[:10]:
    print(stat)

Go Debugging

Delve (dlv)

# Debug a program
dlv debug ./cmd/server

# Debug a test
dlv test ./pkg/handler

# Attach to running process
dlv attach PID

# Connect remotely
dlv debug --headless --listen=:2345 --api-version=2 ./cmd/server
# Then in another terminal:
dlv connect localhost:2345

Delve Commands

# Breakpoints
break main.go:42              Set breakpoint at file:line
break mypackage.MyFunction    Set breakpoint at function
cond 1 x > 100                Make breakpoint 1 conditional
on 1 print x                  Execute command when breakpoint 1 hits
clear 1                       Remove breakpoint 1
clearall                      Remove all breakpoints

# Execution
continue (c)                  Run to next breakpoint
next (n)                      Step over
step (s)                      Step into
stepout (so)                  Step out of current function
restart (r)                   Restart program

# Inspection
print (p) variable            Print variable value
display -a variable           Show variable at every stop
set variable = value          Modify variable value
locals                        Show all local variables
args                          Show function arguments
whatis variable               Show type of variable

# Stack
stack (bt)                    Print stack trace
frame N                       Switch to frame N
up                            Move up one frame
down                          Move down one frame

# Goroutines
goroutines                    List all goroutines
goroutine N                   Switch to goroutine N
goroutines -t                 List goroutines with stack traces
goroutine N bt                Stack trace for goroutine N

Go Race Detector

# Run tests with race detector
go test -race ./...

# Run program with race detector
go run -race ./cmd/server

# Build with race detector
go build -race -o server ./cmd/server

# Example output:
# ==================
# WARNING: DATA RACE
# Write at 0x00c0000b4010 by goroutine 7:
#   main.increment()
#       main.go:15 +0x38
# Previous read at 0x00c0000b4010 by goroutine 6:
#   main.printCount()
#       main.go:20 +0x30
# ==================

Go pprof

// Add to your application
import _ "net/http/pprof"

func main() {
    go func() {
        http.ListenAndServe("localhost:6060", nil)
    }()
    // ... rest of application
}
# CPU profile (30 seconds)
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30

# Heap profile
go tool pprof http://localhost:6060/debug/pprof/heap

# Goroutine profile (find leaks/deadlocks)
go tool pprof http://localhost:6060/debug/pprof/goroutine

# Block profile (find contention)
go tool pprof http://localhost:6060/debug/pprof/block

# Inside pprof interactive mode:
(pprof) top              # Show top consumers
(pprof) top -cum         # Top by cumulative time
(pprof) list funcName    # Show annotated source
(pprof) web              # Open flame graph in browser
(pprof) svg > out.svg    # Export as SVG

Rust Debugging

rust-gdb / rust-lldb

# Build with debug info (default for dev profile)
cargo build

# Debug with GDB (Linux)
rust-gdb target/debug/myapp

# Debug with LLDB (macOS)
rust-lldb target/debug/myapp

# Inside GDB:
(gdb) break main           # Breakpoint at main
(gdb) break src/lib.rs:42  # Breakpoint at file:line
(gdb) run                  # Start program
(gdb) next                 # Step over
(gdb) step                 # Step into
(gdb) print variable       # Print value (Rust-aware pretty printing)
(gdb) bt                   # Backtrace
(gdb) info threads          # List threads
(gdb) thread 2             # Switch to thread 2

Backtrace Control

# Short backtrace (usually sufficient)
RUST_BACKTRACE=1 cargo run

# Full backtrace (all frames including stdlib)
RUST_BACKTRACE=full cargo run

# Combine with log levels
RUST_LOG=debug RUST_BACKTRACE=1 cargo run

Miri (Undefined Behavior Detector)

# Install miri
rustup component add miri

# Run tests under miri (detects UB, use-after-free, data races)
cargo miri test

# Run a specific binary
cargo miri run

# Miri detects:
# - Use of uninitialized memory
# - Accessing memory out of bounds
# - Use-after-free
# - Invalid use of unsafe/raw pointers
# - Data races (with -Zmiri-disable-isolation)
# - Memory leaks (with -Zmiri-leak-check)

cargo-flamegraph

# Install
cargo install flamegraph

# Generate flame graph (needs perf on Linux, dtrace on macOS)
cargo flamegraph

# For a specific binary
cargo flamegraph --bin myapp

# For tests
cargo flamegraph --test my_test

# Output: flamegraph.svg (open in browser)

Database Debugging

PostgreSQL

-- Explain query execution plan
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';

-- Verbose explain with all details
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC LIMIT 10;

-- Key things to look for:
-- Seq Scan (full table scan - usually bad on large tables)
-- Nested Loop (can be slow with large datasets)
-- Sort (in-memory vs disk sort)
-- actual time vs estimated time (large difference = stale statistics)

-- Check for slow queries (enable slow query log)
ALTER SYSTEM SET log_min_duration_statement = 100; -- log queries > 100ms
SELECT pg_reload_conf();

-- View currently running queries
SELECT pid, now() - pg_stat_activity.query_start AS duration,
       query, state, wait_event_type, wait_event
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;

-- Kill a stuck query
SELECT pg_cancel_backend(PID);      -- graceful
SELECT pg_terminate_backend(PID);   -- force

-- Check table statistics
SELECT relname, n_live_tup, n_dead_tup,
       last_vacuum, last_autovacuum, last_analyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;

-- Check index usage
SELECT indexrelname, idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC;  -- low scan count = possibly unused index

-- Lock analysis
SELECT blocked_locks.pid AS blocked_pid,
       blocked_activity.usename AS blocked_user,
       blocking_locks.pid AS blocking_pid,
       blocking_activity.usename AS blocking_user,
       blocked_activity.query AS blocked_query,
       blocking_activity.query AS blocking_query
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity
  ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
  ON blocking_locks.locktype = blocked_locks.locktype
  AND blocking_locks.relation = blocked_locks.relation
  AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity
  ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;

-- Connection pool debugging
SELECT count(*), state FROM pg_stat_activity GROUP BY state;
SHOW max_connections;

pg_stat_statements

-- Enable (requires extension)
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- Top queries by total time
SELECT query, calls, total_exec_time, mean_exec_time,
       rows, shared_blks_hit, shared_blks_read
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

-- Top queries by calls (hot queries)
SELECT query, calls, mean_exec_time
FROM pg_stat_statements
ORDER BY calls DESC
LIMIT 20;

-- Reset statistics
SELECT pg_stat_statements_reset();

Network Debugging

curl Verbose Mode

# Show full request/response headers and TLS handshake
curl -v https://api.example.com/endpoint

# Include timing breakdown
curl -w "\n\nDNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTLS: %{time_appconnect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
  -o /dev/null -s https://api.example.com/endpoint

# Follow redirects and show each hop
curl -vL https://example.com/old-path

# Send with specific headers
curl -H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" \
  -d '{"key":"value"}' https://api.example.com/endpoint

# Save response headers
curl -D headers.txt https://api.example.com/endpoint

mitmproxy (HTTPS Interception)

# Start mitmproxy (TUI)
mitmproxy -p 8080

# Start as transparent proxy
mitmproxy --mode transparent

# Filter specific hosts
mitmproxy --intercept "~d api.example.com"

# Record and replay
mitmdump -w traffic.flow        # Record
mitmdump -r traffic.flow -p 8080 # Replay

# Key bindings in mitmproxy TUI:
# f: set filter expression
# i: set intercept filter
# e: edit intercepted request/response
# r: replay request
# z: clear all flows
# ?: help

tcpdump Quick Reference

# Capture HTTP traffic on port 80
sudo tcpdump -i any port 80 -A

# Capture and save to file
sudo tcpdump -i any -w capture.pcap

# Filter by host
sudo tcpdump -i any host api.example.com

# Filter by host and port
sudo tcpdump -i any host 10.0.0.5 and port 5432

# Read saved capture
tcpdump -r capture.pcap

# Show packet contents in hex and ASCII
sudo tcpdump -XX -i any port 443

Docker Debugging

Container Inspection

# View container logs
docker logs CONTAINER --tail 100 -f        # Last 100 lines, follow
docker logs CONTAINER --since 5m            # Last 5 minutes
docker logs CONTAINER 2>&1 | rg "ERROR"    # Filter for errors

# Execute command in running container
docker exec -it CONTAINER /bin/sh           # Shell into container
docker exec CONTAINER cat /etc/hosts        # Run single command
docker exec CONTAINER env                    # Check environment

# Inspect container configuration
docker inspect CONTAINER                     # Full JSON config
docker inspect --format '{{.State.Status}}' CONTAINER
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' CONTAINER

# View resource usage
docker stats CONTAINER                       # Live CPU, memory, I/O
docker top CONTAINER                         # Running processes

# Check health status
docker inspect --format '{{.State.Health.Status}}' CONTAINER
docker inspect --format '{{json .State.Health}}' CONTAINER | jq .

# Copy files from container for inspection
docker cp CONTAINER:/app/logs/error.log ./error.log

# View filesystem changes
docker diff CONTAINER                        # Shows A(added), C(changed), D(deleted)

Debugging Crashed Containers

# Container exited? Check the exit code and logs
docker ps -a --filter "status=exited"
docker logs CONTAINER

# Create image from stopped container for inspection
docker commit CONTAINER debug-image
docker run -it debug-image /bin/sh

# Override entrypoint to debug startup issues
docker run -it --entrypoint /bin/sh IMAGE

# Check what the container was doing
docker inspect CONTAINER --format '{{.State.Error}}'
docker inspect CONTAINER --format '{{.State.OOMKilled}}'
docker inspect CONTAINER --format '{{.State.ExitCode}}'
# Exit codes: 0=success, 1=app error, 137=OOM/SIGKILL, 139=segfault, 143=SIGTERM

Debugging Networking

# Check container networking
docker network ls
docker network inspect bridge
docker exec CONTAINER ping other-container
docker exec CONTAINER nslookup other-service
docker exec CONTAINER curl -v http://other-service:8080/health

# Debug DNS resolution
docker exec CONTAINER cat /etc/resolv.conf

# Check port mapping
docker port CONTAINER

strace in Container

# Run container with SYS_PTRACE capability
docker run --cap-add SYS_PTRACE IMAGE

# strace inside container
docker exec CONTAINER strace -p 1 -f -e trace=network

# nsenter from host (if strace not in container)
PID=$(docker inspect --format '{{.State.Pid}}' CONTAINER)
nsenter -t $PID -n tcpdump -i any -A