1. 前言
周末找到一篇《对静态分析缺陷报告进行聚类,以降低维护成本》,读了之后受到不少启发,特此将笔记整理出来。
论文出处:
- Published in: 2013 20th Working Conference on Reverse Engineering (WCRE)
- Date of Conference: 14-17 October 2013
- ISSN Information: DOI: 10.1109/WCRE.2013.6671303
- Title: Clustering static analysis defect reports to reduce maintenance costs
- Authors:
- Zachary P. Fry,University of Virginia, Charlottesville, Virginia, USA
- Westley Weimer,University of Virginia, Charlottesville, Virginia, USA
2. 摘要
静态分析工具通过自动识别源代码中的错误来促进软件维护。但是,对于大型系统,这些工具通常会生成大量的缺陷报告,其中许多缺陷报告在概念上是相似的。单独处理缺陷会花费开发人员的工作量,并增加维护负担。建议对生成的缺陷报告进行聚类,以便可以对类似的错误进行分类,并可能一起修复。论文的方法利用静态错误报告中可用的语法和结构信息来准确地聚类相关报告,从而加快维护过程。
论文对总计超过 1400 万行代码,使用 Coverity 静态分析和 FindBugs 工具在 C 和 Java 程序中生成的 8,948 份缺陷报告来评此项技术。开发人员可以对工具生成的缺陷报告聚类可以集中处理,从而减少开发人员的维护工作。此外工具不仅能够完全准确地聚类,而且还可以显著减少开发人员必须手动检查的缺陷报告数量。例如,在90%的准确率水平下,该技术可以将单独检查的缺陷报告数量减少21.33%。
2.1. 需要解决的问题
静态分析工具能够帮助开发人员发现软件维护过程中未知的缺陷,然而缺陷报告中的误报会抵消缺陷检查工具可能节省的时间。在实际的应用中,误报问题变的影响变得更加严重。
报告缺陷通常具有语法和结构上的相似性。尽管有些相似之处可能是语法上的,这些代码很容易被识别出来;但并非所有相似的缺陷报告都涉及语法上相同的代码,虽然已经建立了用于检测代码中语法相似性的工具和自然语言描述中的概念相似性,但用于查找语法不同、语义相似的自动生成的缺陷报告的工具尚未建立。
2.2. 解决思路
论文提出了一种参数化技术,使用相似性指标对缺陷报告进行聚类,这些相似性指标利用了静态错误查找工具生成的语法和结构信息。该技术将静态错误查找器生成的一组缺陷报告作为输入,并将其划分为相关报告集群。生成的聚类必须准确,才能使该技术有用,因为错误分类会抵消所提供的时间节省的一部分。因此倾向于使用聚类,该聚类可以最大化产生的聚类的大小(即节省时间),同时确保聚类缺陷报告实际上相似(即准确)。由于聚类大小和准确性是无可比拟的目标,因此论文的算法可以根据需要进行调整,以偏向于其中一个,从而产生帕累托最优的选项边界。
2.3. 论文的主要贡献
- 提出了一种轻量级的、独立于语言的技术,用于对现有最先进的静态缺陷检测器生成的缺陷报告进行聚类。
- 使用来自 Coverity 的 Static Analyzer 和 FindBugs 的缺陷报告,对用 C 和 Java 编写的大型程序进行实证比较,将实现的技术与代码克隆检测工具进行比较。当指定聚类精度时,工具能够大幅减少整个缺陷报告集的减少。
- 为了验证实现的技术,提出了一项经调查调查,参与者绝大多数同意(99% 的时间),聚类产生的分类和实际报告一起的报告。
2.4. 补充说明:
- 静态分析的实现
静态分析工具本身就是对代码中特定的问题,抽象成检测模型,通过这个模型去识别代码中相似的问题。一个问题往往包含多种场景,模型也需要识别到这些场景进行判断和甄别,最后给出问题代码的告警。对同一问题的不同场景一定存在代码的语法或语义的相似性,由于工具对代码中输入的判定条件的不充分,或者工具bug,必然会导致告警的误报,这些误报也一定存在某种相似性。基于这些告警的相似性,所以论文由此提出了解决问题的方向和思路。
-
代码相似度目前基本上分为四种类型。
- 类型 1:完全相同的代码;
- 类型 2:重命名的代码;
- 类型 3:几乎相同的代码
- 类型 4:语义相似的代码
目前业界的主要研究主要集中在类型 4,即语义的相似,毕竟同样的功能,代码的实现千变万化。
-
帕累托最优(Pareto Optimal)
帕累托最优(Pareto Optimal)是指在某种情况下,不存在一种改变可以使至少一个人的状况变好,而同时不使其他人的状况变坏。
帕累托最优的选项边界是指在所有可能的选项中,那些满足帕累托最优条件的选项所构成的边界。也就是说,这条边界上的选项是在不损害其他方面的情况下,某一方面已经达到了最优的状态。
在实际应用中,帕累托最优的选项边界可以帮助人们做出更合理的决策,因为它考虑了多方面的因素,并在这些因素之间寻求一种平衡,以达到最优的结果。
3. 方法论(METHODOLOGY)
论文提出了一个用于自动报告缺陷的相似性模型,允许使用现成的聚类算法。模型使用静态分析工具报告的信息,考虑了相关性的句法和结构判断。
- 首先概述了自动生成的缺陷报告的结构,并描述了如何提取相关的句法和结构信息。
- 在获得从缺陷报告中获得了结构化信息之后,使用现有技术,又引入新的相似性指标,系统地比较缺陷报告的子部分来衡量缺陷相似性
- 学习使用线性回归的缺陷报告相似性的描述模型
- 解释如何使用生成的相似性度量来对相关缺陷报告进行聚类。
3.1. 静态分析缺陷报告建模
缺陷报告可以看作是 5 元组 〈D, L, P, F, M〉,其中:
- D: 是命名缺陷类型的自由格式字符串;
- L: 〈源file,行号〉对,代表工具直接涉及的行号,包含相关缺陷;
- P:是零个或多个〈源file,行号〉对的序列,编码在执行缺陷时可能访问的行的静态执行路径;
- F:是一个字符串,命名最接近 L 的封闭函数、类或文件;
- M:是一组零个或多个自由格式字符串,其中包含报告的任何其他元信息
缺陷报告组件提供了结构和句法信息的几个潜在来源,可用于衡量两个报告之间的相似性。完全按照缺陷报告中显示的信息使用某些信息,并强制其他信息以最大限度地提高所提取信息的效用。以下段落详细介绍了用于衡量报告相似性的特定信息类型。
- 函数(F):逐字摘自函数,此字符串表示表示表示缺陷表现的线的最近封闭函数的名称。当在函数外部报告缺陷时,使用封闭类或文件。
- 路径线(P):此信息是一组字符串,表示与静态路径相关的源代码行,该路径可以执行以到达缺陷位置(在论文的模型中为 P)。假设相同或相似的执行路径上的错误可能是相关的。除了显式比较这些路径序列之外,还按字母顺序对 P 中的源行进行排序,以帮助揭示与相似代码行有关但顺序不同的缺陷。
- 代码上下文(L):给定 L 中指示的确切行作为 bug 的表示形式,代码上下文是表示原始源文件中出现的前三行和后三行的字符串序列。此代码窗口是 Bug 上下文的近似值。假设在相似的环境中(例如,在块内部)发生的缺陷可能是相似的。
- 宏:通过从上述 L 和 P 中引用的实际源行文本中提取仅包含大写字母或数字的所有标记,此信息近似于任何指示代码行中引用的宏集。在代码中查找确切的宏集需要预处理器,并且成本高得令人望而却步。因此,使用一个近似值:虽然一些以全部大写字母出现的标记实际上可能不是宏对。对 20 个此类实例的随机检查表明,85% 确实是。假设使用相同的宏可能表明缺陷报告之间存在相似性。
- 文件系统路径:此信息是一个字符串,表示给定项目文件结构中指示文件(取自 L)的确切路径,该路径试图链接位于同一模块甚至子文件夹中的缺陷。与封闭函数类似,假设指示本地关闭文件的缺陷报告可能表现出相似性。
- 元标记:如果可用,这是直接从 M 中获取的一组字符串:来自静态分析工具的任何其他信息。缺陷报告中,这些信息包括缺陷的可疑来源。根据用于查找缺陷的工具,信息的类型和数量可能会有很大差异。假设,静态分析工具产生的任何信息在测量相似性时都可能是有用的。
3.2. 缺陷报告相似性指标
提出了一套针对工具报告缺陷的轻量级相似度指标〈D、L、P、F、M〉,这些指标共同适用于句法和结构信息。由于对缺陷报告之间的关系感兴趣,因此衡量相似性的基本单位是一对缺陷报告。通过计算两个缺陷报告各个子组件的相似性的加权和来确定其总体相似性评级。这种相似性模型能够对相关的缺陷报告进行聚类。
除了引入专门适用于该领域中存在的信息结构的新型轻量级相似性指标外,还使用来自信息检索和自然语言处理的指标来比较各个缺陷报告子组件。除非另有说明,否则通过分割空格和标点符号来标记原始字符串。
论文考虑的指标。
-
Exact Equality
两个字符串的字符级布尔匹配。直观地说,具有完全匹配子组件的报告可能是相关的。 -
Strict Pairwise Comparison
严格成对比较。 令牌的百分比来自两个完全匹配的字符串(对于两个标记序列 a 和 b, 比较 aiai 到 bibi)。例如,在比较文本代码行时,此指标可以识别仅在少数变量名称或方法调用中有所不同的相似代码。 -
Levenshtein Edit Distance
改编自信息检索社区,该度量采用近似字符串匹配技术,用于测量将一个字符串转换为另一个字符串所需的增量更改数量。将对字符串进行操作的传统指标提升为标记序列。通过处理任一字符串中所有标记的字母表,计算将一个字符串转换为另一个字符串的令牌级别更改的次数。Levenshtein 距离放宽了严格的成对比较,允许近似对齐。拼写检查器经常使用类似的方法来建议拼写错误的替换单词。从概念上讲,论文的提升 Levenshtein 距离是相似的:它建议缺陷报告包含可能相关的信息。 -
TF-IDF
自然语言处理社区中常见的文档相似度指标。它奖励两个有问题的文件所独有的令牌,并对在全局背景下经常出现的令牌进行折扣。使用 TF-IDF 假设存在一个具有代表性的语料库,从中可以测量所有令牌的相对全局频率。将来自所有工具生成的缺陷报告的所有标记集作为此语料库,这些标记将针对给定程序进行聚类。通过分别检查每个令牌的术语频率 (tf) 和反向文档频率 (idf) 来比较两个文档。术语频率衡量所有单词的相对计数,而反向文档频率衡量术语的“唯一性”,并对 int 或 the 等常见单词进行折扣,同时对唯一且可能更有意义的单词进行加权。例如,缺陷报告,标记出现在报告 B 和 C 中,但仅出现在总体报告中的 0.69%。共享这种稀有令牌会增加这两个报告之间的 TF-IDF 度量,从而暴露出固有的相似性。相比之下,该令牌在所有三个报告中都频繁出现,并且在 Linux 的其他报告中出现率为 4.95%:因此它的 idf 值较低。这样可以防止 TF-IDF 错误地将使用此全局频繁的令牌的所有缺陷报告指示为相似。 -
Largest Common Pairwise Prefix
数量标记 两个字符串在从左到右进行比较时有共同点(即,最大的 i 使得 ∀j.0≤j≤i=⇒aj=bj∀j.0≤j≤i=⇒aj=bj )。为了说明此指标的实用性,请考虑两个语句,它们将类似函数调用的结果分配给同一变量。即使函数调用的参数不同,此指标也会捕获两行之间的初始相似性。换句话说,程序的编写方式有时与英语松散地对应,在英语中,主语和动词通常出现在句子的开头。同样,在许多高级编程语言中最左边的列(例如,通常,被赋值的变量或方法调用的根对象)是程序执行和状态的最基本列。由于这些原因,假设检查基于代码的信息的前缀之间的相似性可能会暴露相关的缺陷报告。 -
Punctuation Edit Distance
标点符号编辑距离 — 结构代码相似度的轻量级指标。传统方法,例如比较控制流图成本高昂且困难重重,因为在会审阶段,可能无法在所有项目上进行编译。取而代之的是,论文采用了一种轻量级的指标,该指标近似于程序结构,同时保留了对程序化事件发生顺序的考虑。计算删除所有非标点符号(例如,仅保留大括号、括号、运算符等)的标记序列之间的 Levenshtein 编辑距离。作为此类指标实用性的示例,请考虑两段代码,它们共享相同的方法调用和类似结构的循环。括号、逗号、大括号和分号的类似模式将有助于使相关性明显。通过抽象出文本标识符,该指标补充了更多以语言为中心的概念。
这些指标适用于各种输入类型。必要时会将一种类型的信息强制转换为另一种信息。例如,任何字符串或字符串集都可以通过在聚合术语频率计数时分割标点符号和空格来被视为“词袋”(TF-IDF 使用的文档数据结构)。同样,可以通过按文本外观顺序或字母数字顺序对字符串进行排序,将一组字符串强制转换为一个序列(例如,Levenshtein 编辑距离使用)。
3.3. 建模报告相似性
基于文本代码和结构编程的特征可作为相似性测量的输入,比较两个自动生成的缺陷报告的子组件。将每个相似性指标应用于所有适用的报告子组件对,以获得每对报告的相似性。为了简单起见,省略了具有很少或没有预测能力的组合。
避免断言这些测量值与一对缺陷报告之间是否相关的先验关系。构建了一个分类器,该分类器检查候选报告对,并根据学习到的加权特征值的线性组合,确定该对是否“相似”。因此,一对缺陷报告的相似性判断是加权特征的总和(其中每个 fifi 一对报告子组件的相似性度量值):
c0+c1f1+c2f2+...+cnfn>c′c0+c1f1+c2f2+...+cnfn>c′ (1)
如果得到的聚合和大于实验选择的临界值,则两个缺陷称为“相似”:c′c′。使用线性回归来学习 c0,c1,到cn和c′c0,c1,到cn和c′ 的值。学习此分类器需要一个训练阶段。论文选择一个线性模型,以便于在给定单个模型的情况下探索一系列平滑的截止。
3.4. 聚类过程
一组缺陷报告,其中每个单独的报告都与所有其他报告“相似”(相对于论文的模型),适合进行聚合分类。在定义了缺陷报告之间的相似性之后,现在需要一种轻量级且准确的方法来聚类相关缺陷。传统的聚类技术(例如 k-medoid 聚类)通常试图在给定正式度量空间的情况下测量单个实体之间的相似性。具体来说,k-medoid 聚类假设所有特征在模型中都是实值且权重相等。首先,不假设模型中的特征保证了相等的权重(如首先是的相似性模型)。此外,特征不是对单个缺陷特性的重估度量,而是相似性的相对度量。因此,论文采用了一种众所周知的算法来测量组件的互连性,以实现聚类的目的。
可以将相似缺陷的聚类视为无向图,其中顶点表示缺陷,边缘表示相似性关系(也就是说,使用公式(1),任何连接的顶点都被认为是“相似”的)。为了获得准确的聚类并避免错误地聚类不相关的缺陷,可以通过在诱导图中找到最大群来进行聚类。查找小圈子可确保小圈子(集群)中的任何缺陷都与其中的所有其他缺陷相似。
提出了一种 2 阶段递归聚类方法:
- 构造一个无向图,其中顶点代表所有剩余的、未聚集的缺陷,边缘表示对“相似性”的定义。
- 找到最大的集团,将所有包含的缺陷输出为一个聚类,并将它们从图中删除;如果没有剩余缺陷,则返回,否则递归。
在最坏的情况下,集团查找需要指数时间:时间复杂度为 O(n×2n)O(n×2n),其中 n 是整个图形中的顶点数。在实践中,“几乎小集团”是罕见的(即,当选择高“相似性”截止值时,簇之间的虚假互连边是稀疏的),论文的实现运行得足够快。例如,在具有 869 个缺陷报告的 Linux 内核模块上,平均运行时间为 0.088 秒。这种聚类方法会产生不同的缺陷集,这些缺陷集显示出高度的内部相似性。
4. 评估
论文的技术目标是通过聚类工具生成的缺陷报告来减少维护工作,使开发人员能够对缺陷进行分类,甚至修复总体缺陷。
- 问题 R1:评估与论文的工具相关的节省工作量的潜力。
- 问题 R2:通过将论文的工具与最接近相关的重复检测技术进行比较,将其置于上下文中。
4.1. 指标
为了评估这两个研究问题,使用了两个不同的成功指标。
首先,测量所有产生的聚类的平均内部精度。也就是说,对于每个提议的聚类,测量最大包含的集团的大小(相对于真值注释)与论文的技术产生的聚类大小之比。例如,如果一个大小为 5 的聚类,其中只有 4 个报告完全互相关,则准确率为 0.8。
其次,计算当使用生成的聚类来处理和分类聚集的缺陷报告时,整个缺陷报告集的大小减少百分比。将每个发出的集群“折叠”为一个有效的缺陷报告,假设(鉴于相关缺陷报告的既定定义)类似的报告可以并行处理。例如,如果有 20 个原始报告,并且一种方法确定了两个大小为 5 的聚类,则生成的有效大小为 12 个概念聚类(10 个单例和 2 个大小为 5)的报告,从而减少了 40% 的报告数量,
这些报告必须单独考虑并使用单独的推理进行处理。并非所有缺陷都需要相同的时间或人力来分类和修复,因此,根据缺陷报告的减少来估算人力劳动的减少将严格来说是一种估计。衡量对任何一个缺陷进行分类和修复所需的工作量与这项工作是正交的,因此,只是衡量评估报告数量的减少。
4.2. 代码克隆工具
据析,目前还没有现有的全自动技术来聚类静态错误查找工具生成的缺陷报告。Kremenek等人提出了一种基于聚类的缺陷报告排序技术,但它依赖于重复的人类反馈,因此无法与论文的技术直接比较。然而,代码克隆检测是一项密切相关的任务——涉及相似代码的报告(例如,来自复制和粘贴开发,或仅来自相似的开发逻辑)可能是相关的,因此可以使用此类技术作为比较的基线。工具生成的缺陷报告包含大量基于代码的信息,因此,针对此任务调整代码克隆检测工具提供了一种直接的比较方法。此外,代码克隆工具几乎完全依赖于句法字符串匹配技术,因此为比较提供了极好的基线:论文的工具在准确性或聚类缺陷报告数量上的任何提高都可以归因于包含的结构信息或使用各种相似性指标。
有许多最先进的技术能够准确地执行克隆检测;采用了三种流行的工具与论文的技术进行比较:ConQAT、PMD 和 Checkstyle。这些工具通常将一组源文件作为输入,并生成所有代码克隆的列表。通过创建一组合成的源文件来使它们适应缺陷报告聚类,每个源文件都来自一个单独的报告。对于给定的缺陷报告,通过连接 L(涉及的源行)和 P(涉及的执行路径源)来构建一个合成的源文件。所有合成文件的集合(对应于所有有问题的缺陷报告)被用作给定克隆检测工具的输入,然后使用代码克隆工具的输出作为缺陷报告相似性度量,并以与论文的技术相同的方式执行聚类。
4.3. 结果
-
图2 - C
-
图3 - Java
图 2 和图 3 显示了在不同的聚类精度水平下使用每种聚类方法时,总体缺陷报告集的减少百分比,分为 C 和 Java 缺陷报告。这些结果以帕累托最优边界的形式呈现,以显示聚类精度与不同缺陷报告数量之间的权衡。帕累托边界上的每个点都代表参数化技术的可能结果。与现成的代码克隆工具相比,论文的方法允许更细粒度的调整,因为它是基于建模相似性的参数化,而不仅仅是代码中找到的匹配项的大小。
与可比的代码克隆检测技术相比,论文的技术在两种语言的几乎所有准确性水平上聚集了更多的缺陷。在考虑 Java 缺陷报告时,论文的技术在所有准确性级别上都优于所有代码克隆工具。此外,论文的技术能够达到完美的准确性(任一图表的右下角),而其他工具则不能。
论文注意到,虽然较低的精度似乎会产生大的聚类,从而大大减少了整体缺陷集,但在实践中,这种聚类中的虚假报告将大大降低总体处理此类缺陷的效益。论文断言,应该支持更高水平的精度,以减少维护工作。为了完整起见,论文提供了所有精度值。
比较竞争性帕累托边界曲线下的面积提供了一种概括所有权衡性能的方法。在考虑所有缺陷报告时,论文技术的曲线下面积分别是 ConQAT 和 PMD 的 1.4 倍和 2.5 倍,论文分别考虑这两种多语言代码克隆工具(Checkstyle 仅适用于 Java 程序,因此这里不考虑)。代码克隆工具主要考虑语法特征,因此与论文的工具相关的性能提高可能归因于包含结构、语义相关的特征。论文认为,代码克隆工具和论文的技术之间的性能差异可以通过概念上相似但在语法上唯一的缺陷报告集群来解释。
- 这部分的图表的生成过程和数据来源,没有给出明确的方法,后期需要进一步的分析和验证。
5. 参考
- Clustering static analysis defect reports to reduce maintenance costs