diff --git a/lib/Fuzzer/FuzzerTracePC.cpp b/lib/Fuzzer/FuzzerTracePC.cpp index 45deaa21a4c..d42bfc253fd 100644 --- a/lib/Fuzzer/FuzzerTracePC.cpp +++ b/lib/Fuzzer/FuzzerTracePC.cpp @@ -174,6 +174,28 @@ void TracePC::UpdateObservedPCs() { } } +inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { + // TODO: this implementation is x86 only. + // see sanitizer_common GetPreviousInstructionPc for full implementation. + return PC - 1; +} + +inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) { + // TODO: this implementation is x86 only. + // see sanitizer_common GetPreviousInstructionPc for full implementation. + return PC + 1; +} + +static std::string GetModuleName(uintptr_t PC) { + char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? + void *OffsetRaw = nullptr; + if (!EF->__sanitizer_get_module_and_offset_for_pc( + reinterpret_cast(PC), ModulePathRaw, + sizeof(ModulePathRaw), &OffsetRaw)) + return ""; + return ModulePathRaw; +} + void TracePC::PrintCoverage() { if (!EF->__sanitizer_symbolize_pc || !EF->__sanitizer_get_module_and_offset_for_pc) { @@ -182,107 +204,54 @@ void TracePC::PrintCoverage() { " not printing coverage\n"); return; } - std::map> CoveredPCsPerModule; - std::map ModuleOffsets; - std::set CoveredDirs, CoveredFiles, CoveredFunctions, - CoveredLines; Printf("COVERAGE:\n"); - for (size_t i = 1; i < GetNumPCs(); i++) { - uintptr_t PC = PCs()[i]; - if (!PC) continue; - std::string FileStr = DescribePC("%s", PC); - if (!IsInterestingCoverageFile(FileStr)) continue; - std::string FixedPCStr = DescribePC("%p", PC); - std::string FunctionStr = DescribePC("%F", PC); - std::string LineStr = DescribePC("%l", PC); - char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? - void *OffsetRaw = nullptr; - if (!EF->__sanitizer_get_module_and_offset_for_pc( - reinterpret_cast(PC), ModulePathRaw, - sizeof(ModulePathRaw), &OffsetRaw)) - continue; - std::string Module = ModulePathRaw; - uintptr_t FixedPC = std::stoull(FixedPCStr, 0, 16); - uintptr_t PcOffset = reinterpret_cast(OffsetRaw); - ModuleOffsets[Module] = FixedPC - PcOffset; - CoveredPCsPerModule[Module].push_back(PcOffset); - CoveredFunctions.insert(FunctionStr); - CoveredFiles.insert(FileStr); - CoveredDirs.insert(DirName(FileStr)); - if (!CoveredLines.insert(FileStr + ":" + LineStr).second) - continue; - Printf("COVERED: %s %s:%s\n", FunctionStr.c_str(), - FileStr.c_str(), LineStr.c_str()); - } + std::string LastFunctionName = ""; + std::string LastFileStr = ""; + std::set UncoveredLines; + std::set CoveredLines; - std::string CoveredDirsStr; - for (auto &Dir : CoveredDirs) { - if (!CoveredDirsStr.empty()) - CoveredDirsStr += ","; - CoveredDirsStr += Dir; - } - Printf("COVERED_DIRS: %s\n", CoveredDirsStr.c_str()); - - for (auto &M : CoveredPCsPerModule) { - std::set UncoveredFiles, UncoveredFunctions; - std::map > UncoveredLines; // Func+File => lines - auto &ModuleName = M.first; - auto &CoveredOffsets = M.second; - uintptr_t ModuleOffset = ModuleOffsets[ModuleName]; - std::sort(CoveredOffsets.begin(), CoveredOffsets.end()); - Printf("MODULE_WITH_COVERAGE: %s\n", ModuleName.c_str()); - // sancov does not yet fully support DSOs. - // std::string Cmd = "sancov -print-coverage-pcs " + ModuleName; - std::string Cmd = DisassembleCmd(ModuleName) + " | " + - SearchRegexCmd("call.*__sanitizer_cov_trace_pc_guard"); - std::string SanCovOutput; - if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) { - Printf("INFO: Command failed: %s\n", Cmd.c_str()); - continue; - } - std::istringstream ISS(SanCovOutput); - std::string S; - while (std::getline(ISS, S, '\n')) { - size_t PcOffsetEnd = S.find(':'); - if (PcOffsetEnd == std::string::npos) - continue; - S.resize(PcOffsetEnd); - uintptr_t PcOffset = std::stoull(S, 0, 16); - if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(), - PcOffset)) { - uintptr_t PC = ModuleOffset + PcOffset; - auto FileStr = DescribePC("%s", PC); - if (!IsInterestingCoverageFile(FileStr)) continue; - if (CoveredFiles.count(FileStr) == 0) { - UncoveredFiles.insert(FileStr); - continue; + auto FunctionEndCallback = [&](const std::string &CurrentFunc, + const std::string &CurrentFile) { + if (LastFunctionName != CurrentFunc) { + if (CoveredLines.empty() && !UncoveredLines.empty()) { + Printf("UNCOVERED_FUNC: %s\n", LastFunctionName.c_str()); + } else { + for (auto Line : UncoveredLines) { + if (!CoveredLines.count(Line)) + Printf("UNCOVERED_LINE: %s %s:%zd\n", LastFunctionName.c_str(), + LastFileStr.c_str(), Line); } - auto FunctionStr = DescribePC("%F", PC); - if (CoveredFunctions.count(FunctionStr) == 0) { - UncoveredFunctions.insert(FunctionStr); - continue; - } - std::string LineStr = DescribePC("%l", PC); - uintptr_t Line = std::stoi(LineStr); - std::string FileLineStr = FileStr + ":" + LineStr; - if (CoveredLines.count(FileLineStr) == 0) - UncoveredLines[FunctionStr + " " + FileStr].insert(Line); } - } - for (auto &FileLine: UncoveredLines) - for (int Line : FileLine.second) - Printf("UNCOVERED_LINE: %s:%d\n", FileLine.first.c_str(), Line); - for (auto &Func : UncoveredFunctions) - Printf("UNCOVERED_FUNC: %s\n", Func.c_str()); - for (auto &File : UncoveredFiles) - Printf("UNCOVERED_FILE: %s\n", File.c_str()); - } -} -inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { - // TODO: this implementation is x86 only. - // see sanitizer_common GetPreviousInstructionPc for full implementation. - return PC - 1; + UncoveredLines.clear(); + CoveredLines.clear(); + LastFunctionName = CurrentFunc; + LastFileStr = CurrentFile; + } + }; + + for (size_t i = 0; i < NumPCTables; i++) { + auto &M = ModulePCTable[i]; + assert(M.Start < M.Stop); + auto ModuleName = GetModuleName(*M.Start); + for (auto Ptr = M.Start; Ptr < M.Stop; Ptr++) { + auto PC = *Ptr; + auto VisualizePC = GetNextInstructionPc(PC); + bool IsObserved = ObservedPCs->count(PC); + std::string FileStr = DescribePC("%s", VisualizePC); + if (!IsInterestingCoverageFile(FileStr)) continue; + std::string FunctionStr = DescribePC("%F", VisualizePC); + FunctionEndCallback(FunctionStr, FileStr); + std::string LineStr = DescribePC("%l", VisualizePC); + size_t Line = std::stoul(LineStr); + if (IsObserved && CoveredLines.insert(Line).second) + Printf("COVERED: %s %s:%zd\n", FunctionStr.c_str(), FileStr.c_str(), + Line); + else + UncoveredLines.insert(Line); + } + } + FunctionEndCallback("", ""); } void TracePC::DumpCoverage() { diff --git a/lib/Fuzzer/test/coverage.test b/lib/Fuzzer/test/coverage.test index f0db3aaa759..ebaea19475e 100644 --- a/lib/Fuzzer/test/coverage.test +++ b/lib/Fuzzer/test/coverage.test @@ -1,16 +1,14 @@ XFAIL: darwin -RUN: %cpp_compiler %S/NullDerefTest.cpp -o %t-NullDerefTest -RUN: %cpp_compiler %S/DSO1.cpp -fPIC -shared -o %t-DSO1.so -RUN: %cpp_compiler %S/DSO2.cpp -fPIC -shared -o %t-DSO2.so -RUN: %cpp_compiler %S/DSOTestMain.cpp %S/DSOTestExtra.cpp -L. %t-DSO1.so %t-DSO2.so -o %t-DSOTest +RUN: %cpp_compiler -mllvm -use-unknown-locations=Disable %S/NullDerefTest.cpp -o %t-NullDerefTest +RUN: %cpp_compiler -mllvm -use-unknown-locations=Disable %S/DSO1.cpp -fPIC -shared -o %t-DSO1.so +RUN: %cpp_compiler -mllvm -use-unknown-locations=Disable %S/DSO2.cpp -fPIC -shared -o %t-DSO2.so +RUN: %cpp_compiler -mllvm -use-unknown-locations=Disable %S/DSOTestMain.cpp %S/DSOTestExtra.cpp -L. %t-DSO1.so %t-DSO2.so -o %t-DSOTest CHECK: COVERAGE: CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:13 CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:14 CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:16 -CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:19 -CHECK: COVERED_DIRS: {{.*}}lib{{[/\\]}}Fuzzer{{[/\\]}}test RUN: not %t-NullDerefTest -print_coverage=1 2>&1 | FileCheck %s RUN: %t-DSOTest -print_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO @@ -23,4 +21,3 @@ DSO-DAG: UNCOVERED_LINE:{{.*}}DSO2{{.*}}DSO2.cpp DSO-DAG: UNCOVERED_FUNC: in Uncovered1 DSO-DAG: UNCOVERED_FUNC: in Uncovered2 DSO-DAG: UNCOVERED_LINE: in LLVMFuzzerTestOneInput -DSO-DAG: UNCOVERED_FILE:{{.*}}DSOTestExtra.cpp