|
34 | 34 | #include "llvm/Support/MD5.h"
|
35 | 35 | #include "llvm/Support/MemoryBuffer.h"
|
36 | 36 | #include "llvm/Support/Path.h"
|
| 37 | +#include "llvm/Support/Regex.h" |
37 | 38 | #include "llvm/Support/ThreadPool.h"
|
38 | 39 | #include "llvm/Support/Threading.h"
|
39 | 40 | #include "llvm/Support/VirtualFileSystem.h"
|
@@ -131,9 +132,11 @@ cl::opt<std::string>
|
131 | 132 | cl::sub(MergeSubcommand));
|
132 | 133 | cl::opt<std::string> FuncNameFilter(
|
133 | 134 | "function",
|
134 |
| - cl::desc("Details for matching functions. For overlapping CSSPGO, this " |
135 |
| - "takes a function name with calling context."), |
136 |
| - cl::sub(ShowSubcommand), cl::sub(OverlapSubcommand)); |
| 135 | + cl::desc("Only functions matching the filter are shown in the output. For " |
| 136 | + "overlapping CSSPGO, this takes a function name with calling " |
| 137 | + "context."), |
| 138 | + cl::sub(ShowSubcommand), cl::sub(OverlapSubcommand), |
| 139 | + cl::sub(MergeSubcommand)); |
137 | 140 |
|
138 | 141 | // TODO: Consider creating a template class (e.g., MergeOption, ShowOption) to
|
139 | 142 | // factor out the common cl::sub in cl::opt constructor for subcommand-specific
|
@@ -243,6 +246,10 @@ cl::opt<uint64_t> TemporalProfMaxTraceLength(
|
243 | 246 | cl::sub(MergeSubcommand),
|
244 | 247 | cl::desc("The maximum length of a single temporal profile trace "
|
245 | 248 | "(default: 10000)"));
|
| 249 | +cl::opt<std::string> FuncNameNegativeFilter( |
| 250 | + "no-function", cl::init(""), |
| 251 | + cl::sub(MergeSubcommand), |
| 252 | + cl::desc("Exclude functions matching the filter from the output.")); |
246 | 253 |
|
247 | 254 | cl::opt<FailureMode>
|
248 | 255 | FailMode("failure-mode", cl::init(failIfAnyAreInvalid),
|
@@ -759,6 +766,62 @@ static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
|
759 | 766 | });
|
760 | 767 | }
|
761 | 768 |
|
| 769 | +static StringRef |
| 770 | +getFuncName(const StringMap<InstrProfWriter::ProfilingData>::value_type &Val) { |
| 771 | + return Val.first(); |
| 772 | +} |
| 773 | + |
| 774 | +static std::string |
| 775 | +getFuncName(const SampleProfileMap::value_type &Val) { |
| 776 | + return Val.second.getContext().toString(); |
| 777 | +} |
| 778 | + |
| 779 | +template <typename T> |
| 780 | +static void filterFunctions(T &ProfileMap) { |
| 781 | + bool hasFilter = !FuncNameFilter.empty(); |
| 782 | + bool hasNegativeFilter = !FuncNameNegativeFilter.empty(); |
| 783 | + if (!hasFilter && !hasNegativeFilter) |
| 784 | + return; |
| 785 | + |
| 786 | + // If filter starts with '?' it is MSVC mangled name, not a regex. |
| 787 | + llvm::Regex ProbablyMSVCMangledName("[?@$_0-9A-Za-z]+"); |
| 788 | + if (hasFilter && FuncNameFilter[0] == '?' && |
| 789 | + ProbablyMSVCMangledName.match(FuncNameFilter)) |
| 790 | + FuncNameFilter = llvm::Regex::escape(FuncNameFilter); |
| 791 | + if (hasNegativeFilter && FuncNameNegativeFilter[0] == '?' && |
| 792 | + ProbablyMSVCMangledName.match(FuncNameNegativeFilter)) |
| 793 | + FuncNameNegativeFilter = llvm::Regex::escape(FuncNameNegativeFilter); |
| 794 | + |
| 795 | + size_t Count = ProfileMap.size(); |
| 796 | + llvm::Regex Pattern(FuncNameFilter); |
| 797 | + llvm::Regex NegativePattern(FuncNameNegativeFilter); |
| 798 | + std::string Error; |
| 799 | + if (hasFilter && !Pattern.isValid(Error)) |
| 800 | + exitWithError(Error); |
| 801 | + if (hasNegativeFilter && !NegativePattern.isValid(Error)) |
| 802 | + exitWithError(Error); |
| 803 | + |
| 804 | + // Handle MD5 profile, so it is still able to match using the original name. |
| 805 | + std::string MD5Name = std::to_string(llvm::MD5Hash(FuncNameFilter)); |
| 806 | + std::string NegativeMD5Name = |
| 807 | + std::to_string(llvm::MD5Hash(FuncNameNegativeFilter)); |
| 808 | + |
| 809 | + for (auto I = ProfileMap.begin(); I != ProfileMap.end();) { |
| 810 | + auto Tmp = I++; |
| 811 | + const auto &FuncName = getFuncName(*Tmp); |
| 812 | + // Negative filter has higher precedence than positive filter. |
| 813 | + if ((hasNegativeFilter && |
| 814 | + (NegativePattern.match(FuncName) || |
| 815 | + (FunctionSamples::UseMD5 && NegativeMD5Name == FuncName))) || |
| 816 | + (hasFilter && !(Pattern.match(FuncName) || |
| 817 | + (FunctionSamples::UseMD5 && MD5Name == FuncName)))) |
| 818 | + ProfileMap.erase(Tmp); |
| 819 | + } |
| 820 | + |
| 821 | + llvm::dbgs() << Count - ProfileMap.size() << " of " << Count << " functions " |
| 822 | + << "in the original profile are filtered.\n"; |
| 823 | +} |
| 824 | + |
762 | 825 | static void writeInstrProfile(StringRef OutputFilename,
|
763 | 826 | ProfileFormat OutputFormat,
|
764 | 827 | InstrProfWriter &Writer) {
|
@@ -878,6 +941,8 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
|
878 | 941 | (NumErrors > 0 && FailMode == failIfAnyAreInvalid))
|
879 | 942 | exitWithError("no profile can be merged");
|
880 | 943 |
|
| 944 | + filterFunctions(Contexts[0]->Writer.getProfileData()); |
| 945 | + |
881 | 946 | writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
|
882 | 947 | }
|
883 | 948 |
|
@@ -1459,6 +1524,8 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs,
|
1459 | 1524 | ProfileIsCS = FunctionSamples::ProfileIsCS = false;
|
1460 | 1525 | }
|
1461 | 1526 |
|
| 1527 | + filterFunctions(ProfileMap); |
| 1528 | + |
1462 | 1529 | auto WriterOrErr =
|
1463 | 1530 | SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
|
1464 | 1531 | if (std::error_code EC = WriterOrErr.getError())
|
|
0 commit comments