From 01c05505841940a899fbfd3e214b5d3214d80ef6 Mon Sep 17 00:00:00 2001 From: Justin Bogner Date: Tue, 4 Feb 2014 10:45:02 +0000 Subject: [PATCH] llvm-cov: Implement the preserve-paths flag Until now, when a path in a gcno file included a directory, we would emit our .gcov file in that directory, whereas gcov always emits the file in the current directory. In doing so, this implements gcov's strange name-mangling -p flag, which is needed to avoid clobbering files when two with the same name exist in different directories. The path mangling is a bit ugly and only handles unix-like paths, but it's simple, and it doesn't make any guesses as to how it should behave outside of what gcov documents. If we decide this should be cross platform later, we can consider the compatibility implications then. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@200754 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Support/GCOV.h | 16 ++-- lib/IR/GCOV.cpp | 59 ++++++++++--- test/tools/llvm-cov/Inputs/test_-b.output | 2 +- test/tools/llvm-cov/Inputs/test_-f.output | 2 +- .../tools/llvm-cov/Inputs/test_no_gcda.output | 2 +- .../llvm-cov/Inputs/test_no_options.output | 2 +- .../Inputs/test_no_preserve_paths.output | 8 ++ .../tools/llvm-cov/Inputs/test_paths.cpp.gcov | 79 ++++++++++++++++++ test/tools/llvm-cov/Inputs/test_paths.gcda | Bin 0 -> 904 bytes test/tools/llvm-cov/Inputs/test_paths.gcno | Bin 0 -> 4476 bytes test/tools/llvm-cov/Inputs/test_paths.h.gcov | 8 ++ .../Inputs/test_preserve_paths.output | 8 ++ test/tools/llvm-cov/llvm-cov.test | 12 +++ tools/llvm-cov/llvm-cov.cpp | 6 +- 14 files changed, 184 insertions(+), 20 deletions(-) create mode 100644 test/tools/llvm-cov/Inputs/test_no_preserve_paths.output create mode 100644 test/tools/llvm-cov/Inputs/test_paths.cpp.gcov create mode 100644 test/tools/llvm-cov/Inputs/test_paths.gcda create mode 100644 test/tools/llvm-cov/Inputs/test_paths.gcno create mode 100644 test/tools/llvm-cov/Inputs/test_paths.h.gcov create mode 100644 test/tools/llvm-cov/Inputs/test_preserve_paths.output diff --git a/include/llvm/Support/GCOV.h b/include/llvm/Support/GCOV.h index 4e7920b8e94..aeac4555cee 100644 --- a/include/llvm/Support/GCOV.h +++ b/include/llvm/Support/GCOV.h @@ -37,14 +37,15 @@ namespace GCOV { /// GCOVOptions - A struct for passing gcov options between functions. struct GCOVOptions { - GCOVOptions(bool A, bool B, bool C, bool F, bool U) : - AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F), UncondBranch(U) - {} + GCOVOptions(bool A, bool B, bool C, bool F, bool P, bool U) + : AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F), + PreservePaths(P), UncondBranch(U) {} bool AllBlocks; bool BranchInfo; bool BranchCount; bool FuncCoverage; + bool PreservePaths; bool UncondBranch; }; @@ -401,8 +402,13 @@ private: StringMap LineInfo; uint32_t RunCount; uint32_t ProgramCount; - SmallVector FileCoverages; - MapVector FuncCoverages; + + typedef SmallVector, 4> + FileCoverageList; + typedef MapVector FuncCoverageMap; + + FileCoverageList FileCoverages; + FuncCoverageMap FuncCoverages; }; } diff --git a/lib/IR/GCOV.cpp b/lib/IR/GCOV.cpp index 466828113b8..45ef78e078a 100644 --- a/lib/IR/GCOV.cpp +++ b/lib/IR/GCOV.cpp @@ -16,8 +16,10 @@ #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/GCOV.h" #include "llvm/Support/MemoryObject.h" +#include "llvm/Support/Path.h" #include "llvm/Support/system_error.h" #include using namespace llvm; @@ -429,6 +431,42 @@ static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) { return OS; } +/// Convert a path to a gcov filename. If PreservePaths is true, this +/// translates "/" to "#", ".." to "^", and drops ".", to match gcov. +static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) { + if (!PreservePaths) + return (sys::path::filename(Filename) + ".gcov").str(); + + // This behaviour is defined by gcov in terms of text replacements, so it's + // not likely to do anything useful on filesystems with different textual + // conventions. + llvm::SmallString<256> Result(""); + StringRef::iterator I, S, E; + for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) { + if (*I != '/') + continue; + + if (I - S == 1 && *S == '.') { + // ".", the current directory, is skipped. + } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') { + // "..", the parent directory, is replaced with "^". + Result.append("^#"); + } else { + if (S < I) + // Leave other components intact, + Result.append(S, I); + // And separate with "#". + Result.push_back('#'); + } + S = I + 1; + } + + if (S < I) + Result.append(S, I); + Result.append(".gcov"); + return Result.str(); +} + /// print - Print source files with collected line count information. void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile) { for (StringMap::const_iterator I = LineInfo.begin(), @@ -441,9 +479,10 @@ void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile) { } StringRef AllLines = Buff->getBuffer(); - std::string CovFilename = Filename.str() + ".gcov"; + std::string CoveragePath = mangleCoveragePath(Filename, + Options.PreservePaths); std::string ErrorInfo; - raw_fd_ostream OS(CovFilename.c_str(), ErrorInfo); + raw_fd_ostream OS(CoveragePath.c_str(), ErrorInfo); if (!ErrorInfo.empty()) errs() << ErrorInfo << "\n"; @@ -555,7 +594,7 @@ void FileInfo::print(StringRef GCNOFile, StringRef GCDAFile) { } } } - FileCoverages.push_back(FileCoverage); + FileCoverages.push_back(std::make_pair(CoveragePath, FileCoverage)); } // FIXME: There is no way to detect calls given current instrumentation. @@ -656,8 +695,8 @@ void FileInfo::printCoverage(const GCOVCoverage &Coverage) const { // printFuncCoverage - Print per-function coverage info. void FileInfo::printFuncCoverage() const { - for (MapVector::const_iterator I = - FuncCoverages.begin(), E = FuncCoverages.end(); I != E; ++I) { + for (FuncCoverageMap::const_iterator I = FuncCoverages.begin(), + E = FuncCoverages.end(); I != E; ++I) { const GCOVCoverage &Coverage = I->second; outs() << "Function '" << Coverage.Name << "'\n"; printCoverage(Coverage); @@ -667,12 +706,12 @@ void FileInfo::printFuncCoverage() const { // printFileCoverage - Print per-file coverage info. void FileInfo::printFileCoverage() const { - for (SmallVectorImpl::const_iterator I = - FileCoverages.begin(), E = FileCoverages.end(); I != E; ++I) { - const GCOVCoverage &Coverage = *I; + for (FileCoverageList::const_iterator I = FileCoverages.begin(), + E = FileCoverages.end(); I != E; ++I) { + const std::string &Filename = I->first; + const GCOVCoverage &Coverage = I->second; outs() << "File '" << Coverage.Name << "'\n"; printCoverage(Coverage); - outs() << Coverage.Name << ":creating '" << Coverage.Name - << ".gcov'\n\n"; + outs() << Coverage.Name << ":creating '" << Filename << "'\n\n"; } } diff --git a/test/tools/llvm-cov/Inputs/test_-b.output b/test/tools/llvm-cov/Inputs/test_-b.output index 4003ce8e692..515987d45ea 100644 --- a/test/tools/llvm-cov/Inputs/test_-b.output +++ b/test/tools/llvm-cov/Inputs/test_-b.output @@ -9,5 +9,5 @@ File './test.h' Lines executed:100.00% of 1 No branches No calls -./test.h:creating './test.h.gcov' +./test.h:creating 'test.h.gcov' diff --git a/test/tools/llvm-cov/Inputs/test_-f.output b/test/tools/llvm-cov/Inputs/test_-f.output index 9e98d888803..d97aa18174a 100644 --- a/test/tools/llvm-cov/Inputs/test_-f.output +++ b/test/tools/llvm-cov/Inputs/test_-f.output @@ -34,5 +34,5 @@ test.cpp:creating 'test.cpp.gcov' File './test.h' Lines executed:100.00% of 1 -./test.h:creating './test.h.gcov' +./test.h:creating 'test.h.gcov' diff --git a/test/tools/llvm-cov/Inputs/test_no_gcda.output b/test/tools/llvm-cov/Inputs/test_no_gcda.output index 69adce209b7..e994be72910 100644 --- a/test/tools/llvm-cov/Inputs/test_no_gcda.output +++ b/test/tools/llvm-cov/Inputs/test_no_gcda.output @@ -4,5 +4,5 @@ test.cpp:creating 'test.cpp.gcov' File './test.h' Lines executed:0.00% of 1 -./test.h:creating './test.h.gcov' +./test.h:creating 'test.h.gcov' diff --git a/test/tools/llvm-cov/Inputs/test_no_options.output b/test/tools/llvm-cov/Inputs/test_no_options.output index 93ea726720a..8be8c1c210a 100644 --- a/test/tools/llvm-cov/Inputs/test_no_options.output +++ b/test/tools/llvm-cov/Inputs/test_no_options.output @@ -4,5 +4,5 @@ test.cpp:creating 'test.cpp.gcov' File './test.h' Lines executed:100.00% of 1 -./test.h:creating './test.h.gcov' +./test.h:creating 'test.h.gcov' diff --git a/test/tools/llvm-cov/Inputs/test_no_preserve_paths.output b/test/tools/llvm-cov/Inputs/test_no_preserve_paths.output new file mode 100644 index 00000000000..ada0c360309 --- /dev/null +++ b/test/tools/llvm-cov/Inputs/test_no_preserve_paths.output @@ -0,0 +1,8 @@ +File 'srcdir/./nested_dir/../test.h' +Lines executed:100.00% of 1 +srcdir/./nested_dir/../test.h:creating 'test.h.gcov' + +File 'srcdir/./nested_dir/../test.cpp' +Lines executed:84.21% of 38 +srcdir/./nested_dir/../test.cpp:creating 'test.cpp.gcov' + diff --git a/test/tools/llvm-cov/Inputs/test_paths.cpp.gcov b/test/tools/llvm-cov/Inputs/test_paths.cpp.gcov new file mode 100644 index 00000000000..3982ddf4e5f --- /dev/null +++ b/test/tools/llvm-cov/Inputs/test_paths.cpp.gcov @@ -0,0 +1,79 @@ + -: 0:Source:srcdir/./nested_dir/../test.cpp + -: 0:Graph:test_paths.gcno + -: 0:Data:test_paths.gcda + -: 0:Runs:3 + -: 0:Programs:1 + -: 1:#include "test.h" + -: 2:#include + -: 3: + -: 4:bool on = false; + -: 5:int len = 42; + -: 6:double grid[10][10] = {0}; + -: 7:const char * hello = "world"; + -: 8:const char * world = "hello"; + -: 9: +12884901888: 10:void A::B() {} + -: 11: + #####: 12:void useless() {} + -: 13: + -: 14:double more_useless() { + #####: 15: return 0; + -: 16:} + -: 17: + -: 18:int foo() { + 3: 19: on = true; + 3: 20: return 3; + -: 21:} + -: 22: + -: 23:int bar() { + #####: 24: len--; + #####: 25: return foo() + 45; + -: 26:} + -: 27: + 12: 28:void assign(int ii, int jj) { + 12: 29: grid[ii][jj] = (ii+1) * (jj+1); + 12: 30:} + -: 31: + -: 32:void initialize_grid() { + 21: 33: for (int ii = 0; ii < 2; ii++) + 36: 34: for (int jj = 0; jj < 2; jj++) + 18: 35: assign(ii, jj); + 3: 36:} + -: 37: + -: 38:int main() { + 3: 39: initialize_grid(); + -: 40: + 3: 41: int a = 2; + 3: 42: on = rand() % 2; + 3: 43: if (on) { + 3: 44: foo(); + 3: 45: ++a; + 3: 46: } else { + #####: 47: bar(); + #####: 48: a += rand(); + -: 49: } + -: 50: + 66: 51: for (int ii = 0; ii < 10; ++ii) { + 30: 52: switch (rand() % 5) { + -: 53: case 0: + 6: 54: a += rand(); + 6: 55: break; + -: 56: case 1: + -: 57: case 2: + 3: 58: a += rand() / rand(); + 3: 59: break; + -: 60: case 3: + 9: 61: a -= rand(); + 9: 62: break; + -: 63: default: + 12: 64: a = -1; + 12: 65: } + 30: 66: } + -: 67: + 3: 68: A thing; +25769803782: 69: for (uint64_t ii = 0; ii < 4294967296; ++ii) +12884901888: 70: thing.B(); + -: 71: + 3: 72: return a + 8 + grid[2][3] + len; + -: 73: return more_useless(); + -: 74:} diff --git a/test/tools/llvm-cov/Inputs/test_paths.gcda b/test/tools/llvm-cov/Inputs/test_paths.gcda new file mode 100644 index 0000000000000000000000000000000000000000..7e2cf9ef20fec17d0b812af31ac1b2f62e879553 GIT binary patch literal 904 zcmYdHNlw=?GB7cSb5LYpU|?he5+ERVLR^CxNXJL{89Ev|xt4)N7c#PdX$A(690)*Z zus%j0|MkSK`Tqr~Cn>QAp$DW04S@Xz(zm<7 zWEsSNW{Jhcndy0%nLts9|9GI9U^I}+$O$AsVOsvnMhrv){b!n)msygTn3GwR8lPU2 znF0w~pcxWCd35)};sYiQ@UT`B=ok@dip`(+)pG wq-SSCl=KWU2Sx*xgY|*jbNEZm9FRCTA2=Hk(zg)i2cQ-d_ks9mU@!QMULklAVzR{Kdc247x14yJbA-$Ej*2al^}}NRB`2uslOi=`w_== zgr?ZLmxWVbchcLhm&&W%e&E~aba})6{tJ0KX#B8)3m*BJsK0-QIQ{zUqi$E1GwjWu zhzUN8Qj14P{}^0}$;7pT_*U-c#xSmjjlM3fNn>aHwK!^AC&-VJaZH$*bA~$n*sHwd zb2i_IqOjcwL)AGuZS0LdHuSYoat1%n&@*NHv4e|T?xN?}_glyDrt>xzcEUl}SP7p8 ze!CyG&?;52o`oXy%6;ZN%AoC~H5a)+eMvvwYnNdgkfRQnSRXuapxykUk@X=*+)OM3 zpR@s+z@bkwu^xE%!o)K8!`~T@a-ZkVb$d81%H}=#_e2(6eAIl;WZ`A6HTZKG{>xr& z6)(Bd;Lm5QFZtErv;3Eu(crVz2M(mcXYIe#xCVcLmy*%+W%&PT?ne%Fu-XVa_)uf( zA{6O7qeqOF_^iODYYp>R1uyj~bth*Pa;YVeK_@K(FEs-_%#-JQKk-a8)2mP)a%Pjc zhK}S;av){g=~0!``p9vzZh!ZiW}fsHGHk$ikS2y3 zq<7P3%fPvibE2_vo1k|DZr*G|&Z5S~BH^kj8>nqLUm6=r8F)F18hpAQkPqoG8vI?- z_cDBtGpezXg@+#?4Stzbha=at^bHL@YklcO8vMOY>+`)ExL51n;@|DHlInN+W5RKt k8vnf;InnmO*bf_=>3cXM>l-g#qsN;6BWmUUJuVaAFMsk{J^%m! literal 0 HcmV?d00001 diff --git a/test/tools/llvm-cov/Inputs/test_paths.h.gcov b/test/tools/llvm-cov/Inputs/test_paths.h.gcov new file mode 100644 index 00000000000..95e90ca664c --- /dev/null +++ b/test/tools/llvm-cov/Inputs/test_paths.h.gcov @@ -0,0 +1,8 @@ + -: 0:Source:srcdir/./nested_dir/../test.h + -: 0:Graph:test_paths.gcno + -: 0:Data:test_paths.gcda + -: 0:Runs:3 + -: 0:Programs:1 + 6: 1:struct A { + -: 2: virtual void B(); + -: 3:}; diff --git a/test/tools/llvm-cov/Inputs/test_preserve_paths.output b/test/tools/llvm-cov/Inputs/test_preserve_paths.output new file mode 100644 index 00000000000..53319725563 --- /dev/null +++ b/test/tools/llvm-cov/Inputs/test_preserve_paths.output @@ -0,0 +1,8 @@ +File 'srcdir/./nested_dir/../test.h' +Lines executed:100.00% of 1 +srcdir/./nested_dir/../test.h:creating 'srcdir#nested_dir#^#test.h.gcov' + +File 'srcdir/./nested_dir/../test.cpp' +Lines executed:84.21% of 38 +srcdir/./nested_dir/../test.cpp:creating 'srcdir#nested_dir#^#test.cpp.gcov' + diff --git a/test/tools/llvm-cov/llvm-cov.test b/test/tools/llvm-cov/llvm-cov.test index 239108240e0..c52a9815229 100644 --- a/test/tools/llvm-cov/llvm-cov.test +++ b/test/tools/llvm-cov/llvm-cov.test @@ -21,6 +21,18 @@ RUN: llvm-cov -o objdir test.c | diff -u test_no_options.output - RUN: diff -aub test_objdir.cpp.gcov test.cpp.gcov RUN: diff -aub test_objdir.h.gcov test.h.gcov +# Preserve paths. This mangles the output filenames. +RUN: mkdir -p %t/srcdir/nested_dir +RUN: cp test.cpp test.h %t/srcdir +RUN: llvm-cov -p test_paths.cpp | diff -u test_preserve_paths.output - +RUN: diff -aub test_paths.cpp.gcov srcdir#nested_dir#^#test.cpp.gcov +RUN: diff -aub test_paths.h.gcov srcdir#nested_dir#^#test.h.gcov + +# Don't preserve paths. Same results as preserve paths, but no mangling. +RUN: llvm-cov test_paths.cpp | diff -u test_no_preserve_paths.output - +RUN: diff -aub test_paths.cpp.gcov test.cpp.gcov +RUN: diff -aub test_paths.h.gcov test.h.gcov + # Function summaries. This changes stdout, but not the gcov files. RUN: llvm-cov test.c -f | diff -u test_-f.output - RUN: diff -aub test_no_options.cpp.gcov test.cpp.gcov diff --git a/tools/llvm-cov/llvm-cov.cpp b/tools/llvm-cov/llvm-cov.cpp index 61bee43d820..d7162c46882 100644 --- a/tools/llvm-cov/llvm-cov.cpp +++ b/tools/llvm-cov/llvm-cov.cpp @@ -47,6 +47,10 @@ static cl::opt ObjectDir("o", cl::value_desc("DIR"), cl::init(""), cl::desc("Search for objects in DIR")); static cl::alias ObjectDirA("object-directory", cl::aliasopt(ObjectDir)); +static cl::opt PreservePaths("p", cl::init(false), + cl::desc("Preserve path components")); +static cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths)); + static cl::opt UncondBranch("u", cl::init(false), cl::desc("Display unconditional branch info " "(requires -b)")); @@ -113,7 +117,7 @@ int main(int argc, char **argv) { GF.dump(); GCOVOptions Options(AllBlocks, BranchProb, BranchCount, FuncSummary, - UncondBranch); + PreservePaths, UncondBranch); FileInfo FI(Options); GF.collectLineCounts(FI); FI.print(InputGCNO, InputGCDA); -- 2.34.1