Index: FuzzerDriver.cpp =================================================================== --- FuzzerDriver.cpp +++ FuzzerDriver.cpp @@ -613,6 +613,7 @@ Options.PrintNewCovPcs = Flags.print_pcs; Options.PrintNewCovFuncs = Flags.print_funcs; Options.PrintFinalStats = Flags.print_final_stats; + Options.PrintMutationStats = Flags.print_mutation_stats; Options.PrintCorpusStats = Flags.print_corpus_stats; Options.PrintCoverage = Flags.print_coverage; Options.DumpCoverage = Flags.dump_coverage; Index: FuzzerFlags.def =================================================================== --- FuzzerFlags.def +++ FuzzerFlags.def @@ -153,3 +153,4 @@ FUZZER_FLAG_INT(analyze_dict, 0, "Experimental") FUZZER_DEPRECATED_FLAG(use_clang_coverage) FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace") +FUZZER_FLAG_INT(print_mutation_stats, 0, "Experimental") Index: FuzzerLoop.cpp =================================================================== --- FuzzerLoop.cpp +++ FuzzerLoop.cpp @@ -355,6 +355,8 @@ TPC.DumpCoverage(); if (Options.PrintCorpusStats) Corpus.PrintStats(); + if (Options.PrintMutationStats) + MD.PrintMutationStats(); if (!Options.PrintFinalStats) return; size_t ExecPerSec = execPerSec(); Index: FuzzerMutate.h =================================================================== --- FuzzerMutate.h +++ FuzzerMutate.h @@ -86,13 +86,16 @@ Random &GetRand() { return Rand; } -private: + void PrintMutationStats(); + + void CountCurrentMutatorSequence(); struct Mutator { size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max); const char *Name; }; +private: size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size, size_t MaxSize); size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, @@ -130,6 +133,7 @@ Vector CurrentMutatorSequence; Vector CurrentDictionaryEntrySequence; + Vector CurrentMutatorIdxSequence; static const size_t kCmpDictionaryEntriesDequeSize = 16; DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize]; @@ -143,6 +147,11 @@ Vector Mutators; Vector DefaultMutators; + + // A total count of each mutation used in the fuzzing process. + Vector TotalMutations; + // The number of each mutation that resulted in new coverage. + Vector UsefulMutations; }; } // namespace fuzzer Index: FuzzerMutate.cpp =================================================================== --- FuzzerMutate.cpp +++ FuzzerMutate.cpp @@ -58,6 +58,10 @@ if (EF->LLVMFuzzerCustomCrossOver) Mutators.push_back( {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"}); + + // Initialize mutation statistic counters. + TotalMutations.resize(Mutators.size(), 0); + UsefulMutations.resize(Mutators.size(), 0); } static char RandCh(Random &Rand) { @@ -261,9 +265,9 @@ DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); } break; case 3: if (Options.UseMemmem) { - auto X = TPC.MMT.Get(Rand.Rand()); - DE = DictionaryEntry(X); - } break; + auto X = TPC.MMT.Get(Rand.Rand()); + DE = DictionaryEntry(X); + } break; default: assert(0); } @@ -431,18 +435,18 @@ auto &U = MutateInPlaceHere; size_t NewSize = 0; switch(Rand(3)) { - case 0: - NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size()); - break; - case 1: - NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize); - if (!NewSize) - NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size()); - break; - case 2: + case 0: + NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size()); + break; + case 1: + NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize); + if (!NewSize) NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size()); - break; - default: assert(0); + break; + case 2: + NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size()); + break; + default: assert(0); } assert(NewSize > 0 && "CrossOver returned empty unit"); assert(NewSize <= MaxSize && "CrossOver returned overisized unit"); @@ -452,6 +456,7 @@ void MutationDispatcher::StartMutationSequence() { CurrentMutatorSequence.clear(); + CurrentMutatorIdxSequence.clear(); CurrentDictionaryEntrySequence.clear(); } @@ -465,6 +470,7 @@ if (!PersistentAutoDictionary.ContainsWord(DE->GetW())) PersistentAutoDictionary.push_back({DE->GetW(), 1}); } + CountCurrentMutatorSequence(); } void MutationDispatcher::PrintRecommendedDictionary() { @@ -514,12 +520,15 @@ // in which case they will return 0. // Try several times before returning un-mutated data. for (int Iter = 0; Iter < 100; Iter++) { - auto M = Mutators[Rand(Mutators.size())]; + size_t MutatorIdx = Rand(Mutators.size()); + auto M = Mutators[MutatorIdx]; size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize); if (NewSize && NewSize <= MaxSize) { if (Options.OnlyASCII) ToASCII(Data, NewSize); CurrentMutatorSequence.push_back(M); + CurrentMutatorIdxSequence.push_back(MutatorIdx); + TotalMutations[MutatorIdx]++; return NewSize; } } @@ -528,8 +537,26 @@ } void MutationDispatcher::AddWordToManualDictionary(const Word &W) { - ManualDictionary.push_back( - {W, std::numeric_limits::max()}); + ManualDictionary.push_back({W, std::numeric_limits::max()}); +} + +void MutationDispatcher::CountCurrentMutatorSequence() { + for (const size_t M : CurrentMutatorIdxSequence) + UsefulMutations[M]++; +} + +void MutationDispatcher::PrintMutationStats() { + Printf("\nstat::mutation_usefulness: "); + for (unsigned int i = 0; i < Mutators.size(); i++) { + double UsefulPercentage = + TotalMutations[i] + ? (100.0 * UsefulMutations[i]) / TotalMutations[i] + : 0; + Printf("%.3f", UsefulPercentage); + if (i < Mutators.size() - 1) + Printf(","); + } + Printf("\n"); } } // namespace fuzzer Index: FuzzerOptions.h =================================================================== --- FuzzerOptions.h +++ FuzzerOptions.h @@ -52,6 +52,7 @@ bool PrintNewCovPcs = false; int PrintNewCovFuncs = 0; bool PrintFinalStats = false; + bool PrintMutationStats = false; bool PrintCorpusStats = false; bool PrintCoverage = false; bool DumpCoverage = false;