Index: tools/sancov/sancov.cc =================================================================== --- tools/sancov/sancov.cc +++ tools/sancov/sancov.cc @@ -11,6 +11,7 @@ // coverage. //===----------------------------------------------------------------------===// #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Twine.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" @@ -34,6 +35,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/Signals.h" #include "llvm/Support/SpecialCaseList.h" #include "llvm/Support/TargetRegistry.h" @@ -70,12 +72,9 @@ "Print HTML coverage report."), clEnumValEnd)); -static cl::list ClInputFiles(cl::Positional, cl::OneOrMore, - cl::desc("")); - -static cl::opt - ClBinaryName("obj", cl::Required, - cl::desc("Path to object file to be symbolized")); +static cl::list + ClInputFiles(cl::Positional, cl::OneOrMore, + cl::desc("|...")); static cl::opt ClDemangle("demangle", cl::init(true), @@ -119,8 +118,8 @@ FailIfError(E.getError()); } -static void FailIfNotEmpty(const std::string &E) { - if (E.empty()) +static void FailIfNotEmpty(const llvm::Twine &E) { + if (E.str().empty()) return; errs() << "Error: " << E << "\n"; exit(1); @@ -180,14 +179,14 @@ // Compute [FileLoc -> FunctionName] map for given addresses. static std::map -computeFunctionsMap(const std::set &Addrs) { +computeFunctionsMap(std::string BinaryName, const std::set &Addrs) { std::map Fns; auto Symbolizer(createSymbolizer()); // Fill in Fns map. for (auto Addr : Addrs) { - auto InliningInfo = Symbolizer->symbolizeInlinedCode(ClBinaryName, Addr); + auto InliningInfo = Symbolizer->symbolizeInlinedCode(BinaryName, Addr); FailIfError(InliningInfo); for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) { auto FrameInfo = InliningInfo->getFrame(I); @@ -203,8 +202,9 @@ // Compute functions for given addresses. It keeps only the first // occurence of a function within a file. -std::set computeFunctionLocs(const std::set &Addrs) { - std::map Fns = computeFunctionsMap(Addrs); +std::set computeFunctionLocs(std::string BinaryName, + const std::set &Addrs) { + std::map Fns = computeFunctionsMap(BinaryName, Addrs); std::set Result; std::string LastFileName; @@ -436,14 +436,14 @@ // Computes a map file_name->{line_number} static std::map> -getFileLines(const std::set &Addrs) { +getFileLines(std::string BinaryName, const std::set &Addrs) { std::map> FileLines; auto Symbolizer(createSymbolizer()); // Fill in FileLines map. for (auto Addr : Addrs) { - auto InliningInfo = Symbolizer->symbolizeInlinedCode(ClBinaryName, Addr); + auto InliningInfo = Symbolizer->symbolizeInlinedCode(BinaryName, Addr); FailIfError(InliningInfo); for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) { auto FrameInfo = InliningInfo->getFrame(I); @@ -456,44 +456,60 @@ return FileLines; } +static ErrorOr isCoverageFile(std::string FileName) { + ErrorOr> BufOrErr = + MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return BufOrErr.getError(); + std::unique_ptr Buf = std::move(BufOrErr.get()); + if (Buf->getBufferSize() < 8) { + return false; + } + const FileHeader *Header = + reinterpret_cast(Buf->getBufferStart()); + return Header->Magic == BinCoverageMagic; +} + class CoverageData { public: // Read single file coverage data. - static ErrorOr> read(std::string FileName) { - ErrorOr> BufOrErr = - MemoryBuffer::getFile(FileName); - if (!BufOrErr) - return BufOrErr.getError(); - std::unique_ptr Buf = std::move(BufOrErr.get()); - if (Buf->getBufferSize() < 8) { - errs() << "File too small (<8): " << Buf->getBufferSize(); - return make_error_code(errc::illegal_byte_sequence); - } - const FileHeader *Header = - reinterpret_cast(Buf->getBufferStart()); - - if (Header->Magic != BinCoverageMagic) { - errs() << "Wrong magic: " << Header->Magic; - return make_error_code(errc::illegal_byte_sequence); - } - - auto Addrs = llvm::make_unique>(); - - switch (Header->Bitness) { - case Bitness64: - readInts(Buf->getBufferStart() + 8, Buf->getBufferEnd(), - Addrs.get()); - break; - case Bitness32: - readInts(Buf->getBufferStart() + 8, Buf->getBufferEnd(), - Addrs.get()); - break; - default: - errs() << "Unsupported bitness: " << Header->Bitness; - return make_error_code(errc::illegal_byte_sequence); - } - - return std::unique_ptr(new CoverageData(std::move(Addrs))); + static ErrorOr> read(std::string BinaryName, + std::string FileName) { + ErrorOr> BufOrErr = + MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return BufOrErr.getError(); + std::unique_ptr Buf = std::move(BufOrErr.get()); + if (Buf->getBufferSize() < 8) { + errs() << "File too small (<8): " << Buf->getBufferSize(); + return make_error_code(errc::illegal_byte_sequence); + } + const FileHeader *Header = + reinterpret_cast(Buf->getBufferStart()); + + if (Header->Magic != BinCoverageMagic) { + errs() << "Wrong magic: " << Header->Magic; + return make_error_code(errc::illegal_byte_sequence); + } + + auto Addrs = llvm::make_unique>(); + + switch (Header->Bitness) { + case Bitness64: + readInts(Buf->getBufferStart() + 8, Buf->getBufferEnd(), + Addrs.get()); + break; + case Bitness32: + readInts(Buf->getBufferStart() + 8, Buf->getBufferEnd(), + Addrs.get()); + break; + default: + errs() << "Unsupported bitness: " << Header->Bitness; + return make_error_code(errc::illegal_byte_sequence); + } + + return std::unique_ptr( + new CoverageData(BinaryName, std::move(Addrs))); } // Merge multiple coverage data together. @@ -501,18 +517,31 @@ merge(const std::vector> &Covs) { auto Addrs = llvm::make_unique>(); - for (const auto &Cov : Covs) + std::string BinaryName; + if (Covs.size() > 0) { + BinaryName = Covs[0]->BinaryName; + } + for (const auto &Cov : Covs) { + if (Cov->BinaryName != BinaryName) { + FailIfNotEmpty( + "Can't merge coverage from different binaries. Expecting: " + + BinaryName + " but was: " + Cov->BinaryName); + } + Addrs->insert(Cov->Addrs->begin(), Cov->Addrs->end()); + } - return std::unique_ptr(new CoverageData(std::move(Addrs))); + return std::unique_ptr( + new CoverageData(BinaryName, std::move(Addrs))); } // Read list of files and merges their coverage info. static ErrorOr> - readAndMerge(const std::vector &FileNames) { + readAndMerge(std::string BinaryName, + const std::vector &FileNames) { std::vector> Covs; for (const auto &FileName : FileNames) { - auto Cov = read(FileName); + auto Cov = read(BinaryName, FileName); if (!Cov) return Cov.getError(); Covs.push_back(std::move(Cov.get())); @@ -532,11 +561,11 @@ void printReport(raw_ostream &OS) { // file_name -> set of covered lines; std::map> CoveredFileLines = - getFileLines(*Addrs); + getFileLines(BinaryName, *Addrs); std::map> CoveragePoints = - getFileLines(getCoveragePoints(ClBinaryName)); + getFileLines(BinaryName, getCoveragePoints(BinaryName)); - std::string Title = stripPathPrefix(ClBinaryName) + " Coverage Report"; + std::string Title = stripPathPrefix(BinaryName) + " Coverage Report"; OS << "\n"; OS << "\n"; @@ -608,15 +637,15 @@ // Print list of covered functions. // Line format: : void printCoveredFunctions(raw_ostream &OS) { - printFunctionLocs(computeFunctionLocs(*Addrs), OS); + printFunctionLocs(computeFunctionLocs(BinaryName, *Addrs), OS); } // Print list of not covered functions. // Line format: : void printNotCoveredFunctions(raw_ostream &OS) { std::set AllFns = - computeFunctionLocs(getCoveragePoints(ClBinaryName)); - std::set CoveredFns = computeFunctionLocs(*Addrs); + computeFunctionLocs(BinaryName, getCoveragePoints(BinaryName)); + std::set CoveredFns = computeFunctionLocs(BinaryName, *Addrs); std::set NotCoveredFns; std::set_difference(AllFns.begin(), AllFns.end(), CoveredFns.begin(), @@ -626,11 +655,108 @@ } private: - explicit CoverageData(std::unique_ptr> Addrs) - : Addrs(std::move(Addrs)) {} + CoverageData(std::string BinaryName, + std::unique_ptr> Addrs) + : BinaryName(BinaryName), Addrs(std::move(Addrs)) {} + const std::string BinaryName; std::unique_ptr> Addrs; }; + +class CoverageDataSet { +public: + static ErrorOr> + readCmdArguments(std::vector FileNames) { + std::map BinFiles; + std::vector CovFiles; + + for (const auto &FileName : FileNames) { + auto ErrorOrIsCoverage = isCoverageFile(FileName); + FailIfError(ErrorOrIsCoverage); + if (ErrorOrIsCoverage.get()) { + CovFiles.push_back(FileName); + } else { + auto ShortFileName = llvm::sys::path::filename(FileName); + if (BinFiles.find(ShortFileName) != BinFiles.end()) { + FailIfNotEmpty("Duplicate binary file with a short name: " + + ShortFileName); + } + + BinFiles[ShortFileName] = FileName; + } + } + + std::map>> + CoverageByBinFile; + Regex SancovRegex("(.*)\\.[0-9]+\\.sancov"); + SmallVector components; + for (const auto &FileName : CovFiles) { + auto ShortFileName = llvm::sys::path::filename(FileName); + auto Ok = SancovRegex.match(ShortFileName, &components); + if (!Ok) { + FailIfNotEmpty("Can't match coverage file name against " + "..sancov pattern: " + + FileName); + } + + auto Iter = BinFiles.find(components[1]); + if (Iter == BinFiles.end()) { + FailIfNotEmpty("Object file for coverage not found: " + FileName); + } + auto BinaryName = Iter->second; + auto CoverageDataOrError = CoverageData::read(BinaryName, FileName); + FailIfError(CoverageDataOrError); + CoverageByBinFile[BinaryName].push_back( + std::move(CoverageDataOrError.get())); + } + + std::vector> MergedCoverage; + for (const auto &Pair : CoverageByBinFile) { + std::unique_ptr MergedData( + CoverageData::merge(Pair.second)); + MergedCoverage.push_back(std::move(MergedData)); + } + + return std::unique_ptr( + new CoverageDataSet(&MergedCoverage)); + } + + void printAddrs(raw_ostream &OS) const { + if (Coverage.size() > 1) { + FailIfNotEmpty("Can't print addresses from multiple object files.\n"); + } + + for (const auto &CovData : Coverage) { + CovData->printAddrs(OS); + } + } + + void printCoveredFunctions(raw_ostream &OS) const { + for (const auto &CovData : Coverage) { + CovData->printCoveredFunctions(OS); + } + } + + void printNotCoveredFunctions(raw_ostream &OS) const { + for (const auto &CovData : Coverage) { + CovData->printNotCoveredFunctions(OS); + } + } + + void printReport(raw_ostream &OS) const { + for (const auto &CovData : Coverage) { + CovData->printReport(OS); + } + } + +private: + explicit CoverageDataSet(std::vector> *Data) { + Data->swap(this->Coverage); + } + + std::vector> Coverage; +}; + } // namespace int main(int argc, char **argv) { @@ -645,24 +771,24 @@ cl::ParseCommandLineOptions(argc, argv, "Sanitizer Coverage Processing Tool"); - auto CovData = CoverageData::readAndMerge(ClInputFiles); - FailIfError(CovData); + auto CovDataSet = CoverageDataSet::readCmdArguments(ClInputFiles); + FailIfError(CovDataSet); switch (Action) { case PrintAction: { - CovData.get()->printAddrs(outs()); + CovDataSet.get()->printAddrs(outs()); return 0; } case CoveredFunctionsAction: { - CovData.get()->printCoveredFunctions(outs()); + CovDataSet.get()->printCoveredFunctions(outs()); return 0; } case NotCoveredFunctionsAction: { - CovData.get()->printNotCoveredFunctions(outs()); + CovDataSet.get()->printNotCoveredFunctions(outs()); return 0; } case HtmlReportAction: { - CovData.get()->printReport(outs()); + CovDataSet.get()->printReport(outs()); return 0; } }