[读论文 ISSTA ’23]Tai-e: A Developer-Friendly Static Analysis Framework for Java by Harnessing the Good Designs of Classics

前言

比起啃代码,感觉还是啃论文舒服点,之前学习了南京大学李樾老师和谭添老师主讲的软件分析课程,对我的帮助很大,这里就来学习一下两位老师的代表作,这篇最初的关于Tai-e的论文。

背景

两位老师这篇论文翻译过来叫做:《利用经典优秀设计的Java开发人员友好型静态分析框架》,从标题也可以看出来,Soot的核心创新点其实就是它是一个开发者友好的静态分析框架,非常适合用户上手,也因此其实软件分析那个课的实验课其实就是利用tai-e完成任务,我后面也会去专门做一下这个实验。

两位老师在摘要部分就提到,尽管在过去的几十年里,静态分析已经取得了很大的进步,并且在这个领域出现了几个知名的框架(如Soot,Wala,SpotBugs和Checker),但是对于依赖它们来创建和实现分析的开发人员来说,这些框架并不是那么容易学习和使用的,因此他们通过遵循HGDC(利用经典优秀设计)原则,基于过去这些优秀框架的长处提出了Tai-e,Tai-e在IR、指针分析和开发新分析等几个方面的设计都是新颖的,最后实现了一个极其容易上手的污点分析框架。

这里他们提到了一个优秀的静态分析框架应该提供的关键组件:

  • 程序抽象。它需要提供一个抽象模型,包括IR、类层次结构等,来表示所有准备用于各种静态分析的程序元素
  • 基础分析(Fundamental Analyses)。它应该支持基础设施,允许分析开发人员使用分析友好的结构(如控制流和调用图)来实现经典的基于图的算法,并利用输入程序的抽象内存信息(如点对/别名关系)来构建复杂的分析
  • 新分析开发(New Analysis Development)。它应该提供一种机制来开发和集成任何新的分析,包括错误检测器和安全分析器等客户端,以及异常和反射分析等更基本的分析;
  • 多重分析管理。当需要多个分析协同完成某项分析任务时,它应该提供一种标准化的方法来管理它们(例如,配置它们的依赖关系或协作它们的输出)。

他们这篇论文的贡献主要有两个,第一个贡献是系统地探讨了各种经典 Java 静态分析框架的设计和实现,并讨论了它们针对不同关键分析组件的合理性,为构建更完善的静态分析基础设施提供了有用的资料和观点;第二个贡献是提出了Tai-e,一个根据 HGDC 原则从零构建的开发者友好的 Java 静态分析框架,Tai-e有几个新颖的设计:

  • Tai-e 提供了一个易于使用的 IR 用于开发分析:与 Soot 和 Wala 的 IR 相比,它能够生成更简洁的代码来实现静态分析算法,并且更容易理解其底层意图。
  • Tai-e 提供了一个高效的指针分析系统,该系统比其他框架更具可扩展性和效率,从而更容易创建各种新的指针分析算法。
  • Tai-e 引入了一个新颖的分析插件系统,可以轻松开发和集成新的分析(与指针分析交互),例如污点分析和异常分析等。

在这里两位老师举了很多使用者的发言来论述Tai-e是一个容易上手的框架(顺便拉踩其他框架)

  • 一位资深教授写信告诉他们:“我的学生告诉我,在Tai-e上编写代码非常流畅,Tai-e在可用性方面明显优于Soot。”
  • 一位来自某著名IT公司的高级静态分析工程师写信道:“在我看来,Tai-e的设计模型值得许多分析工具学习。我发现它可以帮助初级分析开发人员通过调试和阅读框架代码来快速理解分析算法;高级开发人员还可以基于Tai-e的可插拔分析系统快速集成他们的分析模块。Tai-e的整体分析过程清晰可控。”
  • 一位网友在技术博客中公开写道:“回想起来,Tai-e在指针分析和算法设计方面的能力无疑是优秀的,而Tai-e的整个框架设计和它的插件式编程,以及其他方面的设计思维,都远远超过了Soot……”(这句话来自于y4er的博客,不过把后面批评Tai-e的部分删了😂)

程序抽象

静态分析框架需要提供程序的抽象模型,包括IR、类型系统、类层次结构等,以表示静态分析方便获取的所有程序元素。Soot和Wala都有其特定的IR,Tai-e的IR很大程度上受到了他们的启发,但青出于蓝而胜于蓝,可以帮助分析开发人员实现更简洁的分析代码,并更好地理解IR的潜在意图。

