diff --git a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp --- a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp @@ -321,7 +321,12 @@ if (MaxLen && MaxLen < U.size()) U.resize(MaxLen); F->ExecuteCallback(U.data(), U.size()); - F->TryDetectingAMemoryLeak(U.data(), U.size(), true); + if (Flags.print_full_coverage) { + // Leak detection is not needed when collecting full coverage data. + F->TPCUpdateObservedPCs(); + } else { + F->TryDetectingAMemoryLeak(U.data(), U.size(), true); + } return 0; } @@ -743,6 +748,7 @@ Options.PrintFinalStats = Flags.print_final_stats; Options.PrintCorpusStats = Flags.print_corpus_stats; Options.PrintCoverage = Flags.print_coverage; + Options.PrintFullCoverage = Flags.print_full_coverage; if (Flags.exit_on_src_pos) Options.ExitOnSrcPos = Flags.exit_on_src_pos; if (Flags.exit_on_item) diff --git a/compiler-rt/lib/fuzzer/FuzzerFlags.def b/compiler-rt/lib/fuzzer/FuzzerFlags.def --- a/compiler-rt/lib/fuzzer/FuzzerFlags.def +++ b/compiler-rt/lib/fuzzer/FuzzerFlags.def @@ -132,6 +132,8 @@ "If 1, print statistics on corpus elements at exit.") FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text" " at exit.") +FUZZER_FLAG_INT(print_full_coverage, 0, "If 1, print full coverage information " + "(all branches) as text at exit.") FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated.") FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.") diff --git a/compiler-rt/lib/fuzzer/FuzzerInternal.h b/compiler-rt/lib/fuzzer/FuzzerInternal.h --- a/compiler-rt/lib/fuzzer/FuzzerInternal.h +++ b/compiler-rt/lib/fuzzer/FuzzerInternal.h @@ -69,6 +69,7 @@ bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false, InputInfo *II = nullptr, bool ForceAddToCorpus = false, bool *FoundUniqFeatures = nullptr); + void TPCUpdateObservedPCs(); // Merge Corpora[1:] into Corpora[0]. void Merge(const Vector &Corpora); diff --git a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp --- a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp @@ -354,8 +354,10 @@ } void Fuzzer::PrintFinalStats() { + if (Options.PrintFullCoverage) + TPC.PrintCoverage(/*PrintAllCounters=*/true); if (Options.PrintCoverage) - TPC.PrintCoverage(); + TPC.PrintCoverage(/*PrintAllCounters=*/false); if (Options.PrintCorpusStats) Corpus.PrintStats(); if (!Options.PrintFinalStats) @@ -545,6 +547,8 @@ return false; } +void Fuzzer::TPCUpdateObservedPCs() { TPC.UpdateObservedPCs(); } + size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { assert(InFuzzingThread()); *Data = CurrentUnitData; diff --git a/compiler-rt/lib/fuzzer/FuzzerOptions.h b/compiler-rt/lib/fuzzer/FuzzerOptions.h --- a/compiler-rt/lib/fuzzer/FuzzerOptions.h +++ b/compiler-rt/lib/fuzzer/FuzzerOptions.h @@ -68,6 +68,7 @@ bool PrintFinalStats = false; bool PrintCorpusStats = false; bool PrintCoverage = false; + bool PrintFullCoverage = false; bool DumpCoverage = false; bool DetectLeaks = true; int PurgeAllocatorIntervalSec = 1; diff --git a/compiler-rt/lib/fuzzer/FuzzerTracePC.h b/compiler-rt/lib/fuzzer/FuzzerTracePC.h --- a/compiler-rt/lib/fuzzer/FuzzerTracePC.h +++ b/compiler-rt/lib/fuzzer/FuzzerTracePC.h @@ -94,7 +94,7 @@ void PrintModuleInfo(); - void PrintCoverage(); + void PrintCoverage(bool PrintAllCounters); template void IterateCoveredFunctions(CallBack CB); diff --git a/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp b/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp --- a/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp @@ -269,7 +269,7 @@ return FocusFunctionCounterPtr && *FocusFunctionCounterPtr; } -void TracePC::PrintCoverage() { +void TracePC::PrintCoverage(bool PrintAllCounters) { if (!EF->__sanitizer_symbolize_pc || !EF->__sanitizer_get_module_and_offset_for_pc) { Printf("INFO: __sanitizer_symbolize_pc or " @@ -277,7 +277,7 @@ " not printing coverage\n"); return; } - Printf("COVERAGE:\n"); + Printf(PrintAllCounters ? "FULL COVERAGE:\n" : "COVERAGE:\n"); auto CoveredFunctionCallback = [&](const PCTableEntry *First, const PCTableEntry *Last, uintptr_t Counter) { @@ -292,17 +292,33 @@ std::string LineStr = DescribePC("%l", VisualizePC); size_t NumEdges = Last - First; Vector UncoveredPCs; + Vector CoveredPCs; for (auto TE = First; TE < Last; TE++) if (!ObservedPCs.count(TE)) UncoveredPCs.push_back(TE->PC); - Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter); - Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges); - Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(), - LineStr.c_str()); - if (Counter) + else + CoveredPCs.push_back(TE->PC); + + if (PrintAllCounters) { + Printf("U"); for (auto PC : UncoveredPCs) - Printf(" UNCOVERED_PC: %s\n", - DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str()); + Printf(DescribePC(" %l", GetNextInstructionPc(PC)).c_str()); + Printf("\n"); + + Printf("C"); + for (auto PC : CoveredPCs) + Printf(DescribePC(" %l", GetNextInstructionPc(PC)).c_str()); + Printf("\n"); + } else { + Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter); + Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges); + Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(), + LineStr.c_str()); + if (Counter) + for (auto PC : UncoveredPCs) + Printf(" UNCOVERED_PC: %s\n", + DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str()); + } }; IterateCoveredFunctions(CoveredFunctionCallback); diff --git a/compiler-rt/test/fuzzer/dso-cov-input.txt b/compiler-rt/test/fuzzer/dso-cov-input.txt new file mode 100644 --- /dev/null +++ b/compiler-rt/test/fuzzer/dso-cov-input.txt @@ -0,0 +1 @@ +123457 \ No newline at end of file diff --git a/compiler-rt/test/fuzzer/full-coverage.test b/compiler-rt/test/fuzzer/full-coverage.test new file mode 100644 --- /dev/null +++ b/compiler-rt/test/fuzzer/full-coverage.test @@ -0,0 +1,15 @@ +# FIXME: Disabled on Windows because -fPIC cannot be used to compile for Windows. +UNSUPPORTED: windows +XFAIL: s390x +RUN: %cpp_compiler %S/DSO1.cpp -fPIC %ld_flags_rpath_so1 -O0 -shared -o %dynamiclib1 +RUN: %cpp_compiler %S/DSO2.cpp -fPIC %ld_flags_rpath_so2 -O0 -shared -o %dynamiclib2 +RUN: %cpp_compiler %S/DSOTestMain.cpp %S/DSOTestExtra.cpp %ld_flags_rpath_exe1 %ld_flags_rpath_exe2 -o %t-DSOTest + +RUN: %run %t-DSOTest -print_full_coverage=1 %S/dso-cov-input.txt 2>&1 | FileCheck %s +CHECK: FULL COVERAGE: +CHECK-DAG: U{{( [0-9]+)*}} +CHECK-DAG: C{{( [0-9]+)*}} +CHECK-DAG: U{{( [0-9]+)*}} +CHECK-DAG: U{{( [0-9]+)*}} +CHECK-DAG: C{{( [0-9]+)*}} +CHECK-DAG: U{{( [0-9]+)*}}