diff --git a/llvm/test/tools/llvm-profdata/merge-filtering.test b/llvm/test/tools/llvm-profdata/merge-filtering.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/merge-filtering.test @@ -0,0 +1,122 @@ +# Tests for merge of profile files with filtering capabilities + +# no filtering +RUN: llvm-profdata merge --text %p/Inputs/basic.proftext | FileCheck %s + +CHECK: foo +CHECK-NEXT: # Func Hash: +CHECK-NEXT: 10 +CHECK-NEXT: # Num Counters: +CHECK-NEXT: 2 +CHECK-NEXT: # Counter Values: +CHECK-NEXT: 499500 +CHECK-NEXT: 179900 +CHECK: foo2 +CHECK-NEXT: # Func Hash: +CHECK-NEXT: 10 +CHECK-NEXT: # Num Counters: +CHECK-NEXT: 2 +CHECK-NEXT: # Counter Values: +CHECK-NEXT: 500500 +CHECK-NEXT: 180100 +CHECK: main +CHECK-NEXT: # Func Hash: +CHECK-NEXT: 16650 +CHECK-NEXT: # Num Counters: +CHECK-NEXT: 4 +CHECK-NEXT: # Counter Values: +CHECK-NEXT: 1 +CHECK-NEXT: 1000 +CHECK-NEXT: 1000000 +CHECK-NEXT: 499500 + +# excluding profile for one pattern matching one function +RUN: llvm-profdata merge --exclude-function "foo2" --text %p/Inputs/basic.proftext -o - | FileCheck %s --check-prefix=MERGE-EXCL-SINGLE-FUNC +MERGE-EXCL-SINGLE-FUNC-LABEL: foo +MERGE-EXCL-SINGLE-FUNC-NEXT: # Func Hash: +MERGE-EXCL-SINGLE-FUNC-NEXT: 10 +MERGE-EXCL-SINGLE-FUNC-NEXT: # Num Counters: +MERGE-EXCL-SINGLE-FUNC-NEXT: 2 +MERGE-EXCL-SINGLE-FUNC-NEXT: # Counter Values: +MERGE-EXCL-SINGLE-FUNC-NEXT: 499500 +MERGE-EXCL-SINGLE-FUNC-NEXT: 179900 +MERGE-EXCL-SINGLE-FUNC-NOT: foo2 +MERGE-EXCL-SINGLE-FUNC-LABEL: main +MERGE-EXCL-SINGLE-FUNC-NEXT: # Func Hash: +MERGE-EXCL-SINGLE-FUNC-NEXT: 16650 +MERGE-EXCL-SINGLE-FUNC-NEXT: # Num Counters: +MERGE-EXCL-SINGLE-FUNC-NEXT: 4 +MERGE-EXCL-SINGLE-FUNC-NEXT: # Counter Values: +MERGE-EXCL-SINGLE-FUNC-NEXT: 1 +MERGE-EXCL-SINGLE-FUNC-NEXT: 1000 +MERGE-EXCL-SINGLE-FUNC-NEXT: 1000000 +MERGE-EXCL-SINGLE-FUNC-NEXT: 499500 + +# excluding profile for one pattern matching multiple functions +RUN: llvm-profdata merge --exclude-function "foo" --text %p/Inputs/basic.proftext -o - | FileCheck %s --check-prefix=MERGE-EXCL-SINGLE-PAT +MERGE-EXCL-SINGLE-PAT-NOT: foo +MERGE-EXCL-SINGLE-PAT-NOT: foo2 +MERGE-EXCL-SINGLE-PAT: main +MERGE-EXCL-SINGLE-PAT-NEXT: # Func Hash: +MERGE-EXCL-SINGLE-PAT-NEXT: 16650 +MERGE-EXCL-SINGLE-PAT-NEXT: # Num Counters: +MERGE-EXCL-SINGLE-PAT-NEXT: 4 +MERGE-EXCL-SINGLE-PAT-NEXT: # Counter Values: +MERGE-EXCL-SINGLE-PAT-NEXT: 1 +MERGE-EXCL-SINGLE-PAT-NEXT: 1000 +MERGE-EXCL-SINGLE-PAT-NEXT: 1000000 +MERGE-EXCL-SINGLE-PAT-NEXT: 499500 + +# excluding profile for one regex pattern matching multiple functions +RUN: llvm-profdata merge --exclude-function "foo.*" --text %p/Inputs/basic.proftext -o - | FileCheck %s --check-prefix=MERGE-EXCL-REGEX-SINGLE-PAT +MERGE-EXCL-REGEX-SINGLE-PAT-NOT: foo +MERGE-EXCL-REGEX-SINGLE-PAT-NOT: foo2 +MERGE-EXCL-REGEX-SINGLE-PAT: main +MERGE-EXCL-REGEX-SINGLE-PAT-NEXT: # Func Hash: +MERGE-EXCL-REGEX-SINGLE-PAT-NEXT: 16650 +MERGE-EXCL-REGEX-SINGLE-PAT-NEXT: # Num Counters: +MERGE-EXCL-REGEX-SINGLE-PAT-NEXT: 4 +MERGE-EXCL-REGEX-SINGLE-PAT-NEXT: # Counter Values: +MERGE-EXCL-REGEX-SINGLE-PAT-NEXT: 1 +MERGE-EXCL-REGEX-SINGLE-PAT-NEXT: 1000 +MERGE-EXCL-REGEX-SINGLE-PAT-NEXT: 1000000 +MERGE-EXCL-REGEX-SINGLE-PAT-NEXT: 499500 + +# excluding profile for multiple patterns matching multiple functions +RUN: llvm-profdata merge --exclude-function "foo2;main" --text %p/Inputs/basic.proftext -o - | FileCheck %s --check-prefix=MERGE-EXCL-MULT-PAT +MERGE-EXCL-MULT-PAT: foo +MERGE-EXCL-MULT-PAT-NEXT: # Func Hash: +MERGE-EXCL-MULT-PAT-NEXT: 10 +MERGE-EXCL-MULT-PAT-NEXT: # Num Counters: +MERGE-EXCL-MULT-PAT-NEXT: 2 +MERGE-EXCL-MULT-PAT-NEXT: # Counter Values: +MERGE-EXCL-MULT-PAT-NEXT: 499500 +MERGE-EXCL-MULT-PAT-NEXT: 179900 +MERGE-EXCL-MULT-PAT-NOT: foo2 +MERGE-EXCL-MULT-PAT-NOT: main + +# excluding profile for multiple patterns matching multiple functions +RUN: llvm-profdata merge --exclude-function "foo2;main;" --text %p/Inputs/basic.proftext -o - | FileCheck %s --check-prefix=DANGLING-DELIMITER +DANGLING-DELIMITER: foo +DANGLING-DELIMITER-NEXT: # Func Hash: +DANGLING-DELIMITER-NEXT: 10 +DANGLING-DELIMITER-NEXT: # Num Counters: +DANGLING-DELIMITER-NEXT: 2 +DANGLING-DELIMITER-NEXT: # Counter Values: +DANGLING-DELIMITER-NEXT: 499500 +DANGLING-DELIMITER-NEXT: 179900 +DANGLING-DELIMITER-NOT: foo2 +DANGLING-DELIMITER-NOT: main + +# excluding profile for multiple regex patterns matching multiple functions +RUN: llvm-profdata merge --exclude-function "foo2.*;ma.*" --text %p/Inputs/basic.proftext -o - | FileCheck %s --check-prefix=MERGE-EXCL-REGEX-MULT-PAT +MERGE-EXCL-REGEX-MULT-PAT: foo +MERGE-EXCL-REGEX-MULT-PAT-NEXT: # Func Hash: +MERGE-EXCL-REGEX-MULT-PAT-NEXT: 10 +MERGE-EXCL-REGEX-MULT-PAT-NEXT: # Num Counters: +MERGE-EXCL-REGEX-MULT-PAT-NEXT: 2 +MERGE-EXCL-REGEX-MULT-PAT-NEXT: # Counter Values: +MERGE-EXCL-REGEX-MULT-PAT-NEXT: 499500 +MERGE-EXCL-REGEX-MULT-PAT-NEXT: 179900 +MERGE-EXCL-REGEX-MULT-PAT-NOT: foo2 +MERGE-EXCL-REGEX-MULT-PAT-NOT: main diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -34,6 +34,7 @@ #include "llvm/Support/MD5.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/Threading.h" #include "llvm/Support/VirtualFileSystem.h" @@ -251,7 +252,8 @@ /// Load an input into a writer context. static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, const InstrProfCorrelator *Correlator, - const StringRef ProfiledBinary, WriterContext *WC) { + const StringRef ProfiledBinary, WriterContext *WC, + const StringRef ExcludeFuncList) { std::unique_lock CtxGuard{WC->Lock}; // Copy the filename, because llvm::ThreadPool copied the input "const @@ -327,6 +329,33 @@ I.Name = (*Remapper)(I.Name); const StringRef FuncName = I.Name; bool Reported = false; + + bool ToBeExcluded = false; + StringRef RegexList(ExcludeFuncList); + while (!RegexList.empty()) { + std::pair HeadTail = RegexList.split(';'); + if (!HeadTail.first.empty()) { + Regex Re(HeadTail.first); + std::string Err; + if (!Re.isValid(Err)) { + WC->Errors.emplace_back( + make_error(Twine("Regex ") + HeadTail.first + + " is not valid: " + Err, + std::error_code()), + Filename); + return; + } + if (Re.match(FuncName)) { + ToBeExcluded = true; + break; + } + } + RegexList = HeadTail.second; + } + + if (ToBeExcluded) + continue; + WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { if (Reported) { consumeError(std::move(E)); @@ -398,7 +427,8 @@ StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, unsigned NumThreads, FailureMode FailMode, - const StringRef ProfiledBinary) { + const StringRef ProfiledBinary, + const StringRef ExcludeFuncList) { if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) exitWithError("unknown format is specified"); @@ -429,7 +459,7 @@ if (NumThreads == 1) { for (const auto &Input : Inputs) loadInput(Input, Remapper, Correlator.get(), ProfiledBinary, - Contexts[0].get()); + Contexts[0].get(), ExcludeFuncList); } else { ThreadPool Pool(hardware_concurrency(NumThreads)); @@ -437,7 +467,7 @@ unsigned Ctx = 0; for (const auto &Input : Inputs) { Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary, - Contexts[Ctx].get()); + Contexts[Ctx].get(), ExcludeFuncList); Ctx = (Ctx + 1) % NumThreads; } Pool.wait(); @@ -856,7 +886,8 @@ SmallSet WriterErrorCodes; auto WC = std::make_unique(OutputSparse, ErrorLock, WriterErrorCodes); - loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get()); + loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get(), + /*ExcludeFuncList=*/""); if (WC->Errors.size() > 0) exitWithError(std::move(WC->Errors[0].first), InstrFilename); @@ -1254,6 +1285,9 @@ "drop-profile-symbol-list", cl::init(false), cl::Hidden, cl::desc("Drop the profile symbol list when merging AutoFDO profiles " "(only meaningful for -sample)")); + cl::opt ExcludeFuncList( + "exclude-function", cl::desc("Exclude functions whose names match any " + "regex in the semicolon-separated list")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); @@ -1287,16 +1321,25 @@ exitWithError( "-supplement-instr-with-sample can only work with -instr. "); + if (!ExcludeFuncList.empty()) + exitWithError("-exclude-function is not supported with " + "-supplement-instr-with-sample. "); + supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename, OutputFormat, OutputSparse, SupplMinSizeThreshold, ZeroCounterThreshold, InstrProfColdThreshold); return 0; } + if (!ExcludeFuncList.empty()) + if (ProfileKind != instr) + exitWithError( + "-exclude-function is only supported for -instr profiles. "); + if (ProfileKind == instr) mergeInstrProfile(WeightedInputs, DebugInfoFilename, Remapper.get(), OutputFilename, OutputFormat, OutputSparse, NumThreads, - FailureMode, ProfiledBinary); + FailureMode, ProfiledBinary, ExcludeFuncList); else mergeSampleProfile( WeightedInputs, Remapper.get(), OutputFilename, OutputFormat, @@ -1328,7 +1371,8 @@ OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; exit(0); } - loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context); + loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context, + /*ExcludeFuncList=*/""); overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, IsCS); Overlap.dump(OS);