Blog Edition 文本附件：保留技术内容，仅适配路径/链接与运行命令。

# Debugger Walkthrough - `grad(sin(x)*x)`

## 【1. 今天解决的问题】

1. 如何在不修改核心源码的情况下观察关键边界？
2. forward nodes 与 backward contributions 的实际顺序是什么？
3. 当前 commit 的断点应该设在哪里？

## 【2. Mental Model】

源码级 debugger 不用盯住每个 NumPy 内部调用。最有信息量的边界是 API seed、trace start、primitive wrapper、VJPNode construction、backward traversal 与 accumulation。

## 【3. 数学】

```text
f(x)=sin(x)*x
f'(x)=sin(x)+x*cos(x)
x=2 -> 0.077003753731
```

direct multiply path contribution 是 `sin(2)=0.909297...`；sin path 的 multiply upstream 是 `2`，再乘 `cos(2)` 得 `-0.832293...`，汇合为 `0.077003...`。

## 【4. 极简例子】

[EXPERIMENT]
File: experiments/debug_walkthrough.py  
Purpose: 自动记录 sin 与 multiply 的真实源码调用边界。

```python
def f(x):
    return np.sin(x) * x
```

这是实验中的原函数；main 调用 `grad(f)(2.0)`，返回值与解析导数核对。输入在 trace 中被装箱，函数输出经过真实 backward 得到下面的贡献记录。

## 【5. Autograd 中对应哪里】

当前 commit 的建议断点：

| File | Symbol / 当前入口行 | 观察 locals |
| --- | --- | --- |
| `differential_operators.py` | `grad`, 24 | `fun,x,vjp,ans` |
| `core.py` | `make_vjp`, 11 | `start_node,end_value,end_node` |
| `tracer.py` | `trace`, 14 | `t,start_box,end_box` |
| `tracer.py` | primitive inner `f_wrapped`, 52 | `boxed_args,argvals,parents,ans,node` |
| `core.py` | `VJPNode.__init__`, 39 | `fun,parent_argnums,parents,vjpmaker` |
| `core.py` | `backward_pass`, 26 | `g,node,outgrad,ingrads,outgrads` |
| `core.py` | `add_outgrads`, 185 | `prev_g_flagged,g` |

symbol name 优先于固定行号；切换 commit 后应重新搜索。

## 【6. 调用链】

```text
grad -> make_vjp -> trace
  -> sin primitive -> sin VJPNode
  -> multiply primitive -> multiply VJPNode
backward_pass(seed=1)
  -> multiply VJP: sin node gets 2; root gets sin(2)
  -> sin VJP: root gets 2*cos(2)
  -> add_outgrads merges root contributions
```

## 【7. 关键源码】

`experiments/debug_walkthrough.py` 使用标准库 `sys.settrace` 模拟一组自动化源码断点，只记录上述函数的 call/return；它不 monkeypatch 或修改仓库文件。实测捕获到 `sin`/`multiply` 的 `VJPNode.__init__`，以及三次 `add_outgrads`。

最后两次与 root 有关的累加：

```text
root first receives 0.9092974268256817
root then accumulates 2*cos(2)
result becomes 0.0770037537313969
```

## 【8. 实验】

运行：

```powershell
python experiments/debug_walkthrough.py
```

结果依序包含 `grad`, `make_vjp`, `trace`, `new_box`, primitive wrappers, `VJPNode.__init__`, `backward_pass`, `add_outgrads`，最终断言通过。

## 【9. 理论 ↔ 源码映射】

| 要观察的数学量 | debugger locals |
| --- | --- |
| forward x/ans | `x`, `argvals`, `ans`, Box `_value` |
| graph parent | `parents`, node `_node` |
| output adjoint | `backward_pass.g` |
| local contributions | `ingrads` |
| accumulated adjoint | `outgrads`, `add_outgrads` result |

## 【10. Checkpoint】

1. multiply node 为什么先给 sin node 一个 upstream `2`？
2. root 的 `0.909...` 与 `-0.832...` 分别来自哪条路径？
3. debugger 中看到两个 primitive wrapper call/return 层次的原因是什么？

walkthrough 已执行；后续手动调试可使用同一 symbol 表复现。