Tai-e从多个角度增强了开发人员的友好性,包括:

  • IR的设计:与Soot相比,Tai-e区分了不同类型的赋值语句
  • 关联API设计:Tai-e采用具体的返回类型进行表达式检索,避免使用整数作为索引来表示变量,这与Soot和Wala分别有所不同
  • 程序元素(如值、类型和名称)的组织和可访问性:与Wala不同,Tai-e将所有与变量相关的信息集中到一个接口中,而不是将它们分布到不同的接口中。

得益于(1)和(2),Tai-e使开发人员能够编写更简洁的代码,例如可以在使用各种表达式和语句时避免不必要的条件检查和向下转换,此外得益于(3),开发人员能够轻松地学习Tai-e的功能,并允许他们更流畅地创建分析。

在Soot里,所有带 = 的语句都统一表示为 AssignStmt,不区分是 new、load、store、binary 还是 unary 等语句类型,优点自然是IR结构简单,缺点就是类型信息过于粗糙,导致需要大量的手动判断和强制类型转换,比如在例子里:

  • Line 2:只能用 AssignStmt 类型表示,没法更精细地表示它是一个 binary operation。
  • Line 5~6:右操作数需要 instanceof 类型检查,之后才能安全地强制转换。
  • Line 8:即使左操作数是 Local 类型(比如变量 x),getLeftOp() 还是返回最顶层接口 Value,用户要自己去转化成 Local
  • 类似的还有 IfStmt.getCondition(),返回的仍是 Value,而不是更具体的 ConditionExpr,这同样会引入不必要的转换

Wala 的做法使用更明确的类型,比如 SSABinaryOpInstruction 表示二元操作(Line 25),但它使用整型索引 int 来表示操作数,而不是直接存储变量或表达式对象,比如上面的 getUse() 返回 op2(int),代表第几个操作数,然后通过这个索引去 IR 的 symbol table 查询实际的值或常量(Line 37~39),这样导致调试和理解时不直观,且获取变量名和类型也要额外通过 IR(Line 32) 和 TypeInference(Line 34-35),增加了学习和使用成本。

Tai-e同时避免了Soot以及Wala中存在的问题,让Tai-e的IR更加容易上手,同时也引入了一些新的IR设计,使其更容易用于分析,比如为了便于指针分析,Tai-e在其IR中将每个变量v与其相关语句关联起来,一旦在分析过程中v的值发生变化,开发人员可以直接方便地通过IR检索到v的所有相关语句,以便进行进一步的操作,然后这里又放了一些用户的使用评价:

  • “Tai-e的IR简洁易懂,而Soot的则过于复杂。”
  • “Tai-e提供的IR api是不言自明的。”
  • “与Tai-e相比,Soot中对语句和变量的建模并不直接,在遍历图时产生一些不优美的逻辑实现。”
  • “与Soot相比,Tai-e IR的数据结构设计更加直观。例如,我可以通过Tai-e的语句接口直接检索语句的相关信息(不需要花费精力去寻找其他接口来收集信息)。”
  • “你很难通过Wala的许多api(用于IR,类层次结构等)直接获取信息。它通常需要联合使用多个api,导致代码冗长;但你基本上可以通过Tai-e的各种api直接获得目标信息。”
  • “Tai-e 提供了更简洁的 IR 语句表示。与其他分析工具相比,我们可以用更简洁的代码实现基于 Tai-e IR 的分析算法,这也提高了代码的可读性,方便团队成员维护代码。”

基础分析

指针分析

指针分析是最基本的静态程序分析之一,几乎所有其他程序都建立在它的基础上,这些经典的静态分析框架如Soot,Wala等都实现了相同的安德森风格的算法作为核心指针分析,但他们在几个关键点上有所不同:

  • 指向信息的点的表示
  • 处理上下文敏感性的上下文管理器
  • 用于堆对象建模的堆管理器
  • 用于传播点到信息的求解器

在这里论文里举了Tai-e与其他框架不同的两个设计点作为例子。

点到信息的表示

在指针分析中,每个变量可能”指向”(point-to)若干个对象(内存地址或抽象对象)。Points-to set 就是用来表示这些“被指向对象”的集合。例如,若变量 v 可能指向对象 20, 100, 3990, 3993,则它的 points-to set 是 {20, 100, 3990, 3993}

