使用大型语言模型的自动化安卓错误重现

互联不一般哥 2024-06-28 18:56:46

Prompting Is All You Need: Automated Android Bug Replay with Large Language Models

Sidong Feng1, Chunyang Chen1

引用

Feng S, Chen C. Prompting Is All You Need: Automated Android Bug Replay with Large Language Models[C]//Proceedings of the 46th IEEE/ACM International Conference on Software Engineering. 2024: 1-13.

论文:https://dl.acm.org/doi/abs/10.1145/3597503.3608137

摘要

本文提出了AdbGPT,一种通过提示工程自动重现错误报告中的错误的新型轻量级方法。与传统方法依赖手工制作的模式和预定义的词汇列表不同,AdbGPT无需任何训练和硬编码,利用少量示例学习和链式思维推理,从大型语言模型(LLMs)中引出人类知识和逻辑推理,以类似开发人员的方式完成错误重现。评估结果显示,AdbGPT在253.6秒内成功重现了81.3%的错误报告,性能优于现有最先进的基准和消融研究。

1 引言

近年来,移动应用程序获得了极大的普及。在Google Play和Apple App Store上,已经有超过380万个Android应用程序和200万个iPhone应用程序争取用户。一旦应用程序发布,其质量主要通过持续的维护活动来保证,而处理bug报告是最重要的维护任务之一。bug报告是描述软件bug的文档,包含发生了什么以及用户期望发生什么的信息。除了观察到的行为和预期行为外,bug报告通常还包含重现bug的步骤(S2Rs),以帮助开发人员复制和修正bug,尽管这需要相当多的工程努力。

为了加速bug维护,许多研究人员致力于提供自动化的bug重现解决方案。先前的研究应用自然语言处理(NLP)和机器学习(ML)技术从bug报告中提取S2R实体(即操作类型、目标组件、输入值和滚动方向),并使用随机或简单的引导探索算法进行bug重现。不幸的是,在许多情况下,bug报告中的S2R给先前的自动化重放方法带来了重大挑战,甚至专业开发人员手动重现时也会感到困难。首先,由于用户和开发人员之间的认知和词汇差距,S2R通常不清晰、不精确且含糊不清。

给定上述的S2R,从S2R中提取实体需要对语义和句法有深入的理解。例如,s2和s3中的两个“create”词在语义上并不相同;一个指的是“输入”,而另一个指的是“重复”前面的步骤。在s3中,要“创建另一个”,需要深刻理解前面步骤中的具体创建步骤。此外,一个步骤可能包含多个子步骤,例如s5,可以分为“返回书签”和“将‘a’的名字更改为‘b’”。最后,在s5中,连词“after”改变了步骤的时间顺序,意味着应该先执行“将‘a’的名字更改为‘b’”,然后再执行“返回书签”。这些挑战超出了预定义的词列表,难以用基于先前工作的特定模式来解决。其次,S2R往往是不完整的,来自1300多个开源项目的开发人员向GitHub写信表达了他们对S2R在bug报告中常常缺失的沮丧,并请求一个能鼓励用户包含S2R的解决方案。因此,先前的自动化方法无法复制这些bug。

新兴的大型语言模型(LLMs),如GPT-3、PaLM、RoBERTa、T5,经过超大规模语料库训练,在自然语言理解和逻辑推理方面表现出色。OpenAI的GPT-3拥有1750亿参数,能够智能检索信息和指导完形填空。基于GPT-3.5的ChatGPT展示了其理解人类问题的能力,并作为助手与用户互动。受LLMs在信息检索和角色扮演方面进步的启发,我们将LLMs用于从bug报告中提取实体并指导动态GUI组件重现。

本文提出了一种名为AdbGPT的创新方法,使用LLMs自动重现bug。AdbGPT包括S2R实体提取和引导重现两个阶段,采用提示工程方法,极其轻量。我们向LLMs提供实体规格知识,并通过少样本学习和开发者推理链来帮助识别S2R实体。随后,我们提示LLMs根据GUI屏幕动态指导重现,并提出新颖的GUI编码算法确保信息完整性和有效性。

