Isolating Compiler Bugs by Generating Effective Witness Programs With Large Language Models

Isolating Compiler Bugs by Generating Effective Witness Programs With Large Language Models

基本信息

博客贡献人

JokerLin

作者

Haoxin Tu, Zhide Zhou, He Jiang, Imam Nur Bani Yusuf,Yuxian Li,Lingxiao Jiang

标签

SoftwareEngineering,ArtificialIntelligence,Compilers,Large Language Models (LLMs)

摘要

​编译器错误对安全关键型应用程序构成了重大威胁,及时有效地隔离这些错误对于保证编译器的质量至关重要。借鉴 ChatGPT 等预训练大型语言模型( LLMs )在代码生成方面的最新进展,提出了一种新的基于 LLMs 的编译器错误隔离测试程序生成方法—— L L M 4 C B I LLM4CBI LLM4CBI 。由于生成精确提示和选择专业提示两者的挑战,所以直接将 LLMs 用于测试程序变异可能不会产生期望的结果,针对这个问题 L L M 4 C B I LLM4CBI LLM4CBI 设计了三个新组件。首先, L L M 4 C B I LLM4CBI LLM4CBI 利用程序复杂性引导的提示生产组件,该组件利用数据和控制流分析来识别程序中最有价值的变量和位置以进行变异。第二, L L M 4 C B I LLM4CBI LLM4CBI 采用记忆提示选择组件,该组件采用强化学习的方法选择特定的提示,不断地对测试程序进行变异。第三,提出了一个测试程序验证组件,用于选择专门的反馈提示,以避免在变异过程中重复相同的错误。与最先进的方法( DiWi 和 RecBi )相比, L L M 4 C B I LLM4CBI LLM4CBI 可以比 DiWi 和 RecBi 多分离 69.70% / 21.74% 和 24.44% / 8.92% 的错误。此外,本文证明了 LLMs 组件(即,GPT-3.5)可以很容易地被其他 LLMs 替代,同时与相关研究相比仍能获得合理的结果。

问题定义

​针对编译器错误隔离,解决该问题的一个方向是将缺陷隔离问题转化为测试程序变异问题。这种方法背后的核心思想是首先生成一组见证(即可通过)测试程序突变体,其通过突变给定的失败测试程序,然后收集通过和失败的频谱,最后结合基于频谱的故障定位( SBFL )技术,对可疑文件进行排序。然而,现有的程序突变策略(例如 DiWi 和 RecBi )在有效性方面表现出局限性,需要大量的人力。

​受预训练大型语言模型( LLMs )在代码生成方面最新进展的启发,本文提出了 L L M 4 C B I LLM4CBI LLM4CBI , 本文的主要见解是:(1)LLMs 是用大规模代码数据集进行训练的,因此 LLMs 生成的测试程序往往是多样化的; (2)LLMs 有良好的学习和反思机制,可以根据用户的反馈,遵循即时响应对话范式,帮助产生更好的输出;(3)LLMs 使用的提示可以用自然语言表达,更易于用户使用,减少人力完成任务。但是直接使用 LLMs 生成用于编译器错误隔离的有效测试程序存在一些困难,提出了以下两个需要解决的挑战。

  • 制定精确的提示。 提示的质量在使用 LLMs 的程序突变能力中起着至关重要的作用,但使用现有的自然突变描述作为提示可能并不有效。 例如,变异规则“插入 if 语句”是现有工作 RecBi 中使用的变异描述。 这种描述缺乏对使用哪些变量以及在哪里插入语句的精确性,限制了将失败的测试程序转变为通过测试程序的可能性。
  • 专业提示的选择。 当收集到多个提示时,选择专业的提示来改变特定的失败测试程序非常重要且具有挑战性。 原因有两个, 首先编译器错误往往是不同的,并且具有不同的语言特征。 一个提示对于改变一个特定的失败测试程序可能有用,但对于另一失败的测试程序可能没有帮助,因此随机选择提示来改变测试程序可能是无效的。 其次,LLMs 在变异不同的测试程序时可能会犯不同的错误,也应该针对不同的测试程序给予不同的反馈提示。

​为了克服上述挑战, L L M 4 C B I LLM4CBI LLM4CBI 中设计了三个新组件。 首先,设计一个精确提示生产组件来应对第一个挑战,引入了可以准确表示所需变异操作的精确提示模式,然后利用数据和控制流分析测量的程序复杂性度量来识别最相关的变量和最佳插入位置。其次,提出了两个新组件,即记忆提示选择组件轻量级测试程序验证组件,用于选择专用提示。在提示选择组件中, L L M 4 C B I LLM4CBI LLM4CBI 通过强化学习结合了记忆搜索,以根据 LLMs 的表现跟踪和积累奖励,这使得 L L M 4 C B I LLM4CBI LLM4CBI 能够不断选择专门的提示来改变特定的测试程序。 在测试程序验证组件中, L L M 4 C B I LLM4CBI LLM4CBI 利用静态分析来检测和过滤掉可能包含未定义行为的潜在无效测试程序,从而减轻与无效程序相关的风险。
本文所做贡献如下:

  • L L M 4 C B I LLM4CBI LLM4CBI 是第一个旨在利用 LLMs 功能来执行该领域编译器错误隔离任务的工作。

  • 提出了三个新组件,即精确提示生成、记忆提示选择和轻量级测试程序验证,以指导 LLMs 生成有效的测试程序以隔离编译器错误。

  • 我们的实证评估表明, L L M 4 C B I LLM4CBI LLM4CBI 不仅对编译器错误隔离有效,而且具有可扩展性,因为其他 LLMs 可以轻松集成到 L L M 4 C B I LLM4CBI LLM4CBI 中。

  • L L M 4 C B I LLM4CBI LLM4CBI 为编译器错误隔离的未来研究铺平了道路,为进一步探索和利用 LLMs 的功能来实现更高效、更有效的错误隔离技术提供了令人兴奋的机会。

背景

测试程序变异

