Index: include/llvm/Transforms/Instrumentation.h =================================================================== --- include/llvm/Transforms/Instrumentation.h +++ include/llvm/Transforms/Instrumentation.h @@ -64,6 +64,12 @@ // Emit the exit block immediately after the start block, rather than after // all of the function body's blocks. bool ExitBlockBeforeBody; + + // Regex separated by a comma to filter the files to instrument. + std::string Filter; + + // Regex separated by a comma 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 createRegexFromFilterString(StringRef Filter); + static bool doesFilenameMatchARegex(StringRef Filename, + std::vector &Regexs); + // 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 { @@ -422,6 +431,61 @@ }; } +std::vector GCOVProfiler::createRegexFromFilterString(StringRef Filter) { + std::vector Regexs; + while (!Filter.empty()) { + std::pair HeadTail = Filter.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); + } + Regexs.emplace_back(std::move(Re)); + } + Filter = HeadTail.second; + } + return Regexs; +} + +bool GCOVProfiler::doesFilenameMatchARegex(StringRef Filename, + std::vector &Regexs) { + for (Regex &Re : Regexs) { + 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; + const StringRef RealFilename = + sys::fs::real_path(Filename, RealPath) ? Filename : StringRef(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; @@ -469,6 +533,9 @@ this->TLI = &TLI; Ctx = &M.getContext(); + FilterRe = createRegexFromFilterString(Options.Filter); + ExcludeRe = createRegexFromFilterString(Options.Exclude); + if (Options.EmitNotes) emitProfileNotes(); if (Options.EmitData) return emitProfileArcs(); return false; @@ -554,7 +621,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; @@ -632,7 +700,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;