For the complete documentation index, see llms.txt. This page is also available as Markdown.

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

静态分析关注代码可能如何运行,动态分析关注代码实际如何运行。真实系统里,很多事实只有运行起来之后才能观察到:请求经过哪些服务,哪些路径真正高频,哪个函数消耗 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 的解释,而应该看到与代码路径相关的运行时证据。

小结

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

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

Last updated