> For the complete documentation index, see [llms.txt](https://code-visualization.shawnxie.top/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://code-visualization.shawnxie.top/di-san-pian-cheng-xu-fen-xi-yu-dai-ma-tu-pu/dynamic-analysis.md).

# 动态分析：运行起来之后才能知道什么

静态分析关注代码可能如何运行，动态分析关注代码实际如何运行。真实系统里，很多事实只有运行起来之后才能观察到：请求经过哪些服务，哪些路径真正高频，哪个函数消耗 CPU，哪些测试覆盖了目标代码，哪些异常在生产环境出现。

动态分析不是静态分析的替代品，而是对静态分析的校准。静态分析给出全局可能性，动态分析给出运行时证据。本章讨论几类常见动态数据：日志、Trace、Profile、Runtime Call Graph 和 Coverage。

## 为什么需要动态证据

静态图谱经常会比真实运行路径更“宽”。一个接口可能有多个实现，一个方法可能有很多调用方，一个分支可能永远不会被生产流量触发。如果只看静态关系，影响面可能被放大。

动态数据能回答另一类问题：

* 这条调用路径是否真的发生过。
* 哪个入口触发了目标方法。
* 哪些代码处于核心流量路径。
* 哪些测试执行过目标代码。
* 哪些函数或服务是性能热点。
* 哪些异常和日志与目标代码相关。

在代码可视化中，动态数据可以把“可能影响”进一步分层为“真实发生过”“高频发生”“测试覆盖过”“线上热点”。

## 日志：业务语义和异常上下文

日志是最常见的动态数据。它记录业务事件、状态变化、异常和关键参数。

日志的优势是语义强。例如一条日志可能直接说明订单创建失败、库存扣减异常、支付回调重复、用户权限不足。它能补充静态代码看不出来的业务上下文。

日志的局限也明显：

* 埋点不系统。
* 格式不统一。
* 日志级别混乱。
* 关键字段缺失。
* 过多日志造成噪声。

如果要把日志纳入代码图谱，需要建立日志语句与源码位置、请求 Trace、业务事件之间的关系。这样排查问题时，才能从一条异常日志跳回相关代码和 owner。

## Trace 与 Span：请求路径

Trace 用来描述一次请求或任务在系统中的传播路径。一个 Trace 通常包含多个 Span，每个 Span 表示一次操作，例如 HTTP 调用、数据库查询、缓存访问、消息处理或内部函数执行。

Trace 可以回答：

* 一个请求经过哪些服务。
* 每个阶段耗时多少。
* 哪个下游调用失败。
* 一个接口实际依赖哪些资源。
* 入口和底层代码之间的真实路径是什么。

对代码可视化来说，Trace 是连接“运行时路径”和“静态代码图谱”的关键证据。静态调用图可以告诉我们可能路径，Trace 可以告诉我们真实路径和耗时。

在微服务系统中，这一点尤其重要。源码级调用图只能覆盖单个仓库或进程内调用，Trace 可以跨服务、跨语言、跨资源展示请求传播。

## Profile 与火焰图：性能热点

Profile 关注资源消耗，例如 CPU 时间、内存分配、锁等待、I/O 等。火焰图是 Profile 数据的常见可视化方式，它把调用栈聚合成宽度代表耗时或采样次数的图形。

性能分析不能只看代码复杂度。一个复杂函数如果很少执行，未必是性能瓶颈；一个简单函数如果出现在高频路径上，可能占用大量资源。

Profile 可以回答：

* CPU 时间主要消耗在哪些函数。
* 哪些调用栈最常出现。
* 哪些方法分配了大量内存。
* 优化后热点是否下降。

当 Profile 和代码图谱结合时，可以把性能热点映射回文件、方法、模块和 owner。这样性能问题不再只是运行时图表，而能进入代码治理流程。

## Runtime Call Graph：运行时调用关系

静态调用图来自代码分析，Runtime Call Graph 来自运行时观察。它可能来自 Trace、采样、插桩或 APM 数据。

运行时调用图更接近真实执行，但通常覆盖不完整。它只记录发生过的路径，未被流量或测试触达的路径不会出现。

因此，两种调用图应该结合：

* 静态调用图提供全量候选路径。
* 运行时调用图标记真实发生路径。
* 高频路径可以提高风险等级。
* 从未运行的路径需要结合测试和业务判断。

## Coverage：测试触达证据

Coverage 描述测试执行时触达了哪些代码。常见粒度包括行覆盖、分支覆盖、方法覆盖和路径覆盖。

Coverage 可以帮助回答：

* 哪些测试触达了目标方法。
* 修改代码后应该优先运行哪些测试。
* 哪些影响路径缺少覆盖。
* 新增测试是否覆盖了关键分支。

但 Coverage 不等于正确性。代码被执行过，不代表断言充分；覆盖率高，不代表边界条件完整；覆盖率低，也不一定说明代码没有任何测试价值。

在影响面分析中，Coverage 更适合作为“相关性信号”，而不是质量的唯一证明。

## 动态数据的采样和偏差

动态分析也有误差：

* Trace 可能采样，不是每个请求都记录。
* 测试 Coverage 只代表测试环境。
* Profile 受采样频率和运行负载影响。
* 日志质量依赖开发者埋点。
* 线上流量可能没有覆盖低频但关键路径。

因此，动态数据也需要标注来源、时间范围、环境和采样策略。一个来自生产环境过去 7 天的 Trace，和一次本地测试产生的 Trace，证据强度不同。

## 动态数据如何进入代码图谱

动态数据可以转化为代码图谱的属性和边：

* 方法节点增加执行频率、平均耗时、错误率。
* 调用边增加运行时观察次数和耗时分布。
* 测试节点通过 Coverage 连接到代码节点。
* Trace 路径连接服务、接口、方法和资源。
* 日志事件连接源码位置和异常类型。

这样，图谱就不只是静态结构图，而是带有运行时证据的软件事实库。

## 和 AI Review 的关系

AI 修改代码后，动态证据可以回答：

* 受影响路径是否有测试覆盖。
* 这段代码是否处于高频生产路径。
* 相关 Trace 是否显示下游依赖复杂。
* 修改前后性能指标是否变化。
* 是否有新异常日志出现。

这些证据可以进入 AI 生成代码的 Review 报告。人类 Reviewer 不应该只看 AI 的解释，而应该看到与代码路径相关的运行时证据。

## 小结

动态分析让我们看到代码真实运行时的路径、频率、耗时、异常和测试触达情况。它弥补了静态分析的抽象和不确定性。

一个可靠的代码理解系统需要同时使用静态和动态数据：静态分析负责全局结构，动态分析负责真实证据。下一章会引入第三类数据：变更历史。它回答系统是如何演进的，以及风险如何随时间积累。