​图 1 简要说明了现有编译器错误隔离方法的流行工作流程。给定一个失败的测试程序,可以触发编译器错误,现有的方法首先使用不同的变异策略,以产生一个通过测试程序。然后失败和通过的测试程序都要经过编译,从而能够收集编译器源文件的代码覆盖信息。在编译过程中,失败的测试程序所覆盖的所有编译器文件都被认为是可疑的。相反,通过测试程序有助于减轻对可能涉及的无辜文件的怀疑。为了最终隔离有缺陷的文件,遵循基于频谱的故障定位(SBFL)的既定原则,他们使用 Ochiai 等公式比较失败测试程序和通过测试程序之间的执行轨迹(或频谱)。DiWi 和RecBi 遵循相同的策略,他们的目标是生成一组与测试程序相比具有略微不同的控制和数据流信息的程序,以改变编译器执行结果(即由测试失败转变为测试通过),本文目标是通过利用基于 LLMs 的新方法来实现相同的效果。

在这里插入图片描述

图1. 现有编译器错误隔离的一般工作流程

可疑文件排名

L L M 4 C B I LLM4CBI LLM4CBI 利用 SBFL(基于频谱的故障定位)的概念,通过比较失败和通过测试的覆盖率来识别潜在的错误编译器文件。具体来说,由于 Ochiai 表现良好, L L M 4 C B I LLM4CBI LLM4CBI 采用 Ochiai 方程来计算每个语句的可疑得分。

s c o r e ( s ) = e f s ( e f s + n f s ) ( e f s + e p s ) (1) score(s) = \frac{ef_s}{\sqrt{(ef_s + nf_s)(ef_s + ep_s)}} \tag{1} score(s)=(efs+nfs)(efs+eps) efs(1)
​在等式中, e f s ef_s efs n f s nf_s nfs 表示执行和不执行语句 s s s 的失败测试的数量, e p s ep_s eps 表示执行语句 s s s 的通过测试的数量。在 L L M 4 C B I LLM4CBI LLM4CBI 中,由于只有一个给定的失败测试程序,因此执行语句 s s s e f s ef_s efs)的失败测试的数量固定为1。此外, L L M 4 C B I LLM4CBI LLM4CBI 只关注给定失败测试程序执行的语句,这意味着不执行语句 s s s n f s nf_s nfs )的失败测试的数量为0。因此,Ochiai 方程被简化为:
s c o r e ( s ) = 1 ( 1 + e p s ) (2) score(s) = \frac{1}{\sqrt{(1 + ep_s)}} \tag{2} score(s)=(1+eps) 1(2)
​一旦获得每个语句的可疑分数, L L M 4 C B I LLM4CBI LLM4CBI 继续计算每个编译器文件的可疑分数,然后 L L M 4 C B I LLM4CBI LLM4CBI 将编译器文件中给定的失败测试程序所执行的语句的可疑分数进行聚合,以确定文件的可疑分数:
S C O R E ( f ) = ∑ i = 1 n f s c o r e ( s i ) n f (3) SCORE(f) = \frac{\sum_{i=1}^{n_f}{score(s_i)}}{n_f} \tag{3} SCORE(f)=nfi=1nfscore(si)(3)
​其中失败测试程序在编译器文件 f f f 中执行的语句的数量表示为 n f n_f nf L L M 4 C B I LLM4CBI LLM4CBI 利用这些信息来计算每个编译器文件的可疑分数。通过根据编译器文件的可疑分数以降序排列编译器文件, L L M 4 C B I LLM4CBI LLM4CBI 生成了一个可疑文件排名列表。

大型语言模型

​预训练的大型语言模型( LLMs )在许多任务中表现出卓越的性能,如机器翻译、文本摘要、分类和代码生成。从技术上讲,LLMs可以直接用于处理特定的下游任务,方法是向模型提供任务描述(即提示),并且无需对专门的数据集进行微调。在本研究中,目标是在测试程序生成任务领域中释放 LLMs 的能力。许多现有的 LLMs 采用 Transformer 架构的解码器。给定包含任务描述的提示,解码器通过下面的等式逐个生成测试程序 Y Y Y 作为令牌序列:
y t = arg ⁡ max ⁡ y    P ( y    ∣    p    ,    y < t ) (4) y_t = \arg\max_y\;P(y\;|\;p\;,\;y_{<t}) \tag{4} yt=argymaxP(yp,y<t)(4)
​其中 y t y_t yt 是要预测的当前令牌, y < t y_{<t} y<t 是指所有先前预测的令牌,并且 p p p 是输入提示。该等式表明,给定提示 p p p 和之前生成的令牌 y < t y_{<t} y<t ,然后通过选择使条件概率 P ( y    ∣    p    ,    y < t ) P(y\;|\;p\;,\;y_{<t}) P(yp,y<t) 最大化的令牌 y y y 来确定要预测的当前令牌 y t y_t yt。因为生成的测试程序 Y Y Y 取决于输入提示 并且输入提示 p p p 的搜索空间是巨大的,所以找到最佳提示 p p p 以生成有效的测试程序 Y Y Y 可能是具有挑战性的。在这项工作中,我们提出了 L L M 4 C B I LLM4CBI LLM4CBI 自动找到提示 p p p ,可以生成更有效的测试程序 Y Y Y 的编译器错误隔离任务。

​LLMs 有两个主要类别用于代码生成任务:填充和通用。填充模型(例如 CodeGen、Incoder和PolyCoder)用于基于双向上下文插入最自然的代码(例如在代码片段的中间),而通用模型(例如 LLaMA 、Alpaca 、ChatGPT、Vicuna和GPT4ALL )的目标是仅通过自然语言描述生成给定左上下文的完整代码片段。在本文考虑通用模型,主要是由于通用模型遵循的应答对话范式,这涉及到最小的人力资源,符合本文的目标。

示例

在这里插入图片描述

清单1. 失败测试程序                                        清单2. 通过测试程序

图2. LLVM错误#16041(图中突出显示的灰色代码由 $LLM4CBI$ 生成)

​图 2(1)展示了一个失败的测试程序,它暴露了 LLVM-3.4 编译器中 -O3 优化级别的一个错误。该程序在LLVM中的归纳变量消除优化通道中引入了除零,导致错误编译错误。值得注意的是,图2(2)表示由本文提出的解决方案( L L M 4 C B I LLM4CBI LLM4CBI )生成的通过测试程序,该程序不会触发 LLVM 编译器中的错误。下面将展示现有方法的局限性和本文方法在生成通过测试程序的优势。

现有方法的局限性

