private IEnumerable <string> GetOutputComments(CompileResult ogDebugCompile, CompileResult ogRelCompile) { if (ogDebugCompile.RoslynException != null) { yield return($"// Roslyn throws '{ogDebugCompile.RoslynException.GetType()}' when compiling in debug"); yield break; } if (ogRelCompile.RoslynException != null) { yield return($"// Roslyn throws '{ogRelCompile.RoslynException.GetType()}' when compiling in release"); yield break; } if (ogDebugCompile.CompileErrors.Length > 0 || ogRelCompile.CompileErrors.Length > 0) { CSharpCompilationOptions compileOpts = ogDebugCompile.CompileErrors.Length >= 0 ? Compiler.DebugOptions : Compiler.ReleaseOptions; CompileResult result = Compiler.Compile(Reduced.NormalizeWhitespace(), compileOpts); yield return($"// Roslyn gives '{result.CompileErrors[0]}'"); yield break; } ProgramPairResults results = CompileAndRun(Reduced); yield return($"// Debug: {FormatResult(results.DebugResult, results.DebugFirstUnmatch)}"); yield return($"// Release: {FormatResult(results.ReleaseResult, results.ReleaseFirstUnmatch)}"); string FormatResult(ProgramResult result, ChecksumSite unmatch) { if (results.DebugResult.ExceptionType != null || results.ReleaseResult.ExceptionType != null) { if (result.ExceptionType != null) { return($"Throws '{result.ExceptionType}'"); } return("Runs successfully"); } if (results.DebugResult.ChecksumSites.Count != results.ReleaseResult.ChecksumSites.Count) { return($"Prints {result.ChecksumSites.Count} line(s)"); } if (unmatch != null) { return($"Outputs {unmatch.Value}"); } return(""); } }
private static void RemoveFixedPrograms(FuzzlynOptions options, string dir) { string[] files = Directory.GetFiles(dir, "*.cs"); List <ulong> toRereduce = new List <ulong>(); for (int i = 0; i < files.Length; i++) { Console.Title = $"Processing {i + 1}/{files.Length}"; string contents = File.ReadAllText(files[i]); MatchCollection matches = Regex.Matches(contents, "// Seed: ([0-9]+)"); if (matches.Count != 1) { continue; } ulong seed = ulong.Parse(matches[0].Groups[1].Value); options.Seed = seed; var cg = new CodeGenerator(options); CompilationUnitSyntax original = cg.GenerateProgram(false); CompileResult debug = Compiler.Compile(original, Compiler.DebugOptions); CompileResult release = Compiler.Compile(original, Compiler.ReleaseOptions); if (debug.CompileErrors.Length > 0 || release.CompileErrors.Length > 0) { continue; } if (debug.RoslynException != null || release.RoslynException != null) { continue; } ProgramPairResults execResults = ProgramExecutor.RunPair(new ProgramPair(debug.Assembly, release.Assembly)); if (execResults.DebugResult.Checksum != execResults.ReleaseResult.Checksum || execResults.DebugResult.ExceptionType != execResults.ReleaseResult.ExceptionType) { // Execute the reduced form to see if we get interesting behavior. // Otherwise we may need to rereduce it. if (!IsReducedVersionInteresting(execResults, contents)) { toRereduce.Add(seed); Console.WriteLine("Marking {0} for rereduction", Path.GetFileName(files[i])); } continue; } Console.WriteLine("Removing {0}", Path.GetFileName(files[i])); File.Delete(files[i]); } const string rereduceFile = "Rereduce_required.txt"; File.WriteAllText(rereduceFile, string.Join(Environment.NewLine, toRereduce)); Console.WriteLine("Wrote {0} seeds to be rereduced to '{1}'", toRereduce.Count, Path.GetFullPath(rereduceFile)); }
/// <summary> /// Checks if a reduced version (on disk) is interesting by running it and checking for exceptions /// and output. Output is captured by redirecting stdout during executiong. private static bool IsReducedVersionInteresting(ProgramPairResults fullResults, string code) { CompilationUnitSyntax comp = ParseCompilationUnit(code); var debug = Execute(Compiler.DebugOptions); var release = Execute(Compiler.ReleaseOptions); if (fullResults.DebugResult.ExceptionType != fullResults.ReleaseResult.ExceptionType) { return(debug.exceptionType == fullResults.DebugResult.ExceptionType && release.exceptionType == fullResults.ReleaseResult.ExceptionType); } return(debug.stdout != release.stdout); (string stdout, string exceptionType) Execute(CSharpCompilationOptions opts) { CompileResult result = Compiler.Compile(comp, opts); Trace.Assert(result.Assembly != null); Assembly asm = Assembly.Load(result.Assembly); MethodInfo mainMethodInfo = asm.GetType("Program").GetMethod("Main"); Action entryPoint = (Action)Delegate.CreateDelegate(typeof(Action), mainMethodInfo); Exception ex = null; TextWriter origOut = Console.Out; MemoryStream ms = new MemoryStream(); StreamWriter sw = new StreamWriter(ms, Encoding.UTF8); try { Console.SetOut(sw); entryPoint(); } catch (Exception caughtEx) { ex = caughtEx; } finally { Console.SetOut(origOut); sw.Close(); } string stdout = Encoding.UTF8.GetString(ms.ToArray()); return(stdout, ex?.GetType().FullName); } }
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); }
private IEnumerable <string> GetOutputComments(CompileResult ogDebugCompile, CompileResult ogRelCompile) { if (ogDebugCompile.RoslynException != null) { yield return($"// Roslyn throws '{ogDebugCompile.RoslynException.GetType()}' when compiling in debug"); yield break; } if (ogRelCompile.RoslynException != null) { yield return($"// Roslyn throws '{ogRelCompile.RoslynException.GetType()}' when compiling in release"); yield break; } ProgramPairResults results = CompileAndRun(Reduced); yield return($"// Debug: {FormatResult(results.DebugResult, results.DebugFirstUnmatch)}"); yield return($"// Release: {FormatResult(results.ReleaseResult, results.ReleaseFirstUnmatch)}"); string FormatResult(ProgramResult result, ChecksumSite unmatch) { if (results.DebugResult.ExceptionType != null || results.ReleaseResult.ExceptionType != null) { if (result.ExceptionType != null) { return($"Throws '{result.ExceptionType}'"); } return("Runs successfully"); } if (results.DebugResult.ChecksumSites.Count != results.ReleaseResult.ChecksumSites.Count) { return($"Prints {result.ChecksumSites.Count} line(s)"); } if (unmatch != null) { return($"Outputs {unmatch.Value}"); } return(""); } }
private static void RemoveFixedPrograms(FuzzlynOptions options, string dir) { const string rereduceFile = "Rereduce_required.txt"; string[] files = Directory.GetFiles(dir, "*.cs").OrderBy(p => p.ToLowerInvariant()).ToArray(); for (int i = 0; i < files.Length; i++) { Console.Title = $"Processing {i + 1}/{files.Length}"; string contents = File.ReadAllText(files[i]); MatchCollection matches = Regex.Matches(contents, "// Seed: ([0-9]+)"); if (matches.Count != 1) { continue; } ulong seed = ulong.Parse(matches[0].Groups[1].Value); Console.Write("Processing {0}: ", seed); options.Seed = seed; var cg = new CodeGenerator(options); CompilationUnitSyntax original = cg.GenerateProgram(); CompileResult debug = Compiler.Compile(original, Compiler.DebugOptions); CompileResult release = Compiler.Compile(original, Compiler.ReleaseOptions); if (debug.CompileErrors.Length > 0 || release.CompileErrors.Length > 0) { Console.WriteLine("Compiler error"); continue; } if (debug.RoslynException != null || release.RoslynException != null) { Console.WriteLine("Compiler exception"); continue; } ProgramPairResults execResults = ProgramExecutor.RunSeparately( new List <ProgramPair> { new ProgramPair(false, debug.Assembly, release.Assembly) }) ?.Single(); if (execResults == null) { Console.WriteLine("Crashed sub-process, still interesting"); continue; } if (execResults.DebugResult.Checksum != execResults.ReleaseResult.Checksum || execResults.DebugResult.ExceptionType != execResults.ReleaseResult.ExceptionType) { // Execute the reduced form to see if we get interesting behavior. // Otherwise we may need to rereduce it. // HOAX: Currently IsReducedVersionInteresting runs the programs // in our own process, so we are conservative and do not run programs // that may crash us (it is possible that the unreduced example does not // crash, but that the reduced does. if (contents.Contains("Crashes the runtime") || IsReducedVersionInteresting(execResults, contents)) { Console.WriteLine("Still interesting"); } else { File.AppendAllText(rereduceFile, seed + Environment.NewLine); Console.WriteLine("Marked for rereduction"); } continue; } Console.WriteLine("Deleted, no longer interesting"); File.Delete(files[i]); } }
public RunSeparatelyResults(RunSeparatelyResultsKind kind, ProgramPairResults results, string crashError) { Kind = kind; Results = results; CrashError = crashError; }
public static void Main(string[] args) { const int stackSize = 64 * 1024 * 1024; Thread thread = new(() => { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Prevent post-mortem debuggers from launching here. We get // runtime crashes sometimes and the parent Fuzzlyn process will // handle it. SetErrorMode(ErrorModes.SEM_NOGPFAULTERRORBOX); } Func <string> readLine; if (args.Length > 0) { string[] lines = File.ReadAllLines(args[0]); int index = 0; readLine = () => index >= lines.Length ? null : lines[index++]; } else { readLine = Console.ReadLine; } AssemblyLoadContext currentAlc = new ServerAssemblyLoadContext(); int currentRunsInAlc = 0; while (true) { string line = readLine(); if (line == null) { return; } Request req = JsonSerializer.Deserialize <Request>(line); Response resp; switch (req.Kind) { case RequestKind.RunPair: ProgramPairResults results = RunPairAsync(currentAlc, req.Pair); currentRunsInAlc++; resp = new Response { RunPairResult = results }; break; case RequestKind.Shutdown: resp = new Response(); break; default: return; } Console.WriteLine(JsonSerializer.Serialize(resp)); if (req.Kind == RequestKind.Shutdown) { return; } if (currentRunsInAlc > 100) { currentAlc.Unload(); currentAlc = new ServerAssemblyLoadContext(); currentRunsInAlc = 0; } } }, stackSize); thread.Start(); thread.Join(); }
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); }