进行静态分析时,动态分析有什么用?

为了验证软件的质量,您必须使用许多不同的工具,包括静态和动态分析器。 在本文中,我们将尝试找出为什么仅一种类型的分析(静态或动态)可能不足以进行全面的软件分析,以及为什么最好同时使用这两种类型。

图1


我们的团队撰写了大量有关静态分析的有用性及其为您的项目带来的好处的文章。 我们喜欢在各种开源项目上运行我们的工具,以发现可能的错误,这是我们推广静态代码分析方法的方式。 反过来,静态分析有助于使程序更优质,更可靠,并减少潜在漏洞的数量。 也许每个直接从事源代码工作的人都对修复错误感到满意。 但是,即使成功发现(和修复)错误的过程没有触发您的内啡肽,您也一定会喜欢使用静态分析器来减少开发费用的想法,这有助于您的程序员更有效地利用他们的时间。 要了解更多有关如何从金钱方面受益于使用静态分析的信息,请参阅本文 。 它提供了PVS-Studio的近似估算值,但是这些结果可以推断到市场上可用的其他静态分析工具中。

上面所有这些似乎暗示了静态分析的目的是尽早发现源代码中的错误,从而减少了错误修复的费用。 但是,为什么我们需要动态分析,为什么仅坚持两种技术之一可能不够用呢? 让我们给出静态和动态分析的更正式,更清晰的定义,并尝试回答这些问题。

静态代码分析是检测软件源代码中的错误和代码味道的过程。 要分析程序,您无需执行它。 分析将在可用的代码库上进行。 与静态分析最接近的类比是所谓的代码审查,除了静态分析是代码审查的自动版本(即由bot程序执行)。

静态分析的主要优点:

  1. 在开发的早期阶段进行错误检测。 这有助于使错误修复便宜得多,因为尽早检测到缺陷,可以更轻松地(因此也更便宜)进行修复。
  2. 它使您可以在源代码中精确定位潜在的错误。
  3. 完整的代码覆盖率。 无论一个代码块或另一个代码块在执行时得到控制的频率如何,静态分析都会检查整个代码库。
  4. 易于使用。 您无需准备任何输入数据集即可进行检查。
  5. 静态分析仪可以相当快速,轻松地检测到打字错误和与复制粘贴相关的错误。

静态分析的客观缺点:

  1. 不可避免的误报。 静态分析器可能会对实际上没有任何错误的代码片段感到生气。 只有程序员才能解决此问题,并将警告标记为误报,这意味着这将花费他们一些工作时间。
  2. 静态分析通常不利于检测内存泄漏和与并发相关的错误。 要检测此类错误,实际上您必须在虚拟模式下执行程序的某些部分,这是一项极其困难的任务。 此外,此类算法将需要过多的内存和CPU时间。 静态分析器通常不会比分析一些简单的情况更复杂。 动态分析器更适合诊断内存泄漏和与并发相关的错误。

应该注意的是,静态分析器并不仅专注于错误捕获。 例如,他们可以提供有关代码格式的建议。 一些工具使您可以检查代码是否符合公司所遵循的编码标准。 这包括各种结构的缩进,空格/制表符的使用等。 此外,静态分析可能有助于度量指标。 软件度量标准是对程序或其规范具有某种性质的程度的定量度量。 请参阅本文以了解静态分析的其他用途。

动态代码分析是在执行时对程序执行的分析。 这意味着您必须先将源代码转换为可执行文件。 换句话说,这种类型的分析无法检查包含编译或生成错误的代码。 该检查是通过将一组输入数据馈送到要分析的程序来完成的。 因此,动态分析的有效性直接取决于测试输入数据的质量和数量。 正是这些数据确定了测试结束时的代码覆盖范围。

通过动态测试,您可以获得以下指标和警告:

  1. 使用的资源:整个程序或其各个部分的执行时间,外部查询的数量(例如,对数据库的查询),RAM的数量以及程序使用的其他资源。
  2. 测试和其他指标的代码覆盖范围。
  3. 软件错误:被零除,空取消引用,内存泄漏,竞争条件。
  4. 一些安全漏洞。

动态分析的主要优点:

  1. 您不必访问程序的源代码即可对其进行分析。 但是,应注意,动态分析工具的区别在于它们与被分析程序的交互方式(在此将进行更详细的讨论)。 例如,一种非常普遍的动态分析技术涉及在检查之前进行代码检测,即在应用程序的源代码中添加特殊代码片段,以使分析器能够诊断错误。 在这种情况下,您确实需要手头程序的源代码。
  2. 它可以检测复杂的内存处理错误,例如超出数组范围的索引编制和内存泄漏。
  3. 它可以在执行时分析多线程代码,从而检测与共享资源访问或可能出现的死锁有关的潜在问题。
  4. 动态分析器的大多数实现不会产生误报,因为错误会在错误发生时被捕获。 因此,由动态分析器发出的警告不是工具根据程序模型的分析做出的预测,而仅仅是对已发生错误这一事实的陈述。

动态分析的缺点:

  1. 不能保证完整的代码覆盖率。 也就是说,您不太可能通过动态测试获得100%的覆盖率。
  2. 动态分析器不善于检测逻辑错误。 例如,从动态分析器的角度来看,始终为真的条件不是错误,因为这种不正确的检查只会在编译步骤的较早时候消失。
  3. 在代码中精确定位错误更加困难。
  4. 与静态分析相比,动态分析更难使用,因为您需要将足够的数据馈入程序以获得更好的结果并尽可能地覆盖全部代码。

在程序可靠性,响应时间或消耗的资源是主要问题的那些领域中,动态分析特别有用。 管理关键生产部门或数据库服务器的实时系统是此类系统的一些示例。 这些方面的任何错误都可能是至关重要的。

回到为什么仅坚持两种分析中的一种可能还不够的问题,让我们看几个错误的例子,一种错误的诊断方法没有问题,而另一种却不适合检测,反之亦然。

以下示例来自Clang项目:

MapTy PerPtrTopDown; MapTy PerPtrBottomUp; void clearBottomUpPointers() { PerPtrTopDown.clear(); } void clearTopDownPointers() { PerPtrTopDown.clear(); } 

静态分析器将指出这两个函数的主体是相同的。 当然,具有相同主体的两个函数不一定是错误的明确标志,但是它们很可能是由于使用复制粘贴技术以及程序员的粗心大意导致的,并且导致了意外行为。 在这种情况下, clearBottomUpPointers方法应调用PerPtrBottomUp.clear方法。 在此示例中,动态分析不会发现任何错误,因为从它的角度来看,它是绝对合法的代码。

另一个例子。 假设我们具有以下功能:

 void OutstandingIssue(const char *strCount) { unsigned nCount; sscanf_s(strCount, "%u", &nCount); int array[10]; memset(array, 0, nCount * sizeof(int)); } 

从理论上讲,静态分析器可能会怀疑此代码有问题,但是实现这种诊断是一项非常困难且毫无意义的任务。 该示例摘自本文该文章还详细说明了为什么教静态分析器如何诊断此类错误是个坏主意。 简而言之,静态分析器很难弄清楚memset函数的调用可能导致索引超出数组范围,因为它们无法预知将从strCount字符串读取什么数字。 如果从文件中读取了strCount的值,则对于静态分析而言,这完全是不可能的任务。 另一方面,动态分析器可以毫不费力地注意到并指出此代码中的内存处理错误(假设程序已正确输入数据)。

本文的目的不是比较静态分析和动态分析。 没有一种技术可以诊断出各种软件缺陷。 两种分析都不能完全替代另一种。 为了提高程序的质量,您必须使用不同类型的工具,以便它们可以相互补充。 我希望上面展示的例子能令人信服。

我不希望偏向于静态分析,但是最近最常被谈论的是此技术,更重要的是,公司最近将其包含在其CI流程中。 静态分析是构建质量可靠,高质量软件产品的所谓质量门的步骤之一。 我们认为静态分析将在几年内成为标准的软件开发实践,就像单元测试曾经一样。

最后,我想再次指出,动态分析和静态分析只是两种不同的方法,它们相互补充。 最后,所有这些技术仅用于提高软件质量和减少开发费用的目的。

参考文献:

  1. 术语。 静态代码分析
  2. 术语。 动态代码分析
  3. 安德烈·卡波夫(Andrey Karpov)。 静态和动态代码分析
  4. 安德烈·卡波夫(Andrey Karpov)。 关于静态分析的神话。 第三个神话-动态分析优于静态分析
  5. 安德烈·卡波夫(Andrey Karpov)。 PVS-Studio的投资回报率

Source: https://habr.com/ru/post/zh-CN461173/


All Articles