[libFuzzer] form mode: add -ignore_crashes flag, honor the max_total_time flag, print the number of ooms/timeouts/crashes, fix a typo

git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/fuzzer@354175 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/FuzzerFork.cpp b/FuzzerFork.cpp
index eff9009..09da192 100644
--- a/FuzzerFork.cpp
+++ b/FuzzerFork.cpp
@@ -17,6 +17,7 @@
 #include "FuzzerUtil.h"
 
 #include <atomic>
+#include <chrono>
 #include <fstream>
 #include <mutex>
 #include <queue>
@@ -76,10 +77,22 @@
   Set<uint32_t> Features, Cov;
   Vector<std::string> Files;
   Random *Rand;
+  std::chrono::system_clock::time_point ProcessStartTime;
   int Verbosity = 0;
 
+  size_t NumTimeouts = 0;
+  size_t NumOOMs = 0;
+  size_t NumCrashes = 0;
+
+
   size_t NumRuns = 0;
 
+  size_t secondsSinceProcessStartUp() const {
+    return std::chrono::duration_cast<std::chrono::seconds>(
+               std::chrono::system_clock::now() - ProcessStartTime)
+        .count();
+  }
+
   FuzzJob *CreateNewJob(size_t JobId) {
     Command Cmd(Args);
     Cmd.removeFlag("fork");
@@ -146,10 +159,12 @@
 
     auto Stats = ParseFinalStatsFromLog(Job->LogPath);
     NumRuns += Stats.number_of_executed_units;
-    if (!FilesToAdd.empty())
-      Printf("#%zd: cov: %zd ft: %zd corp: %zd exec/s %zd\n", NumRuns,
+    if (!FilesToAdd.empty() || Job->ExitCode != 0)
+      Printf("#%zd: cov: %zd ft: %zd corp: %zd exec/s %zd "
+             "oom/timeout/crash: %zd/%zd/%zd time: %zds\n", NumRuns,
              Cov.size(), Features.size(), Files.size(),
-             Stats.average_exec_per_sec);
+             Stats.average_exec_per_sec,
+             NumOOMs, NumTimeouts, NumCrashes, secondsSinceProcessStartUp());
   }
 };
 
@@ -187,14 +202,14 @@
 void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
                   const Vector<std::string> &Args,
                   const Vector<std::string> &CorpusDirs, int NumJobs) {
-  Printf("INFO: -fork=%d: doing fuzzing in a separate process in order to "
-         "be more resistant to crashes, timeouts, and OOMs\n", NumJobs);
+  Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs);
 
   GlobalEnv Env;
   Env.Args = Args;
   Env.CorpusDirs = CorpusDirs;
   Env.Rand = &Rand;
   Env.Verbosity = Options.Verbosity;
+  Env.ProcessStartTime = std::chrono::system_clock::now();
 
   Vector<SizedFile> SeedFiles;
   for (auto &Dir : CorpusDirs)
@@ -215,8 +230,8 @@
                       {}, &Env.Cov,
                       CFPath, false);
   RemoveFile(CFPath);
-  Printf("INFO: -fork=%d: %zd seeds, starting to fuzz; scratch: %s\n",
-         NumJobs, Env.Files.size(), Env.TempDir.c_str());
+  Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs,
+         Env.Files.size(), Env.TempDir.c_str());
 
   int ExitCode = 0;
 
@@ -230,9 +245,11 @@
     FuzzQ.Push(Env.CreateNewJob(JobId++));
   }
 
-  while (!Stop) {
+  while (true) {
     auto Job = MergeQ.Pop();
     if (!Job) {
+      if (Stop)
+        break;
       SleepSeconds(1);
       continue;
     }
@@ -242,20 +259,42 @@
 
     // Continue if our crash is one of the ignorred ones.
     if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode)
-      ;
+      Env.NumTimeouts++;
     else if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode)
-      ;
+      Env.NumOOMs++;
     else if (ExitCode == Options.InterruptExitCode)
       Stop = true;
     else if (ExitCode != 0) {
-      // And exit if we don't ignore this crash.
-      Printf("INFO: log from the inner process:\n%s",
-                   FileToString(Job->LogPath).c_str());
-      Stop = true;
+      Env.NumCrashes++;
+      if (Options.IgnoreCrashes) {
+        std::ifstream In(Job->LogPath);
+        std::string Line;
+        while (std::getline(In, Line, '\n'))
+          if (Line.find("ERROR:") != Line.npos)
+            Printf("%s\n", Line.c_str());
+      } else {
+        // And exit if we don't ignore this crash.
+        Printf("INFO: log from the inner process:\n%s",
+               FileToString(Job->LogPath).c_str());
+        Stop = true;
+      }
     }
     RemoveFile(Job->LogPath);
     delete Job;
-    FuzzQ.Push(Env.CreateNewJob(JobId++));
+
+    // Stop if we are over the time budget.
+    // This is not precise, since other threads are still running
+    // and we will wait while joining them.
+    // We also don't stop instantly: other jobs need to finish.
+    if (Options.MaxTotalTimeSec > 0 && !Stop &&
+        Env.secondsSinceProcessStartUp() >= (size_t)Options.MaxTotalTimeSec) {
+      Printf("INFO: fuzzed for %zd seconds, wrapping up soon\n",
+             Env.secondsSinceProcessStartUp());
+      Stop = true;
+    }
+
+    if (!Stop)
+      FuzzQ.Push(Env.CreateNewJob(JobId++));
   }
   Stop = true;
 
@@ -265,7 +304,8 @@
   RmDirRecursive(Env.TempDir);
 
   // Use the exit code from the last child process.
-  Printf("Fork: exiting: %d\n", ExitCode);
+  Printf("INFO: exiting: %d time: %zds\n", ExitCode,
+         Env.secondsSinceProcessStartUp());
   exit(ExitCode);
 }