void TracePC::PrintCoverage() { if (!EF->__sanitizer_symbolize_pc || !EF->__sanitizer_get_module_and_offset_for_pc) { Printf("INFO: __sanitizer_symbolize_pc or " "__sanitizer_get_module_and_offset_for_pc is not available," " not printing coverage\n"); return; } Printf("COVERAGE:\n"); std::string LastFunctionName = ""; std::string LastFileStr = ""; std::set<size_t> UncoveredLines; std::set<size_t> CoveredLines; 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); } } 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::PrintCoverage() { if (!EF->__sanitizer_symbolize_pc || !EF->__sanitizer_get_module_and_offset_for_pc) { Printf("INFO: __sanitizer_symbolize_pc or " "__sanitizer_get_module_and_offset_for_pc is not available," " not printing coverage\n"); return; } std::map<std::string, std::vector<uintptr_t>> CoveredPCsPerModule; std::map<std::string, uintptr_t> ModuleOffsets; std::set<std::string> CoveredDirs, CoveredFiles, CoveredFunctions, CoveredLines; Printf("COVERAGE:\n"); for (size_t i = 1; i < GetNumPCs(); i++) { if (!PCs[i]) continue; std::string FileStr = DescribePC("%s", PCs[i]); if (!IsInterestingCoverageFile(FileStr)) continue; std::string FixedPCStr = DescribePC("%p", PCs[i]); std::string FunctionStr = DescribePC("%F", PCs[i]); std::string LineStr = DescribePC("%l", PCs[i]); char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? void *OffsetRaw = nullptr; if (!EF->__sanitizer_get_module_and_offset_for_pc( reinterpret_cast<void *>(PCs[i]), ModulePathRaw, sizeof(ModulePathRaw), &OffsetRaw)) continue; std::string Module = ModulePathRaw; uintptr_t FixedPC = std::stol(FixedPCStr, 0, 16); uintptr_t PcOffset = reinterpret_cast<uintptr_t>(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 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<std::string> UncoveredFiles, UncoveredFunctions; std::map<std::string, std::set<int> > 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 = "objdump -d " + ModuleName + " | grep 'call.*__sanitizer_cov_trace_pc_guard' | awk -F: '{print $1}'"; 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')) { uintptr_t PcOffset = std::stol(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 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()); } }