diff --git a/nondex-maven-plugin/src/main/java/edu/illinois/nondex/plugin/DebugTask.java b/nondex-maven-plugin/src/main/java/edu/illinois/nondex/plugin/DebugTask.java index a8b44183..f3e226a8 100644 --- a/nondex-maven-plugin/src/main/java/edu/illinois/nondex/plugin/DebugTask.java +++ b/nondex-maven-plugin/src/main/java/edu/illinois/nondex/plugin/DebugTask.java @@ -37,6 +37,8 @@ a copy of this software and associated documentation files (the import edu.illinois.nondex.common.Logger; import edu.illinois.nondex.common.Utils; +import org.apache.commons.lang3.tuple.Pair; + import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.BuildPluginManager; @@ -92,26 +94,43 @@ public String debug() throws MojoExecutionException { } private String tryDebugSeeds() { - Configuration failingOne = this.debugWithConfigurations(this.failingConfigurations); + List debuggedOnes = this.debugWithConfigurations(this.failingConfigurations); - if (failingOne != null) { - return failingOne.toArgLine() + String.format("%n") + "DEBUG RESULTS FOR " + failingOne.testName + " AT: " - + failingOne.getDebugPath(); + if (debuggedOnes.size() > 0) { + return makeResultString(debuggedOnes); } // The seeds that failed with the full test-suite no longer fail // Searching for different seeds + Logger.getGlobal().log(Level.FINE, "TRYING NEW SEEDS"); List retryWOtherSeeds = this.createNewSeedsToRetry(); - failingOne = this.debugWithConfigurations(retryWOtherSeeds); + debuggedOnes = this.debugWithConfigurations(retryWOtherSeeds); - if (failingOne != null) { - return failingOne.toArgLine() + String.format("%n") + "DEBUG RESULTS FOR " + failingOne.testName + " AT: " - + failingOne.getDebugPath(); + if (debuggedOnes.size() > 0) { + return makeResultString(debuggedOnes); } return null; } + private String makeResultString(List debuggedOnes) { + StringBuilder sb = new StringBuilder(); + for (Configuration config : debuggedOnes) { + if (config == null) { + continue; + } + sb.append(config.toArgLine()); + sb.append("\nDEBUG RESULTS FOR "); + sb.append(config.testName); + sb.append(" AND SEED: "); + sb.append(config.seed); + sb.append(" AT: "); + sb.append(config.getDebugPath()); + sb.append('\n'); + } + return sb.toString(); + } + private List createNewSeedsToRetry() { Configuration someFailingConfig = this.failingConfigurations.iterator().next(); int newSeed = someFailingConfig.seed * ConfigurationDefaults.SEED_FACTOR; @@ -128,85 +147,114 @@ private List createNewSeedsToRetry() { return retryWOtherSeeds; } - private Configuration debugWithConfigurations(List failingConfigurations) { - Configuration debConfig = null; + private List debugWithConfigurations(List failingConfigurations) { + List allDebuggedConfigs = new LinkedList(); for (Configuration config : failingConfigurations) { Configuration dryConfig; if ((dryConfig = this.failsOnDry(config)) != null) { - Configuration failingConfig = this.startDebugBinary(dryConfig); - - // If debugged down to single choice point, then go ahead and return that - if (failingConfig != null && failingConfig.numChoices() == 0) { - return failingConfig; - } - // Otherwise should go on until finding better one - if (debConfig == null || failingConfig.hasFewerChoicePoints(debConfig)) { - debConfig = failingConfig; - } + // Get all debugged points and just add them to the full list + List debuggedConfigs = this.startDebugBinary(dryConfig); + allDebuggedConfigs.addAll(debuggedConfigs); } } - return debConfig; + return allDebuggedConfigs; } private Configuration failsOnDry(Configuration config) { return this.failsWithConfig(config, Integer.MIN_VALUE, Integer.MAX_VALUE); } - public Configuration startDebugBinary(Configuration config) { - long start = 0; - long end = config.getInvocationCount(); + public List startDebugBinary(Configuration config) { + List allFailingConfigurations = new LinkedList(); + + List, Configuration>> pairs = new LinkedList, Configuration>>(); + pairs.add((Pair, Configuration>)Pair.of((Pair)Pair.of(0L, + (long)config.getInvocationCount()), config)); + Configuration failingConfiguration = null; - while (start < end) { - Logger.getGlobal().log(Level.INFO, "Debugging binary for " + this.test + " " + start + " : " + end); + while (pairs.size() > 0) { + Pair, Configuration> pair = pairs.remove(0); + Pair range = pair.getLeft(); + failingConfiguration = pair.getRight(); + long start = range.getLeft(); + long end = range.getRight(); + + if (start < end) { + Logger.getGlobal().log(Level.INFO, "Debugging binary for " + this.test + " " + start + " : " + end); + + boolean binarySuccess = false; + long midPoint = (start + end) / 2; + if ((failingConfiguration = this.failsWithConfig(config, start, midPoint)) != null) { + pairs.add(Pair.of((Pair)Pair.of(start, midPoint), failingConfiguration)); + binarySuccess = true; + } + if ((failingConfiguration = this.failsWithConfig(config, midPoint + 1, end)) != null) { + pairs.add(Pair.of((Pair)Pair.of(midPoint + 1, end), failingConfiguration)); + binarySuccess = true; + } - long midPoint = (start + end) / 2; - if ((failingConfiguration = this.failsWithConfig(config, start, midPoint)) != null) { - end = midPoint; - continue; - } else if ((failingConfiguration = this.failsWithConfig(config, midPoint + 1, end)) != null) { - start = midPoint + 1; - continue; + // If both halves fail, try the entire range + if (!binarySuccess) { + Logger.getGlobal().log(Level.SEVERE, "Binary splitting did not work. Going to linear"); + allFailingConfigurations.addAll(this.startDebugLinear(config, start, end)); + } } else { - Logger.getGlobal().log(Level.FINE, "Binary splitting did not work. Going to linear"); - failingConfiguration = this.startDebugLinear(config, start, end); - break; + // Since start <= end is always true, this branch means start == end, so reached end + if (failingConfiguration != null) { + allFailingConfigurations.add(this.reportDebugInfo(failingConfiguration)); + } } } - if (failingConfiguration != null) { - return this.reportDebugInfo(failingConfiguration); - } - return failingConfiguration; + + return allFailingConfigurations; } private Configuration reportDebugInfo(Configuration failingConfiguration) { return this.failsWithConfig(failingConfiguration, failingConfiguration.start, failingConfiguration.end, true); } - public Configuration startDebugLinear(Configuration config, long start, long end) { + public List startDebugLinear(Configuration config, long start, long end) { + List allFailingConfigurations = new LinkedList(); + + List, Configuration>> pairs = new LinkedList, Configuration>>(); + pairs.add((Pair, Configuration>)Pair.of((Pair)Pair.of(start, end), + config)); + Configuration failingConfiguration = null; - long localStart = start; - long localEnd = end; - // Give up if range too large - if (localEnd - localStart > 50) { - return null; - } - while (localStart < localEnd) { - Logger.getGlobal().log(Level.INFO, - "Debugging linear for " + this.test + " " + localStart + " : " + localEnd); + while (pairs.size() > 0) { + Pair, Configuration> pair = pairs.remove(0); + Pair range = pair.getLeft(); + failingConfiguration = pair.getRight(); + long localStart = range.getLeft(); + long localEnd = range.getRight(); + + if (localStart < localEnd) { + Logger.getGlobal().log(Level.INFO, "Debugging linear for " + this.test + " " + + localStart + " : " + localEnd); + + boolean found = false; + if ((failingConfiguration = this.failsWithConfig(config, localStart, localEnd - 1)) != null) { + pairs.add(Pair.of((Pair)Pair.of(localStart, localEnd - 1), failingConfiguration)); + found = true; + } + if ((failingConfiguration = this.failsWithConfig(config, localStart + 1, localEnd)) != null) { + pairs.add(Pair.of((Pair)Pair.of(localStart + 1, localEnd), failingConfiguration)); + found = true; + } - if ((failingConfiguration = this.failsWithConfig(config, localStart, localEnd - 1)) != null) { - localEnd = localEnd - 1; - continue; - } else if ((failingConfiguration = this.failsWithConfig(config, localStart + 1, localEnd)) != null) { - localStart = localStart + 1; - continue; + if (!found) { + Logger.getGlobal().log(Level.FINE, "Refining did not work. Does not fail with linear on range " + + localStart + " : " + localEnd + "."); + } } else { - Logger.getGlobal().log(Level.FINE, "Refining did not work. Does not fail with linear."); - break; + // Since start <= end is always true, this branch means start == end, so reached end + if (failingConfiguration != null) { + allFailingConfigurations.add(this.reportDebugInfo(failingConfiguration)); + } } } - return failingConfiguration; + return allFailingConfigurations; } private Configuration failsWithConfig(Configuration config, long start, long end) { diff --git a/nondex-maven-plugin/src/main/java/edu/illinois/nondex/plugin/NonDexMojo.java b/nondex-maven-plugin/src/main/java/edu/illinois/nondex/plugin/NonDexMojo.java index 728b193a..84b5a940 100644 --- a/nondex-maven-plugin/src/main/java/edu/illinois/nondex/plugin/NonDexMojo.java +++ b/nondex-maven-plugin/src/main/java/edu/illinois/nondex/plugin/NonDexMojo.java @@ -62,7 +62,7 @@ a copy of this software and associated documentation files (the public class NonDexMojo extends AbstractNonDexMojo { private List executions = new LinkedList<>(); - private ArrayList executionsWithoutShuffling = + private ArrayList executionsWithoutShuffling = new ArrayList(); @Override