我们进行了全面评估以衡量AdbGPT的有效性和效率。在从88个bug报告中提取S2R实体方面,我们的方法准确率达到了90.4%和90.8%。在引导bug重现方面,我们的方法成功重现了81.3%的bug,优于基线方法,并且每个bug报告平均节省了1105.17秒。此外,我们通过对5个实际bug报告的重现进行小规模用户研究,初步证实了AdbGPT在促进bug重现方面的有用性。总的来说,本文的贡献如下:给定上述的S2R,从S2R中提取实体需要对语义和句法有深入的理解。例如,s2和s3中的两个“create”词在语义上并不相同;一个指的是“输入”,而另一个指的是“重复”前面的步骤。在s3中,要“创建另一个”,需要深刻理解前面步骤中的具体创建步骤。此外,一个步骤可能包含多个子步骤,例如s5,可以分为“返回书签”和“将‘a’的名字更改为‘b’”。最后,在s5中,连词“after”改变了步骤的时间顺序,意味着应该先执行“将‘a’的名字更改为‘b’”,然后再执行“返回书签”。这些挑战超出了预定义的词列表,难以用基于先前工作的特定模式来解决。其次,S2R往往是不完整的,来自1300多个开源项目的开发人员向GitHub写信表达了他们对S2R在bug报告中常常缺失的沮丧,并请求一个能鼓励用户包含S2R的解决方案。因此,先前的自动化方法无法复制这些bug。

新兴的大型语言模型(LLMs),如GPT-3、PaLM、RoBERTa、T5,经过超大规模语料库训练,在自然语言理解和逻辑推理方面表现出色。OpenAI的GPT-3拥有1750亿参数,能够智能检索信息和指导完形填空。基于GPT-3.5的ChatGPT展示了其理解人类问题的能力,并作为助手与用户互动。受LLMs在信息检索和角色扮演方面进步的启发,我们将LLMs用于从bug报告中提取实体并指导动态GUI组件重现。

本文提出了一种名为AdbGPT的创新方法,使用LLMs自动重现bug。AdbGPT包括S2R实体提取和引导重现两个阶段,采用提示工程方法,极其轻量。我们向LLMs提供实体规格知识,并通过少样本学习和开发者推理链来帮助识别S2R实体。随后,我们提示LLMs根据GUI屏幕动态指导重现,并提出新颖的GUI编码算法确保信息完整性和有效性。

我们进行了全面评估以衡量AdbGPT的有效性和效率。在从88个bug报告中提取S2R实体方面,我们的方法准确率达到了90.4%和90.8%。在引导bug重现方面,我们的方法成功重现了81.3%的bug,优于基线方法,并且每个bug报告平均节省了1105.17秒。此外,我们通过对5个实际bug报告的重现进行小规模用户研究,初步证实了AdbGPT在促进bug重现方面的有用性。总的来说,本文的贡献如下:

这是首个将大型语言模型(LLMs)应用于bug报告分析和GUI指导的工作,为软件工程任务中的新机会铺平了道路。我们提出了一种轻量级的方法AdbGPT,利用提示工程、少样本学习和链式思维推理,利用LLMs的知识进行自动化bug重现。综合实验,包括AdbGPT的性能评估及其详细的定性分析,揭示了LLMs在bug重现中的能力。我们还通过用户研究进一步展示了我们方法的实用性。

2 方法

针对一个bug报告和应用程序,我们提出了一种自动化方法来提取S2R实体,并基于当前的GUI状态重现每一步骤以触发应用程序中的bug。我们方法的概述如图1所示,分为两个主要阶段:(i) S2R实体提取阶段,提取定义重现bug报告每一步骤的S2R实体,包括操作类型、目标组件、输入值或滚动方向;(ii) 引导重现阶段,将S2R中的实体与GUI状态匹配,重复bug重现步骤。虽然许多工作尝试使用不同的特设方法来解决这两个阶段,例如使用自然语言处理提取S2R实体和贪心算法引导重现,我们的方法相比之下显得尤为轻量,我们通过创新的提示工程利用单一的LLM解决这两个阶段的问题。

图1:AdbGPT的概述

2.1 S2R实体提取

我们方法的第一阶段是理解、分析、组织和提取bug报告中S2R文本的实体。具体来说,我们利用从大规模训练语料库中学习到知识的LLMs,来深入理解bug报告中呈现的潜在实体。如第1节所述,一个步骤可以用不同的方式和不同的词语来表达。因此,我们首先向LLMs提供有关实体规范的信息,包括可用的操作和操作原语,这些可以映射到设备重现的指令上。为了帮助LLMs识别我们的任务并推断S2R实体,我们提供了一些条件示例,如S2R作为输入,链式思维作为推理,以及最终实体作为输出。提示的一个示例如表1所示。通过这种上下文中的少样本学习,我们以同样的方式提示LLMs从测试bug报告中提取S2R实体。

2.1.1 可用操作

与目标组件和输入值不同,目标组件和输入值根据当前GUI的不确定性和动态性而变化,而可以与设备交互的操作保持一致。我们确定了表1中所示的五种标准操作,包括点击、滚动、输入、双击和长按。虽然还有其他定制操作,如捏合、多手势等,但它们不太常见。为了简洁起见,本文我们集中讨论常用的操作。

2.1.2 操作原语

由于操作的上下文不同,每个操作需要不同的原语集来表示实体。例如,点击操作需要一个目标组件进行交互,如GUI中的按钮。因此,我们将其表述为[点击] [组件]。同样,对于双击和长按操作,我们使用类似的语言原语进行表述。滚动操作需要指明滚动方向,如向上或向下,表述为[滚动] [方向]。输入操作涉及将特定值输入到文本字段组件的过程。为了表述这一操作,我们使用原语[输入] [组件] [值]。

表1:S2R实体提取的提示工程示例

提示类型

实例化

可用操作

点击、滚动、输入、双击、长按

操作原语

[点击] [组件]、[滚动] [方向]、[输入] [组件] [值]、[双击] [组件]、[长按] [组件]

示例输入

1. 打开书签

2. 点击“添加新书签”并创建一个名为“a”的书签

3. 创建另一个名为“b”的书签

4. 点击“a”

5. 将“a”的名字更改为“b”后返回书签

6. 应用程序崩溃

链式思维

第一步是“打开书签”。操作是“打开”,目标组件是“书签”。然而,在可用操作列表中没有明确的“打开”。因此,我们选择最接近的语义操作“点击”。按照操作原语,第一步的实体是[点击] [“书签”]。

第二步是“点击‘添加新书签’并创建一个名为‘a’的书签”。由于连接词“并”,这一步可以分为两个子步骤,“点击‘添加新书签’”和“创建一个名为‘a’的书签”...

第三步是“创建另一个名为‘b’的书签”。由于其语义意义,这一步是要重复前面的步骤以添加另一个名为“b”的书签。因此,它实际上应该是第二步...

第四步是“点击‘a’”...

第五步是“将‘a’的名字更改为‘b’后返回书签”。由于连接词“后”,这一步可以分为两个子步骤,“返回书签”和“将‘a’的名字更改为‘b’”。连接词“后”还改变了子步骤的时间顺序,应先执行“将‘a’的名字更改为‘b’”,然后是“返回书签”...

第六步是“应用程序崩溃”。这一步没有任何操作。

示例输出

总的来说,提取的S2R实体是:

1. [点击] [“书签”]

2. [点击] [“添加新书签”]

3. [输入] [“名字”] [“a”]

4. [点击] [“添加新书签”]

5. [输入] [“名字”] [“b”]

6. [点击] [“a”]

7. [输入] [“名字”] [“b”]

8. [点击] [“返回”]

2.1.3 上下文学习

