1*0b57cec5SDimitry Andric //===- gcov.cpp - GCOV compatible LLVM coverage tool ----------------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // llvm-cov is a command line tools to analyze and report coverage information. 10*0b57cec5SDimitry Andric // 11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 12*0b57cec5SDimitry Andric 13*0b57cec5SDimitry Andric #include "llvm/ProfileData/GCOV.h" 14*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 15*0b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h" 16*0b57cec5SDimitry Andric #include "llvm/Support/Errc.h" 17*0b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 18*0b57cec5SDimitry Andric #include "llvm/Support/Path.h" 19*0b57cec5SDimitry Andric #include <system_error> 20*0b57cec5SDimitry Andric using namespace llvm; 21*0b57cec5SDimitry Andric 22*0b57cec5SDimitry Andric static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, 23*0b57cec5SDimitry Andric const std::string &InputGCNO, 24*0b57cec5SDimitry Andric const std::string &InputGCDA, bool DumpGCOV, 25*0b57cec5SDimitry Andric const GCOV::Options &Options) { 26*0b57cec5SDimitry Andric SmallString<128> CoverageFileStem(ObjectDir); 27*0b57cec5SDimitry Andric if (CoverageFileStem.empty()) { 28*0b57cec5SDimitry Andric // If no directory was specified with -o, look next to the source file. 29*0b57cec5SDimitry Andric CoverageFileStem = sys::path::parent_path(SourceFile); 30*0b57cec5SDimitry Andric sys::path::append(CoverageFileStem, sys::path::stem(SourceFile)); 31*0b57cec5SDimitry Andric } else if (sys::fs::is_directory(ObjectDir)) 32*0b57cec5SDimitry Andric // A directory name was given. Use it and the source file name. 33*0b57cec5SDimitry Andric sys::path::append(CoverageFileStem, sys::path::stem(SourceFile)); 34*0b57cec5SDimitry Andric else 35*0b57cec5SDimitry Andric // A file was given. Ignore the source file and look next to this file. 36*0b57cec5SDimitry Andric sys::path::replace_extension(CoverageFileStem, ""); 37*0b57cec5SDimitry Andric 38*0b57cec5SDimitry Andric std::string GCNO = InputGCNO.empty() 39*0b57cec5SDimitry Andric ? std::string(CoverageFileStem.str()) + ".gcno" 40*0b57cec5SDimitry Andric : InputGCNO; 41*0b57cec5SDimitry Andric std::string GCDA = InputGCDA.empty() 42*0b57cec5SDimitry Andric ? std::string(CoverageFileStem.str()) + ".gcda" 43*0b57cec5SDimitry Andric : InputGCDA; 44*0b57cec5SDimitry Andric GCOVFile GF; 45*0b57cec5SDimitry Andric 46*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff = 47*0b57cec5SDimitry Andric MemoryBuffer::getFileOrSTDIN(GCNO); 48*0b57cec5SDimitry Andric if (std::error_code EC = GCNO_Buff.getError()) { 49*0b57cec5SDimitry Andric errs() << GCNO << ": " << EC.message() << "\n"; 50*0b57cec5SDimitry Andric return; 51*0b57cec5SDimitry Andric } 52*0b57cec5SDimitry Andric GCOVBuffer GCNO_GB(GCNO_Buff.get().get()); 53*0b57cec5SDimitry Andric if (!GF.readGCNO(GCNO_GB)) { 54*0b57cec5SDimitry Andric errs() << "Invalid .gcno File!\n"; 55*0b57cec5SDimitry Andric return; 56*0b57cec5SDimitry Andric } 57*0b57cec5SDimitry Andric 58*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff = 59*0b57cec5SDimitry Andric MemoryBuffer::getFileOrSTDIN(GCDA); 60*0b57cec5SDimitry Andric if (std::error_code EC = GCDA_Buff.getError()) { 61*0b57cec5SDimitry Andric if (EC != errc::no_such_file_or_directory) { 62*0b57cec5SDimitry Andric errs() << GCDA << ": " << EC.message() << "\n"; 63*0b57cec5SDimitry Andric return; 64*0b57cec5SDimitry Andric } 65*0b57cec5SDimitry Andric // Clear the filename to make it clear we didn't read anything. 66*0b57cec5SDimitry Andric GCDA = "-"; 67*0b57cec5SDimitry Andric } else { 68*0b57cec5SDimitry Andric GCOVBuffer GCDA_GB(GCDA_Buff.get().get()); 69*0b57cec5SDimitry Andric if (!GF.readGCDA(GCDA_GB)) { 70*0b57cec5SDimitry Andric errs() << "Invalid .gcda File!\n"; 71*0b57cec5SDimitry Andric return; 72*0b57cec5SDimitry Andric } 73*0b57cec5SDimitry Andric } 74*0b57cec5SDimitry Andric 75*0b57cec5SDimitry Andric if (DumpGCOV) 76*0b57cec5SDimitry Andric GF.print(errs()); 77*0b57cec5SDimitry Andric 78*0b57cec5SDimitry Andric FileInfo FI(Options); 79*0b57cec5SDimitry Andric GF.collectLineCounts(FI); 80*0b57cec5SDimitry Andric FI.print(llvm::outs(), SourceFile, GCNO, GCDA); 81*0b57cec5SDimitry Andric } 82*0b57cec5SDimitry Andric 83*0b57cec5SDimitry Andric int gcovMain(int argc, const char *argv[]) { 84*0b57cec5SDimitry Andric cl::list<std::string> SourceFiles(cl::Positional, cl::OneOrMore, 85*0b57cec5SDimitry Andric cl::desc("SOURCEFILE")); 86*0b57cec5SDimitry Andric 87*0b57cec5SDimitry Andric cl::opt<bool> AllBlocks("a", cl::Grouping, cl::init(false), 88*0b57cec5SDimitry Andric cl::desc("Display all basic blocks")); 89*0b57cec5SDimitry Andric cl::alias AllBlocksA("all-blocks", cl::aliasopt(AllBlocks)); 90*0b57cec5SDimitry Andric 91*0b57cec5SDimitry Andric cl::opt<bool> BranchProb("b", cl::Grouping, cl::init(false), 92*0b57cec5SDimitry Andric cl::desc("Display branch probabilities")); 93*0b57cec5SDimitry Andric cl::alias BranchProbA("branch-probabilities", cl::aliasopt(BranchProb)); 94*0b57cec5SDimitry Andric 95*0b57cec5SDimitry Andric cl::opt<bool> BranchCount("c", cl::Grouping, cl::init(false), 96*0b57cec5SDimitry Andric cl::desc("Display branch counts instead " 97*0b57cec5SDimitry Andric "of percentages (requires -b)")); 98*0b57cec5SDimitry Andric cl::alias BranchCountA("branch-counts", cl::aliasopt(BranchCount)); 99*0b57cec5SDimitry Andric 100*0b57cec5SDimitry Andric cl::opt<bool> LongNames("l", cl::Grouping, cl::init(false), 101*0b57cec5SDimitry Andric cl::desc("Prefix filenames with the main file")); 102*0b57cec5SDimitry Andric cl::alias LongNamesA("long-file-names", cl::aliasopt(LongNames)); 103*0b57cec5SDimitry Andric 104*0b57cec5SDimitry Andric cl::opt<bool> FuncSummary("f", cl::Grouping, cl::init(false), 105*0b57cec5SDimitry Andric cl::desc("Show coverage for each function")); 106*0b57cec5SDimitry Andric cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary)); 107*0b57cec5SDimitry Andric 108*0b57cec5SDimitry Andric cl::opt<bool> NoOutput("n", cl::Grouping, cl::init(false), 109*0b57cec5SDimitry Andric cl::desc("Do not output any .gcov files")); 110*0b57cec5SDimitry Andric cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput)); 111*0b57cec5SDimitry Andric 112*0b57cec5SDimitry Andric cl::opt<std::string> ObjectDir( 113*0b57cec5SDimitry Andric "o", cl::value_desc("DIR|FILE"), cl::init(""), 114*0b57cec5SDimitry Andric cl::desc("Find objects in DIR or based on FILE's path")); 115*0b57cec5SDimitry Andric cl::alias ObjectDirA("object-directory", cl::aliasopt(ObjectDir)); 116*0b57cec5SDimitry Andric cl::alias ObjectDirB("object-file", cl::aliasopt(ObjectDir)); 117*0b57cec5SDimitry Andric 118*0b57cec5SDimitry Andric cl::opt<bool> PreservePaths("p", cl::Grouping, cl::init(false), 119*0b57cec5SDimitry Andric cl::desc("Preserve path components")); 120*0b57cec5SDimitry Andric cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths)); 121*0b57cec5SDimitry Andric 122*0b57cec5SDimitry Andric cl::opt<bool> UncondBranch("u", cl::Grouping, cl::init(false), 123*0b57cec5SDimitry Andric cl::desc("Display unconditional branch info " 124*0b57cec5SDimitry Andric "(requires -b)")); 125*0b57cec5SDimitry Andric cl::alias UncondBranchA("unconditional-branches", cl::aliasopt(UncondBranch)); 126*0b57cec5SDimitry Andric 127*0b57cec5SDimitry Andric cl::opt<bool> HashFilenames("x", cl::Grouping, cl::init(false), 128*0b57cec5SDimitry Andric cl::desc("Hash long pathnames")); 129*0b57cec5SDimitry Andric cl::alias HashFilenamesA("hash-filenames", cl::aliasopt(HashFilenames)); 130*0b57cec5SDimitry Andric 131*0b57cec5SDimitry Andric 132*0b57cec5SDimitry Andric cl::OptionCategory DebugCat("Internal and debugging options"); 133*0b57cec5SDimitry Andric cl::opt<bool> DumpGCOV("dump", cl::init(false), cl::cat(DebugCat), 134*0b57cec5SDimitry Andric cl::desc("Dump the gcov file to stderr")); 135*0b57cec5SDimitry Andric cl::opt<std::string> InputGCNO("gcno", cl::cat(DebugCat), cl::init(""), 136*0b57cec5SDimitry Andric cl::desc("Override inferred gcno file")); 137*0b57cec5SDimitry Andric cl::opt<std::string> InputGCDA("gcda", cl::cat(DebugCat), cl::init(""), 138*0b57cec5SDimitry Andric cl::desc("Override inferred gcda file")); 139*0b57cec5SDimitry Andric 140*0b57cec5SDimitry Andric cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 141*0b57cec5SDimitry Andric 142*0b57cec5SDimitry Andric GCOV::Options Options(AllBlocks, BranchProb, BranchCount, FuncSummary, 143*0b57cec5SDimitry Andric PreservePaths, UncondBranch, LongNames, NoOutput, 144*0b57cec5SDimitry Andric HashFilenames); 145*0b57cec5SDimitry Andric 146*0b57cec5SDimitry Andric for (const auto &SourceFile : SourceFiles) 147*0b57cec5SDimitry Andric reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV, 148*0b57cec5SDimitry Andric Options); 149*0b57cec5SDimitry Andric return 0; 150*0b57cec5SDimitry Andric } 151