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