Soot II: 數據流框架

設計數據流分析包含4步

1. 決定分析的本質。是向前還是向後?是否需要對分支特殊考慮?

2. 決定近似的目標。是may還是must? 具體來說是在merge時找交集還是並集。

3. 展現具體的flow。即對IR中的每條statement列等式(transfer function)

4. 決定初始狀態或者入口(如果是向後分析則是出口)和中間節點的近似。即空集或者滿集,依賴於分析的conservative。

做數據流分析我們需要某種結構來表現數據流是如何穿程序的腸而過的,如控制流圖。Soot的數據流框架能處理各種形式的cfg,通過實現接口soot.toolkits.graph.DirectedGrap。


下面用very-busy expr analysis作爲例子。

1 分析本質

Soot提供三種分析的實現: ForwardFlowAnalysis, BackwardFlowAnalysis 以及ForwardBranchedFlowAnalysis。前兩者除了flow流向外基本相同,結果爲兩張映射: 從節點到IN 集合和從節點到OUT集合。最後一個提供了通過不同branch node傳遞不同信息(比如信息流從帶有if (x > 0)的節點流出時可以是x>0或者x<=0),因此包含3種maps: 從結點到IN集合, 從節點到OUT集合以及從節點到branch OUT集合。所有以上通過worklist算法來解fix-point的機制來實現。如果想用別的算法來實現可以繼承以下三個superclass中的一個: AbstractFlowAnalysis, FlowAnalysis, 或者BranchedFlowAnalysis。

對於very-busy expr analysis, 需要向後分析,因此class定義爲

class Very Busy Expression Analysis extends BackwardFlowAnalysis

構造函數爲

public VeryBusyExpressionAnalysis(DirectedGraph g) {
super(g); // 調用super constructor
doAnalysis(); // 調用fixed-point 機制
}
2. 近似的級別

近似級別由分析如何施展對lattice元素的JOINs來表現。分析可以是may或者must。前者我們用並集來實現Join, 後者則用交集。在Soot框架中jion由merge函數實現。very-busy expr分析是must analysis所以我們用交集實現join。

protected void merge(Object in1, Object in2, Object out) {
FlowSet inSet1 = (FlowSet)in1,
inSet2 = (FlowSet)in2,
outSet = (FlowSet)out;
inSet1.intersection(inSet2, outSet);
}
可以看到抽象級別爲不對lattice元素表現形式做任何假設。我們還需要一種函數把一個lattice元素的內容複製到另一個lattice元素

protected void copy(Object source, Object dest) {
FlowSet srcSet = (FlowSet)source,
destSet = (FlowSet)dest;
srcSet.copy(destSet);
}

3. 表現flow

真正表現information在cfg flow的地方,即transfer function。包含兩部分1)對相應節點,讓信息從IN集合流到OUT集合,除去該結點kills掉的信息 2) 對結點產生的OUT 集合加入結點生成的信息。

對於very-busy expr分析,結點幹掉節點中被重定義了的expr,產生在expr中使用過了expr。

protected void flowThrough(Object in, Object node, Object out) {
FlowSet inSet = (FlowSet)source,
outSet = (FlowSet)dest;
Unit u = (Unit)node;
kill(inSet, u, outSet);
gen(outSet, u);
}
用戶需要自己實現kill和gen函數。在example中能看到相應例子。

4. 初始狀態
包括定義入口結點和其它結點的lattice 元素的內容,通過重載entryInitialFlow和newInitialFlow來實現。對very-busy expr入口結點是最後一條statement(結束點),初始化它和其它所有結點爲空集。

protected Object entryInitialFlow() {
return new ValueArraySparseSet();
}
protected Object newInitialFlow() {
return new ValueArraySparseSet();
}<pre name="code" class="java">

我們需要自己實現ValueArraySparseSet。

5. 侷限
注意我們默認是在Jimple上實現的分析。我們可以根據需求調整具體實現所在,如Grimple更適合分析expr因爲支持更復雜的expr表現形式。


Flow 集合

每個CFG中的結點有一個Flow set與之相聯。如在very-busy expr中flow set是該點busy expr的集合。分爲有界(接口BoundedFlowSet)和無界(接口FlowSet)兩種flow集合,前者知道整個全集的取值,後者未知。對FlowSet接口需要實現的函數有 clone(), clear(), isEmpty(), copy(FlowSet dest) //深copy, union(FlowSet other, FlowSet dest) //dest <- this ∪ other, intersection(FlowSet other, FlowSet Dest) // dest <- this ∩ other, difference(FlowSet other, FlowSet dest) // dest <- this - other

以上這些函數足夠使得一個flow集合成爲合法的lattice元素。

對於BoundedFlowSet, 還需要函數來實現求補集和topped set(例如全集)。

看看我們之前提到的各種set,

ArraySparseSet, 無界的flow set,集合用一個由references組成的數組表示。注意不要調用.equals函數來判斷是否相等,需要自己實現接口soot.EquitvTo

ArrayPackedSet 有界flow set。需要提供FlowUniverse 對象,即代表全集的容器。由一個在integer和object雙向map和一個用來表示全集成員是否在內的bit vector表示。

ToppedSet 在基於上面兩種的set前提下,加入額外信息來表示其爲lattice中的Top。


控制流圖

位於soot.toolkits.graph package內包含多種控制流圖,皆基於DirectedGraph接口。其定義了函數用於獲得: 出入結點,對給定結點的前置和後繼結點,用於遍歷圖的遍歷器以及吐得大小(結點個數)。結點用Soot Units來實現。Base class爲UnitGraph,一個用於幫助創建CFG的抽象類,基於其有三種不同的實現:

BriefUnitGraph 不包含針對exception的邊

ExceptionalUnitGraph 包含exception的邊。

TrapUnitGraph 和前者類似。

通過以下代碼對給定的函數創建CFG

UnitGraph g = new ExceptionalUnitGraph(body);

分析結果打包

AbstractFlowAnalysis的getFlowbefore, FlowAnalysis的getFlowAfter 和BranchedFlowAnalysis的getBranchFlowAfter及getFallFlowAfter函數獲取分析結果。函數返回結果時一個表示lattice元素的對象。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章