1 //===-- sancov.cc --------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is a command-line tool for reading and analyzing sanitizer
11 // coverage.
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"
48 
49 #include <algorithm>
50 #include <set>
51 #include <stdio.h>
52 #include <string>
53 #include <utility>
54 #include <vector>
55 
56 using namespace llvm;
57 
58 namespace {
59 
60 // --------- COMMAND LINE FLAGS ---------
61 
62 enum ActionType {
63   PrintAction,
64   PrintCovPointsAction,
65   CoveredFunctionsAction,
66   NotCoveredFunctionsAction,
67   HtmlReportAction,
68   StatsAction
69 };
70 
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."),
84                clEnumValEnd));
85 
86 static cl::list<std::string>
87     ClInputFiles(cl::Positional, cl::OneOrMore,
88                  cl::desc("(<binary file>|<.sancov file>)..."));
89 
90 static cl::opt<bool> ClDemangle("demangle", cl::init(true),
91                                 cl::desc("Print demangled function name."));
92 
93 static cl::opt<std::string> ClStripPathPrefix(
94     "strip_path_prefix", cl::init(""),
95     cl::desc("Strip this prefix from file paths in reports."));
96 
97 static cl::opt<std::string>
98     ClBlacklist("blacklist", cl::init(""),
99                 cl::desc("Blacklist file (sanitizer blacklist format)."));
100 
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."));
104 
105 static const char *const DefaultBlacklistStr = "fun:__sanitizer_.*\n"
106                                                "src:/usr/include/.*\n"
107                                                "src:.*/libc\\+\\+/.*\n";
108 
109 // --------- FORMAT SPECIFICATION ---------
110 
111 struct FileHeader {
112   uint32_t Bitness;
113   uint32_t Magic;
114 };
115 
116 static const uint32_t BinCoverageMagic = 0xC0BFFFFF;
117 static const uint32_t Bitness32 = 0xFFFFFF32;
118 static const uint32_t Bitness64 = 0xFFFFFF64;
119 
120 // --------- ERROR HANDLING ---------
121 
Fail(const llvm::Twine & E)122 static void Fail(const llvm::Twine &E) {
123   errs() << "Error: " << E << "\n";
124   exit(1);
125 }
126 
FailIfError(std::error_code Error)127 static void FailIfError(std::error_code Error) {
128   if (!Error)
129     return;
130   errs() << "Error: " << Error.message() << "(" << Error.value() << ")\n";
131   exit(1);
132 }
133 
FailIfError(const ErrorOr<T> & E)134 template <typename T> static void FailIfError(const ErrorOr<T> &E) {
135   FailIfError(E.getError());
136 }
137 
FailIfError(Error Err)138 static void FailIfError(Error Err) {
139   if (Err) {
140     logAllUnhandledErrors(std::move(Err), errs(), "Error: ");
141     exit(1);
142   }
143 }
144 
FailIfError(Expected<T> & E)145 template <typename T> static void FailIfError(Expected<T> &E) {
146   FailIfError(E.takeError());
147 }
148 
FailIfNotEmpty(const llvm::Twine & E)149 static void FailIfNotEmpty(const llvm::Twine &E) {
150   if (E.str().empty())
151     return;
152   Fail(E);
153 }
154 
155 template <typename T>
FailIfEmpty(const std::unique_ptr<T> & Ptr,const std::string & Message)156 static void FailIfEmpty(const std::unique_ptr<T> &Ptr,
157                         const std::string &Message) {
158   if (Ptr.get())
159     return;
160   Fail(Message);
161 }
162 
163 // ---------
164 
165 // Produces std::map<K, std::vector<E>> grouping input
166 // elements by FuncTy result.
167 template <class RangeTy, class FuncTy>
group_by(const RangeTy & R,FuncTy F)168 static inline auto group_by(const RangeTy &R, FuncTy F)
169     -> std::map<typename std::decay<decltype(F(*R.begin()))>::type,
170                 std::vector<typename std::decay<decltype(*R.begin())>::type>> {
171   std::map<typename std::decay<decltype(F(*R.begin()))>::type,
172            std::vector<typename std::decay<decltype(*R.begin())>::type>>
173       Result;
174   for (const auto &E : R) {
175     Result[F(E)].push_back(E);
176   }
177   return Result;
178 }
179 
180 template <typename T>
readInts(const char * Start,const char * End,std::set<uint64_t> * Ints)181 static void readInts(const char *Start, const char *End,
182                      std::set<uint64_t> *Ints) {
183   const T *S = reinterpret_cast<const T *>(Start);
184   const T *E = reinterpret_cast<const T *>(End);
185   std::copy(S, E, std::inserter(*Ints, Ints->end()));
186 }
187 
188 struct FileLoc {
operator <__anon15e54a520111::FileLoc189   bool operator<(const FileLoc &RHS) const {
190     return std::tie(FileName, Line) < std::tie(RHS.FileName, RHS.Line);
191   }
192 
193   std::string FileName;
194   uint32_t Line;
195 };
196 
197 struct FileFn {
operator <__anon15e54a520111::FileFn198   bool operator<(const FileFn &RHS) const {
199     return std::tie(FileName, FunctionName) <
200            std::tie(RHS.FileName, RHS.FunctionName);
201   }
202 
203   std::string FileName;
204   std::string FunctionName;
205 };
206 
207 struct FnLoc {
operator <__anon15e54a520111::FnLoc208   bool operator<(const FnLoc &RHS) const {
209     return std::tie(Loc, FunctionName) < std::tie(RHS.Loc, RHS.FunctionName);
210   }
211 
212   FileLoc Loc;
213   std::string FunctionName;
214 };
215 
stripPathPrefix(std::string Path)216 std::string stripPathPrefix(std::string Path) {
217   if (ClStripPathPrefix.empty())
218     return Path;
219   size_t Pos = Path.find(ClStripPathPrefix);
220   if (Pos == std::string::npos)
221     return Path;
222   return Path.substr(Pos + ClStripPathPrefix.size());
223 }
224 
createSymbolizer()225 static std::unique_ptr<symbolize::LLVMSymbolizer> createSymbolizer() {
226   symbolize::LLVMSymbolizer::Options SymbolizerOptions;
227   SymbolizerOptions.Demangle = ClDemangle;
228   SymbolizerOptions.UseSymbolTable = true;
229   return std::unique_ptr<symbolize::LLVMSymbolizer>(
230       new symbolize::LLVMSymbolizer(SymbolizerOptions));
231 }
232 
233 // A DILineInfo with address.
234 struct AddrInfo : public DILineInfo {
235   uint64_t Addr;
236 
AddrInfo__anon15e54a520111::AddrInfo237   AddrInfo(const DILineInfo &DI, uint64_t Addr) : DILineInfo(DI), Addr(Addr) {
238     FileName = normalizeFilename(FileName);
239   }
240 
241 private:
normalizeFilename__anon15e54a520111::AddrInfo242   static std::string normalizeFilename(const std::string &FileName) {
243     SmallString<256> S(FileName);
244     sys::path::remove_dots(S, /* remove_dot_dot */ true);
245     return S.str().str();
246   }
247 };
248 
249 class Blacklists {
250 public:
Blacklists()251   Blacklists()
252       : DefaultBlacklist(createDefaultBlacklist()),
253         UserBlacklist(createUserBlacklist()) {}
254 
255   // AddrInfo contains normalized filename. It is important to check it rather
256   // than DILineInfo.
isBlacklisted(const AddrInfo & AI)257   bool isBlacklisted(const AddrInfo &AI) {
258     if (DefaultBlacklist && DefaultBlacklist->inSection("fun", AI.FunctionName))
259       return true;
260     if (DefaultBlacklist && DefaultBlacklist->inSection("src", AI.FileName))
261       return true;
262     if (UserBlacklist && UserBlacklist->inSection("fun", AI.FunctionName))
263       return true;
264     if (UserBlacklist && UserBlacklist->inSection("src", AI.FileName))
265       return true;
266     return false;
267   }
268 
269 private:
createDefaultBlacklist()270   static std::unique_ptr<SpecialCaseList> createDefaultBlacklist() {
271     if (!ClUseDefaultBlacklist)
272       return std::unique_ptr<SpecialCaseList>();
273     std::unique_ptr<MemoryBuffer> MB =
274         MemoryBuffer::getMemBuffer(DefaultBlacklistStr);
275     std::string Error;
276     auto Blacklist = SpecialCaseList::create(MB.get(), Error);
277     FailIfNotEmpty(Error);
278     return Blacklist;
279   }
280 
createUserBlacklist()281   static std::unique_ptr<SpecialCaseList> createUserBlacklist() {
282     if (ClBlacklist.empty())
283       return std::unique_ptr<SpecialCaseList>();
284 
285     return SpecialCaseList::createOrDie({{ClBlacklist}});
286   }
287   std::unique_ptr<SpecialCaseList> DefaultBlacklist;
288   std::unique_ptr<SpecialCaseList> UserBlacklist;
289 };
290 
291 // Collect all debug info for given addresses.
getAddrInfo(const std::string & ObjectFile,const std::set<uint64_t> & Addrs,bool InlinedCode)292 static std::vector<AddrInfo> getAddrInfo(const std::string &ObjectFile,
293                                          const std::set<uint64_t> &Addrs,
294                                          bool InlinedCode) {
295   std::vector<AddrInfo> Result;
296   auto Symbolizer(createSymbolizer());
297   Blacklists B;
298 
299   for (auto Addr : Addrs) {
300     auto LineInfo = Symbolizer->symbolizeCode(ObjectFile, Addr);
301     FailIfError(LineInfo);
302     auto LineAddrInfo = AddrInfo(*LineInfo, Addr);
303     if (B.isBlacklisted(LineAddrInfo))
304       continue;
305     Result.push_back(LineAddrInfo);
306     if (InlinedCode) {
307       auto InliningInfo = Symbolizer->symbolizeInlinedCode(ObjectFile, Addr);
308       FailIfError(InliningInfo);
309       for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
310         auto FrameInfo = InliningInfo->getFrame(I);
311         auto FrameAddrInfo = AddrInfo(FrameInfo, Addr);
312         if (B.isBlacklisted(FrameAddrInfo))
313           continue;
314         Result.push_back(FrameAddrInfo);
315       }
316     }
317   }
318 
319   return Result;
320 }
321 
322 // Locate __sanitizer_cov* function addresses that are used for coverage
323 // reporting.
324 static std::set<uint64_t>
findSanitizerCovFunctions(const object::ObjectFile & O)325 findSanitizerCovFunctions(const object::ObjectFile &O) {
326   std::set<uint64_t> Result;
327 
328   for (const object::SymbolRef &Symbol : O.symbols()) {
329     Expected<uint64_t> AddressOrErr = Symbol.getAddress();
330     FailIfError(errorToErrorCode(AddressOrErr.takeError()));
331 
332     Expected<StringRef> NameOrErr = Symbol.getName();
333     FailIfError(errorToErrorCode(NameOrErr.takeError()));
334     StringRef Name = NameOrErr.get();
335 
336     if (Name == "__sanitizer_cov" || Name == "__sanitizer_cov_with_check" ||
337         Name == "__sanitizer_cov_trace_func_enter") {
338       if (!(Symbol.getFlags() & object::BasicSymbolRef::SF_Undefined))
339         Result.insert(AddressOrErr.get());
340     }
341   }
342 
343   return Result;
344 }
345 
346 // Locate addresses of all coverage points in a file. Coverage point
347 // is defined as the 'address of instruction following __sanitizer_cov
348 // call - 1'.
getObjectCoveragePoints(const object::ObjectFile & O,std::set<uint64_t> * Addrs)349 static void getObjectCoveragePoints(const object::ObjectFile &O,
350                                     std::set<uint64_t> *Addrs) {
351   Triple TheTriple("unknown-unknown-unknown");
352   TheTriple.setArch(Triple::ArchType(O.getArch()));
353   auto TripleName = TheTriple.getTriple();
354 
355   std::string Error;
356   const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error);
357   FailIfNotEmpty(Error);
358 
359   std::unique_ptr<const MCSubtargetInfo> STI(
360       TheTarget->createMCSubtargetInfo(TripleName, "", ""));
361   FailIfEmpty(STI, "no subtarget info for target " + TripleName);
362 
363   std::unique_ptr<const MCRegisterInfo> MRI(
364       TheTarget->createMCRegInfo(TripleName));
365   FailIfEmpty(MRI, "no register info for target " + TripleName);
366 
367   std::unique_ptr<const MCAsmInfo> AsmInfo(
368       TheTarget->createMCAsmInfo(*MRI, TripleName));
369   FailIfEmpty(AsmInfo, "no asm info for target " + TripleName);
370 
371   std::unique_ptr<const MCObjectFileInfo> MOFI(new MCObjectFileInfo);
372   MCContext Ctx(AsmInfo.get(), MRI.get(), MOFI.get());
373   std::unique_ptr<MCDisassembler> DisAsm(
374       TheTarget->createMCDisassembler(*STI, Ctx));
375   FailIfEmpty(DisAsm, "no disassembler info for target " + TripleName);
376 
377   std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo());
378   FailIfEmpty(MII, "no instruction info for target " + TripleName);
379 
380   std::unique_ptr<const MCInstrAnalysis> MIA(
381       TheTarget->createMCInstrAnalysis(MII.get()));
382   FailIfEmpty(MIA, "no instruction analysis info for target " + TripleName);
383 
384   auto SanCovAddrs = findSanitizerCovFunctions(O);
385   if (SanCovAddrs.empty())
386     Fail("__sanitizer_cov* functions not found");
387 
388   for (object::SectionRef Section : O.sections()) {
389     if (Section.isVirtual() || !Section.isText()) // llvm-objdump does the same.
390       continue;
391     uint64_t SectionAddr = Section.getAddress();
392     uint64_t SectSize = Section.getSize();
393     if (!SectSize)
394       continue;
395 
396     StringRef BytesStr;
397     FailIfError(Section.getContents(BytesStr));
398     ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()),
399                             BytesStr.size());
400 
401     for (uint64_t Index = 0, Size = 0; Index < Section.getSize();
402          Index += Size) {
403       MCInst Inst;
404       if (!DisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
405                                   SectionAddr + Index, nulls(), nulls())) {
406         if (Size == 0)
407           Size = 1;
408         continue;
409       }
410       uint64_t Addr = Index + SectionAddr;
411       // Sanitizer coverage uses the address of the next instruction - 1.
412       uint64_t CovPoint = Addr + Size - 1;
413       uint64_t Target;
414       if (MIA->isCall(Inst) &&
415           MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target) &&
416           SanCovAddrs.find(Target) != SanCovAddrs.end())
417         Addrs->insert(CovPoint);
418     }
419   }
420 }
421 
422 static void
visitObjectFiles(const object::Archive & A,function_ref<void (const object::ObjectFile &)> Fn)423 visitObjectFiles(const object::Archive &A,
424                  function_ref<void(const object::ObjectFile &)> Fn) {
425   Error Err;
426   for (auto &C : A.children(Err)) {
427     Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary();
428     FailIfError(errorToErrorCode(ChildOrErr.takeError()));
429     if (auto *O = dyn_cast<object::ObjectFile>(&*ChildOrErr.get()))
430       Fn(*O);
431     else
432       FailIfError(object::object_error::invalid_file_type);
433   }
434   FailIfError(std::move(Err));
435 }
436 
437 static void
visitObjectFiles(const std::string & FileName,function_ref<void (const object::ObjectFile &)> Fn)438 visitObjectFiles(const std::string &FileName,
439                  function_ref<void(const object::ObjectFile &)> Fn) {
440   Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
441       object::createBinary(FileName);
442   if (!BinaryOrErr)
443     FailIfError(errorToErrorCode(BinaryOrErr.takeError()));
444 
445   object::Binary &Binary = *BinaryOrErr.get().getBinary();
446   if (object::Archive *A = dyn_cast<object::Archive>(&Binary))
447     visitObjectFiles(*A, Fn);
448   else if (object::ObjectFile *O = dyn_cast<object::ObjectFile>(&Binary))
449     Fn(*O);
450   else
451     FailIfError(object::object_error::invalid_file_type);
452 }
453 
findSanitizerCovFunctions(const std::string & FileName)454 std::set<uint64_t> findSanitizerCovFunctions(const std::string &FileName) {
455   std::set<uint64_t> Result;
456   visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
457     auto Addrs = findSanitizerCovFunctions(O);
458     Result.insert(Addrs.begin(), Addrs.end());
459   });
460   return Result;
461 }
462 
463 // Locate addresses of all coverage points in a file. Coverage point
464 // is defined as the 'address of instruction following __sanitizer_cov
465 // call - 1'.
getCoveragePoints(const std::string & FileName)466 std::set<uint64_t> getCoveragePoints(const std::string &FileName) {
467   std::set<uint64_t> Result;
468   visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
469     getObjectCoveragePoints(O, &Result);
470   });
471   return Result;
472 }
473 
printCovPoints(const std::string & ObjFile,raw_ostream & OS)474 static void printCovPoints(const std::string &ObjFile, raw_ostream &OS) {
475   for (uint64_t Addr : getCoveragePoints(ObjFile)) {
476     OS << "0x";
477     OS.write_hex(Addr);
478     OS << "\n";
479   }
480 }
481 
escapeHtml(const std::string & S)482 static std::string escapeHtml(const std::string &S) {
483   std::string Result;
484   Result.reserve(S.size());
485   for (char Ch : S) {
486     switch (Ch) {
487     case '&':
488       Result.append("&amp;");
489       break;
490     case '\'':
491       Result.append("&apos;");
492       break;
493     case '"':
494       Result.append("&quot;");
495       break;
496     case '<':
497       Result.append("&lt;");
498       break;
499     case '>':
500       Result.append("&gt;");
501       break;
502     default:
503       Result.push_back(Ch);
504       break;
505     }
506   }
507   return Result;
508 }
509 
510 // Adds leading zeroes wrapped in 'lz' style.
511 // Leading zeroes help locate 000% coverage.
formatHtmlPct(size_t Pct)512 static std::string formatHtmlPct(size_t Pct) {
513   Pct = std::max(std::size_t{0}, std::min(std::size_t{100}, Pct));
514 
515   std::string Num = std::to_string(Pct);
516   std::string Zeroes(3 - Num.size(), '0');
517   if (!Zeroes.empty())
518     Zeroes = "<span class='lz'>" + Zeroes + "</span>";
519 
520   return Zeroes + Num;
521 }
522 
anchorName(const std::string & Anchor)523 static std::string anchorName(const std::string &Anchor) {
524   llvm::MD5 Hasher;
525   llvm::MD5::MD5Result Hash;
526   Hasher.update(Anchor);
527   Hasher.final(Hash);
528 
529   SmallString<32> HexString;
530   llvm::MD5::stringifyResult(Hash, HexString);
531   return HexString.str().str();
532 }
533 
isCoverageFile(const std::string & FileName)534 static ErrorOr<bool> isCoverageFile(const std::string &FileName) {
535   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
536       MemoryBuffer::getFile(FileName);
537   if (!BufOrErr) {
538     errs() << "Warning: " << BufOrErr.getError().message() << "("
539            << BufOrErr.getError().value()
540            << "), filename: " << llvm::sys::path::filename(FileName) << "\n";
541     return BufOrErr.getError();
542   }
543   std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
544   if (Buf->getBufferSize() < 8) {
545     return false;
546   }
547   const FileHeader *Header =
548       reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
549   return Header->Magic == BinCoverageMagic;
550 }
551 
552 struct CoverageStats {
CoverageStats__anon15e54a520111::CoverageStats553   CoverageStats() : AllPoints(0), CovPoints(0), AllFns(0), CovFns(0) {}
554 
555   size_t AllPoints;
556   size_t CovPoints;
557   size_t AllFns;
558   size_t CovFns;
559 };
560 
operator <<(raw_ostream & OS,const CoverageStats & Stats)561 static raw_ostream &operator<<(raw_ostream &OS, const CoverageStats &Stats) {
562   OS << "all-edges: " << Stats.AllPoints << "\n";
563   OS << "cov-edges: " << Stats.CovPoints << "\n";
564   OS << "all-functions: " << Stats.AllFns << "\n";
565   OS << "cov-functions: " << Stats.CovFns << "\n";
566   return OS;
567 }
568 
569 class CoverageData {
570 public:
571   // Read single file coverage data.
572   static ErrorOr<std::unique_ptr<CoverageData>>
read(const std::string & FileName)573   read(const std::string &FileName) {
574     ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
575         MemoryBuffer::getFile(FileName);
576     if (!BufOrErr)
577       return BufOrErr.getError();
578     std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
579     if (Buf->getBufferSize() < 8) {
580       errs() << "File too small (<8): " << Buf->getBufferSize();
581       return make_error_code(errc::illegal_byte_sequence);
582     }
583     const FileHeader *Header =
584         reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
585 
586     if (Header->Magic != BinCoverageMagic) {
587       errs() << "Wrong magic: " << Header->Magic;
588       return make_error_code(errc::illegal_byte_sequence);
589     }
590 
591     auto Addrs = llvm::make_unique<std::set<uint64_t>>();
592 
593     switch (Header->Bitness) {
594     case Bitness64:
595       readInts<uint64_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
596                          Addrs.get());
597       break;
598     case Bitness32:
599       readInts<uint32_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
600                          Addrs.get());
601       break;
602     default:
603       errs() << "Unsupported bitness: " << Header->Bitness;
604       return make_error_code(errc::illegal_byte_sequence);
605     }
606 
607     return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs)));
608   }
609 
610   // Merge multiple coverage data together.
611   static std::unique_ptr<CoverageData>
merge(const std::vector<std::unique_ptr<CoverageData>> & Covs)612   merge(const std::vector<std::unique_ptr<CoverageData>> &Covs) {
613     auto Addrs = llvm::make_unique<std::set<uint64_t>>();
614 
615     for (const auto &Cov : Covs)
616       Addrs->insert(Cov->Addrs->begin(), Cov->Addrs->end());
617 
618     return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs)));
619   }
620 
621   // Read list of files and merges their coverage info.
622   static ErrorOr<std::unique_ptr<CoverageData>>
readAndMerge(const std::vector<std::string> & FileNames)623   readAndMerge(const std::vector<std::string> &FileNames) {
624     std::vector<std::unique_ptr<CoverageData>> Covs;
625     for (const auto &FileName : FileNames) {
626       auto Cov = read(FileName);
627       if (!Cov)
628         return Cov.getError();
629       Covs.push_back(std::move(Cov.get()));
630     }
631     return merge(Covs);
632   }
633 
634   // Print coverage addresses.
printAddrs(raw_ostream & OS)635   void printAddrs(raw_ostream &OS) {
636     for (auto Addr : *Addrs) {
637       OS << "0x";
638       OS.write_hex(Addr);
639       OS << "\n";
640     }
641   }
642 
643 protected:
CoverageData(std::unique_ptr<std::set<uint64_t>> Addrs)644   explicit CoverageData(std::unique_ptr<std::set<uint64_t>> Addrs)
645       : Addrs(std::move(Addrs)) {}
646 
647   friend class CoverageDataWithObjectFile;
648 
649   std::unique_ptr<std::set<uint64_t>> Addrs;
650 };
651 
652 // Coverage data translated into source code line-level information.
653 // Fetches debug info in constructor and calculates various information per
654 // request.
655 class SourceCoverageData {
656 public:
657   enum LineStatus {
658     // coverage information for the line is not available.
659     // default value in maps.
660     UNKNOWN = 0,
661     // the line is fully covered.
662     COVERED = 1,
663     // the line is fully uncovered.
664     NOT_COVERED = 2,
665     // some points in the line a covered, some are not.
666     MIXED = 3
667   };
668 
SourceCoverageData(std::string ObjectFile,const std::set<uint64_t> & Addrs)669   SourceCoverageData(std::string ObjectFile, const std::set<uint64_t> &Addrs)
670       : AllCovPoints(getCoveragePoints(ObjectFile)) {
671     if (!std::includes(AllCovPoints.begin(), AllCovPoints.end(), Addrs.begin(),
672                        Addrs.end())) {
673       Fail("Coverage points in binary and .sancov file do not match.");
674     }
675 
676     AllAddrInfo = getAddrInfo(ObjectFile, AllCovPoints, true);
677     CovAddrInfo = getAddrInfo(ObjectFile, Addrs, true);
678   }
679 
680   // Compute number of coverage points hit/total in a file.
681   // file_name -> <coverage, all_coverage>
computeFileCoverage()682   std::map<std::string, std::pair<size_t, size_t>> computeFileCoverage() {
683     std::map<std::string, std::pair<size_t, size_t>> FileCoverage;
684     auto AllCovPointsByFile =
685         group_by(AllAddrInfo, [](const AddrInfo &AI) { return AI.FileName; });
686     auto CovPointsByFile =
687         group_by(CovAddrInfo, [](const AddrInfo &AI) { return AI.FileName; });
688 
689     for (const auto &P : AllCovPointsByFile) {
690       const std::string &FileName = P.first;
691 
692       FileCoverage[FileName] =
693           std::make_pair(CovPointsByFile[FileName].size(),
694                          AllCovPointsByFile[FileName].size());
695     }
696     return FileCoverage;
697   }
698 
699   // line_number -> line_status.
700   typedef std::map<int, LineStatus> LineStatusMap;
701   // file_name -> LineStatusMap
702   typedef std::map<std::string, LineStatusMap> FileLineStatusMap;
703 
704   // fills in the {file_name -> {line_no -> status}} map.
computeLineStatusMap()705   FileLineStatusMap computeLineStatusMap() {
706     FileLineStatusMap StatusMap;
707 
708     auto AllLocs = group_by(AllAddrInfo, [](const AddrInfo &AI) {
709       return FileLoc{AI.FileName, AI.Line};
710     });
711     auto CovLocs = group_by(CovAddrInfo, [](const AddrInfo &AI) {
712       return FileLoc{AI.FileName, AI.Line};
713     });
714 
715     for (const auto &P : AllLocs) {
716       const FileLoc &Loc = P.first;
717       auto I = CovLocs.find(Loc);
718 
719       if (I == CovLocs.end()) {
720         StatusMap[Loc.FileName][Loc.Line] = NOT_COVERED;
721       } else {
722         StatusMap[Loc.FileName][Loc.Line] =
723             (I->second.size() == P.second.size()) ? COVERED : MIXED;
724       }
725     }
726     return StatusMap;
727   }
728 
computeAllFunctions() const729   std::set<FileFn> computeAllFunctions() const {
730     std::set<FileFn> Fns;
731     for (const auto &AI : AllAddrInfo) {
732       Fns.insert(FileFn{AI.FileName, AI.FunctionName});
733     }
734     return Fns;
735   }
736 
computeCoveredFunctions() const737   std::set<FileFn> computeCoveredFunctions() const {
738     std::set<FileFn> Fns;
739     auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
740       return FileFn{AI.FileName, AI.FunctionName};
741     });
742 
743     for (const auto &P : CovFns) {
744       Fns.insert(P.first);
745     }
746     return Fns;
747   }
748 
computeNotCoveredFunctions() const749   std::set<FileFn> computeNotCoveredFunctions() const {
750     std::set<FileFn> Fns;
751 
752     auto AllFns = group_by(AllAddrInfo, [](const AddrInfo &AI) {
753       return FileFn{AI.FileName, AI.FunctionName};
754     });
755     auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
756       return FileFn{AI.FileName, AI.FunctionName};
757     });
758 
759     for (const auto &P : AllFns) {
760       if (CovFns.find(P.first) == CovFns.end()) {
761         Fns.insert(P.first);
762       }
763     }
764     return Fns;
765   }
766 
767   // Compute % coverage for each function.
computeFunctionsCoverage() const768   std::map<FileFn, int> computeFunctionsCoverage() const {
769     std::map<FileFn, int> FnCoverage;
770     auto AllFns = group_by(AllAddrInfo, [](const AddrInfo &AI) {
771       return FileFn{AI.FileName, AI.FunctionName};
772     });
773 
774     auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
775       return FileFn{AI.FileName, AI.FunctionName};
776     });
777 
778     for (const auto &P : AllFns) {
779       FileFn F = P.first;
780       FnCoverage[F] = CovFns[F].size() * 100 / P.second.size();
781     }
782 
783     return FnCoverage;
784   }
785 
786   typedef std::map<FileLoc, std::set<std::string>> FunctionLocs;
787   // finds first line number in a file for each function.
resolveFunctions(const std::set<FileFn> & Fns) const788   FunctionLocs resolveFunctions(const std::set<FileFn> &Fns) const {
789     std::vector<AddrInfo> FnAddrs;
790     for (const auto &AI : AllAddrInfo) {
791       if (Fns.find(FileFn{AI.FileName, AI.FunctionName}) != Fns.end())
792         FnAddrs.push_back(AI);
793     }
794 
795     auto GroupedAddrs = group_by(FnAddrs, [](const AddrInfo &AI) {
796       return FnLoc{FileLoc{AI.FileName, AI.Line}, AI.FunctionName};
797     });
798 
799     FunctionLocs Result;
800     std::string LastFileName;
801     std::set<std::string> ProcessedFunctions;
802 
803     for (const auto &P : GroupedAddrs) {
804       const FnLoc &Loc = P.first;
805       std::string FileName = Loc.Loc.FileName;
806       std::string FunctionName = Loc.FunctionName;
807 
808       if (LastFileName != FileName)
809         ProcessedFunctions.clear();
810       LastFileName = FileName;
811 
812       if (!ProcessedFunctions.insert(FunctionName).second)
813         continue;
814 
815       auto FLoc = FileLoc{FileName, Loc.Loc.Line};
816       Result[FLoc].insert(FunctionName);
817     }
818     return Result;
819   }
820 
files() const821   std::set<std::string> files() const {
822     std::set<std::string> Files;
823     for (const auto &AI : AllAddrInfo) {
824       Files.insert(AI.FileName);
825     }
826     return Files;
827   }
828 
collectStats(CoverageStats * Stats) const829   void collectStats(CoverageStats *Stats) const {
830     Stats->AllPoints += AllCovPoints.size();
831     Stats->AllFns += computeAllFunctions().size();
832     Stats->CovFns += computeCoveredFunctions().size();
833   }
834 
835 private:
836   const std::set<uint64_t> AllCovPoints;
837 
838   std::vector<AddrInfo> AllAddrInfo;
839   std::vector<AddrInfo> CovAddrInfo;
840 };
841 
printFunctionLocs(const SourceCoverageData::FunctionLocs & FnLocs,raw_ostream & OS)842 static void printFunctionLocs(const SourceCoverageData::FunctionLocs &FnLocs,
843                               raw_ostream &OS) {
844   for (const auto &Fns : FnLocs) {
845     for (const auto &Fn : Fns.second) {
846       OS << stripPathPrefix(Fns.first.FileName) << ":" << Fns.first.Line << " "
847          << Fn << "\n";
848     }
849   }
850 }
851 
852 // Holder for coverage data + filename of corresponding object file.
853 class CoverageDataWithObjectFile : public CoverageData {
854 public:
855   static ErrorOr<std::unique_ptr<CoverageDataWithObjectFile>>
readAndMerge(const std::string & ObjectFile,const std::vector<std::string> & FileNames)856   readAndMerge(const std::string &ObjectFile,
857                const std::vector<std::string> &FileNames) {
858     auto MergedDataOrError = CoverageData::readAndMerge(FileNames);
859     if (!MergedDataOrError)
860       return MergedDataOrError.getError();
861     return std::unique_ptr<CoverageDataWithObjectFile>(
862         new CoverageDataWithObjectFile(ObjectFile,
863                                        std::move(MergedDataOrError.get())));
864   }
865 
object_file() const866   std::string object_file() const { return ObjectFile; }
867 
868   // Print list of covered functions.
869   // Line format: <file_name>:<line> <function_name>
printCoveredFunctions(raw_ostream & OS) const870   void printCoveredFunctions(raw_ostream &OS) const {
871     SourceCoverageData SCovData(ObjectFile, *Addrs);
872     auto CoveredFns = SCovData.computeCoveredFunctions();
873     printFunctionLocs(SCovData.resolveFunctions(CoveredFns), OS);
874   }
875 
876   // Print list of not covered functions.
877   // Line format: <file_name>:<line> <function_name>
printNotCoveredFunctions(raw_ostream & OS) const878   void printNotCoveredFunctions(raw_ostream &OS) const {
879     SourceCoverageData SCovData(ObjectFile, *Addrs);
880     auto NotCoveredFns = SCovData.computeNotCoveredFunctions();
881     printFunctionLocs(SCovData.resolveFunctions(NotCoveredFns), OS);
882   }
883 
printReport(raw_ostream & OS) const884   void printReport(raw_ostream &OS) const {
885     SourceCoverageData SCovData(ObjectFile, *Addrs);
886     auto LineStatusMap = SCovData.computeLineStatusMap();
887 
888     std::set<FileFn> AllFns = SCovData.computeAllFunctions();
889     // file_loc -> set[function_name]
890     auto AllFnsByLoc = SCovData.resolveFunctions(AllFns);
891     auto FileCoverage = SCovData.computeFileCoverage();
892 
893     auto FnCoverage = SCovData.computeFunctionsCoverage();
894     auto FnCoverageByFile =
895         group_by(FnCoverage, [](const std::pair<FileFn, int> &FileFn) {
896           return FileFn.first.FileName;
897         });
898 
899     // TOC
900 
901     size_t NotCoveredFilesCount = 0;
902     std::set<std::string> Files = SCovData.files();
903 
904     // Covered Files.
905     OS << "<details open><summary>Touched Files</summary>\n";
906     OS << "<table>\n";
907     OS << "<tr><th>File</th><th>Coverage %</th>";
908     OS << "<th>Hit (Total) Fns</th></tr>\n";
909     for (const auto &FileName : Files) {
910       std::pair<size_t, size_t> FC = FileCoverage[FileName];
911       if (FC.first == 0) {
912         NotCoveredFilesCount++;
913         continue;
914       }
915       size_t CovPct = FC.second == 0 ? 100 : 100 * FC.first / FC.second;
916 
917       OS << "<tr><td><a href=\"#" << anchorName(FileName) << "\">"
918          << stripPathPrefix(FileName) << "</a></td>"
919          << "<td>" << formatHtmlPct(CovPct) << "%</td>"
920          << "<td>" << FC.first << " (" << FC.second << ")"
921          << "</tr>\n";
922     }
923     OS << "</table>\n";
924     OS << "</details>\n";
925 
926     // Not covered files.
927     if (NotCoveredFilesCount) {
928       OS << "<details><summary>Not Touched Files</summary>\n";
929       OS << "<table>\n";
930       for (const auto &FileName : Files) {
931         std::pair<size_t, size_t> FC = FileCoverage[FileName];
932         if (FC.first == 0)
933           OS << "<tr><td>" << stripPathPrefix(FileName) << "</td>\n";
934       }
935       OS << "</table>\n";
936       OS << "</details>\n";
937     } else {
938       OS << "<p>Congratulations! All source files are touched.</p>\n";
939     }
940 
941     // Source
942     for (const auto &FileName : Files) {
943       std::pair<size_t, size_t> FC = FileCoverage[FileName];
944       if (FC.first == 0)
945         continue;
946       OS << "<a name=\"" << anchorName(FileName) << "\"></a>\n";
947       OS << "<h2>" << stripPathPrefix(FileName) << "</h2>\n";
948       OS << "<details open><summary>Function Coverage</summary>";
949       OS << "<div class='fnlist'>\n";
950 
951       auto &FileFnCoverage = FnCoverageByFile[FileName];
952 
953       for (const auto &P : FileFnCoverage) {
954         std::string FunctionName = P.first.FunctionName;
955 
956         OS << "<div class='fn' style='order: " << P.second << "'>";
957         OS << "<span class='pct'>" << formatHtmlPct(P.second)
958            << "%</span>&nbsp;";
959         OS << "<span class='name'><a href=\"#"
960            << anchorName(FileName + "::" + FunctionName) << "\">";
961         OS << escapeHtml(FunctionName) << "</a></span>";
962         OS << "</div>\n";
963       }
964       OS << "</div></details>\n";
965 
966       ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
967           MemoryBuffer::getFile(FileName);
968       if (!BufOrErr) {
969         OS << "Error reading file: " << FileName << " : "
970            << BufOrErr.getError().message() << "("
971            << BufOrErr.getError().value() << ")\n";
972         continue;
973       }
974 
975       OS << "<pre>\n";
976       const auto &LineStatuses = LineStatusMap[FileName];
977       for (line_iterator I = line_iterator(*BufOrErr.get(), false);
978            !I.is_at_eof(); ++I) {
979         uint32_t Line = I.line_number();
980         { // generate anchors (if any);
981           FileLoc Loc = FileLoc{FileName, Line};
982           auto It = AllFnsByLoc.find(Loc);
983           if (It != AllFnsByLoc.end()) {
984             for (const std::string &Fn : It->second) {
985               OS << "<a name=\"" << anchorName(FileName + "::" + Fn)
986                  << "\"></a>";
987             };
988           }
989         }
990 
991         OS << "<span ";
992         auto LIT = LineStatuses.find(I.line_number());
993         auto Status = (LIT != LineStatuses.end()) ? LIT->second
994                                                   : SourceCoverageData::UNKNOWN;
995         switch (Status) {
996         case SourceCoverageData::UNKNOWN:
997           OS << "class=unknown";
998           break;
999         case SourceCoverageData::COVERED:
1000           OS << "class=covered";
1001           break;
1002         case SourceCoverageData::NOT_COVERED:
1003           OS << "class=notcovered";
1004           break;
1005         case SourceCoverageData::MIXED:
1006           OS << "class=mixed";
1007           break;
1008         }
1009         OS << ">";
1010         OS << escapeHtml(*I) << "</span>\n";
1011       }
1012       OS << "</pre>\n";
1013     }
1014   }
1015 
collectStats(CoverageStats * Stats) const1016   void collectStats(CoverageStats *Stats) const {
1017     Stats->CovPoints += Addrs->size();
1018 
1019     SourceCoverageData SCovData(ObjectFile, *Addrs);
1020     SCovData.collectStats(Stats);
1021   }
1022 
1023 private:
CoverageDataWithObjectFile(std::string ObjectFile,std::unique_ptr<CoverageData> Coverage)1024   CoverageDataWithObjectFile(std::string ObjectFile,
1025                              std::unique_ptr<CoverageData> Coverage)
1026       : CoverageData(std::move(Coverage->Addrs)),
1027         ObjectFile(std::move(ObjectFile)) {}
1028   const std::string ObjectFile;
1029 };
1030 
1031 // Multiple coverage files data organized by object file.
1032 class CoverageDataSet {
1033 public:
1034   static ErrorOr<std::unique_ptr<CoverageDataSet>>
readCmdArguments(std::vector<std::string> FileNames)1035   readCmdArguments(std::vector<std::string> FileNames) {
1036     // Short name => file name.
1037     std::map<std::string, std::string> ObjFiles;
1038     std::string FirstObjFile;
1039     std::set<std::string> CovFiles;
1040 
1041     // Partition input values into coverage/object files.
1042     for (const auto &FileName : FileNames) {
1043       auto ErrorOrIsCoverage = isCoverageFile(FileName);
1044       if (!ErrorOrIsCoverage)
1045         continue;
1046       if (ErrorOrIsCoverage.get()) {
1047         CovFiles.insert(FileName);
1048       } else {
1049         auto ShortFileName = llvm::sys::path::filename(FileName);
1050         if (ObjFiles.find(ShortFileName) != ObjFiles.end()) {
1051           Fail("Duplicate binary file with a short name: " + ShortFileName);
1052         }
1053 
1054         ObjFiles[ShortFileName] = FileName;
1055         if (FirstObjFile.empty())
1056           FirstObjFile = FileName;
1057       }
1058     }
1059 
1060     Regex SancovRegex("(.*)\\.[0-9]+\\.sancov");
1061     SmallVector<StringRef, 2> Components;
1062 
1063     // Object file => list of corresponding coverage file names.
1064     auto CoverageByObjFile = group_by(CovFiles, [&](std::string FileName) {
1065       auto ShortFileName = llvm::sys::path::filename(FileName);
1066       auto Ok = SancovRegex.match(ShortFileName, &Components);
1067       if (!Ok) {
1068         Fail("Can't match coverage file name against "
1069              "<module_name>.<pid>.sancov pattern: " +
1070              FileName);
1071       }
1072 
1073       auto Iter = ObjFiles.find(Components[1]);
1074       if (Iter == ObjFiles.end()) {
1075         Fail("Object file for coverage not found: " + FileName);
1076       }
1077       return Iter->second;
1078     });
1079 
1080     // Read coverage.
1081     std::vector<std::unique_ptr<CoverageDataWithObjectFile>> MergedCoverage;
1082     for (const auto &Pair : CoverageByObjFile) {
1083       if (findSanitizerCovFunctions(Pair.first).empty()) {
1084         for (const auto &FileName : Pair.second) {
1085           CovFiles.erase(FileName);
1086         }
1087 
1088         errs()
1089             << "Ignoring " << Pair.first
1090             << " and its coverage because  __sanitizer_cov* functions were not "
1091                "found.\n";
1092         continue;
1093       }
1094 
1095       auto DataOrError =
1096           CoverageDataWithObjectFile::readAndMerge(Pair.first, Pair.second);
1097       FailIfError(DataOrError);
1098       MergedCoverage.push_back(std::move(DataOrError.get()));
1099     }
1100 
1101     return std::unique_ptr<CoverageDataSet>(
1102         new CoverageDataSet(FirstObjFile, &MergedCoverage, CovFiles));
1103   }
1104 
printCoveredFunctions(raw_ostream & OS) const1105   void printCoveredFunctions(raw_ostream &OS) const {
1106     for (const auto &Cov : Coverage) {
1107       Cov->printCoveredFunctions(OS);
1108     }
1109   }
1110 
printNotCoveredFunctions(raw_ostream & OS) const1111   void printNotCoveredFunctions(raw_ostream &OS) const {
1112     for (const auto &Cov : Coverage) {
1113       Cov->printNotCoveredFunctions(OS);
1114     }
1115   }
1116 
printStats(raw_ostream & OS) const1117   void printStats(raw_ostream &OS) const {
1118     CoverageStats Stats;
1119     for (const auto &Cov : Coverage) {
1120       Cov->collectStats(&Stats);
1121     }
1122     OS << Stats;
1123   }
1124 
printReport(raw_ostream & OS) const1125   void printReport(raw_ostream &OS) const {
1126     auto Title =
1127         (llvm::sys::path::filename(MainObjFile) + " Coverage Report").str();
1128 
1129     OS << "<html>\n";
1130     OS << "<head>\n";
1131 
1132     // Stylesheet
1133     OS << "<style>\n";
1134     OS << ".covered { background: #7F7; }\n";
1135     OS << ".notcovered { background: #F77; }\n";
1136     OS << ".mixed { background: #FF7; }\n";
1137     OS << "summary { font-weight: bold; }\n";
1138     OS << "details > summary + * { margin-left: 1em; }\n";
1139     OS << ".fnlist { display: flex; flex-flow: column nowrap; }\n";
1140     OS << ".fn { display: flex; flex-flow: row nowrap; }\n";
1141     OS << ".pct { width: 3em; text-align: right; margin-right: 1em; }\n";
1142     OS << ".name { flex: 2; }\n";
1143     OS << ".lz { color: lightgray; }\n";
1144     OS << "</style>\n";
1145     OS << "<title>" << Title << "</title>\n";
1146     OS << "</head>\n";
1147     OS << "<body>\n";
1148 
1149     // Title
1150     OS << "<h1>" << Title << "</h1>\n";
1151 
1152     // Modules TOC.
1153     if (Coverage.size() > 1) {
1154       for (const auto &CovData : Coverage) {
1155         OS << "<li><a href=\"#module_" << anchorName(CovData->object_file())
1156            << "\">" << llvm::sys::path::filename(CovData->object_file())
1157            << "</a></li>\n";
1158       }
1159     }
1160 
1161     for (const auto &CovData : Coverage) {
1162       if (Coverage.size() > 1) {
1163         OS << "<h2>" << llvm::sys::path::filename(CovData->object_file())
1164            << "</h2>\n";
1165       }
1166       OS << "<a name=\"module_" << anchorName(CovData->object_file())
1167          << "\"></a>\n";
1168       CovData->printReport(OS);
1169     }
1170 
1171     // About
1172     OS << "<details><summary>About</summary>\n";
1173     OS << "Coverage files:<ul>";
1174     for (const auto &InputFile : CoverageFiles) {
1175       llvm::sys::fs::file_status Status;
1176       llvm::sys::fs::status(InputFile, Status);
1177       OS << "<li>" << stripPathPrefix(InputFile) << " ("
1178          << Status.getLastModificationTime().str() << ")</li>\n";
1179     }
1180     OS << "</ul></details>\n";
1181 
1182     OS << "</body>\n";
1183     OS << "</html>\n";
1184   }
1185 
empty() const1186   bool empty() const { return Coverage.empty(); }
1187 
1188 private:
CoverageDataSet(const std::string & MainObjFile,std::vector<std::unique_ptr<CoverageDataWithObjectFile>> * Data,const std::set<std::string> & CoverageFiles)1189   explicit CoverageDataSet(
1190       const std::string &MainObjFile,
1191       std::vector<std::unique_ptr<CoverageDataWithObjectFile>> *Data,
1192       const std::set<std::string> &CoverageFiles)
1193       : MainObjFile(MainObjFile), CoverageFiles(CoverageFiles) {
1194     Data->swap(this->Coverage);
1195   }
1196 
1197   const std::string MainObjFile;
1198   std::vector<std::unique_ptr<CoverageDataWithObjectFile>> Coverage;
1199   const std::set<std::string> CoverageFiles;
1200 };
1201 
1202 } // namespace
1203 
main(int argc,char ** argv)1204 int main(int argc, char **argv) {
1205   // Print stack trace if we signal out.
1206   sys::PrintStackTraceOnErrorSignal(argv[0]);
1207   PrettyStackTraceProgram X(argc, argv);
1208   llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
1209 
1210   llvm::InitializeAllTargetInfos();
1211   llvm::InitializeAllTargetMCs();
1212   llvm::InitializeAllDisassemblers();
1213 
1214   cl::ParseCommandLineOptions(argc, argv, "Sanitizer Coverage Processing Tool");
1215 
1216   // -print doesn't need object files.
1217   if (Action == PrintAction) {
1218     auto CovData = CoverageData::readAndMerge(ClInputFiles);
1219     FailIfError(CovData);
1220     CovData.get()->printAddrs(outs());
1221     return 0;
1222   } else if (Action == PrintCovPointsAction) {
1223     // -print-coverage-points doesn't need coverage files.
1224     for (const std::string &ObjFile : ClInputFiles) {
1225       printCovPoints(ObjFile, outs());
1226     }
1227     return 0;
1228   }
1229 
1230   auto CovDataSet = CoverageDataSet::readCmdArguments(ClInputFiles);
1231   FailIfError(CovDataSet);
1232 
1233   if (CovDataSet.get()->empty()) {
1234     Fail("No coverage files specified.");
1235   }
1236 
1237   switch (Action) {
1238   case CoveredFunctionsAction: {
1239     CovDataSet.get()->printCoveredFunctions(outs());
1240     return 0;
1241   }
1242   case NotCoveredFunctionsAction: {
1243     CovDataSet.get()->printNotCoveredFunctions(outs());
1244     return 0;
1245   }
1246   case HtmlReportAction: {
1247     CovDataSet.get()->printReport(outs());
1248     return 0;
1249   }
1250   case StatsAction: {
1251     CovDataSet.get()->printStats(outs());
1252     return 0;
1253   }
1254   case PrintAction:
1255   case PrintCovPointsAction:
1256     llvm_unreachable("unsupported action");
1257   }
1258 }
1259