Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -92,26 +94,43 @@ public String debug() throws MojoExecutionException {
}

private String tryDebugSeeds() {
Configuration failingOne = this.debugWithConfigurations(this.failingConfigurations);
List<Configuration> 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<Configuration> 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<Configuration> 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<Configuration> createNewSeedsToRetry() {
Configuration someFailingConfig = this.failingConfigurations.iterator().next();
int newSeed = someFailingConfig.seed * ConfigurationDefaults.SEED_FACTOR;
Expand All @@ -128,85 +147,114 @@ private List<Configuration> createNewSeedsToRetry() {
return retryWOtherSeeds;
}

private Configuration debugWithConfigurations(List<Configuration> failingConfigurations) {
Configuration debConfig = null;
private List<Configuration> debugWithConfigurations(List<Configuration> failingConfigurations) {
List<Configuration> allDebuggedConfigs = new LinkedList<Configuration>();
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<Configuration> 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<Configuration> startDebugBinary(Configuration config) {
List<Configuration> allFailingConfigurations = new LinkedList<Configuration>();

List<Pair<Pair<Long, Long>, Configuration>> pairs = new LinkedList<Pair<Pair<Long, Long>, Configuration>>();
pairs.add((Pair<Pair<Long, Long>, Configuration>)Pair.of((Pair<Long, Long>)Pair.of(0L,
Comment thread
kaiyaok2 marked this conversation as resolved.
(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<Pair<Long, Long>, Configuration> pair = pairs.remove(0);
Pair<Long, Long> 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<Long, Long>)Pair.of(start, midPoint), failingConfiguration));
binarySuccess = true;
}
if ((failingConfiguration = this.failsWithConfig(config, midPoint + 1, end)) != null) {
pairs.add(Pair.of((Pair<Long, Long>)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<Configuration> startDebugLinear(Configuration config, long start, long end) {
List<Configuration> allFailingConfigurations = new LinkedList<Configuration>();

List<Pair<Pair<Long, Long>, Configuration>> pairs = new LinkedList<Pair<Pair<Long, Long>, Configuration>>();
pairs.add((Pair<Pair<Long, Long>, Configuration>)Pair.of((Pair<Long, Long>)Pair.of(start, end),
Comment thread
kaiyaok2 marked this conversation as resolved.
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<Pair<Long, Long>, Configuration> pair = pairs.remove(0);
Pair<Long, Long> 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<Long, Long>)Pair.of(localStart, localEnd - 1), failingConfiguration));
found = true;
}
if ((failingConfiguration = this.failsWithConfig(config, localStart + 1, localEnd)) != null) {
pairs.add(Pair.of((Pair<Long, Long>)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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ a copy of this software and associated documentation files (the
public class NonDexMojo extends AbstractNonDexMojo {

private List<NonDexSurefireExecution> executions = new LinkedList<>();
private ArrayList<CleanSurefireExecution> executionsWithoutShuffling =
private ArrayList<CleanSurefireExecution> executionsWithoutShuffling =
new ArrayList<CleanSurefireExecution>();

@Override
Expand Down