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