Skip to content

Commit 2be91e4

Browse files
committed
add the implementation of FPC, the fine-grained garbage collector
1 parent 6346050 commit 2be91e4

File tree

10 files changed

+1551
-2
lines changed

10 files changed

+1551
-2
lines changed

soot-infoflow-cmd/src/soot/jimple/infoflow/cmd/MainClass.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,8 @@ else if (solver.equalsIgnoreCase("FLOWINSENSITIVE"))
593593
return DataFlowSolver.FlowInsensitive;
594594
else if (solver.equalsIgnoreCase("GC"))
595595
return DataFlowSolver.GarbageCollecting;
596+
else if (solver.equalsIgnoreCase("FPC"))
597+
return DataFlowSolver.FineGrainedGC;
596598
else {
597599
System.err.println(String.format("Invalid data flow solver: %s", solver));
598600
throw new AbortAnalysisException();

soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.slf4j.Logger;
1919
import org.slf4j.LoggerFactory;
2020

21+
import heros.solver.Pair;
2122
import soot.FastHierarchy;
2223
import soot.G;
2324
import soot.MethodOrMethodContext;
@@ -704,7 +705,13 @@ public Thread newThread(Runnable r) {
704705
manager = initializeInfoflowManager(sourcesSinks, iCfg, globalTaintManager);
705706

706707
// Create the solver peer group
707-
solverPeerGroup = new GCSolverPeerGroup<SootMethod>();
708+
switch (manager.getConfig().getSolverConfiguration().getDataFlowSolver()) {
709+
case FineGrainedGC:
710+
solverPeerGroup = new GCSolverPeerGroup<Pair<SootMethod, Abstraction>>();
711+
break;
712+
default:
713+
solverPeerGroup = new GCSolverPeerGroup<SootMethod>();
714+
}
708715

709716
// Initialize the alias analysis
710717
Abstraction zeroValue = Abstraction.getZeroAbstraction(manager.getConfig().getFlowSensitiveAliasing());
@@ -1243,6 +1250,13 @@ protected IInfoflowSolver createDataFlowSolver(InterruptableExecutor executor, A
12431250
solverPeerGroup.addSolver(solver);
12441251
solver.setPeerGroup(solverPeerGroup);
12451252
return solver;
1253+
case FineGrainedGC:
1254+
logger.info("Using fine-grained garbage-collecting solver");
1255+
IInfoflowSolver fgSolver = new soot.jimple.infoflow.solver.gcSolver.fpc.InfoflowSolver(problem, executor,
1256+
solverConfig.getSleepTime());
1257+
solverPeerGroup.addSolver(fgSolver);
1258+
fgSolver.setPeerGroup(solverPeerGroup);
1259+
return fgSolver;
12461260
default:
12471261
throw new RuntimeException("Unsupported data flow solver");
12481262
}

soot-infoflow/src/soot/jimple/infoflow/InfoflowConfiguration.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,12 @@ public static enum DataFlowSolver {
133133
/**
134134
* Use the garbage-collecting solver
135135
*/
136-
GarbageCollecting
136+
GarbageCollecting,
137+
138+
/**
139+
* Use the fine-grained GC solver
140+
* */
141+
FineGrainedGC,
137142
}
138143

139144
public static enum DataFlowDirection {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package soot.jimple.infoflow.solver.gcSolver.fpc;
2+
3+
import heros.solver.Pair;
4+
import soot.SootMethod;
5+
import soot.jimple.infoflow.collect.ConcurrentHashSet;
6+
7+
import java.util.*;
8+
import java.util.concurrent.ConcurrentHashMap;
9+
import java.util.concurrent.locks.ReentrantLock;
10+
11+
public class AbstrationDependencyGraph<D> implements IGraph<Pair<SootMethod, D>> {
12+
private final ReentrantLock lock = new ReentrantLock();
13+
private final Set<Pair<SootMethod, D>> nodes = new ConcurrentHashSet<>();
14+
private final Map<Pair<SootMethod, D>, Set<Pair<SootMethod, D>>> succMap = new ConcurrentHashMap<>();
15+
private final Map<Pair<SootMethod, D>, Set<Pair<SootMethod, D>>> predMap = new ConcurrentHashMap<>();
16+
17+
@Override
18+
public Set<Pair<SootMethod, D>> getNodes() {
19+
return nodes;
20+
}
21+
22+
@Override
23+
public Set<Pair<SootMethod, D>> succsOf(Pair<SootMethod, D> node) {
24+
return succMap.getOrDefault(node, Collections.emptySet());
25+
}
26+
27+
@Override
28+
public Set<Pair<SootMethod, D>> predsOf(Pair<SootMethod, D> node) {
29+
return predMap.getOrDefault(node, Collections.emptySet());
30+
}
31+
32+
@Override
33+
public void addNode(Pair<SootMethod, D> node) {
34+
nodes.add(node);
35+
}
36+
37+
@Override
38+
public void addEdge(Pair<SootMethod, D> n1, Pair<SootMethod, D> n2) {
39+
addNode(n1);
40+
addNode(n2);
41+
succMap.computeIfAbsent(n1, k -> new ConcurrentHashSet<>()).add(n2);
42+
predMap.computeIfAbsent(n2, k -> new ConcurrentHashSet<>()).add(n1);
43+
}
44+
45+
@Override
46+
public boolean contains(Pair<SootMethod, D> node) {
47+
return nodes.contains(node);
48+
}
49+
50+
@Override
51+
public void removeEdge(Pair<SootMethod, D> n1, Pair<SootMethod, D> n2) {
52+
succsOf(n1).remove(n2);
53+
predsOf(n2).remove(n1);
54+
}
55+
56+
@Override
57+
public void remove(Pair<SootMethod, D> node) {
58+
nodes.remove(node);
59+
for (Pair<SootMethod, D> pred : predsOf(node)) {
60+
removeEdge(pred, node);
61+
}
62+
for (Pair<SootMethod, D> succ : succsOf(node)) {
63+
removeEdge(node, succ);
64+
}
65+
}
66+
67+
public void lock() {
68+
lock.lock();
69+
}
70+
71+
public void unlock() {
72+
if (lock.isHeldByCurrentThread()) {
73+
lock.unlock();
74+
}
75+
}
76+
77+
public int nodeSize() {
78+
return this.nodes.size();
79+
}
80+
81+
public int edgeSize() {
82+
int ret = 0;
83+
for (Set<Pair<SootMethod, D>> vs : succMap.values()) {
84+
ret += vs.size();
85+
}
86+
return ret;
87+
}
88+
89+
public Set<Pair<SootMethod, D>> reachableClosure(Pair<SootMethod, D> source) {
90+
final Set<Pair<SootMethod, D>> visited = new ConcurrentHashSet<>();
91+
final Deque<Pair<SootMethod, D>> stack = new ArrayDeque<>();
92+
stack.push(source);
93+
while (!stack.isEmpty()) {
94+
final Pair<SootMethod, D> node = stack.pop();
95+
visited.add(node);
96+
succsOf(node).stream().filter(n -> !visited.contains(n)).forEach(stack::push);
97+
}
98+
return visited;
99+
}
100+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package soot.jimple.infoflow.solver.gcSolver.fpc;
2+
3+
import heros.solver.Pair;
4+
import heros.solver.PathEdge;
5+
import soot.SootMethod;
6+
import soot.jimple.infoflow.solver.gcSolver.IGCReferenceProvider;
7+
import soot.jimple.toolkits.ide.icfg.BiDiInterproceduralCFG;
8+
import soot.util.ConcurrentHashMultiMap;
9+
10+
public class AggressiveGarbageCollector<N, D> extends FineGrainedReferenceCountingGarbageCollector<N, D> {
11+
public AggressiveGarbageCollector(BiDiInterproceduralCFG<N, SootMethod> icfg,
12+
ConcurrentHashMultiMap<Pair<SootMethod, D>, PathEdge<N, D>> jumpFunctions,
13+
IGCReferenceProvider<Pair<SootMethod, D>> referenceProvider) {
14+
super(icfg, jumpFunctions, referenceProvider);
15+
}
16+
17+
public AggressiveGarbageCollector(BiDiInterproceduralCFG<N, SootMethod> icfg,
18+
ConcurrentHashMultiMap<Pair<SootMethod, D>, PathEdge<N, D>> jumpFunctions) {
19+
super(icfg, jumpFunctions);
20+
}
21+
22+
@Override
23+
protected IGCReferenceProvider<Pair<SootMethod, D>> createReferenceProvider() {
24+
return null;
25+
}
26+
27+
@Override
28+
public boolean hasActiveDependencies(Pair<SootMethod, D> abstraction) {
29+
int changeCounter = -1;
30+
do {
31+
// Update the change counter for the next round
32+
changeCounter = jumpFnCounter.getChangeCounter();
33+
34+
// Check the method itself
35+
if (jumpFnCounter.get(abstraction) > 0)
36+
return true;
37+
38+
} while (checkChangeCounter && changeCounter != jumpFnCounter.getChangeCounter());
39+
return false;
40+
}
41+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package soot.jimple.infoflow.solver.gcSolver.fpc;
2+
3+
import heros.solver.Pair;
4+
import heros.solver.PathEdge;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
import soot.SootMethod;
8+
import soot.jimple.infoflow.solver.gcSolver.AbstractReferenceCountingGarbageCollector;
9+
import soot.jimple.infoflow.solver.gcSolver.IGCReferenceProvider;
10+
import soot.jimple.toolkits.ide.icfg.BiDiInterproceduralCFG;
11+
import soot.util.ConcurrentHashMultiMap;
12+
13+
public abstract class FineGrainedReferenceCountingGarbageCollector<N, D>
14+
extends AbstractReferenceCountingGarbageCollector<N, D, Pair<SootMethod, D>> {
15+
protected static final Logger logger = LoggerFactory.getLogger(FineGrainedReferenceCountingGarbageCollector.class);
16+
17+
public FineGrainedReferenceCountingGarbageCollector(BiDiInterproceduralCFG<N, SootMethod> icfg,
18+
ConcurrentHashMultiMap<Pair<SootMethod, D>, PathEdge<N, D>> jumpFunctions,
19+
IGCReferenceProvider<Pair<SootMethod, D>> referenceProvider) {
20+
super(icfg, jumpFunctions, referenceProvider);
21+
}
22+
23+
public FineGrainedReferenceCountingGarbageCollector(BiDiInterproceduralCFG<N, SootMethod> icfg,
24+
ConcurrentHashMultiMap<Pair<SootMethod, D>, PathEdge<N, D>> jumpFunctions) {
25+
super(icfg, jumpFunctions);
26+
}
27+
28+
private class GCThread extends Thread {
29+
30+
private boolean finished = false;
31+
32+
public GCThread() {
33+
setName("Fine-grained aggressive IFDS Garbage Collector");
34+
}
35+
36+
@Override
37+
public void run() {
38+
while (!finished) {
39+
gcImmediate();
40+
41+
if (sleepTimeSeconds > 0) {
42+
try {
43+
Thread.sleep(sleepTimeSeconds * 1000);
44+
} catch (InterruptedException e) {
45+
break;
46+
}
47+
}
48+
}
49+
}
50+
51+
/**
52+
* Notifies the thread to finish its current garbage collection and then
53+
* terminate
54+
*/
55+
public void finish() {
56+
finished = true;
57+
interrupt();
58+
}
59+
60+
}
61+
62+
protected int sleepTimeSeconds = 1;
63+
protected int maxPathEdgeCount = 0;
64+
protected int maxMemoryConsumption = 0;
65+
66+
protected GCThread gcThread;
67+
68+
@Override
69+
protected void initialize() {
70+
super.initialize();
71+
72+
// Start the garbage collection thread
73+
gcThread = new GCThread();
74+
gcThread.start();
75+
}
76+
77+
@Override
78+
public void gc() {
79+
// nothing to do here
80+
}
81+
82+
@Override
83+
public void notifySolverTerminated() {
84+
gcImmediate();
85+
86+
logger.info(String.format("GC removes %d abstractions", getGcedAbstractions()));
87+
logger.info(String.format("GC removes %d path edges", getGcedEdges()));
88+
logger.info(String.format("Remaining Path edges count is %d", getRemainingPathEdgeCount()));
89+
logger.info(String.format("Recorded Maximum Path edges count is %d", getMaxPathEdgeCount()));
90+
logger.info(String.format("Recorded Maximum memory consumption is %d", getMaxMemoryConsumption()));
91+
gcThread.finish();
92+
}
93+
94+
/**
95+
* Sets the time to wait between garbage collection cycles in seconds
96+
*
97+
* @param sleepTimeSeconds The time to wait between GC cycles in seconds
98+
*/
99+
public void setSleepTimeSeconds(int sleepTimeSeconds) {
100+
this.sleepTimeSeconds = sleepTimeSeconds;
101+
}
102+
103+
private int getUsedMemory() {
104+
Runtime runtime = Runtime.getRuntime();
105+
return (int) Math.round((runtime.totalMemory() - runtime.freeMemory()) / 1E6);
106+
}
107+
108+
public long getMaxPathEdgeCount() {
109+
return this.maxPathEdgeCount;
110+
}
111+
112+
public int getMaxMemoryConsumption() {
113+
return this.maxMemoryConsumption;
114+
}
115+
116+
@Override
117+
protected void onAfterRemoveEdges() {
118+
int pec = 0;
119+
for (Integer i : jumpFnCounter.values()) {
120+
pec += i;
121+
}
122+
this.maxPathEdgeCount = Math.max(this.maxPathEdgeCount, pec);
123+
this.maxMemoryConsumption = Math.max(this.maxMemoryConsumption, getUsedMemory());
124+
}
125+
126+
@Override
127+
protected Pair<SootMethod, D> genAbstraction(PathEdge<N, D> edge) {
128+
SootMethod method = icfg.getMethodOf(edge.getTarget());
129+
return new Pair<>(method, edge.factAtSource());
130+
}
131+
}

0 commit comments

Comments
 (0)