org.evosuite.testcase.execution.ExecutionTracer 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.testcase.execution;
import java.util.Map;
import org.evosuite.coverage.dataflow.DefUsePool;
import org.evosuite.coverage.dataflow.Definition;
import org.evosuite.coverage.dataflow.Use;
import org.evosuite.instrumentation.testability.BooleanHelper;
import org.evosuite.seeding.ConstantPoolManager;
import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class collects information about chosen branches/paths at runtime
*
* @author Gordon Fraser
*/
public class ExecutionTracer {
private static final Logger logger = LoggerFactory.getLogger(ExecutionTracer.class);
private static ExecutionTracer instance = null;
/**
* We need to disable the execution tracer sometimes, e.g. when calling
* equals in the branch distance function
*/
private boolean disabled = true;
/** Flag that is used to kill threads that are stuck in endless loops */
private boolean killSwitch = false;
private int num_statements = 0;
private ExecutionTrace trace;
private static boolean checkCallerThread = true;
/**
* If a thread of a test case survives for some reason (e.g. long call to
* external library), then we don't want its data in the current trace
*/
private static volatile Thread currentThread = null;
/**
*
* setThread
*
*
* @param thread
* a {@link java.lang.Thread} object.
*/
public static void setThread(Thread thread) {
currentThread = thread;
}
/**
*
* disable
*
*/
public static void disable() {
ExecutionTracer tracer = ExecutionTracer.getExecutionTracer();
tracer.disabled = true;
}
/**
*
* enable
*
*/
public static void enable() {
ExecutionTracer tracer = ExecutionTracer.getExecutionTracer();
tracer.disabled = false;
}
/**
*
* isEnabled
*
*
* @return a boolean.
*/
public static boolean isEnabled() {
ExecutionTracer tracer = ExecutionTracer.getExecutionTracer();
return !tracer.disabled;
}
/**
*
* Setter for the field killSwitch
.
*
*
* @param value
* a boolean.
*/
public static void setKillSwitch(boolean value) {
ExecutionTracer tracer = ExecutionTracer.getExecutionTracer();
tracer.killSwitch = value;
}
/**
*
* Setter for the field checkCallerThread
.
*
*
* @param checkCallerThread
* a boolean.
*/
public static void setCheckCallerThread(boolean checkCallerThread) {
ExecutionTracer.checkCallerThread = checkCallerThread;
}
/**
*
* enable context instrumentation
*
*/
public static void enableContext(){
logger.info("enable context and trace instrumentation");
ExecutionTraceImpl.enableContext();
}
/**
*
* disable context instrumentation
*
*/
public static void disableContext(){
logger.info("disable context and trace instrumentation");
ExecutionTraceImpl.disableContext();
}
/**
*
* disableTraceCalls
*
*/
public static void disableTraceCalls() {
ExecutionTraceImpl.disableTraceCalls();
}
/**
*
* enableTraceCalls
*
*/
public static void enableTraceCalls() {
ExecutionTraceImpl.enableTraceCalls();
}
public static boolean isTraceCallsEnabled() {
return ExecutionTraceImpl.isTraceCallsEnabled();
}
/**
*
* getExecutionTracer
*
*
* @return a {@link org.evosuite.testcase.execution.ExecutionTracer} object.
*/
public static ExecutionTracer getExecutionTracer() {
if (instance == null) {
instance = new ExecutionTracer();
}
return instance;
}
/**
* Reset for new execution
*/
public void clear() {
trace = new ExecutionTraceProxy();
BooleanHelper.clearStack();
num_statements = 0;
}
/**
* Obviously more than one thread is executing during the creation of
* concurrent TestCases. #TODO steenbuck we should test if
* Thread.currentThread() is in the set of currently executing threads
*
* @return
*/
public static boolean isThreadNeqCurrentThread() {
if (!checkCallerThread) {
return false;
}
if (currentThread == null) {
logger.error("CurrentThread has not been set!");
Map map = Thread.getAllStackTraces();
for (Thread t : map.keySet()) {
String msg = "Thread: " + t+"\n";
for (StackTraceElement e : map.get(t)) {
msg += " -> " + e + "\n";
}
logger.error(msg);
}
currentThread = Thread.currentThread();
}
return Thread.currentThread() != currentThread;
}
/**
* Return trace of current execution
*
* @return a {@link org.evosuite.testcase.execution.ExecutionTrace} object.
*/
public ExecutionTrace getTrace() {
trace.finishCalls();
return trace;
// ExecutionTrace copy = trace.clone();
// // copy.finishCalls();
// return copy;
}
/**
* Return the last explicitly thrown exception
*
* @return a {@link java.lang.Throwable} object.
*/
public Throwable getLastException() {
return trace.getExplicitException();
}
/**
* Called by instrumented code whenever a new method is called
*
* @param classname
* a {@link java.lang.String} object.
* @param methodname
* a {@link java.lang.String} object.
* @param caller
* a {@link java.lang.Object} object.
* @throws org.evosuite.testcase.execution.TestCaseExecutor$TimeoutExceeded
* if any.
*/
public static void enteredMethod(String classname, String methodname, Object caller)
throws TestCaseExecutor.TimeoutExceeded {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
//logger.trace("Entering method " + classname + "." + methodname);
tracer.trace.enteredMethod(classname, methodname, caller);
}
/**
* Called by instrumented code whenever a return values is produced
*
* @param value
* a int.
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
*/
public static void returnValue(int value, String className, String methodName) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
//logger.trace("Return value: " + value);
tracer.trace.returnValue(className, methodName, value);
}
/**
* Called by instrumented code whenever a return values is produced
*
* @param value
* a {@link java.lang.Object} object.
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
*/
public static void returnValue(Object value, String className, String methodName) {
if (isThreadNeqCurrentThread())
return;
if (!ExecutionTracer.isEnabled())
return;
if (value == null) {
returnValue(0, className, methodName);
return;
}
StringBuilder tmp = null;
try {
// setLineCoverageDeactivated(true);
// logger.warn("Disabling tracer: returnValue");
ExecutionTracer.disable();
tmp = new StringBuilder(value.toString());
} catch (Throwable t) {
return;
} finally {
ExecutionTracer.enable();
}
int index = 0;
int position = 0;
boolean found = false;
boolean deleteAddresses = true;
char c = ' ';
// quite fast method to detect memory addresses in Strings.
while ((position = tmp.indexOf("@", index)) > 0) {
for (index = position + 1; index < position + 17 && index < tmp.length(); index++) {
c = tmp.charAt(index);
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
|| (c >= 'A' && c <= 'F')) {
found = true;
} else {
break;
}
}
if (deleteAddresses && found) {
tmp.delete(position + 1, index);
}
}
returnValue(tmp.toString().hashCode(), className, methodName);
}
/**
* Called by instrumented code whenever a method is left
*
* @param classname
* a {@link java.lang.String} object.
* @param methodname
* a {@link java.lang.String} object.
*/
public static void leftMethod(String classname, String methodname) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
tracer.trace.exitMethod(classname, methodname);
// logger.trace("Left method " + classname + "." + methodname);
}
/**
* Called by the instrumented code each time a new source line is executed
*/
public static void checkTimeout() {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (tracer.killSwitch) {
// logger.info("Raising TimeoutException as kill switch is active - passedLine");
if(!isInStaticInit())
throw new TestCaseExecutor.TimeoutExceeded();
}
}
private static boolean isInStaticInit() {
for(StackTraceElement elem : Thread.currentThread().getStackTrace()) {
if(elem.getMethodName().equals(""))
return true;
}
return false;
}
/**
* Called by the instrumented code each time a new source line is executed
*
* @param line
* a int.
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
*/
public static void passedLine(String className, String methodName, int line) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
tracer.trace.linePassed(className, methodName, line);
}
/**
* Called by the instrumented code each time an unconditional branch is
* taken. This is not enabled by default, only some coverage criteria (e.g.,
* LCSAJ) use it.
*
* @param opcode
* a int.
* @param branch
* a int.
* @param bytecode_id
* a int.
*/
public static void passedUnconditionalBranch(int opcode, int branch, int bytecode_id) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
// Add current branch to control trace
tracer.trace.branchPassed(branch, bytecode_id, 0.0, 0.0);
}
/**
* Called by the instrumented code each time a new branch is taken
*
* @param val
* a int.
* @param opcode
* a int.
* @param branch
* a int.
* @param bytecode_id
* a int.
*/
public static void passedBranch(int val, int opcode, int branch, int bytecode_id) {
ExecutionTracer tracer = getExecutionTracer();
// logger.info("passedBranch val="+val+", opcode="+opcode+", branch="+branch+", bytecode_id="+bytecode_id);
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
ConstantPoolManager.getInstance().addDynamicConstant(val);
// logger.trace("Called passedBranch1 with opcode "+AbstractVisitor.OPCODES[opcode]+" and val "+val+" in branch "+branch);
double distance_true = 0.0;
double distance_false = 0.0;
switch (opcode) {
case Opcodes.IFEQ:
distance_true = Math.abs((double) val); // The greater abs is, the
// further away from 0
distance_false = distance_true == 0 ? 1.0 : 0.0; // Anything but 0
// is good
break;
case Opcodes.IFNE:
distance_false = Math.abs((double) val); // The greater abs is, the
// further away from 0
distance_true = distance_false == 0 ? 1.0 : 0.0; // Anything but 0
// leads to NE
break;
case Opcodes.IFLT:
distance_true = val >= 0 ? val + 1.0 : 0.0; // The greater, the
// further away from < 0
distance_false = val < 0 ? 0.0 - val + 1.0 : 0.0; // The smaller,
// the further
// away from < 0
break;
case Opcodes.IFGT:
distance_true = val <= 0 ? 0.0 - val + 1.0 : 0.0;
distance_false = val > 0 ? val + 1.0 : 0.0;
break;
case Opcodes.IFGE:
distance_true = val < 0 ? 0.0 - val + 1.0 : 0.0;
distance_false = val >= 0 ? val + 1.0 : 0.0;
break;
case Opcodes.IFLE:
distance_true = val > 0 ? val + 1.0 : 0.0; // The greater, the
// further away from < 0
distance_false = val <= 0 ? 0.0 - val + 1.0 : 0.0; // The smaller,
// the further
// away from < 0
break;
default:
logger.error("Unknown opcode: " + opcode);
}
// logger.trace("1 Branch distance true : " + distance_true);
// logger.trace("1 Branch distance false: " + distance_false);
// Add current branch to control trace
tracer.trace.branchPassed(branch, bytecode_id, distance_true, distance_false);
}
public static void passedPutStatic(String classNameWithDots, String fieldName) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
tracer.trace.putStaticPassed(classNameWithDots, fieldName);
}
/**
* This method is added in the transformed bytecode
*
* @param className
*/
public static void exitClassInit(String className) {
final String classNameWithDots = className.replace('/', '.');
ExecutionTracer tracer = getExecutionTracer();
// if (tracer.disabled)
// return;
//
// if (isThreadNeqCurrentThread())
// return;
//
// checkTimeout();
tracer.trace.classInitialized(classNameWithDots);
}
/**
*
* @param classNameWithDots
* @param fieldName
*/
public static void passedGetStatic(String classNameWithDots, String fieldName) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
tracer.trace.getStaticPassed(classNameWithDots, fieldName);
}
/**
* Called by the instrumented code each time a new branch is taken
*
* @param val1
* a int.
* @param val2
* a int.
* @param opcode
* a int.
* @param branch
* a int.
* @param bytecode_id
* a int.
*/
public static void passedBranch(int val1, int val2, int opcode, int branch,
int bytecode_id) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
ConstantPoolManager.getInstance().addDynamicConstant(val1);
ConstantPoolManager.getInstance().addDynamicConstant(val2);
/* logger.trace("Called passedBranch2 with opcode "
+ AbstractVisitor.OPCODES[opcode] + ", val1=" + val1 + ", val2=" + val2
+ " in branch " + branch); */
double distance_true = 0;
double distance_false = 0;
switch (opcode) {
// Problem is that the JVM is a stack machine
// x < 5 gets compiled to a val2 > val1,
// because operators are on the stack in reverse order
case Opcodes.IF_ICMPEQ:
// The greater the difference, the further away
distance_true = Math.abs((double) val1 - (double) val2);
// Anything but 0 is good
distance_false = distance_true == 0 ? 1.0 : 0.0;
break;
case Opcodes.IF_ICMPNE:
// The greater abs is, the further away from 0
distance_false = Math.abs((double) val1 - (double) val2);
// Anything but 0 leads to NE
distance_true = distance_false == 0 ? 1.0 : 0.0;
break;
case Opcodes.IF_ICMPLT:
// val1 >= val2?
distance_true = val1 >= val2 ? (double) val1 - (double) val2 + 1.0 : 0.0;
distance_false = val1 < val2 ? (double) val2 - (double) val1 + 1.0 : 0.0;
break;
case Opcodes.IF_ICMPGE:
// val1 < val2?
distance_true = val1 < val2 ? (double) val2 - (double) val1 + 1.0 : 0.0;
distance_false = val1 >= val2 ? (double) val1 - (double) val2 + 1.0 : 0.0;
break;
case Opcodes.IF_ICMPGT:
// val1 <= val2?
distance_true = val1 <= val2 ? (double) val2 - (double) val1 + 1.0 : 0.0;
distance_false = val1 > val2 ? (double) val1 - (double) val2 + 1.0 : 0.0;
break;
case Opcodes.IF_ICMPLE:
// val1 > val2?
distance_true = val1 > val2 ? (double) val1 - (double) val2 + 1.0 : 0.0;
distance_false = val1 <= val2 ? (double) val2 - (double) val1 + 1.0 : 0.0;
break;
default:
logger.error("Unknown opcode: " + opcode);
}
// logger.trace("2 Branch distance true: " + distance_true);
// logger.trace("2 Branch distance false: " + distance_false);
// Add current branch to control trace
tracer.trace.branchPassed(branch, bytecode_id, distance_true, distance_false);
// tracer.trace.branchPassed(branch, distance_true, distance_false);
}
/**
* Called by the instrumented code each time a new branch is taken
*
* @param val1
* a {@link java.lang.Object} object.
* @param val2
* a {@link java.lang.Object} object.
* @param opcode
* a int.
* @param branch
* a int.
* @param bytecode_id
* a int.
*/
public static void passedBranch(Object val1, Object val2, int opcode, int branch,
int bytecode_id) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
// logger.trace("Called passedBranch3 with opcode "
// + AbstractVisitor.OPCODES[opcode]); // +", val1="+val1+", val2="+val2+" in branch "+branch);
double distance_true = 0;
double distance_false = 0;
// logger.warn("Disabling tracer: passedBranch with 2 Objects");
switch (opcode) {
case Opcodes.IF_ACMPEQ:
if (val1 == null) {
distance_true = val2 == null ? 0.0 : 1.0;
} else {
disable();
try {
distance_true = val1.equals(val2) ? 0.0 : 1.0;
} catch (Throwable t) {
logger.debug("Equality raised exception: " + t);
distance_true = 1.0;
} finally {
enable();
}
}
break;
case Opcodes.IF_ACMPNE:
if (val1 == null) {
distance_true = val2 == null ? 1.0 : 0.0;
} else {
disable();
try {
distance_true = val1.equals(val2) ? 1.0 : 0.0;
} catch (Exception e) {
logger.debug("Caught exception during comparison: " + e);
distance_true = 1.0;
} finally {
enable();
}
}
break;
}
distance_false = distance_true == 0 ? 1.0 : 0.0;
// Add current branch to control trace
tracer.trace.branchPassed(branch, bytecode_id, distance_true, distance_false);
}
/**
* Called by the instrumented code each time a new branch is taken
*
* @param val
* a {@link java.lang.Object} object.
* @param opcode
* a int.
* @param branch
* a int.
* @param bytecode_id
* a int.
*/
public static void passedBranch(Object val, int opcode, int branch, int bytecode_id) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
double distance_true = 0;
double distance_false = 0;
switch (opcode) {
case Opcodes.IFNULL:
distance_true = val == null ? 0.0 : 1.0;
break;
case Opcodes.IFNONNULL:
distance_true = val == null ? 1.0 : 0.0;
break;
default:
logger.error("Warning: encountered opcode " + opcode);
}
distance_false = distance_true == 0 ? 1.0 : 0.0;
// enable();
// logger.trace("Branch distance true: " + distance_true);
// logger.trace("Branch distance false: " + distance_false);
// Add current branch to control trace
tracer.trace.branchPassed(branch, bytecode_id, distance_true, distance_false);
}
/**
* Called by instrumented code each time a variable gets written to (a
* Definition)
*
* @param caller
* a {@link java.lang.Object} object.
* @param defID
* a int.
*/
public static void passedDefinition(Object object, Object caller, int defID) {
if (isThreadNeqCurrentThread())
return;
ExecutionTracer tracer = getExecutionTracer();
if (!tracer.disabled)
tracer.trace.definitionPassed(object, caller, defID);
}
/**
* Called by instrumented code each time a variable is read from (a Use)
*
* @param caller
* a {@link java.lang.Object} object.
* @param useID
* a int.
*/
public static void passedUse(Object object, Object caller, int useID) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
tracer.trace.usePassed(object, caller, useID);
}
/**
* Called by instrumented code each time a field method call is passed
*
* Since it was not clear whether the field method call constitutes a
* definition or a use when the instrumentation was initially added this
* method will redirect the call accordingly
*
* @param caller
* @param defuseId
*/
public static void passedFieldMethodCall(Object callee, Object caller, int defuseId) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
if (DefUsePool.isKnownAsDefinition(defuseId)) {
Definition passedDef = DefUsePool.getDefinitionByDefUseId(defuseId);
passedDefinition(callee, caller, passedDef.getDefId());
} else if (DefUsePool.isKnownAsUse(defuseId)) {
Use passedUse = DefUsePool.getUseByDefUseId(defuseId);
passedUse(callee, caller, passedUse.getUseId());
} else
throw new EvosuiteError(
"instrumentation called passedFieldMethodCall with invalid defuseId: "
+ defuseId + ", known IDs: " + DefUsePool.getDefUseCounter());
}
/**
*
* passedMutation
*
*
* @param distance
* a double.
* @param mutationId
* a int.
*/
public static void passedMutation(double distance, int mutationId) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
tracer.trace.mutationPassed(mutationId, distance);
}
/**
*
* exceptionThrown
*
*
* @param exception
* a {@link java.lang.Object} object.
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
*/
public static void exceptionThrown(Object exception, String className,
String methodName) {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
tracer.trace.setExplicitException((Throwable) exception);
}
/**
*
* statementExecuted
*
*/
public static void statementExecuted() {
ExecutionTracer tracer = getExecutionTracer();
if (tracer.disabled)
return;
if (isThreadNeqCurrentThread())
return;
checkTimeout();
tracer.num_statements++;
}
/**
*
* getNumStatementsExecuted
*
*
* @return a int.
*/
public int getNumStatementsExecuted() {
return num_statements;
}
private ExecutionTracer() {
trace = new ExecutionTraceProxy();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy