@@ -234,6 +234,24 @@ static cl::opt<unsigned> ProfileICPRelativeHotnessSkip(
234
234
cl::desc(
235
235
" Skip relative hotness check for ICP up to given number of targets." ));
236
236
237
+ static cl::opt<unsigned > ChecksumMismatchFuncHotBlockSkip (
238
+ " checksum-mismatch-func-hot-block-skip" , cl::Hidden, cl::init(100 ),
239
+ cl::desc(" For checksum-mismatch error check, skip checking the function "
240
+ " whose num of hot(on average) blocks is smaller than the "
241
+ " given number." ));
242
+
243
+ static cl::opt<unsigned > ChecksumMismatchNumFuncSkip (
244
+ " checksum-mismatch-num-func-skip" , cl::Hidden, cl::init(50 ),
245
+ cl::desc(" For checksum-mismatch error check, skip the check if the total "
246
+ " number of selected function is smaller than the given number." ));
247
+
248
+ static cl::opt<unsigned > ChecksumMismatchErrorThreshold (
249
+ " checksum-mismatch-error-threshold" , cl::Hidden, cl::init(80 ),
250
+ cl::desc(
251
+ " For checksum-mismatch error check, error out if the percentage of "
252
+ " function mismatched-checksum is higher than the given percentage "
253
+ " threshold" ));
254
+
237
255
static cl::opt<bool > CallsitePrioritizedInline (
238
256
" sample-profile-prioritized-inline" , cl::Hidden,
239
257
@@ -630,6 +648,8 @@ class SampleProfileLoader final : public SampleProfileLoaderBaseImpl<Function> {
630
648
std::vector<Function *> buildFunctionOrder (Module &M, LazyCallGraph &CG);
631
649
std::unique_ptr<ProfiledCallGraph> buildProfiledCallGraph (Module &M);
632
650
void generateMDProfMetadata (Function &F);
651
+ bool errorIfHighChecksumMismatch (Module &M, ProfileSummaryInfo *PSI,
652
+ const SampleProfileMap &Profiles);
633
653
634
654
// / Map from function name to Function *. Used to find the function from
635
655
// / the function name. If the function name contains suffix, additional
@@ -2187,6 +2207,61 @@ bool SampleProfileLoader::doInitialization(Module &M,
2187
2207
return true ;
2188
2208
}
2189
2209
2210
+ // Note that this is a module-level check. Even if one module is errored out,
2211
+ // the entire build will be errored out. However, the user could make big
2212
+ // changes to functions in single module but those changes might not be
2213
+ // performance significant to the whole binary. Therefore, we use a conservative
2214
+ // approach to make sure we only error out if it globally impacts the binary
2215
+ // performance. To achieve this, we use heuristics to select a reasonable
2216
+ // big set of functions that are supposed to be globally performance
2217
+ // significant, only compute and check the mismatch within those functions. The
2218
+ // function selection is based on two criteria: 1) The function is "hot" enough,
2219
+ // which is tuned by a hotness-based flag(ChecksumMismatchFuncHotBlockSkip). 2)
2220
+ // The num of function is large enough which is tuned by the
2221
+ // ChecksumMismatchNumFuncSkip flag.
2222
+ bool SampleProfileLoader::errorIfHighChecksumMismatch (
2223
+ Module &M, ProfileSummaryInfo *PSI, const SampleProfileMap &Profiles) {
2224
+ assert (FunctionSamples::ProfileIsProbeBased &&
2225
+ " Only support for probe-based profile" );
2226
+ uint64_t TotalSelectedFunc = 0 ;
2227
+ uint64_t NumMismatchedFunc = 0 ;
2228
+ for (const auto &I : Profiles) {
2229
+ const auto &FS = I.second ;
2230
+ const auto *FuncDesc = ProbeManager->getDesc (FS.getGUID ());
2231
+ if (!FuncDesc)
2232
+ continue ;
2233
+
2234
+ // We want to select a set of functions that are globally performance
2235
+ // significant, in other words, if those functions profiles are
2236
+ // checksum-mismatched and dropped, the whole binary will likely be
2237
+ // impacted, so here we use a hotness-based threshold to control the
2238
+ // selection.
2239
+ if (FS.getTotalSamples () <
2240
+ ChecksumMismatchFuncHotBlockSkip * PSI->getOrCompHotCountThreshold ())
2241
+ continue ;
2242
+
2243
+ TotalSelectedFunc++;
2244
+ if (ProbeManager->profileIsHashMismatched (*FuncDesc, FS))
2245
+ NumMismatchedFunc++;
2246
+ }
2247
+ // Make sure that the num of selected function is not too small to distinguish
2248
+ // from the user's benign changes.
2249
+ if (TotalSelectedFunc < ChecksumMismatchNumFuncSkip)
2250
+ return false ;
2251
+
2252
+ // Finally check the mismatch percentage against the threshold.
2253
+ if (NumMismatchedFunc * 100 >=
2254
+ TotalSelectedFunc * ChecksumMismatchErrorThreshold) {
2255
+ auto &Ctx = M.getContext ();
2256
+ const char *Msg =
2257
+ " The FDO profile is too old and will cause big performance regression, "
2258
+ " please drop the profile and collect a new one." ;
2259
+ Ctx.diagnose (DiagnosticInfoSampleProfile (M.getModuleIdentifier (), Msg));
2260
+ return true ;
2261
+ }
2262
+ return false ;
2263
+ }
2264
+
2190
2265
void SampleProfileMatcher::findIRAnchors (
2191
2266
const Function &F, std::map<LineLocation, StringRef> &IRAnchors) {
2192
2267
// For inlined code, recover the original callsite and callee by finding the
@@ -2718,6 +2793,12 @@ bool SampleProfileLoader::runOnModule(Module &M, ModuleAnalysisManager *AM,
2718
2793
ProfileSummary::PSK_Sample);
2719
2794
PSI->refresh ();
2720
2795
}
2796
+
2797
+ // Error out if the profile checksum mismatch is too high.
2798
+ if (FunctionSamples::ProfileIsProbeBased &&
2799
+ errorIfHighChecksumMismatch (M, PSI, Reader->getProfiles ()))
2800
+ return false ;
2801
+
2721
2802
// Compute the total number of samples collected in this profile.
2722
2803
for (const auto &I : Reader->getProfiles ())
2723
2804
TotalCollectedSamples += I.second .getTotalSamples ();
0 commit comments