Spark 和 Wala 使用混合 points-to 集合,当集合很小时(如指向对象少),用数组来存储这些对象(节省空间和开销),当集合变大时,切换为使用位图(bit set),每一位代表一个对象是否被包含。但这里存在一个缺点,如果只有几个对象,绝大部分位都是 0,造成空间的浪费。

Tai-e使用类虚拟内存的稀疏位图(sparse bit set),对于传统的方式,即使只表示 {20, 100, 3990, 3993},也要维护完整的 4096 位,显然,大多数位是 0,空间严重浪费。而Tai-e 使用一种类似虚拟内存页表(page table)的结构来稀疏地表示这 4096 位中的有效位(即为 1 的位置),它将 4096 位切分成 16 个 256-bit 的块(共 16 × 256 = 4096)。每块的指针用一个 32 位整数表示,因此最多用 16 × 32 = 512 位来存储所有块指针。实际只需要分配真正使用的块,比如只用到了两个块(分别包含 20 和 3990 这些对象),只存两个块 + 块指针,因此这时只需 1024 位(两个 256-bit 块 + 块指针的512位),比完整 4096 位少了 75%。除此之外,Tai-e 不止使用一层页表(page table),而是像操作系统一样使用两级页表,来进一步动态适应不同规模的 points-to set,页表大小也不是固定的,而是可以根据指向对象的数量动态扩展。

上下文管理器

在指针分析中,上下文敏感性是提高分析精度的关键策略。它的核心思想是:对函数调用区分不同的上下文环境,从而更准确地推导变量可能指向的对象,避免不必要的合并。

比如下面的例子里:

void foo(Object o) { ... }

foo(new A()); // 调用1
foo(new B()); // 调用2

如果上下文不敏感,foo 的参数 o 在两个调用中被视为同一个变量,导致 A 和 B 的指针信息混合,如果有上下文敏感性,我们可以区分“从调用1进入的 foo”和“从调用2进入的 foo”,这样 o 就不会混合 A 和 B。

常见的上下文敏感性策略包括:

  • Context length:上下文信息记录的长度,比如 1-call-site(只看上一级调用),2-object(追踪两级调用对象)等。
  • Call-site sensitivity:记录函数是从哪个调用点进入的。
  • Object sensitivity:记录函数是从哪个对象上调用的(常用于面向对象语言)。
  • Type sensitivity:记录传入对象的类型。

Soot内置的 Spark 分析器是上下文不敏感的,而Soot 的 Paddle 模块支持上下文敏感分析,但基于 BDD 实现(布尔决策图),效率低、已不维护。所以总体上,Soot 不适合进行高效的上下文敏感分析。

Doop基于 Datalog,支持优雅的规则系统,可以实现各种上下文敏感性。但因为 Datalog 是声明式语言,每种上下文策略都要写一套不同的规则,比如1-call-site、2-call-site 都得写一套逻辑;方法调用和堆对象的上下文要分开写,导致代码冗余,维护成本高。

Wala仅支持方法调用的上下文敏感性,对堆对象的上下文敏感性是继承于方法的上下文,即分配对象的上下文直接用方法的上下文,且缺乏灵活性,不能独立控制堆对象的上下文策略。

而Tai-e 作为一个命令式实现的分析框架,支持更加灵活和统一的上下文敏感分析策略,首先其上下文长度作为输入参数统一管理,所以不需要为不同策略写多套实现,一个分析逻辑中,只要传不同的参数,就可以切换;其次Tai-e支持方法和堆对象上下文的独立设置,比如可以指定方法调用用 2-object 上下文,而堆对象用 1-call-site 上下文,比 Wala 灵活很多;并且Tai-e支持 Selective Context Sensitivity(选择性上下文敏感性),这是一种可以按需启用上下文敏感性的分析策略,仅对关键路径或热点函数开启高精度分析,其他使用默认或精简策略,可以有效兼顾性能与精度,适用于大规模工程分析;最后Tai-e已经集成多个前沿算法(比如Zipper等),是目前多个主流上下文敏感分析研究的标准平台。

控制/数据流分析

控制流分析

控制流图(CFG)的构建是控制流分析的核心任务。虽然基本的构建算法是标准的,但不同框架在设计细节上的差异会影响开发者的体验,这里作者主要举了边的类型(Edge categories)以及异常处理这两个例子。

Tai-e 和 SpotBugs都会对 CFG 的边进行分类(例如 IF_TRUE, IF_FALSE, CAUGHT_EXCEPTION 等),可以方便做路径敏感或异常相关的分析,且Tai-e 额外标注了更多边信息,如 switch-case 值、异常边的异常类型。而Soot, Wala, Checker不分类边,这让使用者要通过 IR 自己解析边信息,较为麻烦,只不过虽然Checker 虽不分类边,但会分类基本块(如条件块、异常块),开发者可以通过这些块的 API 获取类似信息。

在异常处理方面,Java 的异常包括显式(通过 throw)和隐式(JVM 自动抛出,如空指针)的,完整的 CFG 应该考虑两者,但隐式异常过多,可能影响分析精度。Tai-e 和 Soot 支持区分显式和隐式异常,并允许用户选择是否包含它们,且Tai-e 还实现了先进的异常分析算法,比其他框架更精确。

数据流分析

实现一个数据流分析通常需要定义:

  • 数据事实的抽象与初始化;
  • 各种语句的转移函数(transfer function);
  • 在控制流汇合处合并数据的 meet/join 操作。

在数据初始化方面,Tai-e、SpotBugs、Checker、Soot都可以在分析实现中初始化数据事实,Wala的初始化逻辑在“求解器(solver)”中,写一个新分析还得实现一个新的 solver,设计上不够优雅。一个更理想的设计当然是一个 solver 支持多个分析,开发者只需专注于分析逻辑。

在边转移函数(Edge transfer function)方面,Tai-e、SpotBugs、Wala 支持显式的边转移函数,可以根据控制流分支为不同后继节点传递不同的数据,空边转移函数相当于默认直接传递数据(identity function)。但 Soot 不直接支持,需要用 BranchedFlowAnalysis 并在节点转移函数中处理所有逻辑,比较繁琐。Checker 通过区分转移结果为 then/else 分支,间接实现类似功能,但会把节点与边的逻辑耦合在一起。

在这里Checker比较特殊,Checker 使用 Java 注解系统来增强类型系统,开发者可通过自定义 type qualifier(类型限定符)来实现分析。分析主要通过类型推断机制(type qualifier inference)完成,这种方式已在实践中被证明有效。作者也在考虑未来是否可以把这种注解机制引入 Tai-e。

新的分析开发

这一章介绍了 Tai-e 框架中的新分析开发机制,尤其是它如何通过一种插件系统(plugin system)来简化与指针分析(pointer analysis)交互的复杂分析的开发流程。

在静态分析框架中,添加新的分析(如污点分析、反射分析、异常分析等)往往需要和指针分析深度互动。传统框架(如 Soot、Doop、Wala)要么机制复杂、要么交互能力不足,不便于开发者快速集成新分析。而Tai-e 的优势在于,插件只需实现少量 API 就可以进行复杂分析,无需深入理解指针分析的内部实现;通过分析插件系统(plugin system),可以提供一套简洁的接口,同时支持插件与指针分析“双向交互”。

在插件系统中包含两个关键角色——Solver(指针分析求解器)与Plugin(用户自定义分析插件)

  • Solver 是 Tai-e 的“主力分析引擎”,持续进行指针分析(比如维护变量能指向哪些对象)。
  • Plugin 是你写的新分析,比如反射分析、异常分析、污点分析等。你作为开发者只需要实现这个插件。

二者之间存在一个双向交互关系,当Solver 发现了新信息(如新的点集或调用边)后会“回调”你的 Plugin,让你有机会响应这些变化;反过来,Plugin 在响应这些变化时,也可以调用 Solver 的 API 主动“反向影响”分析,比如告诉它某个变量也应该指向某个对象,或者补充新的调用边。简单来说:Solver 通知 Plugin 变化 → Plugin 响应并可能调用 Solver 反馈新的信息 → Solver 继续更新分析结果 → 重复直到固定点

这里作者给了一个具体的代码例子:

String s1 = x.source();        // 1. 敏感数据源
s3 = s2.concat(s1);            // 2. 传播
y.sink(s3);                    // 3. 泄露点

具体的分析流程如下:

  • 首先当 Solver 发现了一条新的调用边:x.source() → 实际的 source 方法,它会调用 Plugin 的方法onNewCallEdge(edge) 通知 Plugin
  • 接着Plugin在 onNewCallEdge 中识别出这是一个敏感 source,所以要将 taint 对象加到 s1 的 points-to set 中,因此会调用 solver.addPointsTo(s1, TaintObject),此时 Plugin 反过来通知 Solver 我需要让这个变量 tainted
  • 然后当 Solver 接收到 addPointsTo 调用,把 taint 加到了 s1 的 points-to set 中,它会发现变量的指向集合变了,就继续调用 onNewPointsToSet(s1, {TaintObject}),通知 Plugin 变量有了新 points-to 集合
  • 这时在 onNewPointsToSet 中查到 s1 是传给 s2.concat(s1) 的参数,concat 是转发 taint 的方法,于是Plugin调用 solver.addPointsTo(s3, TaintObject),将污染传播到 s3
  • 最后当没有新信息了,Solver 达到“固定点”(fixed point),它调用 onFinish(),在 onFinish 中检查所有 sink 调用,看有没有 taint 传进去,有就报警