​ 现有的方法如 DiWi 和 RecBi,由于三个主要原因,导致在生成上述通过测试程序时面临着限制。

  • DiWi 和 RecBi 中采用的突变策略表现出一定的局限性。例如,DiWi 只支持不改变失败测试程序控制流的局部变异算子。因此它不能生成关键元素,如图 2(2)中突出显示的灰色 if 语句 “if(a== 0)”。类似地,RecBi 允许插入结构化条件,如 “if(a==0)”,但缺乏生成相应语句体的能力,如 “s = v;”。此外 DiWi 和 RecBi 都依赖于随机选择策略来确定在突变过程中使用哪些变量以及在何处插入它们,使得转换结果在很大程度上取决于概率
  • 现有方法中涉及的突变过程需要大量的人力。在突变之前,必须编写额外的代码来收集必要的上下文信息(例如像 s、a、b、c 这样的变量的名称及其定义的类型)并从现有的测试程序提取相关元素(例如 if 语句),需要手动编码来随机确定在突变期间将新代码片段插入失败测试程序的合适位置
  • 很少关注生成的测试程序的有效性,这可能会产生编译器错误隔离的副作用。

​总之上述限制强调了现有方法在生成高质量测试程序方面的无效性和对人力的高需求。

本文方法的优越性

​与 DiWi 和 RecBi 相比, L L M 4 C B I LLM4CBI LLM4CBI 通过利用 LLMs 中的功能,使得在生成有效的通过测试程序方面表现出色。

  • 产生精确的提示,例如 “请通过插入 if 语句并重用第12-18行之间的列表{ a , s , v a,s,v asv}中的变量来生成输入程序 F F F 的变体程序 P P P”,以指导 LLMs 按照某些要求对给定程序进行变异。 L L M 4 C B I LLM4CBI LLM4CBI 没有使用模糊的提示,而是在程序中考虑了更详细的信息,这些信息可以增加从失败到通过转变的可能性,以构建精确的提示。通过这种方式, L L M 4 C B I LLM4CBI LLM4CBI 通过插入图 2(2)中灰色显示的新if语句来生成新的测试程序。值得注意的是, L L M 4 C B I LLM4CBI LLM4CBI 支持在结构突变中插入语句体(如 “s = v;”),这增加了生成的测试程序的多样性
  • 受益于 LLMs 的应答对话范式,整个突变过程只需要很少的人力
  • L L M 4 C B I LLM4CBI LLM4CBI 还可以检测和过滤包含未定义行为的潜在无效测试程序,进一步提升了 L L M 4 C B I LLM4CBI LLM4CBI 在编译器错误隔离方面的能力

方法

​图 3 展示的总体设计说明了 L L M 4 C B I LLM4CBI LLM4CBI 的一般工作流程。

在这里插入图片描述

图3. LLM4CBI 总体设计

具体步骤如下:
  • L L M 4 C B I LLM4CBI LLM4CBI 首先生成精确提示,并在精确提示生成组件中收集所有生成的提示
  • L L M 4 C B I LLM4CBI LLM4CBI 通过存储的提示选择组件选择提示
  • L L M 4 C B I LLM4CBI LLM4CBI 产生新的测试程序
  • 测试程序经历测试程序验证组件
  • 如果生成的程序有效,则编译该程序,并且收集编译器源文件的覆盖信息,如果程序无效则反馈给模型并重新生成新的测试程序
  • 利用相似性和多样性度量来测量所生成的测试程序的质量,所述相似性和多样性度量用作所存储的提示选择组件的输入以帮助选择更好的提示
  • L L M 4 C B I LLM4CBI LLM4CBI 采用 SBFL 沿着失败程序和通过程序的频谱来对可疑文件进行分级
精确提示生成
程序变异提示模式

​以下是 L L M 4 C B I LLM4CBI LLM4CBI 中为构建 LLMs 有效提示而设计的模式。

在这里插入图片描述

​在该模式中, P P P 指的是新生成的测试程序, F F F 指的是给定的失败测试程序。 其中 <mutation rule> 表示实际的变异操作; <variables> 和 <location> 描述了进行突变时的具体要求。 本文在模式中重用现有的变异规则(参见表 1)。 由于编译器使用不同的策略来优化具有不同数据或控制流的程序,因此与现有方法中随机变异程序不同,变异失败程序中最复杂的部分更有可能翻转失败的程序转变为通过的程序。 为此 L L M 4 C B I LLM4CBI LLM4CBI 通过两种方式测量最复杂的部分:保存复杂数据流的变量和涉及复杂控制流的位置。

表1. 提示模式中应用的突变规则

在这里插入图片描述

数据流复杂性引导的变量选择

​数据流分析旨在输出在给定的失败测试程序中定义和使用的最复杂的变量。 由于本文的目的是通过检查变量如何影响以及影响程度来研究变量的复杂性,因此认为变量定义(或分配)越多,对该变量的依赖性就越复杂, 从而增加了数据流的复杂性。 遵循数据流复杂性的现有定义,选择变量 def-use 链来分析程序的数据流复杂性。 具体来说,通过以下等式计算变量( C o m p v a r Comp_{var} Compvar )的复杂度:
C o m p v a r = N d e f + N u s e (5) Comp_{var} = N_{def} + N_{use} \tag{5} Compvar=Ndef+Nuse(5)
​其中 N d e f N_{def} Ndef 计算变量被定义的次数,包括重新定义或赋值。 N u s e N_{use} Nuse 计算变量的使用次数,这是指在某些计算中使用变量值而不修改变量值。 根据上式,对图 2(1)中失败的测试程序进行数据流分析将产生一个变量列表{a,s,v},它代表程序中使用的 Top-3 复杂变量。

控制流复杂性引导的位置选择

​控制流分析的目的是输出程序中最复杂的语句位置。 本文利用输入失败程序的控制流图,其中每个节点代表语句,边缘指示执行流。 当控制流图(即 CFG )可用时,我们根据圈复杂度使用以下等式计算每个语句的复杂度:
C o m p c o n t r o l = N e d g e − N n o d e + 2 Comp_{control} = N_{edge} - N_{node} + 2 Compcontrol=NedgeNnode+2
​其中 N e d g e N_{edge} Nedge N n o d e N_{node} Nnode 分别表示 CFG 中的边和节点的数量。由于圈复杂度不是用来测量语句级的复杂度的,因此我们通过在圈复杂度计算过程中获得复杂度值来计算每个语句的复杂度。本文有意忽略了包含测试预言机的语句(即具有 printf 或 abort 函数)。原因是改变包含测试预言机代码块更有可能破坏预言机,这意味着可能生成一个假的通过测试的程序。再次以图 2(1)所示的代码为例, L L M 4 C B I LLM4CBI LLM4CBI 中设计的控制流分析表明,最复杂的控制流位于第 12-18 行之间的 for 循环中。这样在获得变量列表和所需的插入位置后, L L M 4 C B I LLM4CBI LLM4CBI 会根据设计的模式生成某些提示。例如生成的提示之一是:

