package cmu.utils;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import cmu.conditional.Conditional;
import coverage.Interaction;
import de.fosd.typechef.featureexpr.FeatureExpr;
import de.fosd.typechef.featureexpr.FeatureExprFactory;
import gov.nasa.jpf.JPF;
import gov.nasa.jpf.jvm.bytecode.LocalVariableInstruction;
import gov.nasa.jpf.jvm.bytecode.ReturnInstruction;
import gov.nasa.jpf.vm.ClassInfo;
import gov.nasa.jpf.vm.ElementInfo;
import gov.nasa.jpf.vm.FieldInfo;
import gov.nasa.jpf.vm.Instruction;
import gov.nasa.jpf.vm.LocalVarInfo;
import gov.nasa.jpf.vm.MethodInfo;
import gov.nasa.jpf.vm.StackFrame;
import gov.nasa.jpf.vm.ThreadInfo;
import gov.nasa.jpf.vm.va.HybridStackHandler;
import gov.nasa.jpf.vm.va.IStackHandler;
/**
* This class handles how and which data is covered.
*
* @author Jens Meinicke
*
*/
public class CoverageClass {
private final ThreadInfo ti;
public CoverageClass(ThreadInfo ti) {
this.ti = ti;
}
long time = 0;
int localSize = 0;
Object oldLocal = null;
private void reset() {
time = 0;
localSize = 0;
oldLocal = null;
}
public void preExecuteInstruction(Instruction i) {
if (JPF.COVERAGE != null) {
reset();
if (JPF.SELECTED_COVERAGE_TYPE == JPF.COVERAGE_TYPE.interaction) {
int index = -1;
if (i instanceof LocalVariableInstruction) {
LocalVariableInstruction li = (LocalVariableInstruction) i;
index = li.getLocalVariableIndex();
if (index != -1) {
oldLocal = ti.getTopFrame().stack.getLocal(index);
localSize = ti.getTopFrame().stack.getLocal(ti.getTopFrame().stack.getCtx(), index).toMap().size();
}
}
}
}
if (JPF.SELECTED_COVERAGE_TYPE == JPF.COVERAGE_TYPE.time) {
time = System.nanoTime();
}
}
public void postExecuteInstruction(Instruction i, FeatureExpr ctx) {
if (JPF.COVERAGE != null) {
if (JPF.SELECTED_COVERAGE_TYPE == JPF.COVERAGE_TYPE.time) {
time = System.nanoTime() - time;
}
StackFrame topFrame = ti.getTopFrame();
if (topFrame != null && (topFrame.getMethodInfo() != i.getMethodInfo())) {
return;
}
Object newLocal = null;
int newLocalSize = localSize;
if (JPF.SELECTED_COVERAGE_TYPE == JPF.COVERAGE_TYPE.interaction) {
int index = -1;
if (i instanceof LocalVariableInstruction) {
LocalVariableInstruction li = (LocalVariableInstruction) i;
index = li.getLocalVariableIndex();
}
if (index != -1) {
newLocal = topFrame.stack.getLocal(index);
newLocalSize = topFrame.stack.getLocal(topFrame.stack.getCtx(), index).toMap().size();
}
}
performCoverage(i, ctx, time, localSize, oldLocal, newLocalSize, newLocal);
}
}
/**
* Logs the current instruction / state for coverage.
*
* @param instruction
* The instruction to cover.
* @param ctx
* @param time
* @param localInteractionChange
* @param oldLocal
* @param newLocal
*/
private void performCoverage(Instruction instruction, FeatureExpr ctx, long time, int oldLocalSize, Object oldLocal, int newLocalSize, Object newLocal) {
MethodInfo methodInfo = instruction.getMethodInfo();
if (methodInfo != null) {
ClassInfo classInfo = methodInfo.getClassInfo();
String file = classInfo.getSourceFileName();
if (file != null && ti.getTopFrame() != null) {
file = file.substring(file.lastIndexOf('/') + 1);
switch (JPF.SELECTED_COVERAGE_TYPE) {
case feature:
coverFeature(instruction, ctx, file);
break;
case stack:
coverStack(instruction, ctx, file);
break;
case local:
coverLocal(instruction, file);
break;
case interaction:
coverInteraction(newLocalSize, oldLocalSize, instruction, ctx, newLocal);
break;
case context:// same as for composedContext
case composedContext:
coverContext(instruction, ctx, file);
break;
case time:
coverTime(instruction, time, file);
break;
case frame:
coverFrame(instruction, file);
break;
default:
throw new RuntimeException(JPF.SELECTED_COVERAGE_TYPE + " not implemented");
}
}
}
}
private void coverFrame(Instruction instruction, String file) {
IStackHandler stack = ti.getTopFrame().stack;
if (instruction instanceof ReturnInstruction) {
return;
}
String text = "";
Interaction coverage = JPF.COVERAGE.getCoverage(file, instruction.getLineNumber());
if (coverage != null && coverage.getInteraction() == 10) {
return;
}
int interaction = 0;
if (stack instanceof HybridStackHandler) {
text = ((HybridStackHandler) stack).getStackHandler().getClass().toString();
text += "\n";
text += ti.getTopFrame().getMethodName();
text += "\n";
text += ((HybridStackHandler) stack).getStackHandler();
if (((HybridStackHandler) stack).isLifted()) {
if (stack.getStackWidth() > 1) {
interaction = stack.getStackWidth() + 4;
} else {
interaction = 2;
}
} else {
interaction = 1;
}
}
JPF.COVERAGE.setLineCovered(file, instruction.getLineNumber(), interaction, text + "\n" + instruction);
}
private void coverTime(Instruction instruction, long time, String file) {
final int modifier = 1_000_000;
Interaction interaction = JPF.COVERAGE.getCoverage(file, instruction.getLineNumber());
if (interaction != null) {
@SuppressWarnings({ "rawtypes", "unchecked" })
Map<Instruction, LinkedList<Long>> values = (Map) interaction.getValue();
if (values.containsKey(instruction)) {
LinkedList<Long> times = values.get(instruction);
times.add(time);
} else {
LinkedList<Long> initialValue = new LinkedList<Long>();
initialValue.add(time);
values.put(instruction, initialValue);
}
int degree = interaction.getInteraction();
if ((time / modifier) > degree) {
interaction.setInteraction((int) (time / modifier));
}
} else {
Map<Instruction, LinkedList<Long>> values = new TreeMap<Instruction, LinkedList<Long>>(new Comparator<Instruction>() {
@Override
public int compare(Instruction o1, Instruction o2) {
return o1.insnIndex - o2.insnIndex;
}
}) {
private static final long serialVersionUID = 1L;
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (java.util.Map.Entry<Instruction, LinkedList<Long>> entry : entrySet()) {
builder.append(entry.getKey()).append('\n');
LinkedList<Long> times = entry.getValue();
Collections.sort(times);
long min = times.getFirst();
long max = times.getLast();
long max2 = 0;
if (times.size() >= 2) {
max2 = times.get(times.size() - 2);
}
long sum = 0;
for (long value : times) {
sum += value;
}
long average = sum / times.size();
builder.append("Max:\t\t");
appendTime(builder, max);
builder.append("Max2:\t\t");
appendTime(builder, max2);
builder.append("Average:\t");
appendTime(builder, average);
builder.append("Min: \t\t");
appendTime(builder, min);
}
return builder.toString();
}
private void appendTime(final StringBuilder stringBuilder, final long time) {
stringBuilder.append("Min: \t\t");
if (time < 2000) {
stringBuilder.append(time).append("nano s\n");
} else {
stringBuilder.append(time / 1000).append("micro s\n");
}
}
};
LinkedList<Long> initialValue = new LinkedList<Long>();
initialValue.add(time);
values.put(instruction, initialValue);
JPF.COVERAGE.setLineCovered(file, instruction.getLineNumber(), (int) (time / modifier), values);
}
}
private void coverContext(Instruction instruction, FeatureExpr ctx, String file) {
Interaction interaction = JPF.COVERAGE.getCoverage(file, instruction.getLineNumber());
if (interaction != null) {
@SuppressWarnings({ "rawtypes", "unchecked" })
Map<FeatureExpr, Integer> values = (Map) interaction.getValue();
if (!values.containsKey(ctx)) {
values.put(ctx, 1);
interaction.setInteraction(interaction.getInteraction() + 1);
} else {
Integer runs = values.get(ctx);
values.put(ctx, runs + 1);
}
} else {
Map<FeatureExpr, Integer> values = new HashMap<FeatureExpr, Integer>() {
private static final long serialVersionUID = 1L;
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
FeatureExpr composedContext = FeatureExprFactory.False();
for (FeatureExpr entry : keySet()) {
composedContext = composedContext.or(entry);
}
builder.append("Composed context: ");
builder.append(Conditional.getCTXString(composedContext));
builder.append('\n');
for (java.util.Map.Entry<FeatureExpr, Integer> entry : entrySet()) {
FeatureExpr ctx = entry.getKey();
Integer runs = entry.getValue();
builder.append(Conditional.getCTXString(ctx));
builder.append(' ');
builder.append(runs);
if (runs == 1) {
builder.append(" instruction executed\n");
} else {
builder.append(" instructions executed\n");
}
}
return builder.toString();
}
};
values.put(ctx, 1);
JPF.COVERAGE.setLineCovered(file, instruction.getLineNumber(), 1, values);
}
}
private void coverLocal(Instruction instruction, String file) {
JPF.COVERAGE.setLineCovered(file, instruction.getLineNumber(), ti.getTopFrame().stack.getLocalWidth(), ti.getTopFrame().stack.getMaxLocal());
}
private void coverStack(Instruction instruction, FeatureExpr ctx, String file) {
JPF.COVERAGE.setLineCovered(file, instruction.getLineNumber(), ti.getTopFrame().stack.getStackWidth(), Conditional.getCTXString(ctx));
}
private void coverFeature(Instruction instruction, FeatureExpr ctx, String file) {
JPF.COVERAGE.setLineCovered(file, instruction.getLineNumber(), ctx.collectDistinctFeatures().size(), Conditional.getCTXString(ctx));
}
private void coverInteraction(int newLocalSize, int oldLocalSize, Instruction instruction, FeatureExpr ctx, Object newLocal) {
String localVariableName = "?";
int localInteractionChange = newLocalSize - oldLocalSize;
if (instruction instanceof LocalVariableInstruction) {
LocalVariableInstruction lvi = ((LocalVariableInstruction) instruction);
LocalVarInfo lv = lvi.getLocalVarInfo();
localVariableName = lvi.getLocalVariableName();
LocalVariableInstruction prev = (LocalVariableInstruction) getPrevLocalInstruction(instruction);
if (prev != null) {
if (prev.getLocalVarInfo() != lv) {
// previous value belongs to another variable
localInteractionChange = ti.getTopFrame().stack.getLocal(ti.getTopFrame().stack.getCtx(), ((LocalVariableInstruction) instruction).getLocalVariableIndex()).toMap().size() - 1;
if (localInteractionChange != 0) {
String content = localVariableName + " (" + Conditional.getCTXString(ctx) + "):\n";
content += ti.getTopFrame().trace(ctx) + "\n";
if (newLocal.toString().length() > 800) {
content += newLocal.toString().substring(0, 800) + "...";
} else {
content += newLocal;
}
CoverageLogger.logInteraction(ti.getTopFrame(), localInteractionChange, content, ctx);
}
return;
}
}
}
if (localInteractionChange != 0) {
String content = localVariableName + " (" + Conditional.getCTXString(ctx) + "):\n";
content += ti.getTopFrame().trace(ctx) + "\n";
if (oldLocal.toString().length() > 800) {
content += oldLocalSize + " : " + oldLocal.toString().substring(0, 800) + "...";
} else {
content += oldLocalSize + " : " + oldLocal;
}
content += "\n -> \n";
if (newLocal.toString().length() > 800) {
content += newLocalSize + " : " + newLocal.toString().substring(0, 800) + "...";
} else {
content += newLocalSize + " : " + newLocal;
}
CoverageLogger.logInteraction(ti.getTopFrame(), localInteractionChange, content, ctx);
}
}
private Instruction getPrevLocalInstruction(Instruction instruction) {
Instruction prev = instruction.getPrev();
while (prev != null) {
if (prev instanceof LocalVariableInstruction) {
if (((LocalVariableInstruction) prev).getLocalVariableIndex() == ((LocalVariableInstruction) instruction).getLocalVariableIndex()) {
return prev;
}
}
prev = prev.getPrev();
}
return instruction;
}
public void coverField(FeatureExpr ctx, ElementInfo eiFieldOwner, Conditional<?> val, Conditional<?> oldValue, StackFrame frame, ThreadInfo ti, FieldInfo fi) {
if (JPF.COVERAGE != null) {
if (JPF.SELECTED_COVERAGE_TYPE == JPF.COVERAGE_TYPE.interaction) {
if (val.size() - oldValue.size() != 0) {
StringBuilder text = new StringBuilder();
text.append(fi.getName());
text.append(" (");
text.append(Conditional.getCTXString(ctx));
text.append(")\n");
text.append(frame.trace(ctx));
text.append("\n");
text.append(oldValue.size());
text.append(": ");
if (oldValue.toString().length() > 800) {
text.append(oldValue.toString().substring(0, 800) + "...");
} else {
text.append(oldValue);
}
text.append("\n->\n");
text.append(val.size());
text.append(": ");
if (val.toString().length() > 800) {
text.append(val.toString().substring(0, 800) + "...");
} else {
text.append(val);
}
CoverageLogger.logInteraction(frame, val.size() - oldValue.size(), text, ctx);
}
}
}
}
}