函数调用的数据流分析方法

  一 引言

        在测试人员对代码文件进行静态分析过程中,数据流分析占有举足轻重的作用,很多重要的信息,比如不可达语句、DU、DD、UR…等都是基于数据流分析的结果而得出的。以往,数据流分析通常是由测试人员人工完成。测试者在理解代码逻辑的基础上,从被分析代码的起点跟踪数据的流向,直到到达被分析代码的终点。这项工作看起来简单,但是实际做起来却往往令测试人员感到非常头疼。主要原因有三点:1.数据的变化要随着路径长度的增加不断累积;2.分支条件要随着路径的增长不断累积,并且路径上每增加一个分支节点,都要在路径中最新的数据分析结果的基础上计算分支条件的值; 3.路径数通常随着代码规模的增长以几何级数的速度增长。无论是数据变化结果的累积还是分支条件的累积或者路径数的增长,当任何一个累计到一定数量的时候,对于人脑来说,在进行跟踪与分析就成为一件不可能完成的任务了。所以在实际的工作中,测试人员的通常做法就是尽力而为,能分析多少就分析多少。这就使很多隐藏在代码中的很多问题在测试人员面前逃之夭夭。

         随着计算机技术的不断发展,各行各业的信息化工作也进行得如火如荼。“让电脑代替测试人员进行数据流分析”也不断的被提到测试行业的信息化工作当中。然而却一直进展缓慢,始终达不到工程应用的水平。最主要的拦路石有两块:1 代码中的循环结构的数据流分析;2代码中的函数调用语句的数据流分析。本文阐述的分析方法能够准确高效的完成函数调用的数据流分析。关于循环结构的数据流分析方法在另外一篇文章中阐述。

二 基本概念

        在介绍具体方法之前,先交代涉及的主要的概念。代码的数据流分析,顾名思义就是对一段代码中从起点到终点数据流动的全过程进行分析。在数据流动的过程中,可能会出现数据定值,也可能会出现数据引用。同时,由于代码中有可能会存在多种分支语句,因此,从起点出发,会产生一条或者多条路径,每条路径中由多个节点组成,每个节点对应相应的约束条件,除此之外,每个节点还对应着到达该节点之前所有的变量应用的最终结果,我们将其称之为数据切片。随着路径长度的增长,约束条件不断累积,在到达终点之前,如果约束条件的取值为永假,则路径终止,此路径为不完整路径,如果顺利到达终点,则此路径为完整路径。

三 使用“插队法”实现函数调用的数据流分析

      “插队法”是对一般数据流分析方法的改进,经过改进后,能够完成对包含函数调用的代码段进行正确高效的数据流分析。

1 普通的数据流分析方法

        对于没有循环和函数调用的代码段,一般的数据流分析方法包括变量应用分析、构建块树、建立控制流图、遍历路径等几个步骤。

  • 变量应用分析

        在语法分析的基础上,对在代码中发生定值及引用的语句进行分析,并将定值及引用的变量及其具体值按照发生的顺序进行记录。注意,一定要按照顺序记录,因为不同的顺序会产生不同的数据结果。

  • 构建块树

        在词法分析、语法分析的基础上,以代码段起始点为根节点,分析代码段中各分支语句,如if、else if、for、 while、case…等分支语句以及break 、exit、 return…等强制转向语句的层次关系及先后关系,从而构建成一颗树,通常会将带有函数调用的语句处理为普通语句,不做为单独的一个块来处理。

  •  建立控制流图

        在块树构建完成后,从根节点开始,以根节点作为源点,按照子块的先后顺序及层次关系对子块进行分析,根据每个子块的具体类型建立其所有目标节点,所有块分析完毕后,将会构建成特定代码段的控制流图。

  • 遍历路径

        从控制流图的源点开始,使用深度优先搜索遍历图中所有路径,路径边上面的变量应用都要记录到数据切片上。以最新的数据切片为基础,在遍历的过程中不断计算各节点累积约束条件的逻辑值,计算并记录下每个节点对应的最新的数据切片,获取所关注的数据流分析结果。

2 “插队法”

“插队法”是在普通数据流分析方法基础之上针对函数调用语句进行了特殊处理的方法。

    首先,在构建块树阶段,要将函数调用语句作为一个独立的块处理,由于某些分支语句中会存在函数调用,为了避免重复,要在获取其它类型的子块完成后在获取函数调用子块,如果函数调用语句同其他类型的子块入口语句重合,则不作为函数调用子块。

    其次,在建立控制流图过程中,如果遇到函数调用子块,将其作为一个普通的节点正常承转即可。

    最后,在遍历路径阶段,遇到函数调用节点,要通过“插队”来实现对函数调用语句的正确的数据流分析。具体要经过子函数数据流分析、插队等两个主要步骤来完成。

  • 子函数数据流分析

        在深度优先搜索遍历路径过程中,如果遇到函数调用节点,需要获取当前节点所对应的具体语句内容,继而对其进行表达式分析。当分析到函数调用时候,如果是自定义函数,需要对参数列表进行表达式分析,在分析的过程中要应用当前路径中的最新的数据切片,分析完成后将会获得实参数据列表。接着以实参作为基础数据,以累积的约束条件为基础约束条件,以子函数的起始点为数据流的起点,子函数每个返回语句作为数据流的终点,对子函数进行数据流分析,分析过程同样是具有“插队”功能的数据流分析方法。子函数数据流分析的结果是返回值集合,每条路径都对应一个返回值,每个返回值也同一个约束条件集相关联,同时,每个返回值都同其所属路径的最终数据切片相关联。当然,对于纯粹的过程调用,返回值就不存在了,但每条路径都对应一个最终的约束条件集和最终的数据切片,返回值对应的最终数据切片中应该剔除掉局部变量,仅保留全局变量及输出参数的数据切片。子函数数据流分析完成后,分别按照每条路径的返回值、约束条件集、最终数据切片继续进行函数调用语句的表达式的分析,分析的结果是表达式取值集合,集合中每个表达式取值都对应一组约束条件以及相应的数据切片。如果表达式中存在多个函数调用,要在表达式分析的过程中利用乘法原理对其进行组合,构成完整的表达式取值集合。

  • “插队”

        子函数数据流分析完成后并完成函数调用语句的表达式分析后,遍历表达式取值集合,分别以表达式取值集合中的每个元素为当前节点的目标节点,插队进入到当前路径中,同时,将元素的数据切片同本函数当前路径中的最新的数据切片相整合,相应的约束条件也要进行整合,构成新的数据切片和新的约束条件集。继续在新的控制流图中进行路径遍历,直至完成。在路径中其它类型的节点对应的语句中如果有函数调用,也要先在表达式分析的过程中进行子函数的数据流分析及相应的表达式分析,获取相应的表达式取值结合,继而进行插队,插队完成后,继续在新的控制流图中完成路径的遍历。

四 具体应用

      测试之家开发的ufinder(不可达语句检测工具)V0.97中应用了“插队法”,相对于一般的数据流分析,在ufinder中对数据流分析进行了一定程度的简化,主要有两点:1.数据切片中仅仅存储变量的定值,而没有存储变量的引用;2.遍历过程中如果找到一条完整路径,说明存在一条可达路径,遍历过程立刻停止。通过实际工程应用的检验,“插队法”确实能够准确高效且全面地对包含有函数调用语句的代码段进行数据流分析。

猜你喜欢

转载自blog.csdn.net/plstudio1/article/details/79637698