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 @@ -23,6 +23,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/Analysis/EHPersonalities.h" #include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Config/config.h" #include "llvm/IR/CFG.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugLoc.h" @@ -36,6 +37,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 +98,11 @@ // profiling runtime to emit .gcda files when run. bool emitProfileArcs(); + bool isFunctionInstrumented(const Function &F); + void getRegex(StringRef Filter, std::vector &Regexs); + static bool isFilenameMatchingRegex(const StringRef &Filename, + std::vector &Regexs); + // Get pointers to the functions in the runtime library. Constant *getStartFileFunc(); Constant *getEmitFunctionFunc(); @@ -123,6 +130,9 @@ const TargetLibraryInfo *TLI; LLVMContext *Ctx; SmallVector, 16> Funcs; + std::vector FilterRe; + std::vector ExcludeRe; + StringMap InstrumentedFiles; }; class GCOVProfilerLegacyPass : public ModulePass { @@ -422,6 +432,63 @@ }; } +void GCOVProfiler::getRegex(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; + } +} + +bool GCOVProfiler::isFilenameMatchingRegex(const 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; + } + +#if defined(HAVE_REALPATH) + char CanonicalPath[PATH_MAX]; + const char *RealPath = realpath(Filename.str().c_str(), CanonicalPath); +#else + const char *RealPath = nullptr; +#endif + + const StringRef RealFilename = RealPath ? RealPath : Filename; + bool Ret; + if (FilterRe.empty()) { + Ret = !isFilenameMatchingRegex(RealFilename, ExcludeRe); + } else if (ExcludeRe.empty()) { + Ret = isFilenameMatchingRegex(RealFilename, FilterRe); + } else { + Ret = isFilenameMatchingRegex(RealFilename, FilterRe) && + !isFilenameMatchingRegex(RealFilename, ExcludeRe); + } + InstrumentedFiles[Filename] = Ret; + return Ret; +} + std::string GCOVProfiler::mangleName(const DICompileUnit *CU, GCovFileType OutputType) { bool Notes = OutputType == GCovFileType::GCNO; @@ -469,6 +536,9 @@ this->TLI = &TLI; Ctx = &M.getContext(); + getRegex(Options.Filter, FilterRe); + getRegex(Options.Exclude, ExcludeRe); + if (Options.EmitNotes) emitProfileNotes(); if (Options.EmitData) return emitProfileArcs(); return false; @@ -554,7 +624,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 +703,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;