目录
01 什么是代码可视化?
02 为什么需要代码可视化?
03 怎么实现代码可视化?
04 有哪些应用场景?
05 总结
01 什么是代码可视化?
在今年的敏捷团队建设中,我通过Suite执行器实现了一键自动化单元测试。Juint除了Suite执行器还有哪些执行器呢?由此我的Runner探索之旅开始了!
Code visualization is the process of creating graphical representations of source code to help understand and analyze it.
代码可视化是创建源代码的图形表示以帮助理解和分析它的过程。
通过使用图形化手段(架构图、依赖图、分布式追踪、类图、火焰图、CallGraph等)使代码在某些特征上变得可观测,用于辅助开发人员理解分析项目或建设一些自动化工具。
02 为什么需要代码可视化?
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
场景1:代码逻辑理解困难
项目代码量很大且需求迭代快,每次梳理的文档很快就过时了。新同学入手困难苦不堪言,老手也很难对项目整体的业务逻辑有一个全面的认知,常常需要重新梳理逻辑。
图 1.
场景2:改动影响面难以评估
需求的诉求是修改A页面的逻辑,但由于后端代码很多公用逻辑且调用层级很深,上线才后发现影响了B页面的逻辑,造成了线上事故。
图 2.
场景3:项目重构缺少抓手
老旧项目经过长时间迭代和多次更换团队,导致内部代码逻辑十分混乱且没人能完全讲明白所有逻辑。但新的业务迭代需求源源不断,在原有项目上修改成本越来越高,亟需重构以获得更高地研发效率。
图 3.
其他场景:自动化case回归常常覆盖不到新增逻辑;线上问题排查困难,难以快速定位到出错代码......
03 怎么实现代码可视化?
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
Call Graph是程序中不同函数调用之间关系的图形表示。它显示了程序中的函数如何相互作用,使开发人员能够理解程序的流程并识别潜在的性能问题。
以下讲解代码可视化的一种方式Call Graph的生成方案,可以分为静态和动态分析:
3.1 静态程序分析
1)基于源码生成
在讲解使用源码生成CallGraph的流程前我们先复习一下编译原理的相关知识。
图 4.
其中编译器前端部分主要是与源语言相关,主要包含:
词法分析:也叫扫描(scanning),他的主要任务是从左向右逐行扫描源程序的字符,识别出各个单词,确定单词的类型,将识别出的单词转换成统一的机内表示—— 词法单元(token) 形式。可以类比英语字母合成单词的过程。
图 5.
语法分析:也叫解析(parsing)。语法分析器(parser)从词法分析器输出的token序列中识别出各类短语,从而构造语法分析树(syntax tree),并判断源程序在结构上是否正确。可以类比为英语单词组合成句子。
图 6.
语义分析:使用语法树和符号表中的信息来检查源程序是否和语言定义的语义一致,如:类型检查、上下文相关分析等。可以类比为检查英语句子是否有意义(如:Dog is cat,这种句子语法上没问题但语义上是不对的)。它同时也收集标识符的属性信息,并把这些信息存放在语法树或符号表中,以便在后面中间代码生成过程中使用。
中间代码:一种中间表示方式,所含信息可以推导出有关程序的全部事实。同一种中间代码可以复用优化器逻辑,并直接使用相关的编译器后端功能,使得各环节更独立更利于扩展。从结构上有图IR、线性IR和混合IR。
编译器后端部分主要是与目标语言相关,包含代码优化器和目标代码生成器,这部分和生成CG关系不大不作更多原理阐述,有兴趣的读者可以了解一下LLVM、Graalvm。
图 7.
有了基本的编译原理知识后,来看看通过源码生产CG的过程:
图 8.
可以发现分析其实就是编译器前端流程的复现,其中AST、CFG和CG都算作是图IR。现成的源码分析工具有Antlr/javaparser/soot等。下面以javaparser工具为例简要说明生成流程:
步骤一:导入需要分析项目的源码和依赖包,并使用工具解析
图 9.
步骤二:使用visit模式获取所有方法和调用方法信息
图 10、11.
步骤三:选定一个起始方法,基于方法和调用关系生成CG
优点:语言无关,扩展性强。
缺点:精度较差需要调优;分析速度较慢;非java语言工具掌握有一定难度。
2)基于字节码生成
针对语言特性进行定制开发能够更快获取成果。Java的字节码其实也可以看做一种线性IR,分析的流程也是类似的,同时java有大量的字节码操作工具(ASM、Javaassit、bcel等),使得字节码解析变得很容易。
基本思路是从.class文件中获取类、方法签名信息,再从字节码中找到invoke指令得到调用方法签名,基于这两个信息就可以构建出CG。同时由于字节码中包含了方法的完整签名,因此不用像源码分析那样需要要引入依赖jar一并分析,因此在分析效率上会快很多。
图 12.
下面用bcel工具为例简要说明生成流程:
步骤一:解析目标项目,可以直接使用打包好的jar包
图 13.
步骤二:使用visit模式获取所有方法和调用方法信息
图 14、15.
步骤三:选定一个起始方法,基于方法和调用关系生成CG
优点:分析精确度高;解析速度快。
缺点:语言相关,扩展性差。
PS:推荐一个idea插件call graph,基于idea的psi能力实现,在项目代码量不大的情况下分析还是挺精确的。
图 16.
3.2 动态程序分析
也称运行时程序分析,一般基于agent方式实现,这里暂不展开讲解,后续有机会再单独写一篇文章讲述原理。有兴趣的读者可以试用一下AppMap。
图 17.
04 有哪些应用场景?
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
场景1:变更风险识别
背景:识别基础设施变更、系统外部变更以及系统内部变更带来的风险。
图 18.
场景2:精准测试
背景:精准测试定义为利用技术手段对测试过程产生的数据进行采集存储,计算,汇总,可视化最终帮助团队提升软件测试的效率、并对项目整体质量进行改进和优化的这一系列操作。
图 19.
案例:京东精准测试探索(
场景3:架构守护
背景:在架构治理上,有着诸多挑战
1)设计与实现不匹配。设计的软件架构与真正实施后的架构,存在着巨大的差异。而这个差异,往往需要编码上线、乃至一段时间之后才能发现;
2)没有规范/不遵守规范。作为资深的开发人员,制定了一系列的规范,但是没有多少团队人员愿意遵守;
3)代码量巨大,难以识别问题。一个由十几个或者几十个微服务创建的系统,往往难以快速发现它们之间错综复杂的关系;
4)架构模型的每个层级都可能出错。如服务间 API 耦合、代码间耦合、数据库耦合等等;
5)架构师、开发人员自身缺乏丰富的经验。知道有问题,但是说不出来哪有问题,也不知道如何改进。
因此需要一个平台/工具,帮助解决这些问题。
案例:ArchGuard
提供了基于C4模型(上下文、容器、组件和代码)的可视化分析,并提供了一些架构健康监测指标。
图 20、21.
05 总结
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
代码本身蕴含了大量的信息,通过可视化这把钥匙打开了很多场景自动化的大门。本文简要介绍了代码可视化实现的思路,并给出了一些应用场景和案例。但面对愈发复杂的业务场景,仅靠一种技术是远远不够的,如何综合运用多种技术来解决实际问题,我们还需要更多的探索和实践。