结合静态代码扫描来给插件间接口把把脉

作者:吴静纯

团队:腾讯移动品质中心TMQ

导读

如火如荼的EP建设中小鹅收到了一个小小的需求,如何知道每个版本变更了哪些插件间接口呢,有没有及时覆盖?

问开发,看代码,看变更日志貌似有那么点不太智能,重点是也不能保证有没有遗漏,不能解决测试童鞋的完美主义兼强迫症,有没有一份及时统一的视图可以来review插件间接口的变更和覆盖情况呢?

插件间接口示例

既然是统计插件间接口,我们先认识下手管的插件间接口定义,在手管插件化框架中,各插件相互平行,插件间接口调用即插件数据传递通过框架封装的统一接口进行通信,由框架进行底层的数据封装和传递,具体实现为各插件间维护一份插件对外的插件间接口配置,编译时在框架生成对应的插件常量,插件内部重载消息函数通过判断传递的接口常量进行对应消息处理从而实现接口间同步/异步数据传递。

插件间接口变更统计

每次编译前框架都会解析接口配置xml生成统一的插件接口常量表,那插件的变化情况我们可以从这里入手,从每次编译生成的常量定义中来找到各版本插件接口的变更情况,通过与上个版本的插件列表定义的插件名参数及返回值做一一对比,就可以知道当前版本的接口变更情况以便及时补充接口用例:

7.4变更接口有69个,数量有些超出大家的想象,也顺手给7.0~7.5版本做了个小统计,统计结果手管目前的插件间接口达到了740+,每个版本呈不断增长趋势。

这么多接口是否都是有效的接口呢?

目前已有的插件间接口用例覆盖程度有多少呢?

经过这么多版本的迭代相信应该有不少多余的水分,插件内的代码各FT通常会清理的比较及时也有一些现成的工具做冗余代码清理,但对外的接口大多担心外部兼容性及依赖问题通常清理不及时,有没有什么好的办法来梳理下,给这些对外接口把把脉呢?

插件间接口规则抽象

有没有类似调用链的分析工具呢?但插件化框设计各插件是平行的,调用链均指向框架接口无法解决我们的问题。虽然现成的调用链工具达不到需求,但我们可以借鉴下调用链的方法,重新抽象规则来建立一张我们想要的接口定义-实现-调用的关系图:

抽取规则如下:定义-实现-调用是一个正常接口的三要素。如果三要素有任一缺失,我们可以推测该接口可能无人调用可以清理或者实现者已清理但仍有调用。

规则一:接口定义,在框架中有定义的插件及插件接口常量认为插件已定义。

规则二:接口实现,在插件工程中有调用到本插件常量的则认为是本插件内部的接口实现,如projectA中有调用CosntA.functionid.interfacea1,可以认为是接口a1已实现,记录插件A的a1接口的实现地址。

规则三:接口调用,在插件工程中调用到非被插件常量的则认为是外部接口调用,如projectA中有调用ConstB.functionid.interfaceb1,则认为工程A调用了插件B的b1接口,在b1接口的调用链中添加该插件的调用记录及文件地址。

插件间接口规则实现

考虑插件间接口是通过传递接口常量来完成数据传递,我们可以通过代码扫描来构建我们的上述规则,结合我们的自定义需求来看看目前android常用的三款静态代码扫描工具:

从扩展性的角度看,coverity作为商业软件虽然官方文档也支持自定义扩展,但相关资料太少,个人更倾向于lint和findbugs,不会写还可以从源代码里面偷偷师,考虑到插件间接口传递的是接口常量,字节码在编译优化过程中常量字段被替换可能导致部分路径无法回溯,也不利于我们对结果做进一步的整理分析,所以最终选定lint进行源码扫描处理。

选定了工具之后实现部分就水到渠成了,按lint规则扩展来添加需要的检查规则,下图虚线模块是每个自定义规则需要扩展的地方:

1、注册规则,声明扫描范围为JAVA_FILE_SCOPE:

2、实现检测器,检测器是实现检查逻辑的主体,自定义的FunctionDetector检测器继承自Detector并实现Detector.JavaScanner接口,并定义我们关注的扫描节点:

(1)查找插件接口定义:

在扫描工具中我们可以按抽象语法树来进行代码节点的查找,在Android Lint中scanner通过lombok.ast(Abstract Syntax Tree抽象语法树)API来进行代码节点的查找,有兴趣的童鞋可以参照Eclipse AST介绍。

前面说到,手管编译前编译脚本会根据插件配置在框架生成相应的插件及接口常量类:

因此插件接口我们可以重写visitClassDeclaration(ClassDeclarationnode)函数在类声明节点中查找解析相应的类文件,将functionid的内部类的所有常量定义加入接口名list,并收集相应的location信息:

(2)查找插件接口实现和调用:

获取插件接口实现,调用本插件的插件接口常量可以认为是该插件间接口的实现,在visitVariableReference(VariableReference node)重载函数中对于调用到的常量判断为插件常量格式(如PiConst.FunctionId.FunctionName)则获取其插件常量判断是否为本插件的接口,如是,获取其location信息写入实现位置。

获取插件接口调用,调用非本插件的接口常量则认为是对外部接口的调用,将插件名及location信息加入到该接口的调用列表中。