在这里插入图片描述

此外,LLMs 在改变程序时可能会犯不同的错误:例如 LLMs 可能在失败的测试程序中引发语法错误,但在另一个程序中引发语义错误。没有必要将所有错误信息作为反馈提示,因此 L L M 4 C B I LLM4CBI LLM4CBI 支持并开发了一种专门的快速选择策略。

记忆提示选择
强化学习

​强化学习(一种记忆搜索)是一个研究领域,旨在教一个智能体如何在一个环境中采取行动,以最大限度地提高其长期累积奖励。强化学习的关键因素包括:

  • 主体:学习者和决策者的角色
  • 环境:由智能体以外的东西组成并与之交互的一切
  • 行动:主体的行为表征
  • 状态:能力体从环境中获得的信息
  • 奖赏:来自环境的关于动作的反馈。

​强化学习通常可以分为基于值的算法(例如深度Q学习算法)和基于策略的算法(例如策略一致性算法)。随着强化学习的发展,已经提出了一类称为 Actor-Critic(AC)的算法,结合了基于值和基于策略的元素。在这项研究中,我们采用了 Advantage ActorCritic(即A2C )框架来解决编译器错误隔离的挑战,因为它既有效(与 AC 相比)又适合单线程和多线程系统(与 Asynchronous Advantage Actor-Critic相比,即 A3C ),从而能够为特定的编译器错误生成有效的通过测试程序。

提示选择的强化学习

在这里插入图片描述

图4. 强化学习指导的智能化提示选择

​图 4 概述了 L L M 4 C B I LLM4CBI LLM4CBI 中选择提示的基于强化学习的策略。首先在主体中引入两个神经网络:Actor Neural Network(ANN)和 Critic Neural Network(CNN),然后 L L M 4 C B I LLM4CBI LLM4CBI 选择一个动作来选择一个提示(第一次随机选择)并测量新生成的测试程序的质量( Q t Q_t Qt)。为了便于学习, L L M 4 C B I LLM4CBI LLM4CBI 基于预测的潜在奖励( P R PR PR)和从所选提示中获得的实际奖励( R R R)采用优势损失函数( A l o s s Aloss Aloss)。最后 L L M 4 C B I LLM4CBI LLM4CBI 将所有 ANN、CNN 和状态的状态更新到主体。 L L M 4 C B I LLM4CBI LLM4CBI 重复选择提示以生成测试程序,直到终止条件(例如到达 1 小时限制或生成 10 个程序变体)。下面提供了最重要部分的详细说明,包括实际奖励和优势损失函数的计算。

计算实际奖励

​质量高的通过测试程序需要满足相似性和多样性标准,这种相似性意味着每个通过的测试程序都应该表现出与给定的失败测试程序相当的编译器执行跟踪,可以减少与大量无错误文件相关的怀疑。 而多样性要求不同的通过测试程序具有彼此不同的编译器执行轨迹,以减少对各种无错误文件的怀疑。 相似性和多样性都依赖于距离度量,其定义为:
d i s t ( a , b ) = 1 − C o v a ∩ C o v b C o v a ∪ C o v b (6) dist(a, b) = 1 - \frac{Cov_a \cap Cov_b}{Cov_a \cup Cov_b} \tag{6} dist(a,b)=1CovaCovbCovaCovb(6)
​其中 d i s t ( a , b ) dist(a, b) dist(a,b) 表示两个测试程序 a a a b b b 之间的覆盖距离,这是使用 J a c c a r d Jaccard Jaccard 距离测量确定的。 C o v a Cov_a Cova C o v b Cov_b Covb 分别表示测试程序 a a a b b b 在编译器中覆盖的语句集。 将生成的通过测试程序集表示为 p = p 1 , p 2 , . . . , p n p = {p1,p2, ..., pn} p=p1,p2,...,pn,将失败的测试程序表示为 f f f,本文将通过测试程序集实现的相似性和多样性度量形式化,表示如下等式:
s i m = ∑ i = 1 N ( 1 − d i s t ( p i , f ) ) N (7) sim = \frac{\sum_{i=1}^{N}(1-dist(p_i,f))}{N} \tag{7} sim=Ni=1N(1dist(pi,f))(7)

d i v = ∑ i = 1 N − 1 ∑ j = i + 1 N d i s t ( p i , p j ) N ( N − 1 ) / 2 (8) div=\frac{\sum_{i=1}^{N-1}\sum_{j=i+1}^{N}dist(p_i,p_j)}{N(N-1)/2} \tag{8} div=N(N1)/2i=1N1j=i+1Ndist(pi,pj)(8)

​其中 N N N 是通过测试程序的数量。在步骤 t t t 一旦生成通过测试程序, L L M 4 C B I LLM4CBI LLM4CBI 通过以下线性组合等式获得的相似性和多样性度量来评估当前通过测试程序集的有效性:
Q t = n ( α × d i v t + ( 1 − α ) × s i m t ) (9) Q_t = n(\alpha \times div_t + (1 - \alpha) \times sim_t) \tag{9} Qt=n(α×divt+(1α)×simt)(9)
​其中系数 α \alpha α 表示方程中多样性和相似性线性组合的权重因子。此外方程还包含另一个系数 n n n,它对应于通过测试程序集的大小 。随后, L L M 4 C B I LLM4CBI LLM4CBI 根据与表示为 t − 1 t − 1 t1 的前一时间步相比是否能够增强通过程序集的整体质量来确定是否接受生成的通过测试程序。以下公式概述了相对于前一时间步长的增强质量的计算。
Δ Q t = Q t − Q t − 1 (10) \Delta Q_t = Q_t - Q_{t-1} \tag{10} ΔQt=QtQt1(10)
​ 然而,在每种状态下,仅选择一个提示来生成通过的测试程序,并且根据不同的错误,提示的性能可能会有很大差异。 为了平衡提示不同性能的影响, L L M 4 C B I LLM4CBI LLM4CBI 结合了当前时间步长改进和当前提示的历史累积改进。 该组合值作为当前时间步获得的实际奖励,定义如下:
R t = ∑ i = 1 t Δ Q i T ( m j ) (11) R_t = \frac{\sum_{i=1}^{t}\Delta Q_i}{T(m_j)} \tag{11} Rt=T(mj)i=1tΔQi(11)
​这里的 T ( m j ) T(m_j) T(mj) 表示选择 m j m_j mj(提示)来改变失败的测试程序的次数。 如果第 i t h i^{th} ith 个时间步选择的提示不是 m j m_j mj,则 Q i Q_i Qi 定义为零 ( Δ Q i = 0 \Delta Q_i = 0 ΔQi=0 ); 如果所选提示为 m j m_j mj,则使用公式 10 计算 Q i Q_i Qi

