解决方案:和无用代码说再见!阿里文娱无损代码覆盖率统计方案
优采云 发布时间: 2022-12-02 00:32解决方案:和无用代码说再见!阿里文娱无损代码覆盖率统计方案
背景
为了适应产品的快速迭代,通常会投入大量的研发资源在新功能的开发上,而很少关注无用功能的治理。随着时间的推移,线上应用会积累大量的无用代码,加上人员变动和职能交接,管理无用代码的成本越来越高。最终应用安装包过大,导致应用下载转化率下降,应用平台受限(如超过100M的应用无法上架谷歌商店),研发效率降低。
如何管理无用代码?首先是代码静态扫描。对于Android应用程序,ProGuard工具可以在构建阶段静态分析代码引用关系,自动裁剪掉未引用的代码,减小安装包体积。
当然,仅仅静态代码扫描是不够的,因为它不能代表在线用户的实际使用情况,所以还需要在线用户代码覆盖率的统计解决方案。
接下来将从安卓应用在线代码覆盖率统计入手,分享优酷无用代码治理的技术思路和实施方案。
传统收款解决方案
首先,在需要统计的代码中加入统计代码。当代码执行时,进行统计和报告。应用程序中的代码行数通常是几万行,手动添加显然不现实。因此,统计代码(以下简称存根插入)一般通过面向切面编程(AOP)的方式在构造阶段插入。可以使用一些成熟的AOP中间件完成,例如Jacoco、ASM。
其次,我们需要思考我们期望采集
的粒度是多少?一般来说,粒度从细到粗分为:指令、分支、方法、类级别。粒度越细,代码覆盖率结果越准确,但性能损失越大。例如,如果要在指令级别进行采集
,则需要对每条指令进行检测,但这种检测会使指令数量增加一倍,增加安装包,降低运行时性能。
优酷曾经尝试使用 Jacoco 进行分支粒度检测。当时希望覆盖尽可能多的用户,因为覆盖的用户越多,结果就越准确。但经测试,该方案增加了10M的安装包,运行时性能严重下降,故果断放弃该方案。
为了平衡性能和采集
粒度,目前我们普遍采用类级别的粒度检测。一方面这对性能影响不大,另一方面采集粒度太细会增加业务端治理的难度。但是这个解决方案并不完美:
1)运行时性能:第一次加载类时会执行统计代码,App启动过程中会加载上千个类,对启动性能有一定影响;
2)包大小:有多少类,就会插入多少行统计代码,像优酷这样的大型应用,安装包的大小也会增加很多;
3)构建耗时:由于在构建过程中需要插入每个类,增加了构建时间;
新的采集解决方案——SlimLady
▐目标
优酷希望有一个解决方案,可以无损的采集
在线代码覆盖率。核心目标如下:
运行时性能:无影响;
数据包大小:无影响;
施工耗时:无影响;
" />
▐ 实现
通过研究源码发现,类级别的代码覆盖率可以通过动态查询DVM虚拟机加载类的信息得到。下图中“覆盖率采集”部分是SlimLady采集的*敏*感*词*。这里我们只关注这部分。其他部分将在整体程序的后面进行说明。
类表
Java虚拟机规范规定类必须先被虚拟机加载后才能使用。在Android中,类的加载是通过ClassLoader完成的,最后保存在Native层的ClassTable中,所以如果我们获取到ClassLoader的所有ClassTable对象,就可以判断出哪些类被虚拟机加载了。
首先,获取所有 ClassLoader 对象。对于APK中的类,如果没有特别声明,一般会使用默认的PathClassLoader加载;对于动态加载的类,需要在一个自定义的ClassLoader中加载,比如Atlas会为每个Bundle创建一个对应的ClassLoader,通过这个ClassLoader来加载Bundle中的类。一旦明确了App中使用了哪些ClassLoder,就很容易获取
其次,通过ClassLoader获取ClassTable对象的地址。根据Java层ClassLoader类的源码,ClassLoader有一个成员变量classTable(7.0及以上版本),存放的是ClassTable对象在Native层的地址。我们可以通过反射得到这个地址:
ClassLoader classLoader = XXX;
Field classTableField = ClassLoader.class.getDeclaredField("classTable");
classTableField.setAccessible(true);
long classTableAddr = classTableField.getLong(classLoader);
但是在9.0系统中,成员变量classTable加入了深灰列表,限制了直接反射,需要通过系统类反射才能绕过这个限制:
ClassLoader classLoader = XXX;
Method metaGetDeclaredField = Class.class.getDeclaredMethod("getDeclaredField", String.class);
Field classTableField = (Field) metaGetDeclaredField.invoke(ClassLoader.class, "classTable");
classTableField.setAccessible(true);
long classTableAddr = classTableField.getLong(classLoader);
至此,我们获取了所有ClassTable对象的地址,其中存放了所有的类加载信息。
班级名单
通过阅读源码,我们发现ClassTable有一个方法可以通过类名查询一个类是否已经被加载(下一节会详细介绍),所以我们只需要获取所有类名的列表即可,而然后调用该方法来确定是否已经加载了一个类。
APK中的类名列表可以通过DexFile获取,如下:
List classes = new ArrayList;
DexFile df = new DexFile(context.getPackageCodePath);
for (Enumeration iter = df.entries; iter.hasMoreElements; ) {
classes.add(iter.nextElement);
}
同样,也可以通过DexFile获取动态加载的类;
类是否加载
通过阅读源码,发现ClassTable有一个Lookup方法,传入类名和类名的哈希值,返回类对象的地址,如下:
mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash)
如果返回值为ptr,说明这个类还没有加载,否则说明加载了。
mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash)
获取该方法地址的方法:
载入so:在libart.so中,我们只需要使用dlopen载入libart.so就可以得到这个so的handler。其实在加载之前,libart.so肯定已经加载到当前进程中了。本次加载只是获取handler,并不耗费时间;
" />
符号表:通过readelf查询Lookup符号:_ZN3art10ClassTable6LookupEPKcj;
方法指针:调用dlsym,传入handler和符号表,可以找到Lookup方法的地址;
注意:从7.0系统开始,Google禁止调用系统的Native API。这里我们通过/proc/self/maps找到libart.so的地址,复制里面的符号表,然后绕过这个限制;
至此,我们可以通过调用ClassTable的Lookup方法,传入类名和hash值来判断该类是否已经加载。
总结
这样我们就可以知道某个时刻加载了哪些类,将它们上传,聚合处理,然后比较所有的类名列表,得到代码覆盖率数据。该解决方案不需要仪器,因此可以无损地采集
覆盖范围。
新方案总体设计
上面提到的采集方案是整个方案的核心。此外,还有上下游配套工艺。整体方案设计如下:
1)APK分发:通过构建中心构建最新的APK并分发给用户;
2)触发采集:用户安装应用,在使用过程中,APP备份10秒后,通过采样率计算是否命中,命中则触发代码覆盖率采集
3)配置分布:必要时可通过配置中心分布动态调整功能开关、采样率等配置;
4)数据采集
:代码覆盖率采集
中间件(SlimLady)对加载的类进行统计,将加载的类名保存在一个文件中,进行压缩,并将压缩后的数据传递给上传中间件;
5)数据上传:上传中间件将数据上传到云端;
6)数据下载:服务器定时下载云端数据;
7)类信息提供:服务器从构建中心获取类信息,包括所有类名列表和混淆文件;
8) 数据分析:服务端对代码覆盖率数据按版本进行解压、反混淆、聚合。聚合统计包括加载的类和次数,将它们与所有类名的列表进行比较,以了解哪些类没有被加载,将结果保存到数据库;
9)结果聚合:网页从数据库中读取聚合结果,按模块显示代码覆盖率、模块流行度、模块大小等信息。
总结
该方案突破传统的stub-instrumentation点统计,动态获取虚拟机信息,无损采集代码覆盖率。有了代码覆盖率数据,可以做很多治理,比如:下线无用代码和模块;瘦身或离线调用低频大模块;在集成阶段添加代码覆盖检查点等。
解决方案:优采云
采集器保存发布到数据库(优采云
采集器有什么用)
目录:
1.如何使用优采云
数据采集器
优采云
采集
器保存,在使用优采云
采集
器之前,你需要有HTML知识,必须能够看懂网页的源代码和结构,否则你将无法获取开始了!如果要使用web自动发布或者数据库自动发布,需要对自己的网站系统和数据存储结构有很好的了解。
2. 优采云
将采集
器保存到本地
否则,它将无法使用。如果你对这方面不是很了解,或者没有那么多时间学习,那么只能使用如图所示的更简单的免费采集器(采集后导出各种格式或者选择自动发布) ,只需轻点几下鼠标,就可以轻松获取你想要的数据!!!!
3.优采云
采集后会发布到网站数据库
如何让网站的内容有更多的推荐和排名?1.过多的锚文本会分散权重。当蜘蛛爬到我们网站的内页时,它会认为锚文本指向的链接是锚文本的解释。当我们在文章中添加过多的锚文本时,直接给蜘蛛一个感觉:你的文章应该解释的内容太多,内容太深奥,大众根本看不懂。用户需要的是科普知识,那么多次同一篇文章,加的链接越少,索引越快,排名就越好。
" />
4. 优采云
采集器
发布模块
2.锚文本对排名的影响对于文章中添加锚文本的问题,不同的公司有不同的要求。具体添加多少还以每次SEO的安排为准,因为不同的公司有不同的要求。,而且都非常有道理,所以才实施。这里,我们不妨做个对比:
5. 优采云
采集
器有什么用?
1.在第一段和最后一段添加首页链接关键词,然后在文章末尾写文章来源,并添加网站网址,相当于在一篇文章中添加3 2 . 文章中不要加锚文本,内链已经做好了,什么时候加要根据文章的具体情况而定,但是这个网站的排名一直是高低不一,会不倒,也不会上来,虽然每天都在写文章。可能是 100% 原创。
6. 优采云
采集
和存储
3. 第一段出现的第一个关键词链接到主页,然后第二段出现另一个不同的关键词。添加一个链接,然后在底部添加一个锚文本,然后在其他段落中添加1-2篇单独文章的标题,添加这篇文章的链接,就相当于说这样一篇文章有3-5个链接。
" />
7.优采云
采集
器如何采集
文章
三、文章内容添加锚文本的原则 1、同一页面同一个关键词下不要有不同的链接。,肯定不会出现两个不同的链接,因为这会让蜘蛛无法判断哪个链接才是真正的解释;如果蜘蛛无法判断,那么蜘蛛也只能无视。
8. 优采云
采集
器的使用方法
2. 同一页面的同一个链接不要有不同的关键词。同理,同一个链接不能有不同的关键词。我们的一个链接无法解释两个 关键词。如果我们让蜘蛛对他们看到的所有单词都使用一种解释,那肯定不是这样。这也是为什么很多朋友为了增加首页权重,在同一个页面用不同的词指向首页。
9.优采云
采集器
发布教程
3、给文章添加三个锚文本链接 既然我们知道了如何添加锚文本,那么给文章添加三个锚文本就足够了。核心关键词指向首页,栏目关键词指向栏目页。以指向高质量和相关内容页面的链接结束 关键词。
主题测试文章,仅供测试使用。发布者:小编,转载请注明出处: