/** * Copyright (C) 2010-2017 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 <http://www.gnu.org/licenses/>. */ package org.evosuite.coverage.dataflow; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.evosuite.Properties; import org.evosuite.TestGenerationContext; import org.evosuite.coverage.dataflow.DefUseCoverageTestFitness.DefUsePairType; import org.evosuite.coverage.dataflow.analysis.AllUsesAnalysis; import org.evosuite.graphs.GraphPool; import org.evosuite.graphs.ccfg.ClassControlFlowGraph; import org.evosuite.graphs.cfg.BytecodeInstruction; import org.evosuite.rmi.ClientServices; import org.evosuite.statistics.RuntimeVariable; import org.evosuite.testcase.execution.ExecutionResult; import org.evosuite.testsuite.AbstractFitnessFactory; import org.evosuite.utils.LoggingUtils; import org.evosuite.utils.JdkPureMethodsList; import org.objectweb.asm.Type; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p> * DefUseCoverageFactory class. * </p> * * @author Andre Mis */ public class DefUseCoverageFactory extends AbstractFitnessFactory<DefUseCoverageTestFitness> { private static final Logger logger = LoggerFactory.getLogger(DefUseCoverageFactory.class); // TestSuiteMinimizer seems to call getCoverageGoals() a second time // and since analysis takes a little ... private static boolean called = false; private static List<DefUseCoverageTestFitness> duGoals; // TODO: What's the difference to goals? private static List<DefUseCoverageTestFitness> goals; // map of all NON-parameter-goals private static Map<Definition, Map<Use, DefUseCoverageTestFitness>> goalMap = new HashMap<Definition, Map<Use, DefUseCoverageTestFitness>>(); private static Map<DefUseCoverageTestFitness.DefUsePairType, Integer> goalCounts = new HashMap<DefUseCoverageTestFitness.DefUsePairType, Integer>(); /** * <p> * getDUGoals * </p> * * @return a {@link java.util.List} object. */ public static List<DefUseCoverageTestFitness> getDUGoals() { if (!called) computeGoals(); return duGoals; } /* * (non-Javadoc) * * @see * org.evosuite.coverage.TestFitnessFactory#getCoverageGoals() */ /** {@inheritDoc} */ @Override public List<DefUseCoverageTestFitness> getCoverageGoals() { if (!called) computeGoals(); return goals; } /** * Determines all goals that need to get covered in order to fulfill * DefUseCoverage * * Those are the following: - for each parameterUse this method creates a * goal trying to cover i - for each duPair with a definition clear path * inside the methods of the CUT a goal is created - for each definition in * the CUT with a clear path to an exit of its method and each use with a * clear path from its methods entry a goal is created */ public static void computeGoals() { categorizeFieldMethodCalls(); // XXX testing purposes /*for(String methodInCCFG : GraphPool.getInstance(TestGenerationContext.getClassLoader()).getRawCFGs(Properties.TARGET_CLASS).keySet()) { if(GraphPool.getInstance(TestGenerationContext.getClassLoader()).getCCFG(Properties.TARGET_CLASS).isPure(methodInCCFG)) LoggingUtils.getEvoLogger().debug("PURE method:\t"+methodInCCFG); else LoggingUtils.getEvoLogger().debug("IMPURE method:\t"+methodInCCFG); } */ long start = System.currentTimeMillis(); LoggingUtils.getEvoLogger().info("starting DefUse-Coverage goal generation"); duGoals = new ArrayList<DefUseCoverageTestFitness>(); if(!GraphPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).canMakeCCFGForClass(Properties.TARGET_CLASS)) { goals = new ArrayList<DefUseCoverageTestFitness>(); logger.info("Have no CFGs, is this an interface?"); return; } LoggingUtils.getEvoLogger().info("* Creating DefUse-Pairs from CCFG..."); duGoals.addAll(getCCFGPairs()); LoggingUtils.getEvoLogger().info(" ..created " + getIntraMethodGoalsCount() + " intra-method-, " + getInterMethodGoalsCount() + " inter-method- and " + getIntraClassGoalsCount() + " intra-class-pairs"); LoggingUtils.getEvoLogger().info(" "+duGoals.toString()); LoggingUtils.getEvoLogger().info("* Creating parameter goals..."); duGoals.addAll(getParameterGoals()); LoggingUtils.getEvoLogger().info(" created " + getParamGoalsCount() + " parameter goals"); called = true; goals = new ArrayList<DefUseCoverageTestFitness>(); goals.addAll(duGoals); long end = System.currentTimeMillis(); goalComputationTime = end - start; LoggingUtils.getEvoLogger().info("* Goal computation took: " + goalComputationTime + "ms"); ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.IntraMethodPairs, getIntraMethodGoalsCount()); ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.InterMethodPairs, getInterMethodGoalsCount()); ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.ParameterPairs, getParamGoalsCount()); ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.IntraClassPairs, getIntraClassGoalsCount()); ClientServices.getInstance().getClientNode().trackOutputVariable(RuntimeVariable.DefUsePairs, goals.size()); } /** * Determines for all method calls on fields of the CUT whether the call is * to a pure or impure method. For these calls Uses and Definitions are * created respectively. * * Since purity analysis is used here and requires all classes along the * call tree to be completely analyzed this part of the CUT analysis can not * be done in the CFGMethodAdapter like the rest of it. * */ private static void categorizeFieldMethodCalls() { Set<BytecodeInstruction> fieldMethodCalls = DefUsePool.retrieveFieldMethodCalls(); LoggingUtils.getEvoLogger().info("Categorizing field method calls: " + fieldMethodCalls.size()); for (BytecodeInstruction fieldMethodCall : fieldMethodCalls) { if (GraphPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).canMakeCCFGForClass(fieldMethodCall.getCalledMethodsClass())) { ClassControlFlowGraph ccfg = GraphPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).getCCFG(fieldMethodCall.getCalledMethodsClass()); if (ccfg.isPure(fieldMethodCall.getCalledMethod())) { if (!DefUsePool.addAsUse(fieldMethodCall)) throw new IllegalStateException( "unable to register field method call as a use " + fieldMethodCall.toString()); } else { if (!DefUsePool.addAsDefinition(fieldMethodCall)) throw new IllegalStateException( "unable to register field method call as a definition " + fieldMethodCall.toString()); } } else { String toAnalyze = fieldMethodCall.getCalledMethodsClass() + "." + fieldMethodCall.getCalledMethodName(); if (toAnalyze != null && toAnalyze.startsWith("java.")) { Type[] parameters = org.objectweb.asm.Type.getArgumentTypes(fieldMethodCall.getMethodCallDescriptor()); String newParams = ""; if (parameters.length != 0) { for (Type i : parameters) { newParams = newParams + "," + i.getClassName(); } newParams = newParams.substring(1, newParams.length()); } toAnalyze = fieldMethodCall.getCalledMethodsClass() + "." + fieldMethodCall.getCalledMethodName() + "(" + newParams + ")"; //System.out.println(toAnalyze); if (JdkPureMethodsList.instance.checkPurity(toAnalyze)) { if (!DefUsePool.addAsUse(fieldMethodCall)) throw new IllegalStateException( "unable to register field method call as a use " + fieldMethodCall.toString()); } else { if (!DefUsePool.addAsDefinition(fieldMethodCall)) throw new IllegalStateException( "unable to register field method call as a definition " + fieldMethodCall.toString()); } } else { if (!DefUsePool.addAsUse(fieldMethodCall)) throw new IllegalStateException( "unable to register field method call as a use " + fieldMethodCall.toString()); } } } } private static Set<DefUseCoverageTestFitness> getCCFGPairs() { ClassControlFlowGraph ccfg = GraphPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).getCCFG(Properties.TARGET_CLASS); AllUsesAnalysis aua = new AllUsesAnalysis(ccfg); Set<DefUseCoverageTestFitness> r = aua.determineDefUsePairs(); return r; } /** * Given a definition and a use, this method creates a DefUseCoverageGoal * for this DefUsePair. * * @param def * The definition of the goal * @param use * The use of the goal * @return The created DefUseCoverageGoal * @param type * a * {@link org.evosuite.coverage.dataflow.DefUseCoverageTestFitness.DefUsePairType} * object. */ public static DefUseCoverageTestFitness createGoal(Definition def, Use use, DefUseCoverageTestFitness.DefUsePairType type) { DefUseCoverageTestFitness goal = new DefUseCoverageTestFitness(def, use, type); if (registerGoal(goal)) return goal; else { // System.out.println("Discarding goal: "+goal.toString()); return null; } } /** * Convenience method that retrieves the Definition and Use object for the * given BytecodeInstructions from the DefUsePool and calls * createGoal(Definition,Use) * * @param def * a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. * @param use * a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. * @param type * a * {@link org.evosuite.coverage.dataflow.DefUseCoverageTestFitness.DefUsePairType} * object. * @return a * {@link org.evosuite.coverage.dataflow.DefUseCoverageTestFitness} * object. */ public static DefUseCoverageTestFitness createGoal(BytecodeInstruction def, BytecodeInstruction use, DefUseCoverageTestFitness.DefUsePairType type) { if (def == null) throw new IllegalArgumentException("null given as def"); if (use == null) throw new IllegalArgumentException("null given as use"); Definition definition = DefUsePool.getDefinitionByInstruction(def); Use usee = DefUsePool.getUseByInstruction(use); if (definition == null || usee == null) // can happen in (very, very) // weird cases, ignoring that // for now return null; return createGoal(definition, usee, type); } private static boolean registerGoal(DefUseCoverageTestFitness goal) { if (!goalMap.containsKey(goal.getGoalDefinition())) goalMap.put(goal.getGoalDefinition(), new HashMap<Use, DefUseCoverageTestFitness>()); if (goalMap.get(goal.getGoalDefinition()).containsKey(goal.getGoalUse()) /* && goal.isInterMethodPair() */) // when intra-goal DUs also have free paths to method start and end // it can be declared both an intra-goal and an inter-goal. in this // case we declare the goal to be intra return false; goalMap.get(goal.getGoalDefinition()).put(goal.getGoalUse(), goal); countGoal(goal); return true; } private static void countGoal(DefUseCoverageTestFitness goal) { if (goalCounts.get(goal.getType()) == null) goalCounts.put(goal.getType(), 0); goalCounts.put(goal.getType(), goalCounts.get(goal.getType()) + 1); // LoggingUtils.getEvoLogger().info(goal.toString()); } /** * <p> * retrieveGoal * </p> * * @param defId * a int. * @param useId * a int. * @return a * {@link org.evosuite.coverage.dataflow.DefUseCoverageTestFitness} * object. */ public static DefUseCoverageTestFitness retrieveGoal(int defId, int useId) { Definition def = DefUsePool.getDefinitionByDefId(defId); Use use = DefUsePool.getUseByUseId(useId); return retrieveGoal(def, use); } /** * <p> * retrieveGoal * </p> * * @param def * a {@link org.evosuite.coverage.dataflow.Definition} object. * @param use * a {@link org.evosuite.coverage.dataflow.Use} object. * @return a * {@link org.evosuite.coverage.dataflow.DefUseCoverageTestFitness} * object. */ public static DefUseCoverageTestFitness retrieveGoal(Definition def, Use use) { if (!goalMap.containsKey(def)) return null; if (!goalMap.get(def).containsKey(use)) return null; return goalMap.get(def).get(use); } /** * For each parameterUse in the CUT this method creates a * DefUseCoverageTestFitness that tries to cover that use * * @return a {@link java.util.Set} object. */ public static Set<DefUseCoverageTestFitness> getParameterGoals() { Set<DefUseCoverageTestFitness> r = new HashSet<DefUseCoverageTestFitness>(); Set<Use> parameterUses = DefUsePool.retrieveRegisteredParameterUses(); for (Use use : parameterUses) { DefUseCoverageTestFitness goal = new DefUseCoverageTestFitness(use); r.add(goal); countGoal(goal); } // paramGoalsCount = r.size(); // LoggingUtils.getEvoLogger().debug("# Parameter-Uses: " + r.size()); return r; } /** * <p> * getRegsiteredDefinitions * </p> * * @return a {@link java.util.Set} object. */ public static Set<Definition> getRegisteredDefinitions() { if (!called) computeGoals(); return new HashSet<Definition>(goalMap.keySet()); } /** * <p> * getRegisteredGoalsForDefinition * </p> * * @param def * a {@link org.evosuite.coverage.dataflow.Definition} object. * @return a {@link java.util.Map} object. */ public static Map<Use, DefUseCoverageTestFitness> getRegisteredGoalsForDefinition( Definition def) { if (!called) computeGoals(); return goalMap.get(def); } // Getter /** * <p> * getParamGoalsCount * </p> * * @return a int. */ public static int getParamGoalsCount() { Integer r = goalCounts.get(DefUsePairType.PARAMETER); if (r == null) return 0; return r; } /** * <p> * getIntraMethodGoalsCount * </p> * * @return a int. */ public static int getIntraMethodGoalsCount() { Integer r = goalCounts.get(DefUsePairType.INTRA_METHOD); if (r == null) return 0; return r; } /** * <p> * getInterMethodGoalsCount * </p> * * @return a int. */ public static int getInterMethodGoalsCount() { Integer r = goalCounts.get(DefUsePairType.INTER_METHOD); if (r == null) return 0; return r; } /** * <p> * getIntraClassGoalsCount * </p> * * @return a int. */ public static int getIntraClassGoalsCount() { Integer r = goalCounts.get(DefUsePairType.INTRA_CLASS); if (r == null) return 0; return r; } public static void clear() { if (called) { called = false; duGoals.clear(); goals.clear(); goalMap.clear(); goalCounts.clear(); } } public static boolean detectAliasingGoals(List<ExecutionResult> results) { if (!Properties.DEFUSE_ALIASES) return false; Set<DefUseCoverageTestFitness> aliasingGoals = new HashSet<DefUseCoverageTestFitness>(); for (ExecutionResult result : results) { aliasingGoals.addAll(detectAliasingGoals(result)); } // Need to add here to avoid concurrent access if (!aliasingGoals.isEmpty()) { goals.addAll(aliasingGoals); duGoals.addAll(aliasingGoals); } return !aliasingGoals.isEmpty(); } private static Set<DefUseCoverageTestFitness> detectAliasingGoals( ExecutionResult result) { Map<String, HashMap<Integer, HashMap<Integer, Object>>> passedDefsObject = result.getTrace().getDefinitionDataObjects(); Map<String, HashMap<Integer, HashMap<Integer, Object>>> passedUsesObject = result.getTrace().getUseDataObjects(); Map<String, HashMap<Integer, HashMap<Integer, Integer>>> passedDefs = result.getTrace().getDefinitionData(); Map<String, HashMap<Integer, HashMap<Integer, Integer>>> passedUses = result.getTrace().getUseData(); Set<DefUseCoverageTestFitness> aliasingGoals = new HashSet<DefUseCoverageTestFitness>(); for (String goalVariable : passedUsesObject.keySet()) { for (Integer objectId : passedUsesObject.get(goalVariable).keySet()) { for (Object o1 : passedUsesObject.get(goalVariable).get(objectId).values()) { for (String otherGoalVariable : passedDefsObject.keySet()) { for (Integer otherObjectId : passedDefsObject.get(otherGoalVariable).keySet()) { for (Object o2 : passedDefsObject.get(otherGoalVariable).get(otherObjectId).values()) { if (o1 != null && o1 == o2 && objectId == otherObjectId && !goalVariable.equals(otherGoalVariable)) { Map<Integer, Integer> currentDefMap = passedDefs.get(otherGoalVariable).get(objectId); Map<Integer, Integer> currentUseMap = passedUses.get(goalVariable).get(objectId); List<Integer> duCounterTrace = new ArrayList<Integer>( currentDefMap.keySet()); duCounterTrace.addAll(currentUseMap.keySet()); // System.out.println(duCounterTrace.size()); oO for ncs.Bessj these can be up to 50k entries big Collections.sort(duCounterTrace); int traceLength = duCounterTrace.size(); Integer[] sortedDefDUTrace = duCounterTrace.toArray(new Integer[traceLength]); int activeDef = -1; for (int i = 0; i < traceLength; i++) { int currentDUCounter = sortedDefDUTrace[i]; if (currentDefMap.containsKey(currentDUCounter)) { activeDef = currentDefMap.get(currentDUCounter); } else if (activeDef != -1) { int currentUse = currentUseMap.get(currentDUCounter); DefUseCoverageTestFitness currentGoal = DefUseCoverageFactory.retrieveGoal(activeDef, currentUse); if (currentGoal == null) { logger.info("New alias found: Variable defined as " + otherGoalVariable + " appeared in use as " + goalVariable); Definition def = DefUsePool.getDefinitionByDefId(activeDef); Use use = DefUsePool.getUseByUseId(currentUse); for (DefUseCoverageTestFitness defUse : duGoals) { // Find all other defs of the variable in the use if (defUse.getGoalUse().equals(use)) { Map<Use, DefUseCoverageTestFitness> defUseMap = getRegisteredGoalsForDefinition(defUse.getGoalDefinition()); for (Use otherUse : defUseMap.keySet()) { // For each defuse pair, add new defuse pair with alternative def DefUseCoverageTestFitness goal = DefUseCoverageFactory.createGoal(def, otherUse, defUseMap.get(otherUse).getType()); if (goal != null) { logger.info("Created new defuse pair: " + goal + " of type " + goal.getType()); aliasingGoals.add(goal); } } } } } } } } } } } } } } return aliasingGoals; } }