前端渲染函数调用图工具的设计与实现
2022-02-15孙卫真
孙卫真,孙 星,向 勇
(1.首都师范大学 信息工程学院,北京 100048;2.清华大学 计算机科学与技术系,北京 100084)
0 引 言
在分析复杂结构软件或操作系统内核时,研究者会关注各模块及函数之间的关系,借助源码分析工具可以减少人工分辨模块关系。传统分析工具如LXR[1]、Kythe[2]通过检索源码,建立索引关系,以标记和链接的方式辅助分析。文献[3]静态数据获取使用基于寄存器传送语言方式,读取内核编译中间结果,形成更准确的调用关系。为更好表现模块及函数间的调用关系,有研究和工具运用流程图或有向图[4]等方式展现。随着Web技术的广泛应用与在线工具的出现,用户能够在线进行源码分析工作,减少研究者本地部署的操作,文献[5]使用数据库存储调用数据,Web服务器读取数据并渲染SVG格式的函数调用图,通过静态页面展现。但服务器处理和渲染过程计算量大,传输数据存在大量冗余,导致等待时间过长。因此提高数据处理和渲染速度,减小传输数据量,能够提高函数调用图的生成速度。
本文工作概括如下:
(1)使用DBCG-RTL(date based call graph tool based on RTL)数据获取方法,更新运行环境,生成新版本内核调用数据,存储在MySQL数据库中,用于服务器数据获取;
(2)基于Node.js使用egg.js框架搭建API服务器,使用异步方式处理并返回客户端所需数据;
(3)基于Vue.js搭建页面,使用Antv-G6可视化库,实现在线函数调用图模块,动态加载图数据并更新调用图;
(4)使用Linux-4.15.18 x64内核数据,对DBCG-RTL与FRCG工具进行测试,结果表明FRCG工具生成50节点以下函数调用图时,平均减少60.09%等待时间,平均减少92.24%传输数据。
1 相关工作
在内核源码的分析过程中,由于Linux源码版本多、文件和函数间交叉引用数量巨大。传统工具LXR[1]、Kythe[2]等通过检索源码,识别函数定义与调用的文件位置进行记录,以链接方式展示调用关系,但大型项目或内核中存在大量同名函数,扫描静态源码,无法准确匹配调用函数,只能提供索引列表。
借助可视化工具,能够更直观查看函数调用关系。毛等[3]开发了DCG-RTL工具,将内核二次编译读取调用关系数据与模拟器动态监控数据结合,构建内核调用关系图,但其采用文件形式存储,在渲染过程中,查找速度较慢。在此基础上,贾等[5]开发了DBCG-RTL工具,使用数据库存储函数调用关系,提高查询速度,并将函数调用图和函数调用列表等模块集成在LXR的Web服务器中。由于LXR为服务器渲染生成页面,数据读取、处理与调用图、页面的渲染都在服务器上进行,造成计算量大,等待时间长等问题。同时页面渲染和数据处理代码耦合在一起,加大了维护和拓展的难度,造成工具难以移植和更新。
针对Web服务器处理时间长的问题,有研究人员对不同服务器进行对比,Chitra等[6]对比Node.js和传统Web服务器性能,得出Node.js服务器具有轻巧高效的特点;Chaniotis等[7]对Apache,PHP,Node.js和Nginx进行性能分析,指出Node.js在内存和CPU的利用率明显优于Apache 和PHP,并提出将Nginx与Node.js结合的框架,用于开发具有模块化、实时信息通讯和拓展性强等需求的Web应用;有研究人员基于Node.js实现前后端分离的平台系统,朱等[8]使用基于Node.js的Koa后端框架和Vue.js前端框架,对物流信息管理系统进行研究与实现,通过Node.js非阻塞I/O、事件驱动等特性,实现较快的页面加载时间。Eric Wohlgethan[9]对Angular、React、Vue.js前端框架进行了分析,总结Vue.js用于开发单页面应用,学习成本低,同时框架实现数据和视图的双向绑定,视图的更新由框架提供,开发过程只需要专注于数据的处理。
对于数据可视化,DBCG-RTL工具[5]处理调用图数据后,使用Graphviz工具渲染生成SVG图,该工具语法简单,能够自动布局节点分布。但脚本进行渲染和布局的速度慢,同时会增加服务器计算压力;生成SVG中包含渲染后属性,增加传输的数据量。基于前端可视化实现主要有:Cytoscape.js[10]是一个开源关系图可视化库,提供多种布局方式,功能完善,但其视图操作基于选择器实现,不适用在Vue模块化的前端框架中;Echarts[11,12]为百度团队开发的前端图库,支持基础图表,也支持树图、关系图等,具有良好的可视化效果,但其对于关系图的支持较为不足,需要自行实现相关功能;冉等[13]基于Echarts模块,对专利数据进行可视化处理;AntV[14]作为阿里旗下蚂蚁金服可视化解决方案,将不同类别图进行了不同的划分,G2可视化、G6图可视化、F2移动可视化、L7地理空间可视化。其中G6对图的数据关系,具有完善的功能支持,能良好兼容Vue模块化前端框架,文档丰富,便于二次开发;文献[15]使用G2图库开发了LnCompare,可以对长非编码RNA数据进行在线可视化分析。
本文目的是提高分析工具调用图的生成速度,使用前后端分离方式,将数据处理和可视化独立到前后端完成;转移服务器渲染调用图的计算量,从而提高数据处理速度,减小前后端传输数据量;前端页面使用Vue框架降低页面框架内容实现难度,调用图使用AntV-G6实现调用数据的可视化,动态渲染调用图,解决DBCG-RTL[5]调用图渲染时间过长的问题,并提高调用图的灵活性。同时前后端统一开发环境,降低工具拓展和维护难度。
2 FRCG工具总体框架
基于前后端分离思想,FRCG函数调用图工具主要由Node.js搭建的API服务器和Vue.js搭建的单页面应用两个部分组成,通过docker容器进行部署,如图1所示,框架分为MySQL、Node和Nginx这3个容器。Nginx容器提供页面文件Web服务和对API接口的反向代理,Node容器提供处理API接口请求的服务器,MySQL容器作为数据存储。
(1)MySQL容器:使用DBCG-RTL[5]方法,生成Linux 4.15版本之后内核调用数据,存储在MySQL数据库中。Node.js服务器从中查询所需要的内核数据。
(2)Node容器:基于Node.js的egg.js框架搭建API服务器,使用异步方式处理请求内容和查询数据库,并将数据转换成对应格式,作为请求的返回数据。为前端页面提供数据接口。
(3)Nginx容器:基于Vue.js搭建的单页面应用通过打包形成静态页面文件,使用Nginx部署页面资源。用户使用浏览器访问,获取页面文件到本地浏览器中渲染执行,页面中调用图、调用表等模块使用Http请求服务器对应API接口获取数据,得到数据后,页面脚本进行可视化渲染。作为FRCG工具的主要展现方式。
图1 FRCG总体框架
图2 服务器框架
3 服务器主要接口设计与实现
Node.js服务器框架,主要分为路由、控制器、服务、插件这4部分组成,如图2所示。
路由(Router)中定义了接口URL对应的控制器模块,根据获取数据的不同设计为3种接口,分别是函数调用图接口/graphs用于获取调用图数据;函数调用表接口/functions用于获取调用表数据;配置信息接口/options用于页面获取配置模块所需数据,实现页面登录和菜单列表功能。
控制器(Controller)负责解析路由分发请求中包含的参数,调用验证插件进行合法性验证,提取请求中的数据作为参数,调用对应的服务模块函数进行数据处理,返回响应数据。
服务(Service)负责处理数据,简化控制器代码,独立数据的处理函数。模块根据调用参数,进行数据查询和特定规则的计算,生成所需数据,根据需求写入数据库并返回给控制器。
插件(Plugin)主要为官方或三方实现的功能模块,如安全验证、数据验证、MySQL访问等。
3.1 API设计
根据页面模块所需数据为调用图的节点及边数据和调用表的详细数据,将服务器API接口设计为获取调用图数据的API接口和获取调用表数据的API接口。
3.1.1 调用图API设计
页面调用图所需数据主要为:获取对应内核版本、编译环境下,两个路径间的调用关系图数据;获取调用图中某节点下一级节点与当前图中节点调用关系的图数据;上传调用图数据,存储并返回分享id;获取分享id对应的调用图数据。
根据页面需求调用图API需要实现:依据请求参数返回调用图数据、存储请求中上传的调用图数据并返回存储id、获取对应id的调用图数据。具体接口定义见表1。
表1 调用图API定义
根据生成内核函数调用图的不同,需求的参数列表见表2。请求中包含内核版本、平台、生成调用图的源路径、目标路径的必须参数,根据调用图的功能不同,请求中可以增加配置参数:用于获取不显示同级路径调用图的per参数、用于获取展开节点数据的id、expand和expanded参数。
表2 调用图API参数
服务器读取参数后,根据配置进行数据处理,以JSON格式返回,返回数据中,包含图id、节点数据、边数据等。以请求Linux-4.15.18内核fs和mm模块返回数据为例,部分内容如代码1所示。
代码1:调用图返回数据
params: {
id: "4-15-18 x86_64 /fs /mm"
data: {n
nodes:[
{id: "/mm/gup.c", type: 0},
…
],
edges:[
{source: "/mm/gup.c", target: "/fs", sourceWeight: 8, type: 2},
…
]
}
}
3.1.2 调用表API设计
页面中调用表需要内核版本、编译环境下,两个路径间的调用数据,同时为了实现源码跳转功能,会获取某个函数的详细数据。调用表API功能简单,主要为获取路径间调用表和获取函数定义,具体定义见表3。
表3 调用表API定义
请求中需求参数为内核版本、平台、源路径、目标路径,见表4。请求返回数据中包含函数调用的数据:源函数名、源函数所在文件、行号、调用行号、目标函数名、目标函数所在文件、行号、调用次数等。
表4 调用表API参数
3.2 API实现
服务器中请求处理过程相似,流程为路由分配,控制器调用服务生成数据并返回响应数据。不同模块控制器会根据功能需求对数据进行验证,调用对应服务进行处理,通过不同的处理方式,生成不同请求所需的数据。
3.2.1 调用图数据处理实现
客户端请求经路由分配到使用GET方法的/graphs接口后,处理过程如下:
(1)控制器处理请求:控制器验证请求数据,验证通过后,控制器提取请求数据作为参数,调用对应服务进行处理;
(2)服务模块读取调用参数:获取参数中的内核版本、平台和路径等数据,并读取非必须的配置项,用于区分所需函数调用图功能,如:存在expand属性则此请求为对现有函数调用图节点展开数据的请求,存在per属性为false则此请求为不需要同级节点数据的内部函数调用图。对于无额外配置项的参数,默认为获取对应内核版本下两路径之间的函数调用图;
(3)初始化配置数据:使用参数中的内核版本等数据,初始化调用图配置数据,如查询数据库的表名、图id等;
(4)检索历史数据库:根据初始化后的图id,检索历史数据中是否存在该图数据,若存在历史数据,将数据返回给控制器。若无历史数据,则通过异步方式查询数据库生成调用图数据,将生成数据返回给控制器,并写入历史数据库;
(5)返回数据:控制器得到服务文件返回数据,作为请求数据返回给客户端,请求处理完成。
过程优化:
DBCG-RTL中计算调用图数据主要过程为:
(1)查询静态函数定义表(FDLIST),获取源路径和目标路径的下级路径。若输入为路径,下一级则为子路径DBCG-RTL或文件,若输入为文件,下一级为文件内函数名。同时使用递归方式逐级查找同级和上级路径。将查找到的路径,所属类别转换成节点集合;
(2)得到节点的集合后,依次遍历节点集合中不同的两个节点,查询静态外部引用列表(SOLIST)获取两个节点间的调用次数;
(3)函数调用图所需数据查询完毕,需要对数据格式进行处理,生成符合Graphviz需求格式的数据;
(4)针对调用图计算过程中,对调用关系的大量查询,本文采用异步处理方式提高处理速度;同时前后端统一使用JSON数据传输,减少传输图数据的大小。
提高计算速度:
查询节点间的调用关系可以看作查询N节点的有向图,遍历不同节点间的调用关系需要进行复杂度为O(N2)的查询。DBCG-RTL通过Ruby脚本线性执行SQL语句直接查询MySQL数据,本文通过egg-mysql插件代理访问,线性执行相同的查询操作,处理时间比Ruby脚本慢。
基于Node.js的事件循环和非阻塞I/O机制:对于连续的查询操作,Node.js将其视为一组事件队列,依次执行其中的查询操作。在发起查询数据库操作后,不会阻塞查询的继续执行,而是将返回数据的操作放在回调函数中,并继续下一次查询。当数据库查询操作返回结果后,将处理数据的回调函数插入事件队列,等待执行。Node.js会不断检查当前事件队列,并以一定顺序去执行各类回调函数。
而在处理请求所需的节点调用数据过程中,通过异步方式发起查询,在回调函数还未执行时,请求处理函数就已经执行完成并返回,造成返回数据列表为空的情况。为解决返回数据为空的问题,本文构造Promise对象列表,每个Promise对象为查询数据库中两节点间的调用频度函数,数据返回后通过回调函数将调用数据添加到边数据列表中。执行Promise.all(list)函数,实现以非阻塞的方式执行对象列表中全部查询,以阻塞的方式执行回调函数,得到全部节点间的调用数据。同步执行和异步读取结合,提高数据获取速度。
减小数据大小:
DBCG-RTL生成的SVG图,节点和边存在大量未进行优化的冗余内容和渲染属性数据,导致传输数据较大;Node.js服务器提供处理后的图数据,只包含节点和边的基础属性数据,如节点id、节点类型、边类型、边源和目标等数据,能够大幅度减少传输数据。如图3所示:本文分离页面文件和数据的传输,减小了每次请求所需的页面文件,调用图数据由页面动态请求;前后端使用JSON格式传输数据,减小SVG中的渲染数据和XML标签结构数据,降低传输数据大小,提升传输速度。
图3 传输内容对比
3.2.2 调用表数据处理实现
Get/functions接口数据处理流程与调用图处理流程相似,具体差异在数据的查询和处理部分。
调用表数据查询过程与DBCG-RTL方法相似,查询SOLIST表得到两路径之间存在调用关系的函数、所在文件和调用次数;根据函数名和所在文件查询FDLIST表,得到函数在数据库中的id和文件内起始行号;根据源函数id和被调用函数id查询SLIST表得到全部调用行号。
过程优化:
计算调用表数据优化与调用图优化相似,使用异步处理大量查询。处理流程中查询SOLIST可以得到调用关系数据,数据中缺少函数定义数据和调用行号数据;函数定义数据,通过构造Promise对象实现异步查询FDLIST表,得到函数在数据库中的id和文件内起始行号;调用行号数据,通过构造Promise对象实现异步查询SLIST表得到全部调用行号。使用Promise.all()函数同步得到全部查询结果。
4 调用图页面实现
前端页面作为FRCG工具的主要展示途径,提供了可交互的调用图和详细数据的调用表。同时提供自定义布局模块,实现对调用图和调用表模块布局的自定义。页面各部分框架如图4所示,本文通过EventBus构建事件总线,完成调用图组件对其它组件的事件传递,实现调用图的复杂交互功能。
图4 页面交互事件
调用图页面中,用户通过选择器组件修改页面配置数据(config),此配置作为页面子组件:调用图组件和调用表组件的输入参数。图组件中用户的操作,如点击右键菜单执行切换调用图,会向页面发布修改配置数据的事件,触发页面修改对应配置数据,调用图组件会根据输入配置参数的变化,更新图数据重新渲染生成调用图。
4.1 选择器组件
图页面的一组选择器,供用户配置图所需的参数:内核版本、内核平台、源路径、目标路径、仅显示内部节点、图布局方式。内核版本及平台等数据,在页面初始化过程中,会向服务器请求获取,用户选择版本内核后,会动态加载对应内核的路径数据。这些数据存储在内存中,切换同一内核版本调用图时,能够直接获取内核版本数据;切换不同内核调用图时,当选择器选中后,会更新内存中的内核版本数据。
组件中选中的配置数据,绑定到图组件和表组件的输入参数,当调用图显示时,用户通过选择器组件更改配置数据,会触发调用图的更新。
4.2 自定义布局组件
在页面中引入Vue-grid-layout组件实现拖拽布局,将调用图模块和调用表模块嵌套在布局组件中,并绑定布局组件大小变化事件到调用图组件size传入数据上,通过调用图组件中监控size变化,执行调用图的更新宽高函数。自定义布局可以改变调用图和调用表大小和位置,默认页面布局为上下两部分,用户通过改变模块尺寸,拖拽模块等操作来实现左右或交换上下位置的布局。
4.3 调用图组件
图组件接收页面传入的配置数据(config),用户通过选择器选择配置时会更新图组件传入的config,当配置项完整后页面才会显示图组件,避免由于配置数据不完整而造成请求调用图数据失败。
调用图组件由:筛选开关组件、G6图组件、右键菜单组件、弹窗组件组成。
(1)筛选开关组件:通过一组开关组件,绑定一组筛选配置数组,用于过滤图中不同种类边,辅助使用者找到分析的目标节点和调用边,提供分类有:源为非所选路径节点、目标为非所选路径节点、源路径节点到目标路径节点、目标路径节点到源路径节点、源路径节点到源路径节点、目标路径节点到目标路径节点。开关组件绑定数据改边时,会对当前调用图中全部边进行遍历,根据筛选配置数组,编辑边的显示属性。同时,当调用图更新时,也需要对边的显示属性进行检查;
(2)G6图组件:作为调用图组件的主体,用于显示函数调用图,还包含大量图交互事件,如右键弹出对应菜单;鼠标覆盖节点和边进行高亮相关元素;拖拽移动图和节点;缩小放大调用图等;
(3)右键菜单组件:作为切换和跳转的主要途径,如图所示组件中包含大量传递给其它组件的事件,为统一事件传递方式,采用EventBus(事件总线)进行传递,由组件根据用户点击菜单内容,向总线中发布事件,其它组件订阅相应事件,执行相应操作;
(4)弹窗组件:用于显示错误信息及显示分享图的链接信息。
4.4 调用表组件
调用表组件接收页面提供的config,其中包含内核版本、平台、源路径、目标路径,当配置项完整时,使用element-ui中的表格组件进行渲染,在组件声明表格列,并配置列的属性项如:列排序依据、列内元素类型等。当列内元素为按钮类型时,需要在列中定义按钮组件,并传递数据显示,绑定函数实现点击行号跳转源码页面操作。
通过调用图边的右键菜单发出事件show function list、页面补全调用表配置、渲染显示调用表。
4.5 主要功能实现
调用图组件基于G6模块实现,通过定义初始化函数,对调用图的显示和交互进行定制和拓展,通过G6模块提供的函数,实现更新数据、渲染调用图、保存当前图数据等功能。FRCG实现对图数据的更新、添加和拷贝,调用图的显示、切换、局部更新和服务器备份等功能,对比DBCG-RTL通过静态图文件的方式展示调用图,FRCG基于数据的操作具有更多灵活性,同时数据能够进行二次利用和分析,具有更多的拓展性。
4.5.1 调用图实现
用户通过选择器组件配置完整后,图组件进行渲染显示,生成调用图过程如下:
(1)图组件将输入的配置参数进行处理,提取并保存为内部配置;
(2)初始化G6图组件,绑定图组件内部响应事件,如鼠标覆盖高亮、提示框弹出、右键菜单弹出与关闭等;
(3)根据当前配置数据,向服务器发起图数据请求,得到返回数据后,对数据进行处理,根据节点id信息,增加节点label属性,用于提示框显示,根据节点和边的type属性,为节点和边增加显示颜色;
(4)将处理后的数据加载到G6图组件中,调用组件render()函数渲染调用图;
(5)模块会监控输入配置参数,当属性发生变化时,根据配置参数获取新的调用图数据,调用组件data()方法更新数据,并使用render()方法重新渲染调用图。
4.5.2 局部更新数据
将函数调用数据作为操作对象后,调用图的更新能够通过增加部分数据,达到增量更新的效果。在请求数据时,配置非必须参数:id、expand、expanded,用于获取符合对应图中展开节点后的新增数据。页面通过事件总线将更新函数绑定右键菜单中的节点展开按钮,展开节点时,调用图组件会遍历当前图中节点与边,删除被展开节点与相关联的边,以代码2所示请求数据向服务器请求展开节点数据:
代码2:请求数据
params: {
version: "4-xx-xx",
source: "/xx",
target: "/xx",
id: "4-xx-xx /xx /xx",
espand: "/xx/xx"
}
得到请求返回的数据,将数据和现有数据合并,使用合并后的数据重新渲染调用图,实现增量更新展开节点。
需要注意的是,当图中存在多个展开节点时,页面会提示“存在多个展开节点,展开节点之间的边不会显示”当图中展开节点,达到3个时,将会弹出错误提示,并拒绝继续加载新的展开节点。
4.5.3 分享调用图
通过G6模块graph.save()方法,可以保存当前图中的节点数据,数据中包含节点坐标等渲染后的图数据。将此数据上传到服务器,实现保存特定调用图的目的。在调用图中,通过右键弹出菜单选择分享图。点击后,页面将通过G6图中graph.save()函数获取当前图中节点和边的详细数据,对数据进行过滤,只将当前图的必要数据上传给服务器。服务器收到数据进行存储,将图的版本、源路径和目标路径等标志信息进行MD5处理,得到哈希值作为数据的索引,返回给页面此哈希数据。页面补全URL路径生成分享链接,并弹窗显示分享链接。
在新的页面中打开分享链接,页面初始化过程中会根据URL中包含的哈希数据,向服务器获取分享页面的配置和图数据。获取到数据后会先根据数据中的原有配置更新页面和图配置,将页面得到图数据通过图组件的ex_data进行传递,同时将图配置的ex属性设为true。图组件初始化后,加载数据时ex属性为true则使用外部数据,保存的图数据具有节点坐标,在图渲染完成后,会使用数据中的坐标信息更新节点位置,使接收方查看的图与分享方图一致。
5 应用实例
研究Linux系统的启动过程是对其优化和定制的基础,通过分析平台辅助去了解和认识启动函数,图形化的调用图、详细的调用表与源码结合,更直观认识模块调用和函数信息。
内核启动函数start_kernel所在文件为/init/main.c,在FRCG工具页面中,选择内核版本和平台,通过输入查找,快速找到源路径/init/main.c,目标路径选为/,配置完整调用图自动加载,使用调用图筛选和拖动等功能,实现调用如图5所示,鼠标停留在启动函数节点上,高亮显示关联模块和函数节点,无调用关系路径和函数节点透明度降低,排除干扰。
图5 内核启动函数调用
对于需要分析的模块,可以通过右键菜单中节点展开、查看函数调用表等,获取详细信息。如图6为查看start_kernel函数中对fs模块的调用表。
图6 启动函数对fs模块的调用列表
调用表中点击所在文件和行号列内容可以在新页面显示对应源码,提供研究者分析模块间调用关系的同时快速确定源码中函数所在位置及模块间调用关系。
FRCG工具使研究人员快速查看函数调用关系,区分模块间是否存在调用,跳转查看文件或函数源码。传统源码分析平台,对于调用的函数无法提供详细信息,遇到重名函数时,检索函数,并罗列出该函数名的全部定义和调用,用户需要自行区分,找到当前文件中对应的函数位置,进行查看。
函数调用图模块的链接源码功能,源数据确定的函数调用,包含函数的文件路径和所在行等信息,用户可以在右键菜单中选择查看对应源码,平台会跳转到当前显示节点的源码位置,用户无须分辨重名函数。
6 评估与比较
本文FRCG工具针对DBCG-RTL静态函数调用图部分使用前后端分离方式重构优化,保留原有静态调用图部分全部功能,并大幅提高调用图生成速度,增加系统部署的灵活性。本文使用i5-6500,8 G RAM,Win10-1909搭建测试平台,使用VMware 15,分配4核,4 GB内存虚拟机运行Ubuntu 1804操作系统、Node.js 12.16.2、MySQL 8作为服务器;测试数据为Linux-4.15.18内核在x64平台下的顶层路径之间调用关系。
6.1 调用图显示总时间
根据调用图从请求到查看流程,DBCG-RTL中包含服务器Ruby脚本查询数据库、处理数据、服务器Ruby脚本渲染SVG图、网络传输和用户浏览器显示,本文测试阶段忽略浏览器渲染SVG图时间,将处理、渲染、传输的分段时间进行统计;FRCG工具包含Node.js服务器读取数据库并处理、请求数据传输和前端页面渲染生成调用图,将处理、传输、渲染的分段时间进行统计,结果如图7所示。
由于DBCG-RTL工具Ruby脚本在查询数据库阶段为线性执行,大量查询等待时间堆积造成处理时间过长,而渲染阶段需要先生成中间文件,存储到文件系统,调用外部脚本处理生成为SVG图格式,再次写入文件存储,最后读取文件进行返回。多次文件读写和外部脚本调用,随着文件容量的增加造成过长的等待时间。而FRCG工具,实现异步读取数据库,提高查询速度,查询完成后直接转换为JSON格式返回,内存级数据读写,大幅提高处理速度,同时渲染过程在客户端进行,不占用服务器的计算资源,服务器能够更快返回数据。
对更大范围路径进行测试,将工具阶段时间汇总,统计处理、渲染、传输的总时间,对比结果如图8所示。
图8 总时间对比
增加幂函数回归[16]趋势线辅助观察数据变化趋势,如图8所示,FRCG处理总时间低于DBCG-RTL总时间。随元素数上升,总时间差距越大。查看统计数据,当前测试平台下全部测试路径内:DBCG-RTL工具总时间平均为273 288 ms,即4 min 33 s,而FRCG总时间平均为3125 ms即3.1 s。耗时最长的路径fs-kernel DBCG-RTL渲染阶段用时5222.512 s,长达1 h 27 min,而FRCG处理总时间,只有11.818 s,FRCG处理速度明显快于DBCG-RTL工具。
6.2 传输数据大小
对比两工具传输数据,DBCG-RTL生成的SVG图,存在如点击响应的链接和渲染后的坐标信息等冗余内容,同时XML的标签结构数据随着元素数量上升而增加,导致数据量大;Node处理后的图数据,只包含节点和边的属性信息,节点和边的跳转事件由页面动态创建,节点和边无需存储跳转链接数据。同时图的渲染由页面组件实现,数据中无需包含渲染坐标等信息。实现大幅度减少传输数据,并随着图上元素数量的增多,传输数据量减小的比例更大。
对数据减小率散点数据,如图9所示,增加幂函数回归[16]趋势线辅助,随着图中元素数量增加FRCG传输数据减小比例越大,对于全部测试路径,最小减少90%传输数据大小,平均减少93.53%传输数据大小。
图9 数据减小比
6.3 考虑服务器缓存进行对比
Node.js服务器开启历史记录,将历史数据写入数据库,并设置过期清除。对比无历史数据时,平均减少72.64%等待时间。
考虑SVG图静态内容存放在服务器中,用户访问已有SVG图时,直接获取资源。对比用户查看已有SVG图和存在历史记录的Node.js服务器的加载时间最值见表5,平均时间差为506 ms。
表5 DBCG-RTL与FRCG调用图加载时间最值对比/ms
在实际分析工作中,研究人员需要经常查看的调用图大概率存在历史缓存,而详细路径下函数关系图存在缓存的概率较低。故对研究人员连续查看多个路径函数调用图的情况进行模拟,假设部分路径存在历史数据,部分路径没有历史记录需要计算,随机取10,20,30,40个路径,每个路径存在历史数据概率为20%,50%,80%,90%,100%,重复10次取平均得到实验结果如图10所示。
图10 服务器存在历史记录的加载时间对比
当缓存概率大于90%时,DBCG-RTL工具才能比FRCG工具更快显示。连续多次查看存在历史缓存的调用图时,SVG图的获取时间明显快于FRCG工具,但SVG图中包含大量冗余数据,全部存储会占用大量存储空间。而FRCG工具,能够在牺牲少量加载时间的情况下,减少历史记录所占用空间,同时对于查看无缓存的调用图,FRCG能够大幅提高加载速度。
综上所述,FRCG工具在调用图的数据计算和渲染速度上优于DBCG-RTL,同时网路传输数据量更小。DBCG-RTL的优势在于能够存储静态调用图进行显示,FRCG使用缓存数据时,仍需在页面中渲染生成调用图,查看调用图过程无法避免页面的渲染过程。
7 结束语
本文将前后端分离框架与分析工具结合实现FRCG工具,将调用图的生成过程,拆分为后端处理数据,前端渲染调用图,平均减少了83.02%的调用图等待时间。服务器处理阶段,采用Promise对象异步读取数据库,提高图数据的处理速度,同时优化传输的数据结构,平均减小93.53%传输数据量。前端页面通过对数据操作实现切换调用图和增量更新调用图,提高了调用图的灵活性。
FRCG工具主要实现静态调用数据的处理和展现,源码查看由链接外部页面实现,要形成完善的分析工具,需要将多种分析工具整合到工具中,下一步工作目标为增加源码模块,并实现源码模块与调用图和调用表的有效结合;同时增加系统监控模块,通过动态监控工具,实现显示运行中系统调用,力求实现静态分析与动态分析的结合。