一个具有代表性的示例有助于模型提取完成任务所需的特定知识和抽象。常见的示例选择策略是从数据集中随机抽样。然而,随机的bug报告可能无法涵盖任务的所有复杂性,从而限制LLMs全面理解问题的能力。为了选择具有代表性的示例,我们招募了三名有四年修复和分类bug经验的专业开发人员。每位开发人员被要求独立审查从先前工作中收集的大量bug报告并选择具有挑战性的报告,并选择了7个具有挑战性的。开发人员精炼数据集直到达成共识,总共收集了15个bug报告作为我们的代表性数据集,其中一个示例在表1中展示。

2.1.4 链式思维推理

提供链式思维推理,使LLMs能够生成一系列连贯的中间步骤,导致合理的输出。为了采用这种范式,我们要求开发人员独立为代表性数据集中的每个bug报告编写链式思维推理,遵循先前的研究。然后,三名开发人员与第一作者会面,讨论每个bug报告的最佳推理,旨在表达清晰的逐步思维过程,最终得出S2R实体。链式思维推理的一个示例在表1中展示。在第1步中,我们明确解释了操作(“打开”)和目标组件(“书签”)。由于“打开”不在可用操作中,我们将其映射到最接近的语义操作“点击”。对于第2步和第5步,我们明确解释了导致多个子步骤和时间顺序变化的连接词。第3步更为复杂,我们明确解释了该步骤的语义意义及其与前几步的关系。总体而言,这种逐步思维导致最终提取的S2R实体输出。

2.1.5 提示构建

我们将上述信息在表1中结合为输入提示,即(⟨可用操作⟩ + ⟨操作原语⟩ + ⟨示例输入⟩ + ⟨链式思维⟩ + ⟨示例输出⟩)。请注意,由于LLMs的鲁棒性,提示句子不需要严格遵循语法规则。接下来,我们将测试bug报告作为测试提示输入并查询S2R实体。由于少样本学习和链式思维推理的优势,LLMs将始终生成一个数字列表,以与我们示例输出相同的格式表示提取的S2R,这可以使用正则表达式进行推断。

2.2 引导重现

第二阶段是探索应用程序,将提取的S2R实体与一系列GUI事件匹配,以重现bug。一种看似简单的解决方案是使用词汇计算将提取的组件与当前GUI屏幕上显示的组件文本进行匹配。然而,如第1节所述,这种组件映射过程受到缺失步骤问题的限制,即在GUI屏幕上没有匹配的组件。为了解决这个问题,我们采用了具有少样本学习和链式思维推理的LLMs来生成GUI屏幕上的动态指导,从而即使在步骤缺失的情况下也能自动重现步骤。使用LLMs进行GUI指导的一个挑战是它们只能处理适当大小的文本输入。为了帮助LLMs内在地理解GUI屏幕,我们提出了一种新方法,将GUI转换为LLMs能够理解的特定领域提示。给定每个步骤及其当前的GUI屏幕,我们提示LLMs生成要操作的目标组件,最终触发bug。

2.2.1 GUI编码

为了提示LLMs根据GUI引导bug重现,我们需要动态描述当前的GUI屏幕及其包含的组件信息,如文本、类型、图像等。为了设计我们的屏幕编码,我们利用了这样一个见解:如果我们的GUI编码落在LLMs的训练数据分布内,它更有可能表现更好。由于大多数LLMs的训练数据通常是从原始网页中抓取的,我们通过将视图层次数据转换为HTML语法来在文本中编码GUI。然而,这有两个基本限制。首先,视图层次中的本地类不总是与HTML标签匹配,例如,层次中的<CheckBox>对应于<input type="checkbox">和<label>的组合。其次,包括所有GUI组件的属性将导致过长的HTML文本,可能会影响LLMs的理解。

因此,我们采用启发式方法将GUI屏幕的视图层次转换为HTML文本。图2展示了GUI屏幕和编码后的HTML文本的示例。编码是通过使用深度优先搜索遍历视图层次树进行的。我们将每个节点转换为HTML语法,保留视图层次中相关的属性子集。

