org.evosuite.instrumentation.coverage.BranchInstrumentation Maven / Gradle / Ivy
The newest version!
/**
* Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see .
*/
/**
*
*/
package org.evosuite.instrumentation.coverage;
import java.util.Iterator;
import java.util.List;
import org.evosuite.coverage.branch.Branch;
import org.evosuite.coverage.branch.BranchPool;
import org.evosuite.graphs.GraphPool;
import org.evosuite.graphs.cfg.BytecodeInstruction;
import org.evosuite.graphs.cfg.RawControlFlowGraph;
import org.evosuite.runtime.instrumentation.AnnotatedLabel;
import org.evosuite.testcase.execution.ExecutionTrace;
import org.evosuite.testcase.execution.ExecutionTracer;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* BranchInstrumentation class.
*
*
* @author Copied from CFGMethodAdapter
*/
public class BranchInstrumentation implements MethodInstrumentation {
/** Constant logger
*/
protected static final Logger logger = LoggerFactory.getLogger(BranchInstrumentation.class);
private static final String EXECUTION_TRACER = Type.getInternalName(ExecutionTracer.class);
private ClassLoader classLoader;
/*
* (non-Javadoc)
*
* @see
* org.evosuite.cfg.MethodInstrumentation#analyze(org.objectweb
* .asm.tree.MethodNode, org.jgrapht.Graph, java.lang.String,
* java.lang.String, int)
*/
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
public void analyze(ClassLoader classLoader, MethodNode mn, String className,
String methodName, int access) {
this.classLoader = classLoader;
RawControlFlowGraph graph = GraphPool.getInstance(classLoader).getRawCFG(className,
methodName);
Iterator j = mn.instructions.iterator();
while (j.hasNext()) {
AbstractInsnNode in = j.next();
for (BytecodeInstruction v : graph.vertexSet()) {
// If this is in the CFG and it's a branch...
if (in.equals(v.getASMNode())) {
if (v.isBranch()) {
if (in.getPrevious() instanceof LabelNode) {
LabelNode label = (LabelNode) in.getPrevious();
if (label.getLabel() instanceof AnnotatedLabel) {
AnnotatedLabel aLabel = (AnnotatedLabel) label.getLabel();
if(aLabel.isStartTag()) {
if(!aLabel.shouldIgnore()) {
logger.debug("Found artificial branch: "+v);
Branch b = BranchPool.getInstance(classLoader).getBranchForInstruction(v);
b.setInstrumented(true);
} else {
continue;
}
}
}
}
mn.instructions.insertBefore(v.getASMNode(), getInstrumentation(v));
} else if (v.isSwitch()) {
mn.instructions.insertBefore(v.getASMNode(),
getSwitchInstrumentation(v, mn,
className,
methodName));
}
}
}
}
mn.maxStack += 4;
}
/**
*
* getInstrumentation
*
*
* @param instruction
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @return a {@link org.objectweb.asm.tree.InsnList} object.
*/
protected InsnList getInstrumentation(BytecodeInstruction instruction) {
if (instruction == null)
throw new IllegalArgumentException("null given");
if (!instruction.isActualBranch())
throw new IllegalArgumentException("branch instruction expected");
if (!BranchPool.getInstance(classLoader).isKnownAsNormalBranchInstruction(instruction))
throw new IllegalArgumentException(
"expect given instruction to be known by the BranchPool as a normal branch instruction");
int opcode = instruction.getASMNode().getOpcode();
int instructionId = instruction.getInstructionId();
int branchId = BranchPool.getInstance(classLoader).getActualBranchIdForNormalBranchInstruction(instruction);
if (branchId < 0)
throw new IllegalStateException(
"expect BranchPool to know branchId for all branch instructions");
InsnList instrumentation = new InsnList();
switch (opcode) {
case Opcodes.IFEQ:
case Opcodes.IFNE:
case Opcodes.IFLT:
case Opcodes.IFGE:
case Opcodes.IFGT:
case Opcodes.IFLE:
instrumentation.add(new InsnNode(Opcodes.DUP));
instrumentation.add(new LdcInsnNode(opcode));
// instrumentation.add(new LdcInsnNode(id));
instrumentation.add(new LdcInsnNode(branchId));
instrumentation.add(new LdcInsnNode(instructionId));
instrumentation.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
EXECUTION_TRACER, "passedBranch", "(IIII)V", false));
logger.debug("Adding passedBranch val=?, opcode=" + opcode + ", branch="
+ branchId + ", bytecode_id=" + instructionId);
break;
case Opcodes.IF_ICMPEQ:
case Opcodes.IF_ICMPNE:
case Opcodes.IF_ICMPLT:
case Opcodes.IF_ICMPGE:
case Opcodes.IF_ICMPGT:
case Opcodes.IF_ICMPLE:
instrumentation.add(new InsnNode(Opcodes.DUP2));
instrumentation.add(new LdcInsnNode(opcode));
// instrumentation.add(new LdcInsnNode(id));
instrumentation.add(new LdcInsnNode(branchId));
instrumentation.add(new LdcInsnNode(instructionId));
instrumentation.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
EXECUTION_TRACER, "passedBranch", "(IIIII)V", false));
break;
case Opcodes.IF_ACMPEQ:
case Opcodes.IF_ACMPNE:
instrumentation.add(new InsnNode(Opcodes.DUP2));
instrumentation.add(new LdcInsnNode(opcode));
// instrumentation.add(new LdcInsnNode(id));
instrumentation.add(new LdcInsnNode(branchId));
instrumentation.add(new LdcInsnNode(instructionId));
instrumentation.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
EXECUTION_TRACER, "passedBranch",
"(Ljava/lang/Object;Ljava/lang/Object;III)V", false));
break;
case Opcodes.IFNULL:
case Opcodes.IFNONNULL:
instrumentation.add(new InsnNode(Opcodes.DUP));
instrumentation.add(new LdcInsnNode(opcode));
// instrumentation.add(new LdcInsnNode(id));
instrumentation.add(new LdcInsnNode(branchId));
instrumentation.add(new LdcInsnNode(instructionId));
instrumentation.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
EXECUTION_TRACER, "passedBranch",
"(Ljava/lang/Object;III)V", false));
break;
}
return instrumentation;
}
/**
* Creates the instrumentation for switch statements as follows:
*
* For each case : in the switch, two calls to the ExecutionTracer are
* added to the instrumentation, indicating whether the case is hit directly
* or not. This is done by addInstrumentationForSwitchCases().
*
* Additionally in order to trace the execution of the default: case of the
* switch, the following instrumentation is added using
* addDefaultCaseInstrumentation():
*
* A new switch, holding the same s as the original switch we want to
* cover. All cases point to a label after which a call to the
* ExecutionTracer is added, indicating that the default case was not hit
* directly. Symmetrically the new switch has a default case: holding a call
* to the ExecutionTracer to indicate that the default will be hit directly.
*
* @param v
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @param mn
* a {@link org.objectweb.asm.tree.MethodNode} object.
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @return a {@link org.objectweb.asm.tree.InsnList} object.
*/
protected InsnList getSwitchInstrumentation(BytecodeInstruction v, MethodNode mn,
String className, String methodName) {
InsnList instrumentation = new InsnList();
if (!v.isSwitch())
throw new IllegalArgumentException("switch instruction expected");
addInstrumentationForDefaultSwitchCase(v, instrumentation);
addInstrumentationForSwitchCases(v, instrumentation, className, methodName);
return instrumentation;
}
/**
* For each actual case : of a switch this method adds instrumentation
* for the Branch corresponding to that case to the given instruction list.
*
* @param v
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @param instrumentation
* a {@link org.objectweb.asm.tree.InsnList} object.
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
*/
protected void addInstrumentationForSwitchCases(BytecodeInstruction v,
InsnList instrumentation, String className, String methodName) {
if (!v.isSwitch())
throw new IllegalArgumentException("switch instruction expected");
List caseBranches = BranchPool.getInstance(classLoader).getCaseBranchesForSwitch(v);
if (caseBranches == null || caseBranches.isEmpty())
throw new IllegalStateException(
"expect BranchPool to know at least one Branch for each switch instruction");
for (Branch targetCaseBranch : caseBranches) {
if (targetCaseBranch.isDefaultCase())
continue; // handled elsewhere
Integer targetCaseValue = targetCaseBranch.getTargetCaseValue();
Integer targetCaseBranchId = targetCaseBranch.getActualBranchId();
instrumentation.add(new InsnNode(Opcodes.DUP));
instrumentation.add(new LdcInsnNode(targetCaseValue));
instrumentation.add(new LdcInsnNode(Opcodes.IF_ICMPEQ));
instrumentation.add(new LdcInsnNode(targetCaseBranchId));
instrumentation.add(new LdcInsnNode(v.getInstructionId()));
instrumentation.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
EXECUTION_TRACER, "passedBranch", "(IIIII)V", false));
}
}
/**
*
* addInstrumentationForDefaultSwitchCase
*
*
* @param v
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @param instrumentation
* a {@link org.objectweb.asm.tree.InsnList} object.
*/
protected void addInstrumentationForDefaultSwitchCase(BytecodeInstruction v,
InsnList instrumentation) {
if (v.isTableSwitch())
addInstrumentationForDefaultTableswitchCase(v, instrumentation);
if (v.isLookupSwitch())
addInstrumentationForDefaultLookupswitchCase(v, instrumentation);
}
/**
*
* addInstrumentationForDefaultTableswitchCase
*
*
* @param v
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @param instrumentation
* a {@link org.objectweb.asm.tree.InsnList} object.
*/
protected void addInstrumentationForDefaultTableswitchCase(BytecodeInstruction v,
InsnList instrumentation) {
if (!v.isTableSwitch())
throw new IllegalArgumentException("tableswitch instruction expected");
// setup instructions
TableSwitchInsnNode toInstrument = (TableSwitchInsnNode) v.getASMNode();
LabelNode caseLabel = new LabelNode();
LabelNode defaultLabel = new LabelNode();
LabelNode endLabel = new LabelNode();
int keySize = (toInstrument.max - toInstrument.min) + 1;
LabelNode[] caseLabels = new LabelNode[keySize];
for (int i = 0; i < keySize; i++)
caseLabels[i] = caseLabel;
TableSwitchInsnNode mySwitch = new TableSwitchInsnNode(toInstrument.min,
toInstrument.max, defaultLabel, caseLabels);
// add instrumentation
addDefaultCaseInstrumentation(v, instrumentation, mySwitch, defaultLabel,
caseLabel, endLabel);
}
/**
*
* addInstrumentationForDefaultLookupswitchCase
*
*
* @param v
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @param instrumentation
* a {@link org.objectweb.asm.tree.InsnList} object.
*/
protected void addInstrumentationForDefaultLookupswitchCase(BytecodeInstruction v,
InsnList instrumentation) {
if (!v.isLookupSwitch())
throw new IllegalArgumentException("lookup switch expected");
// setup instructions
LookupSwitchInsnNode toInstrument = (LookupSwitchInsnNode) v.getASMNode();
LabelNode caseLabel = new LabelNode();
LabelNode defaultLabel = new LabelNode();
LabelNode endLabel = new LabelNode();
int keySize = toInstrument.keys.size();
int[] keys = new int[keySize];
LabelNode[] labels = new LabelNode[keySize];
for (int i = 0; i < keySize; i++) {
keys[i] = (Integer) toInstrument.keys.get(i);
labels[i] = caseLabel;
}
LookupSwitchInsnNode myLookup = new LookupSwitchInsnNode(defaultLabel, keys,
labels);
addDefaultCaseInstrumentation(v, instrumentation, myLookup, defaultLabel,
caseLabel, endLabel);
}
/**
*
* addDefaultCaseInstrumentation
*
*
* @param v
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @param instrumentation
* a {@link org.objectweb.asm.tree.InsnList} object.
* @param mySwitch
* a {@link org.objectweb.asm.tree.AbstractInsnNode} object.
* @param defaultLabel
* a {@link org.objectweb.asm.tree.LabelNode} object.
* @param caseLabel
* a {@link org.objectweb.asm.tree.LabelNode} object.
* @param endLabel
* a {@link org.objectweb.asm.tree.LabelNode} object.
*/
protected void addDefaultCaseInstrumentation(BytecodeInstruction v,
InsnList instrumentation, AbstractInsnNode mySwitch, LabelNode defaultLabel,
LabelNode caseLabel, LabelNode endLabel) {
int defaultCaseBranchId = BranchPool.getInstance(classLoader).getDefaultBranchForSwitch(v).getActualBranchId();
// add helper switch
instrumentation.add(new InsnNode(Opcodes.DUP));
instrumentation.add(mySwitch);
// add call for default case not covered
instrumentation.add(caseLabel);
addDefaultCaseNotCoveredCall(v, instrumentation, defaultCaseBranchId);
// jump over default (break)
instrumentation.add(new JumpInsnNode(Opcodes.GOTO, endLabel));
// add call for default case covered
instrumentation.add(defaultLabel);
addDefaultCaseCoveredCall(v, instrumentation, defaultCaseBranchId);
instrumentation.add(endLabel);
}
/**
*
* addDefaultCaseCoveredCall
*
*
* @param v
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @param instrumentation
* a {@link org.objectweb.asm.tree.InsnList} object.
* @param defaultCaseBranchId
* a int.
*/
protected void addDefaultCaseCoveredCall(BytecodeInstruction v,
InsnList instrumentation, int defaultCaseBranchId) {
instrumentation.add(new LdcInsnNode(0));
instrumentation.add(new LdcInsnNode(Opcodes.IFEQ));
instrumentation.add(new LdcInsnNode(defaultCaseBranchId));
instrumentation.add(new LdcInsnNode(v.getInstructionId()));
instrumentation.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
EXECUTION_TRACER, "passedBranch", "(IIII)V", false));
}
/**
*
* addDefaultCaseNotCoveredCall
*
*
* @param v
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @param instrumentation
* a {@link org.objectweb.asm.tree.InsnList} object.
* @param defaultCaseBranchId
* a int.
*/
protected void addDefaultCaseNotCoveredCall(BytecodeInstruction v,
InsnList instrumentation, int defaultCaseBranchId) {
instrumentation.add(new LdcInsnNode(0));
instrumentation.add(new LdcInsnNode(Opcodes.IFNE));
instrumentation.add(new LdcInsnNode(defaultCaseBranchId));
instrumentation.add(new LdcInsnNode(v.getInstructionId()));
instrumentation.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
EXECUTION_TRACER, "passedBranch", "(IIII)V", false));
}
/*
* (non-Javadoc)
*
* @see
* org.evosuite.cfg.MethodInstrumentation#executeOnExcludedMethods
* ()
*/
/** {@inheritDoc} */
@Override
public boolean executeOnExcludedMethods() {
return false;
}
/*
* (non-Javadoc)
*
* @see
* org.evosuite.cfg.MethodInstrumentation#executeOnMainMethod()
*/
/** {@inheritDoc} */
@Override
public boolean executeOnMainMethod() {
return false;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy