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