3、确认全部插件工程都扫描完成后,在afterCheckProject(Context)重载函数中判断每个接口状态:

1)有实现有调用列表的为正常接口;

2)无实现仍有调用的为冗余未清理接口,可清理接口定义及调用;

3)有实现但无调用的疑为冗余未清理接口,可清理接口定义及实现;

4)仅有定义,疑为冗余未清理接口,可清理接口定义。

得到了748个接口的状态信息,有30%接口有清理空间,我们抽查了主界面的几个,比如主界面REPORT_MESSAGE接口为5.x的消息中心接口,在7.0改版时该功能已全部去掉但仍有6个其他业务插件引用在继续给主界面发消息。

是否可清理呢?

答案是肯定的,接口定义及外部插件的引用均可删除,只删除定义会导致编译不过通知引用插件删除相应的调用即可。旧版本插件调用是否会有crash问题呢?

插件化框架无法保证插件是一定存在的,插件进行接口间调用时就需要进行容错处理,所以插件间接口也不是只增不减的,可以删除。

我们粗略做个统计:

接口定义(xml配置接口及参数返回值定义不会进入编译)常量接口1行,非normal接口共240个;

接口实现,接口参数及返回值均值为2.05个,假设为10行,有实现但无调用的有148个;

接口调用,import及无效调用假设每个引用5行,无实现的调用列表有40个。

240+14810+405=1920保守估计可以清理约2000行代码,相关的资源及配置也可以做进一步清理。

插件间接口视图的其他应用扩展

除了代码清理,插件间接口梳理结果是否还有其他应用呢?

比如查看插件用例覆盖程度,插件间接口测试也是通过调用插件接口调用来进行接口验证,因此调用列表中包含pitest插件的可认为是已覆盖的插件间接口,过滤调用列表中包含pitest的有178个,目前插件间接口pitest的覆盖率为23.8%。

比如作为插件用例的下架指引,状态为非NORMAL或者插件列表如果仅有pitest插件的可推测该接口已废弃,测试用例可以考虑从日常监控中下架。

比如调用列表数量是否可认为是接口覆盖的优先程度,调用较多的是否可认为该接口使用率更高优先级更高,需要更多的关注和验证呢?

……

插件间接口整理只是我们静态代码扫描在缺陷/规范扫描之外结合业务的一个小应用,通过梳理业务定义处理规则,把代码的问题回到代码中来处理。照此思路,一些日益膨胀的公共lib库,裁剪对外提供的sdk也可以进行精确的梳理瘦身,其他各层级的接口是否也可以梳理出这样的可视化图表呢?统计点是否也可以进行类似的梳理验证呢?

结合业务代码扫描我们还可以做的更多,也许你也有更多的代码扫描的应用场景也欢迎大家一起探讨~

关注微信公众号:腾讯移动品质中心TMQ,获取更多测试干货!

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏SDNLAB

OpenFlow协议库开发者指南

介绍 OpenFlow协议库是OpenDaylight的一个组件,调解OpenDaylight controller和支持OpenFlow协议的硬件设备之间通...

65580
来自专栏蓝天

redis的一些简介

Redis是Remote Dictionary Server的缩写,他本质上一个Key/Value数据库,与Memcached类似的NoSQL型数据库。

11810
来自专栏斑斓

Redux框架reducer对状态的处理

前言 在react+redux项目里,关于reducer处理state的方式,在redux官方文档中有这样一段描述: 不要修改 state。 使用 Objec...

38050
来自专栏竹清助手

NodeJS错误处理最佳实践

NodeJS的错误处理让人痛苦,在很长的一段时间里,大量的错误被放任不管。但是要想建立一个健壮的Node.js程序就必须正确的处理这些错误,而且这并不难学。如果...

28830
来自专栏iOS 开发杂谈

iOS多线程之一:基本概念

进程:就是一个正在执行的程序。 线程:是执行程序最基本的单元,它有自己栈和寄存器。

7410
来自专栏北京马哥教育

JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解

DK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jConsole外,还有jps、jstack、jmap、jhat、jstat、hp...

46750
来自专栏Java架构沉思录

再谈如何优雅地使用Redis之位图操作

在之前的文章《如何优雅地使用Redis之位图操作》里为大家介绍了Redis位图操作常见的应用场景,今天继续聊聊Redis位图的其他应用。

18210
来自专栏owent

pbc的proto3接入

Protobuf 的 proto3发布也有挺长一段时间了。现在很多新项目慢慢转变用proto3来开发。这篇文章主要记录一下我在给pbc写对proto3支持时的一...

27810
来自专栏信安之路

ring3层恶意代码实例汇总

之前一期我们学习了 IAT 的基本结构,相信大家对 C++ 有了一个基本的认识,这一期放点干货,我把 ring3 层恶意代码常用的编程技术给大家整理了一下,所有...

16900
来自专栏杨建荣的学习笔记

一个细小的空间问题触发的报警(r11笔记第68天)

今天有一个数据库服务器报警,报警信息是来自于一个异机备库。可以看到这台服务器空间只有300多G,而剩余空间只剩下了不到30G.所以这样一个问题就很奇怪了...

37570

扫码关注云+社区

领取腾讯云代金券