debugging-tools
Use this skill when debugging applications using Chrome DevTools, lldb, strace, network tools, or memory profilers. Triggers on Chrome DevTools, debugger, breakpoints, network debugging, memory profiling, strace, ltrace, core dumps, and any task requiring systematic debugging with specialized tools.
devtools debuggingdevtoolsprofilingstracenetworkmemoryWhat is debugging-tools?
Use this skill when debugging applications using Chrome DevTools, lldb, strace, network tools, or memory profilers. Triggers on Chrome DevTools, debugger, breakpoints, network debugging, memory profiling, strace, ltrace, core dumps, and any task requiring systematic debugging with specialized tools.
debugging-tools
debugging-tools is a production-ready AI agent skill for claude-code, gemini-cli, openai-codex. Debugging applications using Chrome DevTools, lldb, strace, network tools, or memory profilers.
Quick Facts
| Field | Value |
|---|---|
| Category | devtools |
| Version | 0.1.0 |
| Platforms | claude-code, gemini-cli, openai-codex |
| License | MIT |
How to Install
- Make sure you have Node.js installed on your machine.
- Run the following command in your terminal:
npx skills add AbsolutelySkilled/AbsolutelySkilled --skill debugging-tools- The debugging-tools skill is now available in your AI coding agent (Claude Code, Gemini CLI, OpenAI Codex, etc.).
Overview
Systematic debugging is a discipline, not a guessing game. This skill covers the principal tools used to diagnose bugs across the full stack - browser front-ends, Node.js servers, native binaries, and the network between them. The underlying mindset is consistent: form a hypothesis, isolate the variable, confirm or refute, then move inward. Tools are instruments; systematic thinking is the method.
Tags
debugging devtools profiling strace network memory
Platforms
- claude-code
- gemini-cli
- openai-codex
Related Skills
Pair debugging-tools with these complementary skills:
Frequently Asked Questions
What is debugging-tools?
Use this skill when debugging applications using Chrome DevTools, lldb, strace, network tools, or memory profilers. Triggers on Chrome DevTools, debugger, breakpoints, network debugging, memory profiling, strace, ltrace, core dumps, and any task requiring systematic debugging with specialized tools.
How do I install debugging-tools?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill debugging-tools in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support debugging-tools?
This skill works with claude-code, gemini-cli, openai-codex. Install it once and use it across any supported AI coding agent.
Maintainers
Generated from AbsolutelySkilled
SKILL.md
Debugging Tools
Systematic debugging is a discipline, not a guessing game. This skill covers the principal tools used to diagnose bugs across the full stack - browser front-ends, Node.js servers, native binaries, and the network between them. The underlying mindset is consistent: form a hypothesis, isolate the variable, confirm or refute, then move inward. Tools are instruments; systematic thinking is the method.
When to use this skill
Trigger this skill when the user:
- Opens Chrome DevTools to investigate a performance, network, or memory problem
- Wants to set breakpoints, step through code, or inspect call stacks
- Needs to debug a Node.js process with
--inspector--inspect-brk - Is tracing system calls with
straceorltraceon Linux/macOS - Needs to find a memory leak using heap snapshots or the Memory tab
- Is capturing or replaying network traffic with
curl,tcpdump, or Wireshark - Is analyzing a core dump or a crash from a native application with
lldb/gdb - Wants to use conditional breakpoints or logpoints instead of
console.logspam
Do NOT trigger this skill for:
- General code review or refactoring (use clean-code or refactoring-patterns)
- CI/CD pipeline failures that are config errors, not runtime bugs
Key principles
Reproduce before debugging - A bug you cannot reproduce reliably cannot be debugged reliably. Before touching any tool, find the minimal set of steps that trigger the problem every time. A flaky reproduction is a second bug to solve.
Binary search the problem space - Never start debugging from line 1. Bisect: is the bug in the frontend or backend? In the request or the response? In the query or the result processing? Each question cuts the search space in half.
git bisectapplies this directly to commit history.Read the error message twice - The first read captures what you expect to see. The second read captures what it actually says. Most debugging time is lost chasing the wrong problem because the error message was skimmed. Copy the exact message. Look up exact error codes.
Check the obvious first - Before reaching for
straceor heap profilers, verify: Is the service running? Are environment variables set? Is the right binary being executed? Is the config pointing to the right database? Exotic tools are for exotic problems.Automate reproduction - Once you can reproduce a bug manually, write a script or test that reproduces it. This prevents regression, speeds up iteration, and becomes the fix's test case. A bug with an automated reproduction is already halfway fixed.
Core concepts
Breakpoints vs logging
console.log debugging is slow and noisy. Breakpoints pause execution at a precise
point and let you inspect the entire state. Use logging when you need a history of
state over time (e.g., a value changing across many requests). Use breakpoints when
you need to inspect a single moment in detail.
Logpoints (Chrome DevTools, VS Code) are a middle ground: they log a value at a
line without pausing execution and without modifying source code. Prefer logpoints
over adding and removing console.log statements.
Call stacks
A call stack is a snapshot of how execution reached the current point. It reads bottom-to-top (oldest frame at bottom). When debugging, always read the full stack, not just the top frame. The top frame is where the error surfaced; the root cause is often several frames down, at the point where your code made an incorrect assumption.
Heap vs stack memory
The stack holds function call frames and local variables. It is fast, bounded,
and automatically managed. Stack overflows (infinite recursion) are immediately fatal.
The heap holds all dynamically allocated objects. Heap memory leaks are slow and
insidious - the process grows until it crashes or becomes unresponsive. Heap profiling
tools (DevTools Memory tab, valgrind, heaptrack) identify objects that accumulate
without being freed.
Syscalls
Every interaction between a process and the OS kernel is a syscall: file reads,
network connections, process creation, memory allocation. strace captures these
calls with arguments and return values. When a program hangs or fails with a cryptic
error, strace often shows exactly which syscall failed and why (e.g.,
ENOENT: no such file or directory on a missing config path).
Network layers
Network bugs live at different layers. HTTP-level bugs (wrong status codes, missing
headers, bad JSON) are visible with curl -v or browser DevTools Network tab.
TCP-level bugs (connections refused, timeouts, RST packets) require tcpdump or
Wireshark. DNS bugs (resolving the wrong IP, NXDOMAIN) are diagnosed with dig
and nslookup.
Common tasks
Profile a slow page with Chrome DevTools Performance tab
- Open DevTools (
F12) > Performance tab - Click Record, perform the slow action, click Stop
- In the Flame Chart, find the widest bars - these are the most expensive calls
- Look for Long Tasks (red corner flags, >50ms on the main thread)
- Identify the function consuming the most self-time vs total-time
Self time = time spent in the function itself
Total time = self time + time in all functions it calledKey areas to check:
- Scripting (yellow) - JS execution, event handlers
- Rendering (purple) - style recalc, layout (reflow)
- Painting (green) - compositing, rasterization
Rule: a layout thrash occurs when JS reads then writes DOM geometry in a loop. Fix by batching reads before writes, or using
requestAnimationFrame.
Find memory leaks with the Memory tab
- Open DevTools > Memory tab
- Take a Heap Snapshot (baseline)
- Perform the action suspected of leaking (e.g., open and close a modal 10x)
- Force GC (trash can icon), then take a second snapshot
- In the second snapshot, select Comparison view
- Sort by # Delta descending - objects with a growing positive delta are leaking
Common leak sources:
- Event listeners added but never removed
- Closures capturing DOM nodes that were removed
- Global variables holding references to large objects
- setInterval / setTimeout callbacks referencing stale stateDebug Node.js with the inspector protocol
# Start with inspector (connects DevTools or VS Code)
node --inspect server.js
# Break immediately on start (useful when the bug is at startup)
node --inspect-brk server.js
# Attach to a running process by PID
kill -USR1 <pid>Then open chrome://inspect in Chrome and click inspect under Remote Target.
Full Chrome DevTools is now connected to the Node process. Set breakpoints in the
Sources panel, use the Console to evaluate expressions in any stack frame.
For production processes, prefer --inspect=127.0.0.1:9229 to avoid exposing the
debug port publicly.
Trace syscalls with strace / ltrace
# Trace all syscalls of a new process
strace ./myapp
# Attach to a running process
strace -p <pid>
# Filter to specific syscalls (file operations)
strace -e trace=openat,read,write,close ./myapp
# Timestamp each call and show duration
strace -T -tt ./myapp
# Write output to file (avoids mixing with stderr)
strace -o /tmp/trace.log ./myapp
# ltrace: trace library calls instead of syscalls
ltrace ./myappReading strace output:
openat(AT_FDCWD, "/etc/app.conf", O_RDONLY) = -1 ENOENT (No such file or directory)Format: syscall(args) = return_value [error]. A negative return value with an
error name is a failure. This line shows the app tried to open a config file that
does not exist.
Debug network issues with curl / tcpdump / Wireshark
# Verbose HTTP request - shows headers, TLS handshake info
curl -v https://api.example.com/users
# Show only HTTP response headers
curl -sI https://api.example.com/users
# Time each phase of the request
curl -w "@curl-format.txt" -o /dev/null -s https://api.example.com/users
# curl-format.txt: time_namelookup, time_connect, time_appconnect, time_total
# Capture all traffic on port 443 to a file for Wireshark
tcpdump -i eth0 -w capture.pcap port 443
# Capture HTTP traffic and print to stdout
tcpdump -i eth0 -A port 80
# DNS resolution chain
dig +trace api.example.comFor Wireshark analysis:
- Filter by
httporhttp2for application layer - Use
tcp.analysis.retransmissionto find packet loss - Use
tcp.flags.reset == 1to find unexpected connection resets
Debug crashes with core dumps
# Enable core dumps (Linux - set in /etc/security/limits.conf for persistence)
ulimit -c unlimited
# Run the crashing program
./myapp # produces core or core.<pid>
# Open with lldb (macOS / modern Linux)
lldb ./myapp core
# Open with gdb (Linux)
gdb ./myapp core
# Inside lldb/gdb: key commands
(lldb) bt # print backtrace (call stack at crash)
(lldb) frame 3 # switch to frame 3
(lldb) print ptr # print value of variable 'ptr'
(lldb) info locals # show all local variables in current frame
(lldb) list # show source around current lineA crash in a null dereference will show the offending frame in bt. Navigate to
the frame with frame select N, then inspect variables to find which pointer was
null and why it was never initialized.
Use conditional breakpoints and logpoints
Conditional breakpoint - pauses only when an expression is true:
In Chrome DevTools: right-click a line number > Add conditional breakpoint
// Only pause when userId is the problematic one
userId === 'abc-123'In VS Code launch.json:
{
"condition": "i > 100 && items[i] === null"
}Logpoint - logs a message without pausing (non-intrusive, no source changes):
In Chrome DevTools: right-click a line number > Add logpoint
User {userId} called checkout with {items.length} itemsIn VS Code: right-click breakpoint > Edit Breakpoint > select Log Message
Use conditional breakpoints when iterating over large collections and the bug only manifests for a specific element. Use logpoints when you need time-series data across many invocations.
Anti-patterns / common mistakes
| Mistake | Why it's wrong | What to do instead |
|---|---|---|
console.log driven development |
Clutters output, requires code changes, leaves logs in production | Use logpoints or structured logging with debug levels |
| Debugging on production | Modifying production state to understand a bug risks data corruption and outages | Reproduce locally or in staging; use read-only observation tools (strace -p) |
| Fixing without understanding | Changing code until tests pass without knowing root cause leads to the same bug resurfacing in a different form | State the hypothesis in writing before making any change |
| Ignoring the call stack | Looking only at the top frame of an exception misses the call path that created the bad state | Always read the full stack; the root cause is usually 3-5 frames down |
| Heap snapshot without baseline | Comparing one snapshot gives no signal - you cannot tell what grew | Always take a baseline snapshot before the action under test |
Running strace on production without -o |
strace output mixed with the program's stderr and interleaved in logs | Always use strace -o /tmp/trace.log to isolate output |
Gotchas
stracesignificantly slows the traced process - On production systems, attachingstracecan reduce throughput by 50-80%. Use-e trace=filters to limit syscalls and-o fileto write output off stderr. Never attach to a high-traffic process without understanding the performance impact.Chrome DevTools heap snapshots don't capture the GC root path automatically - The Comparison view shows object counts but you must manually navigate to the Retainers pane to find which object is keeping a reference alive. Without checking retainers, you know something is leaking but not why.
node --inspectexposes the debug port on0.0.0.0by default in some environments - If running in a container or VM, the port is accessible from outside. Always use--inspect=127.0.0.1:9229to bind only to localhost.--inspect-brkbreaks on the first line of Node.js internals, not your code - After attaching DevTools, you need to click "Resume" once to get past the internal breakpoint and land in your application code. Many people miss this and think the debugger is broken.Core dumps require debug symbols to be useful - A stripped production binary produces a backtrace with
??instead of function names. Build with-gor retain a separate debug symbol file (.dSYMon macOS,.debugon Linux) thatlldb/gdbcan load alongside the binary.
References
For detailed command references, read the relevant file from references/:
references/tool-guide.md- Quick reference for each debugging tool with key commands
Only load the references file when you need the full command reference for a specific tool and the task at hand requires precise flag-level detail.
References
tool-guide.md
Debugging Tool Quick Reference
Chrome DevTools
Opening DevTools
| Action | Shortcut |
|---|---|
| Open DevTools | F12 or Cmd+Option+I |
| Open Console | Cmd+Option+J |
| Open Sources | Cmd+Option+P then type file name |
| Toggle device mode | Cmd+Shift+M |
Console
console.log('%o', obj) // interactive object inspector
console.table(arrayOfObjects) // tabular display
console.time('label') // start timer
console.timeEnd('label') // stop and print elapsed
console.trace() // print current call stack
console.group('name') // collapsible group start
console.groupEnd()Sources panel - breakpoints
| Action | How |
|---|---|
| Add breakpoint | Click line number |
| Conditional breakpoint | Right-click line > Add conditional breakpoint |
| Logpoint | Right-click line > Add logpoint |
| DOM breakpoint | Elements panel > right-click node > Break on... |
| XHR breakpoint | Sources > XHR/fetch Breakpoints > + |
| Event listener breakpoint | Sources > Event Listener Breakpoints |
Stepping controls
| Key | Action |
|---|---|
F8 |
Resume / pause |
F10 |
Step over (next line) |
F11 |
Step into (enter function) |
Shift+F11 |
Step out (exit current function) |
Performance tab workflow
- Record > perform action > Stop
- Flame chart - x axis = time, y axis = call depth
- Summary pie: Scripting / Rendering / Painting / Other / Idle
- Bottom-Up: sort by Self Time to find hot functions
- Call Tree: find cumulative time per function subtree
- Long Tasks filter:
> 50mstasks flagged with red corner
Memory tab - heap snapshot
- Heap snapshot - point-in-time object graph
- Allocation instrumentation on timeline - records every allocation over time
- Allocation sampling - low-overhead statistical profiler
Snapshot views:
- Summary - objects grouped by constructor
- Comparison - delta between two snapshots (sort by
# Delta) - Containment - full object graph from roots
- Statistics - memory breakdown by category
Network tab
| Column | Meaning |
|---|---|
| Waterfall | Timeline of each request |
| Time | Total duration |
| Size | Transfer size / decoded size |
| Initiator | What triggered the request |
Key filters: XHR, JS, CSS, Img, WS (WebSocket), Fetch
Throttle presets: Fast 3G, Slow 3G, Offline
Node.js Inspector
node --inspect server.js # listen on 127.0.0.1:9229
node --inspect=0.0.0.0:9229 server.js # listen on all interfaces (dev only)
node --inspect-brk server.js # break before first line
# Attach to running process
kill -USR1 <pid> # Linux/macOSConnect via chrome://inspect > Remote Target > inspect.
VS Code launch.json for attach:
{
"type": "node",
"request": "attach",
"name": "Attach to Node",
"port": 9229,
"restart": true,
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app"
}strace
# Basic usage
strace command [args] # trace new process
strace -p <pid> # attach to running process
strace -f command # follow forks (child processes too)
# Output control
strace -o /tmp/out.log command # write to file
strace -e trace=network command # filter: network syscalls only
strace -e trace=openat,read command # filter: specific syscalls
strace -e trace=file command # filter: all file-related
strace -e trace=process command # filter: process lifecycle
# Timing
strace -T command # show syscall duration
strace -tt command # absolute timestamps (microseconds)
strace -ttt command # epoch time
# Common filters
-e trace=network # socket, connect, send, recv, ...
-e trace=file # open, openat, stat, access, ...
-e trace=ipc # mmap, mprotect, ...
-e trace=signal # kill, sigaction, ...
-e trace=desc # file descriptor ops: read, write, close, ...Reading output format:
syscall(arg1, arg2, ...) = retval [errno string]- Return
>= 0= success (often fd number or bytes count) - Return
-1= failure; error name and description follow +++ exited with 1 +++= process exited with code 1
ltrace
ltrace ./myapp # trace shared library calls
ltrace -p <pid> # attach to running process
ltrace -e malloc+free ./myapp # filter to specific functions
ltrace -c ./myapp # summary: count, time, function nameUse ltrace when you need to see calls to C library functions (malloc, fopen,
strcmp) rather than raw syscalls.
lldb
# Start with core dump
lldb ./myapp core
# Start and run
lldb ./myapp
(lldb) run [args]
# Attach to running process
lldb -p <pid>Key commands
| Command | Description |
|---|---|
bt |
Backtrace (full call stack) |
bt 20 |
Show top 20 frames |
frame select 3 |
Switch to frame 3 |
frame variable |
Show local variables in current frame |
print expr |
Evaluate and print expression |
po obj |
Print Objective-C / Swift object description |
memory read 0xaddr |
Dump memory at address |
watchpoint set variable x |
Break when x changes |
breakpoint set -n funcname |
Set breakpoint by function name |
breakpoint set -f file.c -l 42 |
Set breakpoint at file:line |
continue |
Resume execution |
step |
Step into |
next |
Step over |
finish |
Step out |
quit |
Exit lldb |
Reading a backtrace
frame #0: 0x00007fff libsystem_kernel.dylib`__pthread_kill
frame #1: 0x00007fff libsystem_pthread.dylib`pthread_kill
frame #2: 0x00007fff libsystem_c.dylib`abort
frame #3: 0x0000000100003a12 myapp`handle_request + 142 at server.c:87
frame #4: 0x000000010000290f myapp`main + 63 at main.c:12Read bottom-to-top for call order. Frame 3 is where your code called abort. Frame
4 is the caller. + 142 is the byte offset into the function; the source location
follows.
gdb
gdb ./myapp core # open core dump
gdb -p <pid> # attach to process| Command | Description |
|---|---|
bt |
Backtrace |
frame N |
Select frame N |
info locals |
Local variables |
print expr |
Print expression |
list |
Show source around current line |
break file.c:42 |
Set breakpoint |
watch var |
Watchpoint on variable |
continue |
Resume |
step |
Step into |
next |
Step over |
finish |
Step out |
quit |
Exit |
curl
# Verbose - shows request and response headers
curl -v https://api.example.com/path
# Show only response headers
curl -sI https://api.example.com/path
# POST with JSON body
curl -X POST https://api.example.com/path \
-H "Content-Type: application/json" \
-d '{"key": "value"}'
# Follow redirects, show final URL
curl -L -w "%{url_effective}\n" -o /dev/null -s https://short.url/x
# Time each phase (save format file first)
curl -w "dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} total:%{time_total}\n" \
-o /dev/null -s https://api.example.com/path
# Use specific DNS resolver (bypass system DNS)
curl --dns-servers 8.8.8.8 https://api.example.com/path
# Ignore TLS errors (testing only, never in production)
curl -k https://api.example.com/pathtcpdump
# Capture on all interfaces, save to file
tcpdump -i any -w /tmp/capture.pcap
# Capture on specific interface, port filter
tcpdump -i eth0 port 443 -w /tmp/tls.pcap
# Print HTTP traffic to stdout (ASCII)
tcpdump -i eth0 -A port 80
# Filter by host
tcpdump host api.example.com
# Combined filter
tcpdump -i eth0 host api.example.com and port 443 -w /tmp/out.pcap
# Read a saved capture
tcpdump -r /tmp/capture.pcap
# Don't resolve hostnames (faster, shows IPs)
tcpdump -n -i eth0 port 80Common Wireshark display filters:
http # all HTTP
http.request.method == "POST" # POST requests only
tcp.analysis.retransmission # retransmitted packets (packet loss)
tcp.flags.reset == 1 # TCP RST (connection reset)
dns # all DNS
http.response.code >= 500 # server errorsdig (DNS)
dig api.example.com # A record (IPv4)
dig AAAA api.example.com # IPv6
dig MX example.com # mail servers
dig TXT example.com # TXT records (SPF, DKIM, etc.)
dig +trace api.example.com # full resolution chain from root
dig @8.8.8.8 api.example.com # query specific resolver (Google DNS)
dig +short api.example.com # just the IP addressesgit bisect
git bisect start
git bisect bad # current commit is broken
git bisect good v1.2.0 # this tag was working
# git checks out midpoint commit automatically
# Test it, then tell git the result:
git bisect good # this commit is fine
git bisect bad # this commit is broken
# Repeat until git identifies the first bad commit
# When done:
git bisect reset # return to original HEAD
# Automate with a test script
git bisect run ./test.sh # script exits 0 = good, non-zero = bad Frequently Asked Questions
What is debugging-tools?
Use this skill when debugging applications using Chrome DevTools, lldb, strace, network tools, or memory profilers. Triggers on Chrome DevTools, debugger, breakpoints, network debugging, memory profiling, strace, ltrace, core dumps, and any task requiring systematic debugging with specialized tools.
How do I install debugging-tools?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill debugging-tools in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support debugging-tools?
debugging-tools works with claude-code, gemini-cli, openai-codex. Install it once and use it across any supported AI coding agent.