private ProgramPairResults CompileAndRun(CompilationUnitSyntax prog) { CompileResult progDebug = Compiler.Compile(prog, Compiler.DebugOptions); CompileResult progRelease = Compiler.Compile(prog, Compiler.ReleaseOptions); if (progDebug.Assembly == null || progRelease.Assembly == null) { return(null); } ProgramPair pair = new ProgramPair(progDebug.Assembly, progRelease.Assembly); ProgramPairResults results = ProgramExecutor.RunPair(pair); return(results); }
public RunSeparatelyResults RunPair(ProgramPair pair, TimeSpan timeout) { ReceiveResult result = RequestAndReceive(new Request { Kind = RequestKind.RunPair, Pair = pair, }, timeout); if (result.Ended) { return(new RunSeparatelyResults(RunSeparatelyResultsKind.Crash, null, result.Stderr)); } if (result.Timeout) { return(new RunSeparatelyResults(RunSeparatelyResultsKind.Timeout, null, null)); } return(new RunSeparatelyResults(RunSeparatelyResultsKind.Success, result.Response.RunPairResult, null)); }
private static ProgramPairResults RunPairAsync(AssemblyLoadContext alc, ProgramPair pair) { ProgramResult debugResult = RunAndGetResultAsync(pair.Debug); ProgramResult releaseResult = RunAndGetResultAsync(pair.Release); ChecksumSite unmatch1 = null; ChecksumSite unmatch2 = null; if (debugResult.Checksum != releaseResult.Checksum && pair.TrackOutput) { int index; int count = Math.Min(debugResult.ChecksumSites.Count, releaseResult.ChecksumSites.Count); for (index = 0; index < count; index++) { ChecksumSite val1 = debugResult.ChecksumSites[index]; ChecksumSite val2 = releaseResult.ChecksumSites[index]; if (val1 != val2) { break; } } if (index < debugResult.ChecksumSites.Count) { unmatch1 = debugResult.ChecksumSites[index]; } if (index < releaseResult.ChecksumSites.Count) { unmatch2 = releaseResult.ChecksumSites[index]; } } return(new ProgramPairResults(debugResult, releaseResult, unmatch1, unmatch2)); ProgramResult RunAndGetResultAsync(byte[] bytes) { Assembly asm = alc.LoadFromStream(new MemoryStream(bytes)); MethodInfo mainMethodInfo = asm.GetType("Program").GetMethod("Main"); Action <IRuntime> entryPoint = (Action <IRuntime>)Delegate.CreateDelegate(typeof(Action <IRuntime>), mainMethodInfo); var runtime = new Runtime(); List <ChecksumSite> TakeChecksumSites() { // A reference to the runtime stays in the loaded assembly and there may be a lot of checksum sites // so during reduction this can use a lot of memory. List <ChecksumSite> checksumSites = runtime.ChecksumSites; runtime.ChecksumSites = null; return(checksumSites); } if (pair.TrackOutput) { runtime.ChecksumSites = new List <ChecksumSite>(); } int threadID = Environment.CurrentManagedThreadId; List <Exception> exceptions = null; void FirstChanceExceptionHandler(object sender, FirstChanceExceptionEventArgs args) { if (Environment.CurrentManagedThreadId == threadID) { (exceptions ??= new List <Exception>()).Add(args.Exception); } } AppDomain.CurrentDomain.FirstChanceException += FirstChanceExceptionHandler; try { entryPoint(runtime); } catch { // We consider the innermost exception the root cause and only report it. // Otherwise we may be confusing the viewer about what the problem is. // Consider for example (adapted from a real example): // try // { // value = -1; // FunctionThatJitAssertsInDebug(); // value = 1; // } // finally // { // int.MinValue / value; // } // We are interested in the JIT assert that was hit, and not the OverflowException // thrown because value = 1 did not get to run. Exception ex = exceptions[0]; if (ex is InvalidProgramException && ex.Message.Contains("JIT assert failed")) { return(new ProgramResult { Kind = ProgramResultKind.HitsJitAssert, Checksum = runtime.FinishHashCode(), ChecksumSites = TakeChecksumSites(), JitAssertError = ex.Message, }); } return(new ProgramResult { Kind = ProgramResultKind.ThrowsException, Checksum = runtime.FinishHashCode(), ChecksumSites = TakeChecksumSites(), ExceptionType = ex.GetType().FullName, ExceptionText = ex.ToString(), ExceptionStackTrace = ex.StackTrace, }); } finally { AppDomain.CurrentDomain.FirstChanceException -= FirstChanceExceptionHandler; } return(new ProgramResult { Kind = ProgramResultKind.RunsSuccessfully, Checksum = runtime.FinishHashCode(), ChecksumSites = TakeChecksumSites(), }); } }
public CompilationUnitSyntax Reduce() { CompileResult debug = Compiler.Compile(Original, Compiler.DebugOptions); CompileResult release = Compiler.Compile(Original, Compiler.ReleaseOptions); Func <CompilationUnitSyntax, bool> isInteresting; if (debug.RoslynException != null || release.RoslynException != null) { CSharpCompilationOptions opts = debug.RoslynException != null ? Compiler.DebugOptions : Compiler.ReleaseOptions; isInteresting = program => Compiler.Compile(program, opts).RoslynException != null; } else if (debug.CompileErrors.Length > 0 || release.CompileErrors.Length > 0) { CSharpCompilationOptions opts = debug.CompileErrors.Length > 0 ? Compiler.DebugOptions : Compiler.ReleaseOptions; isInteresting = program => { CompileResult recompiled = Compiler.Compile(program, opts); if (recompiled.CompileErrors.Length <= 0) { return(false); } return(recompiled.CompileErrors[0].Id == (debug.CompileErrors.Length > 0 ? debug.CompileErrors[0] : release.CompileErrors[0]).Id); }; } else { var origPair = new ProgramPair(debug.Assembly, release.Assembly); ProgramPairResults origResults = ProgramExecutor.RunPair(origPair); if (origResults.DebugResult.Checksum == origResults.ReleaseResult.Checksum && origResults.DebugResult.ExceptionType == origResults.ReleaseResult.ExceptionType) { throw new InvalidOperationException("Program has no errors"); } isInteresting = prog => { ProgramPairResults results = CompileAndRun(prog); if (results == null) { return(false); } // Do exceptions first because they will almost always change checksum if (origResults.DebugResult.ExceptionType != origResults.ReleaseResult.ExceptionType) { // Must throw same exceptions in debug and release to be bad. return(results.DebugResult.ExceptionType == origResults.DebugResult.ExceptionType && results.ReleaseResult.ExceptionType == origResults.ReleaseResult.ExceptionType); } else { if (results.DebugResult.ExceptionType != origResults.DebugResult.ExceptionType || results.ReleaseResult.ExceptionType != origResults.ReleaseResult.ExceptionType) { return(false); } } return(results.DebugResult.Checksum != results.ReleaseResult.Checksum); }; } // Save original comments as simplification may remove it by removing an unnecessary type. SyntaxTriviaList originalTrivia = Original.GetLeadingTrivia(); Reduced = Original.WithLeadingTrivia(); Reduced = CoarseSimplify(Reduced, isInteresting); List <SyntaxNode> simplifiedNodes = new List <SyntaxNode>(); bool first = true; bool any = true; while (any) { any = false; while (true) { if (!SimplifyOne("Statements", Reduced.DescendantNodes().Where(n => n is StatementSyntax).ToList())) { break; } any = true; } while (true) { if (!SimplifyOne("Expressions", Reduced.DescendantNodes().Where(n => n is ExpressionSyntax).ToList())) { break; } any = true; } while (true) { List <SyntaxNode> members = Reduced.DescendantNodesAndSelf().Where(n => n is MemberDeclarationSyntax || n is CompilationUnitSyntax).ToList(); if (!SimplifyOne("Members", members)) { break; } any = true; } first = false; bool SimplifyOne(string name, List <SyntaxNode> list) { for (int i = 0; i < 2000; i++) { Console.Title = $"Simplifying {name}. Iter: {i}"; SyntaxNode node = list[_rng.Next(list.Count)]; // Do not optimize checksum args and call itself. // We still want to remove these statements, however, so we focus on the expression only. InvocationExpressionSyntax invocParent = node.FirstAncestorOrSelf <InvocationExpressionSyntax>(); if (invocParent != null && IsChecksumCall(invocParent)) { continue; } // If we fail at creating a new bad example, then we want to be able to restore the state // so the reducer will not blow these up unnecessarily. int origVarCounter = _varCounter; simplifiedNodes.Clear(); SimplifyNode(node, !first, simplifiedNodes); foreach (SyntaxNode candidateNode in simplifiedNodes) { CompilationUnitSyntax candidate = Reduced.ReplaceNode(node, candidateNode); if (isInteresting(candidate)) { Reduced = candidate; return(true); } } _varCounter = origVarCounter; } return(false); } } List <SyntaxTrivia> outputComments = GetOutputComments(debug, release).Select(Comment).ToList(); SimplifyRuntime(); double oldSizeKiB = Original.NormalizeWhitespace().ToString().Length / 1024.0; double newSizeKiB = Reduced.NormalizeWhitespace().ToString().Length / 1024.0; SyntaxTriviaList newTrivia = originalTrivia.Add(Comment(FormattableString.Invariant($"// Reduced from {oldSizeKiB:F1} KiB to {newSizeKiB:F1} KiB"))) .AddRange(outputComments); Reduced = Reduced.WithLeadingTrivia(newTrivia); return(Reduced); }