debugging
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDebugging
调试
This skill equips an AI agent with a systematic methodology for diagnosing and resolving software bugs. Rather than guessing at fixes, the agent follows a structured process — reproduce, isolate, diagnose, fix, verify — to find root causes and produce reliable corrections. It handles a wide range of bug categories including logic errors, runtime exceptions, race conditions, memory leaks, and performance regressions across multiple languages and runtime environments.
此技能为AI Agent提供了一套诊断和解决软件缺陷的系统性方法。Agent不会盲目尝试修复,而是遵循结构化流程——复现、定位、诊断、修复、验证——来找到问题根源并生成可靠的修正方案。它能处理多种类型的缺陷,包括逻辑错误、运行时异常、竞态条件、内存泄漏和性能退化,覆盖多语言及多运行时环境。
Workflow
工作流
-
Reproduce the problem. Confirm the bug is observable and repeatable. Gather the exact error message, stack trace, log output, or description of unexpected behavior. Identify the minimum input or sequence of steps that triggers the issue. If the bug is intermittent, note the frequency and any environmental conditions (load, timing, specific data) that correlate with its appearance.
-
Isolate the fault location. Use the stack trace, error message, and code structure to narrow down the region of code responsible. Trace data flow backward from the point of failure to find where the value diverged from expectations. Eliminate unrelated code paths by checking whether the bug persists when components are stubbed out or bypassed. For large codebases, use binary search strategies — disable half the system, check if the bug still occurs, and repeat.
-
Diagnose the root cause. Once the faulty region is identified, determine exactly why the code misbehaves. Common root causes include: incorrect assumptions about input (null, empty, out-of-range), state mutation from a concurrent thread, stale cache or memoized value, incorrect operator precedence, missing await on an async call, or a dependency version incompatibility. Distinguish the root cause from its symptoms — a NullPointerException is a symptom; the root cause may be a missing validation three function calls earlier.
-
Develop and apply the fix. Write the smallest change that addresses the root cause without introducing side effects. If the fix involves changing a shared interface, trace all callers to ensure compatibility. Prefer defensive fixes that handle the error class broadly (e.g., adding input validation) over narrow patches that only address the single observed failure.
-
Verify the fix and prevent regression. Run the reproduction steps again to confirm the bug is resolved. Write or update a test case that encodes the previously-failing scenario so the bug cannot silently return. Check that existing tests still pass. If the bug was in a critical path, consider adding logging or monitoring to detect similar issues in the future.
-
复现问题。确认缺陷可观察且可重复。收集完整的错误信息、堆栈跟踪、日志输出或异常行为描述。找出触发问题的最小输入或操作序列。若缺陷为间歇性出现,记录其出现频率及相关环境条件(负载、时序、特定数据)。
-
定位故障位置。利用堆栈跟踪、错误信息和代码结构缩小故障代码范围。从失败点反向追踪数据流,找到值偏离预期的位置。通过存根化或绕过组件,检查缺陷是否仍存在,以此排除无关代码路径。对于大型代码库,可使用二分查找策略——禁用一半系统,检查缺陷是否仍出现,重复此过程。
-
诊断根本原因。确定故障区域后,明确代码行为异常的具体原因。常见根本原因包括:对输入的错误假设(空值、空字符串、超出范围)、并发线程导致的状态突变、缓存或记忆值过期、运算符优先级错误、异步调用缺失await、依赖版本不兼容等。需区分根本原因与症状——NullPointerException是症状,根本原因可能是三个调用之前缺失的输入验证。
-
制定并应用修复方案。编写最小化的修改来解决根本原因,且不引入副作用。若修复涉及修改共享接口,需追踪所有调用方以确保兼容性。优先选择能广泛处理错误类型的防御性修复(如添加输入验证),而非仅针对单一观测故障的局部补丁。
-
验证修复并防止回归。再次执行复现步骤,确认缺陷已解决。编写或更新测试用例,将之前失败的场景纳入其中,防止缺陷悄然重现。检查现有测试是否仍能通过。若缺陷位于关键路径,可考虑添加日志或监控以检测未来类似问题。
Supported Technologies
支持的技术
| Category | Tools and Techniques |
|---|---|
| Stack traces | Python tracebacks, Java/JS stack traces, Go panic output, Rust backtraces |
| Logging | Python |
| Debuggers | |
| Profiling | |
| Memory analysis | |
| Concurrency | Thread dumps, |
| 类别 | 工具与技术 |
|---|---|
| 堆栈跟踪(Stack traces) | Python tracebacks, Java/JS stack traces, Go panic output, Rust backtraces |
| 日志(Logging) | Python |
| 调试器(Debuggers) | |
| 性能分析(Profiling) | |
| 内存分析(Memory analysis) | |
| 并发(Concurrency) | 线程转储(Thread dumps), |
Usage
使用方式
Provide one or more of the following inputs:
- Code snippet or file path containing the buggy code.
- Error message or stack trace — paste the full output, not a summary.
- Description of unexpected behavior — what you expected versus what happened.
- Steps to reproduce — the exact commands, inputs, or user actions that trigger the bug.
- Logs — relevant log output from before and during the failure.
The more context you provide, the faster and more accurately the agent can diagnose the issue.
提供以下一项或多项输入:
- 包含缺陷代码的代码片段或文件路径
- 错误信息或堆栈跟踪——粘贴完整输出,而非摘要
- 异常行为描述——预期结果与实际结果的差异
- 复现步骤——触发缺陷的具体命令、输入或用户操作
- 日志——故障发生前后的相关日志输出
提供的上下文越丰富,Agent诊断问题的速度和准确性越高。
Examples
示例
Example 1 — Debugging a race condition in Python async code
示例1——调试Python异步代码中的竞态条件
Bug report: "Sometimes the response is missing the field, but only under load."
user_profileBuggy code in :
src/api/handler.pypython
import asyncio
import aiohttp
user_cache = {}
async def fetch_user(session, user_id):
if user_id in user_cache:
return user_cache[user_id]
async with session.get(f"https://api.example.com/users/{user_id}") as resp:
data = await resp.json()
user_cache[user_id] = data
return data
async def build_response(user_id):
async with aiohttp.ClientSession() as session:
profile, orders = await asyncio.gather(
fetch_user(session, user_id),
fetch_orders(session, user_id),
)
return {"user_profile": profile, "orders": orders}Agent diagnosis:
The dictionary is shared mutable state accessed by concurrent coroutines without synchronization. When two requests for the same arrive simultaneously, both see a cache miss, both issue HTTP requests, and the second write may overwrite the first with a partially different object. More critically, dictionary mutation during concurrent iteration can produce on Python 3.
user_cacheuser_idRuntimeErrorRoot cause: Unsynchronized shared mutable state in a concurrent context.
Fix:
python
import asyncio
import aiohttp
user_cache = {}
cache_locks = {}
global_lock = asyncio.Lock()
async def fetch_user(session, user_id):
async with global_lock:
if user_id not in cache_locks:
cache_locks[user_id] = asyncio.Lock()
lock = cache_locks[user_id]
async with lock:
if user_id in user_cache:
return user_cache[user_id]
async with session.get(f"https://api.example.com/users/{user_id}") as resp:
data = await resp.json()
user_cache[user_id] = data
return dataThe per-key lock ensures only one coroutine fetches a given user at a time. Others wait and then read from the cache.
缺陷报告: "有时响应会缺失字段,但仅在高负载下出现。"
user_profilesrc/api/handler.pypython
import asyncio
import aiohttp
user_cache = {}
async def fetch_user(session, user_id):
if user_id in user_cache:
return user_cache[user_id]
async with session.get(f"https://api.example.com/users/{user_id}") as resp:
data = await resp.json()
user_cache[user_id] = data
return data
async def build_response(user_id):
async with aiohttp.ClientSession() as session:
profile, orders = await asyncio.gather(
fetch_user(session, user_id),
fetch_orders(session, user_id),
)
return {"user_profile": profile, "orders": orders}Agent诊断:
user_cacheuser_idRuntimeError根本原因: 并发环境中未同步的共享可变状态。
修复方案:
python
import asyncio
import aiohttp
user_cache = {}
cache_locks = {}
global_lock = asyncio.Lock()
async def fetch_user(session, user_id):
async with global_lock:
if user_id not in cache_locks:
cache_locks[user_id] = asyncio.Lock()
lock = cache_locks[user_id]
async with lock:
if user_id in user_cache:
return user_cache[user_id]
async with session.get(f"https://api.example.com/users/{user_id}") as resp:
data = await resp.json()
user_cache[user_id] = data
return data按用户ID分配的锁确保同一时间只有一个协程获取指定用户的数据。其他协程会等待,之后从缓存中读取数据。
Example 2 — Debugging a memory leak in a Node.js server
示例2——调试Node.js服务器中的内存泄漏
Bug report: "Server memory usage grows from 150 MB to 2 GB over 24 hours, then crashes with OOM."
Buggy code in :
src/server.jsjavascript
const express = require("express");
const app = express();
const requestLog = [];
app.use((req, res, next) => {
requestLog.push({
method: req.method,
url: req.url,
headers: { ...req.headers },
timestamp: new Date(),
});
next();
});
app.get("/health", (req, res) => {
res.json({ status: "ok", requestCount: requestLog.length });
});
app.listen(3000);Agent diagnosis:
The array is a module-level variable that grows without bound. Every incoming request appends an object containing a full copy of the request headers. At 1000 requests per minute with an average header size of 2 KB, this accumulates roughly 2.8 GB per day — matching the reported OOM timeline.
requestLogRoot cause: Unbounded in-memory array that is never pruned or rotated.
Fix:
javascript
const express = require("express");
const app = express();
const MAX_LOG_SIZE = 10000;
const requestLog = [];
app.use((req, res, next) => {
if (requestLog.length >= MAX_LOG_SIZE) {
requestLog.shift();
}
requestLog.push({
method: req.method,
url: req.url,
timestamp: new Date(),
});
next();
});Key changes: (1) cap the array at a fixed size and evict the oldest entry, (2) stop storing full headers — log only what is needed, (3) for production use, replace the in-memory array with a proper logging pipeline (e.g., write to a log file or send to an external service).
Verification: Run a load test with and monitor memory via . Memory should plateau at the cap size rather than climbing linearly.
autocannon -d 60 http://localhost:3000/healthprocess.memoryUsage()缺陷报告: "服务器内存占用在24小时内从150 MB增长到2 GB,随后因内存不足(OOM)崩溃。"
src/server.jsjavascript
const express = require("express");
const app = express();
const requestLog = [];
app.use((req, res, next) => {
requestLog.push({
method: req.method,
url: req.url,
headers: { ...req.headers },
timestamp: new Date(),
});
next();
});
app.get("/health", (req, res) => {
res.json({ status: "ok", requestCount: requestLog.length });
});
app.listen(3000);Agent诊断:
requestLog根本原因: 未做修剪或轮转的无限制内存数组。
修复方案:
javascript
const express = require("express");
const app = express();
const MAX_LOG_SIZE = 10000;
const requestLog = [];
app.use((req, res, next) => {
if (requestLog.length >= MAX_LOG_SIZE) {
requestLog.shift();
}
requestLog.push({
method: req.method,
url: req.url,
timestamp: new Date(),
});
next();
});关键修改:(1) 将数组大小限制为固定值,移除最旧的条目;(2) 停止存储完整请求头——仅记录必要信息;(3) 生产环境中,将内存数组替换为专业的日志管道(如写入日志文件或发送至外部服务)。
验证: 使用进行负载测试,通过监控内存。内存应稳定在限制大小,而非线性增长。
autocannon -d 60 http://localhost:3000/healthprocess.memoryUsage()Best Practices
最佳实践
- Read the entire stack trace, bottom to top. The root cause is often in the deepest application frame, not the top-level exception. Framework frames can be skipped, but your code frames should be read in order.
- Change one thing at a time. When testing a hypothesis, make a single modification and re-run. Changing multiple things simultaneously makes it impossible to determine which change had the effect.
- Use logging strategically. Insert log statements at the entry and exit of suspect functions, printing key variable values. Remove or reduce log verbosity after the bug is fixed.
- Check recent changes first. If the bug appeared after a specific deployment or commit, or reviewing the recent diff is often the fastest path to the root cause.
git bisect - Reproduce before fixing. Never apply a fix to a bug you cannot reproduce. Without reproduction, you cannot verify the fix works, and you risk introducing a change that masks the symptom without addressing the cause.
- Write a regression test. Every fixed bug should produce a new test case that fails before the fix and passes after. This is the most reliable way to prevent the same bug from returning.
- 完整阅读堆栈跟踪,从下到上。根本原因通常位于最深层的应用框架中,而非顶层异常。可跳过框架层,但需按顺序阅读自己的代码层。
- 每次只修改一处。测试假设时,仅做一处修改后重新运行。同时修改多处会无法确定哪项修改产生了效果。
- 策略性使用日志。在可疑函数的入口和出口插入日志语句,打印关键变量值。缺陷修复后移除或降低日志冗余度。
- 优先检查近期变更。若缺陷在特定部署或提交后出现,或查看近期差异通常是找到根本原因的最快方式。
git bisect - 修复前先复现。切勿对无法复现的缺陷应用修复。没有复现,就无法验证修复是否有效,还可能引入仅掩盖症状而未解决根本原因的变更。
- 编写回归测试。每个修复的缺陷都应生成新的测试用例,该用例在修复前失败,修复后通过。这是防止同一缺陷重现的最可靠方式。
Edge Cases
边缘情况
- Heisenbugs: Some bugs disappear when debugging tools are attached (e.g., timing changes from breakpoints mask race conditions). For these, use logging or tracing instead of interactive debuggers, and consider running with the language's race detector if available.
- Environment-specific bugs: A bug that only appears in production may depend on OS version, memory limits, network latency, or configuration that differs from development. The agent will ask for environment details and suggest reproducing with matching constraints (e.g., Docker with memory limits).
- Third-party library bugs: If the root cause is in a dependency rather than application code, the fix may involve upgrading the library, applying a workaround, or pinning a known-good version. The agent will check changelogs and issue trackers before recommending a path.
- Compiler or runtime bugs: Rarely, the bug is in the language runtime itself. The agent will exhaust application-level explanations first, then suggest testing on a different runtime version if no application-level cause is found.
- Corrupted state: If the bug involves corrupted data (e.g., a half-written database row), diagnosis requires examining the data alongside the code. The agent will ask for sample data or database state to correlate with the code path analysis.
- 海森堡缺陷(Heisenbugs):部分缺陷在附加调试工具后会消失(如断点导致的时序变化掩盖了竞态条件)。对于此类缺陷,应使用日志或追踪而非交互式调试器,若可用,可考虑使用语言的竞态检测器。
- 环境特定缺陷:仅在生产环境出现的缺陷可能依赖于与开发环境不同的OS版本、内存限制、网络延迟或配置。Agent会询问环境细节,并建议在匹配约束的条件下复现(如设置内存限制的Docker)。
- 第三方库缺陷:若根本原因在依赖库而非应用代码中,修复方案可能包括升级库、应用变通方法或固定已知可用版本。Agent会先检查变更日志和问题追踪器,再推荐解决方案。
- 编译器或运行时缺陷:极少数情况下,缺陷源于语言运行时本身。Agent会先排除应用层面的原因,若未找到,会建议在不同版本的运行时上测试。
- 状态损坏:若缺陷涉及损坏的数据(如半写入的数据库行),诊断需要结合数据与代码分析。Agent会请求样本数据或数据库状态,以与代码路径分析关联。