Index: lib/Fuzzer/FuzzerDriver.cpp =================================================================== --- lib/Fuzzer/FuzzerDriver.cpp +++ lib/Fuzzer/FuzzerDriver.cpp @@ -245,6 +245,7 @@ Options.OnlyASCII = Flags.only_ascii; Options.TBMDepth = Flags.tbm_depth; Options.TBMWidth = Flags.tbm_width; + Options.OutputCSV = Flags.output_csv; if (Flags.runs >= 0) Options.MaxNumberOfRuns = Flags.runs; if (!Inputs->empty()) Index: lib/Fuzzer/FuzzerFlags.def =================================================================== --- lib/Fuzzer/FuzzerFlags.def +++ lib/Fuzzer/FuzzerFlags.def @@ -67,3 +67,4 @@ FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, " "timeout, or slow inputs) as " "$(artifact_prefix)file") +FUZZER_FLAG_INT(output_csv, 0, "Enable pulse output in CSV format.") Index: lib/Fuzzer/FuzzerInternal.h =================================================================== --- lib/Fuzzer/FuzzerInternal.h +++ lib/Fuzzer/FuzzerInternal.h @@ -96,6 +96,7 @@ std::string ArtifactPrefix = "./"; std::vector Dictionary; bool SaveArtifacts = true; + bool OutputCSV = false; }; Fuzzer(UserSuppliedFuzzer &USF, FuzzingOptions Options); void AddToCorpus(const Unit &U) { Corpus.push_back(U); } Index: lib/Fuzzer/FuzzerLoop.cpp =================================================================== --- lib/Fuzzer/FuzzerLoop.cpp +++ lib/Fuzzer/FuzzerLoop.cpp @@ -107,9 +107,23 @@ } void Fuzzer::PrintStats(const char *Where, const char *End) { - if (!Options.Verbosity) return; size_t Seconds = secondsSinceProcessStartUp(); size_t ExecPerSec = (Seconds ? TotalNumberOfRuns / Seconds : 0); + + if (Options.OutputCSV) { + static bool csvHeaderPrinted = false; + if (!csvHeaderPrinted) { + csvHeaderPrinted = true; + Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n"); + } + Printf("%zd,%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns, + LastRecordedBlockCoverage, TotalBits(), + LastRecordedCallerCalleeCoverage, Corpus.size(), ExecPerSec, + TotalNumberOfExecutedTraceBasedMutations, Where); + } + + if (!Options.Verbosity) + return; Printf("#%zd\t%s", TotalNumberOfRuns, Where); if (LastRecordedBlockCoverage) Printf(" cov: %zd", LastRecordedBlockCoverage); @@ -143,8 +157,7 @@ CurrentUnit.insert(CurrentUnit.begin(), X.begin(), X.end()); if (RunOne(CurrentUnit)) { Corpus.push_back(X); - if (Options.Verbosity >= 1) - PrintStats("RELOAD"); + PrintStats("RELOAD"); } } } @@ -197,9 +210,8 @@ auto UnitStopTime = system_clock::now(); auto TimeOfUnit = duration_cast(UnitStopTime - UnitStartTime).count(); - if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) - && secondsSinceProcessStartUp() >= 2 - && Options.Verbosity) + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && + secondsSinceProcessStartUp() >= 2) PrintStats("pulse "); if (TimeOfUnit > TimeOfLongestUnitInSeconds && TimeOfUnit >= Options.ReportSlowUnits) { @@ -400,11 +412,11 @@ SyncCorpus(); RereadOutputCorpus(); if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) - return; + break; if (Options.MaxTotalTimeSec > 0 && secondsSinceProcessStartUp() > static_cast(Options.MaxTotalTimeSec)) - return; + break; CurrentUnit = Corpus[J1]; // Optionally, cross with another unit. if (Options.DoCrossOver && USF.GetRand().RandBool()) { @@ -424,6 +436,8 @@ // Perform several mutations and runs. MutateAndTestOne(&CurrentUnit); } + + PrintStats("DONE ", ""); } void Fuzzer::SyncCorpus() {