多重分析管理

在许多情况下,一项分析依赖于其他分析的结果,如果框架能够提供一种协调多项分析的机制当然会有极其重要的实用价值,但这里存在两个问题:如何配置一个分析及其依赖以及如何保存与访问分析结果

首先对于第一个问题,分析之间是会互相依赖的,比如污点分析依赖指针分析、反射分析依赖类型推导,所以一个分析框架需要提供机制,来正确执行多个分析,且保证执行顺序正确。不同框架的做法如下:

  • WALA:没有显式的多重分析管理机制,你要手动控制每个分析的执行,无法统一管理。
  • Soot :需要硬编码注册每个分析 Transformer,要把每个分析写入 Java 代码,并且还需要在命令行中列出所有依赖(包括依赖的依赖),容易出错,特别是对初学者来说很难弄清楚有哪些分析依赖哪些分析

而 Tai-e 通过配置文件注册所有分析及其依赖,框架会自动处理执行顺序,每个分析都可以在配置文件里声明它依赖哪个分析,框架自动解析依赖图,并按照依赖顺序执行。开发者不需要写命令行或硬编码分析注册逻辑,分析之间可以共享组件,实现代码解耦,且支持用条件语句描述分析是否执行(例如:配置中某项开启才运行某个分析),这让我们可以只关注自己的分析逻辑,不用费心怎么把多个分析组合起来、执行顺序对不对、有没有少注册依赖。

对于第二个问题,分析做完之后通常会输出结构化信息,比如哪些类的继承结构(类型分析)、哪些变量被污染了(污点分析)、哪些方法是反射调用的目标(反射分析)。WALA 与 Soot 没有统一的结果管理接口,Soot 只会把部分结果(如指针分析)存到一个全局单例 Scene 中,想拿到结果,开发者必须知道每个分析使用了什么类、哪个字段或方法,自己去查找,结果读取方法分散,难记、易错。SpotBugs 有统一结构但接口复杂,每种分析还是需要用不同方法、传不同参数去取结果,使用体验一般。

而在Tai-e中,所有分析统一调用 getResult("分析名") 方法访问结果,只需要记住一个接口比如 Object result = getResult("TaintAnalysis"),框架自动根据分析类型(方法级、类级、程序级)把结果分类存储,用户不需要知道这些细节,只用记一个名字,就能获取结果,这让我们更容易使用其他分析的结果,也便于多个分析之间组合调用和复用,这里举了两个用户的真实体验:

  • “Soot 没有分析管理功能,新开发者搞不清楚 Soot 已经有哪些分析,社区的开发者也很难共享各自实现的分析;而且分析之间有依赖,没人理清楚就容易出问题。”
  • “WALA 没法自动组合多个分析,但 Tai-e 允许轻松组合多个分析,而且内置了很多现成分析。”

评估

RQ1 Tai-e对开发者友好吗?

这里两位老师经过采访调查,总结出了四个Tai-e比其他框架更友好的点:

  • Tai-e 的框架结构、代码及其 API 设计更易于理解和使用
  • Tai-e 的 IR 更直观、更易于使用,从而实现更简洁的实现
  • Tai-e 的分析插件系统简单但高效
  • Tai-e 拥有更强大的指针分析系统

然后举了很多被采访者的发言:

  • “Tai-e 的设计模式、代码风格和扩展能力远优于 Soot。”
  • “Tai-e 代码的可读性很好,而 Soot 代码则不然。因此,Tai-e 的代码比 Soot 的代码更容易理解。”
  • “与 Wala 相比,Tai-e 更像是一个可靠的 Java 静态分析框架。它拥有各种优美的 OOP 设计,使得代码的扩展和重载变得简单。”
  • “Soot 给我的印象是‘最好只使用它提供的功能,而不是构建你需要的功能’,而 Tai-e 更有利于程序员开发新的静态分析。因此,与 Tai-e 相比,我认为 Soot 更像是一个分析工具,而不是一个框架,因为它缺乏一个优秀框架应具备的特性,包括可维护性、代码可读性和可扩展性。”