计算优势损失

​为了有效地考虑未来因素,A2C 包含了优势损失函数。 该函数旨在解决两个神经网络中的高方差问题并防止收敛到局部最优。 优势损失函数如下:
A l o s s ( t ) = ∑ i = t t + u ( γ i − t R i ) + γ u + 1 P R ( t + u ) − P R t (12) Aloss(t) = \sum_{i=t}^{t+u}(\gamma^{i-t}R_i) + \gamma^{u+1}PR(t+u) - PR_t \tag{12} Aloss(t)=i=tt+u(γitRi)+γu+1PR(t+u)PRt(12)
​变量 u u u 表示 CNN 在预测潜在奖励时考虑未来 u u u 个连续的状态和动作。 γ \gamma γ 表示分配给实际未来奖励的权重。 P R ( t + u ) PR(t+u) PR(t+u) P R t PR_t PRt 分别表示由 CNN 确定的第 ( t + u ) (t + u) (t+u) 和第 t t t 个时间步的预测潜在奖励。 使用优势函数计算出的损失, L L M 4 C B I LLM4CBI LLM4CBI 根据以下公式继续更新 ANN 和 CNN 的权重。
ω = ω + β ∂ ( l o g P ω ( a t ∣ s t ) A l o s s ( t ) ) ∂ ω (13) \omega = \omega + \beta \frac{\partial (log P_\omega(a_t|s_t)Aloss(t))}{\partial \omega} \tag{13} ω=ω+βω(logPω(atst)Aloss(t))(13)
​其中 s t s_t st 表示当前状态, a t a_t at 表示相应的动作。 P ω ( a t ∣ s t ) A l o s s ( t ) P_\omega(a_t|s_t)Aloss(t) Pω(atst)Aloss(t) 表示 ANN 和 CNN 中基于参数 ω \omega ω 在状态 s t s_t st 执行动作的概率。 β \beta β 表示权重更新的学习率。

轻量级测试程序验证

​现有的研究很少关注突变测试程序的有效性。

  • 这些方法生成的测试程序可能包含未定义的行为
  • 现有方法可能会生成没有测试预言机的测试程序,这也会影响错误隔离的有效性
  • 现有的研究并没有意识到他们在变异过程中所犯的错误,因此在变异测试程序时经常犯同样的错误。

为了解决上述限制, L L M 4 C B I LLM4CBI LLM4CBI 设计了语义验证来过滤掉语义上无效的测试程序,并利用测试预言机验证来修复违反测试预言机的测试程序。 另外, L L M 4 C B I LLM4CBI LLM4CBI 会收集 LLMs 生成的测试程序中的所有验证错误,并将此类信息作为反馈提示提供给 LLMs,以避免重复同样的错误。

程序语义验证

​本文选择对可能包含任何类型的未定义行为的新生成的测试程序进行静态分析检查。 基于 Frama-C 的分析功能,本文在 L L M 4 C B I LLM4CBI LLM4CBI 中选择五种不同类别的未定义行为:

  • 内存访问:无效的内存访问,例如越界读取或越界写入
  • 移位:右移或左移操作的 RHS(右移)或 LHS(左移)操作数无效
  • 索引范围:访问数组的越界索引
  • 初始化:访问未初始化的左值,即使用未初始化之前的变量
  • 除零:数字除以零

​如果检测到上述一种未定义行为,语义错误信息将更新到 LLMs,以避免重复相同的错误。

如果一个新的测试程序包含除零行为,则在步骤 8 中会向 LLMs 提供额外的提示:“上述程序包含一种未定义的除零行为,请不要再次生成此类测试程序”。

测试预言机验证

​程序语义验证后还需要检查生成的测试程序是通过还是失败。 根据编译器错误的类型(即崩溃 bug 和错误代码 bug), L L M 4 C B I LLM4CBI LLM4CBI 考虑了两种类型的测试预言机 :

  • 关于崩溃 bug(即使用某些编译选项编译测试程序时,编译器崩溃),测试预言机是使用相同的编译选项编译生成的测试程序时编译器是否仍然崩溃。
  • 对于错误代码 bug(即编译器错误地编译了一个测试程序,没有任何失败信息,导致测试程序在不同编译选项下执行结果不一致),测试预言是生成的测试程序在与之前不一致的编译选项下是否仍然产生不一致的执行结果。

​与 DiWi 和 RecBi 类似,LLMs 可能不会在生成的测试程序中放入测试预言机; 因此本文应用另一种启发式验证来检查丢失的预言机。 具体来说,本文检查生成的测试程序是否包含与给定的失败测试程序相同数量的 abort 或 printf 语句。

实验

实验准备

L L M 4 C B I LLM4CBI LLM4CBI 实施利用 OClint (v22.02)、srcSlice (v1.0)、Gcov (v4.8.0) 和 PyTorch。

  • OClint:计算每个语句的圈复杂度
  • srcSlice :获取在程序中定义和使用的变量的数据流复杂度
  • Gcov:收集编译器覆盖范围信息
  • PyTorch:支持 A2C 框架

​对于测试程序的验证,采用 Frama-C (Phosphorus-20170501) 来检查测试程序的语义有效性,对于 LLMs 组件采用 GPT-3.5 作为 L L M 4 C B I LLM4CBI LLM4CBI 中的默认 LLMs。编译器使用 GCC 和 LLVM 作为研究对象来评估 L L M 4 C B I LLM4CBI LLM4CBI 的有效性,对于基准使用由 120 个真实编译器错误组成的基准测试,平均分布 60 个 GCC 错误和 60 个 LLVM 错误。

