From 3168ab55d2167de5f9953bf52c9c5e3421963319 Mon Sep 17 00:00:00 2001 From: Zbynek Slajchrt <zbynek.slajchrt@oracle.com> Date: Fri, 30 Sep 2016 17:55:43 +0200 Subject: [PATCH] Chimney-sweeping adapted to processing all builtins in batch --- .../truffle/r/nodes/test/ChimneySweeping.java | 142 +++++++++++------- .../r/nodes/test/DefaultArgsExtractor.java | 21 ++- .../r/nodes/test/RBuiltinDiagnostics.java | 63 +++++--- mx.fastr/mx_fastr.py | 4 +- 4 files changed, 145 insertions(+), 85 deletions(-) diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java index a02b86e9fc..1a7c9033ad 100644 --- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java @@ -36,8 +36,12 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.SortedMap; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; +import javax.swing.SwingWorker; + import org.junit.Assert; import com.oracle.truffle.api.dsl.UnsupportedSpecializationException; @@ -65,8 +69,25 @@ import com.oracle.truffle.r.test.generate.GnuROneShotRSession; import com.oracle.truffle.r.test.generate.TestOutputManager; import com.oracle.truffle.r.test.generate.TestOutputManager.TestInfo; +/** + * Use the following command to sweep all builtins + * + * <pre> + * mx rbdiag --sweep --mnonly --matchLevel=error --maxSweeps=30 --outMaxLev=0 + * </pre> + * + * . + * + */ class ChimneySweeping extends SingleBuiltinDiagnostics { + private Set<String> blacklistedBuiltins = new HashSet<>(); + { + blacklistedBuiltins.add(".dfltWarn"); + blacklistedBuiltins.add("browser"); + blacklistedBuiltins.add(".fastr.context.r"); + } + private static final String TEST_PREFIX = "com.oracle.truffle.r.test.builtins.TestBuiltin_"; private static final String SWEEP_MODE_ARG = "--sweep"; private static final String SWEEP_MODE_ARG_SPEC = SWEEP_MODE_ARG + "="; @@ -74,6 +95,7 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { private static final String MISSING_AND_NULL_SAMPLES_ONLY_ARG = "--mnonly"; private static final String OUTPUT_MATCH_LEVEL = "--matchLevel"; private static final String OUTPUT_MATCH_LEVEL_SPEC = OUTPUT_MATCH_LEVEL + "="; + private static final String MAX_SWEEPS_ARG = "--maxSweeps="; enum ChimneySweepingMode { auto, @@ -109,6 +131,7 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { OutputMatchLevel outputMatchLevel; boolean missingAndNullSamplesOnly; boolean performPipelineSelfTest; + int maxSweeps; } static class ChimneySweepingSuite extends RBuiltinDiagnostics { @@ -122,13 +145,13 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { super(config); this.diagConfig = config; - System.out.println("Loading GnuR ..."); + print(1, "Loading GnuR ..."); gnuRSession = new GnuROneShotRSession(); - System.out.println("Loading FastR ..."); + print(1, "Loading FastR ..."); fastRSession = FastRSession.create(); - System.out.println("Loading test outputs ..."); + print(1, "Loading test outputs ..."); outputManager = loadTestOutputManager(); } @@ -148,6 +171,7 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { // The pipeline self-test is disabled when only RMissing and RNull samples are used as // these values are not determined via the pipeline static type analysis config.performPipelineSelfTest = config.missingAndNullSamplesOnly ? false : !Arrays.stream(args).filter(arg -> NO_SELF_TEST_ARG.equals(arg)).findFirst().isPresent(); + config.maxSweeps = Arrays.stream(args).filter(arg -> arg.startsWith(MAX_SWEEPS_ARG)).map(x -> Integer.parseInt(x.split("=")[1])).findFirst().orElse(Integer.MAX_VALUE); return RBuiltinDiagnostics.initDiagConfig(config, args); } @@ -194,6 +218,9 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { ChimneySweeping(ChimneySweepingSuite diagSuite, RIntBuiltinDiagFactory builtinFactory) { super(diagSuite, builtinFactory); + + print(0, "\n*** Chimney-sweeping of '" + builtinName + "' (" + builtinFactory.getBuiltinNodeClass().getName() + ") ***"); + this.kind = builtinFactory.getBuiltinKind(); this.diagSuite = diagSuite; this.validArgsList = extractValidArgsForBuiltin(); @@ -202,17 +229,21 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { @Override public void diagnoseBuiltin() throws Exception { - super.diagnoseBuiltin(); + // super.diagnoseBuiltin(); - sweepChimney(); + if (blacklistedBuiltins.contains(builtinName)) { + print(1, "Builtin '" + builtinName + "' blacklisted for chimney-sweeping"); + } else { + sweepChimney(); + } } @Override protected void diagnosePipeline(int i) { super.diagnosePipeline(i); - System.out.println(" Samples:"); - System.out.println(argSamples.get(i)); + print(1, " Samples:"); + print(1, argSamples.get(i)); if (diagSuite.diagConfig.performPipelineSelfTest) { checkPipelines(i); @@ -221,7 +252,7 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { @SuppressWarnings({"rawtypes", "unchecked"}) private List<Samples<?>> createSamples() { - DefaultArgsExtractor defArgExt = new DefaultArgsExtractor(diagSuite.fastRSession); + DefaultArgsExtractor defArgExt = new DefaultArgsExtractor(diagSuite.fastRSession, msg -> print(1, msg)); Map<String, Samples<?>> defaultArgs = defArgExt.extractDefaultArgs(builtinName); List<Samples<?>> as = new ArrayList<>(); @@ -276,15 +307,15 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { if (cn != null) { Samples<?> samples = argSamples.get(i); if (samples.positiveSamples().isEmpty() && samples.negativeSamples().isEmpty()) { - System.out.println("No samples"); + print(1, "No samples"); } else { testPipeline(cn, samples); - System.out.println("Pipeline check OK (" + samples.positiveSamples().size() + "," + samples.negativeSamples().size() + ")"); + print(1, "Pipeline check OK (" + samples.positiveSamples().size() + "," + samples.negativeSamples().size() + ")"); } } } - private static void testPipeline(CastNode cn, Samples<?> samples) { + private void testPipeline(CastNode cn, Samples<?> samples) { NodeHandle<CastNode> argCastNodeHandle = TestUtilities.createHandle(cn, (node, args) -> { return node.execute(args[0]); }); @@ -293,7 +324,7 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { try { argCastNodeHandle.call(sample); } catch (UnsupportedSpecializationException e) { - System.out.println("Warning: No specialization to handle arg " + sample + " : " + e.getMessage()); + print(1, "Warning: No specialization to handle arg " + sample + " : " + e.getMessage()); } catch (Exception e) { e.printStackTrace(); fail("Unexpectedly negative sample: " + sample); @@ -338,36 +369,24 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { a -> a != null && !"".equals(a)).collect(Collectors.toSet()); Set<RList> args = validArgs.stream().map(a -> evalValidArgs(a, vm)).filter(a -> a != null).collect(Collectors.toSet()); - if (args.isEmpty()) { - Object[] nullArgs = new Object[this.argLength]; - Arrays.fill(nullArgs, RNull.instance); - args = Collections.singleton(RDataFactory.createList(nullArgs)); - System.out.println("No suitable test snippets found. Using the default RNull argument list"); - } - return args; } finally { vm.dispose(); } } - private static RList evalValidArgs(String argsExpr, PolyglotEngine vm) { + private RList evalValidArgs(String argsExpr, PolyglotEngine vm) { try { Value eval = vm.eval(RSource.fromTextInternal(argsExpr, RSource.Internal.UNIT_TEST)); RList args = (RList) eval.get(); return args; } catch (Exception e) { - System.out.println("Warning: Cannot parse arguments: " + argsExpr); + print(1, "Warning: Cannot parse arguments: " + argsExpr); return null; } } - private void sweepChimney() throws IOException { - System.out.println("++++++++++++++++++++++"); - System.out.println("+ Chimney-sweeping +"); - System.out.println("++++++++++++++++++++++"); - System.out.println(); - + private void sweepChimney() { boolean useDiagonalGen; long totalCombinations = calculateNumOfSampleCombinations(argSamples); @@ -389,14 +408,11 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { List<List<Object>> generatedCombinations = generateSampleArgCombinations(argSamples, useDiagonalGen); - System.out.println("Springboard argument lists: " + validArgsList.size()); - System.out.println("Used sample combinations: " + generatedCombinations.size() + " (from total " + totalCombinations + ")"); - System.out.println("Sweeps to perform: " + generatedCombinations.size() * validArgsList.size()); + print(1, "Springboard argument lists: " + validArgsList.size()); + print(1, "Used sample combinations: " + generatedCombinations.size() + " (from total " + totalCombinations + ")"); - System.out.println(); - System.out.println(); - System.out.println("Press Enter to continue ..."); - System.in.read(); + int sweepsToPerform = Math.min(generatedCombinations.size() * validArgsList.size(), diagSuite.diagConfig.maxSweeps); + print(0, "Sweeps to perform: " + sweepsToPerform); evalArgsWithSampleCombinations(generatedCombinations); } @@ -404,8 +420,12 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { private void evalBuiltin(RList validArgs, List<List<Object>> argSampleCombinations) { List<List<Object>> mergedSampleAndValidArgs = mergeValidAndSampleArgs(validArgs, argSampleCombinations); + boolean isOriginal = true; for (List<Object> evalArgs : mergedSampleAndValidArgs) { - evalBuiltin(evalArgs); + if (!evalBuiltin(evalArgs, isOriginal)) { + return; + } + isOriginal = false; } } @@ -415,7 +435,11 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { validArgsList.forEach(validArgs -> evalBuiltin(validArgs, argSampleCombinations)); } - private void evalBuiltin(List<Object> args) { + private boolean evalBuiltin(List<Object> args, boolean isOriginal) { + if (sweepCounter > diagSuite.diagConfig.maxSweeps) { + return false; + } + StringBuilder sb = new StringBuilder(); try { for (int i = 0; i < args.size(); i++) { @@ -437,13 +461,17 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { } String call; - switch (kind) { - case INTERNAL: - call = ".Internal(" + builtinName + "(" + sb + "))"; - break; - default: - call = builtinName + "(" + sb + ")"; - break; + if ("(".equals(builtinName)) { + call = "(" + sb + ")"; + } else { + switch (kind) { + case INTERNAL: + call = ".Internal(" + builtinName + "(" + sb + "))"; + break; + default: + call = builtinName + "(" + sb + ")"; + break; + } } String output; @@ -456,16 +484,24 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { List<String> outputPair = Arrays.asList(output, outputGnu); + if (isOriginal && !output.equals(outputGnu)) { + // The original test may not be passing (e.g. marked by Ignored.Inknown etc.). + // Skip these arguments. + System.out.print('I'); + print(1, "Ignoring original test arguments: " + sb); + return false; + } + if (compareOutputs(output, outputGnu)) { System.out.print('.'); } else if (!printedOutputPairs.contains(outputPair)) { - System.out.println("\n#" + sweepCounter + "> " + call); - System.out.println("\n====== FastR output ======"); - System.out.println(output); + print(0, "\n#" + sweepCounter + "> " + call); + print(0, "\n====== FastR output ======"); + print(0, output); - System.out.println("====== GnuR output ======"); - System.out.println(outputGnu); - System.out.println("=========================="); + print(0, "====== GnuR output ======"); + print(0, outputGnu); + print(0, "=========================="); printedOutputPairs.add(outputPair); } else { @@ -473,17 +509,17 @@ class ChimneySweeping extends SingleBuiltinDiagnostics { } } catch (Throwable e) { - // throw new RuntimeException(e); - // e.printStackTrace(); if (!printedErrors.contains(e.getMessage())) { String call = ".Internal(" + builtinName + "(" + sb + "))"; - System.out.println("\n[" + sweepCounter + "]> " + call); - System.out.println("ERROR: " + e.getMessage()); + print(0, "\n[" + sweepCounter + "]> " + call); + print(0, "ERROR: " + e.getMessage()); printedErrors.add(e.getMessage()); } } finally { sweepCounter++; } + + return true; } private boolean compareOutputs(String output, String outputGnu) { diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java index c11be9eb79..a4551f6255 100644 --- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java @@ -26,6 +26,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.vm.PolyglotEngine; @@ -47,20 +49,24 @@ import com.oracle.truffle.r.test.generate.FastRSession; class DefaultArgsExtractor { private final FastRSession fastRSession; + private final Consumer<Object> printer; - DefaultArgsExtractor(FastRSession fastRSession) { + DefaultArgsExtractor(FastRSession fastRSession, Consumer<Object> printer) { this.fastRSession = fastRSession; + this.printer = printer; } Map<String, Samples<?>> extractDefaultArgs(String functionName) { final PolyglotEngine vm = fastRSession.checkContext(null).createVM(); + HashMap<String, Samples<?>> samplesMap = new HashMap<>(); try { - HashMap<String, Samples<?>> samplesMap = new HashMap<>(); Source source = RSource.fromTextInternal("formals(" + functionName + ")", RSource.Internal.UNIT_TEST); + Value defArgVal = vm.eval(source); + if (defArgVal.get() instanceof RPairList) { RPairList formals = (RPairList) defArgVal.get(); RStringVector names = formals.getNames(null); @@ -75,8 +81,8 @@ class DefaultArgsExtractor { Value eval = vm.eval(RSource.fromTextInternal(deparsedDefVal, RSource.Internal.UNIT_TEST)); defVal = eval.get(); - } catch (Exception e) { - System.out.println("Warning: Unable to evaluate the default value of argument " + name + ". Expression: " + deparsedDefVal); + } catch (Throwable t) { + printer.accept("Warning: Unable to evaluate the default value of argument " + name + ". Expression: " + deparsedDefVal); continue; } @@ -106,11 +112,12 @@ class DefaultArgsExtractor { } - return samplesMap; - } catch (Exception e) { - throw new RuntimeException(e); + } catch (Throwable t) { + printer.accept("Warning: Unable to evaluate formal arguments of function " + functionName); } finally { vm.dispose(); } + + return samplesMap; } } diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java index 86bcced497..274b3ae20a 100644 --- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java @@ -63,11 +63,14 @@ import com.oracle.truffle.r.runtime.nodes.RNode; public class RBuiltinDiagnostics { + private static final String OUTPUT_MAX_LEVEL_ARG = "--outMaxLev="; + static class DiagConfig { boolean verbose; boolean ignoreRNull; boolean ignoreRMissing; long maxTotalCombinations = 500L; + int outputMaxLevel; } static { @@ -89,6 +92,7 @@ public class RBuiltinDiagnostics { diagConfig.verbose = Arrays.stream(args).filter(arg -> "-v".equals(arg)).findFirst().isPresent(); diagConfig.ignoreRNull = Arrays.stream(args).filter(arg -> "-n".equals(arg)).findFirst().isPresent(); diagConfig.ignoreRMissing = Arrays.stream(args).filter(arg -> "-m".equals(arg)).findFirst().isPresent(); + diagConfig.outputMaxLevel = Arrays.stream(args).filter(arg -> arg.startsWith(OUTPUT_MAX_LEVEL_ARG)).map(x -> Integer.parseInt(x.split("=")[1])).findFirst().orElse(Integer.MAX_VALUE); return diagConfig; } @@ -117,7 +121,7 @@ public class RBuiltinDiagnostics { try { bdf = RExtBuiltinDiagFactory.create(builtinName); } catch (Exception e) { - System.out.println("No builtin '" + builtinName + "' found"); + print(0, "No builtin '" + builtinName + "' found"); return; } } else { @@ -126,8 +130,8 @@ public class RBuiltinDiagnostics { createBuiltinDiagnostics(bdf).diagnoseBuiltin(); - System.out.println("Finished"); - System.out.println("--------"); + print(0, "Finished"); + print(0, "--------"); System.exit(0); } @@ -138,16 +142,23 @@ public class RBuiltinDiagnostics { try { createBuiltinDiagnostics(new RIntBuiltinDiagFactory((bf))).diagnoseBuiltin(); } catch (Exception e) { - System.out.println(bf.getName() + " failed: " + e.getMessage()); + e.printStackTrace(); + print(0, bf.getName() + " failed: " + e.getMessage()); } } - System.out.println("Finished"); - System.out.println("--------"); + print(0, "Finished"); + print(0, "--------"); System.exit(0); } + protected void print(int level, Object x) { + if (level <= diagConfig.outputMaxLevel) { + System.out.println(x); + } + } + static class SingleBuiltinDiagnostics { private final RBuiltinDiagnostics diagSuite; final RBuiltinDiagFactory builtinFactory; @@ -167,7 +178,7 @@ public class RBuiltinDiagnostics { String[] pn = builtinFactory.getParameterNames(); this.argLength = pn.length; - this.parameterNames = Arrays.stream(pn).map(n -> n.isEmpty() ? null : n).toArray(String[]::new); + this.parameterNames = Arrays.stream(pn).map(n -> n == null || n.isEmpty() ? null : n).toArray(String[]::new); this.castNodes = getCastNodesFromBuiltin(); @@ -182,6 +193,10 @@ public class RBuiltinDiagnostics { this.nonCoveredArgsSet = combineArguments(); } + protected void print(int level, Object x) { + diagSuite.print(level, x); + } + private HashMap<Method, List<Set<Cast>>> createConvResultTypePerSpecialization() { HashMap<Method, List<Set<Cast>>> convResultTypes = new HashMap<>(); @@ -214,29 +229,29 @@ public class RBuiltinDiagnostics { } public void diagnoseBuiltin() throws Exception { - System.out.println("****************************************************************************"); - System.out.println("Builtin: " + builtinName + " (" + builtinFactory.getBuiltinNodeClass().getName() + ")"); - System.out.println("****************************************************************************"); + print(0, "****************************************************************************"); + print(0, "Builtin: " + builtinName + " (" + builtinFactory.getBuiltinNodeClass().getName() + ")"); + print(0, "****************************************************************************"); - System.out.println("Argument cast pipelines binding:"); + print(0, "Argument cast pipelines binding:"); for (int i = 0; i < argLength; i++) { diagnosePipeline(i); } - System.out.println("\nUnhandled argument combinations: " + nonCoveredArgsSet.size()); - System.out.println(""); + print(0, "\nUnhandled argument combinations: " + nonCoveredArgsSet.size()); + print(0, ""); printDeadSpecs(); if (diagSuite.diagConfig.verbose) { for (List<Type> uncoveredArgs : nonCoveredArgsSet) { - System.out.println(uncoveredArgs.stream().map(t -> typeName(t)).collect(Collectors.toList())); + print(0, uncoveredArgs.stream().map(t -> typeName(t)).collect(Collectors.toList())); } } } private void printDeadSpecs() { - System.out.println("Dead specializations: "); + print(0, "Dead specializations: "); for (Map.Entry<Method, List<Set<Cast>>> resTpPerSpec : convResultTypePerSpec.entrySet()) { List<Set<Cast>> argsCasts = resTpPerSpec.getValue(); List<Integer> missingCasts = new ArrayList<>(); @@ -248,33 +263,33 @@ public class RBuiltinDiagnostics { } if (!missingCasts.isEmpty()) { - System.out.println(" " + methodName(resTpPerSpec.getKey(), missingCasts)); + print(0, " " + methodName(resTpPerSpec.getKey(), missingCasts)); } } - System.out.println(""); + print(0, ""); } protected void diagnosePipeline(int i) { TypeExpr argResultSet = argResultSets.get(i); - System.out.println("\n Pipeline for '" + parameterNames[i] + "' (arg[" + i + "]):"); - System.out.println(" Result types union:"); + print(0, "\n Pipeline for '" + parameterNames[i] + "' (arg[" + i + "]):"); + print(0, " Result types union:"); Set<Type> argSetNorm = argResultSet.normalize(); - System.out.println(" " + argSetNorm.stream().map(argType -> typeName(argType)).collect(Collectors.toSet())); - System.out.println(" Bound result types:"); + print(0, " " + argSetNorm.stream().map(argType -> typeName(argType)).collect(Collectors.toSet())); + print(0, " Bound result types:"); final int curParIndex = i; Set<Type> unboundArgTypes = new HashSet<>(argSetNorm); for (Map.Entry<Method, List<Set<Cast>>> entry : convResultTypePerSpec.entrySet()) { Set<Cast> argCastInSpec = entry.getValue().get(i); argCastInSpec.stream().forEach( partialCast -> { - System.out.println(" " + partialCast.coverage() + " (" + typeName(partialCast.inputType()) + "->" + typeName(partialCast.resultType()) + ")" + " in " + + print(0, " " + partialCast.coverage() + " (" + typeName(partialCast.inputType()) + "->" + typeName(partialCast.resultType()) + ")" + " in " + methodName(entry.getKey(), Collections.singleton(curParIndex))); unboundArgTypes.remove(partialCast.inputType()); }); } - System.out.println(" Unbound types:"); - System.out.println(" " + unboundArgTypes.stream().map(argType -> typeName(argType)).collect(Collectors.toSet())); + print(0, " Unbound types:"); + print(0, " " + unboundArgTypes.stream().map(argType -> typeName(argType)).collect(Collectors.toSet())); } diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py index 2b63d8eecd..c446cbe52e 100644 --- a/mx.fastr/mx_fastr.py +++ b/mx.fastr/mx_fastr.py @@ -466,6 +466,8 @@ def rbdiag(args): --sweep=total Performs the 'chimney-sweeping'. The total sample selection method is used. --matchLevel=same Outputs produced by FastR and GnuR must be same (default) --matchLevel=error Outputs are considered matching if none or both outputs contain an error + --maxSweeps=N Sets the maximum number of sweeps + --outMaxLev=N Sets the maximum output detail level for report messages. Use 0 for the basic messages only. If no builtin is specified, all registered builtins are diagnosed. An external builtin is specified by the fully qualified name of its node class. @@ -529,7 +531,7 @@ _commands = { 'junitnopkgs' : [junit_nopkgs, ['options']], 'unittest' : [unittest, ['options']], 'rbcheck' : [rbcheck, '--filter [gnur-only,fastr-only,both,both-diff]'], - 'rbdiag' : [rbdiag, '(builtin)* [-v] [-n] [-m] [--sweep | --sweep=lite | --sweep=total] [--mnonly] [--noSelfTest] [--matchLevel=same | --matchLevel=error]'], + 'rbdiag' : [rbdiag, '(builtin)* [-v] [-n] [-m] [--sweep | --sweep=lite | --sweep=total] [--mnonly] [--noSelfTest] [--matchLevel=same | --matchLevel=error] [--maxSweeps=N] [--outMaxLev=N]'], 'rcmplib' : [rcmplib, ['options']], 'rrepl' : [rrepl, '[options]'], 'rembed' : [rembed, '[options]'], -- GitLab