resource_id:组件的唯一资源IDclass:本地组件类型text:组件的文本content_desc:视觉组件的描述

我们首先使用一个特设的启发式方法将类映射到具有类似功能的HTML标签。然后,我们在HTML标签的开闭标签之间插入组件的文本属性,遵循HTML中文本的标准语法。resource_id属性通常包含由开发人员编写的关于组件功能或目的的附加描述。最后,我们按照视图层次树中的遍历顺序,将唯一的数字索引插入每个元素作为“id”属性。

GUI 屏幕 HTML 语法

图2:GUI编码的图例

2.2.2 上下文学习

手动收集用于引导重现的代表性示例(例如检查是否缺少步骤和识别可能触发bug的目标组件)可能非常费力且耗时。为此,我们旨在合成一些代表性示例,以帮助LLMs理解S2Rs中缺失步骤的挑战。为了合成缺失的步骤,我们随机省略bug报告中的0到2个步骤。针对这些可能缺少步骤的示例,我们请三位开发人员验证这些示例,并按照第3.1.3节中的类似程序选择具有挑战性的示例,最终形成5个代表性示例的数据集。

表2:引导重现的提示工程示例

提示类型

实例化

示例1

GUI编码

<html> ... </html>

示例输入

如果我需要[点击] [“登录”],我应该在GUI上操作哪个组件id?

链式思维

当前GUI屏幕上没有明确的“登录”组件。然而,有一个语义最接近的组件“登录”按钮。“登录”组件的id属性是6。因此,我们可以在屏幕上操作[id=6]。

示例输出

[id=6]

示例2

GUI编码

<html> ... </html>

示例输入

如果我需要[点击] [“暗模式”],我应该在GUI上操作哪个组件id?

链式思维

当前GUI屏幕上没有明确和语义相似的“暗模式”组件,因此它看起来是一个[缺失]步骤。然而,“暗模式”可能与屏幕上的“显示”按钮相关。“显示”组件的id属性是1。因此,我们可以在屏幕上操作[id=1]组件。

示例输出

[缺失] [id=1]

2.2.3 链式思维推理

我们进一步请三位开发人员编写他们的链式思维推理,形成最终输出。记录过程类似于第2.1.4节中的前一阶段,先是独立编写,然后讨论选择最具代表性的逐步推理。我们在表2中展示了两个具体的推理示例。在示例1中,我们明确解释了一个引导步骤的语义示例,该步骤并不完全与S2R(“登录”)和GUI(“登录”)组件匹配。在示例2中,我们明确解释了一个引导缺失步骤的示例,其中包含一个潜在的目标组件和一个缺失标签。

2.2.4 提示构建

我们提供了一些带有链式思维的示例作为输入提示,即(⟨GUI编码⟩ + ⟨示例输入⟩ + ⟨链式思维⟩ + ⟨示例输出⟩)。给定S2R实体提取阶段生成的每个步骤和当前GUI编码作为测试提示,我们接下来查询目标组件选择以重现步骤。请注意,我们使用“id”来推断目标组件,这比拼出完整的HTML标签更高效且节省空间。在缺失步骤的情况下,输出提示将额外预测一个“缺失”标签,以允许我们在应用程序中迭代探索步骤,直到找到目标组件。如果LLMs未能探索出潜在的目标组件以恢复缺失步骤(例如,推断中没有“id”),我们会返回到上一步并提示模型探索新组件。

2.3 实现

对于LLMs,我们使用了最先进的ChatGPT模型。根据小规模的试点研究,我们将少样本学习示例的数量设置在1到3之间。这是可以适应LLM隐含的最大输入令牌(即ChatGPT为4,096个)的示例数量。由于LLMs可能生成冗长的输出(如重复的问题、链式思维推理等),我们使用“[]”来推断特定的预测,例如S2R实体提取的实体,以及引导重现的目标组件id属性或缺失步骤标志。注意,用户可能会模糊地描述输入操作,例如“输入名字”。为了解决这个问题,如果步骤中没有明确的输入值,我们设置一个“测试”值。

