10b57cec5SDimitry Andric //===- gcov.cpp - GCOV compatible LLVM coverage tool ----------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // llvm-cov is a command line tools to analyze and report coverage information. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #include "llvm/ProfileData/GCOV.h" 140b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 150b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h" 160b57cec5SDimitry Andric #include "llvm/Support/Errc.h" 170b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 180b57cec5SDimitry Andric #include "llvm/Support/Path.h" 190b57cec5SDimitry Andric #include <system_error> 200b57cec5SDimitry Andric using namespace llvm; 210b57cec5SDimitry Andric 220b57cec5SDimitry Andric static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, 230b57cec5SDimitry Andric const std::string &InputGCNO, 240b57cec5SDimitry Andric const std::string &InputGCDA, bool DumpGCOV, 250b57cec5SDimitry Andric const GCOV::Options &Options) { 260b57cec5SDimitry Andric SmallString<128> CoverageFileStem(ObjectDir); 270b57cec5SDimitry Andric if (CoverageFileStem.empty()) { 280b57cec5SDimitry Andric // If no directory was specified with -o, look next to the source file. 290b57cec5SDimitry Andric CoverageFileStem = sys::path::parent_path(SourceFile); 300b57cec5SDimitry Andric sys::path::append(CoverageFileStem, sys::path::stem(SourceFile)); 310b57cec5SDimitry Andric } else if (sys::fs::is_directory(ObjectDir)) 320b57cec5SDimitry Andric // A directory name was given. Use it and the source file name. 330b57cec5SDimitry Andric sys::path::append(CoverageFileStem, sys::path::stem(SourceFile)); 340b57cec5SDimitry Andric else 350b57cec5SDimitry Andric // A file was given. Ignore the source file and look next to this file. 360b57cec5SDimitry Andric sys::path::replace_extension(CoverageFileStem, ""); 370b57cec5SDimitry Andric 38*7a6dacacSDimitry Andric std::string GCNO = 39*7a6dacacSDimitry Andric InputGCNO.empty() ? std::string(CoverageFileStem) + ".gcno" : InputGCNO; 40*7a6dacacSDimitry Andric std::string GCDA = 41*7a6dacacSDimitry Andric InputGCDA.empty() ? std::string(CoverageFileStem) + ".gcda" : InputGCDA; 420b57cec5SDimitry Andric GCOVFile GF; 430b57cec5SDimitry Andric 44e8d8bef9SDimitry Andric // Open .gcda and .gcda without requiring a NUL terminator. The concurrent 45e8d8bef9SDimitry Andric // modification may nullify the NUL terminator condition. 460b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff = 47fe6060f1SDimitry Andric MemoryBuffer::getFileOrSTDIN(GCNO, /*IsText=*/false, 48fe6060f1SDimitry Andric /*RequiresNullTerminator=*/false); 490b57cec5SDimitry Andric if (std::error_code EC = GCNO_Buff.getError()) { 500b57cec5SDimitry Andric errs() << GCNO << ": " << EC.message() << "\n"; 510b57cec5SDimitry Andric return; 520b57cec5SDimitry Andric } 530b57cec5SDimitry Andric GCOVBuffer GCNO_GB(GCNO_Buff.get().get()); 540b57cec5SDimitry Andric if (!GF.readGCNO(GCNO_GB)) { 550b57cec5SDimitry Andric errs() << "Invalid .gcno File!\n"; 560b57cec5SDimitry Andric return; 570b57cec5SDimitry Andric } 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff = 60fe6060f1SDimitry Andric MemoryBuffer::getFileOrSTDIN(GCDA, /*IsText=*/false, 61fe6060f1SDimitry Andric /*RequiresNullTerminator=*/false); 620b57cec5SDimitry Andric if (std::error_code EC = GCDA_Buff.getError()) { 630b57cec5SDimitry Andric if (EC != errc::no_such_file_or_directory) { 640b57cec5SDimitry Andric errs() << GCDA << ": " << EC.message() << "\n"; 650b57cec5SDimitry Andric return; 660b57cec5SDimitry Andric } 670b57cec5SDimitry Andric // Clear the filename to make it clear we didn't read anything. 680b57cec5SDimitry Andric GCDA = "-"; 690b57cec5SDimitry Andric } else { 705ffd83dbSDimitry Andric GCOVBuffer gcda_buf(GCDA_Buff.get().get()); 715ffd83dbSDimitry Andric if (!gcda_buf.readGCDAFormat()) 725ffd83dbSDimitry Andric errs() << GCDA << ":not a gcov data file\n"; 735ffd83dbSDimitry Andric else if (!GF.readGCDA(gcda_buf)) 740b57cec5SDimitry Andric errs() << "Invalid .gcda File!\n"; 750b57cec5SDimitry Andric } 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric if (DumpGCOV) 780b57cec5SDimitry Andric GF.print(errs()); 790b57cec5SDimitry Andric 80e8d8bef9SDimitry Andric gcovOneInput(Options, SourceFile, GCNO, GCDA, GF); 810b57cec5SDimitry Andric } 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric int gcovMain(int argc, const char *argv[]) { 840b57cec5SDimitry Andric cl::list<std::string> SourceFiles(cl::Positional, cl::OneOrMore, 850b57cec5SDimitry Andric cl::desc("SOURCEFILE")); 860b57cec5SDimitry Andric 870b57cec5SDimitry Andric cl::opt<bool> AllBlocks("a", cl::Grouping, cl::init(false), 880b57cec5SDimitry Andric cl::desc("Display all basic blocks")); 890b57cec5SDimitry Andric cl::alias AllBlocksA("all-blocks", cl::aliasopt(AllBlocks)); 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric cl::opt<bool> BranchProb("b", cl::Grouping, cl::init(false), 920b57cec5SDimitry Andric cl::desc("Display branch probabilities")); 930b57cec5SDimitry Andric cl::alias BranchProbA("branch-probabilities", cl::aliasopt(BranchProb)); 940b57cec5SDimitry Andric 950b57cec5SDimitry Andric cl::opt<bool> BranchCount("c", cl::Grouping, cl::init(false), 960b57cec5SDimitry Andric cl::desc("Display branch counts instead " 970b57cec5SDimitry Andric "of percentages (requires -b)")); 980b57cec5SDimitry Andric cl::alias BranchCountA("branch-counts", cl::aliasopt(BranchCount)); 990b57cec5SDimitry Andric 1000b57cec5SDimitry Andric cl::opt<bool> LongNames("l", cl::Grouping, cl::init(false), 1010b57cec5SDimitry Andric cl::desc("Prefix filenames with the main file")); 1020b57cec5SDimitry Andric cl::alias LongNamesA("long-file-names", cl::aliasopt(LongNames)); 1030b57cec5SDimitry Andric 1040b57cec5SDimitry Andric cl::opt<bool> FuncSummary("f", cl::Grouping, cl::init(false), 1050b57cec5SDimitry Andric cl::desc("Show coverage for each function")); 1060b57cec5SDimitry Andric cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary)); 1070b57cec5SDimitry Andric 1085ffd83dbSDimitry Andric // Supported by gcov 4.9~8. gcov 9 (GCC r265587) removed --intermediate-format 1095ffd83dbSDimitry Andric // and -i was changed to mean --json-format. We consider this format still 1105ffd83dbSDimitry Andric // useful and support -i. 1115ffd83dbSDimitry Andric cl::opt<bool> Intermediate( 1125ffd83dbSDimitry Andric "intermediate-format", cl::init(false), 1135ffd83dbSDimitry Andric cl::desc("Output .gcov in intermediate text format")); 1145ffd83dbSDimitry Andric cl::alias IntermediateA("i", cl::desc("Alias for --intermediate-format"), 1155ffd83dbSDimitry Andric cl::Grouping, cl::NotHidden, 1165ffd83dbSDimitry Andric cl::aliasopt(Intermediate)); 1175ffd83dbSDimitry Andric 118e8d8bef9SDimitry Andric cl::opt<bool> Demangle("demangled-names", cl::init(false), 119e8d8bef9SDimitry Andric cl::desc("Demangle function names")); 120e8d8bef9SDimitry Andric cl::alias DemangleA("m", cl::desc("Alias for --demangled-names"), 121e8d8bef9SDimitry Andric cl::Grouping, cl::NotHidden, cl::aliasopt(Demangle)); 122e8d8bef9SDimitry Andric 1230b57cec5SDimitry Andric cl::opt<bool> NoOutput("n", cl::Grouping, cl::init(false), 1240b57cec5SDimitry Andric cl::desc("Do not output any .gcov files")); 1250b57cec5SDimitry Andric cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput)); 1260b57cec5SDimitry Andric 1270b57cec5SDimitry Andric cl::opt<std::string> ObjectDir( 1280b57cec5SDimitry Andric "o", cl::value_desc("DIR|FILE"), cl::init(""), 1290b57cec5SDimitry Andric cl::desc("Find objects in DIR or based on FILE's path")); 1300b57cec5SDimitry Andric cl::alias ObjectDirA("object-directory", cl::aliasopt(ObjectDir)); 1310b57cec5SDimitry Andric cl::alias ObjectDirB("object-file", cl::aliasopt(ObjectDir)); 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric cl::opt<bool> PreservePaths("p", cl::Grouping, cl::init(false), 1340b57cec5SDimitry Andric cl::desc("Preserve path components")); 1350b57cec5SDimitry Andric cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths)); 1360b57cec5SDimitry Andric 137e8d8bef9SDimitry Andric cl::opt<bool> RelativeOnly( 138e8d8bef9SDimitry Andric "r", cl::Grouping, 139e8d8bef9SDimitry Andric cl::desc("Only dump files with relative paths or absolute paths with the " 140e8d8bef9SDimitry Andric "prefix specified by -s")); 141e8d8bef9SDimitry Andric cl::alias RelativeOnlyA("relative-only", cl::aliasopt(RelativeOnly)); 142e8d8bef9SDimitry Andric cl::opt<std::string> SourcePrefix("s", cl::desc("Source prefix to elide")); 143e8d8bef9SDimitry Andric cl::alias SourcePrefixA("source-prefix", cl::aliasopt(SourcePrefix)); 144e8d8bef9SDimitry Andric 1455ffd83dbSDimitry Andric cl::opt<bool> UseStdout("t", cl::Grouping, cl::init(false), 1465ffd83dbSDimitry Andric cl::desc("Print to stdout")); 1475ffd83dbSDimitry Andric cl::alias UseStdoutA("stdout", cl::aliasopt(UseStdout)); 1485ffd83dbSDimitry Andric 1490b57cec5SDimitry Andric cl::opt<bool> UncondBranch("u", cl::Grouping, cl::init(false), 1500b57cec5SDimitry Andric cl::desc("Display unconditional branch info " 1510b57cec5SDimitry Andric "(requires -b)")); 1520b57cec5SDimitry Andric cl::alias UncondBranchA("unconditional-branches", cl::aliasopt(UncondBranch)); 1530b57cec5SDimitry Andric 1540b57cec5SDimitry Andric cl::opt<bool> HashFilenames("x", cl::Grouping, cl::init(false), 1550b57cec5SDimitry Andric cl::desc("Hash long pathnames")); 1560b57cec5SDimitry Andric cl::alias HashFilenamesA("hash-filenames", cl::aliasopt(HashFilenames)); 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric 1590b57cec5SDimitry Andric cl::OptionCategory DebugCat("Internal and debugging options"); 1600b57cec5SDimitry Andric cl::opt<bool> DumpGCOV("dump", cl::init(false), cl::cat(DebugCat), 1610b57cec5SDimitry Andric cl::desc("Dump the gcov file to stderr")); 1620b57cec5SDimitry Andric cl::opt<std::string> InputGCNO("gcno", cl::cat(DebugCat), cl::init(""), 1630b57cec5SDimitry Andric cl::desc("Override inferred gcno file")); 1640b57cec5SDimitry Andric cl::opt<std::string> InputGCDA("gcda", cl::cat(DebugCat), cl::init(""), 1650b57cec5SDimitry Andric cl::desc("Override inferred gcda file")); 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 1680b57cec5SDimitry Andric 1690b57cec5SDimitry Andric GCOV::Options Options(AllBlocks, BranchProb, BranchCount, FuncSummary, 1705ffd83dbSDimitry Andric PreservePaths, UncondBranch, Intermediate, LongNames, 171e8d8bef9SDimitry Andric Demangle, NoOutput, RelativeOnly, UseStdout, 172e8d8bef9SDimitry Andric HashFilenames, SourcePrefix); 1730b57cec5SDimitry Andric 1740b57cec5SDimitry Andric for (const auto &SourceFile : SourceFiles) 1750b57cec5SDimitry Andric reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV, 1760b57cec5SDimitry Andric Options); 1770b57cec5SDimitry Andric return 0; 1780b57cec5SDimitry Andric } 179