软件分析08 Datalog
2021-08-13 14:29:00

Datalog最早作为数据库的查询语言出现,是图灵完备的编程语言

语法

和数理逻辑的命题逻辑、谓词逻辑基本是一致的,如果看的教材和我一样是Mathematical Logic in Computer Science的话甚至会发现很多描述的语句都是一样的

,表示and

;表示or

!表示not,这些运算可以用括号改变优先级

每条语句的最后要加.表示结束,这个老是忘....

Predicate

即谓词。谓词可以看成是一个函数,n元谓词就是一个\(D^n\mapsto \left\{\;0,1\;\right\}\)的函数,其中\(D\)是命题变元(变量)与命题常元(常量)构成的集合

在Datalog中,我们讨论的都是有限论域,因此使谓词P为真的命题有限。记\(F(P)\)为所有在谓词P下为真的n元组,则我们称这些元素为Facts。这样就把一个函数变成判断元素是否属于某集合的问题了。

谓词分为两类,算术谓词x >= y和关系谓词Friends(x, y),算术谓词是Unbounded的

Inference Rules

即推理规则。用PPT的例子就是Datalog的推理规则形如


Adult(x) <- Person(x, age), age >= 18.

<-可以看成是向左的箭头(好形象!),箭头左侧的称为Head,右侧是Body。语句的意思是"若Body为真,则Head为真"

这句话的意思是"若x是age岁的人 且 age>=18 那么x是成年人"

考虑形式化命题会怎么说

$((x)(Person(x)Age(x)>=18))Adult(x) $

观察区别即可发现,如果不使用函数的概念(形式化命题里的\(Age()\)),我们就需要维护某个元素所有相关的信息,即用元组

求解

就是列真值表,\(\prod\limits_{B\in Body} |B|\)枚举所有可能的值求解\(F(P)\)

Safe Rules

既然涉及到枚举,就要看看是不是可穷尽的

考虑如下两组Rules


A(x) <- B(y), x >= y.

A(x) <- B(y), !C(x, y).

对于1. 如果我们讨论的是正整数域,且\(F(B)\)非空,则必然存在无穷多的x满足要求...

对于2. 由于\(F(C)\)有限,因此\(F(!C)\)有无穷多元素,因此也存在无穷多的x满足要求...

基于上述情况,Datalog对Rules作出了规定:

Head中的变元必须在Body中的某个非否定谓词中出现过(即必须是Bounded Variable)

同样,再考虑如下Rule


A(x) <- !A(x).

很显然就不是合理的Rule,毕竟都不是重言式...因此设定Rules的时候也要考虑是否永真的问题(不能有矛盾)

IDB & EDB

IDB = Intensional DataBase 内涵谓词

EDB = Extensional DataBase 外延谓词

大概理解就是在初始阶段会规定一些谓词,这些就是EDB

而在推理过程中出现的谓词则是IDB。Head一定是IDB,而Body可以IDB、EDB

通常EDB是不可变的(常元),而IDB可以随着程序的进行而修改,例如如果我想表示"喜欢猫 或 喜欢狗 的人是好人",那么我可以写


Good(x) <- Likes(x, "Dog").

Good(x) <- Likes(x, "Cat").

当然也可以写


Good(x) <- Likes(x, "Dog"); Likes(x, "Cat").

递归

Datalog的强大之处就在于递归

考虑给定等价关系集合的传递闭包,就可以这么写


Eq(x, y) <- Rel(x, y)


Eq(x, y) <- Eq(x, z), Rel(z, y)

其中Rel(x, y)是EDB中提前定义好的常元

在指针分析中的应用

应用Datalog就能很容易翻译和实现前面讲到的Flow Insensitive & Context Insensitive分析

首先规定下面几个谓词


New(x, o)

Assign(x, y)

Store(x, f, y)

Load(x, y, f)


VCall(l, x, k) #l: x.k(a1,a2...an)

Dispatch(o, k, m)

ThisVar(m, this)

Argument(l, i, ai) #ith argument at l: is ai

Parameter(m, i, pi) #ith parameter at method m is pi

MethodReturn(m, ret) #return var at method m is ret

CallReturn(l, r) #r receives return value at l:

推导规则就是翻译PPT


VarPointsTo(x, o) <- New(x, o).

VarPointsTo(x, o) <- VarPointsTo(y, o), Assign(x, y).

FieldPointsTo(o1, f, o2) <- VarPointsTo(x, o1), VarPointsTo(y, o2), Store(x,
f, y).

VarPointsTo(x, o2) <- VarPointsTo(y, o1), FieldPointsTo(o1, f, o2), Load(x, y,
f).


CallGraph(l, m), Reachable(m), VarPointsTo(this, o) <- VCall(l, x, k),
VarPointsTo(x, o), Dispatch(o, k, m), ThisVar(m, this).

VarPointsTo(pi, o) <- CallGraph(l, m), Argument(l, i, ai), Parameter(m, i,
pi), VarPointsTo(ai, o).

VarPointsTo(r, o) <- MethodReturn(m, ret), CallGraph(l, m), CallReturn(l, r),
VarPointsTo(ret, o).

全程序分析还要加上一个默认的入口方法,没了