xref: /llvm-project/llvm/tools/sancov/sancov.cpp (revision dd647e3e608ed0b2bac7c588d5859b80ef4a5976)
1 //===-- sancov.cpp --------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 // This file is a command-line tool for reading and analyzing sanitizer
9 // coverage.
10 //===----------------------------------------------------------------------===//
11 #include "llvm/ADT/STLExtras.h"
12 #include "llvm/ADT/StringExtras.h"
13 #include "llvm/ADT/Twine.h"
14 #include "llvm/DebugInfo/Symbolize/SymbolizableModule.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/MCInstrAnalysis.h"
21 #include "llvm/MC/MCInstrInfo.h"
22 #include "llvm/MC/MCObjectFileInfo.h"
23 #include "llvm/MC/MCRegisterInfo.h"
24 #include "llvm/MC/MCSubtargetInfo.h"
25 #include "llvm/MC/MCTargetOptions.h"
26 #include "llvm/MC/TargetRegistry.h"
27 #include "llvm/Object/Archive.h"
28 #include "llvm/Object/Binary.h"
29 #include "llvm/Object/COFF.h"
30 #include "llvm/Object/MachO.h"
31 #include "llvm/Object/ObjectFile.h"
32 #include "llvm/Option/ArgList.h"
33 #include "llvm/Option/Option.h"
34 #include "llvm/Support/Casting.h"
35 #include "llvm/Support/CommandLine.h"
36 #include "llvm/Support/Errc.h"
37 #include "llvm/Support/ErrorOr.h"
38 #include "llvm/Support/FileSystem.h"
39 #include "llvm/Support/JSON.h"
40 #include "llvm/Support/LLVMDriver.h"
41 #include "llvm/Support/MD5.h"
42 #include "llvm/Support/MemoryBuffer.h"
43 #include "llvm/Support/Path.h"
44 #include "llvm/Support/Regex.h"
45 #include "llvm/Support/SHA1.h"
46 #include "llvm/Support/SourceMgr.h"
47 #include "llvm/Support/SpecialCaseList.h"
48 #include "llvm/Support/TargetSelect.h"
49 #include "llvm/Support/VirtualFileSystem.h"
50 #include "llvm/Support/YAMLParser.h"
51 #include "llvm/Support/raw_ostream.h"
52 
53 #include <set>
54 #include <vector>
55 
56 using namespace llvm;
57 
58 namespace {
59 
60 // Command-line option boilerplate.
61 namespace {
62 using namespace llvm::opt;
63 enum ID {
64   OPT_INVALID = 0, // This is not an option ID.
65 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
66 #include "Opts.inc"
67 #undef OPTION
68 };
69 
70 #define OPTTABLE_STR_TABLE_CODE
71 #include "Opts.inc"
72 #undef OPTTABLE_STR_TABLE_CODE
73 
74 #define OPTTABLE_PREFIXES_TABLE_CODE
75 #include "Opts.inc"
76 #undef OPTTABLE_PREFIXES_TABLE_CODE
77 
78 static constexpr opt::OptTable::Info InfoTable[] = {
79 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
80 #include "Opts.inc"
81 #undef OPTION
82 };
83 
84 class SancovOptTable : public opt::GenericOptTable {
85 public:
86   SancovOptTable()
87       : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
88 };
89 } // namespace
90 
91 // --------- COMMAND LINE FLAGS ---------
92 
93 enum ActionType {
94   CoveredFunctionsAction,
95   HtmlReportAction,
96   MergeAction,
97   NotCoveredFunctionsAction,
98   PrintAction,
99   PrintCovPointsAction,
100   StatsAction,
101   SymbolizeAction
102 };
103 
104 static ActionType Action;
105 static std::vector<std::string> ClInputFiles;
106 static bool ClDemangle;
107 static bool ClSkipDeadFiles;
108 static bool ClUseDefaultIgnorelist;
109 static std::string ClStripPathPrefix;
110 static std::string ClIgnorelist;
111 
112 static const char *const DefaultIgnorelistStr = "fun:__sanitizer_.*\n"
113                                                 "src:/usr/include/.*\n"
114                                                 "src:.*/libc\\+\\+/.*\n";
115 
116 // --------- FORMAT SPECIFICATION ---------
117 
118 struct FileHeader {
119   uint32_t Bitness;
120   uint32_t Magic;
121 };
122 
123 static const uint32_t BinCoverageMagic = 0xC0BFFFFF;
124 static const uint32_t Bitness32 = 0xFFFFFF32;
125 static const uint32_t Bitness64 = 0xFFFFFF64;
126 
127 static const Regex SancovFileRegex("(.*)\\.[0-9]+\\.sancov");
128 static const Regex SymcovFileRegex(".*\\.symcov");
129 
130 // --------- MAIN DATASTRUCTURES ----------
131 
132 // Contents of .sancov file: list of coverage point addresses that were
133 // executed.
134 struct RawCoverage {
135   explicit RawCoverage(std::unique_ptr<std::set<uint64_t>> Addrs)
136       : Addrs(std::move(Addrs)) {}
137 
138   // Read binary .sancov file.
139   static ErrorOr<std::unique_ptr<RawCoverage>>
140   read(const std::string &FileName);
141 
142   std::unique_ptr<std::set<uint64_t>> Addrs;
143 };
144 
145 // Coverage point has an opaque Id and corresponds to multiple source locations.
146 struct CoveragePoint {
147   explicit CoveragePoint(const std::string &Id) : Id(Id) {}
148 
149   std::string Id;
150   SmallVector<DILineInfo, 1> Locs;
151 };
152 
153 // Symcov file content: set of covered Ids plus information about all available
154 // coverage points.
155 struct SymbolizedCoverage {
156   // Read json .symcov file.
157   static std::unique_ptr<SymbolizedCoverage> read(const std::string &InputFile);
158 
159   std::set<std::string> CoveredIds;
160   std::string BinaryHash;
161   std::vector<CoveragePoint> Points;
162 };
163 
164 struct CoverageStats {
165   size_t AllPoints;
166   size_t CovPoints;
167   size_t AllFns;
168   size_t CovFns;
169 };
170 
171 // --------- ERROR HANDLING ---------
172 
173 static void fail(const llvm::Twine &E) {
174   errs() << "ERROR: " << E << "\n";
175   exit(1);
176 }
177 
178 static void failIf(bool B, const llvm::Twine &E) {
179   if (B)
180     fail(E);
181 }
182 
183 static void failIfError(std::error_code Error) {
184   if (!Error)
185     return;
186   errs() << "ERROR: " << Error.message() << "(" << Error.value() << ")\n";
187   exit(1);
188 }
189 
190 template <typename T> static void failIfError(const ErrorOr<T> &E) {
191   failIfError(E.getError());
192 }
193 
194 static void failIfError(Error Err) {
195   if (Err) {
196     logAllUnhandledErrors(std::move(Err), errs(), "ERROR: ");
197     exit(1);
198   }
199 }
200 
201 template <typename T> static void failIfError(Expected<T> &E) {
202   failIfError(E.takeError());
203 }
204 
205 static void failIfNotEmpty(const llvm::Twine &E) {
206   if (E.str().empty())
207     return;
208   fail(E);
209 }
210 
211 template <typename T>
212 static void failIfEmpty(const std::unique_ptr<T> &Ptr,
213                         const std::string &Message) {
214   if (Ptr.get())
215     return;
216   fail(Message);
217 }
218 
219 // ----------- Coverage I/O ----------
220 template <typename T>
221 static void readInts(const char *Start, const char *End,
222                      std::set<uint64_t> *Ints) {
223   const T *S = reinterpret_cast<const T *>(Start);
224   const T *E = reinterpret_cast<const T *>(End);
225   std::copy(S, E, std::inserter(*Ints, Ints->end()));
226 }
227 
228 ErrorOr<std::unique_ptr<RawCoverage>>
229 RawCoverage::read(const std::string &FileName) {
230   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
231       MemoryBuffer::getFile(FileName);
232   if (!BufOrErr)
233     return BufOrErr.getError();
234   std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
235   if (Buf->getBufferSize() < 8) {
236     errs() << "File too small (<8): " << Buf->getBufferSize() << '\n';
237     return make_error_code(errc::illegal_byte_sequence);
238   }
239   const FileHeader *Header =
240       reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
241 
242   if (Header->Magic != BinCoverageMagic) {
243     errs() << "Wrong magic: " << Header->Magic << '\n';
244     return make_error_code(errc::illegal_byte_sequence);
245   }
246 
247   auto Addrs = std::make_unique<std::set<uint64_t>>();
248 
249   switch (Header->Bitness) {
250   case Bitness64:
251     readInts<uint64_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
252                        Addrs.get());
253     break;
254   case Bitness32:
255     readInts<uint32_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
256                        Addrs.get());
257     break;
258   default:
259     errs() << "Unsupported bitness: " << Header->Bitness << '\n';
260     return make_error_code(errc::illegal_byte_sequence);
261   }
262 
263   // Ignore slots that are zero, so a runtime implementation is not required
264   // to compactify the data.
265   Addrs->erase(0);
266 
267   return std::make_unique<RawCoverage>(std::move(Addrs));
268 }
269 
270 // Print coverage addresses.
271 raw_ostream &operator<<(raw_ostream &OS, const RawCoverage &CoverageData) {
272   for (auto Addr : *CoverageData.Addrs) {
273     OS << "0x";
274     OS.write_hex(Addr);
275     OS << "\n";
276   }
277   return OS;
278 }
279 
280 static raw_ostream &operator<<(raw_ostream &OS, const CoverageStats &Stats) {
281   OS << "all-edges: " << Stats.AllPoints << "\n";
282   OS << "cov-edges: " << Stats.CovPoints << "\n";
283   OS << "all-functions: " << Stats.AllFns << "\n";
284   OS << "cov-functions: " << Stats.CovFns << "\n";
285   return OS;
286 }
287 
288 // Output symbolized information for coverage points in JSON.
289 // Format:
290 // {
291 //   '<file_name>' : {
292 //     '<function_name>' : {
293 //       '<point_id'> : '<line_number>:'<column_number'.
294 //          ....
295 //       }
296 //    }
297 // }
298 static void operator<<(json::OStream &W,
299                        const std::vector<CoveragePoint> &Points) {
300   // Group points by file.
301   std::map<std::string, std::vector<const CoveragePoint *>> PointsByFile;
302   for (const auto &Point : Points) {
303     for (const DILineInfo &Loc : Point.Locs) {
304       PointsByFile[Loc.FileName].push_back(&Point);
305     }
306   }
307 
308   for (const auto &P : PointsByFile) {
309     std::string FileName = P.first;
310     std::map<std::string, std::vector<const CoveragePoint *>> PointsByFn;
311     for (auto PointPtr : P.second) {
312       for (const DILineInfo &Loc : PointPtr->Locs) {
313         PointsByFn[Loc.FunctionName].push_back(PointPtr);
314       }
315     }
316 
317     W.attributeObject(P.first, [&] {
318       // Group points by function.
319       for (const auto &P : PointsByFn) {
320         std::string FunctionName = P.first;
321         std::set<std::string> WrittenIds;
322 
323         W.attributeObject(FunctionName, [&] {
324           for (const CoveragePoint *Point : P.second) {
325             for (const auto &Loc : Point->Locs) {
326               if (Loc.FileName != FileName || Loc.FunctionName != FunctionName)
327                 continue;
328               if (!WrittenIds.insert(Point->Id).second)
329                 continue;
330 
331               // Output <point_id> : "<line>:<col>".
332               W.attribute(Point->Id,
333                           (utostr(Loc.Line) + ":" + utostr(Loc.Column)));
334             }
335           }
336         });
337       }
338     });
339   }
340 }
341 
342 static void operator<<(json::OStream &W, const SymbolizedCoverage &C) {
343   W.object([&] {
344     W.attributeArray("covered-points", [&] {
345       for (const std::string &P : C.CoveredIds) {
346         W.value(P);
347       }
348     });
349     W.attribute("binary-hash", C.BinaryHash);
350     W.attributeObject("point-symbol-info", [&] { W << C.Points; });
351   });
352 }
353 
354 static std::string parseScalarString(yaml::Node *N) {
355   SmallString<64> StringStorage;
356   yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
357   failIf(!S, "expected string");
358   return std::string(S->getValue(StringStorage));
359 }
360 
361 std::unique_ptr<SymbolizedCoverage>
362 SymbolizedCoverage::read(const std::string &InputFile) {
363   auto Coverage(std::make_unique<SymbolizedCoverage>());
364 
365   std::map<std::string, CoveragePoint> Points;
366   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
367       MemoryBuffer::getFile(InputFile);
368   failIfError(BufOrErr);
369 
370   SourceMgr SM;
371   yaml::Stream S(**BufOrErr, SM);
372 
373   yaml::document_iterator DI = S.begin();
374   failIf(DI == S.end(), "empty document: " + InputFile);
375   yaml::Node *Root = DI->getRoot();
376   failIf(!Root, "expecting root node: " + InputFile);
377   yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
378   failIf(!Top, "expecting mapping node: " + InputFile);
379 
380   for (auto &KVNode : *Top) {
381     auto Key = parseScalarString(KVNode.getKey());
382 
383     if (Key == "covered-points") {
384       yaml::SequenceNode *Points =
385           dyn_cast<yaml::SequenceNode>(KVNode.getValue());
386       failIf(!Points, "expected array: " + InputFile);
387 
388       for (auto I = Points->begin(), E = Points->end(); I != E; ++I) {
389         Coverage->CoveredIds.insert(parseScalarString(&*I));
390       }
391     } else if (Key == "binary-hash") {
392       Coverage->BinaryHash = parseScalarString(KVNode.getValue());
393     } else if (Key == "point-symbol-info") {
394       yaml::MappingNode *PointSymbolInfo =
395           dyn_cast<yaml::MappingNode>(KVNode.getValue());
396       failIf(!PointSymbolInfo, "expected mapping node: " + InputFile);
397 
398       for (auto &FileKVNode : *PointSymbolInfo) {
399         auto Filename = parseScalarString(FileKVNode.getKey());
400 
401         yaml::MappingNode *FileInfo =
402             dyn_cast<yaml::MappingNode>(FileKVNode.getValue());
403         failIf(!FileInfo, "expected mapping node: " + InputFile);
404 
405         for (auto &FunctionKVNode : *FileInfo) {
406           auto FunctionName = parseScalarString(FunctionKVNode.getKey());
407 
408           yaml::MappingNode *FunctionInfo =
409               dyn_cast<yaml::MappingNode>(FunctionKVNode.getValue());
410           failIf(!FunctionInfo, "expected mapping node: " + InputFile);
411 
412           for (auto &PointKVNode : *FunctionInfo) {
413             auto PointId = parseScalarString(PointKVNode.getKey());
414             auto Loc = parseScalarString(PointKVNode.getValue());
415 
416             size_t ColonPos = Loc.find(':');
417             failIf(ColonPos == std::string::npos, "expected ':': " + InputFile);
418 
419             auto LineStr = Loc.substr(0, ColonPos);
420             auto ColStr = Loc.substr(ColonPos + 1, Loc.size());
421 
422             DILineInfo LineInfo;
423             LineInfo.FileName = Filename;
424             LineInfo.FunctionName = FunctionName;
425             char *End;
426             LineInfo.Line = std::strtoul(LineStr.c_str(), &End, 10);
427             LineInfo.Column = std::strtoul(ColStr.c_str(), &End, 10);
428 
429             CoveragePoint *CoveragePoint =
430                 &Points.try_emplace(PointId, PointId).first->second;
431             CoveragePoint->Locs.push_back(LineInfo);
432           }
433         }
434       }
435     } else {
436       errs() << "Ignoring unknown key: " << Key << "\n";
437     }
438   }
439 
440   for (auto &KV : Points) {
441     Coverage->Points.push_back(KV.second);
442   }
443 
444   return Coverage;
445 }
446 
447 // ---------- MAIN FUNCTIONALITY ----------
448 
449 std::string stripPathPrefix(std::string Path) {
450   if (ClStripPathPrefix.empty())
451     return Path;
452   size_t Pos = Path.find(ClStripPathPrefix);
453   if (Pos == std::string::npos)
454     return Path;
455   return Path.substr(Pos + ClStripPathPrefix.size());
456 }
457 
458 static std::unique_ptr<symbolize::LLVMSymbolizer> createSymbolizer() {
459   symbolize::LLVMSymbolizer::Options SymbolizerOptions;
460   SymbolizerOptions.Demangle = ClDemangle;
461   SymbolizerOptions.UseSymbolTable = true;
462   return std::make_unique<symbolize::LLVMSymbolizer>(SymbolizerOptions);
463 }
464 
465 static std::string normalizeFilename(const std::string &FileName) {
466   SmallString<256> S(FileName);
467   sys::path::remove_dots(S, /* remove_dot_dot */ true);
468   return stripPathPrefix(sys::path::convert_to_slash(std::string(S)));
469 }
470 
471 class Ignorelists {
472 public:
473   Ignorelists()
474       : DefaultIgnorelist(createDefaultIgnorelist()),
475         UserIgnorelist(createUserIgnorelist()) {}
476 
477   bool isIgnorelisted(const DILineInfo &I) {
478     if (DefaultIgnorelist &&
479         DefaultIgnorelist->inSection("sancov", "fun", I.FunctionName))
480       return true;
481     if (DefaultIgnorelist &&
482         DefaultIgnorelist->inSection("sancov", "src", I.FileName))
483       return true;
484     if (UserIgnorelist &&
485         UserIgnorelist->inSection("sancov", "fun", I.FunctionName))
486       return true;
487     if (UserIgnorelist &&
488         UserIgnorelist->inSection("sancov", "src", I.FileName))
489       return true;
490     return false;
491   }
492 
493 private:
494   static std::unique_ptr<SpecialCaseList> createDefaultIgnorelist() {
495     if (!ClUseDefaultIgnorelist)
496       return std::unique_ptr<SpecialCaseList>();
497     std::unique_ptr<MemoryBuffer> MB =
498         MemoryBuffer::getMemBuffer(DefaultIgnorelistStr);
499     std::string Error;
500     auto Ignorelist = SpecialCaseList::create(MB.get(), Error);
501     failIfNotEmpty(Error);
502     return Ignorelist;
503   }
504 
505   static std::unique_ptr<SpecialCaseList> createUserIgnorelist() {
506     if (ClIgnorelist.empty())
507       return std::unique_ptr<SpecialCaseList>();
508     return SpecialCaseList::createOrDie({{ClIgnorelist}},
509                                         *vfs::getRealFileSystem());
510   }
511   std::unique_ptr<SpecialCaseList> DefaultIgnorelist;
512   std::unique_ptr<SpecialCaseList> UserIgnorelist;
513 };
514 
515 static std::vector<CoveragePoint>
516 getCoveragePoints(const std::string &ObjectFile,
517                   const std::set<uint64_t> &Addrs,
518                   const std::set<uint64_t> &CoveredAddrs) {
519   std::vector<CoveragePoint> Result;
520   auto Symbolizer(createSymbolizer());
521   Ignorelists Ig;
522 
523   std::set<std::string> CoveredFiles;
524   if (ClSkipDeadFiles) {
525     for (auto Addr : CoveredAddrs) {
526       // TODO: it would be neccessary to set proper section index here.
527       // object::SectionedAddress::UndefSection works for only absolute
528       // addresses.
529       object::SectionedAddress ModuleAddress = {
530           Addr, object::SectionedAddress::UndefSection};
531 
532       auto LineInfo = Symbolizer->symbolizeCode(ObjectFile, ModuleAddress);
533       failIfError(LineInfo);
534       CoveredFiles.insert(LineInfo->FileName);
535       auto InliningInfo =
536           Symbolizer->symbolizeInlinedCode(ObjectFile, ModuleAddress);
537       failIfError(InliningInfo);
538       for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
539         auto FrameInfo = InliningInfo->getFrame(I);
540         CoveredFiles.insert(FrameInfo.FileName);
541       }
542     }
543   }
544 
545   for (auto Addr : Addrs) {
546     std::set<DILineInfo> Infos; // deduplicate debug info.
547 
548     // TODO: it would be neccessary to set proper section index here.
549     // object::SectionedAddress::UndefSection works for only absolute addresses.
550     object::SectionedAddress ModuleAddress = {
551         Addr, object::SectionedAddress::UndefSection};
552 
553     auto LineInfo = Symbolizer->symbolizeCode(ObjectFile, ModuleAddress);
554     failIfError(LineInfo);
555     if (ClSkipDeadFiles &&
556         CoveredFiles.find(LineInfo->FileName) == CoveredFiles.end())
557       continue;
558     LineInfo->FileName = normalizeFilename(LineInfo->FileName);
559     if (Ig.isIgnorelisted(*LineInfo))
560       continue;
561 
562     auto Id = utohexstr(Addr, true);
563     auto Point = CoveragePoint(Id);
564     Infos.insert(*LineInfo);
565     Point.Locs.push_back(*LineInfo);
566 
567     auto InliningInfo =
568         Symbolizer->symbolizeInlinedCode(ObjectFile, ModuleAddress);
569     failIfError(InliningInfo);
570     for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
571       auto FrameInfo = InliningInfo->getFrame(I);
572       if (ClSkipDeadFiles &&
573           CoveredFiles.find(FrameInfo.FileName) == CoveredFiles.end())
574         continue;
575       FrameInfo.FileName = normalizeFilename(FrameInfo.FileName);
576       if (Ig.isIgnorelisted(FrameInfo))
577         continue;
578       if (Infos.insert(FrameInfo).second)
579         Point.Locs.push_back(FrameInfo);
580     }
581 
582     Result.push_back(Point);
583   }
584 
585   return Result;
586 }
587 
588 static bool isCoveragePointSymbol(StringRef Name) {
589   return Name == "__sanitizer_cov" || Name == "__sanitizer_cov_with_check" ||
590          Name == "__sanitizer_cov_trace_func_enter" ||
591          Name == "__sanitizer_cov_trace_pc_guard" ||
592          // Mac has '___' prefix
593          Name == "___sanitizer_cov" || Name == "___sanitizer_cov_with_check" ||
594          Name == "___sanitizer_cov_trace_func_enter" ||
595          Name == "___sanitizer_cov_trace_pc_guard";
596 }
597 
598 // Locate __sanitizer_cov* function addresses inside the stubs table on MachO.
599 static void findMachOIndirectCovFunctions(const object::MachOObjectFile &O,
600                                           std::set<uint64_t> *Result) {
601   MachO::dysymtab_command Dysymtab = O.getDysymtabLoadCommand();
602   MachO::symtab_command Symtab = O.getSymtabLoadCommand();
603 
604   for (const auto &Load : O.load_commands()) {
605     if (Load.C.cmd == MachO::LC_SEGMENT_64) {
606       MachO::segment_command_64 Seg = O.getSegment64LoadCommand(Load);
607       for (unsigned J = 0; J < Seg.nsects; ++J) {
608         MachO::section_64 Sec = O.getSection64(Load, J);
609 
610         uint32_t SectionType = Sec.flags & MachO::SECTION_TYPE;
611         if (SectionType == MachO::S_SYMBOL_STUBS) {
612           uint32_t Stride = Sec.reserved2;
613           uint32_t Cnt = Sec.size / Stride;
614           uint32_t N = Sec.reserved1;
615           for (uint32_t J = 0; J < Cnt && N + J < Dysymtab.nindirectsyms; J++) {
616             uint32_t IndirectSymbol =
617                 O.getIndirectSymbolTableEntry(Dysymtab, N + J);
618             uint64_t Addr = Sec.addr + J * Stride;
619             if (IndirectSymbol < Symtab.nsyms) {
620               object::SymbolRef Symbol = *(O.getSymbolByIndex(IndirectSymbol));
621               Expected<StringRef> Name = Symbol.getName();
622               failIfError(Name);
623               if (isCoveragePointSymbol(Name.get())) {
624                 Result->insert(Addr);
625               }
626             }
627           }
628         }
629       }
630     }
631     if (Load.C.cmd == MachO::LC_SEGMENT) {
632       errs() << "ERROR: 32 bit MachO binaries not supported\n";
633     }
634   }
635 }
636 
637 // Locate __sanitizer_cov* function addresses that are used for coverage
638 // reporting.
639 static std::set<uint64_t>
640 findSanitizerCovFunctions(const object::ObjectFile &O) {
641   std::set<uint64_t> Result;
642 
643   for (const object::SymbolRef &Symbol : O.symbols()) {
644     Expected<uint64_t> AddressOrErr = Symbol.getAddress();
645     failIfError(AddressOrErr);
646     uint64_t Address = AddressOrErr.get();
647 
648     Expected<StringRef> NameOrErr = Symbol.getName();
649     failIfError(NameOrErr);
650     StringRef Name = NameOrErr.get();
651 
652     Expected<uint32_t> FlagsOrErr = Symbol.getFlags();
653     // TODO: Test this error.
654     failIfError(FlagsOrErr);
655     uint32_t Flags = FlagsOrErr.get();
656 
657     if (!(Flags & object::BasicSymbolRef::SF_Undefined) &&
658         isCoveragePointSymbol(Name)) {
659       Result.insert(Address);
660     }
661   }
662 
663   if (const auto *CO = dyn_cast<object::COFFObjectFile>(&O)) {
664     for (const object::ExportDirectoryEntryRef &Export :
665          CO->export_directories()) {
666       uint32_t RVA;
667       failIfError(Export.getExportRVA(RVA));
668 
669       StringRef Name;
670       failIfError(Export.getSymbolName(Name));
671 
672       if (isCoveragePointSymbol(Name))
673         Result.insert(CO->getImageBase() + RVA);
674     }
675   }
676 
677   if (const auto *MO = dyn_cast<object::MachOObjectFile>(&O)) {
678     findMachOIndirectCovFunctions(*MO, &Result);
679   }
680 
681   return Result;
682 }
683 
684 // Ported from
685 // compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h:GetPreviousInstructionPc
686 // GetPreviousInstructionPc.
687 static uint64_t getPreviousInstructionPc(uint64_t PC, Triple TheTriple) {
688   if (TheTriple.isARM())
689     return (PC - 3) & (~1);
690   if (TheTriple.isMIPS() || TheTriple.isSPARC())
691     return PC - 8;
692   if (TheTriple.isRISCV())
693     return PC - 2;
694   if (TheTriple.isX86() || TheTriple.isSystemZ())
695     return PC - 1;
696   return PC - 4;
697 }
698 
699 // Locate addresses of all coverage points in a file. Coverage point
700 // is defined as the 'address of instruction following __sanitizer_cov
701 // call - 1'.
702 static void getObjectCoveragePoints(const object::ObjectFile &O,
703                                     std::set<uint64_t> *Addrs) {
704   Triple TheTriple("unknown-unknown-unknown");
705   TheTriple.setArch(Triple::ArchType(O.getArch()));
706   auto TripleName = TheTriple.getTriple();
707 
708   std::string Error;
709   const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error);
710   failIfNotEmpty(Error);
711 
712   std::unique_ptr<const MCSubtargetInfo> STI(
713       TheTarget->createMCSubtargetInfo(TripleName, "", ""));
714   failIfEmpty(STI, "no subtarget info for target " + TripleName);
715 
716   std::unique_ptr<const MCRegisterInfo> MRI(
717       TheTarget->createMCRegInfo(TripleName));
718   failIfEmpty(MRI, "no register info for target " + TripleName);
719 
720   MCTargetOptions MCOptions;
721   std::unique_ptr<const MCAsmInfo> AsmInfo(
722       TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
723   failIfEmpty(AsmInfo, "no asm info for target " + TripleName);
724 
725   MCContext Ctx(TheTriple, AsmInfo.get(), MRI.get(), STI.get());
726   std::unique_ptr<MCDisassembler> DisAsm(
727       TheTarget->createMCDisassembler(*STI, Ctx));
728   failIfEmpty(DisAsm, "no disassembler info for target " + TripleName);
729 
730   std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo());
731   failIfEmpty(MII, "no instruction info for target " + TripleName);
732 
733   std::unique_ptr<const MCInstrAnalysis> MIA(
734       TheTarget->createMCInstrAnalysis(MII.get()));
735   failIfEmpty(MIA, "no instruction analysis info for target " + TripleName);
736 
737   auto SanCovAddrs = findSanitizerCovFunctions(O);
738   if (SanCovAddrs.empty())
739     fail("__sanitizer_cov* functions not found");
740 
741   for (object::SectionRef Section : O.sections()) {
742     if (Section.isVirtual() || !Section.isText()) // llvm-objdump does the same.
743       continue;
744     uint64_t SectionAddr = Section.getAddress();
745     uint64_t SectSize = Section.getSize();
746     if (!SectSize)
747       continue;
748 
749     Expected<StringRef> BytesStr = Section.getContents();
750     failIfError(BytesStr);
751     ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(*BytesStr);
752 
753     for (uint64_t Index = 0, Size = 0; Index < Section.getSize();
754          Index += Size) {
755       MCInst Inst;
756       ArrayRef<uint8_t> ThisBytes = Bytes.slice(Index);
757       uint64_t ThisAddr = SectionAddr + Index;
758       if (!DisAsm->getInstruction(Inst, Size, ThisBytes, ThisAddr, nulls())) {
759         if (Size == 0)
760           Size = std::min<uint64_t>(
761               ThisBytes.size(),
762               DisAsm->suggestBytesToSkip(ThisBytes, ThisAddr));
763         continue;
764       }
765       uint64_t Addr = Index + SectionAddr;
766       // Sanitizer coverage uses the address of the next instruction - 1.
767       uint64_t CovPoint = getPreviousInstructionPc(Addr + Size, TheTriple);
768       uint64_t Target;
769       if (MIA->isCall(Inst) &&
770           MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target) &&
771           SanCovAddrs.find(Target) != SanCovAddrs.end())
772         Addrs->insert(CovPoint);
773     }
774   }
775 }
776 
777 static void
778 visitObjectFiles(const object::Archive &A,
779                  function_ref<void(const object::ObjectFile &)> Fn) {
780   Error Err = Error::success();
781   for (auto &C : A.children(Err)) {
782     Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary();
783     failIfError(ChildOrErr);
784     if (auto *O = dyn_cast<object::ObjectFile>(&*ChildOrErr.get()))
785       Fn(*O);
786     else
787       failIfError(object::object_error::invalid_file_type);
788   }
789   failIfError(std::move(Err));
790 }
791 
792 static void
793 visitObjectFiles(const std::string &FileName,
794                  function_ref<void(const object::ObjectFile &)> Fn) {
795   Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
796       object::createBinary(FileName);
797   if (!BinaryOrErr)
798     failIfError(BinaryOrErr);
799 
800   object::Binary &Binary = *BinaryOrErr.get().getBinary();
801   if (object::Archive *A = dyn_cast<object::Archive>(&Binary))
802     visitObjectFiles(*A, Fn);
803   else if (object::ObjectFile *O = dyn_cast<object::ObjectFile>(&Binary))
804     Fn(*O);
805   else
806     failIfError(object::object_error::invalid_file_type);
807 }
808 
809 static std::set<uint64_t>
810 findSanitizerCovFunctions(const std::string &FileName) {
811   std::set<uint64_t> Result;
812   visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
813     auto Addrs = findSanitizerCovFunctions(O);
814     Result.insert(Addrs.begin(), Addrs.end());
815   });
816   return Result;
817 }
818 
819 // Locate addresses of all coverage points in a file. Coverage point
820 // is defined as the 'address of instruction following __sanitizer_cov
821 // call - 1'.
822 static std::set<uint64_t> findCoveragePointAddrs(const std::string &FileName) {
823   std::set<uint64_t> Result;
824   visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
825     getObjectCoveragePoints(O, &Result);
826   });
827   return Result;
828 }
829 
830 static void printCovPoints(const std::string &ObjFile, raw_ostream &OS) {
831   for (uint64_t Addr : findCoveragePointAddrs(ObjFile)) {
832     OS << "0x";
833     OS.write_hex(Addr);
834     OS << "\n";
835   }
836 }
837 
838 static ErrorOr<bool> isCoverageFile(const std::string &FileName) {
839   auto ShortFileName = llvm::sys::path::filename(FileName);
840   if (!SancovFileRegex.match(ShortFileName))
841     return false;
842 
843   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
844       MemoryBuffer::getFile(FileName);
845   if (!BufOrErr) {
846     errs() << "Warning: " << BufOrErr.getError().message() << "("
847            << BufOrErr.getError().value()
848            << "), filename: " << llvm::sys::path::filename(FileName) << "\n";
849     return BufOrErr.getError();
850   }
851   std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
852   if (Buf->getBufferSize() < 8) {
853     return false;
854   }
855   const FileHeader *Header =
856       reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
857   return Header->Magic == BinCoverageMagic;
858 }
859 
860 static bool isSymbolizedCoverageFile(const std::string &FileName) {
861   auto ShortFileName = llvm::sys::path::filename(FileName);
862   return SymcovFileRegex.match(ShortFileName);
863 }
864 
865 static std::unique_ptr<SymbolizedCoverage>
866 symbolize(const RawCoverage &Data, const std::string ObjectFile) {
867   auto Coverage = std::make_unique<SymbolizedCoverage>();
868 
869   ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
870       MemoryBuffer::getFile(ObjectFile);
871   failIfError(BufOrErr);
872   SHA1 Hasher;
873   Hasher.update((*BufOrErr)->getBuffer());
874   Coverage->BinaryHash = toHex(Hasher.final());
875 
876   Ignorelists Ig;
877   auto Symbolizer(createSymbolizer());
878 
879   for (uint64_t Addr : *Data.Addrs) {
880     // TODO: it would be neccessary to set proper section index here.
881     // object::SectionedAddress::UndefSection works for only absolute addresses.
882     auto LineInfo = Symbolizer->symbolizeCode(
883         ObjectFile, {Addr, object::SectionedAddress::UndefSection});
884     failIfError(LineInfo);
885     if (Ig.isIgnorelisted(*LineInfo))
886       continue;
887 
888     Coverage->CoveredIds.insert(utohexstr(Addr, true));
889   }
890 
891   std::set<uint64_t> AllAddrs = findCoveragePointAddrs(ObjectFile);
892   if (!std::includes(AllAddrs.begin(), AllAddrs.end(), Data.Addrs->begin(),
893                      Data.Addrs->end())) {
894     fail("Coverage points in binary and .sancov file do not match.");
895   }
896   Coverage->Points = getCoveragePoints(ObjectFile, AllAddrs, *Data.Addrs);
897   return Coverage;
898 }
899 
900 struct FileFn {
901   bool operator<(const FileFn &RHS) const {
902     return std::tie(FileName, FunctionName) <
903            std::tie(RHS.FileName, RHS.FunctionName);
904   }
905 
906   std::string FileName;
907   std::string FunctionName;
908 };
909 
910 static std::set<FileFn>
911 computeFunctions(const std::vector<CoveragePoint> &Points) {
912   std::set<FileFn> Fns;
913   for (const auto &Point : Points) {
914     for (const auto &Loc : Point.Locs) {
915       Fns.insert(FileFn{Loc.FileName, Loc.FunctionName});
916     }
917   }
918   return Fns;
919 }
920 
921 static std::set<FileFn>
922 computeNotCoveredFunctions(const SymbolizedCoverage &Coverage) {
923   auto Fns = computeFunctions(Coverage.Points);
924 
925   for (const auto &Point : Coverage.Points) {
926     if (Coverage.CoveredIds.find(Point.Id) == Coverage.CoveredIds.end())
927       continue;
928 
929     for (const auto &Loc : Point.Locs) {
930       Fns.erase(FileFn{Loc.FileName, Loc.FunctionName});
931     }
932   }
933 
934   return Fns;
935 }
936 
937 static std::set<FileFn>
938 computeCoveredFunctions(const SymbolizedCoverage &Coverage) {
939   auto AllFns = computeFunctions(Coverage.Points);
940   std::set<FileFn> Result;
941 
942   for (const auto &Point : Coverage.Points) {
943     if (Coverage.CoveredIds.find(Point.Id) == Coverage.CoveredIds.end())
944       continue;
945 
946     for (const auto &Loc : Point.Locs) {
947       Result.insert(FileFn{Loc.FileName, Loc.FunctionName});
948     }
949   }
950 
951   return Result;
952 }
953 
954 typedef std::map<FileFn, std::pair<uint32_t, uint32_t>> FunctionLocs;
955 // finds first location in a file for each function.
956 static FunctionLocs resolveFunctions(const SymbolizedCoverage &Coverage,
957                                      const std::set<FileFn> &Fns) {
958   FunctionLocs Result;
959   for (const auto &Point : Coverage.Points) {
960     for (const auto &Loc : Point.Locs) {
961       FileFn Fn = FileFn{Loc.FileName, Loc.FunctionName};
962       if (Fns.find(Fn) == Fns.end())
963         continue;
964 
965       auto P = std::make_pair(Loc.Line, Loc.Column);
966       auto [It, Inserted] = Result.try_emplace(Fn, P);
967       if (!Inserted && It->second > P)
968         It->second = P;
969     }
970   }
971   return Result;
972 }
973 
974 static void printFunctionLocs(const FunctionLocs &FnLocs, raw_ostream &OS) {
975   for (const auto &P : FnLocs) {
976     OS << stripPathPrefix(P.first.FileName) << ":" << P.second.first << " "
977        << P.first.FunctionName << "\n";
978   }
979 }
980 CoverageStats computeStats(const SymbolizedCoverage &Coverage) {
981   CoverageStats Stats = {Coverage.Points.size(), Coverage.CoveredIds.size(),
982                          computeFunctions(Coverage.Points).size(),
983                          computeCoveredFunctions(Coverage).size()};
984   return Stats;
985 }
986 
987 // Print list of covered functions.
988 // Line format: <file_name>:<line> <function_name>
989 static void printCoveredFunctions(const SymbolizedCoverage &CovData,
990                                   raw_ostream &OS) {
991   auto CoveredFns = computeCoveredFunctions(CovData);
992   printFunctionLocs(resolveFunctions(CovData, CoveredFns), OS);
993 }
994 
995 // Print list of not covered functions.
996 // Line format: <file_name>:<line> <function_name>
997 static void printNotCoveredFunctions(const SymbolizedCoverage &CovData,
998                                      raw_ostream &OS) {
999   auto NotCoveredFns = computeNotCoveredFunctions(CovData);
1000   printFunctionLocs(resolveFunctions(CovData, NotCoveredFns), OS);
1001 }
1002 
1003 // Read list of files and merges their coverage info.
1004 static void readAndPrintRawCoverage(const std::vector<std::string> &FileNames,
1005                                     raw_ostream &OS) {
1006   std::vector<std::unique_ptr<RawCoverage>> Covs;
1007   for (const auto &FileName : FileNames) {
1008     auto Cov = RawCoverage::read(FileName);
1009     if (!Cov)
1010       continue;
1011     OS << *Cov.get();
1012   }
1013 }
1014 
1015 static std::unique_ptr<SymbolizedCoverage>
1016 merge(const std::vector<std::unique_ptr<SymbolizedCoverage>> &Coverages) {
1017   if (Coverages.empty())
1018     return nullptr;
1019 
1020   auto Result = std::make_unique<SymbolizedCoverage>();
1021 
1022   for (size_t I = 0; I < Coverages.size(); ++I) {
1023     const SymbolizedCoverage &Coverage = *Coverages[I];
1024     std::string Prefix;
1025     if (Coverages.size() > 1) {
1026       // prefix is not needed when there's only one file.
1027       Prefix = utostr(I);
1028     }
1029 
1030     for (const auto &Id : Coverage.CoveredIds) {
1031       Result->CoveredIds.insert(Prefix + Id);
1032     }
1033 
1034     for (const auto &CovPoint : Coverage.Points) {
1035       CoveragePoint NewPoint(CovPoint);
1036       NewPoint.Id = Prefix + CovPoint.Id;
1037       Result->Points.push_back(NewPoint);
1038     }
1039   }
1040 
1041   if (Coverages.size() == 1) {
1042     Result->BinaryHash = Coverages[0]->BinaryHash;
1043   }
1044 
1045   return Result;
1046 }
1047 
1048 static std::unique_ptr<SymbolizedCoverage>
1049 readSymbolizeAndMergeCmdArguments(std::vector<std::string> FileNames) {
1050   std::vector<std::unique_ptr<SymbolizedCoverage>> Coverages;
1051 
1052   {
1053     // Short name => file name.
1054     std::map<std::string, std::string, std::less<>> ObjFiles;
1055     std::string FirstObjFile;
1056     std::set<std::string> CovFiles;
1057 
1058     // Partition input values into coverage/object files.
1059     for (const auto &FileName : FileNames) {
1060       if (isSymbolizedCoverageFile(FileName)) {
1061         Coverages.push_back(SymbolizedCoverage::read(FileName));
1062       }
1063 
1064       auto ErrorOrIsCoverage = isCoverageFile(FileName);
1065       if (!ErrorOrIsCoverage)
1066         continue;
1067       if (ErrorOrIsCoverage.get()) {
1068         CovFiles.insert(FileName);
1069       } else {
1070         auto ShortFileName = llvm::sys::path::filename(FileName);
1071         if (ObjFiles.find(ShortFileName) != ObjFiles.end()) {
1072           fail("Duplicate binary file with a short name: " + ShortFileName);
1073         }
1074 
1075         ObjFiles[std::string(ShortFileName)] = FileName;
1076         if (FirstObjFile.empty())
1077           FirstObjFile = FileName;
1078       }
1079     }
1080 
1081     SmallVector<StringRef, 2> Components;
1082 
1083     // Object file => list of corresponding coverage file names.
1084     std::map<std::string, std::vector<std::string>> CoverageByObjFile;
1085     for (const auto &FileName : CovFiles) {
1086       auto ShortFileName = llvm::sys::path::filename(FileName);
1087       auto Ok = SancovFileRegex.match(ShortFileName, &Components);
1088       if (!Ok) {
1089         fail("Can't match coverage file name against "
1090              "<module_name>.<pid>.sancov pattern: " +
1091              FileName);
1092       }
1093 
1094       auto Iter = ObjFiles.find(Components[1]);
1095       if (Iter == ObjFiles.end()) {
1096         fail("Object file for coverage not found: " + FileName);
1097       }
1098 
1099       CoverageByObjFile[Iter->second].push_back(FileName);
1100     };
1101 
1102     for (const auto &Pair : ObjFiles) {
1103       auto FileName = Pair.second;
1104       if (CoverageByObjFile.find(FileName) == CoverageByObjFile.end())
1105         errs() << "WARNING: No coverage file for " << FileName << "\n";
1106     }
1107 
1108     // Read raw coverage and symbolize it.
1109     for (const auto &Pair : CoverageByObjFile) {
1110       if (findSanitizerCovFunctions(Pair.first).empty()) {
1111         errs()
1112             << "WARNING: Ignoring " << Pair.first
1113             << " and its coverage because  __sanitizer_cov* functions were not "
1114                "found.\n";
1115         continue;
1116       }
1117 
1118       for (const std::string &CoverageFile : Pair.second) {
1119         auto DataOrError = RawCoverage::read(CoverageFile);
1120         failIfError(DataOrError);
1121         Coverages.push_back(symbolize(*DataOrError.get(), Pair.first));
1122       }
1123     }
1124   }
1125 
1126   return merge(Coverages);
1127 }
1128 
1129 } // namespace
1130 
1131 static void parseArgs(int Argc, char **Argv) {
1132   SancovOptTable Tbl;
1133   llvm::BumpPtrAllocator A;
1134   llvm::StringSaver Saver{A};
1135   opt::InputArgList Args =
1136       Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
1137         llvm::errs() << Msg << '\n';
1138         std::exit(1);
1139       });
1140 
1141   if (Args.hasArg(OPT_help)) {
1142     Tbl.printHelp(
1143         llvm::outs(),
1144         "sancov [options] <action> <binary files...> <.sancov files...> "
1145         "<.symcov files...>",
1146         "Sanitizer Coverage Processing Tool (sancov)\n\n"
1147         "  This tool can extract various coverage-related information from: \n"
1148         "  coverage-instrumented binary files, raw .sancov files and their "
1149         "symbolized .symcov version.\n"
1150         "  Depending on chosen action the tool expects different input files:\n"
1151         "    -print-coverage-pcs     - coverage-instrumented binary files\n"
1152         "    -print-coverage         - .sancov files\n"
1153         "    <other actions>         - .sancov files & corresponding binary "
1154         "files, .symcov files\n");
1155     std::exit(0);
1156   }
1157 
1158   if (Args.hasArg(OPT_version)) {
1159     cl::PrintVersionMessage();
1160     std::exit(0);
1161   }
1162 
1163   if (Args.hasMultipleArgs(OPT_action_grp)) {
1164     fail("Only one action option is allowed");
1165   }
1166 
1167   for (const opt::Arg *A : Args.filtered(OPT_INPUT)) {
1168     ClInputFiles.emplace_back(A->getValue());
1169   }
1170 
1171   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_action_grp)) {
1172     switch (A->getOption().getID()) {
1173     case OPT_print:
1174       Action = ActionType::PrintAction;
1175       break;
1176     case OPT_printCoveragePcs:
1177       Action = ActionType::PrintCovPointsAction;
1178       break;
1179     case OPT_coveredFunctions:
1180       Action = ActionType::CoveredFunctionsAction;
1181       break;
1182     case OPT_notCoveredFunctions:
1183       Action = ActionType::NotCoveredFunctionsAction;
1184       break;
1185     case OPT_printCoverageStats:
1186       Action = ActionType::StatsAction;
1187       break;
1188     case OPT_htmlReport:
1189       Action = ActionType::HtmlReportAction;
1190       break;
1191     case OPT_symbolize:
1192       Action = ActionType::SymbolizeAction;
1193       break;
1194     case OPT_merge:
1195       Action = ActionType::MergeAction;
1196       break;
1197     default:
1198       fail("Invalid Action");
1199     }
1200   }
1201 
1202   ClDemangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
1203   ClSkipDeadFiles = Args.hasFlag(OPT_skipDeadFiles, OPT_no_skipDeadFiles, true);
1204   ClUseDefaultIgnorelist =
1205       Args.hasFlag(OPT_useDefaultIgnoreList, OPT_no_useDefaultIgnoreList, true);
1206 
1207   ClStripPathPrefix = Args.getLastArgValue(OPT_stripPathPrefix_EQ);
1208   ClIgnorelist = Args.getLastArgValue(OPT_ignorelist_EQ);
1209 }
1210 
1211 int sancov_main(int Argc, char **Argv, const llvm::ToolContext &) {
1212   llvm::InitializeAllTargetInfos();
1213   llvm::InitializeAllTargetMCs();
1214   llvm::InitializeAllDisassemblers();
1215 
1216   parseArgs(Argc, Argv);
1217 
1218   // -print doesn't need object files.
1219   if (Action == PrintAction) {
1220     readAndPrintRawCoverage(ClInputFiles, outs());
1221     return 0;
1222   }
1223   if (Action == PrintCovPointsAction) {
1224     // -print-coverage-points doesn't need coverage files.
1225     for (const std::string &ObjFile : ClInputFiles) {
1226       printCovPoints(ObjFile, outs());
1227     }
1228     return 0;
1229   }
1230 
1231   auto Coverage = readSymbolizeAndMergeCmdArguments(ClInputFiles);
1232   failIf(!Coverage, "No valid coverage files given.");
1233 
1234   switch (Action) {
1235   case CoveredFunctionsAction: {
1236     printCoveredFunctions(*Coverage, outs());
1237     return 0;
1238   }
1239   case NotCoveredFunctionsAction: {
1240     printNotCoveredFunctions(*Coverage, outs());
1241     return 0;
1242   }
1243   case StatsAction: {
1244     outs() << computeStats(*Coverage);
1245     return 0;
1246   }
1247   case MergeAction:
1248   case SymbolizeAction: { // merge & symbolize are synonims.
1249     json::OStream W(outs(), 2);
1250     W << *Coverage;
1251     return 0;
1252   }
1253   case HtmlReportAction:
1254     errs() << "-html-report option is removed: "
1255               "use -symbolize & coverage-report-server.py instead\n";
1256     return 1;
1257   case PrintAction:
1258   case PrintCovPointsAction:
1259     llvm_unreachable("unsupported action");
1260   }
1261 
1262   return 0;
1263 }
1264