​关于评估指标如下:

  • Top-N:该指标表示有效隔离并包含在 Top-N 位置内的错误数量, Top-N 的值越高表示方法的性能越好
  • MFR:该指标表示每个错误排名列表中第一个错误文件的平均排名,MFR的值越小越好
  • MAR:该指标衡量每个错误的排名列表中每个错误文件的平均排名的平均值,与MFR类似,MAR的值越小越好
研究问题

​在本实验中,旨在回答以下问题:

  • 在编译器错误隔离的有效性和效率方面, L L M 4 C B I LLM4CBI LLM4CBI 能否超越最先进的方法(即 DiWi 和 RecBi )?
  • 每个主要组件,即精确提示生成、记忆提示选择和轻量级测试程序验证,能否对 L L M 4 C B I LLM4CBI LLM4CBI 做出贡献?
  • L L M 4 C B I LLM4CBI LLM4CBI 可以轻松地与其他 LLMs 一起扩展以完成编译器错误隔离任务吗?
实验一
实验设置

​本文针对问题一做以下两个实验设置:

  • 以相同的运行时间(即一小时)终止
  • 当生成相同数量(即 10 个)的通过测试程序时终止
设置一结果

​比较结果如表 2 所示。第一列代表编译器,第二列显示不同的方法。 第 3-10 列提供从每种方法 10 次运行获得的平均值得出的 Top-N 指标,包括 Top-N 的数量(即 Num. Top-N)和 L L M 4 C B I LLM4CBI LLM4CBI 实现的改进(即 ⇑Top−N (% )) 。 第 11-14 列代表 MFR(平均第一排名)和 MAR(平均排名)指标,以及 L L M 4 C B I LLM4CBI LLM4CBI 实现的改进。

表2. 编译器错误隔离效果与两种最先进方法的比较(在 RQ1 中的 SETTING-1 下)

在这里插入图片描述

L L M 4 C B I LLM4CBI LLM4CBI 通过成功隔离 Top-1、Top-5、Top-10 和 Top-20 文件中的 16.80、47.60、70.80 和 92.80 个编译器错误来展示其功能。 这比 DiWi 分别提高了 69.70%、21.74%、17.02%和13.59%,比 RecBi 分别提高了24.44%、8.92%、10.28% 和 9.31%。 L L M 4 C B I LLM4CBI LLM4CBI 为 GCC 实现的 MFR 和 MAR 值为 15.36 和 14.45, LLVM 的相应值为 15.60 和 15.90。

​评估表明, L L M 4 C B I LLM4CBI LLM4CBI 与 DiWi 和 RecBi 相比在所有指标以及 GCC 和 LLVM 编译器方面均优于 DiWi 和 RecBi,即 L L M 4 C B I LLM4CBI LLM4CBI 比 DiWi 和 RecBi 能够及时、准确地隔离更多的编译器错误。

设置二结果

​实验结果如表 3 所示,对于所有 120 个错误,可以知道 L L M 4 C B I LLM4CBI LLM4CBI 也优于对比方法( DiWi 和 RecBi ), L L M 4 C B I LLM4CBI LLM4CBI 可以隔离更多错误并保持最低的 MFR 和 MAR。

表3. 编译器错误隔离效果与两种最先进方法的比较(在 RQ1 中的 SETTING-2 下)

在这里插入图片描述

​对于效率方面,图 5 展示了详细的性能结果。 图 5(a)中的 x 轴显示了 GCC 和 LLVM 中的不同方法,y 轴表示隔离每个 bug 所花费的时间,从图中可以观察到 L L M 4 C B I LLM4CBI LLM4CBI 在隔离大多数错误时花费的时间更少。本文还计算了 L L M 4 C B I LLM4CBI LLM4CBI 实现的加速。 具体按照下面的等式来计算加速比:
T b a s e l i n e − T L L M 4 C B I T b a s e l i n e × 100 (14) \frac{T_{baseline} - T_{LLM4CBI}}{T_{baseline}} \times 100 \tag{14} TbaselineTbaselineTLLM4CBI×100(14)
​其中 T b a s e l i n e T_{baseline} Tbaseline 表示基线(即 DiWi 和 RecBi)用于生成 10 个通过测试程序平均花费的时间, T L L M 4 C B I T_{ LLM4CBI } TLLM4CBI 描述了我们提出的 L L M 4 C B I LLM4CBI LLM4CBI 平均生成相同数量的通过测试程序所花费的时间。 图 5(b)显示了结果,可以看到对于 GCC 和 LLVM, L L M 4 C B I LLM4CBI LLM4CBI 能够比 DiWi 和 RecBi 分别实现 53.19% 和 64.54% 以及 47.31% 和 63.19% 的性能提升。 与设置 2 相比,设置 1 中 L L M 4 C B I LLM4CBI LLM4CBI 的总体结果更好,因为 L L M 4 C B I LLM4CBI LLM4CBI 可以在一小时内生成更多通过的测试程序,从而提高结果.

在这里插入图片描述

(a) 执行时间的分布                                          (b) LLM4CBI 对比 DiWi 和 RecBi 实现的加速比

图5. DiWi、RecBi 和 LLM4CBI 的性能比较(在 RQ1 中的 SETTING-2 下)

​为了进一步减少随机性带来的威胁,我们还使用 A ^ 12 \widehat{A}_{12} A 12 统计数据计算了 L L M 4 C B I LLM4CBI LLM4CBI 与基线之间差异的影响大小。 在上下文中,给定性能度量 M(例如 Top-1), A ^ 12 \widehat{A}_{12} A 12 统计量测量运行方法 A(例如 L L M 4 C B I LLM4CBI LLM4CBI )比运行另一种方法 B(例如 DiWi)产生更高 M 值的概率。 如果两种方法等效,则 A ^ 12 \widehat{A}_{12} A 12 =0.5;如果 A ^ 12 \widehat{A}_{12} A 12 = 0.9 意味着我们在 90% 的情况下使用方法 A 会获得比方法 B 更高的结果。

表4. SETTING-1 和 SETTING-2 下的统计测试结果( LLM4CBI 与最先进的方法)

在这里插入图片描述

