Index: include/llvm/Transforms/Instrumentation.h =================================================================== --- include/llvm/Transforms/Instrumentation.h +++ include/llvm/Transforms/Instrumentation.h @@ -75,6 +75,12 @@ // Emit the exit block immediately after the start block, rather than after // all of the function body's blocks. bool ExitBlockBeforeBody; + + // Regexes separated by a semi-colon to filter the files to instrument. + std::string Filter; + + // Regexes separated by a semi-colon to filter the files to not instrument. + std::string Exclude; }; ModulePass *createGCOVProfilerPass(const GCOVOptions &Options = Index: lib/Transforms/Instrumentation/GCOVProfiling.cpp =================================================================== --- lib/Transforms/Instrumentation/GCOVProfiling.cpp +++ lib/Transforms/Instrumentation/GCOVProfiling.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Instrumentation/GCOVProfiler.h" @@ -96,6 +97,11 @@ // profiling runtime to emit .gcda files when run. bool emitProfileArcs(); + bool isFunctionInstrumented(const Function &F); + std::vector createRegexesFromString(StringRef RegexesStr); + static bool doesFilenameMatchARegex(StringRef Filename, + std::vector &Regexes); + // Get pointers to the functions in the runtime library. Constant *getStartFileFunc(); Constant *getEmitFunctionFunc(); @@ -123,6 +129,9 @@ const TargetLibraryInfo *TLI; LLVMContext *Ctx; SmallVector, 16> Funcs; + std::vector FilterRe; + std::vector ExcludeRe; + StringMap InstrumentedFiles; }; class GCOVProfilerLegacyPass : public ModulePass { @@ -421,6 +430,72 @@ }; } +// RegexesStr is a string containing differents regex separated by a semi-colon. +// For example "foo\..*$;bar\..*$". +std::vector GCOVProfiler::createRegexesFromString(StringRef RegexesStr) { + std::vector Regexes; + while (!RegexesStr.empty()) { + std::pair HeadTail = RegexesStr.split(';'); + if (!HeadTail.first.empty()) { + Regex Re(HeadTail.first); + std::string Err; + if (!Re.isValid(Err)) { + Ctx->emitError(Twine("Regex ") + HeadTail.first + + " is not valid: " + Err); + } + Regexes.emplace_back(std::move(Re)); + } + RegexesStr = HeadTail.second; + } + return Regexes; +} + +bool GCOVProfiler::doesFilenameMatchARegex(StringRef Filename, + std::vector &Regexes) { + for (Regex &Re : Regexes) { + if (Re.match(Filename)) { + return true; + } + } + return false; +} + +bool GCOVProfiler::isFunctionInstrumented(const Function &F) { + if (FilterRe.empty() && ExcludeRe.empty()) { + return true; + } + const StringRef Filename = F.getSubprogram()->getFilename(); + auto It = InstrumentedFiles.find(Filename); + if (It != InstrumentedFiles.end()) { + return It->second; + } + + SmallString<256> RealPath; + StringRef RealFilename; + + // Path can be + // /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/*.h so for + // such a case we must get the real_path. + if (sys::fs::real_path(Filename, RealPath)) { + // real_path can fail with path like "foo.c". + RealFilename = Filename; + } else { + RealFilename = RealPath; + } + + bool ShouldInstrument; + if (FilterRe.empty()) { + ShouldInstrument = !doesFilenameMatchARegex(RealFilename, ExcludeRe); + } else if (ExcludeRe.empty()) { + ShouldInstrument = doesFilenameMatchARegex(RealFilename, FilterRe); + } else { + ShouldInstrument = doesFilenameMatchARegex(RealFilename, FilterRe) && + !doesFilenameMatchARegex(RealFilename, ExcludeRe); + } + InstrumentedFiles[Filename] = ShouldInstrument; + return ShouldInstrument; +} + std::string GCOVProfiler::mangleName(const DICompileUnit *CU, GCovFileType OutputType) { bool Notes = OutputType == GCovFileType::GCNO; @@ -468,6 +543,9 @@ this->TLI = &TLI; Ctx = &M.getContext(); + FilterRe = createRegexesFromString(Options.Filter); + ExcludeRe = createRegexesFromString(Options.Exclude); + if (Options.EmitNotes) emitProfileNotes(); if (Options.EmitData) return emitProfileArcs(); return false; @@ -553,7 +631,8 @@ for (auto &F : M->functions()) { DISubprogram *SP = F.getSubprogram(); if (!SP) continue; - if (!functionHasLines(F)) continue; + if (!functionHasLines(F) || !isFunctionInstrumented(F)) + continue; // TODO: Functions using scope-based EH are currently not supported. if (isUsingScopeBasedEH(F)) continue; @@ -637,7 +716,8 @@ for (auto &F : M->functions()) { DISubprogram *SP = F.getSubprogram(); if (!SP) continue; - if (!functionHasLines(F)) continue; + if (!functionHasLines(F) || !isFunctionInstrumented(F)) + continue; // TODO: Functions using scope-based EH are currently not supported. if (isUsingScopeBasedEH(F)) continue; if (!Result) Result = true;