sharing data per class analysis instead of per thread

This commit is contained in:
Ilya Klyuchnikov
2014-11-12 16:44:04 +03:00
parent b016946f70
commit f257da5961
4 changed files with 30 additions and 36 deletions
@@ -203,13 +203,6 @@ abstract class Analysis<Res> {
public static final int STEPS_LIMIT = 30000;
public static final int EQUATION_SIZE_LIMIT = 30;
protected static final ThreadLocal<State[]> ourPendingStates = new ThreadLocal<State[]>() {
@Override
protected State[] initialValue() {
return new State[STEPS_LIMIT];
}
};
final RichControlFlow richControlFlow;
final Direction direction;
final ControlFlowGraph controlFlow;
@@ -77,6 +77,13 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
public static Map<Key, List<Equation<Key, Value>>> processClass(final ClassReader classReader, final String presentableUrl) {
// It is OK to share pending states, actions and results for analyses.
// Analyses are designed in such a way that they first write to states/actions/results and then read only those portion
// of states/actions/results which were written by the current pass of the analysis.
// Since states/actions/results are quite expensive to create (32K array) for each analysis, we create them once per class analysis.
final State[] sharedPendingStates = new State[Analysis.STEPS_LIMIT];
final PendingAction[] sharedPendingActions = new PendingAction[Analysis.STEPS_LIMIT];
final PResults.PResult[] sharedResults = new PResults.PResult[Analysis.STEPS_LIMIT];
final Map<Key, List<Equation<Key, Value>>> equations = new HashMap<Key, List<Equation<Key, Value>>>();
classReader.accept(new ClassVisitor(Opcodes.ASM5) {
@@ -215,7 +222,7 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
Equation<Key, Value> outEquation =
isInterestingResult ?
new InOutAnalysis(richControlFlow, Out, origins, stable).analyze() :
new InOutAnalysis(richControlFlow, Out, origins, stable, sharedPendingStates).analyze() :
null;
if (isReferenceResult) {
@@ -229,7 +236,8 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
if (ASMUtils.isReferenceType(argumentTypes[i])) {
boolean possibleNPE = false;
if (leakingParameters[i]) {
NonNullInAnalysis notNullInAnalysis = new NonNullInAnalysis(richControlFlow, new In(i, In.NOT_NULL_MASK), stable);
NonNullInAnalysis notNullInAnalysis =
new NonNullInAnalysis(richControlFlow, new In(i, In.NOT_NULL_MASK), stable, sharedPendingActions, sharedResults);
Equation<Key, Value> notNullParamEquation = notNullInAnalysis.analyze();
possibleNPE = notNullInAnalysis.possibleNPE;
notNullParam = notNullParamEquation.rhs.equals(FINAL_NOT_NULL);
@@ -245,7 +253,7 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
result.add(new Equation<Key, Value>(new Key(method, new In(i, In.NULLABLE_MASK), stable), FINAL_TOP));
}
else {
result.add(new NullableInAnalysis(richControlFlow, new In(i, In.NULLABLE_MASK), stable).analyze());
result.add(new NullableInAnalysis(richControlFlow, new In(i, In.NULLABLE_MASK), stable, sharedPendingStates).analyze());
}
}
else {
@@ -260,9 +268,9 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
}
else {
// may be null on some branch, running "null->..." analysis
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.Null), origins, stable).analyze());
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.Null), origins, stable, sharedPendingStates).analyze());
}
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.NotNull), origins, stable).analyze());
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.NotNull), origins, stable, sharedPendingStates).analyze());
}
else {
// parameter is not leaking, so a contract is the same as for the whole method
@@ -41,7 +41,7 @@ class InOutAnalysis extends Analysis<Result<Key, Value>> {
static final ResultUtil<Key, Value> resultUtil =
new ResultUtil<Key, Value>(new ELattice<Value>(Value.Bot, Value.Top));
final private State[] pending = ourPendingStates.get();
final private State[] pending;
private final InOutInterpreter interpreter;
private final Value inValue;
private final int generalizeShift;
@@ -49,8 +49,9 @@ class InOutAnalysis extends Analysis<Result<Key, Value>> {
private int id = 0;
private int pendingTop = 0;
protected InOutAnalysis(RichControlFlow richControlFlow, Direction direction, boolean[] resultOrigins, boolean stable) {
protected InOutAnalysis(RichControlFlow richControlFlow, Direction direction, boolean[] resultOrigins, boolean stable, State[] pending) {
super(richControlFlow, direction, stable);
this.pending = pending;
interpreter = new InOutInterpreter(direction, richControlFlow.controlFlow.methodNode.instructions, resultOrigins);
inValue = direction instanceof InOut ? ((InOut)direction).inValue : null;
generalizeShift = (methodNode.access & ACC_STATIC) == 0 ? 1 : 0;
@@ -167,30 +167,21 @@ class MakeResult implements PendingAction {
}
class NonNullInAnalysis extends Analysis<PResult> {
private static final ThreadLocal<PendingAction[]> ourPending = new ThreadLocal<PendingAction[]>() {
@Override
protected PendingAction[] initialValue() {
return new PendingAction[Analysis.STEPS_LIMIT];
}
};
private static final ThreadLocal<PResult[]> ourResults = new ThreadLocal<PResult[]>() {
@Override
protected PResult[] initialValue() {
return new PResult[Analysis.STEPS_LIMIT];
}
};
final private PendingAction[] pending = ourPending.get();
final private PendingAction[] pendingActions;
private final PResult[] results;
private final NotNullInterpreter interpreter = new NotNullInterpreter();
private PResult[] results;
// Flag saying that at some branch NPE was found. Used later as an evidence that this param is *NOT* @Nullable (optimization).
boolean possibleNPE;
protected NonNullInAnalysis(RichControlFlow richControlFlow, Direction direction, boolean stable) {
protected NonNullInAnalysis(RichControlFlow richControlFlow,
Direction direction,
boolean stable,
PendingAction[] pendingActions,
PResult[] results) {
super(richControlFlow, direction, stable);
results = ourResults.get();
this.pendingActions = pendingActions;
this.results = results;
}
PResult combineResults(PResult delta, int[] subResults) throws AnalyzerException {
@@ -232,7 +223,7 @@ class NonNullInAnalysis extends Analysis<PResult> {
if (steps >= STEPS_LIMIT) {
throw new AnalyzerException(null, "limit is reached, steps: " + steps + " in method " + method);
}
PendingAction action = pending[--pendingTop];
PendingAction action = pendingActions[--pendingTop];
if (action instanceof MakeResult) {
MakeResult makeResult = (MakeResult) action;
PResult result = combineResults(makeResult.subResult, makeResult.indices);
@@ -397,7 +388,7 @@ class NonNullInAnalysis extends Analysis<PResult> {
if (pendingTop >= STEPS_LIMIT) {
throw new AnalyzerException(null, "limit is reached in method " + method);
}
pending[pendingTop++] = action;
pendingActions[pendingTop++] = action;
}
private void execute(Frame<BasicValue> frame, AbstractInsnNode insnNode) throws AnalyzerException {
@@ -418,12 +409,13 @@ class NonNullInAnalysis extends Analysis<PResult> {
}
class NullableInAnalysis extends Analysis<PResult> {
final private State[] pending = ourPendingStates.get();
final private State[] pending;
private final NullableInterpreter interpreter = new NullableInterpreter();
protected NullableInAnalysis(RichControlFlow richControlFlow, Direction direction, boolean stable) {
protected NullableInAnalysis(RichControlFlow richControlFlow, Direction direction, boolean stable, State[] pending) {
super(richControlFlow, direction, stable);
this.pending = pending;
}
@NotNull