​两种实验设置的总体统计结果如表 4 所示。表中 p p p 值小于0.05 显示 L L M 4 C B I LLM4CBI LLM4CBI 的性能明显优于最先进的方法(即 Diwi 和 RecBi )。 此外还可以观察到,设置一的所有效应大小都大于 0.71,设置二的所有效应大小都大于 0.68,这表明 L L M 4 C B I LLM4CBI LLM4CBI 比两种最先进的方法更有可能获得更好的结果。

实验总结

​评估表明, L L M 4 C B I LLM4CBI LLM4CBI 在两种不同的设置中显着优于两种最先进的方法:与 DiWi 和 RecBi 相比, L L M 4 C B I LLM4CBI LLM4CBI 能够有效且高效地生成用于编译器错误隔离的见证测试程序。

实验二
实验设置

​使用 L L M 4 C B I LLM4CBI LLM4CBI 的以下变体,旨在区分 L L M 4 C B I LLM4CBI LLM4CBI 中主要组件的效果:

  • L L M 4 C B I e p LLM4CBI_{ep} LLM4CBIep 使用简单的提示,无需数据流和控制流分析。 也就是说, L L M 4 C B I e p LLM4CBI_{ep} LLM4CBIep 只保留<mutation rule>,不依赖<variables>和<location>信息来填充设计的模式
  • L L M 4 C B I s p LLM4CBI_{sp} LLM4CBIsp 通过给出“最复杂的数据和控制流”来利用特定的提示。 这意味着 L L M 4 C B I s p LLM4CBI_{sp} LLM4CBIsp 将<variables>替换为“最复杂的变量”,将<location>替换为“在最复杂的语句中”
  • L L M 4 C B I r a n d LLM4CBI_{rand} LLM4CBIrand 随机选择一个提示,而不使用记忆的提示选择
  • L L M 4 C B I s e l n o v LLM4CBI_{selnov} LLM4CBIselnov L L M 4 C B I LLM4CBI LLM4CBI 的提示选择过程中删除了测试程序验证组件

​在这些变体中, L L M 4 C B I e p LLM4CBI_{ep} LLM4CBIep L L M 4 C B I s p LLM4CBI_{sp} LLM4CBIsp 旨在研究新的程序复杂性引导的提示生成模式是否有效,而 L L M 4 C B I r a n d LLM4CBI_{rand} LLM4CBIrand L L M 4 C B I s e l n o v LLM4CBI_{selnov} LLM4CBIselnov 的目标分别是了解记忆提示选择和测试程序验证是否有助于 L L M 4 C B I LLM4CBI LLM4CBI

实验结果

L L M 4 C B I LLM4CBI LLM4CBI 与比较变体方法之间的比较结果如表 5 所示。

表5. 编译器错误隔离效果与 LLM4CBI 的四个变体的比较

在这里插入图片描述

​ (1)就 GCC 和 LLVM 中所有 120 个错误的所有指标而言, L L M 4 C B I LLM4CBI LLM4CBI 优于 L L M 4 C B I e p LLM4CBI_{ep} LLM4CBIep L L M 4 C B I s p LLM4CBI_{sp} LLM4CBIsp L L M 4 C B I LLM4CBI LLM4CBI 能够隔离 Top-1 和 Top-5 文件中 37.70% 和 10.44% 的错误,并且在 MFR 和 MAR 方面也比 L L M 4 C B I e p LLM4CBI_{ep} LLM4CBIep 提高了 14.91% 和 14.61%。与 L L M 4 C B I e p LLM4CBI_{ep} LLM4CBIep L L M 4 C B I s p LLM4CBI_{sp} LLM4CBIsp 相比, L L M 4 C B I LLM4CBI LLM4CBI 在 Top-N 指标上显示出更好的结果。 此外 L L M 4 C B I LLM4CBI LLM4CBI 的 MFR 和 MAR 值也较小,表明 L L M 4 C B I LLM4CBI LLM4CBI 具有更好的编译器错误隔离能力。

​ (2) 对比 L L M 4 C B I r a n d LLM4CBI_{rand} LLM4CBIrand L L M 4 C B I LLM4CBI LLM4CBI 来研究记忆提示选择组件的贡献,结果表明 L L M 4 C B I LLM4CBI LLM4CBI 的性能也优于 L L M 4 C B I r a n d LLM4CBI_{rand} LLM4CBIrand。 具体而言, L L M 4 C B I LLM4CBI LLM4CBI L L M 4 C B I r a n d LLM4CBI_{rand} LLM4CBIrand 相比表现出更好的性能,在 Top-1、Top-5、Top-10、Top-20、MFR 和 MAR 指标方面显着提高了 33.33%、9.93%、9.60%、9.82%、10.57% 和 10.43% 。 这些结果强调了基于强化学习的记忆提示选择相对于随机策略的优越性。

​ (3)对于 GCC 和 LLVM 的所有指标, L L M 4 C B I LLM4CBI LLM4CBI 均优于 L L M 4 C B I s e l n o v LLM4CBI_{selnov} LLM4CBIselnov。 对于整体基准测试, L L M 4 C B I LLM4CBI LLM4CBI 可以比 L L M 4 C B I s e l n o v LLM4CBI_{selnov} LLM4CBIselnov 分别多隔离 23.53% 和 10.19% 的 Top-1 和 Top-5 文件错误。 在MFR和MAR方面, L L M 4 C B I LLM4CBI LLM4CBI 也比 L L M 4 C B I s e l n o v LLM4CBI_{selnov} LLM4CBIselnov 表现出15.42%和14.88%的显着改善。 这些结果提供了令人信服的证据,表明合并测试程序验证组件可以增强编译器错误隔离的有效性,从而证实其在 L L M 4 C B I LLM4CBI LLM4CBI 中的宝贵贡献。

​针对产生第三个实验结果的原因,主要是因为 L L M 4 C B I s e l n o v LLM4CBI_{selnov} LLM4CBIselnov 存在两个严重问题,由于未定义行为的存在而降低了 bug 隔离的性能,而 LLVM4CBI 可以通过过滤这些测试程序来有效减少威胁。两个问题如下:

  • 不可靠的测试预言机,会干扰对文件的可疑分数的计算
  • 处理未定义行为的编译器实现代码之间的干扰并引发编译器错误,导致影响错误隔离的有效性

​为了了解 L L M 4 C B I LLM4CBI LLM4CBI L L M 4 C B I LLM4CBI LLM4CBI selnov相比可以生成更多的有效测试程序,本文在设置 2 中运行 L L M 4 C B I LLM4CBI LLM4CBI L L M 4 C B I s e l n o v LLM4CBI_{selnov} LLM4CBIselnov,即当生成10个通过的测试程序时,两种方法都终止。实验结果如图6中的箱形图所示。

在这里插入图片描述

图6. LLM4CBIselnov生成的无效程序数

​其中 x 轴表示基准测试中包含 GCC bugs、LLVM bugs 和ALL(GCC+LLVM)bugs 的无效测试程序的错误数量,其中后面括号中的数字表示基准测试中生成无效测试程序的错误数量。可以观察到总共有30个(14个用于GCC,16个用于LLVM)错误, L L M 4 C B I s e l n o v LLM4CBI_{selnov} LLM4CBIselnov 为它们生成了无效的测试程序。y 轴表示这些基准中无效测试程序数量的分布,结果显示GCC、LLVM和ALL的平均无效测试程序数量分别为2.57、2.50和2.53。这意味着 L L M 4 C B I LLM4CBI LLM4CBI 能够在相同的实验设置中比 L L M 4 C B I s e l n o v LLM4CBI_{selnov} LLM4CBIselnov 多生成 2.5 个有效的测试程序。

​最后针对表 5 的实验结果进行了统计检验分析,使用与问题一相同的方法来计算本问题中的 p p p 值和有效大小 A ^ 12 \widehat{A}_{12} A 12。表 6 中示出了总体统计结果。表中小于 0.05 的 p p p 值表明 L L M 4 C B I LLM4CBI LLM4CBI 的性能显著优于各种变体方法。此外,我们可以观察到所有效应量都大于 0.70,这表明 L L M 4 C B I LLM4CBI LLM4CBI 比比较变体方法获得更好结果的概率更高。

表6. 统计检验结果( LLM4CBI 与变体方法)

在这里插入图片描述

实验总结

​所有这三个组成部分,包括精确的提示生产,记忆提示选择和轻量级测试程序验证,都有助于 L L M 4 C B I LLM4CBI LLM4CBI 的有效性。

实验三
实验设置

​表 7 列出了本研究中评估的 LLMs,Sizes反映了数十亿参数的模型大小,Release-Date显示了 LLMs 发布的时间,Popularity表示到2023年6月1日统计的 GitHub 获得的star或用户数量。本文评估了三个最具代表性和最受欢迎的 LLMs,并设计了三种新的变体方法,即 L L M 4 C B I LLM4CBI LLM4CBI (Alpaca)、 L L M 4 C B I LLM4CBI LLM4CBI (Vicuna)和 L L M 4 C B I LLM4CBI LLM4CBI (GPT4ALL),通过替换 L L M 4 C B I LLM4CBI LLM4CBI 中的 LLMs 来回答此问题。本文将在一小时内运行这些变体,然后比较每种方法的前N名指标。

表7. 在 LLM4CBI 中评估开源 LLMS

在这里插入图片描述

实验结果

表8. LLM4CBI 中不同LLMS的编译器 BUG 隔离能力(1小时时限)

在这里插入图片描述

​表 8 显示了所有设计的变体方法,即 L L M 4 C B I LLM4CBI LLM4CBI (Alpaca)、 L L M 4 C B I LLM4CBI LLM4CBI (Vicuna)和 L L M 4 C B I LLM4CBI LLM4CBI (GPT4ALL)有助于编译器错误隔离任务。结果表明,不同的 LLMs 可以有不同的能力,生成测试程序的编译器错误隔离,GPT-3.5(作为默认模型在 L L M 4 C B I LLM4CBI LLM4CBI )相比其他 LLMs 实现了最好的性能。有以下几个原因使得其他LLM的性能不如 L L M 4 C B I LLM4CBI LLM4CBI 中使用的GPT-3.5:

  • 代码生成能力有限。 尽管其他 LLMs 的目标是与 GPT-3.5 或 GPT-4 相当的性能,但它们可能仍然受到训练数据规模的限制。
  • 性能问题。 运行 LLMs 通常需要强大的 GPU 才能获得更好的性能。 由于本实验仅在没有 GPU 的 CPU 上运行,因此其他 LLMs 的响应时间更长。

​为了进一步支持用其他 LLMs 替换 ChatGPT 的潜在用途,本文将运行设置更改为生成相同数量(即 5 个)的通过测试程序,结果如表 9 所示。 从表中可以观察到使用其他 LLMs 可以比之前的两项研究(DiWi 和 RecBi)表现更好。

表9. LLM4CBI 中不同 LLMS 的编译器 Bug 隔离能力(生成相同数量的通过测试程序)

在这里插入图片描述

实验总结

L L M 4 C B I LLM4CBI LLM4CBI 是可扩展的,这意味着 LLMs 组件(即,GPT-3.5)可以容易地被其它 LLMs(例如,Alpaca、Vicuna和GPT4ALL)替换,同时与相关研究相比仍然取得了合理的结果。

总结

局限
  • 使用的SBFL方法存在领带问题,即多个代码元素获得相同的可疑分数
  • LLMs 可能会生成语法无效的程序,从而降低 L L M 4 C B I LLM4CBI LLM4CBI 的有效性
启发
  • 交互式错误隔离。从利用用户反馈的交互式故障定位技术部分得出可以将交互式元素纳入 L L M 4 C B I LLM4CBI LLM4CBI
  • 智能错误隔离。为了使 LLMs 能够获得更详细的信息,可以使他们能够对潜在的错误文件做出明智的决策,比如指导 LLMs 采用不同的评估策略并微调 LLMs 的参数以便于编译器错误隔离

相关知识链接

@article{Tu_2024,
   title={Isolating Compiler Bugs by Generating Effective Witness Programs With Large Language Models},
   volume={50},
   ISSN={2326-3881},
   url={http://dx.doi.org/10.1109/TSE.2024.3397822},
   DOI={10.1109/tse.2024.3397822},
   number={7},
   journal={IEEE Transactions on Software Engineering},
   publisher={Institute of Electrical and Electronics Engineers (IEEE)},
   author={Tu, Haoxin and Zhou, Zhide and Jiang, He and Yusuf, Imam Nur Bani and Li, Yuxian and Jiang, Lingxiao},
   year={2024},
   month=jul, pages={1768–1788} 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值