这里他们还做一个统计,评估在 Tai-e 和 Soot/Wala 上实现一组重要的基础分析功能需要的时间。就结果而言,实验人员在 Tai-e 上平均花费 29 小时,而在 Soot/Wala 上至少花费 49 小时,且其中许多人声称由于功能、调试和其他挑战而无法在 Soot/Wala 上完成任务,因此在 Soot/Wala 上实际花费的时间应该更长。

RQ2 Tai-e的插件系统有用吗?

这里直接举了很多被采访者的发言:

  • “Tai-e 的插件系统非常有用。这种设计让我免受指针分析底层实现的复杂性的困扰,实现了关注点分离,让我能够专注于自己的分析。Soot 没有这样的系统,在构建分析之前,你必须了解其指针分析的细节,这是一个很大的负担”
  • “Tai-e 的分析插件系统非常有用,而 Wala 没有在指针分析中注入自定义对象的机制,而且它通过实现 ContextSelector 和 ContextInterpreter 来生成 IR 的方案在某些分析中受到限制。因此,在实现某些规则时,需要与 Wala 的指针分析进行交互,这需要了解其实现细节。”
  • “Tai-e 的插件系统有三个好处。首先,理解各种分析的原理非常耗时,但通过这个插件系统,我们可以将更多精力放在自己的分析上,从而简化分析开发。其次,将额外的分析功能封装到插件中,对代码扩展和错误定位都有好处。第三,它大大减少了需要编写的代码量,节省了大量的时间和精力,并在一定程度上提高了代码质量。”
  • “根据我为企业应用程序开发分析的经验,Tai-e 的插件系统让我能够非常方便地将我的逻辑添加到指针分析流程中,同时,它使我的分析与下游的安全分析能够自然地交互。此外,在测试过程中,开发人员可以使用该系统轻松地编写所需的功能。”

RQ3 Tai-e的缺陷和对有效性的威胁

这里提到了Tai-e的两个最主要的缺陷:

  • Tai-e的文档不够
  • Tai-e生态系统的强度不足

这里举了几个使用者的发言:

  • “Tai-e 的主要缺陷在于文档太少(包括教程、JavaDoc 等)。虽然它的优势(例如自解释的 API)极大地弥补了这一缺陷,但如果能提供更详尽的文档就更好了。”
  • “可能是因为 Tai-e 创建时间较短,其生态系统不如 Soot,可供参考的项目也较少。”

这里也提到,因为被采访者大部分都是他们的学生或者同事,所以这些调查结果确实受到一定的偏见影响,他们也在尽可能的消除这种偏见,比如这只是一个自愿的、不含学分的、没有补偿的教程。

RQ4 Tai-e 有效吗?

虽然 Tai-e 很“开发者友好”,但如果它分析速度太慢,开发者也不会用它,尤其是像“指针分析”这样的核心任务,几乎所有高级分析(比如污点分析、反射分析等)都依赖它,因此它的效率对整体分析性能有很大影响。

首先在指针分析的评估上,Tai-e 的指针分析在所有程序和配置下的 recall(可达方法和调用边)都是最高的。同时分析速度也优于其他框架(几乎所有情况都更快);WALA 被排除在外,因为它不支持处理动态反射的输入,也在上下文敏感配置下表现很慢;Doop 在这些测试中 recall 表现不好,因为没有充分利用反射信息;即便 Tai-e 分析了更多代码,它依然跑得更快,原因是它在指针分析的实现中做了大量优化,以及它能很好地支持 Java 的复杂特性(如反射、native 代码等)。

#reach指能分析到的真实可达方法数量,#edges指分析出来的调用边数量

数据流分析方面,这里主要比较了 Live Variable Analysis(活跃变量分析),这是所有框架都支持的最基础数据流分析。其中Checker 框架未参与比较,因为它只能分析源码,而 DaCapo 是字节码。而SpotBugs 分析范围比其他框架广(因为它会分析整个 jar 中的所有类),可能导致更慢。所以为了公平性,引入了一个新的衡量指标:#Methods/s —— 每秒分析的方法数,越高表示越快。

结果显示 Tai-e 比 WALA 和 SpotBugs 更快,但不如 Soot 快,原因是Tai-e 没有对这个“本来就很快”的分析再下太多功夫,而 Soot 对数据流分析做了很多底层优化。

结论

Tai-e真牛逼,能忍住不学的也是神人了。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