Changeset View
Changeset View
Standalone View
Standalone View
llvm-cov/CodeCoverage.cpp
Show First 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | private: | ||||
/// \brief Create the source view of a particular function. | /// \brief Create the source view of a particular function. | ||||
std::unique_ptr<SourceCoverageView> | std::unique_ptr<SourceCoverageView> | ||||
createFunctionView(const FunctionRecord &Function, | createFunctionView(const FunctionRecord &Function, | ||||
const CoverageMapping &Coverage); | const CoverageMapping &Coverage); | ||||
/// \brief Create the main source view of a particular source file. | /// \brief Create the main source view of a particular source file. | ||||
std::unique_ptr<SourceCoverageView> | std::unique_ptr<SourceCoverageView> | ||||
createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage); | createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage, | ||||
ArrayRef<std::string> ObjectFiles); | |||||
/// \brief Load the coverage mapping data. Return nullptr if an error occured. | /// \brief Load the coverage mapping data. Return nullptr if an error occured. | ||||
std::unique_ptr<CoverageMapping> load(); | std::unique_ptr<CoverageMapping> load(StringRef ObjectFilename); | ||||
/// \brief set the map of RemappedFilenames. | |||||
void setRemappedFilenames(const std::vector<StringRef> CoveredFiles); | |||||
/// \brief Remove input source files which aren't mapped by \p Coverage. | /// \brief Remove input source files which aren't mapped by \p Coverage. | ||||
void removeUnmappedInputs(const CoverageMapping &Coverage); | void removeUnmappedInputs(const CoverageMapping &Coverage); | ||||
/// \brief If a demangler is available, demangle all symbol names. | /// \brief If a demangler is available, demangle all symbol names. | ||||
void demangleSymbols(const CoverageMapping &Coverage); | void demangleSymbols(const CoverageMapping &Coverage); | ||||
/// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym. | /// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym. | ||||
StringRef getSymbolForHumans(StringRef Sym) const; | StringRef getSymbolForHumans(StringRef Sym) const; | ||||
/// Return true if the function \p Function is defined in the given source | |||||
/// files \p SourceFiles. Otherwise, return false. | |||||
bool functionInAnySourceFiles(const FunctionRecord &Function, | |||||
ArrayRef<std::string> SourceFiles); | |||||
/// Return true if the function \p Function has been covered and a coverage | |||||
/// report was created. Otherwise, return false. | |||||
bool functionHasBeenCovered( | |||||
const FunctionRecord &Function, | |||||
llvm::StringMap<std::vector<std::string>> &CoveredFunctions); | |||||
/// Add the function \p Function to the covered functions map \p | |||||
/// CoveredFunctions. | |||||
void setFunctionAsCovered( | |||||
const FunctionRecord &Function, | |||||
llvm::StringMap<std::vector<std::string>> &CoveredFunctions); | |||||
typedef llvm::function_ref<int(int, const char **)> CommandLineParserType; | typedef llvm::function_ref<int(int, const char **)> CommandLineParserType; | ||||
int show(int argc, const char **argv, | int show(int argc, const char **argv, | ||||
CommandLineParserType commandLineParser); | CommandLineParserType commandLineParser); | ||||
int report(int argc, const char **argv, | int report(int argc, const char **argv, | ||||
CommandLineParserType commandLineParser); | CommandLineParserType commandLineParser); | ||||
int export_(int argc, const char **argv, | int export_(int argc, const char **argv, | ||||
CommandLineParserType commandLineParser); | CommandLineParserType commandLineParser); | ||||
std::string ObjectFilename; | std::vector<std::string> ObjectFilenames; | ||||
CoverageViewOptions ViewOpts; | CoverageViewOptions ViewOpts; | ||||
CoverageFiltersMatchAll Filters; | CoverageFiltersMatchAll Filters; | ||||
/// The path to the indexed profile. | /// The path to the indexed profile. | ||||
std::string PGOFilename; | std::string PGOFilename; | ||||
/// A list of input source files. | /// A list of input source files. | ||||
std::vector<std::string> SourceFiles; | std::vector<std::string> SourceFiles; | ||||
Show All 26 Lines | static std::string getErrorString(const Twine &Message, StringRef Whence, | ||||
std::string Str = (Warning ? "warning" : "error"); | std::string Str = (Warning ? "warning" : "error"); | ||||
Str += ": "; | Str += ": "; | ||||
if (!Whence.empty()) | if (!Whence.empty()) | ||||
Str += Whence.str() + ": "; | Str += Whence.str() + ": "; | ||||
Str += Message.str() + "\n"; | Str += Message.str() + "\n"; | ||||
return Str; | return Str; | ||||
} | } | ||||
// Return the full file path if \p Filename has been made absolute, native and | |||||
// cleaned-up (removed-dots), otherwise a platform-specific error_code. | |||||
static llvm::ErrorOr<std::string> normalizeFilename(StringRef Filename) { | |||||
SmallString<128> ObjectFilePath(Filename); | |||||
sys::path::remove_dots(ObjectFilePath, /*remove_dot_dots=*/true); | |||||
sys::path::native(ObjectFilePath); | |||||
if (std::error_code EC = sys::fs::make_absolute(ObjectFilePath)) | |||||
return EC; | |||||
return ObjectFilePath.str().str(); | |||||
} | |||||
void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { | void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { | ||||
std::unique_lock<std::mutex> Guard{ErrsLock}; | std::unique_lock<std::mutex> Guard{ErrsLock}; | ||||
ViewOpts.colored_ostream(errs(), raw_ostream::RED) | ViewOpts.colored_ostream(errs(), raw_ostream::RED) | ||||
<< getErrorString(Message, Whence, false); | << getErrorString(Message, Whence, false); | ||||
} | } | ||||
void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) { | void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) { | ||||
std::unique_lock<std::mutex> Guard{ErrsLock}; | std::unique_lock<std::mutex> Guard{ErrsLock}; | ||||
▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | auto View = SourceCoverageView::create(getSymbolForHumans(Function.Name), | ||||
std::move(FunctionCoverage)); | std::move(FunctionCoverage)); | ||||
attachExpansionSubViews(*View, Expansions, Coverage); | attachExpansionSubViews(*View, Expansions, Coverage); | ||||
return View; | return View; | ||||
} | } | ||||
std::unique_ptr<SourceCoverageView> | std::unique_ptr<SourceCoverageView> | ||||
CodeCoverageTool::createSourceFileView(StringRef SourceFile, | CodeCoverageTool::createSourceFileView(StringRef SourceFile, | ||||
const CoverageMapping &Coverage) { | const CoverageMapping &Coverage, | ||||
ArrayRef<std::string> ObjectFilenames) { | |||||
auto SourceBuffer = getSourceFile(SourceFile); | auto SourceBuffer = getSourceFile(SourceFile); | ||||
if (!SourceBuffer) | if (!SourceBuffer) | ||||
return nullptr; | return nullptr; | ||||
auto FileCoverage = Coverage.getCoverageForFile(SourceFile); | auto FileCoverage = Coverage.getCoverageForFile(SourceFile); | ||||
if (FileCoverage.empty()) | if (FileCoverage.empty()) | ||||
return nullptr; | return nullptr; | ||||
auto Expansions = FileCoverage.getExpansions(); | auto Expansions = FileCoverage.getExpansions(); | ||||
auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), | auto View = | ||||
ViewOpts, std::move(FileCoverage)); | SourceCoverageView::create(SourceFile, SourceBuffer.get(), ViewOpts, | ||||
std::move(FileCoverage), ObjectFilenames); | |||||
attachExpansionSubViews(*View, Expansions, Coverage); | attachExpansionSubViews(*View, Expansions, Coverage); | ||||
for (const auto *Function : Coverage.getInstantiations(SourceFile)) { | for (const auto *Function : Coverage.getInstantiations(SourceFile)) { | ||||
std::unique_ptr<SourceCoverageView> SubView{nullptr}; | std::unique_ptr<SourceCoverageView> SubView{nullptr}; | ||||
StringRef Funcname = getSymbolForHumans(Function->Name); | StringRef Funcname = getSymbolForHumans(Function->Name); | ||||
if (Function->ExecutionCount > 0) { | if (Function->ExecutionCount > 0) { | ||||
Show All 20 Lines | if (sys::fs::status(LHS, Status)) | ||||
return false; | return false; | ||||
auto LHSTime = Status.getLastModificationTime(); | auto LHSTime = Status.getLastModificationTime(); | ||||
if (sys::fs::status(RHS, Status)) | if (sys::fs::status(RHS, Status)) | ||||
return false; | return false; | ||||
auto RHSTime = Status.getLastModificationTime(); | auto RHSTime = Status.getLastModificationTime(); | ||||
return LHSTime > RHSTime; | return LHSTime > RHSTime; | ||||
} | } | ||||
std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { | std::unique_ptr<CoverageMapping> | ||||
CodeCoverageTool::load(StringRef ObjectFilename) { | |||||
if (modifiedTimeGT(ObjectFilename, PGOFilename)) | if (modifiedTimeGT(ObjectFilename, PGOFilename)) | ||||
warning("profile data may be out of date - object is newer", | warning("profile data may be out of date - object is newer", | ||||
ObjectFilename); | ObjectFilename); | ||||
auto CoverageOrErr = | auto CoverageOrErr = | ||||
CoverageMapping::load(ObjectFilename, PGOFilename, CoverageArch); | CoverageMapping::load(ObjectFilename, PGOFilename, CoverageArch); | ||||
if (Error E = CoverageOrErr.takeError()) { | if (Error E = CoverageOrErr.takeError()) { | ||||
error("Failed to load coverage: " + toString(std::move(E)), ObjectFilename); | error("Failed to load coverage: " + toString(std::move(E)), ObjectFilename); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
auto Coverage = std::move(CoverageOrErr.get()); | auto Coverage = std::move(CoverageOrErr.get()); | ||||
unsigned Mismatched = Coverage->getMismatchedCount(); | unsigned Mismatched = Coverage->getMismatchedCount(); | ||||
if (Mismatched) | if (Mismatched) | ||||
warning(utostr(Mismatched) + " functions have mismatched data"); | warning(utostr(Mismatched) + " functions have mismatched data"); | ||||
if (!SourceFiles.empty()) | if (!SourceFiles.empty() && ObjectFilenames.size() == 1) | ||||
removeUnmappedInputs(*Coverage); | removeUnmappedInputs(*Coverage); | ||||
demangleSymbols(*Coverage); | demangleSymbols(*Coverage); | ||||
return Coverage; | return Coverage; | ||||
} | } | ||||
void CodeCoverageTool::setRemappedFilenames( | |||||
const std::vector<StringRef> CoveredFiles) { | |||||
for (auto &SF : SourceFiles) { | |||||
StringRef SFBase = sys::path::filename(SF); | |||||
for (const auto &CF : CoveredFiles) { | |||||
if (SFBase == sys::path::filename(CF) && !RemappedFilenames.count(CF)) { | |||||
RemappedFilenames[CF] = SF; | |||||
SF = CF; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) { | void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) { | ||||
std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles(); | std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles(); | ||||
auto UncoveredFilesIt = SourceFiles.end(); | auto UncoveredFilesIt = SourceFiles.end(); | ||||
if (!CompareFilenamesOnly) { | if (!CompareFilenamesOnly) { | ||||
// The user may have specified source files which aren't in the coverage | // The user may have specified source files which aren't in the coverage | ||||
// mapping. Filter these files away. | // mapping. Filter these files away. | ||||
UncoveredFilesIt = std::remove_if( | UncoveredFilesIt = std::remove_if( | ||||
SourceFiles.begin(), SourceFiles.end(), [&](const std::string &SF) { | SourceFiles.begin(), SourceFiles.end(), [&](const std::string &SF) { | ||||
return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), | return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), | ||||
SF); | SF); | ||||
}); | }); | ||||
} else { | } else { | ||||
for (auto &SF : SourceFiles) { | setRemappedFilenames(CoveredFiles); | ||||
StringRef SFBase = sys::path::filename(SF); | |||||
for (const auto &CF : CoveredFiles) { | |||||
if (SFBase == sys::path::filename(CF)) { | |||||
RemappedFilenames[CF] = SF; | |||||
SF = CF; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
UncoveredFilesIt = std::remove_if( | UncoveredFilesIt = std::remove_if( | ||||
SourceFiles.begin(), SourceFiles.end(), | SourceFiles.begin(), SourceFiles.end(), | ||||
[&](const std::string &SF) { return !RemappedFilenames.count(SF); }); | [&](const std::string &SF) { return !RemappedFilenames.count(SF); }); | ||||
} | } | ||||
SourceFiles.erase(UncoveredFilesIt, SourceFiles.end()); | SourceFiles.erase(UncoveredFilesIt, SourceFiles.end()); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | |||||
StringRef CodeCoverageTool::getSymbolForHumans(StringRef Sym) const { | StringRef CodeCoverageTool::getSymbolForHumans(StringRef Sym) const { | ||||
const auto DemangledName = DemangledNames.find(Sym); | const auto DemangledName = DemangledNames.find(Sym); | ||||
if (DemangledName == DemangledNames.end()) | if (DemangledName == DemangledNames.end()) | ||||
return Sym; | return Sym; | ||||
return DemangledName->getValue(); | return DemangledName->getValue(); | ||||
} | } | ||||
bool CodeCoverageTool::functionInAnySourceFiles( | |||||
const FunctionRecord &Function, ArrayRef<std::string> SourceFiles) { | |||||
if (SourceFiles.empty()) | |||||
return false; | |||||
for (const auto &FunctionFileName : Function.Filenames) | |||||
if (std::find(SourceFiles.begin(), SourceFiles.end(), FunctionFileName) != | |||||
SourceFiles.end()) | |||||
return true; | |||||
return false; | |||||
} | |||||
bool CodeCoverageTool::functionHasBeenCovered( | |||||
const FunctionRecord &Function, | |||||
llvm::StringMap<std::vector<std::string>> &CoveredFunctions) { | |||||
if (CoveredFunctions.count(Function.Name)) | |||||
return functionInAnySourceFiles(Function, CoveredFunctions[Function.Name]); | |||||
return false; | |||||
} | |||||
void CodeCoverageTool::setFunctionAsCovered( | |||||
const FunctionRecord &Function, | |||||
llvm::StringMap<std::vector<std::string>> &CoveredFunctions) { | |||||
if (CoveredFunctions.count(Function.Name)) { | |||||
for (const auto Filename : Function.Filenames) | |||||
if (std::find(CoveredFunctions[Function.Name].begin(), | |||||
CoveredFunctions[Function.Name].end(), | |||||
Filename) == CoveredFunctions[Function.Name].end()) | |||||
CoveredFunctions[Function.Name].push_back(Filename); | |||||
} else | |||||
CoveredFunctions[Function.Name] = Function.Filenames; | |||||
} | |||||
int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { | int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { | ||||
std::string ObjectFilenameOptionLoc; | |||||
cl::opt<std::string, true> ObjectFilename( | cl::opt<std::string, true> ObjectFilename( | ||||
cl::Positional, cl::Required, cl::location(this->ObjectFilename), | cl::Positional, cl::Required, cl::location(ObjectFilenameOptionLoc), | ||||
cl::desc("Covered executable or object file.")); | cl::desc("Covered executable or object file.")); | ||||
cl::list<std::string> InputSourceFiles( | cl::list<std::string> InputSourceFiles( | ||||
cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); | cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); | ||||
cl::opt<bool> DebugDumpCollectedPaths( | cl::opt<bool> DebugDumpCollectedPaths( | ||||
"dump-collected-paths", cl::Optional, cl::Hidden, | "dump-collected-paths", cl::Optional, cl::Hidden, | ||||
cl::desc("Show the collected paths to source files")); | cl::desc("Show the collected paths to source files")); | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | cl::list<std::string> DemanglerOpts( | ||||
"Xdemangler", cl::desc("<demangler-path>|<demangler-option>")); | "Xdemangler", cl::desc("<demangler-path>|<demangler-option>")); | ||||
auto commandLineParser = [&, this](int argc, const char **argv) -> int { | auto commandLineParser = [&, this](int argc, const char **argv) -> int { | ||||
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); | cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); | ||||
ViewOpts.Debug = DebugDump; | ViewOpts.Debug = DebugDump; | ||||
CompareFilenamesOnly = FilenameEquivalence; | CompareFilenamesOnly = FilenameEquivalence; | ||||
ViewOpts.Format = Format; | ViewOpts.Format = Format; | ||||
SmallString<128> ObjectFilePath(this->ObjectFilename); | auto FilenameOrErr = normalizeFilename(StringRef(ObjectFilenameOptionLoc)); | ||||
if (std::error_code EC = sys::fs::make_absolute(ObjectFilePath)) { | if (auto EC = FilenameOrErr.getError()) { | ||||
error(EC.message(), this->ObjectFilename); | error(EC.message(), FilenameOrErr.get()); | ||||
return 1; | return 1; | ||||
} | } | ||||
sys::path::native(ObjectFilePath); | ObjectFilenames.push_back(FilenameOrErr.get()); | ||||
ViewOpts.ObjectFilename = ObjectFilePath.c_str(); | |||||
switch (ViewOpts.Format) { | switch (ViewOpts.Format) { | ||||
case CoverageViewOptions::OutputFormat::Text: | case CoverageViewOptions::OutputFormat::Text: | ||||
ViewOpts.Colors = UseColor == cl::BOU_UNSET | ViewOpts.Colors = UseColor == cl::BOU_UNSET | ||||
? sys::Process::StandardOutHasColors() | ? sys::Process::StandardOutHasColors() | ||||
: UseColor == cl::BOU_TRUE; | : UseColor == cl::BOU_TRUE; | ||||
break; | break; | ||||
case CoverageViewOptions::OutputFormat::HTML: | case CoverageViewOptions::OutputFormat::HTML: | ||||
if (UseColor == cl::BOU_FALSE) | if (UseColor == cl::BOU_FALSE) | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | cl::opt<uint32_t> TabSize( | ||||
"tab-size", cl::init(2), | "tab-size", cl::init(2), | ||||
cl::desc( | cl::desc( | ||||
"Set tab expansion size for html coverage reports (default = 2)")); | "Set tab expansion size for html coverage reports (default = 2)")); | ||||
cl::opt<std::string> ProjectTitle( | cl::opt<std::string> ProjectTitle( | ||||
"project-title", cl::Optional, | "project-title", cl::Optional, | ||||
cl::desc("Set project title for the coverage report")); | cl::desc("Set project title for the coverage report")); | ||||
cl::list<std::string> AdditionalObjectFilenames( | |||||
"bin", cl::CommaSeparated, cl::ZeroOrMore, | |||||
cl::desc("Additional covered executable or object files."), | |||||
cl::value_desc("test.elf,test2.elf...")); | |||||
auto Err = commandLineParser(argc, argv); | auto Err = commandLineParser(argc, argv); | ||||
if (Err) | if (Err) | ||||
return Err; | return Err; | ||||
ViewOpts.ShowLineNumbers = true; | ViewOpts.ShowLineNumbers = true; | ||||
ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || | ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || | ||||
!ShowRegions || ShowBestLineRegionsCounts; | !ShowRegions || ShowBestLineRegionsCounts; | ||||
ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; | ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; | ||||
Show All 19 Lines | int CodeCoverageTool::show(int argc, const char **argv, | ||||
auto ModifiedTime = Status.getLastModificationTime(); | auto ModifiedTime = Status.getLastModificationTime(); | ||||
std::string ModifiedTimeStr = ModifiedTime.str(); | std::string ModifiedTimeStr = ModifiedTime.str(); | ||||
size_t found = ModifiedTimeStr.rfind(":"); | size_t found = ModifiedTimeStr.rfind(":"); | ||||
ViewOpts.CreatedTimeStr = (found != std::string::npos) | ViewOpts.CreatedTimeStr = (found != std::string::npos) | ||||
? "Created: " + ModifiedTimeStr.substr(0, found) | ? "Created: " + ModifiedTimeStr.substr(0, found) | ||||
: "Created: " + ModifiedTimeStr; | : "Created: " + ModifiedTimeStr; | ||||
auto Coverage = load(); | for (const auto &Filename : AdditionalObjectFilenames) { | ||||
auto FilenameOrErr = normalizeFilename(StringRef(Filename)); | |||||
if (auto EC = FilenameOrErr.getError()) { | |||||
error(EC.message(), Filename); | |||||
return 1; | |||||
} | |||||
if (std::find(ObjectFilenames.begin(), ObjectFilenames.end(), | |||||
FilenameOrErr.get()) == ObjectFilenames.end()) | |||||
ObjectFilenames.push_back(FilenameOrErr.get()); | |||||
} | |||||
StringSet<> UncoveredFunctions; | |||||
StringMap<std::vector<std::string>> FunctionNameToCoveredFunctions; | |||||
StringMap<std::vector<std::string>> SourceToObjectMappings; | |||||
ObjectFilesMap ObjectMappings; | |||||
for (const auto &ObjectFile : ObjectFilenames) { | |||||
auto Coverage = load(ObjectFile); | |||||
if (!Coverage) | if (!Coverage) | ||||
return 1; | return 1; | ||||
std::vector<std::string> CurObjSourceFiles; | |||||
std::vector<StringRef> CoveredFiles = Coverage->getUniqueSourceFiles(); | |||||
if (CompareFilenamesOnly && ObjectFilenames.size() > 1) | |||||
setRemappedFilenames(CoveredFiles); | |||||
for (StringRef Filename : Coverage->getUniqueSourceFiles()) { | |||||
if (SourceFiles.empty() || | |||||
(std::find(SourceFiles.begin(), SourceFiles.end(), Filename) != | |||||
SourceFiles.end())) { | |||||
CurObjSourceFiles.push_back(Filename); | |||||
SourceToObjectMappings[Filename].push_back(ObjectFile); | |||||
} | |||||
} | |||||
ObjectMappings[ObjectFile] = | |||||
std::make_pair(CurObjSourceFiles, std::move(Coverage)); | |||||
} | |||||
// Get the source files from ObjectMappings. | |||||
if (SourceFiles.empty()) { | |||||
for (const auto &ObjectFile : ObjectFilenames) { | |||||
auto Files = ObjectMappings[ObjectFile].first; | |||||
for (StringRef SourceFile : Files) | |||||
if (std::find(SourceFiles.begin(), SourceFiles.end(), SourceFile) == | |||||
SourceFiles.end()) | |||||
SourceFiles.push_back(SourceFile); | |||||
} | |||||
} | |||||
auto Printer = CoveragePrinter::create(ViewOpts); | auto Printer = CoveragePrinter::create(ViewOpts); | ||||
if (!Filters.empty()) { | if (!Filters.empty()) { | ||||
auto OSOrErr = Printer->createViewFile("functions", /*InToplevel=*/true); | auto OSOrErr = Printer->createViewFile("functions", /*InToplevel=*/true); | ||||
if (Error E = OSOrErr.takeError()) { | if (Error E = OSOrErr.takeError()) { | ||||
error("Could not create view file!", toString(std::move(E))); | error("Could not create view file!", toString(std::move(E))); | ||||
return 1; | return 1; | ||||
} | } | ||||
auto OS = std::move(OSOrErr.get()); | auto OS = std::move(OSOrErr.get()); | ||||
// Show functions. | // Show functions. | ||||
for (const auto &Function : Coverage->getCoveredFunctions()) { | for (const auto &ObjectFile : ObjectFilenames) { | ||||
auto Coverage = *ObjectMappings[ObjectFile].second.get(); | |||||
for (const auto &Function : Coverage.getCoveredFunctions()) { | |||||
if (!Filters.matches(Function)) | if (!Filters.matches(Function)) | ||||
continue; | continue; | ||||
auto mainView = createFunctionView(Function, *Coverage); | if (!functionInAnySourceFiles(Function, SourceFiles)) { | ||||
UncoveredFunctions.insert(Function.Name); | |||||
continue; | |||||
} | |||||
if (functionHasBeenCovered(Function, FunctionNameToCoveredFunctions)) | |||||
continue; | |||||
setFunctionAsCovered(Function, FunctionNameToCoveredFunctions); | |||||
if (UncoveredFunctions.count(Function.Name)) | |||||
UncoveredFunctions.erase(Function.Name); | |||||
auto mainView = createFunctionView(Function, Coverage); | |||||
if (!mainView) { | if (!mainView) { | ||||
warning("Could not read coverage for '" + Function.Name + "'."); | warning("Could not read coverage for '" + Function.Name + "'."); | ||||
continue; | continue; | ||||
} | } | ||||
mainView->print(*OS.get(), /*WholeFile=*/false, | |||||
mainView->print(*OS.get(), /*WholeFile=*/false, /*ShowSourceName=*/true); | /*ShowSourceName=*/true); | ||||
} | |||||
} | } | ||||
Printer->closeViewFile(std::move(OS)); | Printer->closeViewFile(std::move(OS)); | ||||
if (!UncoveredFunctions.empty()) | |||||
for (const auto &FunctionName : UncoveredFunctions) { | |||||
ViewOpts.colored_ostream(errs(), raw_ostream::RED) | |||||
<< "warning: Could not read coverage for '" | |||||
<< FunctionName.getKey().str() << "'." | |||||
<< "\n"; | |||||
} | |||||
return 0; | return 0; | ||||
} | } | ||||
// Show files | // Show files | ||||
bool ShowFilenames = | bool ShowFilenames = | ||||
(SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || | (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || | ||||
(ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); | (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); | ||||
if (SourceFiles.empty()) | |||||
// Get the source files from the function coverage mapping. | |||||
for (StringRef Filename : Coverage->getUniqueSourceFiles()) | |||||
SourceFiles.push_back(Filename); | |||||
// Create an index out of the source files. | // Create an index out of the source files. | ||||
if (ViewOpts.hasOutputDirectory()) { | if (ViewOpts.hasOutputDirectory()) { | ||||
if (Error E = Printer->createIndexFile(SourceFiles, *Coverage)) { | if (Error E = Printer->createIndexFile(ObjectMappings)) { | ||||
error("Could not create index file!", toString(std::move(E))); | error("Could not create index file!", toString(std::move(E))); | ||||
return 1; | return 1; | ||||
} | } | ||||
} | } | ||||
// In -output-dir mode, it's safe to use multiple threads to print files. | // In -output-dir mode, it's safe to use multiple threads to print files. | ||||
unsigned ThreadCount = 1; | unsigned ThreadCount = 1; | ||||
if (ViewOpts.hasOutputDirectory()) | if (ViewOpts.hasOutputDirectory()) | ||||
ThreadCount = std::thread::hardware_concurrency(); | ThreadCount = std::thread::hardware_concurrency(); | ||||
ThreadPool Pool(ThreadCount); | ThreadPool Pool(ThreadCount); | ||||
for (const std::string &SourceFile : SourceFiles) { | for (const std::string &SourceFile : SourceFiles) { | ||||
Pool.async([this, &SourceFile, &Coverage, &Printer, ShowFilenames] { | if (!SourceToObjectMappings[SourceFile].size()) { | ||||
auto View = createSourceFileView(SourceFile, *Coverage); | error("Missing source file", SourceFile); | ||||
continue; | |||||
} | |||||
auto Coverage = | |||||
*ObjectMappings[SourceToObjectMappings[SourceFile][0]].second; | |||||
auto ObjectFilenames = SourceToObjectMappings[SourceFile]; | |||||
Pool.async([this, &SourceFile, Coverage, &Printer, ShowFilenames, | |||||
ObjectFilenames] { | |||||
auto View = createSourceFileView(SourceFile, Coverage, ObjectFilenames); | |||||
if (!View) { | if (!View) { | ||||
warning("The file '" + SourceFile + "' isn't covered."); | warning("The file '" + SourceFile + "' isn't covered."); | ||||
return; | return; | ||||
} | } | ||||
auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false); | auto OSOrErr = | ||||
Printer->createViewFile(SourceFile, /*InToplevel=*/false); | |||||
if (Error E = OSOrErr.takeError()) { | if (Error E = OSOrErr.takeError()) { | ||||
error("Could not create view file!", toString(std::move(E))); | error("Could not create view file!", toString(std::move(E))); | ||||
return; | return; | ||||
} | } | ||||
auto OS = std::move(OSOrErr.get()); | auto OS = std::move(OSOrErr.get()); | ||||
View->print(*OS.get(), /*Wholefile=*/true, | View->print(*OS.get(), /*Wholefile=*/true, | ||||
/*ShowSourceName=*/ShowFilenames); | /*ShowSourceName=*/ShowFilenames); | ||||
Printer->closeViewFile(std::move(OS)); | Printer->closeViewFile(std::move(OS)); | ||||
}); | }); | ||||
} | } | ||||
Pool.wait(); | Pool.wait(); | ||||
return 0; | return 0; | ||||
} | } | ||||
int CodeCoverageTool::report(int argc, const char **argv, | int CodeCoverageTool::report(int argc, const char **argv, | ||||
CommandLineParserType commandLineParser) { | CommandLineParserType commandLineParser) { | ||||
auto Err = commandLineParser(argc, argv); | auto Err = commandLineParser(argc, argv); | ||||
if (Err) | if (Err) | ||||
return Err; | return Err; | ||||
if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) | if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) | ||||
error("HTML output for summary reports is not yet supported."); | error("HTML output for summary reports is not yet supported."); | ||||
auto Coverage = load(); | for (const auto &ObjectFile : ObjectFilenames) { | ||||
auto Coverage = load(ObjectFile); | |||||
if (!Coverage) | if (!Coverage) | ||||
return 1; | return 1; | ||||
CoverageReport Report(ViewOpts, *Coverage.get()); | CoverageReport Report(ViewOpts, *Coverage.get()); | ||||
if (SourceFiles.empty()) | if (SourceFiles.empty()) | ||||
Report.renderFileReports(llvm::outs()); | Report.renderFileReports(llvm::outs()); | ||||
else | else | ||||
Report.renderFunctionReports(SourceFiles, llvm::outs()); | Report.renderFunctionReports(SourceFiles, llvm::outs()); | ||||
} | |||||
return 0; | return 0; | ||||
} | } | ||||
int CodeCoverageTool::export_(int argc, const char **argv, | int CodeCoverageTool::export_(int argc, const char **argv, | ||||
CommandLineParserType commandLineParser) { | CommandLineParserType commandLineParser) { | ||||
auto Err = commandLineParser(argc, argv); | auto Err = commandLineParser(argc, argv); | ||||
if (Err) | if (Err) | ||||
return Err; | return Err; | ||||
auto Coverage = load(); | for (const auto &ObjectFile : ObjectFilenames) { | ||||
auto Coverage = load(ObjectFile); | |||||
if (!Coverage) { | if (!Coverage) { | ||||
error("Could not load coverage information"); | error("Could not load coverage information"); | ||||
return 1; | return 1; | ||||
} | } | ||||
exportCoverageDataToJson(ObjectFilename, *Coverage.get(), outs()); | exportCoverageDataToJson(ObjectFile, *Coverage.get(), outs()); | ||||
} | |||||
return 0; | return 0; | ||||
} | } | ||||
int showMain(int argc, const char *argv[]) { | int showMain(int argc, const char *argv[]) { | ||||
CodeCoverageTool Tool; | CodeCoverageTool Tool; | ||||
return Tool.run(CodeCoverageTool::Show, argc, argv); | return Tool.run(CodeCoverageTool::Show, argc, argv); | ||||
} | } | ||||
Show All 10 Lines |