1 //===-- sancov.cc --------------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file is a command-line tool for reading and analyzing sanitizer
12 //===----------------------------------------------------------------------===//
13 #include "llvm/ADT/STLExtras.h"
14 #include "llvm/ADT/Twine.h"
15 #include "llvm/DebugInfo/Symbolize/Symbolize.h"
16 #include "llvm/MC/MCAsmInfo.h"
17 #include "llvm/MC/MCContext.h"
18 #include "llvm/MC/MCDisassembler/MCDisassembler.h"
19 #include "llvm/MC/MCInst.h"
20 #include "llvm/MC/MCInstPrinter.h"
21 #include "llvm/MC/MCInstrAnalysis.h"
22 #include "llvm/MC/MCInstrInfo.h"
23 #include "llvm/MC/MCObjectFileInfo.h"
24 #include "llvm/MC/MCRegisterInfo.h"
25 #include "llvm/MC/MCSubtargetInfo.h"
26 #include "llvm/Object/Archive.h"
27 #include "llvm/Object/Binary.h"
28 #include "llvm/Object/ELFObjectFile.h"
29 #include "llvm/Object/ObjectFile.h"
30 #include "llvm/Support/Casting.h"
31 #include "llvm/Support/CommandLine.h"
32 #include "llvm/Support/Errc.h"
33 #include "llvm/Support/ErrorOr.h"
34 #include "llvm/Support/FileSystem.h"
35 #include "llvm/Support/LineIterator.h"
36 #include "llvm/Support/MD5.h"
37 #include "llvm/Support/ManagedStatic.h"
38 #include "llvm/Support/MemoryBuffer.h"
39 #include "llvm/Support/Path.h"
40 #include "llvm/Support/PrettyStackTrace.h"
41 #include "llvm/Support/Regex.h"
42 #include "llvm/Support/Signals.h"
43 #include "llvm/Support/SpecialCaseList.h"
44 #include "llvm/Support/TargetRegistry.h"
45 #include "llvm/Support/TargetSelect.h"
46 #include "llvm/Support/ToolOutputFile.h"
47 #include "llvm/Support/raw_ostream.h"
60 // --------- COMMAND LINE FLAGS ---------
65 CoveredFunctionsAction,
66 NotCoveredFunctionsAction,
71 cl::opt<ActionType> Action(
72 cl::desc("Action (required)"), cl::Required,
73 cl::values(clEnumValN(PrintAction, "print", "Print coverage addresses"),
74 clEnumValN(PrintCovPointsAction, "print-coverage-pcs",
75 "Print coverage instrumentation points addresses."),
76 clEnumValN(CoveredFunctionsAction, "covered-functions",
77 "Print all covered funcions."),
78 clEnumValN(NotCoveredFunctionsAction, "not-covered-functions",
79 "Print all not covered funcions."),
80 clEnumValN(HtmlReportAction, "html-report",
81 "Print HTML coverage report."),
82 clEnumValN(StatsAction, "print-coverage-stats",
83 "Print coverage statistics."),
86 static cl::list<std::string>
87 ClInputFiles(cl::Positional, cl::OneOrMore,
88 cl::desc("(<binary file>|<.sancov file>)..."));
90 static cl::opt<bool> ClDemangle("demangle", cl::init(true),
91 cl::desc("Print demangled function name."));
93 static cl::opt<std::string> ClStripPathPrefix(
94 "strip_path_prefix", cl::init(""),
95 cl::desc("Strip this prefix from file paths in reports."));
97 static cl::opt<std::string>
98 ClBlacklist("blacklist", cl::init(""),
99 cl::desc("Blacklist file (sanitizer blacklist format)."));
101 static cl::opt<bool> ClUseDefaultBlacklist(
102 "use_default_blacklist", cl::init(true), cl::Hidden,
103 cl::desc("Controls if default blacklist should be used."));
105 static const char *const DefaultBlacklistStr = "fun:__sanitizer_.*\n"
106 "src:/usr/include/.*\n"
107 "src:.*/libc\\+\\+/.*\n";
109 // --------- FORMAT SPECIFICATION ---------
116 static const uint32_t BinCoverageMagic = 0xC0BFFFFF;
117 static const uint32_t Bitness32 = 0xFFFFFF32;
118 static const uint32_t Bitness64 = 0xFFFFFF64;
120 // --------- ERROR HANDLING ---------
122 static void Fail(const llvm::Twine &E) {
123 errs() << "Error: " << E << "\n";
127 static void FailIfError(std::error_code Error) {
130 errs() << "Error: " << Error.message() << "(" << Error.value() << ")\n";
134 template <typename T> static void FailIfError(const ErrorOr<T> &E) {
135 FailIfError(E.getError());
138 static void FailIfNotEmpty(const llvm::Twine &E) {
144 template <typename T>
145 static void FailIfEmpty(const std::unique_ptr<T> &Ptr,
146 const std::string &Message) {
154 // Produces std::map<K, std::vector<E>> grouping input
155 // elements by FuncTy result.
156 template <class RangeTy, class FuncTy>
157 static inline auto group_by(const RangeTy &R, FuncTy F)
158 -> std::map<typename std::decay<decltype(F(*R.begin()))>::type,
159 std::vector<typename std::decay<decltype(*R.begin())>::type>> {
160 std::map<typename std::decay<decltype(F(*R.begin()))>::type,
161 std::vector<typename std::decay<decltype(*R.begin())>::type>>
163 for (const auto &E : R) {
164 Result[F(E)].push_back(E);
169 template <typename T>
170 static void readInts(const char *Start, const char *End,
171 std::set<uint64_t> *Ints) {
172 const T *S = reinterpret_cast<const T *>(Start);
173 const T *E = reinterpret_cast<const T *>(End);
174 std::copy(S, E, std::inserter(*Ints, Ints->end()));
178 bool operator<(const FileLoc &RHS) const {
179 return std::tie(FileName, Line) < std::tie(RHS.FileName, RHS.Line);
182 std::string FileName;
187 bool operator<(const FileFn &RHS) const {
188 return std::tie(FileName, FunctionName) <
189 std::tie(RHS.FileName, RHS.FunctionName);
192 std::string FileName;
193 std::string FunctionName;
197 bool operator<(const FnLoc &RHS) const {
198 return std::tie(Loc, FunctionName) < std::tie(RHS.Loc, RHS.FunctionName);
202 std::string FunctionName;
205 std::string stripPathPrefix(std::string Path) {
206 if (ClStripPathPrefix.empty())
208 size_t Pos = Path.find(ClStripPathPrefix);
209 if (Pos == std::string::npos)
211 return Path.substr(Pos + ClStripPathPrefix.size());
214 static std::unique_ptr<symbolize::LLVMSymbolizer> createSymbolizer() {
215 symbolize::LLVMSymbolizer::Options SymbolizerOptions;
216 SymbolizerOptions.Demangle = ClDemangle;
217 SymbolizerOptions.UseSymbolTable = true;
218 return std::unique_ptr<symbolize::LLVMSymbolizer>(
219 new symbolize::LLVMSymbolizer(SymbolizerOptions));
222 // A DILineInfo with address.
223 struct AddrInfo : public DILineInfo {
226 AddrInfo(const DILineInfo &DI, uint64_t Addr) : DILineInfo(DI), Addr(Addr) {
227 FileName = normalizeFilename(FileName);
231 static std::string normalizeFilename(std::string FileName) {
232 SmallString<256> S(FileName);
233 sys::path::remove_dots(S, /* remove_dot_dot */ true);
234 return S.str().str();
241 : DefaultBlacklist(createDefaultBlacklist()),
242 UserBlacklist(createUserBlacklist()) {}
244 // AddrInfo contains normalized filename. It is important to check it rather
246 bool isBlacklisted(const AddrInfo &AI) {
247 if (DefaultBlacklist && DefaultBlacklist->inSection("fun", AI.FunctionName))
249 if (DefaultBlacklist && DefaultBlacklist->inSection("src", AI.FileName))
251 if (UserBlacklist && UserBlacklist->inSection("fun", AI.FunctionName))
253 if (UserBlacklist && UserBlacklist->inSection("src", AI.FileName))
259 static std::unique_ptr<SpecialCaseList> createDefaultBlacklist() {
260 if (!ClUseDefaultBlacklist)
261 return std::unique_ptr<SpecialCaseList>();
262 std::unique_ptr<MemoryBuffer> MB =
263 MemoryBuffer::getMemBuffer(DefaultBlacklistStr);
265 auto Blacklist = SpecialCaseList::create(MB.get(), Error);
266 FailIfNotEmpty(Error);
270 static std::unique_ptr<SpecialCaseList> createUserBlacklist() {
271 if (ClBlacklist.empty())
272 return std::unique_ptr<SpecialCaseList>();
274 return SpecialCaseList::createOrDie({{ClBlacklist}});
276 std::unique_ptr<SpecialCaseList> DefaultBlacklist;
277 std::unique_ptr<SpecialCaseList> UserBlacklist;
280 // Collect all debug info for given addresses.
281 static std::vector<AddrInfo> getAddrInfo(std::string ObjectFile,
282 const std::set<uint64_t> &Addrs,
284 std::vector<AddrInfo> Result;
285 auto Symbolizer(createSymbolizer());
288 for (auto Addr : Addrs) {
289 auto LineInfo = Symbolizer->symbolizeCode(ObjectFile, Addr);
290 FailIfError(LineInfo);
291 auto LineAddrInfo = AddrInfo(*LineInfo, Addr);
292 if (B.isBlacklisted(LineAddrInfo))
294 Result.push_back(LineAddrInfo);
296 auto InliningInfo = Symbolizer->symbolizeInlinedCode(ObjectFile, Addr);
297 FailIfError(InliningInfo);
298 for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
299 auto FrameInfo = InliningInfo->getFrame(I);
300 auto FrameAddrInfo = AddrInfo(FrameInfo, Addr);
301 if (B.isBlacklisted(FrameAddrInfo))
303 Result.push_back(FrameAddrInfo);
311 // Locate __sanitizer_cov* function addresses that are used for coverage
313 static std::set<uint64_t>
314 findSanitizerCovFunctions(const object::ObjectFile &O) {
315 std::set<uint64_t> Result;
317 for (const object::SymbolRef &Symbol : O.symbols()) {
318 ErrorOr<uint64_t> AddressOrErr = Symbol.getAddress();
319 FailIfError(AddressOrErr);
321 Expected<StringRef> NameOrErr = Symbol.getName();
322 FailIfError(errorToErrorCode(NameOrErr.takeError()));
323 StringRef Name = NameOrErr.get();
325 if (Name == "__sanitizer_cov" || Name == "__sanitizer_cov_with_check" ||
326 Name == "__sanitizer_cov_trace_func_enter") {
327 if (!(Symbol.getFlags() & object::BasicSymbolRef::SF_Undefined))
328 Result.insert(AddressOrErr.get());
335 // Locate addresses of all coverage points in a file. Coverage point
336 // is defined as the 'address of instruction following __sanitizer_cov
338 static void getObjectCoveragePoints(const object::ObjectFile &O,
339 std::set<uint64_t> *Addrs) {
340 Triple TheTriple("unknown-unknown-unknown");
341 TheTriple.setArch(Triple::ArchType(O.getArch()));
342 auto TripleName = TheTriple.getTriple();
345 const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error);
346 FailIfNotEmpty(Error);
348 std::unique_ptr<const MCSubtargetInfo> STI(
349 TheTarget->createMCSubtargetInfo(TripleName, "", ""));
350 FailIfEmpty(STI, "no subtarget info for target " + TripleName);
352 std::unique_ptr<const MCRegisterInfo> MRI(
353 TheTarget->createMCRegInfo(TripleName));
354 FailIfEmpty(MRI, "no register info for target " + TripleName);
356 std::unique_ptr<const MCAsmInfo> AsmInfo(
357 TheTarget->createMCAsmInfo(*MRI, TripleName));
358 FailIfEmpty(AsmInfo, "no asm info for target " + TripleName);
360 std::unique_ptr<const MCObjectFileInfo> MOFI(new MCObjectFileInfo);
361 MCContext Ctx(AsmInfo.get(), MRI.get(), MOFI.get());
362 std::unique_ptr<MCDisassembler> DisAsm(
363 TheTarget->createMCDisassembler(*STI, Ctx));
364 FailIfEmpty(DisAsm, "no disassembler info for target " + TripleName);
366 std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo());
367 FailIfEmpty(MII, "no instruction info for target " + TripleName);
369 std::unique_ptr<const MCInstrAnalysis> MIA(
370 TheTarget->createMCInstrAnalysis(MII.get()));
371 FailIfEmpty(MIA, "no instruction analysis info for target " + TripleName);
373 auto SanCovAddrs = findSanitizerCovFunctions(O);
374 if (SanCovAddrs.empty())
375 Fail("__sanitizer_cov* functions not found");
377 for (object::SectionRef Section : O.sections()) {
378 if (Section.isVirtual() || !Section.isText()) // llvm-objdump does the same.
380 uint64_t SectionAddr = Section.getAddress();
381 uint64_t SectSize = Section.getSize();
386 FailIfError(Section.getContents(BytesStr));
387 ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()),
390 for (uint64_t Index = 0, Size = 0; Index < Section.getSize();
393 if (!DisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
394 SectionAddr + Index, nulls(), nulls())) {
399 uint64_t Addr = Index + SectionAddr;
400 // Sanitizer coverage uses the address of the next instruction - 1.
401 uint64_t CovPoint = Addr + Size - 1;
403 if (MIA->isCall(Inst) &&
404 MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target) &&
405 SanCovAddrs.find(Target) != SanCovAddrs.end())
406 Addrs->insert(CovPoint);
412 visitObjectFiles(const object::Archive &A,
413 std::function<void(const object::ObjectFile &)> Fn) {
414 for (auto &ErrorOrChild : A.children()) {
415 FailIfError(ErrorOrChild);
416 const object::Archive::Child &C = *ErrorOrChild;
417 Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary();
418 FailIfError(errorToErrorCode(ChildOrErr.takeError()));
419 if (auto *O = dyn_cast<object::ObjectFile>(&*ChildOrErr.get()))
422 FailIfError(object::object_error::invalid_file_type);
427 visitObjectFiles(std::string FileName,
428 std::function<void(const object::ObjectFile &)> Fn) {
429 Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
430 object::createBinary(FileName);
432 FailIfError(errorToErrorCode(BinaryOrErr.takeError()));
434 object::Binary &Binary = *BinaryOrErr.get().getBinary();
435 if (object::Archive *A = dyn_cast<object::Archive>(&Binary))
436 visitObjectFiles(*A, Fn);
437 else if (object::ObjectFile *O = dyn_cast<object::ObjectFile>(&Binary))
440 FailIfError(object::object_error::invalid_file_type);
443 std::set<uint64_t> findSanitizerCovFunctions(std::string FileName) {
444 std::set<uint64_t> Result;
445 visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
446 auto Addrs = findSanitizerCovFunctions(O);
447 Result.insert(Addrs.begin(), Addrs.end());
452 // Locate addresses of all coverage points in a file. Coverage point
453 // is defined as the 'address of instruction following __sanitizer_cov
455 std::set<uint64_t> getCoveragePoints(std::string FileName) {
456 std::set<uint64_t> Result;
457 visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
458 getObjectCoveragePoints(O, &Result);
463 static void printCovPoints(std::string ObjFile, raw_ostream &OS) {
464 for (uint64_t Addr : getCoveragePoints(ObjFile)) {
471 static std::string escapeHtml(const std::string &S) {
473 Result.reserve(S.size());
477 Result.append("&");
480 Result.append("'");
483 Result.append(""");
486 Result.append("<");
489 Result.append(">");
492 Result.push_back(Ch);
499 // Adds leading zeroes wrapped in 'lz' style.
500 // Leading zeroes help locate 000% coverage.
501 static std::string formatHtmlPct(size_t Pct) {
502 Pct = std::max(std::size_t{0}, std::min(std::size_t{100}, Pct));
504 std::string Num = std::to_string(Pct);
505 std::string Zeroes(3 - Num.size(), '0');
507 Zeroes = "<span class='lz'>" + Zeroes + "</span>";
512 static std::string anchorName(std::string Anchor) {
514 llvm::MD5::MD5Result Hash;
515 Hasher.update(Anchor);
518 SmallString<32> HexString;
519 llvm::MD5::stringifyResult(Hash, HexString);
520 return HexString.str().str();
523 static ErrorOr<bool> isCoverageFile(std::string FileName) {
524 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
525 MemoryBuffer::getFile(FileName);
527 errs() << "Warning: " << BufOrErr.getError().message() << "("
528 << BufOrErr.getError().value()
529 << "), filename: " << llvm::sys::path::filename(FileName) << "\n";
530 return BufOrErr.getError();
532 std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
533 if (Buf->getBufferSize() < 8) {
536 const FileHeader *Header =
537 reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
538 return Header->Magic == BinCoverageMagic;
541 struct CoverageStats {
542 CoverageStats() : AllPoints(0), CovPoints(0), AllFns(0), CovFns(0) {}
550 static raw_ostream &operator<<(raw_ostream &OS, const CoverageStats &Stats) {
551 OS << "all-edges: " << Stats.AllPoints << "\n";
552 OS << "cov-edges: " << Stats.CovPoints << "\n";
553 OS << "all-functions: " << Stats.AllFns << "\n";
554 OS << "cov-functions: " << Stats.CovFns << "\n";
560 // Read single file coverage data.
561 static ErrorOr<std::unique_ptr<CoverageData>> read(std::string FileName) {
562 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
563 MemoryBuffer::getFile(FileName);
565 return BufOrErr.getError();
566 std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
567 if (Buf->getBufferSize() < 8) {
568 errs() << "File too small (<8): " << Buf->getBufferSize();
569 return make_error_code(errc::illegal_byte_sequence);
571 const FileHeader *Header =
572 reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
574 if (Header->Magic != BinCoverageMagic) {
575 errs() << "Wrong magic: " << Header->Magic;
576 return make_error_code(errc::illegal_byte_sequence);
579 auto Addrs = llvm::make_unique<std::set<uint64_t>>();
581 switch (Header->Bitness) {
583 readInts<uint64_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
587 readInts<uint32_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
591 errs() << "Unsupported bitness: " << Header->Bitness;
592 return make_error_code(errc::illegal_byte_sequence);
595 return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs)));
598 // Merge multiple coverage data together.
599 static std::unique_ptr<CoverageData>
600 merge(const std::vector<std::unique_ptr<CoverageData>> &Covs) {
601 auto Addrs = llvm::make_unique<std::set<uint64_t>>();
603 for (const auto &Cov : Covs)
604 Addrs->insert(Cov->Addrs->begin(), Cov->Addrs->end());
606 return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs)));
609 // Read list of files and merges their coverage info.
610 static ErrorOr<std::unique_ptr<CoverageData>>
611 readAndMerge(const std::vector<std::string> &FileNames) {
612 std::vector<std::unique_ptr<CoverageData>> Covs;
613 for (const auto &FileName : FileNames) {
614 auto Cov = read(FileName);
616 return Cov.getError();
617 Covs.push_back(std::move(Cov.get()));
622 // Print coverage addresses.
623 void printAddrs(raw_ostream &OS) {
624 for (auto Addr : *Addrs) {
632 explicit CoverageData(std::unique_ptr<std::set<uint64_t>> Addrs)
633 : Addrs(std::move(Addrs)) {}
635 friend class CoverageDataWithObjectFile;
637 std::unique_ptr<std::set<uint64_t>> Addrs;
640 // Coverage data translated into source code line-level information.
641 // Fetches debug info in constructor and calculates various information per
643 class SourceCoverageData {
646 // coverage information for the line is not available.
647 // default value in maps.
649 // the line is fully covered.
651 // the line is fully uncovered.
653 // some points in the line a covered, some are not.
657 SourceCoverageData(std::string ObjectFile, const std::set<uint64_t> &Addrs)
658 : AllCovPoints(getCoveragePoints(ObjectFile)) {
659 if (!std::includes(AllCovPoints.begin(), AllCovPoints.end(), Addrs.begin(),
661 Fail("Coverage points in binary and .sancov file do not match.");
664 AllAddrInfo = getAddrInfo(ObjectFile, AllCovPoints, true);
665 CovAddrInfo = getAddrInfo(ObjectFile, Addrs, true);
668 // Compute number of coverage points hit/total in a file.
669 // file_name -> <coverage, all_coverage>
670 std::map<std::string, std::pair<size_t, size_t>> computeFileCoverage() {
671 std::map<std::string, std::pair<size_t, size_t>> FileCoverage;
672 auto AllCovPointsByFile =
673 group_by(AllAddrInfo, [](const AddrInfo &AI) { return AI.FileName; });
674 auto CovPointsByFile =
675 group_by(CovAddrInfo, [](const AddrInfo &AI) { return AI.FileName; });
677 for (const auto &P : AllCovPointsByFile) {
678 const std::string &FileName = P.first;
680 FileCoverage[FileName] =
681 std::make_pair(CovPointsByFile[FileName].size(),
682 AllCovPointsByFile[FileName].size());
687 // line_number -> line_status.
688 typedef std::map<int, LineStatus> LineStatusMap;
689 // file_name -> LineStatusMap
690 typedef std::map<std::string, LineStatusMap> FileLineStatusMap;
692 // fills in the {file_name -> {line_no -> status}} map.
693 FileLineStatusMap computeLineStatusMap() {
694 FileLineStatusMap StatusMap;
696 auto AllLocs = group_by(AllAddrInfo, [](const AddrInfo &AI) {
697 return FileLoc{AI.FileName, AI.Line};
699 auto CovLocs = group_by(CovAddrInfo, [](const AddrInfo &AI) {
700 return FileLoc{AI.FileName, AI.Line};
703 for (const auto &P : AllLocs) {
704 const FileLoc &Loc = P.first;
705 auto I = CovLocs.find(Loc);
707 if (I == CovLocs.end()) {
708 StatusMap[Loc.FileName][Loc.Line] = NOT_COVERED;
710 StatusMap[Loc.FileName][Loc.Line] =
711 (I->second.size() == P.second.size()) ? COVERED : MIXED;
717 std::set<FileFn> computeAllFunctions() const {
718 std::set<FileFn> Fns;
719 for (const auto &AI : AllAddrInfo) {
720 Fns.insert(FileFn{AI.FileName, AI.FunctionName});
725 std::set<FileFn> computeCoveredFunctions() const {
726 std::set<FileFn> Fns;
727 auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
728 return FileFn{AI.FileName, AI.FunctionName};
731 for (const auto &P : CovFns) {
737 std::set<FileFn> computeNotCoveredFunctions() const {
738 std::set<FileFn> Fns;
740 auto AllFns = group_by(AllAddrInfo, [](const AddrInfo &AI) {
741 return FileFn{AI.FileName, AI.FunctionName};
743 auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
744 return FileFn{AI.FileName, AI.FunctionName};
747 for (const auto &P : AllFns) {
748 if (CovFns.find(P.first) == CovFns.end()) {
755 // Compute % coverage for each function.
756 std::map<FileFn, int> computeFunctionsCoverage() const {
757 std::map<FileFn, int> FnCoverage;
758 auto AllFns = group_by(AllAddrInfo, [](const AddrInfo &AI) {
759 return FileFn{AI.FileName, AI.FunctionName};
762 auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
763 return FileFn{AI.FileName, AI.FunctionName};
766 for (const auto &P : AllFns) {
768 FnCoverage[F] = CovFns[F].size() * 100 / P.second.size();
774 typedef std::map<FileLoc, std::set<std::string>> FunctionLocs;
775 // finds first line number in a file for each function.
776 FunctionLocs resolveFunctions(const std::set<FileFn> &Fns) const {
777 std::vector<AddrInfo> FnAddrs;
778 for (const auto &AI : AllAddrInfo) {
779 if (Fns.find(FileFn{AI.FileName, AI.FunctionName}) != Fns.end())
780 FnAddrs.push_back(AI);
783 auto GroupedAddrs = group_by(FnAddrs, [](const AddrInfo &AI) {
784 return FnLoc{FileLoc{AI.FileName, AI.Line}, AI.FunctionName};
788 std::string LastFileName;
789 std::set<std::string> ProcessedFunctions;
791 for (const auto &P : GroupedAddrs) {
792 const FnLoc &Loc = P.first;
793 std::string FileName = Loc.Loc.FileName;
794 std::string FunctionName = Loc.FunctionName;
796 if (LastFileName != FileName)
797 ProcessedFunctions.clear();
798 LastFileName = FileName;
800 if (!ProcessedFunctions.insert(FunctionName).second)
803 auto FLoc = FileLoc{FileName, Loc.Loc.Line};
804 Result[FLoc].insert(FunctionName);
809 std::set<std::string> files() const {
810 std::set<std::string> Files;
811 for (const auto &AI : AllAddrInfo) {
812 Files.insert(AI.FileName);
817 void collectStats(CoverageStats *Stats) const {
818 Stats->AllPoints += AllCovPoints.size();
819 Stats->AllFns += computeAllFunctions().size();
820 Stats->CovFns += computeCoveredFunctions().size();
824 const std::set<uint64_t> AllCovPoints;
826 std::vector<AddrInfo> AllAddrInfo;
827 std::vector<AddrInfo> CovAddrInfo;
830 static void printFunctionLocs(const SourceCoverageData::FunctionLocs &FnLocs,
832 for (const auto &Fns : FnLocs) {
833 for (const auto &Fn : Fns.second) {
834 OS << stripPathPrefix(Fns.first.FileName) << ":" << Fns.first.Line << " "
840 // Holder for coverage data + filename of corresponding object file.
841 class CoverageDataWithObjectFile : public CoverageData {
843 static ErrorOr<std::unique_ptr<CoverageDataWithObjectFile>>
844 readAndMerge(std::string ObjectFile,
845 const std::vector<std::string> &FileNames) {
846 auto MergedDataOrError = CoverageData::readAndMerge(FileNames);
847 if (!MergedDataOrError)
848 return MergedDataOrError.getError();
849 return std::unique_ptr<CoverageDataWithObjectFile>(
850 new CoverageDataWithObjectFile(ObjectFile,
851 std::move(MergedDataOrError.get())));
854 std::string object_file() const { return ObjectFile; }
856 // Print list of covered functions.
857 // Line format: <file_name>:<line> <function_name>
858 void printCoveredFunctions(raw_ostream &OS) const {
859 SourceCoverageData SCovData(ObjectFile, *Addrs);
860 auto CoveredFns = SCovData.computeCoveredFunctions();
861 printFunctionLocs(SCovData.resolveFunctions(CoveredFns), OS);
864 // Print list of not covered functions.
865 // Line format: <file_name>:<line> <function_name>
866 void printNotCoveredFunctions(raw_ostream &OS) const {
867 SourceCoverageData SCovData(ObjectFile, *Addrs);
868 auto NotCoveredFns = SCovData.computeNotCoveredFunctions();
869 printFunctionLocs(SCovData.resolveFunctions(NotCoveredFns), OS);
872 void printReport(raw_ostream &OS) const {
873 SourceCoverageData SCovData(ObjectFile, *Addrs);
874 auto LineStatusMap = SCovData.computeLineStatusMap();
876 std::set<FileFn> AllFns = SCovData.computeAllFunctions();
877 // file_loc -> set[function_name]
878 auto AllFnsByLoc = SCovData.resolveFunctions(AllFns);
879 auto FileCoverage = SCovData.computeFileCoverage();
881 auto FnCoverage = SCovData.computeFunctionsCoverage();
882 auto FnCoverageByFile =
883 group_by(FnCoverage, [](const std::pair<FileFn, int> &FileFn) {
884 return FileFn.first.FileName;
889 size_t NotCoveredFilesCount = 0;
890 std::set<std::string> Files = SCovData.files();
893 OS << "<details open><summary>Touched Files</summary>\n";
895 OS << "<tr><th>File</th><th>Coverage %</th>";
896 OS << "<th>Hit (Total) Fns</th></tr>\n";
897 for (const auto &FileName : Files) {
898 std::pair<size_t, size_t> FC = FileCoverage[FileName];
900 NotCoveredFilesCount++;
903 size_t CovPct = FC.second == 0 ? 100 : 100 * FC.first / FC.second;
905 OS << "<tr><td><a href=\"#" << anchorName(FileName) << "\">"
906 << stripPathPrefix(FileName) << "</a></td>"
907 << "<td>" << formatHtmlPct(CovPct) << "%</td>"
908 << "<td>" << FC.first << " (" << FC.second << ")"
912 OS << "</details>\n";
914 // Not covered files.
915 if (NotCoveredFilesCount) {
916 OS << "<details><summary>Not Touched Files</summary>\n";
918 for (const auto &FileName : Files) {
919 std::pair<size_t, size_t> FC = FileCoverage[FileName];
921 OS << "<tr><td>" << stripPathPrefix(FileName) << "</td>\n";
924 OS << "</details>\n";
926 OS << "<p>Congratulations! All source files are touched.</p>\n";
930 for (const auto &FileName : Files) {
931 std::pair<size_t, size_t> FC = FileCoverage[FileName];
934 OS << "<a name=\"" << anchorName(FileName) << "\"></a>\n";
935 OS << "<h2>" << stripPathPrefix(FileName) << "</h2>\n";
936 OS << "<details open><summary>Function Coverage</summary>";
937 OS << "<div class='fnlist'>\n";
939 auto &FileFnCoverage = FnCoverageByFile[FileName];
941 for (const auto &P : FileFnCoverage) {
942 std::string FunctionName = P.first.FunctionName;
944 OS << "<div class='fn' style='order: " << P.second << "'>";
945 OS << "<span class='pct'>" << formatHtmlPct(P.second)
947 OS << "<span class='name'><a href=\"#"
948 << anchorName(FileName + "::" + FunctionName) << "\">";
949 OS << escapeHtml(FunctionName) << "</a></span>";
952 OS << "</div></details>\n";
954 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
955 MemoryBuffer::getFile(FileName);
957 OS << "Error reading file: " << FileName << " : "
958 << BufOrErr.getError().message() << "("
959 << BufOrErr.getError().value() << ")\n";
964 const auto &LineStatuses = LineStatusMap[FileName];
965 for (line_iterator I = line_iterator(*BufOrErr.get(), false);
966 !I.is_at_eof(); ++I) {
967 uint32_t Line = I.line_number();
968 { // generate anchors (if any);
969 FileLoc Loc = FileLoc{FileName, Line};
970 auto It = AllFnsByLoc.find(Loc);
971 if (It != AllFnsByLoc.end()) {
972 for (const std::string &Fn : It->second) {
973 OS << "<a name=\"" << anchorName(FileName + "::" + Fn)
980 auto LIT = LineStatuses.find(I.line_number());
981 auto Status = (LIT != LineStatuses.end()) ? LIT->second
982 : SourceCoverageData::UNKNOWN;
984 case SourceCoverageData::UNKNOWN:
985 OS << "class=unknown";
987 case SourceCoverageData::COVERED:
988 OS << "class=covered";
990 case SourceCoverageData::NOT_COVERED:
991 OS << "class=notcovered";
993 case SourceCoverageData::MIXED:
998 OS << escapeHtml(*I) << "</span>\n";
1004 void collectStats(CoverageStats *Stats) const {
1005 Stats->CovPoints += Addrs->size();
1007 SourceCoverageData SCovData(ObjectFile, *Addrs);
1008 SCovData.collectStats(Stats);
1012 CoverageDataWithObjectFile(std::string ObjectFile,
1013 std::unique_ptr<CoverageData> Coverage)
1014 : CoverageData(std::move(Coverage->Addrs)),
1015 ObjectFile(std::move(ObjectFile)) {}
1016 const std::string ObjectFile;
1019 // Multiple coverage files data organized by object file.
1020 class CoverageDataSet {
1022 static ErrorOr<std::unique_ptr<CoverageDataSet>>
1023 readCmdArguments(std::vector<std::string> FileNames) {
1024 // Short name => file name.
1025 std::map<std::string, std::string> ObjFiles;
1026 std::string FirstObjFile;
1027 std::set<std::string> CovFiles;
1029 // Partition input values into coverage/object files.
1030 for (const auto &FileName : FileNames) {
1031 auto ErrorOrIsCoverage = isCoverageFile(FileName);
1032 if (!ErrorOrIsCoverage)
1034 if (ErrorOrIsCoverage.get()) {
1035 CovFiles.insert(FileName);
1037 auto ShortFileName = llvm::sys::path::filename(FileName);
1038 if (ObjFiles.find(ShortFileName) != ObjFiles.end()) {
1039 Fail("Duplicate binary file with a short name: " + ShortFileName);
1042 ObjFiles[ShortFileName] = FileName;
1043 if (FirstObjFile.empty())
1044 FirstObjFile = FileName;
1048 Regex SancovRegex("(.*)\\.[0-9]+\\.sancov");
1049 SmallVector<StringRef, 2> Components;
1051 // Object file => list of corresponding coverage file names.
1052 auto CoverageByObjFile = group_by(CovFiles, [&](std::string FileName) {
1053 auto ShortFileName = llvm::sys::path::filename(FileName);
1054 auto Ok = SancovRegex.match(ShortFileName, &Components);
1056 Fail("Can't match coverage file name against "
1057 "<module_name>.<pid>.sancov pattern: " +
1061 auto Iter = ObjFiles.find(Components[1]);
1062 if (Iter == ObjFiles.end()) {
1063 Fail("Object file for coverage not found: " + FileName);
1065 return Iter->second;
1069 std::vector<std::unique_ptr<CoverageDataWithObjectFile>> MergedCoverage;
1070 for (const auto &Pair : CoverageByObjFile) {
1071 if (findSanitizerCovFunctions(Pair.first).empty()) {
1072 for (const auto &FileName : Pair.second) {
1073 CovFiles.erase(FileName);
1077 << "Ignoring " << Pair.first
1078 << " and its coverage because __sanitizer_cov* functions were not "
1084 CoverageDataWithObjectFile::readAndMerge(Pair.first, Pair.second);
1085 FailIfError(DataOrError);
1086 MergedCoverage.push_back(std::move(DataOrError.get()));
1089 return std::unique_ptr<CoverageDataSet>(
1090 new CoverageDataSet(FirstObjFile, &MergedCoverage, CovFiles));
1093 void printCoveredFunctions(raw_ostream &OS) const {
1094 for (const auto &Cov : Coverage) {
1095 Cov->printCoveredFunctions(OS);
1099 void printNotCoveredFunctions(raw_ostream &OS) const {
1100 for (const auto &Cov : Coverage) {
1101 Cov->printNotCoveredFunctions(OS);
1105 void printStats(raw_ostream &OS) const {
1106 CoverageStats Stats;
1107 for (const auto &Cov : Coverage) {
1108 Cov->collectStats(&Stats);
1113 void printReport(raw_ostream &OS) const {
1115 (llvm::sys::path::filename(MainObjFile) + " Coverage Report").str();
1122 OS << ".covered { background: #7F7; }\n";
1123 OS << ".notcovered { background: #F77; }\n";
1124 OS << ".mixed { background: #FF7; }\n";
1125 OS << "summary { font-weight: bold; }\n";
1126 OS << "details > summary + * { margin-left: 1em; }\n";
1127 OS << ".fnlist { display: flex; flex-flow: column nowrap; }\n";
1128 OS << ".fn { display: flex; flex-flow: row nowrap; }\n";
1129 OS << ".pct { width: 3em; text-align: right; margin-right: 1em; }\n";
1130 OS << ".name { flex: 2; }\n";
1131 OS << ".lz { color: lightgray; }\n";
1133 OS << "<title>" << Title << "</title>\n";
1138 OS << "<h1>" << Title << "</h1>\n";
1141 if (Coverage.size() > 1) {
1142 for (const auto &CovData : Coverage) {
1143 OS << "<li><a href=\"#module_" << anchorName(CovData->object_file())
1144 << "\">" << llvm::sys::path::filename(CovData->object_file())
1149 for (const auto &CovData : Coverage) {
1150 if (Coverage.size() > 1) {
1151 OS << "<h2>" << llvm::sys::path::filename(CovData->object_file())
1154 OS << "<a name=\"module_" << anchorName(CovData->object_file())
1156 CovData->printReport(OS);
1160 OS << "<details><summary>About</summary>\n";
1161 OS << "Coverage files:<ul>";
1162 for (const auto &InputFile : CoverageFiles) {
1163 llvm::sys::fs::file_status Status;
1164 llvm::sys::fs::status(InputFile, Status);
1165 OS << "<li>" << stripPathPrefix(InputFile) << " ("
1166 << Status.getLastModificationTime().str() << ")</li>\n";
1168 OS << "</ul></details>\n";
1174 bool empty() const { return Coverage.empty(); }
1177 explicit CoverageDataSet(
1178 const std::string &MainObjFile,
1179 std::vector<std::unique_ptr<CoverageDataWithObjectFile>> *Data,
1180 const std::set<std::string> &CoverageFiles)
1181 : MainObjFile(MainObjFile), CoverageFiles(CoverageFiles) {
1182 Data->swap(this->Coverage);
1185 const std::string MainObjFile;
1186 std::vector<std::unique_ptr<CoverageDataWithObjectFile>> Coverage;
1187 const std::set<std::string> CoverageFiles;
1192 int main(int argc, char **argv) {
1193 // Print stack trace if we signal out.
1194 sys::PrintStackTraceOnErrorSignal();
1195 PrettyStackTraceProgram X(argc, argv);
1196 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
1198 llvm::InitializeAllTargetInfos();
1199 llvm::InitializeAllTargetMCs();
1200 llvm::InitializeAllDisassemblers();
1202 cl::ParseCommandLineOptions(argc, argv, "Sanitizer Coverage Processing Tool");
1204 // -print doesn't need object files.
1205 if (Action == PrintAction) {
1206 auto CovData = CoverageData::readAndMerge(ClInputFiles);
1207 FailIfError(CovData);
1208 CovData.get()->printAddrs(outs());
1210 } else if (Action == PrintCovPointsAction) {
1211 // -print-coverage-points doesn't need coverage files.
1212 for (const std::string &ObjFile : ClInputFiles) {
1213 printCovPoints(ObjFile, outs());
1218 auto CovDataSet = CoverageDataSet::readCmdArguments(ClInputFiles);
1219 FailIfError(CovDataSet);
1221 if (CovDataSet.get()->empty()) {
1222 Fail("No coverage files specified.");
1226 case CoveredFunctionsAction: {
1227 CovDataSet.get()->printCoveredFunctions(outs());
1230 case NotCoveredFunctionsAction: {
1231 CovDataSet.get()->printNotCoveredFunctions(outs());
1234 case HtmlReportAction: {
1235 CovDataSet.get()->printReport(outs());
1239 CovDataSet.get()->printStats(outs());
1243 case PrintCovPointsAction:
1244 llvm_unreachable("unsupported action");