Skip to content

Commit

Permalink
Allow debug to report multiple invocations (#168)
Browse files Browse the repository at this point in the history
Squashed four commits
  • Loading branch information
kaiyaok2 authored Aug 27, 2022
1 parent d191e05 commit fe85fb7
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 60 deletions.
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,
(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),
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

0 comments on commit fe85fb7

Please sign in to comment.