我们的AdbGPT实现为一个完全自动化的bug重现工具。具体来说,我们使用Genymotion [5]运行和控制虚拟Android设备,使用Android UIAutomator导出GUI视图层次,使用Android Debug Bridge (ADB)重现步骤。

3 实验评估

3.1 实验设置

在本节中,我们描述了用于自动评估AdbGPT性能的程序。由于我们的方法包含两个主要阶段,我们评估了AdbGPT的每个阶段,包括S2R实体提取和引导重现。

RQ1:我们的方法在提取S2R实体方面的准确性如何?

RQ2:我们的方法在引导bug重现方面的准确性如何?

RQ3:我们的方法在bug重现中的效率如何?

RQ4:我们的方法在实际开发环境中对开发人员的有用性如何?

对于RQ1,我们展示了我们方法在S2R实体提取方面的一般性能,并与最先进的基线方法进行了比较。此外,我们还展示了不同上下文学习变体(如少样本学习与零样本学习)之间的性能比较,以及通过对比有无链式思维的性能来评估推理的贡献。对于RQ2,我们进行了实验,以检查我们的方法是否能够触发目标组件,并与基线方法和消融研究进行了比较。对于RQ3,我们评估了我们的方法在bug重现中的运行时间开销。对于RQ4,我们进行了一项小规模的用户研究,以评估AdbGPT在实际开发环境中自动重现bug报告的感知有用性。

RQ1:S2R实体提取的性能

表3显示了我们方法在从bug报告中提取S2R实体方面的性能。我们的方法在所有指标上的性能显著优于其他基线方法,即在步骤提取和实体提取方面,平均比最佳基线方法(MaCa)准确39.3%和42.2%。此外,应用少样本学习和链式思维(提供带有中间推理的示例)可以使LLMs更好地理解任务,从而使性能从84.6%、87.6%提升到90.8%。

表3:S2R实体提取的性能比较

RQ2:引导重现的性能

表4显示了每个bug的可重现性详细结果。我们的方法在所有基线和消融研究中表现最佳,即平均81.3%,而ReCDroid、AdbGPT w/o Few和AdbGPT w/o CoT分别为45.8%、58.3%和62.5%。我们观察到,链式思维显著提高了AdbGPT的性能(即提高了18.8%),表明LLMs通过逐步处理任务可以更好地理解任务。

表4:引导重现的性能比较

RQ3 效率性能

表5显示了与基线方法的性能比较。我们的方法平均需要255.75秒来重现一个bug报告,即2.11秒从bug报告中提取S2R实体,253.64秒在设备中重现S2R。相比之下,ReCDroid方法平均需要1360.92秒,表明我们的方法效率较高,每次bug重现节省了近5倍的时间。值得注意的是,与ReCDroid相比,我们的方法在GUI屏幕上的重现指导时间加速了435%。这主要是由于S2R中的缺失步骤导致ReCDroid随机探索应用程序,反复进行耗时的来回探索。相比之下,我们的方法引导LLMs理解S2R上下文和当前GUI的语义,预测最可能的组件以弥合缺失步骤。我们预计,随着更先进的硬件,我们的方法的开销可以进一步加快。

表5:每个错误报告的平均运行时间的性能比较

RQ4 AdbGPT的有用性

表6显示了详细的实验结果。尽管大多数参与者能够按时完成bug重现,但AdbGPT重现bug报告的速度比参与者快得多(平均480.7秒对比269.4秒)。事实上,参与者的重现平均时间被低估了,因为他们在10分钟内未能重现3个bug,这意味着参与者可能需要更多时间来重现bug。相比之下,我们的自动化方法在7分钟内完成了所有任务。

表6:有用性评估的性能比较

转述:何晨曦

0 阅读:0

互联不一般哥

简介:感谢大家的关注