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