数据流分析
IMPORTANT 数据流分析推导数据沿着程序执行路径流动的信息。
分类
| 分类依据 | 具体分类 | 简要解释 |
|---|
| 依据语句顺序 | 流敏感分析 | 考虑程序语句的执行顺序 |
| 流不敏感分析 | 不考虑语句执行顺序 |
| 依据流的方向 | 前向分析 | 从程序入口开始,沿着执行路径向出口方向分析 |
| 后向分析 | 从程序出口开始,逆着执行路径向入口方向分析 |
| 依据路径的量化信息 | May (Union) 分析 | 考虑程序所有可能执行的路径 |
| Must (Intersection) 分析 | 考虑程序必然执行的路径 |
| 依据对过程的处理 | 过程内分析 | 仅分析单个过程(函数/方法)内部的逻辑 |
| 过程间分析 | 分析多个过程间的调用关系 |
命令式程序语言 WHILE
在保证分析有效性的前提下,为了降低分析复杂度,通常会定义一种简化抽象的语言作为程序分析对象,从而剥离无关细节,避免陷入真实程序设计语言的细节泥潭。这里给出一种简单命令式语言(无过程或高级数据结构)—— WHILE 语言。
语法
| Category | Domain | Meta variable |
|---|
| Numbers | Z={0,1,−1,…} | z |
| Truth values | B={true,false} | t |
| Variables | Var={x,y,…} | x |
| Arithmetic expressions | AExp | a |
| Boolean expressions | BExp | b |
| Commands (statements) | Cmd | c |
| 类别 | 语法定义 | 所属集合 |
|---|
| Arithmetic expressions (AExp) | a::=z∣x∣a1+a2∣a1−a2∣a1∗a2∈AExp | AExp |
| Boolean expressions (BExp) | b::=t∣a1=a2∣a1>a2∣¬b∣b1∧b2∣b1∨b2∈BExp | BExp |
| Commands (statements, Cmd) | c::=skip∣x:=a∣c1;c2∣if b then c1 else c2∣while b do c∈Cmd | Cmd |
示例
下面是一个 WHILE 语言示例:
x:=6;y:=7;z:=0;while x>0 dox:=x−1;v:=y;while v>0 dov:=v−1;z:=z+1
控制流表示
打标签的程序(Labelled Programs)
IMPORTANT 为了标注分析信息所附着的位置信息,通常会为程序的每一条可执行语句、控制结构的关键节点(如循环判断、分支判断点)分配唯一的标签(Label)。
对于 WHILE 语言,数据流信息附着于 skip 语句、赋值语句、条件测试语句以及循环语句。即:
a::=z∣x∣a1+a2∣a1−a2∣a1∗a2∈AExpb::=t∣a1=a2∣a1>a2∣¬b∣b1∧b2∣b1∨b2∈BExpc::=[skip]l∣[x:=a]l∣c1;c2∣if [b]l then c1 else c2∣while [b]l do c∈Cmd
TIP 这里注意 c1;c2 作为顺序复合语句,不需要标注,因为其中的 c1 和 c2 本身包含标签。
IMPORTANT 对于程序 c 中被打标签的片段称为块,记作 Blkc。
对于前面的示例代码,对其标注标签后:
[x:=6]1;[y:=7]2;[z:=0]3;while [x>0]4 do[x:=x−1]5;[v:=y]6;while [v>0]7 do[v:=v−1]8;[z:=z+1]9
控制流表示
IMPORTANT 每个打标签的语句有一个单入口(initial labels)和可能的多个出口(final labels)。
init 映射
对于程序语句存在映射:
init: Cmd→Lab
其中,Cmd 表示程序中的语句集合,Lab 表示标签集合。init 映射是一个全函数,即 ∀c∈Cmd, ∃l∈Lab 使得 init(c)=l,其中 l 表示语句 c 的唯一入口。
针对 WHILE 语言,定义 init 映射为:
init([skip]l)init([x:=a]l)init(c1;c2)init(if [b]l then c1 else c2)init(while [b]l do c):=l:=l:=init(c1):=l:=l
其中,(3) 表示复合语句应递归地获取该复合语句第一条语句的入口作为整个符合语句的入口。
final 映射
此外,程序语句存在映射:
final: Cmd→2Lab
TIP 这里的 2Lab 表示 Lab 标签集合的幂集,其每个元素均为 Lab 集合的子集,即 ∀l∈2Lab, l⊆Lab。
其中,Cmd 表示程序中的语句集合,Lab 表示标签集合。final 映射同样是一个全函数,∀c∈Cmd,∃l∈2Lab 使得 final(c)=l,其中 l 表示语句 c 所有可能的出口集合。
对于 WHILE 语言,定义 final 映射为:
final([skip]l)final([x:=a]l)final(c1;c2)final(if [b]l then c1 else c2)final(while [b]l do c):={l}:={l}:=final(c2):=final(c1)∪final(c2):={l}
需要注意第 (10) 条,对于循环语句,跳出循环前要判断循环条件 b 不成立。
(控制)流关系
IMPORTANT 前面定义了程序语句的位置标签、init 与 final 映射,于是可以定义程序(控制)流关系:
flow(c)⊆Lab×Lab其中,Lab×Lab 即笛卡尔积。对于笛卡尔积 A×B 有, ∀<a,b>∈A×B ,其中 a∈A 且 b∈B。
对于 flow(c) ,∀<l,l′>∈flow(c) ,则 <l,l′> 表示在程序语句 c 中,存在控制流从位置标签 l 流向了 l′。
对于 WHILE 语言,定义 flow 为:
flow([skip]l):=∅flow([x:=a]l):=∅flow(c1;c2):=flow(c1)∪flow(c2)∪{(l,init(c2))∣l∈final(c1)}flow(if [b]l then c1 else c2):=flow(c1)∪flow(c2)∪{(l,init(c1)),(l,init(c2))}flow(while [b]l do c):=flow(c)∪{(l,init(c))}∪{(l′,l)∣l′∈final(c)}
TIP 关注标红部分,对于顺序复合语句 c1;c2,需要增加从语句 c1 到 c2 的控制流边;对于条件判断语句 if [b]l then c1 else c2 需要增加由判断条件 [b]l 到语句 c1 以及 判断条件 [b]l 到语句 c2 的控制流边;对于循环判断语句 while [b]l do c 则不仅需要添加判断条件 [b]l 到语句 c 的控制流边,还需添加语句 c 所有可能出口到判断条件 [b]l 的控制流边。
示例
c=[z:=1]1;while [x>0]2 do[z:=z∗y]3;[x:=x−1]4
计算 init、final 映射以及控制流关系:
init(c)final(c)flow(c)===1{2}{(1,2),(2,3),(3,4),(4,2)}
从而根据上述信息可以构建控制流图:
控制流图
NOTE 数据流分析本质上都是把程序看成是一个图,分析算法都是基于图表示的。
Comments