File: | build/llvm-toolchain-snapshot-15~++20220214111405+a87d3ba61c64/llvm/tools/llvm-profdata/llvm-profdata.cpp |
Warning: | line 95, column 26 Moved-from object 'E' is moved |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// | |||
2 | // | |||
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||
4 | // See https://llvm.org/LICENSE.txt for license information. | |||
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||
6 | // | |||
7 | //===----------------------------------------------------------------------===// | |||
8 | // | |||
9 | // llvm-profdata merges .profdata files. | |||
10 | // | |||
11 | //===----------------------------------------------------------------------===// | |||
12 | ||||
13 | #include "llvm/ADT/SmallSet.h" | |||
14 | #include "llvm/ADT/SmallVector.h" | |||
15 | #include "llvm/ADT/StringRef.h" | |||
16 | #include "llvm/DebugInfo/DWARF/DWARFContext.h" | |||
17 | #include "llvm/IR/LLVMContext.h" | |||
18 | #include "llvm/Object/Binary.h" | |||
19 | #include "llvm/ProfileData/InstrProfCorrelator.h" | |||
20 | #include "llvm/ProfileData/InstrProfReader.h" | |||
21 | #include "llvm/ProfileData/InstrProfWriter.h" | |||
22 | #include "llvm/ProfileData/MemProf.h" | |||
23 | #include "llvm/ProfileData/ProfileCommon.h" | |||
24 | #include "llvm/ProfileData/RawMemProfReader.h" | |||
25 | #include "llvm/ProfileData/SampleProfReader.h" | |||
26 | #include "llvm/ProfileData/SampleProfWriter.h" | |||
27 | #include "llvm/Support/CommandLine.h" | |||
28 | #include "llvm/Support/Discriminator.h" | |||
29 | #include "llvm/Support/Errc.h" | |||
30 | #include "llvm/Support/FileSystem.h" | |||
31 | #include "llvm/Support/Format.h" | |||
32 | #include "llvm/Support/FormattedStream.h" | |||
33 | #include "llvm/Support/InitLLVM.h" | |||
34 | #include "llvm/Support/MemoryBuffer.h" | |||
35 | #include "llvm/Support/Path.h" | |||
36 | #include "llvm/Support/ThreadPool.h" | |||
37 | #include "llvm/Support/Threading.h" | |||
38 | #include "llvm/Support/WithColor.h" | |||
39 | #include "llvm/Support/raw_ostream.h" | |||
40 | #include <algorithm> | |||
41 | ||||
42 | using namespace llvm; | |||
43 | ||||
44 | enum ProfileFormat { | |||
45 | PF_None = 0, | |||
46 | PF_Text, | |||
47 | PF_Compact_Binary, | |||
48 | PF_Ext_Binary, | |||
49 | PF_GCC, | |||
50 | PF_Binary | |||
51 | }; | |||
52 | ||||
53 | static void warn(Twine Message, std::string Whence = "", | |||
54 | std::string Hint = "") { | |||
55 | WithColor::warning(); | |||
56 | if (!Whence.empty()) | |||
57 | errs() << Whence << ": "; | |||
58 | errs() << Message << "\n"; | |||
59 | if (!Hint.empty()) | |||
60 | WithColor::note() << Hint << "\n"; | |||
61 | } | |||
62 | ||||
63 | static void warn(Error E, StringRef Whence = "") { | |||
64 | if (E.isA<InstrProfError>()) { | |||
65 | handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { | |||
66 | warn(IPE.message(), std::string(Whence), std::string("")); | |||
67 | }); | |||
68 | } | |||
69 | } | |||
70 | ||||
71 | static void exitWithError(Twine Message, std::string Whence = "", | |||
72 | std::string Hint = "") { | |||
73 | WithColor::error(); | |||
74 | if (!Whence.empty()) | |||
75 | errs() << Whence << ": "; | |||
76 | errs() << Message << "\n"; | |||
77 | if (!Hint.empty()) | |||
78 | WithColor::note() << Hint << "\n"; | |||
79 | ::exit(1); | |||
80 | } | |||
81 | ||||
82 | static void exitWithError(Error E, StringRef Whence = "") { | |||
83 | if (E.isA<InstrProfError>()) { | |||
84 | handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { | |||
85 | instrprof_error instrError = IPE.get(); | |||
86 | StringRef Hint = ""; | |||
87 | if (instrError == instrprof_error::unrecognized_format) { | |||
88 | // Hint in case user missed specifying the profile type. | |||
89 | Hint = "Perhaps you forgot to use the --sample or --memory option?"; | |||
90 | } | |||
91 | exitWithError(IPE.message(), std::string(Whence), std::string(Hint)); | |||
92 | }); | |||
93 | } | |||
94 | ||||
95 | exitWithError(toString(std::move(E)), std::string(Whence)); | |||
| ||||
96 | } | |||
97 | ||||
98 | static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { | |||
99 | exitWithError(EC.message(), std::string(Whence)); | |||
100 | } | |||
101 | ||||
102 | namespace { | |||
103 | enum ProfileKinds { instr, sample, memory }; | |||
104 | enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid }; | |||
105 | } | |||
106 | ||||
107 | static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, | |||
108 | StringRef Whence = "") { | |||
109 | if (FailMode == failIfAnyAreInvalid) | |||
110 | exitWithErrorCode(EC, Whence); | |||
111 | else | |||
112 | warn(EC.message(), std::string(Whence)); | |||
113 | } | |||
114 | ||||
115 | static void handleMergeWriterError(Error E, StringRef WhenceFile = "", | |||
116 | StringRef WhenceFunction = "", | |||
117 | bool ShowHint = true) { | |||
118 | if (!WhenceFile.empty()) | |||
119 | errs() << WhenceFile << ": "; | |||
120 | if (!WhenceFunction.empty()) | |||
121 | errs() << WhenceFunction << ": "; | |||
122 | ||||
123 | auto IPE = instrprof_error::success; | |||
124 | E = handleErrors(std::move(E), | |||
125 | [&IPE](std::unique_ptr<InstrProfError> E) -> Error { | |||
126 | IPE = E->get(); | |||
127 | return Error(std::move(E)); | |||
128 | }); | |||
129 | errs() << toString(std::move(E)) << "\n"; | |||
130 | ||||
131 | if (ShowHint) { | |||
132 | StringRef Hint = ""; | |||
133 | if (IPE != instrprof_error::success) { | |||
134 | switch (IPE) { | |||
135 | case instrprof_error::hash_mismatch: | |||
136 | case instrprof_error::count_mismatch: | |||
137 | case instrprof_error::value_site_count_mismatch: | |||
138 | Hint = "Make sure that all profile data to be merged is generated " | |||
139 | "from the same binary."; | |||
140 | break; | |||
141 | default: | |||
142 | break; | |||
143 | } | |||
144 | } | |||
145 | ||||
146 | if (!Hint.empty()) | |||
147 | errs() << Hint << "\n"; | |||
148 | } | |||
149 | } | |||
150 | ||||
151 | namespace { | |||
152 | /// A remapper from original symbol names to new symbol names based on a file | |||
153 | /// containing a list of mappings from old name to new name. | |||
154 | class SymbolRemapper { | |||
155 | std::unique_ptr<MemoryBuffer> File; | |||
156 | DenseMap<StringRef, StringRef> RemappingTable; | |||
157 | ||||
158 | public: | |||
159 | /// Build a SymbolRemapper from a file containing a list of old/new symbols. | |||
160 | static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) { | |||
161 | auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); | |||
162 | if (!BufOrError) | |||
163 | exitWithErrorCode(BufOrError.getError(), InputFile); | |||
164 | ||||
165 | auto Remapper = std::make_unique<SymbolRemapper>(); | |||
166 | Remapper->File = std::move(BufOrError.get()); | |||
167 | ||||
168 | for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); | |||
169 | !LineIt.is_at_eof(); ++LineIt) { | |||
170 | std::pair<StringRef, StringRef> Parts = LineIt->split(' '); | |||
171 | if (Parts.first.empty() || Parts.second.empty() || | |||
172 | Parts.second.count(' ')) { | |||
173 | exitWithError("unexpected line in remapping file", | |||
174 | (InputFile + ":" + Twine(LineIt.line_number())).str(), | |||
175 | "expected 'old_symbol new_symbol'"); | |||
176 | } | |||
177 | Remapper->RemappingTable.insert(Parts); | |||
178 | } | |||
179 | return Remapper; | |||
180 | } | |||
181 | ||||
182 | /// Attempt to map the given old symbol into a new symbol. | |||
183 | /// | |||
184 | /// \return The new symbol, or \p Name if no such symbol was found. | |||
185 | StringRef operator()(StringRef Name) { | |||
186 | StringRef New = RemappingTable.lookup(Name); | |||
187 | return New.empty() ? Name : New; | |||
188 | } | |||
189 | }; | |||
190 | } | |||
191 | ||||
192 | struct WeightedFile { | |||
193 | std::string Filename; | |||
194 | uint64_t Weight; | |||
195 | }; | |||
196 | typedef SmallVector<WeightedFile, 5> WeightedFileVector; | |||
197 | ||||
198 | /// Keep track of merged data and reported errors. | |||
199 | struct WriterContext { | |||
200 | std::mutex Lock; | |||
201 | InstrProfWriter Writer; | |||
202 | std::vector<std::pair<Error, std::string>> Errors; | |||
203 | std::mutex &ErrLock; | |||
204 | SmallSet<instrprof_error, 4> &WriterErrorCodes; | |||
205 | ||||
206 | WriterContext(bool IsSparse, std::mutex &ErrLock, | |||
207 | SmallSet<instrprof_error, 4> &WriterErrorCodes) | |||
208 | : Writer(IsSparse), ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) { | |||
209 | } | |||
210 | }; | |||
211 | ||||
212 | /// Computer the overlap b/w profile BaseFilename and TestFileName, | |||
213 | /// and store the program level result to Overlap. | |||
214 | static void overlapInput(const std::string &BaseFilename, | |||
215 | const std::string &TestFilename, WriterContext *WC, | |||
216 | OverlapStats &Overlap, | |||
217 | const OverlapFuncFilters &FuncFilter, | |||
218 | raw_fd_ostream &OS, bool IsCS) { | |||
219 | auto ReaderOrErr = InstrProfReader::create(TestFilename); | |||
220 | if (Error E = ReaderOrErr.takeError()) { | |||
221 | // Skip the empty profiles by returning sliently. | |||
222 | instrprof_error IPE = InstrProfError::take(std::move(E)); | |||
223 | if (IPE != instrprof_error::empty_raw_profile) | |||
224 | WC->Errors.emplace_back(make_error<InstrProfError>(IPE), TestFilename); | |||
225 | return; | |||
226 | } | |||
227 | ||||
228 | auto Reader = std::move(ReaderOrErr.get()); | |||
229 | for (auto &I : *Reader) { | |||
230 | OverlapStats FuncOverlap(OverlapStats::FunctionLevel); | |||
231 | FuncOverlap.setFuncInfo(I.Name, I.Hash); | |||
232 | ||||
233 | WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); | |||
234 | FuncOverlap.dump(OS); | |||
235 | } | |||
236 | } | |||
237 | ||||
238 | /// Load an input into a writer context. | |||
239 | static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, | |||
240 | const InstrProfCorrelator *Correlator, | |||
241 | WriterContext *WC) { | |||
242 | std::unique_lock<std::mutex> CtxGuard{WC->Lock}; | |||
243 | ||||
244 | // Copy the filename, because llvm::ThreadPool copied the input "const | |||
245 | // WeightedFile &" by value, making a reference to the filename within it | |||
246 | // invalid outside of this packaged task. | |||
247 | std::string Filename = Input.Filename; | |||
248 | ||||
249 | auto ReaderOrErr = InstrProfReader::create(Input.Filename, Correlator); | |||
250 | if (Error E = ReaderOrErr.takeError()) { | |||
251 | // Skip the empty profiles by returning sliently. | |||
252 | instrprof_error IPE = InstrProfError::take(std::move(E)); | |||
253 | if (IPE != instrprof_error::empty_raw_profile) | |||
254 | WC->Errors.emplace_back(make_error<InstrProfError>(IPE), Filename); | |||
255 | return; | |||
256 | } | |||
257 | ||||
258 | auto Reader = std::move(ReaderOrErr.get()); | |||
259 | if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) { | |||
260 | consumeError(std::move(E)); | |||
261 | WC->Errors.emplace_back( | |||
262 | make_error<StringError>( | |||
263 | "Merge IR generated profile with Clang generated profile.", | |||
264 | std::error_code()), | |||
265 | Filename); | |||
266 | return; | |||
267 | } | |||
268 | ||||
269 | for (auto &I : *Reader) { | |||
270 | if (Remapper) | |||
271 | I.Name = (*Remapper)(I.Name); | |||
272 | const StringRef FuncName = I.Name; | |||
273 | bool Reported = false; | |||
274 | WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { | |||
275 | if (Reported) { | |||
276 | consumeError(std::move(E)); | |||
277 | return; | |||
278 | } | |||
279 | Reported = true; | |||
280 | // Only show hint the first time an error occurs. | |||
281 | instrprof_error IPE = InstrProfError::take(std::move(E)); | |||
282 | std::unique_lock<std::mutex> ErrGuard{WC->ErrLock}; | |||
283 | bool firstTime = WC->WriterErrorCodes.insert(IPE).second; | |||
284 | handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename, | |||
285 | FuncName, firstTime); | |||
286 | }); | |||
287 | } | |||
288 | if (Reader->hasError()) | |||
289 | if (Error E = Reader->getError()) | |||
290 | WC->Errors.emplace_back(std::move(E), Filename); | |||
291 | } | |||
292 | ||||
293 | /// Merge the \p Src writer context into \p Dst. | |||
294 | static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { | |||
295 | for (auto &ErrorPair : Src->Errors) | |||
296 | Dst->Errors.push_back(std::move(ErrorPair)); | |||
297 | Src->Errors.clear(); | |||
298 | ||||
299 | Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { | |||
300 | instrprof_error IPE = InstrProfError::take(std::move(E)); | |||
301 | std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock}; | |||
302 | bool firstTime = Dst->WriterErrorCodes.insert(IPE).second; | |||
303 | if (firstTime) | |||
304 | warn(toString(make_error<InstrProfError>(IPE))); | |||
305 | }); | |||
306 | } | |||
307 | ||||
308 | static void writeInstrProfile(StringRef OutputFilename, | |||
309 | ProfileFormat OutputFormat, | |||
310 | InstrProfWriter &Writer) { | |||
311 | std::error_code EC; | |||
312 | raw_fd_ostream Output(OutputFilename.data(), EC, | |||
313 | OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF | |||
314 | : sys::fs::OF_None); | |||
315 | if (EC) | |||
316 | exitWithErrorCode(EC, OutputFilename); | |||
317 | ||||
318 | if (OutputFormat == PF_Text) { | |||
319 | if (Error E = Writer.writeText(Output)) | |||
320 | warn(std::move(E)); | |||
321 | } else { | |||
322 | if (Output.is_displayed()) | |||
323 | exitWithError("cannot write a non-text format profile to the terminal"); | |||
324 | if (Error E = Writer.write(Output)) | |||
325 | warn(std::move(E)); | |||
326 | } | |||
327 | } | |||
328 | ||||
329 | static void mergeInstrProfile(const WeightedFileVector &Inputs, | |||
330 | StringRef DebugInfoFilename, | |||
331 | SymbolRemapper *Remapper, | |||
332 | StringRef OutputFilename, | |||
333 | ProfileFormat OutputFormat, bool OutputSparse, | |||
334 | unsigned NumThreads, FailureMode FailMode) { | |||
335 | if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && | |||
336 | OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) | |||
337 | exitWithError("unknown format is specified"); | |||
338 | ||||
339 | std::unique_ptr<InstrProfCorrelator> Correlator; | |||
340 | if (!DebugInfoFilename.empty()) { | |||
341 | if (auto Err = | |||
342 | InstrProfCorrelator::get(DebugInfoFilename).moveInto(Correlator)) | |||
343 | exitWithError(std::move(Err), DebugInfoFilename); | |||
344 | if (auto Err = Correlator->correlateProfileData()) | |||
345 | exitWithError(std::move(Err), DebugInfoFilename); | |||
346 | } | |||
347 | ||||
348 | std::mutex ErrorLock; | |||
349 | SmallSet<instrprof_error, 4> WriterErrorCodes; | |||
350 | ||||
351 | // If NumThreads is not specified, auto-detect a good default. | |||
352 | if (NumThreads == 0) | |||
353 | NumThreads = std::min(hardware_concurrency().compute_thread_count(), | |||
354 | unsigned((Inputs.size() + 1) / 2)); | |||
355 | // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails | |||
356 | // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't | |||
357 | // merged, thus the emitted file ends up with a PF_Unknown kind. | |||
358 | ||||
359 | // Initialize the writer contexts. | |||
360 | SmallVector<std::unique_ptr<WriterContext>, 4> Contexts; | |||
361 | for (unsigned I = 0; I < NumThreads; ++I) | |||
362 | Contexts.emplace_back(std::make_unique<WriterContext>( | |||
363 | OutputSparse, ErrorLock, WriterErrorCodes)); | |||
364 | ||||
365 | if (NumThreads == 1) { | |||
366 | for (const auto &Input : Inputs) | |||
367 | loadInput(Input, Remapper, Correlator.get(), Contexts[0].get()); | |||
368 | } else { | |||
369 | ThreadPool Pool(hardware_concurrency(NumThreads)); | |||
370 | ||||
371 | // Load the inputs in parallel (N/NumThreads serial steps). | |||
372 | unsigned Ctx = 0; | |||
373 | for (const auto &Input : Inputs) { | |||
374 | Pool.async(loadInput, Input, Remapper, Correlator.get(), | |||
375 | Contexts[Ctx].get()); | |||
376 | Ctx = (Ctx + 1) % NumThreads; | |||
377 | } | |||
378 | Pool.wait(); | |||
379 | ||||
380 | // Merge the writer contexts together (~ lg(NumThreads) serial steps). | |||
381 | unsigned Mid = Contexts.size() / 2; | |||
382 | unsigned End = Contexts.size(); | |||
383 | assert(Mid > 0 && "Expected more than one context")(static_cast <bool> (Mid > 0 && "Expected more than one context" ) ? void (0) : __assert_fail ("Mid > 0 && \"Expected more than one context\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 383, __extension__ __PRETTY_FUNCTION__)); | |||
384 | do { | |||
385 | for (unsigned I = 0; I < Mid; ++I) | |||
386 | Pool.async(mergeWriterContexts, Contexts[I].get(), | |||
387 | Contexts[I + Mid].get()); | |||
388 | Pool.wait(); | |||
389 | if (End & 1) { | |||
390 | Pool.async(mergeWriterContexts, Contexts[0].get(), | |||
391 | Contexts[End - 1].get()); | |||
392 | Pool.wait(); | |||
393 | } | |||
394 | End = Mid; | |||
395 | Mid /= 2; | |||
396 | } while (Mid > 0); | |||
397 | } | |||
398 | ||||
399 | // Handle deferred errors encountered during merging. If the number of errors | |||
400 | // is equal to the number of inputs the merge failed. | |||
401 | unsigned NumErrors = 0; | |||
402 | for (std::unique_ptr<WriterContext> &WC : Contexts) { | |||
403 | for (auto &ErrorPair : WC->Errors) { | |||
404 | ++NumErrors; | |||
405 | warn(toString(std::move(ErrorPair.first)), ErrorPair.second); | |||
406 | } | |||
407 | } | |||
408 | if (NumErrors == Inputs.size() || | |||
409 | (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) | |||
410 | exitWithError("no profile can be merged"); | |||
411 | ||||
412 | writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer); | |||
413 | } | |||
414 | ||||
415 | /// The profile entry for a function in instrumentation profile. | |||
416 | struct InstrProfileEntry { | |||
417 | uint64_t MaxCount = 0; | |||
418 | float ZeroCounterRatio = 0.0; | |||
419 | InstrProfRecord *ProfRecord; | |||
420 | InstrProfileEntry(InstrProfRecord *Record); | |||
421 | InstrProfileEntry() = default; | |||
422 | }; | |||
423 | ||||
424 | InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) { | |||
425 | ProfRecord = Record; | |||
426 | uint64_t CntNum = Record->Counts.size(); | |||
427 | uint64_t ZeroCntNum = 0; | |||
428 | for (size_t I = 0; I < CntNum; ++I) { | |||
429 | MaxCount = std::max(MaxCount, Record->Counts[I]); | |||
430 | ZeroCntNum += !Record->Counts[I]; | |||
431 | } | |||
432 | ZeroCounterRatio = (float)ZeroCntNum / CntNum; | |||
433 | } | |||
434 | ||||
435 | /// Either set all the counters in the instr profile entry \p IFE to -1 | |||
436 | /// in order to drop the profile or scale up the counters in \p IFP to | |||
437 | /// be above hot threshold. We use the ratio of zero counters in the | |||
438 | /// profile of a function to decide the profile is helpful or harmful | |||
439 | /// for performance, and to choose whether to scale up or drop it. | |||
440 | static void updateInstrProfileEntry(InstrProfileEntry &IFE, | |||
441 | uint64_t HotInstrThreshold, | |||
442 | float ZeroCounterThreshold) { | |||
443 | InstrProfRecord *ProfRecord = IFE.ProfRecord; | |||
444 | if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) { | |||
445 | // If all or most of the counters of the function are zero, the | |||
446 | // profile is unaccountable and shuld be dropped. Reset all the | |||
447 | // counters to be -1 and PGO profile-use will drop the profile. | |||
448 | // All counters being -1 also implies that the function is hot so | |||
449 | // PGO profile-use will also set the entry count metadata to be | |||
450 | // above hot threshold. | |||
451 | for (size_t I = 0; I < ProfRecord->Counts.size(); ++I) | |||
452 | ProfRecord->Counts[I] = -1; | |||
453 | return; | |||
454 | } | |||
455 | ||||
456 | // Scale up the MaxCount to be multiple times above hot threshold. | |||
457 | const unsigned MultiplyFactor = 3; | |||
458 | uint64_t Numerator = HotInstrThreshold * MultiplyFactor; | |||
459 | uint64_t Denominator = IFE.MaxCount; | |||
460 | ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) { | |||
461 | warn(toString(make_error<InstrProfError>(E))); | |||
462 | }); | |||
463 | } | |||
464 | ||||
465 | const uint64_t ColdPercentileIdx = 15; | |||
466 | const uint64_t HotPercentileIdx = 11; | |||
467 | ||||
468 | using sampleprof::FSDiscriminatorPass; | |||
469 | ||||
470 | // Internal options to set FSDiscriminatorPass. Used in merge and show | |||
471 | // commands. | |||
472 | static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption( | |||
473 | "fs-discriminator-pass", cl::init(PassLast), cl::Hidden, | |||
474 | cl::desc("Zero out the discriminator bits for the FS discrimiantor " | |||
475 | "pass beyond this value. The enum values are defined in " | |||
476 | "Support/Discriminator.h"), | |||
477 | cl::values(clEnumVal(Base, "Use base discriminators only")llvm::cl::OptionEnumValue { "Base", int(Base), "Use base discriminators only" }, | |||
478 | clEnumVal(Pass1, "Use base and pass 1 discriminators")llvm::cl::OptionEnumValue { "Pass1", int(Pass1), "Use base and pass 1 discriminators" }, | |||
479 | clEnumVal(Pass2, "Use base and pass 1-2 discriminators")llvm::cl::OptionEnumValue { "Pass2", int(Pass2), "Use base and pass 1-2 discriminators" }, | |||
480 | clEnumVal(Pass3, "Use base and pass 1-3 discriminators")llvm::cl::OptionEnumValue { "Pass3", int(Pass3), "Use base and pass 1-3 discriminators" }, | |||
481 | clEnumVal(PassLast, "Use all discriminator bits (default)")llvm::cl::OptionEnumValue { "PassLast", int(PassLast), "Use all discriminator bits (default)" })); | |||
482 | ||||
483 | static unsigned getDiscriminatorMask() { | |||
484 | return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue())); | |||
485 | } | |||
486 | ||||
487 | /// Adjust the instr profile in \p WC based on the sample profile in | |||
488 | /// \p Reader. | |||
489 | static void | |||
490 | adjustInstrProfile(std::unique_ptr<WriterContext> &WC, | |||
491 | std::unique_ptr<sampleprof::SampleProfileReader> &Reader, | |||
492 | unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, | |||
493 | unsigned InstrProfColdThreshold) { | |||
494 | // Function to its entry in instr profile. | |||
495 | StringMap<InstrProfileEntry> InstrProfileMap; | |||
496 | InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs); | |||
497 | for (auto &PD : WC->Writer.getProfileData()) { | |||
498 | // Populate IPBuilder. | |||
499 | for (const auto &PDV : PD.getValue()) { | |||
500 | InstrProfRecord Record = PDV.second; | |||
501 | IPBuilder.addRecord(Record); | |||
502 | } | |||
503 | ||||
504 | // If a function has multiple entries in instr profile, skip it. | |||
505 | if (PD.getValue().size() != 1) | |||
506 | continue; | |||
507 | ||||
508 | // Initialize InstrProfileMap. | |||
509 | InstrProfRecord *R = &PD.getValue().begin()->second; | |||
510 | InstrProfileMap[PD.getKey()] = InstrProfileEntry(R); | |||
511 | } | |||
512 | ||||
513 | ProfileSummary InstrPS = *IPBuilder.getSummary(); | |||
514 | ProfileSummary SamplePS = Reader->getSummary(); | |||
515 | ||||
516 | // Compute cold thresholds for instr profile and sample profile. | |||
517 | uint64_t ColdSampleThreshold = | |||
518 | ProfileSummaryBuilder::getEntryForPercentile( | |||
519 | SamplePS.getDetailedSummary(), | |||
520 | ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx]) | |||
521 | .MinCount; | |||
522 | uint64_t HotInstrThreshold = | |||
523 | ProfileSummaryBuilder::getEntryForPercentile( | |||
524 | InstrPS.getDetailedSummary(), | |||
525 | ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx]) | |||
526 | .MinCount; | |||
527 | uint64_t ColdInstrThreshold = | |||
528 | InstrProfColdThreshold | |||
529 | ? InstrProfColdThreshold | |||
530 | : ProfileSummaryBuilder::getEntryForPercentile( | |||
531 | InstrPS.getDetailedSummary(), | |||
532 | ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx]) | |||
533 | .MinCount; | |||
534 | ||||
535 | // Find hot/warm functions in sample profile which is cold in instr profile | |||
536 | // and adjust the profiles of those functions in the instr profile. | |||
537 | for (const auto &PD : Reader->getProfiles()) { | |||
538 | auto &FContext = PD.first; | |||
539 | const sampleprof::FunctionSamples &FS = PD.second; | |||
540 | auto It = InstrProfileMap.find(FContext.toString()); | |||
541 | if (FS.getHeadSamples() > ColdSampleThreshold && | |||
542 | It != InstrProfileMap.end() && | |||
543 | It->second.MaxCount <= ColdInstrThreshold && | |||
544 | FS.getBodySamples().size() >= SupplMinSizeThreshold) { | |||
545 | updateInstrProfileEntry(It->second, HotInstrThreshold, | |||
546 | ZeroCounterThreshold); | |||
547 | } | |||
548 | } | |||
549 | } | |||
550 | ||||
551 | /// The main function to supplement instr profile with sample profile. | |||
552 | /// \Inputs contains the instr profile. \p SampleFilename specifies the | |||
553 | /// sample profile. \p OutputFilename specifies the output profile name. | |||
554 | /// \p OutputFormat specifies the output profile format. \p OutputSparse | |||
555 | /// specifies whether to generate sparse profile. \p SupplMinSizeThreshold | |||
556 | /// specifies the minimal size for the functions whose profile will be | |||
557 | /// adjusted. \p ZeroCounterThreshold is the threshold to check whether | |||
558 | /// a function contains too many zero counters and whether its profile | |||
559 | /// should be dropped. \p InstrProfColdThreshold is the user specified | |||
560 | /// cold threshold which will override the cold threshold got from the | |||
561 | /// instr profile summary. | |||
562 | static void supplementInstrProfile( | |||
563 | const WeightedFileVector &Inputs, StringRef SampleFilename, | |||
564 | StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, | |||
565 | unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, | |||
566 | unsigned InstrProfColdThreshold) { | |||
567 | if (OutputFilename.compare("-") == 0) | |||
568 | exitWithError("cannot write indexed profdata format to stdout"); | |||
569 | if (Inputs.size() != 1) | |||
570 | exitWithError("expect one input to be an instr profile"); | |||
571 | if (Inputs[0].Weight != 1) | |||
572 | exitWithError("expect instr profile doesn't have weight"); | |||
573 | ||||
574 | StringRef InstrFilename = Inputs[0].Filename; | |||
575 | ||||
576 | // Read sample profile. | |||
577 | LLVMContext Context; | |||
578 | auto ReaderOrErr = sampleprof::SampleProfileReader::create( | |||
579 | SampleFilename.str(), Context, FSDiscriminatorPassOption); | |||
580 | if (std::error_code EC = ReaderOrErr.getError()) | |||
581 | exitWithErrorCode(EC, SampleFilename); | |||
582 | auto Reader = std::move(ReaderOrErr.get()); | |||
583 | if (std::error_code EC = Reader->read()) | |||
584 | exitWithErrorCode(EC, SampleFilename); | |||
585 | ||||
586 | // Read instr profile. | |||
587 | std::mutex ErrorLock; | |||
588 | SmallSet<instrprof_error, 4> WriterErrorCodes; | |||
589 | auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock, | |||
590 | WriterErrorCodes); | |||
591 | loadInput(Inputs[0], nullptr, nullptr, WC.get()); | |||
592 | if (WC->Errors.size() > 0) | |||
593 | exitWithError(std::move(WC->Errors[0].first), InstrFilename); | |||
594 | ||||
595 | adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold, | |||
596 | InstrProfColdThreshold); | |||
597 | writeInstrProfile(OutputFilename, OutputFormat, WC->Writer); | |||
598 | } | |||
599 | ||||
600 | /// Make a copy of the given function samples with all symbol names remapped | |||
601 | /// by the provided symbol remapper. | |||
602 | static sampleprof::FunctionSamples | |||
603 | remapSamples(const sampleprof::FunctionSamples &Samples, | |||
604 | SymbolRemapper &Remapper, sampleprof_error &Error) { | |||
605 | sampleprof::FunctionSamples Result; | |||
606 | Result.setName(Remapper(Samples.getName())); | |||
607 | Result.addTotalSamples(Samples.getTotalSamples()); | |||
608 | Result.addHeadSamples(Samples.getHeadSamples()); | |||
609 | for (const auto &BodySample : Samples.getBodySamples()) { | |||
610 | uint32_t MaskedDiscriminator = | |||
611 | BodySample.first.Discriminator & getDiscriminatorMask(); | |||
612 | Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator, | |||
613 | BodySample.second.getSamples()); | |||
614 | for (const auto &Target : BodySample.second.getCallTargets()) { | |||
615 | Result.addCalledTargetSamples(BodySample.first.LineOffset, | |||
616 | MaskedDiscriminator, | |||
617 | Remapper(Target.first()), Target.second); | |||
618 | } | |||
619 | } | |||
620 | for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { | |||
621 | sampleprof::FunctionSamplesMap &Target = | |||
622 | Result.functionSamplesAt(CallsiteSamples.first); | |||
623 | for (const auto &Callsite : CallsiteSamples.second) { | |||
624 | sampleprof::FunctionSamples Remapped = | |||
625 | remapSamples(Callsite.second, Remapper, Error); | |||
626 | MergeResult(Error, | |||
627 | Target[std::string(Remapped.getName())].merge(Remapped)); | |||
628 | } | |||
629 | } | |||
630 | return Result; | |||
631 | } | |||
632 | ||||
633 | static sampleprof::SampleProfileFormat FormatMap[] = { | |||
634 | sampleprof::SPF_None, | |||
635 | sampleprof::SPF_Text, | |||
636 | sampleprof::SPF_Compact_Binary, | |||
637 | sampleprof::SPF_Ext_Binary, | |||
638 | sampleprof::SPF_GCC, | |||
639 | sampleprof::SPF_Binary}; | |||
640 | ||||
641 | static std::unique_ptr<MemoryBuffer> | |||
642 | getInputFileBuf(const StringRef &InputFile) { | |||
643 | if (InputFile == "") | |||
644 | return {}; | |||
645 | ||||
646 | auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); | |||
647 | if (!BufOrError) | |||
648 | exitWithErrorCode(BufOrError.getError(), InputFile); | |||
649 | ||||
650 | return std::move(*BufOrError); | |||
651 | } | |||
652 | ||||
653 | static void populateProfileSymbolList(MemoryBuffer *Buffer, | |||
654 | sampleprof::ProfileSymbolList &PSL) { | |||
655 | if (!Buffer) | |||
656 | return; | |||
657 | ||||
658 | SmallVector<StringRef, 32> SymbolVec; | |||
659 | StringRef Data = Buffer->getBuffer(); | |||
660 | Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); | |||
661 | ||||
662 | for (StringRef SymbolStr : SymbolVec) | |||
663 | PSL.add(SymbolStr.trim()); | |||
664 | } | |||
665 | ||||
666 | static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, | |||
667 | ProfileFormat OutputFormat, | |||
668 | MemoryBuffer *Buffer, | |||
669 | sampleprof::ProfileSymbolList &WriterList, | |||
670 | bool CompressAllSections, bool UseMD5, | |||
671 | bool GenPartialProfile) { | |||
672 | populateProfileSymbolList(Buffer, WriterList); | |||
673 | if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary) | |||
674 | warn("Profile Symbol list is not empty but the output format is not " | |||
675 | "ExtBinary format. The list will be lost in the output. "); | |||
676 | ||||
677 | Writer.setProfileSymbolList(&WriterList); | |||
678 | ||||
679 | if (CompressAllSections) { | |||
680 | if (OutputFormat != PF_Ext_Binary) | |||
681 | warn("-compress-all-section is ignored. Specify -extbinary to enable it"); | |||
682 | else | |||
683 | Writer.setToCompressAllSections(); | |||
684 | } | |||
685 | if (UseMD5) { | |||
686 | if (OutputFormat != PF_Ext_Binary) | |||
687 | warn("-use-md5 is ignored. Specify -extbinary to enable it"); | |||
688 | else | |||
689 | Writer.setUseMD5(); | |||
690 | } | |||
691 | if (GenPartialProfile) { | |||
692 | if (OutputFormat != PF_Ext_Binary) | |||
693 | warn("-gen-partial-profile is ignored. Specify -extbinary to enable it"); | |||
694 | else | |||
695 | Writer.setPartialProfile(); | |||
696 | } | |||
697 | } | |||
698 | ||||
699 | static void | |||
700 | mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, | |||
701 | StringRef OutputFilename, ProfileFormat OutputFormat, | |||
702 | StringRef ProfileSymbolListFile, bool CompressAllSections, | |||
703 | bool UseMD5, bool GenPartialProfile, bool GenCSNestedProfile, | |||
704 | bool SampleMergeColdContext, bool SampleTrimColdContext, | |||
705 | bool SampleColdContextFrameDepth, FailureMode FailMode) { | |||
706 | using namespace sampleprof; | |||
707 | SampleProfileMap ProfileMap; | |||
708 | SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; | |||
709 | LLVMContext Context; | |||
710 | sampleprof::ProfileSymbolList WriterList; | |||
711 | Optional<bool> ProfileIsProbeBased; | |||
712 | Optional<bool> ProfileIsCSFlat; | |||
713 | for (const auto &Input : Inputs) { | |||
714 | auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, | |||
715 | FSDiscriminatorPassOption); | |||
716 | if (std::error_code EC = ReaderOrErr.getError()) { | |||
717 | warnOrExitGivenError(FailMode, EC, Input.Filename); | |||
718 | continue; | |||
719 | } | |||
720 | ||||
721 | // We need to keep the readers around until after all the files are | |||
722 | // read so that we do not lose the function names stored in each | |||
723 | // reader's memory. The function names are needed to write out the | |||
724 | // merged profile map. | |||
725 | Readers.push_back(std::move(ReaderOrErr.get())); | |||
726 | const auto Reader = Readers.back().get(); | |||
727 | if (std::error_code EC = Reader->read()) { | |||
728 | warnOrExitGivenError(FailMode, EC, Input.Filename); | |||
729 | Readers.pop_back(); | |||
730 | continue; | |||
731 | } | |||
732 | ||||
733 | SampleProfileMap &Profiles = Reader->getProfiles(); | |||
734 | if (ProfileIsProbeBased.hasValue() && | |||
735 | ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased) | |||
736 | exitWithError( | |||
737 | "cannot merge probe-based profile with non-probe-based profile"); | |||
738 | ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased; | |||
739 | if (ProfileIsCSFlat.hasValue() && | |||
740 | ProfileIsCSFlat != FunctionSamples::ProfileIsCSFlat) | |||
741 | exitWithError("cannot merge CS profile with non-CS profile"); | |||
742 | ProfileIsCSFlat = FunctionSamples::ProfileIsCSFlat; | |||
743 | for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end(); | |||
744 | I != E; ++I) { | |||
745 | sampleprof_error Result = sampleprof_error::success; | |||
746 | FunctionSamples Remapped = | |||
747 | Remapper ? remapSamples(I->second, *Remapper, Result) | |||
748 | : FunctionSamples(); | |||
749 | FunctionSamples &Samples = Remapper ? Remapped : I->second; | |||
750 | SampleContext FContext = Samples.getContext(); | |||
751 | MergeResult(Result, ProfileMap[FContext].merge(Samples, Input.Weight)); | |||
752 | if (Result != sampleprof_error::success) { | |||
753 | std::error_code EC = make_error_code(Result); | |||
754 | handleMergeWriterError(errorCodeToError(EC), Input.Filename, | |||
755 | FContext.toString()); | |||
756 | } | |||
757 | } | |||
758 | ||||
759 | std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList = | |||
760 | Reader->getProfileSymbolList(); | |||
761 | if (ReaderList) | |||
762 | WriterList.merge(*ReaderList); | |||
763 | } | |||
764 | ||||
765 | if (ProfileIsCSFlat && (SampleMergeColdContext || SampleTrimColdContext)) { | |||
766 | // Use threshold calculated from profile summary unless specified. | |||
767 | SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); | |||
768 | auto Summary = Builder.computeSummaryForProfiles(ProfileMap); | |||
769 | uint64_t SampleProfColdThreshold = | |||
770 | ProfileSummaryBuilder::getColdCountThreshold( | |||
771 | (Summary->getDetailedSummary())); | |||
772 | ||||
773 | // Trim and merge cold context profile using cold threshold above; | |||
774 | SampleContextTrimmer(ProfileMap) | |||
775 | .trimAndMergeColdContextProfiles( | |||
776 | SampleProfColdThreshold, SampleTrimColdContext, | |||
777 | SampleMergeColdContext, SampleColdContextFrameDepth, false); | |||
778 | } | |||
779 | ||||
780 | if (ProfileIsCSFlat && GenCSNestedProfile) { | |||
781 | CSProfileConverter CSConverter(ProfileMap); | |||
782 | CSConverter.convertProfiles(); | |||
783 | ProfileIsCSFlat = FunctionSamples::ProfileIsCSFlat = false; | |||
784 | } | |||
785 | ||||
786 | auto WriterOrErr = | |||
787 | SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); | |||
788 | if (std::error_code EC = WriterOrErr.getError()) | |||
789 | exitWithErrorCode(EC, OutputFilename); | |||
790 | ||||
791 | auto Writer = std::move(WriterOrErr.get()); | |||
792 | // WriterList will have StringRef refering to string in Buffer. | |||
793 | // Make sure Buffer lives as long as WriterList. | |||
794 | auto Buffer = getInputFileBuf(ProfileSymbolListFile); | |||
795 | handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, | |||
796 | CompressAllSections, UseMD5, GenPartialProfile); | |||
797 | if (std::error_code EC = Writer->write(ProfileMap)) | |||
798 | exitWithErrorCode(std::move(EC)); | |||
799 | } | |||
800 | ||||
801 | static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { | |||
802 | StringRef WeightStr, FileName; | |||
803 | std::tie(WeightStr, FileName) = WeightedFilename.split(','); | |||
804 | ||||
805 | uint64_t Weight; | |||
806 | if (WeightStr.getAsInteger(10, Weight) || Weight < 1) | |||
807 | exitWithError("input weight must be a positive integer"); | |||
808 | ||||
809 | return {std::string(FileName), Weight}; | |||
810 | } | |||
811 | ||||
812 | static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { | |||
813 | StringRef Filename = WF.Filename; | |||
814 | uint64_t Weight = WF.Weight; | |||
815 | ||||
816 | // If it's STDIN just pass it on. | |||
817 | if (Filename == "-") { | |||
818 | WNI.push_back({std::string(Filename), Weight}); | |||
819 | return; | |||
820 | } | |||
821 | ||||
822 | llvm::sys::fs::file_status Status; | |||
823 | llvm::sys::fs::status(Filename, Status); | |||
824 | if (!llvm::sys::fs::exists(Status)) | |||
825 | exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), | |||
826 | Filename); | |||
827 | // If it's a source file, collect it. | |||
828 | if (llvm::sys::fs::is_regular_file(Status)) { | |||
829 | WNI.push_back({std::string(Filename), Weight}); | |||
830 | return; | |||
831 | } | |||
832 | ||||
833 | if (llvm::sys::fs::is_directory(Status)) { | |||
834 | std::error_code EC; | |||
835 | for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E; | |||
836 | F != E && !EC; F.increment(EC)) { | |||
837 | if (llvm::sys::fs::is_regular_file(F->path())) { | |||
838 | addWeightedInput(WNI, {F->path(), Weight}); | |||
839 | } | |||
840 | } | |||
841 | if (EC) | |||
842 | exitWithErrorCode(EC, Filename); | |||
843 | } | |||
844 | } | |||
845 | ||||
846 | static void parseInputFilenamesFile(MemoryBuffer *Buffer, | |||
847 | WeightedFileVector &WFV) { | |||
848 | if (!Buffer) | |||
849 | return; | |||
850 | ||||
851 | SmallVector<StringRef, 8> Entries; | |||
852 | StringRef Data = Buffer->getBuffer(); | |||
853 | Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); | |||
854 | for (const StringRef &FileWeightEntry : Entries) { | |||
855 | StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r"); | |||
856 | // Skip comments. | |||
857 | if (SanitizedEntry.startswith("#")) | |||
858 | continue; | |||
859 | // If there's no comma, it's an unweighted profile. | |||
860 | else if (!SanitizedEntry.contains(',')) | |||
861 | addWeightedInput(WFV, {std::string(SanitizedEntry), 1}); | |||
862 | else | |||
863 | addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); | |||
864 | } | |||
865 | } | |||
866 | ||||
867 | static int merge_main(int argc, const char *argv[]) { | |||
868 | cl::list<std::string> InputFilenames(cl::Positional, | |||
869 | cl::desc("<filename...>")); | |||
870 | cl::list<std::string> WeightedInputFilenames("weighted-input", | |||
871 | cl::desc("<weight>,<filename>")); | |||
872 | cl::opt<std::string> InputFilenamesFile( | |||
873 | "input-files", cl::init(""), | |||
874 | cl::desc("Path to file containing newline-separated " | |||
875 | "[<weight>,]<filename> entries")); | |||
876 | cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), | |||
877 | cl::aliasopt(InputFilenamesFile)); | |||
878 | cl::opt<bool> DumpInputFileList( | |||
879 | "dump-input-file-list", cl::init(false), cl::Hidden, | |||
880 | cl::desc("Dump the list of input files and their weights, then exit")); | |||
881 | cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"), | |||
882 | cl::desc("Symbol remapping file")); | |||
883 | cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), | |||
884 | cl::aliasopt(RemappingFile)); | |||
885 | cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), | |||
886 | cl::init("-"), cl::desc("Output file")); | |||
887 | cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), | |||
888 | cl::aliasopt(OutputFilename)); | |||
889 | cl::opt<ProfileKinds> ProfileKind( | |||
890 | cl::desc("Profile kind:"), cl::init(instr), | |||
891 | cl::values(clEnumVal(instr, "Instrumentation profile (default)")llvm::cl::OptionEnumValue { "instr", int(instr), "Instrumentation profile (default)" }, | |||
892 | clEnumVal(sample, "Sample profile")llvm::cl::OptionEnumValue { "sample", int(sample), "Sample profile" })); | |||
893 | cl::opt<ProfileFormat> OutputFormat( | |||
894 | cl::desc("Format of output profile"), cl::init(PF_Binary), | |||
895 | cl::values( | |||
896 | clEnumValN(PF_Binary, "binary", "Binary encoding (default)")llvm::cl::OptionEnumValue { "binary", int(PF_Binary), "Binary encoding (default)" }, | |||
897 | clEnumValN(PF_Compact_Binary, "compbinary",llvm::cl::OptionEnumValue { "compbinary", int(PF_Compact_Binary ), "Compact binary encoding" } | |||
898 | "Compact binary encoding")llvm::cl::OptionEnumValue { "compbinary", int(PF_Compact_Binary ), "Compact binary encoding" }, | |||
899 | clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding")llvm::cl::OptionEnumValue { "extbinary", int(PF_Ext_Binary), "Extensible binary encoding" }, | |||
900 | clEnumValN(PF_Text, "text", "Text encoding")llvm::cl::OptionEnumValue { "text", int(PF_Text), "Text encoding" }, | |||
901 | clEnumValN(PF_GCC, "gcc",llvm::cl::OptionEnumValue { "gcc", int(PF_GCC), "GCC encoding (only meaningful for -sample)" } | |||
902 | "GCC encoding (only meaningful for -sample)")llvm::cl::OptionEnumValue { "gcc", int(PF_GCC), "GCC encoding (only meaningful for -sample)" })); | |||
903 | cl::opt<FailureMode> FailureMode( | |||
904 | "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"), | |||
905 | cl::values(clEnumValN(failIfAnyAreInvalid, "any",llvm::cl::OptionEnumValue { "any", int(failIfAnyAreInvalid), "Fail if any profile is invalid." } | |||
906 | "Fail if any profile is invalid.")llvm::cl::OptionEnumValue { "any", int(failIfAnyAreInvalid), "Fail if any profile is invalid." }, | |||
907 | clEnumValN(failIfAllAreInvalid, "all",llvm::cl::OptionEnumValue { "all", int(failIfAllAreInvalid), "Fail only if all profiles are invalid." } | |||
908 | "Fail only if all profiles are invalid.")llvm::cl::OptionEnumValue { "all", int(failIfAllAreInvalid), "Fail only if all profiles are invalid." })); | |||
909 | cl::opt<bool> OutputSparse("sparse", cl::init(false), | |||
910 | cl::desc("Generate a sparse profile (only meaningful for -instr)")); | |||
911 | cl::opt<unsigned> NumThreads( | |||
912 | "num-threads", cl::init(0), | |||
913 | cl::desc("Number of merge threads to use (default: autodetect)")); | |||
914 | cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), | |||
915 | cl::aliasopt(NumThreads)); | |||
916 | cl::opt<std::string> ProfileSymbolListFile( | |||
917 | "prof-sym-list", cl::init(""), | |||
918 | cl::desc("Path to file containing the list of function symbols " | |||
919 | "used to populate profile symbol list")); | |||
920 | cl::opt<bool> CompressAllSections( | |||
921 | "compress-all-sections", cl::init(false), cl::Hidden, | |||
922 | cl::desc("Compress all sections when writing the profile (only " | |||
923 | "meaningful for -extbinary)")); | |||
924 | cl::opt<bool> UseMD5( | |||
925 | "use-md5", cl::init(false), cl::Hidden, | |||
926 | cl::desc("Choose to use MD5 to represent string in name table (only " | |||
927 | "meaningful for -extbinary)")); | |||
928 | cl::opt<bool> SampleMergeColdContext( | |||
929 | "sample-merge-cold-context", cl::init(false), cl::Hidden, | |||
930 | cl::desc( | |||
931 | "Merge context sample profiles whose count is below cold threshold")); | |||
932 | cl::opt<bool> SampleTrimColdContext( | |||
933 | "sample-trim-cold-context", cl::init(false), cl::Hidden, | |||
934 | cl::desc( | |||
935 | "Trim context sample profiles whose count is below cold threshold")); | |||
936 | cl::opt<uint32_t> SampleColdContextFrameDepth( | |||
937 | "sample-frame-depth-for-cold-context", cl::init(1), cl::ZeroOrMore, | |||
938 | cl::desc("Keep the last K frames while merging cold profile. 1 means the " | |||
939 | "context-less base profile")); | |||
940 | cl::opt<bool> GenPartialProfile( | |||
941 | "gen-partial-profile", cl::init(false), cl::Hidden, | |||
942 | cl::desc("Generate a partial profile (only meaningful for -extbinary)")); | |||
943 | cl::opt<std::string> SupplInstrWithSample( | |||
944 | "supplement-instr-with-sample", cl::init(""), cl::Hidden, | |||
945 | cl::desc("Supplement an instr profile with sample profile, to correct " | |||
946 | "the profile unrepresentativeness issue. The sample " | |||
947 | "profile is the input of the flag. Output will be in instr " | |||
948 | "format (The flag only works with -instr)")); | |||
949 | cl::opt<float> ZeroCounterThreshold( | |||
950 | "zero-counter-threshold", cl::init(0.7), cl::Hidden, | |||
951 | cl::desc("For the function which is cold in instr profile but hot in " | |||
952 | "sample profile, if the ratio of the number of zero counters " | |||
953 | "divided by the the total number of counters is above the " | |||
954 | "threshold, the profile of the function will be regarded as " | |||
955 | "being harmful for performance and will be dropped.")); | |||
956 | cl::opt<unsigned> SupplMinSizeThreshold( | |||
957 | "suppl-min-size-threshold", cl::init(10), cl::Hidden, | |||
958 | cl::desc("If the size of a function is smaller than the threshold, " | |||
959 | "assume it can be inlined by PGO early inliner and it won't " | |||
960 | "be adjusted based on sample profile.")); | |||
961 | cl::opt<unsigned> InstrProfColdThreshold( | |||
962 | "instr-prof-cold-threshold", cl::init(0), cl::Hidden, | |||
963 | cl::desc("User specified cold threshold for instr profile which will " | |||
964 | "override the cold threshold got from profile summary. ")); | |||
965 | cl::opt<bool> GenCSNestedProfile( | |||
966 | "gen-cs-nested-profile", cl::Hidden, cl::init(false), | |||
967 | cl::desc("Generate nested function profiles for CSSPGO")); | |||
968 | cl::opt<std::string> DebugInfoFilename( | |||
969 | "debug-info", cl::init(""), | |||
970 | cl::desc("Use the provided debug info to correlate the raw profile.")); | |||
971 | ||||
972 | cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); | |||
973 | ||||
974 | WeightedFileVector WeightedInputs; | |||
975 | for (StringRef Filename : InputFilenames) | |||
976 | addWeightedInput(WeightedInputs, {std::string(Filename), 1}); | |||
977 | for (StringRef WeightedFilename : WeightedInputFilenames) | |||
978 | addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); | |||
979 | ||||
980 | // Make sure that the file buffer stays alive for the duration of the | |||
981 | // weighted input vector's lifetime. | |||
982 | auto Buffer = getInputFileBuf(InputFilenamesFile); | |||
983 | parseInputFilenamesFile(Buffer.get(), WeightedInputs); | |||
984 | ||||
985 | if (WeightedInputs.empty()) | |||
986 | exitWithError("no input files specified. See " + | |||
987 | sys::path::filename(argv[0]) + " -help"); | |||
988 | ||||
989 | if (DumpInputFileList) { | |||
990 | for (auto &WF : WeightedInputs) | |||
991 | outs() << WF.Weight << "," << WF.Filename << "\n"; | |||
992 | return 0; | |||
993 | } | |||
994 | ||||
995 | std::unique_ptr<SymbolRemapper> Remapper; | |||
996 | if (!RemappingFile.empty()) | |||
997 | Remapper = SymbolRemapper::create(RemappingFile); | |||
998 | ||||
999 | if (!SupplInstrWithSample.empty()) { | |||
1000 | if (ProfileKind != instr) | |||
1001 | exitWithError( | |||
1002 | "-supplement-instr-with-sample can only work with -instr. "); | |||
1003 | ||||
1004 | supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename, | |||
1005 | OutputFormat, OutputSparse, SupplMinSizeThreshold, | |||
1006 | ZeroCounterThreshold, InstrProfColdThreshold); | |||
1007 | return 0; | |||
1008 | } | |||
1009 | ||||
1010 | if (ProfileKind == instr) | |||
1011 | mergeInstrProfile(WeightedInputs, DebugInfoFilename, Remapper.get(), | |||
1012 | OutputFilename, OutputFormat, OutputSparse, NumThreads, | |||
1013 | FailureMode); | |||
1014 | else | |||
1015 | mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, | |||
1016 | OutputFormat, ProfileSymbolListFile, CompressAllSections, | |||
1017 | UseMD5, GenPartialProfile, GenCSNestedProfile, | |||
1018 | SampleMergeColdContext, SampleTrimColdContext, | |||
1019 | SampleColdContextFrameDepth, FailureMode); | |||
1020 | return 0; | |||
1021 | } | |||
1022 | ||||
1023 | /// Computer the overlap b/w profile BaseFilename and profile TestFilename. | |||
1024 | static void overlapInstrProfile(const std::string &BaseFilename, | |||
1025 | const std::string &TestFilename, | |||
1026 | const OverlapFuncFilters &FuncFilter, | |||
1027 | raw_fd_ostream &OS, bool IsCS) { | |||
1028 | std::mutex ErrorLock; | |||
1029 | SmallSet<instrprof_error, 4> WriterErrorCodes; | |||
1030 | WriterContext Context(false, ErrorLock, WriterErrorCodes); | |||
1031 | WeightedFile WeightedInput{BaseFilename, 1}; | |||
1032 | OverlapStats Overlap; | |||
1033 | Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS); | |||
1034 | if (E) | |||
1035 | exitWithError(std::move(E), "error in getting profile count sums"); | |||
1036 | if (Overlap.Base.CountSum < 1.0f) { | |||
1037 | OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; | |||
1038 | exit(0); | |||
1039 | } | |||
1040 | if (Overlap.Test.CountSum < 1.0f) { | |||
1041 | OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; | |||
1042 | exit(0); | |||
1043 | } | |||
1044 | loadInput(WeightedInput, nullptr, nullptr, &Context); | |||
1045 | overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, | |||
1046 | IsCS); | |||
1047 | Overlap.dump(OS); | |||
1048 | } | |||
1049 | ||||
1050 | namespace { | |||
1051 | struct SampleOverlapStats { | |||
1052 | SampleContext BaseName; | |||
1053 | SampleContext TestName; | |||
1054 | // Number of overlap units | |||
1055 | uint64_t OverlapCount; | |||
1056 | // Total samples of overlap units | |||
1057 | uint64_t OverlapSample; | |||
1058 | // Number of and total samples of units that only present in base or test | |||
1059 | // profile | |||
1060 | uint64_t BaseUniqueCount; | |||
1061 | uint64_t BaseUniqueSample; | |||
1062 | uint64_t TestUniqueCount; | |||
1063 | uint64_t TestUniqueSample; | |||
1064 | // Number of units and total samples in base or test profile | |||
1065 | uint64_t BaseCount; | |||
1066 | uint64_t BaseSample; | |||
1067 | uint64_t TestCount; | |||
1068 | uint64_t TestSample; | |||
1069 | // Number of and total samples of units that present in at least one profile | |||
1070 | uint64_t UnionCount; | |||
1071 | uint64_t UnionSample; | |||
1072 | // Weighted similarity | |||
1073 | double Similarity; | |||
1074 | // For SampleOverlapStats instances representing functions, weights of the | |||
1075 | // function in base and test profiles | |||
1076 | double BaseWeight; | |||
1077 | double TestWeight; | |||
1078 | ||||
1079 | SampleOverlapStats() | |||
1080 | : OverlapCount(0), OverlapSample(0), BaseUniqueCount(0), | |||
1081 | BaseUniqueSample(0), TestUniqueCount(0), TestUniqueSample(0), | |||
1082 | BaseCount(0), BaseSample(0), TestCount(0), TestSample(0), UnionCount(0), | |||
1083 | UnionSample(0), Similarity(0.0), BaseWeight(0.0), TestWeight(0.0) {} | |||
1084 | }; | |||
1085 | } // end anonymous namespace | |||
1086 | ||||
1087 | namespace { | |||
1088 | struct FuncSampleStats { | |||
1089 | uint64_t SampleSum; | |||
1090 | uint64_t MaxSample; | |||
1091 | uint64_t HotBlockCount; | |||
1092 | FuncSampleStats() : SampleSum(0), MaxSample(0), HotBlockCount(0) {} | |||
1093 | FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample, | |||
1094 | uint64_t HotBlockCount) | |||
1095 | : SampleSum(SampleSum), MaxSample(MaxSample), | |||
1096 | HotBlockCount(HotBlockCount) {} | |||
1097 | }; | |||
1098 | } // end anonymous namespace | |||
1099 | ||||
1100 | namespace { | |||
1101 | enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None }; | |||
1102 | ||||
1103 | // Class for updating merging steps for two sorted maps. The class should be | |||
1104 | // instantiated with a map iterator type. | |||
1105 | template <class T> class MatchStep { | |||
1106 | public: | |||
1107 | MatchStep() = delete; | |||
1108 | ||||
1109 | MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd) | |||
1110 | : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter), | |||
1111 | SecondEnd(SecondEnd), Status(MS_None) {} | |||
1112 | ||||
1113 | bool areBothFinished() const { | |||
1114 | return (FirstIter == FirstEnd && SecondIter == SecondEnd); | |||
1115 | } | |||
1116 | ||||
1117 | bool isFirstFinished() const { return FirstIter == FirstEnd; } | |||
1118 | ||||
1119 | bool isSecondFinished() const { return SecondIter == SecondEnd; } | |||
1120 | ||||
1121 | /// Advance one step based on the previous match status unless the previous | |||
1122 | /// status is MS_None. Then update Status based on the comparison between two | |||
1123 | /// container iterators at the current step. If the previous status is | |||
1124 | /// MS_None, it means two iterators are at the beginning and no comparison has | |||
1125 | /// been made, so we simply update Status without advancing the iterators. | |||
1126 | void updateOneStep(); | |||
1127 | ||||
1128 | T getFirstIter() const { return FirstIter; } | |||
1129 | ||||
1130 | T getSecondIter() const { return SecondIter; } | |||
1131 | ||||
1132 | MatchStatus getMatchStatus() const { return Status; } | |||
1133 | ||||
1134 | private: | |||
1135 | // Current iterator and end iterator of the first container. | |||
1136 | T FirstIter; | |||
1137 | T FirstEnd; | |||
1138 | // Current iterator and end iterator of the second container. | |||
1139 | T SecondIter; | |||
1140 | T SecondEnd; | |||
1141 | // Match status of the current step. | |||
1142 | MatchStatus Status; | |||
1143 | }; | |||
1144 | } // end anonymous namespace | |||
1145 | ||||
1146 | template <class T> void MatchStep<T>::updateOneStep() { | |||
1147 | switch (Status) { | |||
1148 | case MS_Match: | |||
1149 | ++FirstIter; | |||
1150 | ++SecondIter; | |||
1151 | break; | |||
1152 | case MS_FirstUnique: | |||
1153 | ++FirstIter; | |||
1154 | break; | |||
1155 | case MS_SecondUnique: | |||
1156 | ++SecondIter; | |||
1157 | break; | |||
1158 | case MS_None: | |||
1159 | break; | |||
1160 | } | |||
1161 | ||||
1162 | // Update Status according to iterators at the current step. | |||
1163 | if (areBothFinished()) | |||
1164 | return; | |||
1165 | if (FirstIter != FirstEnd && | |||
1166 | (SecondIter == SecondEnd || FirstIter->first < SecondIter->first)) | |||
1167 | Status = MS_FirstUnique; | |||
1168 | else if (SecondIter != SecondEnd && | |||
1169 | (FirstIter == FirstEnd || SecondIter->first < FirstIter->first)) | |||
1170 | Status = MS_SecondUnique; | |||
1171 | else | |||
1172 | Status = MS_Match; | |||
1173 | } | |||
1174 | ||||
1175 | // Return the sum of line/block samples, the max line/block sample, and the | |||
1176 | // number of line/block samples above the given threshold in a function | |||
1177 | // including its inlinees. | |||
1178 | static void getFuncSampleStats(const sampleprof::FunctionSamples &Func, | |||
1179 | FuncSampleStats &FuncStats, | |||
1180 | uint64_t HotThreshold) { | |||
1181 | for (const auto &L : Func.getBodySamples()) { | |||
1182 | uint64_t Sample = L.second.getSamples(); | |||
1183 | FuncStats.SampleSum += Sample; | |||
1184 | FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample); | |||
1185 | if (Sample >= HotThreshold) | |||
1186 | ++FuncStats.HotBlockCount; | |||
1187 | } | |||
1188 | ||||
1189 | for (const auto &C : Func.getCallsiteSamples()) { | |||
1190 | for (const auto &F : C.second) | |||
1191 | getFuncSampleStats(F.second, FuncStats, HotThreshold); | |||
1192 | } | |||
1193 | } | |||
1194 | ||||
1195 | /// Predicate that determines if a function is hot with a given threshold. We | |||
1196 | /// keep it separate from its callsites for possible extension in the future. | |||
1197 | static bool isFunctionHot(const FuncSampleStats &FuncStats, | |||
1198 | uint64_t HotThreshold) { | |||
1199 | // We intentionally compare the maximum sample count in a function with the | |||
1200 | // HotThreshold to get an approximate determination on hot functions. | |||
1201 | return (FuncStats.MaxSample >= HotThreshold); | |||
1202 | } | |||
1203 | ||||
1204 | namespace { | |||
1205 | class SampleOverlapAggregator { | |||
1206 | public: | |||
1207 | SampleOverlapAggregator(const std::string &BaseFilename, | |||
1208 | const std::string &TestFilename, | |||
1209 | double LowSimilarityThreshold, double Epsilon, | |||
1210 | const OverlapFuncFilters &FuncFilter) | |||
1211 | : BaseFilename(BaseFilename), TestFilename(TestFilename), | |||
1212 | LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon), | |||
1213 | FuncFilter(FuncFilter) {} | |||
1214 | ||||
1215 | /// Detect 0-sample input profile and report to output stream. This interface | |||
1216 | /// should be called after loadProfiles(). | |||
1217 | bool detectZeroSampleProfile(raw_fd_ostream &OS) const; | |||
1218 | ||||
1219 | /// Write out function-level similarity statistics for functions specified by | |||
1220 | /// options --function, --value-cutoff, and --similarity-cutoff. | |||
1221 | void dumpFuncSimilarity(raw_fd_ostream &OS) const; | |||
1222 | ||||
1223 | /// Write out program-level similarity and overlap statistics. | |||
1224 | void dumpProgramSummary(raw_fd_ostream &OS) const; | |||
1225 | ||||
1226 | /// Write out hot-function and hot-block statistics for base_profile, | |||
1227 | /// test_profile, and their overlap. For both cases, the overlap HO is | |||
1228 | /// calculated as follows: | |||
1229 | /// Given the number of functions (or blocks) that are hot in both profiles | |||
1230 | /// HCommon and the number of functions (or blocks) that are hot in at | |||
1231 | /// least one profile HUnion, HO = HCommon / HUnion. | |||
1232 | void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const; | |||
1233 | ||||
1234 | /// This function tries matching functions in base and test profiles. For each | |||
1235 | /// pair of matched functions, it aggregates the function-level | |||
1236 | /// similarity into a profile-level similarity. It also dump function-level | |||
1237 | /// similarity information of functions specified by --function, | |||
1238 | /// --value-cutoff, and --similarity-cutoff options. The program-level | |||
1239 | /// similarity PS is computed as follows: | |||
1240 | /// Given function-level similarity FS(A) for all function A, the | |||
1241 | /// weight of function A in base profile WB(A), and the weight of function | |||
1242 | /// A in test profile WT(A), compute PS(base_profile, test_profile) = | |||
1243 | /// sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0 | |||
1244 | /// meaning no-overlap. | |||
1245 | void computeSampleProfileOverlap(raw_fd_ostream &OS); | |||
1246 | ||||
1247 | /// Initialize ProfOverlap with the sum of samples in base and test | |||
1248 | /// profiles. This function also computes and keeps the sum of samples and | |||
1249 | /// max sample counts of each function in BaseStats and TestStats for later | |||
1250 | /// use to avoid re-computations. | |||
1251 | void initializeSampleProfileOverlap(); | |||
1252 | ||||
1253 | /// Load profiles specified by BaseFilename and TestFilename. | |||
1254 | std::error_code loadProfiles(); | |||
1255 | ||||
1256 | using FuncSampleStatsMap = | |||
1257 | std::unordered_map<SampleContext, FuncSampleStats, SampleContext::Hash>; | |||
1258 | ||||
1259 | private: | |||
1260 | SampleOverlapStats ProfOverlap; | |||
1261 | SampleOverlapStats HotFuncOverlap; | |||
1262 | SampleOverlapStats HotBlockOverlap; | |||
1263 | std::string BaseFilename; | |||
1264 | std::string TestFilename; | |||
1265 | std::unique_ptr<sampleprof::SampleProfileReader> BaseReader; | |||
1266 | std::unique_ptr<sampleprof::SampleProfileReader> TestReader; | |||
1267 | // BaseStats and TestStats hold FuncSampleStats for each function, with | |||
1268 | // function name as the key. | |||
1269 | FuncSampleStatsMap BaseStats; | |||
1270 | FuncSampleStatsMap TestStats; | |||
1271 | // Low similarity threshold in floating point number | |||
1272 | double LowSimilarityThreshold; | |||
1273 | // Block samples above BaseHotThreshold or TestHotThreshold are considered hot | |||
1274 | // for tracking hot blocks. | |||
1275 | uint64_t BaseHotThreshold; | |||
1276 | uint64_t TestHotThreshold; | |||
1277 | // A small threshold used to round the results of floating point accumulations | |||
1278 | // to resolve imprecision. | |||
1279 | const double Epsilon; | |||
1280 | std::multimap<double, SampleOverlapStats, std::greater<double>> | |||
1281 | FuncSimilarityDump; | |||
1282 | // FuncFilter carries specifications in options --value-cutoff and | |||
1283 | // --function. | |||
1284 | OverlapFuncFilters FuncFilter; | |||
1285 | // Column offsets for printing the function-level details table. | |||
1286 | static const unsigned int TestWeightCol = 15; | |||
1287 | static const unsigned int SimilarityCol = 30; | |||
1288 | static const unsigned int OverlapCol = 43; | |||
1289 | static const unsigned int BaseUniqueCol = 53; | |||
1290 | static const unsigned int TestUniqueCol = 67; | |||
1291 | static const unsigned int BaseSampleCol = 81; | |||
1292 | static const unsigned int TestSampleCol = 96; | |||
1293 | static const unsigned int FuncNameCol = 111; | |||
1294 | ||||
1295 | /// Return a similarity of two line/block sample counters in the same | |||
1296 | /// function in base and test profiles. The line/block-similarity BS(i) is | |||
1297 | /// computed as follows: | |||
1298 | /// For an offsets i, given the sample count at i in base profile BB(i), | |||
1299 | /// the sample count at i in test profile BT(i), the sum of sample counts | |||
1300 | /// in this function in base profile SB, and the sum of sample counts in | |||
1301 | /// this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB - | |||
1302 | /// BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap. | |||
1303 | double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample, | |||
1304 | const SampleOverlapStats &FuncOverlap) const; | |||
1305 | ||||
1306 | void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample, | |||
1307 | uint64_t HotBlockCount); | |||
1308 | ||||
1309 | void getHotFunctions(const FuncSampleStatsMap &ProfStats, | |||
1310 | FuncSampleStatsMap &HotFunc, | |||
1311 | uint64_t HotThreshold) const; | |||
1312 | ||||
1313 | void computeHotFuncOverlap(); | |||
1314 | ||||
1315 | /// This function updates statistics in FuncOverlap, HotBlockOverlap, and | |||
1316 | /// Difference for two sample units in a matched function according to the | |||
1317 | /// given match status. | |||
1318 | void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample, | |||
1319 | uint64_t HotBlockCount, | |||
1320 | SampleOverlapStats &FuncOverlap, | |||
1321 | double &Difference, MatchStatus Status); | |||
1322 | ||||
1323 | /// This function updates statistics in FuncOverlap, HotBlockOverlap, and | |||
1324 | /// Difference for unmatched callees that only present in one profile in a | |||
1325 | /// matched caller function. | |||
1326 | void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func, | |||
1327 | SampleOverlapStats &FuncOverlap, | |||
1328 | double &Difference, MatchStatus Status); | |||
1329 | ||||
1330 | /// This function updates sample overlap statistics of an overlap function in | |||
1331 | /// base and test profile. It also calculates a function-internal similarity | |||
1332 | /// FIS as follows: | |||
1333 | /// For offsets i that have samples in at least one profile in this | |||
1334 | /// function A, given BS(i) returned by computeBlockSimilarity(), compute | |||
1335 | /// FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with | |||
1336 | /// 0.0 meaning no overlap. | |||
1337 | double computeSampleFunctionInternalOverlap( | |||
1338 | const sampleprof::FunctionSamples &BaseFunc, | |||
1339 | const sampleprof::FunctionSamples &TestFunc, | |||
1340 | SampleOverlapStats &FuncOverlap); | |||
1341 | ||||
1342 | /// Function-level similarity (FS) is a weighted value over function internal | |||
1343 | /// similarity (FIS). This function computes a function's FS from its FIS by | |||
1344 | /// applying the weight. | |||
1345 | double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample, | |||
1346 | uint64_t TestFuncSample) const; | |||
1347 | ||||
1348 | /// The function-level similarity FS(A) for a function A is computed as | |||
1349 | /// follows: | |||
1350 | /// Compute a function-internal similarity FIS(A) by | |||
1351 | /// computeSampleFunctionInternalOverlap(). Then, with the weight of | |||
1352 | /// function A in base profile WB(A), and the weight of function A in test | |||
1353 | /// profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A))) | |||
1354 | /// ranging in [0.0f to 1.0f] with 0.0 meaning no overlap. | |||
1355 | double | |||
1356 | computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc, | |||
1357 | const sampleprof::FunctionSamples *TestFunc, | |||
1358 | SampleOverlapStats *FuncOverlap, | |||
1359 | uint64_t BaseFuncSample, | |||
1360 | uint64_t TestFuncSample); | |||
1361 | ||||
1362 | /// Profile-level similarity (PS) is a weighted aggregate over function-level | |||
1363 | /// similarities (FS). This method weights the FS value by the function | |||
1364 | /// weights in the base and test profiles for the aggregation. | |||
1365 | double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample, | |||
1366 | uint64_t TestFuncSample) const; | |||
1367 | }; | |||
1368 | } // end anonymous namespace | |||
1369 | ||||
1370 | bool SampleOverlapAggregator::detectZeroSampleProfile( | |||
1371 | raw_fd_ostream &OS) const { | |||
1372 | bool HaveZeroSample = false; | |||
1373 | if (ProfOverlap.BaseSample == 0) { | |||
1374 | OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n"; | |||
1375 | HaveZeroSample = true; | |||
1376 | } | |||
1377 | if (ProfOverlap.TestSample == 0) { | |||
1378 | OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n"; | |||
1379 | HaveZeroSample = true; | |||
1380 | } | |||
1381 | return HaveZeroSample; | |||
1382 | } | |||
1383 | ||||
1384 | double SampleOverlapAggregator::computeBlockSimilarity( | |||
1385 | uint64_t BaseSample, uint64_t TestSample, | |||
1386 | const SampleOverlapStats &FuncOverlap) const { | |||
1387 | double BaseFrac = 0.0; | |||
1388 | double TestFrac = 0.0; | |||
1389 | if (FuncOverlap.BaseSample > 0) | |||
1390 | BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample; | |||
1391 | if (FuncOverlap.TestSample > 0) | |||
1392 | TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample; | |||
1393 | return 1.0 - std::fabs(BaseFrac - TestFrac); | |||
1394 | } | |||
1395 | ||||
1396 | void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample, | |||
1397 | uint64_t TestSample, | |||
1398 | uint64_t HotBlockCount) { | |||
1399 | bool IsBaseHot = (BaseSample >= BaseHotThreshold); | |||
1400 | bool IsTestHot = (TestSample >= TestHotThreshold); | |||
1401 | if (!IsBaseHot && !IsTestHot) | |||
1402 | return; | |||
1403 | ||||
1404 | HotBlockOverlap.UnionCount += HotBlockCount; | |||
1405 | if (IsBaseHot) | |||
1406 | HotBlockOverlap.BaseCount += HotBlockCount; | |||
1407 | if (IsTestHot) | |||
1408 | HotBlockOverlap.TestCount += HotBlockCount; | |||
1409 | if (IsBaseHot && IsTestHot) | |||
1410 | HotBlockOverlap.OverlapCount += HotBlockCount; | |||
1411 | } | |||
1412 | ||||
1413 | void SampleOverlapAggregator::getHotFunctions( | |||
1414 | const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc, | |||
1415 | uint64_t HotThreshold) const { | |||
1416 | for (const auto &F : ProfStats) { | |||
1417 | if (isFunctionHot(F.second, HotThreshold)) | |||
1418 | HotFunc.emplace(F.first, F.second); | |||
1419 | } | |||
1420 | } | |||
1421 | ||||
1422 | void SampleOverlapAggregator::computeHotFuncOverlap() { | |||
1423 | FuncSampleStatsMap BaseHotFunc; | |||
1424 | getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold); | |||
1425 | HotFuncOverlap.BaseCount = BaseHotFunc.size(); | |||
1426 | ||||
1427 | FuncSampleStatsMap TestHotFunc; | |||
1428 | getHotFunctions(TestStats, TestHotFunc, TestHotThreshold); | |||
1429 | HotFuncOverlap.TestCount = TestHotFunc.size(); | |||
1430 | HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount; | |||
1431 | ||||
1432 | for (const auto &F : BaseHotFunc) { | |||
1433 | if (TestHotFunc.count(F.first)) | |||
1434 | ++HotFuncOverlap.OverlapCount; | |||
1435 | else | |||
1436 | ++HotFuncOverlap.UnionCount; | |||
1437 | } | |||
1438 | } | |||
1439 | ||||
1440 | void SampleOverlapAggregator::updateOverlapStatsForFunction( | |||
1441 | uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount, | |||
1442 | SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) { | |||
1443 | assert(Status != MS_None &&(static_cast <bool> (Status != MS_None && "Match status should be updated before updating overlap statistics" ) ? void (0) : __assert_fail ("Status != MS_None && \"Match status should be updated before updating overlap statistics\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1444, __extension__ __PRETTY_FUNCTION__)) | |||
1444 | "Match status should be updated before updating overlap statistics")(static_cast <bool> (Status != MS_None && "Match status should be updated before updating overlap statistics" ) ? void (0) : __assert_fail ("Status != MS_None && \"Match status should be updated before updating overlap statistics\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1444, __extension__ __PRETTY_FUNCTION__)); | |||
1445 | if (Status == MS_FirstUnique) { | |||
1446 | TestSample = 0; | |||
1447 | FuncOverlap.BaseUniqueSample += BaseSample; | |||
1448 | } else if (Status == MS_SecondUnique) { | |||
1449 | BaseSample = 0; | |||
1450 | FuncOverlap.TestUniqueSample += TestSample; | |||
1451 | } else { | |||
1452 | ++FuncOverlap.OverlapCount; | |||
1453 | } | |||
1454 | ||||
1455 | FuncOverlap.UnionSample += std::max(BaseSample, TestSample); | |||
1456 | FuncOverlap.OverlapSample += std::min(BaseSample, TestSample); | |||
1457 | Difference += | |||
1458 | 1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap); | |||
1459 | updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount); | |||
1460 | } | |||
1461 | ||||
1462 | void SampleOverlapAggregator::updateForUnmatchedCallee( | |||
1463 | const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap, | |||
1464 | double &Difference, MatchStatus Status) { | |||
1465 | assert((Status == MS_FirstUnique || Status == MS_SecondUnique) &&(static_cast <bool> ((Status == MS_FirstUnique || Status == MS_SecondUnique) && "Status must be either of the two unmatched cases" ) ? void (0) : __assert_fail ("(Status == MS_FirstUnique || Status == MS_SecondUnique) && \"Status must be either of the two unmatched cases\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1466, __extension__ __PRETTY_FUNCTION__)) | |||
1466 | "Status must be either of the two unmatched cases")(static_cast <bool> ((Status == MS_FirstUnique || Status == MS_SecondUnique) && "Status must be either of the two unmatched cases" ) ? void (0) : __assert_fail ("(Status == MS_FirstUnique || Status == MS_SecondUnique) && \"Status must be either of the two unmatched cases\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1466, __extension__ __PRETTY_FUNCTION__)); | |||
1467 | FuncSampleStats FuncStats; | |||
1468 | if (Status == MS_FirstUnique) { | |||
1469 | getFuncSampleStats(Func, FuncStats, BaseHotThreshold); | |||
1470 | updateOverlapStatsForFunction(FuncStats.SampleSum, 0, | |||
1471 | FuncStats.HotBlockCount, FuncOverlap, | |||
1472 | Difference, Status); | |||
1473 | } else { | |||
1474 | getFuncSampleStats(Func, FuncStats, TestHotThreshold); | |||
1475 | updateOverlapStatsForFunction(0, FuncStats.SampleSum, | |||
1476 | FuncStats.HotBlockCount, FuncOverlap, | |||
1477 | Difference, Status); | |||
1478 | } | |||
1479 | } | |||
1480 | ||||
1481 | double SampleOverlapAggregator::computeSampleFunctionInternalOverlap( | |||
1482 | const sampleprof::FunctionSamples &BaseFunc, | |||
1483 | const sampleprof::FunctionSamples &TestFunc, | |||
1484 | SampleOverlapStats &FuncOverlap) { | |||
1485 | ||||
1486 | using namespace sampleprof; | |||
1487 | ||||
1488 | double Difference = 0; | |||
1489 | ||||
1490 | // Accumulate Difference for regular line/block samples in the function. | |||
1491 | // We match them through sort-merge join algorithm because | |||
1492 | // FunctionSamples::getBodySamples() returns a map of sample counters ordered | |||
1493 | // by their offsets. | |||
1494 | MatchStep<BodySampleMap::const_iterator> BlockIterStep( | |||
1495 | BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(), | |||
1496 | TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend()); | |||
1497 | BlockIterStep.updateOneStep(); | |||
1498 | while (!BlockIterStep.areBothFinished()) { | |||
1499 | uint64_t BaseSample = | |||
1500 | BlockIterStep.isFirstFinished() | |||
1501 | ? 0 | |||
1502 | : BlockIterStep.getFirstIter()->second.getSamples(); | |||
1503 | uint64_t TestSample = | |||
1504 | BlockIterStep.isSecondFinished() | |||
1505 | ? 0 | |||
1506 | : BlockIterStep.getSecondIter()->second.getSamples(); | |||
1507 | updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap, | |||
1508 | Difference, BlockIterStep.getMatchStatus()); | |||
1509 | ||||
1510 | BlockIterStep.updateOneStep(); | |||
1511 | } | |||
1512 | ||||
1513 | // Accumulate Difference for callsite lines in the function. We match | |||
1514 | // them through sort-merge algorithm because | |||
1515 | // FunctionSamples::getCallsiteSamples() returns a map of callsite records | |||
1516 | // ordered by their offsets. | |||
1517 | MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep( | |||
1518 | BaseFunc.getCallsiteSamples().cbegin(), | |||
1519 | BaseFunc.getCallsiteSamples().cend(), | |||
1520 | TestFunc.getCallsiteSamples().cbegin(), | |||
1521 | TestFunc.getCallsiteSamples().cend()); | |||
1522 | CallsiteIterStep.updateOneStep(); | |||
1523 | while (!CallsiteIterStep.areBothFinished()) { | |||
1524 | MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus(); | |||
1525 | assert(CallsiteStepStatus != MS_None &&(static_cast <bool> (CallsiteStepStatus != MS_None && "Match status should be updated before entering loop body") ? void (0) : __assert_fail ("CallsiteStepStatus != MS_None && \"Match status should be updated before entering loop body\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1526, __extension__ __PRETTY_FUNCTION__)) | |||
1526 | "Match status should be updated before entering loop body")(static_cast <bool> (CallsiteStepStatus != MS_None && "Match status should be updated before entering loop body") ? void (0) : __assert_fail ("CallsiteStepStatus != MS_None && \"Match status should be updated before entering loop body\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1526, __extension__ __PRETTY_FUNCTION__)); | |||
1527 | ||||
1528 | if (CallsiteStepStatus != MS_Match) { | |||
1529 | auto Callsite = (CallsiteStepStatus == MS_FirstUnique) | |||
1530 | ? CallsiteIterStep.getFirstIter() | |||
1531 | : CallsiteIterStep.getSecondIter(); | |||
1532 | for (const auto &F : Callsite->second) | |||
1533 | updateForUnmatchedCallee(F.second, FuncOverlap, Difference, | |||
1534 | CallsiteStepStatus); | |||
1535 | } else { | |||
1536 | // There may be multiple inlinees at the same offset, so we need to try | |||
1537 | // matching all of them. This match is implemented through sort-merge | |||
1538 | // algorithm because callsite records at the same offset are ordered by | |||
1539 | // function names. | |||
1540 | MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep( | |||
1541 | CallsiteIterStep.getFirstIter()->second.cbegin(), | |||
1542 | CallsiteIterStep.getFirstIter()->second.cend(), | |||
1543 | CallsiteIterStep.getSecondIter()->second.cbegin(), | |||
1544 | CallsiteIterStep.getSecondIter()->second.cend()); | |||
1545 | CalleeIterStep.updateOneStep(); | |||
1546 | while (!CalleeIterStep.areBothFinished()) { | |||
1547 | MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus(); | |||
1548 | if (CalleeStepStatus != MS_Match) { | |||
1549 | auto Callee = (CalleeStepStatus == MS_FirstUnique) | |||
1550 | ? CalleeIterStep.getFirstIter() | |||
1551 | : CalleeIterStep.getSecondIter(); | |||
1552 | updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference, | |||
1553 | CalleeStepStatus); | |||
1554 | } else { | |||
1555 | // An inlined function can contain other inlinees inside, so compute | |||
1556 | // the Difference recursively. | |||
1557 | Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap( | |||
1558 | CalleeIterStep.getFirstIter()->second, | |||
1559 | CalleeIterStep.getSecondIter()->second, | |||
1560 | FuncOverlap); | |||
1561 | } | |||
1562 | CalleeIterStep.updateOneStep(); | |||
1563 | } | |||
1564 | } | |||
1565 | CallsiteIterStep.updateOneStep(); | |||
1566 | } | |||
1567 | ||||
1568 | // Difference reflects the total differences of line/block samples in this | |||
1569 | // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to | |||
1570 | // reflect the similarity between function profiles in [0.0f to 1.0f]. | |||
1571 | return (2.0 - Difference) / 2; | |||
1572 | } | |||
1573 | ||||
1574 | double SampleOverlapAggregator::weightForFuncSimilarity( | |||
1575 | double FuncInternalSimilarity, uint64_t BaseFuncSample, | |||
1576 | uint64_t TestFuncSample) const { | |||
1577 | // Compute the weight as the distance between the function weights in two | |||
1578 | // profiles. | |||
1579 | double BaseFrac = 0.0; | |||
1580 | double TestFrac = 0.0; | |||
1581 | assert(ProfOverlap.BaseSample > 0 &&(static_cast <bool> (ProfOverlap.BaseSample > 0 && "Total samples in base profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.BaseSample > 0 && \"Total samples in base profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1582, __extension__ __PRETTY_FUNCTION__)) | |||
1582 | "Total samples in base profile should be greater than 0")(static_cast <bool> (ProfOverlap.BaseSample > 0 && "Total samples in base profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.BaseSample > 0 && \"Total samples in base profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1582, __extension__ __PRETTY_FUNCTION__)); | |||
1583 | BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample; | |||
1584 | assert(ProfOverlap.TestSample > 0 &&(static_cast <bool> (ProfOverlap.TestSample > 0 && "Total samples in test profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.TestSample > 0 && \"Total samples in test profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1585, __extension__ __PRETTY_FUNCTION__)) | |||
1585 | "Total samples in test profile should be greater than 0")(static_cast <bool> (ProfOverlap.TestSample > 0 && "Total samples in test profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.TestSample > 0 && \"Total samples in test profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1585, __extension__ __PRETTY_FUNCTION__)); | |||
1586 | TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample; | |||
1587 | double WeightDistance = std::fabs(BaseFrac - TestFrac); | |||
1588 | ||||
1589 | // Take WeightDistance into the similarity. | |||
1590 | return FuncInternalSimilarity * (1 - WeightDistance); | |||
1591 | } | |||
1592 | ||||
1593 | double | |||
1594 | SampleOverlapAggregator::weightByImportance(double FuncSimilarity, | |||
1595 | uint64_t BaseFuncSample, | |||
1596 | uint64_t TestFuncSample) const { | |||
1597 | ||||
1598 | double BaseFrac = 0.0; | |||
1599 | double TestFrac = 0.0; | |||
1600 | assert(ProfOverlap.BaseSample > 0 &&(static_cast <bool> (ProfOverlap.BaseSample > 0 && "Total samples in base profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.BaseSample > 0 && \"Total samples in base profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1601, __extension__ __PRETTY_FUNCTION__)) | |||
1601 | "Total samples in base profile should be greater than 0")(static_cast <bool> (ProfOverlap.BaseSample > 0 && "Total samples in base profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.BaseSample > 0 && \"Total samples in base profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1601, __extension__ __PRETTY_FUNCTION__)); | |||
1602 | BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0; | |||
1603 | assert(ProfOverlap.TestSample > 0 &&(static_cast <bool> (ProfOverlap.TestSample > 0 && "Total samples in test profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.TestSample > 0 && \"Total samples in test profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1604, __extension__ __PRETTY_FUNCTION__)) | |||
1604 | "Total samples in test profile should be greater than 0")(static_cast <bool> (ProfOverlap.TestSample > 0 && "Total samples in test profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.TestSample > 0 && \"Total samples in test profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1604, __extension__ __PRETTY_FUNCTION__)); | |||
1605 | TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0; | |||
1606 | return FuncSimilarity * (BaseFrac + TestFrac); | |||
1607 | } | |||
1608 | ||||
1609 | double SampleOverlapAggregator::computeSampleFunctionOverlap( | |||
1610 | const sampleprof::FunctionSamples *BaseFunc, | |||
1611 | const sampleprof::FunctionSamples *TestFunc, | |||
1612 | SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample, | |||
1613 | uint64_t TestFuncSample) { | |||
1614 | // Default function internal similarity before weighted, meaning two functions | |||
1615 | // has no overlap. | |||
1616 | const double DefaultFuncInternalSimilarity = 0; | |||
1617 | double FuncSimilarity; | |||
1618 | double FuncInternalSimilarity; | |||
1619 | ||||
1620 | // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap. | |||
1621 | // In this case, we use DefaultFuncInternalSimilarity as the function internal | |||
1622 | // similarity. | |||
1623 | if (!BaseFunc || !TestFunc) { | |||
1624 | FuncInternalSimilarity = DefaultFuncInternalSimilarity; | |||
1625 | } else { | |||
1626 | assert(FuncOverlap != nullptr &&(static_cast <bool> (FuncOverlap != nullptr && "FuncOverlap should be provided in this case" ) ? void (0) : __assert_fail ("FuncOverlap != nullptr && \"FuncOverlap should be provided in this case\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1627, __extension__ __PRETTY_FUNCTION__)) | |||
1627 | "FuncOverlap should be provided in this case")(static_cast <bool> (FuncOverlap != nullptr && "FuncOverlap should be provided in this case" ) ? void (0) : __assert_fail ("FuncOverlap != nullptr && \"FuncOverlap should be provided in this case\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1627, __extension__ __PRETTY_FUNCTION__)); | |||
1628 | FuncInternalSimilarity = computeSampleFunctionInternalOverlap( | |||
1629 | *BaseFunc, *TestFunc, *FuncOverlap); | |||
1630 | // Now, FuncInternalSimilarity may be a little less than 0 due to | |||
1631 | // imprecision of floating point accumulations. Make it zero if the | |||
1632 | // difference is below Epsilon. | |||
1633 | FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon) | |||
1634 | ? 0 | |||
1635 | : FuncInternalSimilarity; | |||
1636 | } | |||
1637 | FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity, | |||
1638 | BaseFuncSample, TestFuncSample); | |||
1639 | return FuncSimilarity; | |||
1640 | } | |||
1641 | ||||
1642 | void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) { | |||
1643 | using namespace sampleprof; | |||
1644 | ||||
1645 | std::unordered_map<SampleContext, const FunctionSamples *, | |||
1646 | SampleContext::Hash> | |||
1647 | BaseFuncProf; | |||
1648 | const auto &BaseProfiles = BaseReader->getProfiles(); | |||
1649 | for (const auto &BaseFunc : BaseProfiles) { | |||
1650 | BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second)); | |||
1651 | } | |||
1652 | ProfOverlap.UnionCount = BaseFuncProf.size(); | |||
1653 | ||||
1654 | const auto &TestProfiles = TestReader->getProfiles(); | |||
1655 | for (const auto &TestFunc : TestProfiles) { | |||
1656 | SampleOverlapStats FuncOverlap; | |||
1657 | FuncOverlap.TestName = TestFunc.second.getContext(); | |||
1658 | assert(TestStats.count(FuncOverlap.TestName) &&(static_cast <bool> (TestStats.count(FuncOverlap.TestName ) && "TestStats should have records for all functions in test profile " "except inlinees") ? void (0) : __assert_fail ("TestStats.count(FuncOverlap.TestName) && \"TestStats should have records for all functions in test profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1660, __extension__ __PRETTY_FUNCTION__)) | |||
1659 | "TestStats should have records for all functions in test profile "(static_cast <bool> (TestStats.count(FuncOverlap.TestName ) && "TestStats should have records for all functions in test profile " "except inlinees") ? void (0) : __assert_fail ("TestStats.count(FuncOverlap.TestName) && \"TestStats should have records for all functions in test profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1660, __extension__ __PRETTY_FUNCTION__)) | |||
1660 | "except inlinees")(static_cast <bool> (TestStats.count(FuncOverlap.TestName ) && "TestStats should have records for all functions in test profile " "except inlinees") ? void (0) : __assert_fail ("TestStats.count(FuncOverlap.TestName) && \"TestStats should have records for all functions in test profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1660, __extension__ __PRETTY_FUNCTION__)); | |||
1661 | FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum; | |||
1662 | ||||
1663 | bool Matched = false; | |||
1664 | const auto Match = BaseFuncProf.find(FuncOverlap.TestName); | |||
1665 | if (Match == BaseFuncProf.end()) { | |||
1666 | const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName]; | |||
1667 | ++ProfOverlap.TestUniqueCount; | |||
1668 | ProfOverlap.TestUniqueSample += FuncStats.SampleSum; | |||
1669 | FuncOverlap.TestUniqueSample = FuncStats.SampleSum; | |||
1670 | ||||
1671 | updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount); | |||
1672 | ||||
1673 | double FuncSimilarity = computeSampleFunctionOverlap( | |||
1674 | nullptr, nullptr, nullptr, 0, FuncStats.SampleSum); | |||
1675 | ProfOverlap.Similarity += | |||
1676 | weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum); | |||
1677 | ||||
1678 | ++ProfOverlap.UnionCount; | |||
1679 | ProfOverlap.UnionSample += FuncStats.SampleSum; | |||
1680 | } else { | |||
1681 | ++ProfOverlap.OverlapCount; | |||
1682 | ||||
1683 | // Two functions match with each other. Compute function-level overlap and | |||
1684 | // aggregate them into profile-level overlap. | |||
1685 | FuncOverlap.BaseName = Match->second->getContext(); | |||
1686 | assert(BaseStats.count(FuncOverlap.BaseName) &&(static_cast <bool> (BaseStats.count(FuncOverlap.BaseName ) && "BaseStats should have records for all functions in base profile " "except inlinees") ? void (0) : __assert_fail ("BaseStats.count(FuncOverlap.BaseName) && \"BaseStats should have records for all functions in base profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1688, __extension__ __PRETTY_FUNCTION__)) | |||
1687 | "BaseStats should have records for all functions in base profile "(static_cast <bool> (BaseStats.count(FuncOverlap.BaseName ) && "BaseStats should have records for all functions in base profile " "except inlinees") ? void (0) : __assert_fail ("BaseStats.count(FuncOverlap.BaseName) && \"BaseStats should have records for all functions in base profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1688, __extension__ __PRETTY_FUNCTION__)) | |||
1688 | "except inlinees")(static_cast <bool> (BaseStats.count(FuncOverlap.BaseName ) && "BaseStats should have records for all functions in base profile " "except inlinees") ? void (0) : __assert_fail ("BaseStats.count(FuncOverlap.BaseName) && \"BaseStats should have records for all functions in base profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1688, __extension__ __PRETTY_FUNCTION__)); | |||
1689 | FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum; | |||
1690 | ||||
1691 | FuncOverlap.Similarity = computeSampleFunctionOverlap( | |||
1692 | Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample, | |||
1693 | FuncOverlap.TestSample); | |||
1694 | ProfOverlap.Similarity += | |||
1695 | weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample, | |||
1696 | FuncOverlap.TestSample); | |||
1697 | ProfOverlap.OverlapSample += FuncOverlap.OverlapSample; | |||
1698 | ProfOverlap.UnionSample += FuncOverlap.UnionSample; | |||
1699 | ||||
1700 | // Accumulate the percentage of base unique and test unique samples into | |||
1701 | // ProfOverlap. | |||
1702 | ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample; | |||
1703 | ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample; | |||
1704 | ||||
1705 | // Remove matched base functions for later reporting functions not found | |||
1706 | // in test profile. | |||
1707 | BaseFuncProf.erase(Match); | |||
1708 | Matched = true; | |||
1709 | } | |||
1710 | ||||
1711 | // Print function-level similarity information if specified by options. | |||
1712 | assert(TestStats.count(FuncOverlap.TestName) &&(static_cast <bool> (TestStats.count(FuncOverlap.TestName ) && "TestStats should have records for all functions in test profile " "except inlinees") ? void (0) : __assert_fail ("TestStats.count(FuncOverlap.TestName) && \"TestStats should have records for all functions in test profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1714, __extension__ __PRETTY_FUNCTION__)) | |||
1713 | "TestStats should have records for all functions in test profile "(static_cast <bool> (TestStats.count(FuncOverlap.TestName ) && "TestStats should have records for all functions in test profile " "except inlinees") ? void (0) : __assert_fail ("TestStats.count(FuncOverlap.TestName) && \"TestStats should have records for all functions in test profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1714, __extension__ __PRETTY_FUNCTION__)) | |||
1714 | "except inlinees")(static_cast <bool> (TestStats.count(FuncOverlap.TestName ) && "TestStats should have records for all functions in test profile " "except inlinees") ? void (0) : __assert_fail ("TestStats.count(FuncOverlap.TestName) && \"TestStats should have records for all functions in test profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1714, __extension__ __PRETTY_FUNCTION__)); | |||
1715 | if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff || | |||
1716 | (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) || | |||
1717 | (Matched && !FuncFilter.NameFilter.empty() && | |||
1718 | FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) != | |||
1719 | std::string::npos)) { | |||
1720 | assert(ProfOverlap.BaseSample > 0 &&(static_cast <bool> (ProfOverlap.BaseSample > 0 && "Total samples in base profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.BaseSample > 0 && \"Total samples in base profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1721, __extension__ __PRETTY_FUNCTION__)) | |||
1721 | "Total samples in base profile should be greater than 0")(static_cast <bool> (ProfOverlap.BaseSample > 0 && "Total samples in base profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.BaseSample > 0 && \"Total samples in base profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1721, __extension__ __PRETTY_FUNCTION__)); | |||
1722 | FuncOverlap.BaseWeight = | |||
1723 | static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample; | |||
1724 | assert(ProfOverlap.TestSample > 0 &&(static_cast <bool> (ProfOverlap.TestSample > 0 && "Total samples in test profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.TestSample > 0 && \"Total samples in test profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1725, __extension__ __PRETTY_FUNCTION__)) | |||
1725 | "Total samples in test profile should be greater than 0")(static_cast <bool> (ProfOverlap.TestSample > 0 && "Total samples in test profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.TestSample > 0 && \"Total samples in test profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1725, __extension__ __PRETTY_FUNCTION__)); | |||
1726 | FuncOverlap.TestWeight = | |||
1727 | static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample; | |||
1728 | FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap); | |||
1729 | } | |||
1730 | } | |||
1731 | ||||
1732 | // Traverse through functions in base profile but not in test profile. | |||
1733 | for (const auto &F : BaseFuncProf) { | |||
1734 | assert(BaseStats.count(F.second->getContext()) &&(static_cast <bool> (BaseStats.count(F.second->getContext ()) && "BaseStats should have records for all functions in base profile " "except inlinees") ? void (0) : __assert_fail ("BaseStats.count(F.second->getContext()) && \"BaseStats should have records for all functions in base profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1736, __extension__ __PRETTY_FUNCTION__)) | |||
1735 | "BaseStats should have records for all functions in base profile "(static_cast <bool> (BaseStats.count(F.second->getContext ()) && "BaseStats should have records for all functions in base profile " "except inlinees") ? void (0) : __assert_fail ("BaseStats.count(F.second->getContext()) && \"BaseStats should have records for all functions in base profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1736, __extension__ __PRETTY_FUNCTION__)) | |||
1736 | "except inlinees")(static_cast <bool> (BaseStats.count(F.second->getContext ()) && "BaseStats should have records for all functions in base profile " "except inlinees") ? void (0) : __assert_fail ("BaseStats.count(F.second->getContext()) && \"BaseStats should have records for all functions in base profile \" \"except inlinees\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1736, __extension__ __PRETTY_FUNCTION__)); | |||
1737 | const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()]; | |||
1738 | ++ProfOverlap.BaseUniqueCount; | |||
1739 | ProfOverlap.BaseUniqueSample += FuncStats.SampleSum; | |||
1740 | ||||
1741 | updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount); | |||
1742 | ||||
1743 | double FuncSimilarity = computeSampleFunctionOverlap( | |||
1744 | nullptr, nullptr, nullptr, FuncStats.SampleSum, 0); | |||
1745 | ProfOverlap.Similarity += | |||
1746 | weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0); | |||
1747 | ||||
1748 | ProfOverlap.UnionSample += FuncStats.SampleSum; | |||
1749 | } | |||
1750 | ||||
1751 | // Now, ProfSimilarity may be a little greater than 1 due to imprecision | |||
1752 | // of floating point accumulations. Make it 1.0 if the difference is below | |||
1753 | // Epsilon. | |||
1754 | ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon) | |||
1755 | ? 1 | |||
1756 | : ProfOverlap.Similarity; | |||
1757 | ||||
1758 | computeHotFuncOverlap(); | |||
1759 | } | |||
1760 | ||||
1761 | void SampleOverlapAggregator::initializeSampleProfileOverlap() { | |||
1762 | const auto &BaseProf = BaseReader->getProfiles(); | |||
1763 | for (const auto &I : BaseProf) { | |||
1764 | ++ProfOverlap.BaseCount; | |||
1765 | FuncSampleStats FuncStats; | |||
1766 | getFuncSampleStats(I.second, FuncStats, BaseHotThreshold); | |||
1767 | ProfOverlap.BaseSample += FuncStats.SampleSum; | |||
1768 | BaseStats.emplace(I.second.getContext(), FuncStats); | |||
1769 | } | |||
1770 | ||||
1771 | const auto &TestProf = TestReader->getProfiles(); | |||
1772 | for (const auto &I : TestProf) { | |||
1773 | ++ProfOverlap.TestCount; | |||
1774 | FuncSampleStats FuncStats; | |||
1775 | getFuncSampleStats(I.second, FuncStats, TestHotThreshold); | |||
1776 | ProfOverlap.TestSample += FuncStats.SampleSum; | |||
1777 | TestStats.emplace(I.second.getContext(), FuncStats); | |||
1778 | } | |||
1779 | ||||
1780 | ProfOverlap.BaseName = StringRef(BaseFilename); | |||
1781 | ProfOverlap.TestName = StringRef(TestFilename); | |||
1782 | } | |||
1783 | ||||
1784 | void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const { | |||
1785 | using namespace sampleprof; | |||
1786 | ||||
1787 | if (FuncSimilarityDump.empty()) | |||
1788 | return; | |||
1789 | ||||
1790 | formatted_raw_ostream FOS(OS); | |||
1791 | FOS << "Function-level details:\n"; | |||
1792 | FOS << "Base weight"; | |||
1793 | FOS.PadToColumn(TestWeightCol); | |||
1794 | FOS << "Test weight"; | |||
1795 | FOS.PadToColumn(SimilarityCol); | |||
1796 | FOS << "Similarity"; | |||
1797 | FOS.PadToColumn(OverlapCol); | |||
1798 | FOS << "Overlap"; | |||
1799 | FOS.PadToColumn(BaseUniqueCol); | |||
1800 | FOS << "Base unique"; | |||
1801 | FOS.PadToColumn(TestUniqueCol); | |||
1802 | FOS << "Test unique"; | |||
1803 | FOS.PadToColumn(BaseSampleCol); | |||
1804 | FOS << "Base samples"; | |||
1805 | FOS.PadToColumn(TestSampleCol); | |||
1806 | FOS << "Test samples"; | |||
1807 | FOS.PadToColumn(FuncNameCol); | |||
1808 | FOS << "Function name\n"; | |||
1809 | for (const auto &F : FuncSimilarityDump) { | |||
1810 | double OverlapPercent = | |||
1811 | F.second.UnionSample > 0 | |||
1812 | ? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample | |||
1813 | : 0; | |||
1814 | double BaseUniquePercent = | |||
1815 | F.second.BaseSample > 0 | |||
1816 | ? static_cast<double>(F.second.BaseUniqueSample) / | |||
1817 | F.second.BaseSample | |||
1818 | : 0; | |||
1819 | double TestUniquePercent = | |||
1820 | F.second.TestSample > 0 | |||
1821 | ? static_cast<double>(F.second.TestUniqueSample) / | |||
1822 | F.second.TestSample | |||
1823 | : 0; | |||
1824 | ||||
1825 | FOS << format("%.2f%%", F.second.BaseWeight * 100); | |||
1826 | FOS.PadToColumn(TestWeightCol); | |||
1827 | FOS << format("%.2f%%", F.second.TestWeight * 100); | |||
1828 | FOS.PadToColumn(SimilarityCol); | |||
1829 | FOS << format("%.2f%%", F.second.Similarity * 100); | |||
1830 | FOS.PadToColumn(OverlapCol); | |||
1831 | FOS << format("%.2f%%", OverlapPercent * 100); | |||
1832 | FOS.PadToColumn(BaseUniqueCol); | |||
1833 | FOS << format("%.2f%%", BaseUniquePercent * 100); | |||
1834 | FOS.PadToColumn(TestUniqueCol); | |||
1835 | FOS << format("%.2f%%", TestUniquePercent * 100); | |||
1836 | FOS.PadToColumn(BaseSampleCol); | |||
1837 | FOS << F.second.BaseSample; | |||
1838 | FOS.PadToColumn(TestSampleCol); | |||
1839 | FOS << F.second.TestSample; | |||
1840 | FOS.PadToColumn(FuncNameCol); | |||
1841 | FOS << F.second.TestName.toString() << "\n"; | |||
1842 | } | |||
1843 | } | |||
1844 | ||||
1845 | void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const { | |||
1846 | OS << "Profile overlap infomation for base_profile: " | |||
1847 | << ProfOverlap.BaseName.toString() | |||
1848 | << " and test_profile: " << ProfOverlap.TestName.toString() | |||
1849 | << "\nProgram level:\n"; | |||
1850 | ||||
1851 | OS << " Whole program profile similarity: " | |||
1852 | << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n"; | |||
1853 | ||||
1854 | assert(ProfOverlap.UnionSample > 0 &&(static_cast <bool> (ProfOverlap.UnionSample > 0 && "Total samples in two profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.UnionSample > 0 && \"Total samples in two profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1855, __extension__ __PRETTY_FUNCTION__)) | |||
1855 | "Total samples in two profile should be greater than 0")(static_cast <bool> (ProfOverlap.UnionSample > 0 && "Total samples in two profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.UnionSample > 0 && \"Total samples in two profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1855, __extension__ __PRETTY_FUNCTION__)); | |||
1856 | double OverlapPercent = | |||
1857 | static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample; | |||
1858 | assert(ProfOverlap.BaseSample > 0 &&(static_cast <bool> (ProfOverlap.BaseSample > 0 && "Total samples in base profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.BaseSample > 0 && \"Total samples in base profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1859, __extension__ __PRETTY_FUNCTION__)) | |||
1859 | "Total samples in base profile should be greater than 0")(static_cast <bool> (ProfOverlap.BaseSample > 0 && "Total samples in base profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.BaseSample > 0 && \"Total samples in base profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1859, __extension__ __PRETTY_FUNCTION__)); | |||
1860 | double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) / | |||
1861 | ProfOverlap.BaseSample; | |||
1862 | assert(ProfOverlap.TestSample > 0 &&(static_cast <bool> (ProfOverlap.TestSample > 0 && "Total samples in test profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.TestSample > 0 && \"Total samples in test profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1863, __extension__ __PRETTY_FUNCTION__)) | |||
1863 | "Total samples in test profile should be greater than 0")(static_cast <bool> (ProfOverlap.TestSample > 0 && "Total samples in test profile should be greater than 0") ? void (0) : __assert_fail ("ProfOverlap.TestSample > 0 && \"Total samples in test profile should be greater than 0\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1863, __extension__ __PRETTY_FUNCTION__)); | |||
1864 | double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) / | |||
1865 | ProfOverlap.TestSample; | |||
1866 | ||||
1867 | OS << " Whole program sample overlap: " | |||
1868 | << format("%.3f%%", OverlapPercent * 100) << "\n"; | |||
1869 | OS << " percentage of samples unique in base profile: " | |||
1870 | << format("%.3f%%", BaseUniquePercent * 100) << "\n"; | |||
1871 | OS << " percentage of samples unique in test profile: " | |||
1872 | << format("%.3f%%", TestUniquePercent * 100) << "\n"; | |||
1873 | OS << " total samples in base profile: " << ProfOverlap.BaseSample << "\n" | |||
1874 | << " total samples in test profile: " << ProfOverlap.TestSample << "\n"; | |||
1875 | ||||
1876 | assert(ProfOverlap.UnionCount > 0 &&(static_cast <bool> (ProfOverlap.UnionCount > 0 && "There should be at least one function in two input profiles" ) ? void (0) : __assert_fail ("ProfOverlap.UnionCount > 0 && \"There should be at least one function in two input profiles\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1877, __extension__ __PRETTY_FUNCTION__)) | |||
1877 | "There should be at least one function in two input profiles")(static_cast <bool> (ProfOverlap.UnionCount > 0 && "There should be at least one function in two input profiles" ) ? void (0) : __assert_fail ("ProfOverlap.UnionCount > 0 && \"There should be at least one function in two input profiles\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1877, __extension__ __PRETTY_FUNCTION__)); | |||
1878 | double FuncOverlapPercent = | |||
1879 | static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount; | |||
1880 | OS << " Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100) | |||
1881 | << "\n"; | |||
1882 | OS << " overlap functions: " << ProfOverlap.OverlapCount << "\n"; | |||
1883 | OS << " functions unique in base profile: " << ProfOverlap.BaseUniqueCount | |||
1884 | << "\n"; | |||
1885 | OS << " functions unique in test profile: " << ProfOverlap.TestUniqueCount | |||
1886 | << "\n"; | |||
1887 | } | |||
1888 | ||||
1889 | void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap( | |||
1890 | raw_fd_ostream &OS) const { | |||
1891 | assert(HotFuncOverlap.UnionCount > 0 &&(static_cast <bool> (HotFuncOverlap.UnionCount > 0 && "There should be at least one hot function in two input profiles" ) ? void (0) : __assert_fail ("HotFuncOverlap.UnionCount > 0 && \"There should be at least one hot function in two input profiles\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1892, __extension__ __PRETTY_FUNCTION__)) | |||
1892 | "There should be at least one hot function in two input profiles")(static_cast <bool> (HotFuncOverlap.UnionCount > 0 && "There should be at least one hot function in two input profiles" ) ? void (0) : __assert_fail ("HotFuncOverlap.UnionCount > 0 && \"There should be at least one hot function in two input profiles\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1892, __extension__ __PRETTY_FUNCTION__)); | |||
1893 | OS << " Hot-function overlap: " | |||
1894 | << format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) / | |||
1895 | HotFuncOverlap.UnionCount * 100) | |||
1896 | << "\n"; | |||
1897 | OS << " overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n"; | |||
1898 | OS << " hot functions unique in base profile: " | |||
1899 | << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n"; | |||
1900 | OS << " hot functions unique in test profile: " | |||
1901 | << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n"; | |||
1902 | ||||
1903 | assert(HotBlockOverlap.UnionCount > 0 &&(static_cast <bool> (HotBlockOverlap.UnionCount > 0 && "There should be at least one hot block in two input profiles" ) ? void (0) : __assert_fail ("HotBlockOverlap.UnionCount > 0 && \"There should be at least one hot block in two input profiles\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1904, __extension__ __PRETTY_FUNCTION__)) | |||
1904 | "There should be at least one hot block in two input profiles")(static_cast <bool> (HotBlockOverlap.UnionCount > 0 && "There should be at least one hot block in two input profiles" ) ? void (0) : __assert_fail ("HotBlockOverlap.UnionCount > 0 && \"There should be at least one hot block in two input profiles\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 1904, __extension__ __PRETTY_FUNCTION__)); | |||
1905 | OS << " Hot-block overlap: " | |||
1906 | << format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) / | |||
1907 | HotBlockOverlap.UnionCount * 100) | |||
1908 | << "\n"; | |||
1909 | OS << " overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n"; | |||
1910 | OS << " hot blocks unique in base profile: " | |||
1911 | << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n"; | |||
1912 | OS << " hot blocks unique in test profile: " | |||
1913 | << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n"; | |||
1914 | } | |||
1915 | ||||
1916 | std::error_code SampleOverlapAggregator::loadProfiles() { | |||
1917 | using namespace sampleprof; | |||
1918 | ||||
1919 | LLVMContext Context; | |||
1920 | auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, | |||
1921 | FSDiscriminatorPassOption); | |||
1922 | if (std::error_code EC = BaseReaderOrErr.getError()) | |||
1923 | exitWithErrorCode(EC, BaseFilename); | |||
1924 | ||||
1925 | auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, | |||
1926 | FSDiscriminatorPassOption); | |||
1927 | if (std::error_code EC = TestReaderOrErr.getError()) | |||
1928 | exitWithErrorCode(EC, TestFilename); | |||
1929 | ||||
1930 | BaseReader = std::move(BaseReaderOrErr.get()); | |||
1931 | TestReader = std::move(TestReaderOrErr.get()); | |||
1932 | ||||
1933 | if (std::error_code EC = BaseReader->read()) | |||
1934 | exitWithErrorCode(EC, BaseFilename); | |||
1935 | if (std::error_code EC = TestReader->read()) | |||
1936 | exitWithErrorCode(EC, TestFilename); | |||
1937 | if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased()) | |||
1938 | exitWithError( | |||
1939 | "cannot compare probe-based profile with non-probe-based profile"); | |||
1940 | if (BaseReader->profileIsCSFlat() != TestReader->profileIsCSFlat()) | |||
1941 | exitWithError("cannot compare CS profile with non-CS profile"); | |||
1942 | ||||
1943 | // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in | |||
1944 | // profile summary. | |||
1945 | ProfileSummary &BasePS = BaseReader->getSummary(); | |||
1946 | ProfileSummary &TestPS = TestReader->getSummary(); | |||
1947 | BaseHotThreshold = | |||
1948 | ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary()); | |||
1949 | TestHotThreshold = | |||
1950 | ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary()); | |||
1951 | ||||
1952 | return std::error_code(); | |||
1953 | } | |||
1954 | ||||
1955 | void overlapSampleProfile(const std::string &BaseFilename, | |||
1956 | const std::string &TestFilename, | |||
1957 | const OverlapFuncFilters &FuncFilter, | |||
1958 | uint64_t SimilarityCutoff, raw_fd_ostream &OS) { | |||
1959 | using namespace sampleprof; | |||
1960 | ||||
1961 | // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics | |||
1962 | // report 2--3 places after decimal point in percentage numbers. | |||
1963 | SampleOverlapAggregator OverlapAggr( | |||
1964 | BaseFilename, TestFilename, | |||
1965 | static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter); | |||
1966 | if (std::error_code EC = OverlapAggr.loadProfiles()) | |||
1967 | exitWithErrorCode(EC); | |||
1968 | ||||
1969 | OverlapAggr.initializeSampleProfileOverlap(); | |||
1970 | if (OverlapAggr.detectZeroSampleProfile(OS)) | |||
1971 | return; | |||
1972 | ||||
1973 | OverlapAggr.computeSampleProfileOverlap(OS); | |||
1974 | ||||
1975 | OverlapAggr.dumpProgramSummary(OS); | |||
1976 | OverlapAggr.dumpHotFuncAndBlockOverlap(OS); | |||
1977 | OverlapAggr.dumpFuncSimilarity(OS); | |||
1978 | } | |||
1979 | ||||
1980 | static int overlap_main(int argc, const char *argv[]) { | |||
1981 | cl::opt<std::string> BaseFilename(cl::Positional, cl::Required, | |||
1982 | cl::desc("<base profile file>")); | |||
1983 | cl::opt<std::string> TestFilename(cl::Positional, cl::Required, | |||
1984 | cl::desc("<test profile file>")); | |||
1985 | cl::opt<std::string> Output("output", cl::value_desc("output"), cl::init("-"), | |||
1986 | cl::desc("Output file")); | |||
1987 | cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); | |||
1988 | cl::opt<bool> IsCS( | |||
1989 | "cs", cl::init(false), | |||
1990 | cl::desc("For context sensitive PGO counts. Does not work with CSSPGO.")); | |||
1991 | cl::opt<unsigned long long> ValueCutoff( | |||
1992 | "value-cutoff", cl::init(-1), | |||
1993 | cl::desc( | |||
1994 | "Function level overlap information for every function (with calling " | |||
1995 | "context for csspgo) in test " | |||
1996 | "profile with max count value greater then the parameter value")); | |||
1997 | cl::opt<std::string> FuncNameFilter( | |||
1998 | "function", | |||
1999 | cl::desc("Function level overlap information for matching functions. For " | |||
2000 | "CSSPGO this takes a a function name with calling context")); | |||
2001 | cl::opt<unsigned long long> SimilarityCutoff( | |||
2002 | "similarity-cutoff", cl::init(0), | |||
2003 | cl::desc("For sample profiles, list function names (with calling context " | |||
2004 | "for csspgo) for overlapped functions " | |||
2005 | "with similarities below the cutoff (percentage times 10000).")); | |||
2006 | cl::opt<ProfileKinds> ProfileKind( | |||
2007 | cl::desc("Profile kind:"), cl::init(instr), | |||
2008 | cl::values(clEnumVal(instr, "Instrumentation profile (default)")llvm::cl::OptionEnumValue { "instr", int(instr), "Instrumentation profile (default)" }, | |||
2009 | clEnumVal(sample, "Sample profile")llvm::cl::OptionEnumValue { "sample", int(sample), "Sample profile" })); | |||
2010 | cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); | |||
2011 | ||||
2012 | std::error_code EC; | |||
2013 | raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF); | |||
2014 | if (EC) | |||
2015 | exitWithErrorCode(EC, Output); | |||
2016 | ||||
2017 | if (ProfileKind == instr) | |||
2018 | overlapInstrProfile(BaseFilename, TestFilename, | |||
2019 | OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, | |||
2020 | IsCS); | |||
2021 | else | |||
2022 | overlapSampleProfile(BaseFilename, TestFilename, | |||
2023 | OverlapFuncFilters{ValueCutoff, FuncNameFilter}, | |||
2024 | SimilarityCutoff, OS); | |||
2025 | ||||
2026 | return 0; | |||
2027 | } | |||
2028 | ||||
2029 | namespace { | |||
2030 | struct ValueSitesStats { | |||
2031 | ValueSitesStats() | |||
2032 | : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), | |||
2033 | TotalNumValues(0) {} | |||
2034 | uint64_t TotalNumValueSites; | |||
2035 | uint64_t TotalNumValueSitesWithValueProfile; | |||
2036 | uint64_t TotalNumValues; | |||
2037 | std::vector<unsigned> ValueSitesHistogram; | |||
2038 | }; | |||
2039 | } // namespace | |||
2040 | ||||
2041 | static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, | |||
2042 | ValueSitesStats &Stats, raw_fd_ostream &OS, | |||
2043 | InstrProfSymtab *Symtab) { | |||
2044 | uint32_t NS = Func.getNumValueSites(VK); | |||
2045 | Stats.TotalNumValueSites += NS; | |||
2046 | for (size_t I = 0; I < NS; ++I) { | |||
2047 | uint32_t NV = Func.getNumValueDataForSite(VK, I); | |||
2048 | std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I); | |||
2049 | Stats.TotalNumValues += NV; | |||
2050 | if (NV) { | |||
2051 | Stats.TotalNumValueSitesWithValueProfile++; | |||
2052 | if (NV > Stats.ValueSitesHistogram.size()) | |||
2053 | Stats.ValueSitesHistogram.resize(NV, 0); | |||
2054 | Stats.ValueSitesHistogram[NV - 1]++; | |||
2055 | } | |||
2056 | ||||
2057 | uint64_t SiteSum = 0; | |||
2058 | for (uint32_t V = 0; V < NV; V++) | |||
2059 | SiteSum += VD[V].Count; | |||
2060 | if (SiteSum == 0) | |||
2061 | SiteSum = 1; | |||
2062 | ||||
2063 | for (uint32_t V = 0; V < NV; V++) { | |||
2064 | OS << "\t[ " << format("%2u", I) << ", "; | |||
2065 | if (Symtab == nullptr) | |||
2066 | OS << format("%4" PRIu64"l" "u", VD[V].Value); | |||
2067 | else | |||
2068 | OS << Symtab->getFuncName(VD[V].Value); | |||
2069 | OS << ", " << format("%10" PRId64"l" "d", VD[V].Count) << " ] (" | |||
2070 | << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n"; | |||
2071 | } | |||
2072 | } | |||
2073 | } | |||
2074 | ||||
2075 | static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, | |||
2076 | ValueSitesStats &Stats) { | |||
2077 | OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; | |||
2078 | OS << " Total number of sites with values: " | |||
2079 | << Stats.TotalNumValueSitesWithValueProfile << "\n"; | |||
2080 | OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; | |||
2081 | ||||
2082 | OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; | |||
2083 | for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { | |||
2084 | if (Stats.ValueSitesHistogram[I] > 0) | |||
2085 | OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; | |||
2086 | } | |||
2087 | } | |||
2088 | ||||
2089 | static int showInstrProfile(const std::string &Filename, bool ShowCounts, | |||
2090 | uint32_t TopN, bool ShowIndirectCallTargets, | |||
2091 | bool ShowMemOPSizes, bool ShowDetailedSummary, | |||
2092 | std::vector<uint32_t> DetailedSummaryCutoffs, | |||
2093 | bool ShowAllFunctions, bool ShowCS, | |||
2094 | uint64_t ValueCutoff, bool OnlyListBelow, | |||
2095 | const std::string &ShowFunction, bool TextFormat, | |||
2096 | bool ShowBinaryIds, bool ShowCovered, | |||
2097 | raw_fd_ostream &OS) { | |||
2098 | auto ReaderOrErr = InstrProfReader::create(Filename); | |||
2099 | std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs); | |||
2100 | if (ShowDetailedSummary && Cutoffs.empty()) { | |||
| ||||
2101 | Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990}; | |||
2102 | } | |||
2103 | InstrProfSummaryBuilder Builder(std::move(Cutoffs)); | |||
2104 | if (Error E = ReaderOrErr.takeError()) | |||
2105 | exitWithError(std::move(E), Filename); | |||
2106 | ||||
2107 | auto Reader = std::move(ReaderOrErr.get()); | |||
2108 | bool IsIRInstr = Reader->isIRLevelProfile(); | |||
2109 | size_t ShownFunctions = 0; | |||
2110 | size_t BelowCutoffFunctions = 0; | |||
2111 | int NumVPKind = IPVK_Last - IPVK_First + 1; | |||
2112 | std::vector<ValueSitesStats> VPStats(NumVPKind); | |||
2113 | ||||
2114 | auto MinCmp = [](const std::pair<std::string, uint64_t> &v1, | |||
2115 | const std::pair<std::string, uint64_t> &v2) { | |||
2116 | return v1.second > v2.second; | |||
2117 | }; | |||
2118 | ||||
2119 | std::priority_queue<std::pair<std::string, uint64_t>, | |||
2120 | std::vector<std::pair<std::string, uint64_t>>, | |||
2121 | decltype(MinCmp)> | |||
2122 | HottestFuncs(MinCmp); | |||
2123 | ||||
2124 | if (!TextFormat && OnlyListBelow) { | |||
2125 | OS << "The list of functions with the maximum counter less than " | |||
2126 | << ValueCutoff << ":\n"; | |||
2127 | } | |||
2128 | ||||
2129 | // Add marker so that IR-level instrumentation round-trips properly. | |||
2130 | if (TextFormat && IsIRInstr) | |||
2131 | OS << ":ir\n"; | |||
2132 | ||||
2133 | for (const auto &Func : *Reader) { | |||
2134 | if (Reader->isIRLevelProfile()) { | |||
2135 | bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); | |||
2136 | if (FuncIsCS != ShowCS) | |||
2137 | continue; | |||
2138 | } | |||
2139 | bool Show = ShowAllFunctions || | |||
2140 | (!ShowFunction.empty() && Func.Name.contains(ShowFunction)); | |||
2141 | ||||
2142 | bool doTextFormatDump = (Show && TextFormat); | |||
2143 | ||||
2144 | if (doTextFormatDump) { | |||
2145 | InstrProfSymtab &Symtab = Reader->getSymtab(); | |||
2146 | InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, | |||
2147 | OS); | |||
2148 | continue; | |||
2149 | } | |||
2150 | ||||
2151 | assert(Func.Counts.size() > 0 && "function missing entry counter")(static_cast <bool> (Func.Counts.size() > 0 && "function missing entry counter") ? void (0) : __assert_fail ("Func.Counts.size() > 0 && \"function missing entry counter\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 2151, __extension__ __PRETTY_FUNCTION__)); | |||
2152 | Builder.addRecord(Func); | |||
2153 | ||||
2154 | if (ShowCovered) { | |||
2155 | if (std::any_of(Func.Counts.begin(), Func.Counts.end(), | |||
2156 | [](uint64_t C) { return C; })) | |||
2157 | OS << Func.Name << "\n"; | |||
2158 | continue; | |||
2159 | } | |||
2160 | ||||
2161 | uint64_t FuncMax = 0; | |||
2162 | uint64_t FuncSum = 0; | |||
2163 | for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { | |||
2164 | if (Func.Counts[I] == (uint64_t)-1) | |||
2165 | continue; | |||
2166 | FuncMax = std::max(FuncMax, Func.Counts[I]); | |||
2167 | FuncSum += Func.Counts[I]; | |||
2168 | } | |||
2169 | ||||
2170 | if (FuncMax < ValueCutoff) { | |||
2171 | ++BelowCutoffFunctions; | |||
2172 | if (OnlyListBelow) { | |||
2173 | OS << " " << Func.Name << ": (Max = " << FuncMax | |||
2174 | << " Sum = " << FuncSum << ")\n"; | |||
2175 | } | |||
2176 | continue; | |||
2177 | } else if (OnlyListBelow) | |||
2178 | continue; | |||
2179 | ||||
2180 | if (TopN) { | |||
2181 | if (HottestFuncs.size() == TopN) { | |||
2182 | if (HottestFuncs.top().second < FuncMax) { | |||
2183 | HottestFuncs.pop(); | |||
2184 | HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); | |||
2185 | } | |||
2186 | } else | |||
2187 | HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); | |||
2188 | } | |||
2189 | ||||
2190 | if (Show) { | |||
2191 | if (!ShownFunctions) | |||
2192 | OS << "Counters:\n"; | |||
2193 | ||||
2194 | ++ShownFunctions; | |||
2195 | ||||
2196 | OS << " " << Func.Name << ":\n" | |||
2197 | << " Hash: " << format("0x%016" PRIx64"l" "x", Func.Hash) << "\n" | |||
2198 | << " Counters: " << Func.Counts.size() << "\n"; | |||
2199 | if (!IsIRInstr) | |||
2200 | OS << " Function count: " << Func.Counts[0] << "\n"; | |||
2201 | ||||
2202 | if (ShowIndirectCallTargets) | |||
2203 | OS << " Indirect Call Site Count: " | |||
2204 | << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; | |||
2205 | ||||
2206 | uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); | |||
2207 | if (ShowMemOPSizes && NumMemOPCalls > 0) | |||
2208 | OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls | |||
2209 | << "\n"; | |||
2210 | ||||
2211 | if (ShowCounts) { | |||
2212 | OS << " Block counts: ["; | |||
2213 | size_t Start = (IsIRInstr ? 0 : 1); | |||
2214 | for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) { | |||
2215 | OS << (I == Start ? "" : ", ") << Func.Counts[I]; | |||
2216 | } | |||
2217 | OS << "]\n"; | |||
2218 | } | |||
2219 | ||||
2220 | if (ShowIndirectCallTargets) { | |||
2221 | OS << " Indirect Target Results:\n"; | |||
2222 | traverseAllValueSites(Func, IPVK_IndirectCallTarget, | |||
2223 | VPStats[IPVK_IndirectCallTarget], OS, | |||
2224 | &(Reader->getSymtab())); | |||
2225 | } | |||
2226 | ||||
2227 | if (ShowMemOPSizes && NumMemOPCalls > 0) { | |||
2228 | OS << " Memory Intrinsic Size Results:\n"; | |||
2229 | traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, | |||
2230 | nullptr); | |||
2231 | } | |||
2232 | } | |||
2233 | } | |||
2234 | if (Reader->hasError()) | |||
2235 | exitWithError(Reader->getError(), Filename); | |||
2236 | ||||
2237 | if (TextFormat || ShowCovered) | |||
2238 | return 0; | |||
2239 | std::unique_ptr<ProfileSummary> PS(Builder.getSummary()); | |||
2240 | bool IsIR = Reader->isIRLevelProfile(); | |||
2241 | OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end"); | |||
2242 | if (IsIR) | |||
2243 | OS << " entry_first = " << Reader->instrEntryBBEnabled(); | |||
2244 | OS << "\n"; | |||
2245 | if (ShowAllFunctions || !ShowFunction.empty()) | |||
2246 | OS << "Functions shown: " << ShownFunctions << "\n"; | |||
2247 | OS << "Total functions: " << PS->getNumFunctions() << "\n"; | |||
2248 | if (ValueCutoff > 0) { | |||
2249 | OS << "Number of functions with maximum count (< " << ValueCutoff | |||
2250 | << "): " << BelowCutoffFunctions << "\n"; | |||
2251 | OS << "Number of functions with maximum count (>= " << ValueCutoff | |||
2252 | << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; | |||
2253 | } | |||
2254 | OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; | |||
2255 | OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; | |||
2256 | ||||
2257 | if (TopN) { | |||
2258 | std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs; | |||
2259 | while (!HottestFuncs.empty()) { | |||
2260 | SortedHottestFuncs.emplace_back(HottestFuncs.top()); | |||
2261 | HottestFuncs.pop(); | |||
2262 | } | |||
2263 | OS << "Top " << TopN | |||
2264 | << " functions with the largest internal block counts: \n"; | |||
2265 | for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) | |||
2266 | OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; | |||
2267 | } | |||
2268 | ||||
2269 | if (ShownFunctions && ShowIndirectCallTargets) { | |||
2270 | OS << "Statistics for indirect call sites profile:\n"; | |||
2271 | showValueSitesStats(OS, IPVK_IndirectCallTarget, | |||
2272 | VPStats[IPVK_IndirectCallTarget]); | |||
2273 | } | |||
2274 | ||||
2275 | if (ShownFunctions && ShowMemOPSizes) { | |||
2276 | OS << "Statistics for memory intrinsic calls sizes profile:\n"; | |||
2277 | showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); | |||
2278 | } | |||
2279 | ||||
2280 | if (ShowDetailedSummary) { | |||
2281 | OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; | |||
2282 | OS << "Total count: " << PS->getTotalCount() << "\n"; | |||
2283 | PS->printDetailedSummary(OS); | |||
2284 | } | |||
2285 | ||||
2286 | if (ShowBinaryIds) | |||
2287 | if (Error E = Reader->printBinaryIds(OS)) | |||
2288 | exitWithError(std::move(E), Filename); | |||
2289 | ||||
2290 | return 0; | |||
2291 | } | |||
2292 | ||||
2293 | static void showSectionInfo(sampleprof::SampleProfileReader *Reader, | |||
2294 | raw_fd_ostream &OS) { | |||
2295 | if (!Reader->dumpSectionInfo(OS)) { | |||
2296 | WithColor::warning() << "-show-sec-info-only is only supported for " | |||
2297 | << "sample profile in extbinary format and is " | |||
2298 | << "ignored for other formats.\n"; | |||
2299 | return; | |||
2300 | } | |||
2301 | } | |||
2302 | ||||
2303 | namespace { | |||
2304 | struct HotFuncInfo { | |||
2305 | std::string FuncName; | |||
2306 | uint64_t TotalCount; | |||
2307 | double TotalCountPercent; | |||
2308 | uint64_t MaxCount; | |||
2309 | uint64_t EntryCount; | |||
2310 | ||||
2311 | HotFuncInfo() | |||
2312 | : TotalCount(0), TotalCountPercent(0.0f), MaxCount(0), EntryCount(0) {} | |||
2313 | ||||
2314 | HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES) | |||
2315 | : FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP), | |||
2316 | MaxCount(MS), EntryCount(ES) {} | |||
2317 | }; | |||
2318 | } // namespace | |||
2319 | ||||
2320 | // Print out detailed information about hot functions in PrintValues vector. | |||
2321 | // Users specify titles and offset of every columns through ColumnTitle and | |||
2322 | // ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same | |||
2323 | // and at least 4. Besides, users can optionally give a HotFuncMetric string to | |||
2324 | // print out or let it be an empty string. | |||
2325 | static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle, | |||
2326 | const std::vector<int> &ColumnOffset, | |||
2327 | const std::vector<HotFuncInfo> &PrintValues, | |||
2328 | uint64_t HotFuncCount, uint64_t TotalFuncCount, | |||
2329 | uint64_t HotProfCount, uint64_t TotalProfCount, | |||
2330 | const std::string &HotFuncMetric, | |||
2331 | uint32_t TopNFunctions, raw_fd_ostream &OS) { | |||
2332 | assert(ColumnOffset.size() == ColumnTitle.size() &&(static_cast <bool> (ColumnOffset.size() == ColumnTitle .size() && "ColumnOffset and ColumnTitle should have the same size" ) ? void (0) : __assert_fail ("ColumnOffset.size() == ColumnTitle.size() && \"ColumnOffset and ColumnTitle should have the same size\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 2333, __extension__ __PRETTY_FUNCTION__)) | |||
2333 | "ColumnOffset and ColumnTitle should have the same size")(static_cast <bool> (ColumnOffset.size() == ColumnTitle .size() && "ColumnOffset and ColumnTitle should have the same size" ) ? void (0) : __assert_fail ("ColumnOffset.size() == ColumnTitle.size() && \"ColumnOffset and ColumnTitle should have the same size\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 2333, __extension__ __PRETTY_FUNCTION__)); | |||
2334 | assert(ColumnTitle.size() >= 4 &&(static_cast <bool> (ColumnTitle.size() >= 4 && "ColumnTitle should have at least 4 elements") ? void (0) : __assert_fail ("ColumnTitle.size() >= 4 && \"ColumnTitle should have at least 4 elements\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 2335, __extension__ __PRETTY_FUNCTION__)) | |||
2335 | "ColumnTitle should have at least 4 elements")(static_cast <bool> (ColumnTitle.size() >= 4 && "ColumnTitle should have at least 4 elements") ? void (0) : __assert_fail ("ColumnTitle.size() >= 4 && \"ColumnTitle should have at least 4 elements\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 2335, __extension__ __PRETTY_FUNCTION__)); | |||
2336 | assert(TotalFuncCount > 0 &&(static_cast <bool> (TotalFuncCount > 0 && "There should be at least one function in the profile" ) ? void (0) : __assert_fail ("TotalFuncCount > 0 && \"There should be at least one function in the profile\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 2337, __extension__ __PRETTY_FUNCTION__)) | |||
2337 | "There should be at least one function in the profile")(static_cast <bool> (TotalFuncCount > 0 && "There should be at least one function in the profile" ) ? void (0) : __assert_fail ("TotalFuncCount > 0 && \"There should be at least one function in the profile\"" , "llvm/tools/llvm-profdata/llvm-profdata.cpp", 2337, __extension__ __PRETTY_FUNCTION__)); | |||
2338 | double TotalProfPercent = 0; | |||
2339 | if (TotalProfCount > 0) | |||
2340 | TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100; | |||
2341 | ||||
2342 | formatted_raw_ostream FOS(OS); | |||
2343 | FOS << HotFuncCount << " out of " << TotalFuncCount | |||
2344 | << " functions with profile (" | |||
2345 | << format("%.2f%%", | |||
2346 | (static_cast<double>(HotFuncCount) / TotalFuncCount * 100)) | |||
2347 | << ") are considered hot functions"; | |||
2348 | if (!HotFuncMetric.empty()) | |||
2349 | FOS << " (" << HotFuncMetric << ")"; | |||
2350 | FOS << ".\n"; | |||
2351 | FOS << HotProfCount << " out of " << TotalProfCount << " profile counts (" | |||
2352 | << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n"; | |||
2353 | ||||
2354 | for (size_t I = 0; I < ColumnTitle.size(); ++I) { | |||
2355 | FOS.PadToColumn(ColumnOffset[I]); | |||
2356 | FOS << ColumnTitle[I]; | |||
2357 | } | |||
2358 | FOS << "\n"; | |||
2359 | ||||
2360 | uint32_t Count = 0; | |||
2361 | for (const auto &R : PrintValues) { | |||
2362 | if (TopNFunctions && (Count++ == TopNFunctions)) | |||
2363 | break; | |||
2364 | FOS.PadToColumn(ColumnOffset[0]); | |||
2365 | FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")"; | |||
2366 | FOS.PadToColumn(ColumnOffset[1]); | |||
2367 | FOS << R.MaxCount; | |||
2368 | FOS.PadToColumn(ColumnOffset[2]); | |||
2369 | FOS << R.EntryCount; | |||
2370 | FOS.PadToColumn(ColumnOffset[3]); | |||
2371 | FOS << R.FuncName << "\n"; | |||
2372 | } | |||
2373 | } | |||
2374 | ||||
2375 | static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles, | |||
2376 | ProfileSummary &PS, uint32_t TopN, | |||
2377 | raw_fd_ostream &OS) { | |||
2378 | using namespace sampleprof; | |||
2379 | ||||
2380 | const uint32_t HotFuncCutoff = 990000; | |||
2381 | auto &SummaryVector = PS.getDetailedSummary(); | |||
2382 | uint64_t MinCountThreshold = 0; | |||
2383 | for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) { | |||
2384 | if (SummaryEntry.Cutoff == HotFuncCutoff) { | |||
2385 | MinCountThreshold = SummaryEntry.MinCount; | |||
2386 | break; | |||
2387 | } | |||
2388 | } | |||
2389 | ||||
2390 | // Traverse all functions in the profile and keep only hot functions. | |||
2391 | // The following loop also calculates the sum of total samples of all | |||
2392 | // functions. | |||
2393 | std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>, | |||
2394 | std::greater<uint64_t>> | |||
2395 | HotFunc; | |||
2396 | uint64_t ProfileTotalSample = 0; | |||
2397 | uint64_t HotFuncSample = 0; | |||
2398 | uint64_t HotFuncCount = 0; | |||
2399 | ||||
2400 | for (const auto &I : Profiles) { | |||
2401 | FuncSampleStats FuncStats; | |||
2402 | const FunctionSamples &FuncProf = I.second; | |||
2403 | ProfileTotalSample += FuncProf.getTotalSamples(); | |||
2404 | getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold); | |||
2405 | ||||
2406 | if (isFunctionHot(FuncStats, MinCountThreshold)) { | |||
2407 | HotFunc.emplace(FuncProf.getTotalSamples(), | |||
2408 | std::make_pair(&(I.second), FuncStats.MaxSample)); | |||
2409 | HotFuncSample += FuncProf.getTotalSamples(); | |||
2410 | ++HotFuncCount; | |||
2411 | } | |||
2412 | } | |||
2413 | ||||
2414 | std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample", | |||
2415 | "Entry sample", "Function name"}; | |||
2416 | std::vector<int> ColumnOffset{0, 24, 42, 58}; | |||
2417 | std::string Metric = | |||
2418 | std::string("max sample >= ") + std::to_string(MinCountThreshold); | |||
2419 | std::vector<HotFuncInfo> PrintValues; | |||
2420 | for (const auto &FuncPair : HotFunc) { | |||
2421 | const FunctionSamples &Func = *FuncPair.second.first; | |||
2422 | double TotalSamplePercent = | |||
2423 | (ProfileTotalSample > 0) | |||
2424 | ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample | |||
2425 | : 0; | |||
2426 | PrintValues.emplace_back(HotFuncInfo( | |||
2427 | Func.getContext().toString(), Func.getTotalSamples(), | |||
2428 | TotalSamplePercent, FuncPair.second.second, Func.getEntrySamples())); | |||
2429 | } | |||
2430 | dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, | |||
2431 | Profiles.size(), HotFuncSample, ProfileTotalSample, | |||
2432 | Metric, TopN, OS); | |||
2433 | ||||
2434 | return 0; | |||
2435 | } | |||
2436 | ||||
2437 | static int showSampleProfile(const std::string &Filename, bool ShowCounts, | |||
2438 | uint32_t TopN, bool ShowAllFunctions, | |||
2439 | bool ShowDetailedSummary, | |||
2440 | const std::string &ShowFunction, | |||
2441 | bool ShowProfileSymbolList, | |||
2442 | bool ShowSectionInfoOnly, bool ShowHotFuncList, | |||
2443 | raw_fd_ostream &OS) { | |||
2444 | using namespace sampleprof; | |||
2445 | LLVMContext Context; | |||
2446 | auto ReaderOrErr = | |||
2447 | SampleProfileReader::create(Filename, Context, FSDiscriminatorPassOption); | |||
2448 | if (std::error_code EC = ReaderOrErr.getError()) | |||
2449 | exitWithErrorCode(EC, Filename); | |||
2450 | ||||
2451 | auto Reader = std::move(ReaderOrErr.get()); | |||
2452 | if (ShowSectionInfoOnly) { | |||
2453 | showSectionInfo(Reader.get(), OS); | |||
2454 | return 0; | |||
2455 | } | |||
2456 | ||||
2457 | if (std::error_code EC = Reader->read()) | |||
2458 | exitWithErrorCode(EC, Filename); | |||
2459 | ||||
2460 | if (ShowAllFunctions || ShowFunction.empty()) | |||
2461 | Reader->dump(OS); | |||
2462 | else | |||
2463 | // TODO: parse context string to support filtering by contexts. | |||
2464 | Reader->dumpFunctionProfile(StringRef(ShowFunction), OS); | |||
2465 | ||||
2466 | if (ShowProfileSymbolList) { | |||
2467 | std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList = | |||
2468 | Reader->getProfileSymbolList(); | |||
2469 | ReaderList->dump(OS); | |||
2470 | } | |||
2471 | ||||
2472 | if (ShowDetailedSummary) { | |||
2473 | auto &PS = Reader->getSummary(); | |||
2474 | PS.printSummary(OS); | |||
2475 | PS.printDetailedSummary(OS); | |||
2476 | } | |||
2477 | ||||
2478 | if (ShowHotFuncList || TopN) | |||
2479 | showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), TopN, OS); | |||
2480 | ||||
2481 | return 0; | |||
2482 | } | |||
2483 | ||||
2484 | static int showMemProfProfile(const std::string &Filename, | |||
2485 | const std::string &ProfiledBinary, | |||
2486 | raw_fd_ostream &OS) { | |||
2487 | auto ReaderOr = | |||
2488 | llvm::memprof::RawMemProfReader::create(Filename, ProfiledBinary); | |||
2489 | if (Error E = ReaderOr.takeError()) | |||
2490 | // Since the error can be related to the profile or the binary we do not | |||
2491 | // pass whence. Instead additional context is provided where necessary in | |||
2492 | // the error message. | |||
2493 | exitWithError(std::move(E), /*Whence*/ ""); | |||
2494 | ||||
2495 | std::unique_ptr<llvm::memprof::RawMemProfReader> Reader( | |||
2496 | ReaderOr.get().release()); | |||
2497 | ||||
2498 | Reader->printYAML(OS); | |||
2499 | return 0; | |||
2500 | } | |||
2501 | ||||
2502 | static int showDebugInfoCorrelation(const std::string &Filename, | |||
2503 | bool ShowDetailedSummary, | |||
2504 | bool ShowProfileSymbolList, | |||
2505 | raw_fd_ostream &OS) { | |||
2506 | std::unique_ptr<InstrProfCorrelator> Correlator; | |||
2507 | if (auto Err = InstrProfCorrelator::get(Filename).moveInto(Correlator)) | |||
2508 | exitWithError(std::move(Err), Filename); | |||
2509 | if (auto Err = Correlator->correlateProfileData()) | |||
2510 | exitWithError(std::move(Err), Filename); | |||
2511 | ||||
2512 | InstrProfSymtab Symtab; | |||
2513 | if (auto Err = Symtab.create( | |||
2514 | StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize()))) | |||
2515 | exitWithError(std::move(Err), Filename); | |||
2516 | ||||
2517 | if (ShowProfileSymbolList) | |||
2518 | Symtab.dumpNames(OS); | |||
2519 | // TODO: Read "Profile Data Type" from debug info to compute and show how many | |||
2520 | // counters the section holds. | |||
2521 | if (ShowDetailedSummary) | |||
2522 | OS << "Counters section size: 0x" | |||
2523 | << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n"; | |||
2524 | OS << "Found " << Correlator->getDataSize() << " functions\n"; | |||
2525 | ||||
2526 | return 0; | |||
2527 | } | |||
2528 | ||||
2529 | static int show_main(int argc, const char *argv[]) { | |||
2530 | cl::opt<std::string> Filename(cl::Positional, cl::desc("<profdata-file>")); | |||
2531 | ||||
2532 | cl::opt<bool> ShowCounts("counts", cl::init(false), | |||
2533 | cl::desc("Show counter values for shown functions")); | |||
2534 | cl::opt<bool> TextFormat( | |||
2535 | "text", cl::init(false), | |||
2536 | cl::desc("Show instr profile data in text dump format")); | |||
2537 | cl::opt<bool> ShowIndirectCallTargets( | |||
2538 | "ic-targets", cl::init(false), | |||
2539 | cl::desc("Show indirect call site target values for shown functions")); | |||
2540 | cl::opt<bool> ShowMemOPSizes( | |||
2541 | "memop-sizes", cl::init(false), | |||
2542 | cl::desc("Show the profiled sizes of the memory intrinsic calls " | |||
2543 | "for shown functions")); | |||
2544 | cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false), | |||
2545 | cl::desc("Show detailed profile summary")); | |||
2546 | cl::list<uint32_t> DetailedSummaryCutoffs( | |||
2547 | cl::CommaSeparated, "detailed-summary-cutoffs", | |||
2548 | cl::desc( | |||
2549 | "Cutoff percentages (times 10000) for generating detailed summary"), | |||
2550 | cl::value_desc("800000,901000,999999")); | |||
2551 | cl::opt<bool> ShowHotFuncList( | |||
2552 | "hot-func-list", cl::init(false), | |||
2553 | cl::desc("Show profile summary of a list of hot functions")); | |||
2554 | cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false), | |||
2555 | cl::desc("Details for every function")); | |||
2556 | cl::opt<bool> ShowCS("showcs", cl::init(false), | |||
2557 | cl::desc("Show context sensitive counts")); | |||
2558 | cl::opt<std::string> ShowFunction("function", | |||
2559 | cl::desc("Details for matching functions")); | |||
2560 | ||||
2561 | cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), | |||
2562 | cl::init("-"), cl::desc("Output file")); | |||
2563 | cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), | |||
2564 | cl::aliasopt(OutputFilename)); | |||
2565 | cl::opt<ProfileKinds> ProfileKind( | |||
2566 | cl::desc("Profile kind:"), cl::init(instr), | |||
2567 | cl::values(clEnumVal(instr, "Instrumentation profile (default)")llvm::cl::OptionEnumValue { "instr", int(instr), "Instrumentation profile (default)" }, | |||
2568 | clEnumVal(sample, "Sample profile")llvm::cl::OptionEnumValue { "sample", int(sample), "Sample profile" }, | |||
2569 | clEnumVal(memory, "MemProf memory access profile")llvm::cl::OptionEnumValue { "memory", int(memory), "MemProf memory access profile" })); | |||
2570 | cl::opt<uint32_t> TopNFunctions( | |||
2571 | "topn", cl::init(0), | |||
2572 | cl::desc("Show the list of functions with the largest internal counts")); | |||
2573 | cl::opt<uint32_t> ValueCutoff( | |||
2574 | "value-cutoff", cl::init(0), | |||
2575 | cl::desc("Set the count value cutoff. Functions with the maximum count " | |||
2576 | "less than this value will not be printed out. (Default is 0)")); | |||
2577 | cl::opt<bool> OnlyListBelow( | |||
2578 | "list-below-cutoff", cl::init(false), | |||
2579 | cl::desc("Only output names of functions whose max count values are " | |||
2580 | "below the cutoff value")); | |||
2581 | cl::opt<bool> ShowProfileSymbolList( | |||
2582 | "show-prof-sym-list", cl::init(false), | |||
2583 | cl::desc("Show profile symbol list if it exists in the profile. ")); | |||
2584 | cl::opt<bool> ShowSectionInfoOnly( | |||
2585 | "show-sec-info-only", cl::init(false), | |||
2586 | cl::desc("Show the information of each section in the sample profile. " | |||
2587 | "The flag is only usable when the sample profile is in " | |||
2588 | "extbinary format")); | |||
2589 | cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false), | |||
2590 | cl::desc("Show binary ids in the profile. ")); | |||
2591 | cl::opt<std::string> DebugInfoFilename( | |||
2592 | "debug-info", cl::init(""), | |||
2593 | cl::desc("Read and extract profile metadata from debug info and show " | |||
2594 | "the functions it found.")); | |||
2595 | cl::opt<bool> ShowCovered( | |||
2596 | "covered", cl::init(false), | |||
2597 | cl::desc("Show only the functions that have been executed.")); | |||
2598 | cl::opt<std::string> ProfiledBinary( | |||
2599 | "profiled-binary", cl::init(""), | |||
2600 | cl::desc("Path to binary from which the profile was collected.")); | |||
2601 | ||||
2602 | cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); | |||
2603 | ||||
2604 | if (Filename.empty() && DebugInfoFilename.empty()) | |||
2605 | exitWithError( | |||
2606 | "the positional argument '<profdata-file>' is required unless '--" + | |||
2607 | DebugInfoFilename.ArgStr + "' is provided"); | |||
2608 | ||||
2609 | if (Filename == OutputFilename) { | |||
2610 | errs() << sys::path::filename(argv[0]) | |||
2611 | << ": Input file name cannot be the same as the output file name!\n"; | |||
2612 | return 1; | |||
2613 | } | |||
2614 | ||||
2615 | std::error_code EC; | |||
2616 | raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); | |||
2617 | if (EC) | |||
2618 | exitWithErrorCode(EC, OutputFilename); | |||
2619 | ||||
2620 | if (ShowAllFunctions && !ShowFunction.empty()) | |||
2621 | WithColor::warning() << "-function argument ignored: showing all functions\n"; | |||
2622 | ||||
2623 | if (!DebugInfoFilename.empty()) | |||
2624 | return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary, | |||
2625 | ShowProfileSymbolList, OS); | |||
2626 | ||||
2627 | if (ProfileKind == instr) | |||
2628 | return showInstrProfile( | |||
2629 | Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, | |||
2630 | ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, | |||
2631 | ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction, | |||
2632 | TextFormat, ShowBinaryIds, ShowCovered, OS); | |||
2633 | if (ProfileKind == sample) | |||
2634 | return showSampleProfile(Filename, ShowCounts, TopNFunctions, | |||
2635 | ShowAllFunctions, ShowDetailedSummary, | |||
2636 | ShowFunction, ShowProfileSymbolList, | |||
2637 | ShowSectionInfoOnly, ShowHotFuncList, OS); | |||
2638 | return showMemProfProfile(Filename, ProfiledBinary, OS); | |||
2639 | } | |||
2640 | ||||
2641 | int main(int argc, const char *argv[]) { | |||
2642 | InitLLVM X(argc, argv); | |||
2643 | ||||
2644 | StringRef ProgName(sys::path::filename(argv[0])); | |||
2645 | if (argc > 1) { | |||
2646 | int (*func)(int, const char *[]) = nullptr; | |||
2647 | ||||
2648 | if (strcmp(argv[1], "merge") == 0) | |||
2649 | func = merge_main; | |||
2650 | else if (strcmp(argv[1], "show") == 0) | |||
2651 | func = show_main; | |||
2652 | else if (strcmp(argv[1], "overlap") == 0) | |||
2653 | func = overlap_main; | |||
2654 | ||||
2655 | if (func) { | |||
2656 | std::string Invocation(ProgName.str() + " " + argv[1]); | |||
2657 | argv[1] = Invocation.c_str(); | |||
2658 | return func(argc - 1, argv + 1); | |||
2659 | } | |||
2660 | ||||
2661 | if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || | |||
2662 | strcmp(argv[1], "--help") == 0) { | |||
2663 | ||||
2664 | errs() << "OVERVIEW: LLVM profile data tools\n\n" | |||
2665 | << "USAGE: " << ProgName << " <command> [args...]\n" | |||
2666 | << "USAGE: " << ProgName << " <command> -help\n\n" | |||
2667 | << "See each individual command --help for more details.\n" | |||
2668 | << "Available commands: merge, show, overlap\n"; | |||
2669 | return 0; | |||
2670 | } | |||
2671 | } | |||
2672 | ||||
2673 | if (argc < 2) | |||
2674 | errs() << ProgName << ": No command specified!\n"; | |||
2675 | else | |||
2676 | errs() << ProgName << ": Unknown command!\n"; | |||
2677 | ||||
2678 | errs() << "USAGE: " << ProgName << " <merge|show|overlap> [args...]\n"; | |||
2679 | return 1; | |||
2680 | } |
1 | //===- llvm/Support/Error.h - Recoverable error handling --------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file defines an API used to report recoverable errors. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef LLVM_SUPPORT_ERROR_H |
14 | #define LLVM_SUPPORT_ERROR_H |
15 | |
16 | #include "llvm-c/Error.h" |
17 | #include "llvm/ADT/SmallVector.h" |
18 | #include "llvm/ADT/StringExtras.h" |
19 | #include "llvm/ADT/Twine.h" |
20 | #include "llvm/Config/abi-breaking.h" |
21 | #include "llvm/Support/AlignOf.h" |
22 | #include "llvm/Support/Compiler.h" |
23 | #include "llvm/Support/Debug.h" |
24 | #include "llvm/Support/ErrorHandling.h" |
25 | #include "llvm/Support/ErrorOr.h" |
26 | #include "llvm/Support/Format.h" |
27 | #include "llvm/Support/raw_ostream.h" |
28 | #include <cassert> |
29 | #include <cstdint> |
30 | #include <cstdlib> |
31 | #include <functional> |
32 | #include <memory> |
33 | #include <new> |
34 | #include <string> |
35 | #include <system_error> |
36 | #include <type_traits> |
37 | #include <utility> |
38 | #include <vector> |
39 | |
40 | namespace llvm { |
41 | |
42 | class ErrorSuccess; |
43 | |
44 | /// Base class for error info classes. Do not extend this directly: Extend |
45 | /// the ErrorInfo template subclass instead. |
46 | class ErrorInfoBase { |
47 | public: |
48 | virtual ~ErrorInfoBase() = default; |
49 | |
50 | /// Print an error message to an output stream. |
51 | virtual void log(raw_ostream &OS) const = 0; |
52 | |
53 | /// Return the error message as a string. |
54 | virtual std::string message() const { |
55 | std::string Msg; |
56 | raw_string_ostream OS(Msg); |
57 | log(OS); |
58 | return OS.str(); |
59 | } |
60 | |
61 | /// Convert this error to a std::error_code. |
62 | /// |
63 | /// This is a temporary crutch to enable interaction with code still |
64 | /// using std::error_code. It will be removed in the future. |
65 | virtual std::error_code convertToErrorCode() const = 0; |
66 | |
67 | // Returns the class ID for this type. |
68 | static const void *classID() { return &ID; } |
69 | |
70 | // Returns the class ID for the dynamic type of this ErrorInfoBase instance. |
71 | virtual const void *dynamicClassID() const = 0; |
72 | |
73 | // Check whether this instance is a subclass of the class identified by |
74 | // ClassID. |
75 | virtual bool isA(const void *const ClassID) const { |
76 | return ClassID == classID(); |
77 | } |
78 | |
79 | // Check whether this instance is a subclass of ErrorInfoT. |
80 | template <typename ErrorInfoT> bool isA() const { |
81 | return isA(ErrorInfoT::classID()); |
82 | } |
83 | |
84 | private: |
85 | virtual void anchor(); |
86 | |
87 | static char ID; |
88 | }; |
89 | |
90 | /// Lightweight error class with error context and mandatory checking. |
91 | /// |
92 | /// Instances of this class wrap a ErrorInfoBase pointer. Failure states |
93 | /// are represented by setting the pointer to a ErrorInfoBase subclass |
94 | /// instance containing information describing the failure. Success is |
95 | /// represented by a null pointer value. |
96 | /// |
97 | /// Instances of Error also contains a 'Checked' flag, which must be set |
98 | /// before the destructor is called, otherwise the destructor will trigger a |
99 | /// runtime error. This enforces at runtime the requirement that all Error |
100 | /// instances be checked or returned to the caller. |
101 | /// |
102 | /// There are two ways to set the checked flag, depending on what state the |
103 | /// Error instance is in. For Error instances indicating success, it |
104 | /// is sufficient to invoke the boolean conversion operator. E.g.: |
105 | /// |
106 | /// @code{.cpp} |
107 | /// Error foo(<...>); |
108 | /// |
109 | /// if (auto E = foo(<...>)) |
110 | /// return E; // <- Return E if it is in the error state. |
111 | /// // We have verified that E was in the success state. It can now be safely |
112 | /// // destroyed. |
113 | /// @endcode |
114 | /// |
115 | /// A success value *can not* be dropped. For example, just calling 'foo(<...>)' |
116 | /// without testing the return value will raise a runtime error, even if foo |
117 | /// returns success. |
118 | /// |
119 | /// For Error instances representing failure, you must use either the |
120 | /// handleErrors or handleAllErrors function with a typed handler. E.g.: |
121 | /// |
122 | /// @code{.cpp} |
123 | /// class MyErrorInfo : public ErrorInfo<MyErrorInfo> { |
124 | /// // Custom error info. |
125 | /// }; |
126 | /// |
127 | /// Error foo(<...>) { return make_error<MyErrorInfo>(...); } |
128 | /// |
129 | /// auto E = foo(<...>); // <- foo returns failure with MyErrorInfo. |
130 | /// auto NewE = |
131 | /// handleErrors(E, |
132 | /// [](const MyErrorInfo &M) { |
133 | /// // Deal with the error. |
134 | /// }, |
135 | /// [](std::unique_ptr<OtherError> M) -> Error { |
136 | /// if (canHandle(*M)) { |
137 | /// // handle error. |
138 | /// return Error::success(); |
139 | /// } |
140 | /// // Couldn't handle this error instance. Pass it up the stack. |
141 | /// return Error(std::move(M)); |
142 | /// ); |
143 | /// // Note - we must check or return NewE in case any of the handlers |
144 | /// // returned a new error. |
145 | /// @endcode |
146 | /// |
147 | /// The handleAllErrors function is identical to handleErrors, except |
148 | /// that it has a void return type, and requires all errors to be handled and |
149 | /// no new errors be returned. It prevents errors (assuming they can all be |
150 | /// handled) from having to be bubbled all the way to the top-level. |
151 | /// |
152 | /// *All* Error instances must be checked before destruction, even if |
153 | /// they're moved-assigned or constructed from Success values that have already |
154 | /// been checked. This enforces checking through all levels of the call stack. |
155 | class LLVM_NODISCARD[[clang::warn_unused_result]] Error { |
156 | // ErrorList needs to be able to yank ErrorInfoBase pointers out of Errors |
157 | // to add to the error list. It can't rely on handleErrors for this, since |
158 | // handleErrors does not support ErrorList handlers. |
159 | friend class ErrorList; |
160 | |
161 | // handleErrors needs to be able to set the Checked flag. |
162 | template <typename... HandlerTs> |
163 | friend Error handleErrors(Error E, HandlerTs &&... Handlers); |
164 | |
165 | // Expected<T> needs to be able to steal the payload when constructed from an |
166 | // error. |
167 | template <typename T> friend class Expected; |
168 | |
169 | // wrap needs to be able to steal the payload. |
170 | friend LLVMErrorRef wrap(Error); |
171 | |
172 | protected: |
173 | /// Create a success value. Prefer using 'Error::success()' for readability |
174 | Error() { |
175 | setPtr(nullptr); |
176 | setChecked(false); |
177 | } |
178 | |
179 | public: |
180 | /// Create a success value. |
181 | static ErrorSuccess success(); |
182 | |
183 | // Errors are not copy-constructable. |
184 | Error(const Error &Other) = delete; |
185 | |
186 | /// Move-construct an error value. The newly constructed error is considered |
187 | /// unchecked, even if the source error had been checked. The original error |
188 | /// becomes a checked Success value, regardless of its original state. |
189 | Error(Error &&Other) { |
190 | setChecked(true); |
191 | *this = std::move(Other); |
192 | } |
193 | |
194 | /// Create an error value. Prefer using the 'make_error' function, but |
195 | /// this constructor can be useful when "re-throwing" errors from handlers. |
196 | Error(std::unique_ptr<ErrorInfoBase> Payload) { |
197 | setPtr(Payload.release()); |
198 | setChecked(false); |
199 | } |
200 | |
201 | // Errors are not copy-assignable. |
202 | Error &operator=(const Error &Other) = delete; |
203 | |
204 | /// Move-assign an error value. The current error must represent success, you |
205 | /// you cannot overwrite an unhandled error. The current error is then |
206 | /// considered unchecked. The source error becomes a checked success value, |
207 | /// regardless of its original state. |
208 | Error &operator=(Error &&Other) { |
209 | // Don't allow overwriting of unchecked values. |
210 | assertIsChecked(); |
211 | setPtr(Other.getPtr()); |
212 | |
213 | // This Error is unchecked, even if the source error was checked. |
214 | setChecked(false); |
215 | |
216 | // Null out Other's payload and set its checked bit. |
217 | Other.setPtr(nullptr); |
218 | Other.setChecked(true); |
219 | |
220 | return *this; |
221 | } |
222 | |
223 | /// Destroy a Error. Fails with a call to abort() if the error is |
224 | /// unchecked. |
225 | ~Error() { |
226 | assertIsChecked(); |
227 | delete getPtr(); |
228 | } |
229 | |
230 | /// Bool conversion. Returns true if this Error is in a failure state, |
231 | /// and false if it is in an accept state. If the error is in a Success state |
232 | /// it will be considered checked. |
233 | explicit operator bool() { |
234 | setChecked(getPtr() == nullptr); |
235 | return getPtr() != nullptr; |
236 | } |
237 | |
238 | /// Check whether one error is a subclass of another. |
239 | template <typename ErrT> bool isA() const { |
240 | return getPtr() && getPtr()->isA(ErrT::classID()); |
241 | } |
242 | |
243 | /// Returns the dynamic class id of this error, or null if this is a success |
244 | /// value. |
245 | const void* dynamicClassID() const { |
246 | if (!getPtr()) |
247 | return nullptr; |
248 | return getPtr()->dynamicClassID(); |
249 | } |
250 | |
251 | private: |
252 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
253 | // assertIsChecked() happens very frequently, but under normal circumstances |
254 | // is supposed to be a no-op. So we want it to be inlined, but having a bunch |
255 | // of debug prints can cause the function to be too large for inlining. So |
256 | // it's important that we define this function out of line so that it can't be |
257 | // inlined. |
258 | [[noreturn]] void fatalUncheckedError() const; |
259 | #endif |
260 | |
261 | void assertIsChecked() { |
262 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
263 | if (LLVM_UNLIKELY(!getChecked() || getPtr())__builtin_expect((bool)(!getChecked() || getPtr()), false)) |
264 | fatalUncheckedError(); |
265 | #endif |
266 | } |
267 | |
268 | ErrorInfoBase *getPtr() const { |
269 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
270 | return reinterpret_cast<ErrorInfoBase*>( |
271 | reinterpret_cast<uintptr_t>(Payload) & |
272 | ~static_cast<uintptr_t>(0x1)); |
273 | #else |
274 | return Payload; |
275 | #endif |
276 | } |
277 | |
278 | void setPtr(ErrorInfoBase *EI) { |
279 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
280 | Payload = reinterpret_cast<ErrorInfoBase*>( |
281 | (reinterpret_cast<uintptr_t>(EI) & |
282 | ~static_cast<uintptr_t>(0x1)) | |
283 | (reinterpret_cast<uintptr_t>(Payload) & 0x1)); |
284 | #else |
285 | Payload = EI; |
286 | #endif |
287 | } |
288 | |
289 | bool getChecked() const { |
290 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
291 | return (reinterpret_cast<uintptr_t>(Payload) & 0x1) == 0; |
292 | #else |
293 | return true; |
294 | #endif |
295 | } |
296 | |
297 | void setChecked(bool V) { |
298 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
299 | Payload = reinterpret_cast<ErrorInfoBase*>( |
300 | (reinterpret_cast<uintptr_t>(Payload) & |
301 | ~static_cast<uintptr_t>(0x1)) | |
302 | (V ? 0 : 1)); |
303 | #endif |
304 | } |
305 | |
306 | std::unique_ptr<ErrorInfoBase> takePayload() { |
307 | std::unique_ptr<ErrorInfoBase> Tmp(getPtr()); |
308 | setPtr(nullptr); |
309 | setChecked(true); |
310 | return Tmp; |
311 | } |
312 | |
313 | friend raw_ostream &operator<<(raw_ostream &OS, const Error &E) { |
314 | if (auto *P = E.getPtr()) |
315 | P->log(OS); |
316 | else |
317 | OS << "success"; |
318 | return OS; |
319 | } |
320 | |
321 | ErrorInfoBase *Payload = nullptr; |
322 | }; |
323 | |
324 | /// Subclass of Error for the sole purpose of identifying the success path in |
325 | /// the type system. This allows to catch invalid conversion to Expected<T> at |
326 | /// compile time. |
327 | class ErrorSuccess final : public Error {}; |
328 | |
329 | inline ErrorSuccess Error::success() { return ErrorSuccess(); } |
330 | |
331 | /// Make a Error instance representing failure using the given error info |
332 | /// type. |
333 | template <typename ErrT, typename... ArgTs> Error make_error(ArgTs &&... Args) { |
334 | return Error(std::make_unique<ErrT>(std::forward<ArgTs>(Args)...)); |
335 | } |
336 | |
337 | /// Base class for user error types. Users should declare their error types |
338 | /// like: |
339 | /// |
340 | /// class MyError : public ErrorInfo<MyError> { |
341 | /// .... |
342 | /// }; |
343 | /// |
344 | /// This class provides an implementation of the ErrorInfoBase::kind |
345 | /// method, which is used by the Error RTTI system. |
346 | template <typename ThisErrT, typename ParentErrT = ErrorInfoBase> |
347 | class ErrorInfo : public ParentErrT { |
348 | public: |
349 | using ParentErrT::ParentErrT; // inherit constructors |
350 | |
351 | static const void *classID() { return &ThisErrT::ID; } |
352 | |
353 | const void *dynamicClassID() const override { return &ThisErrT::ID; } |
354 | |
355 | bool isA(const void *const ClassID) const override { |
356 | return ClassID == classID() || ParentErrT::isA(ClassID); |
357 | } |
358 | }; |
359 | |
360 | /// Special ErrorInfo subclass representing a list of ErrorInfos. |
361 | /// Instances of this class are constructed by joinError. |
362 | class ErrorList final : public ErrorInfo<ErrorList> { |
363 | // handleErrors needs to be able to iterate the payload list of an |
364 | // ErrorList. |
365 | template <typename... HandlerTs> |
366 | friend Error handleErrors(Error E, HandlerTs &&... Handlers); |
367 | |
368 | // joinErrors is implemented in terms of join. |
369 | friend Error joinErrors(Error, Error); |
370 | |
371 | public: |
372 | void log(raw_ostream &OS) const override { |
373 | OS << "Multiple errors:\n"; |
374 | for (const auto &ErrPayload : Payloads) { |
375 | ErrPayload->log(OS); |
376 | OS << "\n"; |
377 | } |
378 | } |
379 | |
380 | std::error_code convertToErrorCode() const override; |
381 | |
382 | // Used by ErrorInfo::classID. |
383 | static char ID; |
384 | |
385 | private: |
386 | ErrorList(std::unique_ptr<ErrorInfoBase> Payload1, |
387 | std::unique_ptr<ErrorInfoBase> Payload2) { |
388 | assert(!Payload1->isA<ErrorList>() && !Payload2->isA<ErrorList>() &&(static_cast <bool> (!Payload1->isA<ErrorList> () && !Payload2->isA<ErrorList>() && "ErrorList constructor payloads should be singleton errors") ? void (0) : __assert_fail ("!Payload1->isA<ErrorList>() && !Payload2->isA<ErrorList>() && \"ErrorList constructor payloads should be singleton errors\"" , "llvm/include/llvm/Support/Error.h", 389, __extension__ __PRETTY_FUNCTION__ )) |
389 | "ErrorList constructor payloads should be singleton errors")(static_cast <bool> (!Payload1->isA<ErrorList> () && !Payload2->isA<ErrorList>() && "ErrorList constructor payloads should be singleton errors") ? void (0) : __assert_fail ("!Payload1->isA<ErrorList>() && !Payload2->isA<ErrorList>() && \"ErrorList constructor payloads should be singleton errors\"" , "llvm/include/llvm/Support/Error.h", 389, __extension__ __PRETTY_FUNCTION__ )); |
390 | Payloads.push_back(std::move(Payload1)); |
391 | Payloads.push_back(std::move(Payload2)); |
392 | } |
393 | |
394 | static Error join(Error E1, Error E2) { |
395 | if (!E1) |
396 | return E2; |
397 | if (!E2) |
398 | return E1; |
399 | if (E1.isA<ErrorList>()) { |
400 | auto &E1List = static_cast<ErrorList &>(*E1.getPtr()); |
401 | if (E2.isA<ErrorList>()) { |
402 | auto E2Payload = E2.takePayload(); |
403 | auto &E2List = static_cast<ErrorList &>(*E2Payload); |
404 | for (auto &Payload : E2List.Payloads) |
405 | E1List.Payloads.push_back(std::move(Payload)); |
406 | } else |
407 | E1List.Payloads.push_back(E2.takePayload()); |
408 | |
409 | return E1; |
410 | } |
411 | if (E2.isA<ErrorList>()) { |
412 | auto &E2List = static_cast<ErrorList &>(*E2.getPtr()); |
413 | E2List.Payloads.insert(E2List.Payloads.begin(), E1.takePayload()); |
414 | return E2; |
415 | } |
416 | return Error(std::unique_ptr<ErrorList>( |
417 | new ErrorList(E1.takePayload(), E2.takePayload()))); |
418 | } |
419 | |
420 | std::vector<std::unique_ptr<ErrorInfoBase>> Payloads; |
421 | }; |
422 | |
423 | /// Concatenate errors. The resulting Error is unchecked, and contains the |
424 | /// ErrorInfo(s), if any, contained in E1, followed by the |
425 | /// ErrorInfo(s), if any, contained in E2. |
426 | inline Error joinErrors(Error E1, Error E2) { |
427 | return ErrorList::join(std::move(E1), std::move(E2)); |
428 | } |
429 | |
430 | /// Tagged union holding either a T or a Error. |
431 | /// |
432 | /// This class parallels ErrorOr, but replaces error_code with Error. Since |
433 | /// Error cannot be copied, this class replaces getError() with |
434 | /// takeError(). It also adds an bool errorIsA<ErrT>() method for testing the |
435 | /// error class type. |
436 | /// |
437 | /// Example usage of 'Expected<T>' as a function return type: |
438 | /// |
439 | /// @code{.cpp} |
440 | /// Expected<int> myDivide(int A, int B) { |
441 | /// if (B == 0) { |
442 | /// // return an Error |
443 | /// return createStringError(inconvertibleErrorCode(), |
444 | /// "B must not be zero!"); |
445 | /// } |
446 | /// // return an integer |
447 | /// return A / B; |
448 | /// } |
449 | /// @endcode |
450 | /// |
451 | /// Checking the results of to a function returning 'Expected<T>': |
452 | /// @code{.cpp} |
453 | /// if (auto E = Result.takeError()) { |
454 | /// // We must consume the error. Typically one of: |
455 | /// // - return the error to our caller |
456 | /// // - toString(), when logging |
457 | /// // - consumeError(), to silently swallow the error |
458 | /// // - handleErrors(), to distinguish error types |
459 | /// errs() << "Problem with division " << toString(std::move(E)) << "\n"; |
460 | /// return; |
461 | /// } |
462 | /// // use the result |
463 | /// outs() << "The answer is " << *Result << "\n"; |
464 | /// @endcode |
465 | /// |
466 | /// For unit-testing a function returning an 'Expceted<T>', see the |
467 | /// 'EXPECT_THAT_EXPECTED' macros in llvm/Testing/Support/Error.h |
468 | |
469 | template <class T> class LLVM_NODISCARD[[clang::warn_unused_result]] Expected { |
470 | template <class T1> friend class ExpectedAsOutParameter; |
471 | template <class OtherT> friend class Expected; |
472 | |
473 | static constexpr bool isRef = std::is_reference<T>::value; |
474 | |
475 | using wrap = std::reference_wrapper<std::remove_reference_t<T>>; |
476 | |
477 | using error_type = std::unique_ptr<ErrorInfoBase>; |
478 | |
479 | public: |
480 | using storage_type = std::conditional_t<isRef, wrap, T>; |
481 | using value_type = T; |
482 | |
483 | private: |
484 | using reference = std::remove_reference_t<T> &; |
485 | using const_reference = const std::remove_reference_t<T> &; |
486 | using pointer = std::remove_reference_t<T> *; |
487 | using const_pointer = const std::remove_reference_t<T> *; |
488 | |
489 | public: |
490 | /// Create an Expected<T> error value from the given Error. |
491 | Expected(Error Err) |
492 | : HasError(true) |
493 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
494 | // Expected is unchecked upon construction in Debug builds. |
495 | , Unchecked(true) |
496 | #endif |
497 | { |
498 | assert(Err && "Cannot create Expected<T> from Error success value.")(static_cast <bool> (Err && "Cannot create Expected<T> from Error success value." ) ? void (0) : __assert_fail ("Err && \"Cannot create Expected<T> from Error success value.\"" , "llvm/include/llvm/Support/Error.h", 498, __extension__ __PRETTY_FUNCTION__ )); |
499 | new (getErrorStorage()) error_type(Err.takePayload()); |
500 | } |
501 | |
502 | /// Forbid to convert from Error::success() implicitly, this avoids having |
503 | /// Expected<T> foo() { return Error::success(); } which compiles otherwise |
504 | /// but triggers the assertion above. |
505 | Expected(ErrorSuccess) = delete; |
506 | |
507 | /// Create an Expected<T> success value from the given OtherT value, which |
508 | /// must be convertible to T. |
509 | template <typename OtherT> |
510 | Expected(OtherT &&Val, |
511 | std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) |
512 | : HasError(false) |
513 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
514 | // Expected is unchecked upon construction in Debug builds. |
515 | , |
516 | Unchecked(true) |
517 | #endif |
518 | { |
519 | new (getStorage()) storage_type(std::forward<OtherT>(Val)); |
520 | } |
521 | |
522 | /// Move construct an Expected<T> value. |
523 | Expected(Expected &&Other) { moveConstruct(std::move(Other)); } |
524 | |
525 | /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT |
526 | /// must be convertible to T. |
527 | template <class OtherT> |
528 | Expected( |
529 | Expected<OtherT> &&Other, |
530 | std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) { |
531 | moveConstruct(std::move(Other)); |
532 | } |
533 | |
534 | /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT |
535 | /// isn't convertible to T. |
536 | template <class OtherT> |
537 | explicit Expected( |
538 | Expected<OtherT> &&Other, |
539 | std::enable_if_t<!std::is_convertible<OtherT, T>::value> * = nullptr) { |
540 | moveConstruct(std::move(Other)); |
541 | } |
542 | |
543 | /// Move-assign from another Expected<T>. |
544 | Expected &operator=(Expected &&Other) { |
545 | moveAssign(std::move(Other)); |
546 | return *this; |
547 | } |
548 | |
549 | /// Destroy an Expected<T>. |
550 | ~Expected() { |
551 | assertIsChecked(); |
552 | if (!HasError) |
553 | getStorage()->~storage_type(); |
554 | else |
555 | getErrorStorage()->~error_type(); |
556 | } |
557 | |
558 | /// Return false if there is an error. |
559 | explicit operator bool() { |
560 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
561 | Unchecked = HasError; |
562 | #endif |
563 | return !HasError; |
564 | } |
565 | |
566 | /// Returns a reference to the stored T value. |
567 | reference get() { |
568 | assertIsChecked(); |
569 | return *getStorage(); |
570 | } |
571 | |
572 | /// Returns a const reference to the stored T value. |
573 | const_reference get() const { |
574 | assertIsChecked(); |
575 | return const_cast<Expected<T> *>(this)->get(); |
576 | } |
577 | |
578 | /// Returns \a takeError() after moving the held T (if any) into \p V. |
579 | template <class OtherT> |
580 | Error moveInto(OtherT &Value, |
581 | std::enable_if_t<std::is_assignable<OtherT &, T &&>::value> * = |
582 | nullptr) && { |
583 | if (*this) |
584 | Value = std::move(get()); |
585 | return takeError(); |
586 | } |
587 | |
588 | /// Check that this Expected<T> is an error of type ErrT. |
589 | template <typename ErrT> bool errorIsA() const { |
590 | return HasError && (*getErrorStorage())->template isA<ErrT>(); |
591 | } |
592 | |
593 | /// Take ownership of the stored error. |
594 | /// After calling this the Expected<T> is in an indeterminate state that can |
595 | /// only be safely destructed. No further calls (beside the destructor) should |
596 | /// be made on the Expected<T> value. |
597 | Error takeError() { |
598 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
599 | Unchecked = false; |
600 | #endif |
601 | return HasError ? Error(std::move(*getErrorStorage())) : Error::success(); |
602 | } |
603 | |
604 | /// Returns a pointer to the stored T value. |
605 | pointer operator->() { |
606 | assertIsChecked(); |
607 | return toPointer(getStorage()); |
608 | } |
609 | |
610 | /// Returns a const pointer to the stored T value. |
611 | const_pointer operator->() const { |
612 | assertIsChecked(); |
613 | return toPointer(getStorage()); |
614 | } |
615 | |
616 | /// Returns a reference to the stored T value. |
617 | reference operator*() { |
618 | assertIsChecked(); |
619 | return *getStorage(); |
620 | } |
621 | |
622 | /// Returns a const reference to the stored T value. |
623 | const_reference operator*() const { |
624 | assertIsChecked(); |
625 | return *getStorage(); |
626 | } |
627 | |
628 | private: |
629 | template <class T1> |
630 | static bool compareThisIfSameType(const T1 &a, const T1 &b) { |
631 | return &a == &b; |
632 | } |
633 | |
634 | template <class T1, class T2> |
635 | static bool compareThisIfSameType(const T1 &, const T2 &) { |
636 | return false; |
637 | } |
638 | |
639 | template <class OtherT> void moveConstruct(Expected<OtherT> &&Other) { |
640 | HasError = Other.HasError; |
641 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
642 | Unchecked = true; |
643 | Other.Unchecked = false; |
644 | #endif |
645 | |
646 | if (!HasError) |
647 | new (getStorage()) storage_type(std::move(*Other.getStorage())); |
648 | else |
649 | new (getErrorStorage()) error_type(std::move(*Other.getErrorStorage())); |
650 | } |
651 | |
652 | template <class OtherT> void moveAssign(Expected<OtherT> &&Other) { |
653 | assertIsChecked(); |
654 | |
655 | if (compareThisIfSameType(*this, Other)) |
656 | return; |
657 | |
658 | this->~Expected(); |
659 | new (this) Expected(std::move(Other)); |
660 | } |
661 | |
662 | pointer toPointer(pointer Val) { return Val; } |
663 | |
664 | const_pointer toPointer(const_pointer Val) const { return Val; } |
665 | |
666 | pointer toPointer(wrap *Val) { return &Val->get(); } |
667 | |
668 | const_pointer toPointer(const wrap *Val) const { return &Val->get(); } |
669 | |
670 | storage_type *getStorage() { |
671 | assert(!HasError && "Cannot get value when an error exists!")(static_cast <bool> (!HasError && "Cannot get value when an error exists!" ) ? void (0) : __assert_fail ("!HasError && \"Cannot get value when an error exists!\"" , "llvm/include/llvm/Support/Error.h", 671, __extension__ __PRETTY_FUNCTION__ )); |
672 | return reinterpret_cast<storage_type *>(&TStorage); |
673 | } |
674 | |
675 | const storage_type *getStorage() const { |
676 | assert(!HasError && "Cannot get value when an error exists!")(static_cast <bool> (!HasError && "Cannot get value when an error exists!" ) ? void (0) : __assert_fail ("!HasError && \"Cannot get value when an error exists!\"" , "llvm/include/llvm/Support/Error.h", 676, __extension__ __PRETTY_FUNCTION__ )); |
677 | return reinterpret_cast<const storage_type *>(&TStorage); |
678 | } |
679 | |
680 | error_type *getErrorStorage() { |
681 | assert(HasError && "Cannot get error when a value exists!")(static_cast <bool> (HasError && "Cannot get error when a value exists!" ) ? void (0) : __assert_fail ("HasError && \"Cannot get error when a value exists!\"" , "llvm/include/llvm/Support/Error.h", 681, __extension__ __PRETTY_FUNCTION__ )); |
682 | return reinterpret_cast<error_type *>(&ErrorStorage); |
683 | } |
684 | |
685 | const error_type *getErrorStorage() const { |
686 | assert(HasError && "Cannot get error when a value exists!")(static_cast <bool> (HasError && "Cannot get error when a value exists!" ) ? void (0) : __assert_fail ("HasError && \"Cannot get error when a value exists!\"" , "llvm/include/llvm/Support/Error.h", 686, __extension__ __PRETTY_FUNCTION__ )); |
687 | return reinterpret_cast<const error_type *>(&ErrorStorage); |
688 | } |
689 | |
690 | // Used by ExpectedAsOutParameter to reset the checked flag. |
691 | void setUnchecked() { |
692 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
693 | Unchecked = true; |
694 | #endif |
695 | } |
696 | |
697 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
698 | [[noreturn]] LLVM_ATTRIBUTE_NOINLINE__attribute__((noinline)) void fatalUncheckedExpected() const { |
699 | dbgs() << "Expected<T> must be checked before access or destruction.\n"; |
700 | if (HasError) { |
701 | dbgs() << "Unchecked Expected<T> contained error:\n"; |
702 | (*getErrorStorage())->log(dbgs()); |
703 | } else |
704 | dbgs() << "Expected<T> value was in success state. (Note: Expected<T> " |
705 | "values in success mode must still be checked prior to being " |
706 | "destroyed).\n"; |
707 | abort(); |
708 | } |
709 | #endif |
710 | |
711 | void assertIsChecked() const { |
712 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
713 | if (LLVM_UNLIKELY(Unchecked)__builtin_expect((bool)(Unchecked), false)) |
714 | fatalUncheckedExpected(); |
715 | #endif |
716 | } |
717 | |
718 | union { |
719 | AlignedCharArrayUnion<storage_type> TStorage; |
720 | AlignedCharArrayUnion<error_type> ErrorStorage; |
721 | }; |
722 | bool HasError : 1; |
723 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
724 | bool Unchecked : 1; |
725 | #endif |
726 | }; |
727 | |
728 | /// Report a serious error, calling any installed error handler. See |
729 | /// ErrorHandling.h. |
730 | [[noreturn]] void report_fatal_error(Error Err, bool gen_crash_diag = true); |
731 | |
732 | /// Report a fatal error if Err is a failure value. |
733 | /// |
734 | /// This function can be used to wrap calls to fallible functions ONLY when it |
735 | /// is known that the Error will always be a success value. E.g. |
736 | /// |
737 | /// @code{.cpp} |
738 | /// // foo only attempts the fallible operation if DoFallibleOperation is |
739 | /// // true. If DoFallibleOperation is false then foo always returns |
740 | /// // Error::success(). |
741 | /// Error foo(bool DoFallibleOperation); |
742 | /// |
743 | /// cantFail(foo(false)); |
744 | /// @endcode |
745 | inline void cantFail(Error Err, const char *Msg = nullptr) { |
746 | if (Err) { |
747 | if (!Msg) |
748 | Msg = "Failure value returned from cantFail wrapped call"; |
749 | #ifndef NDEBUG |
750 | std::string Str; |
751 | raw_string_ostream OS(Str); |
752 | OS << Msg << "\n" << Err; |
753 | Msg = OS.str().c_str(); |
754 | #endif |
755 | llvm_unreachable(Msg)::llvm::llvm_unreachable_internal(Msg, "llvm/include/llvm/Support/Error.h" , 755); |
756 | } |
757 | } |
758 | |
759 | /// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and |
760 | /// returns the contained value. |
761 | /// |
762 | /// This function can be used to wrap calls to fallible functions ONLY when it |
763 | /// is known that the Error will always be a success value. E.g. |
764 | /// |
765 | /// @code{.cpp} |
766 | /// // foo only attempts the fallible operation if DoFallibleOperation is |
767 | /// // true. If DoFallibleOperation is false then foo always returns an int. |
768 | /// Expected<int> foo(bool DoFallibleOperation); |
769 | /// |
770 | /// int X = cantFail(foo(false)); |
771 | /// @endcode |
772 | template <typename T> |
773 | T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) { |
774 | if (ValOrErr) |
775 | return std::move(*ValOrErr); |
776 | else { |
777 | if (!Msg) |
778 | Msg = "Failure value returned from cantFail wrapped call"; |
779 | #ifndef NDEBUG |
780 | std::string Str; |
781 | raw_string_ostream OS(Str); |
782 | auto E = ValOrErr.takeError(); |
783 | OS << Msg << "\n" << E; |
784 | Msg = OS.str().c_str(); |
785 | #endif |
786 | llvm_unreachable(Msg)::llvm::llvm_unreachable_internal(Msg, "llvm/include/llvm/Support/Error.h" , 786); |
787 | } |
788 | } |
789 | |
790 | /// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and |
791 | /// returns the contained reference. |
792 | /// |
793 | /// This function can be used to wrap calls to fallible functions ONLY when it |
794 | /// is known that the Error will always be a success value. E.g. |
795 | /// |
796 | /// @code{.cpp} |
797 | /// // foo only attempts the fallible operation if DoFallibleOperation is |
798 | /// // true. If DoFallibleOperation is false then foo always returns a Bar&. |
799 | /// Expected<Bar&> foo(bool DoFallibleOperation); |
800 | /// |
801 | /// Bar &X = cantFail(foo(false)); |
802 | /// @endcode |
803 | template <typename T> |
804 | T& cantFail(Expected<T&> ValOrErr, const char *Msg = nullptr) { |
805 | if (ValOrErr) |
806 | return *ValOrErr; |
807 | else { |
808 | if (!Msg) |
809 | Msg = "Failure value returned from cantFail wrapped call"; |
810 | #ifndef NDEBUG |
811 | std::string Str; |
812 | raw_string_ostream OS(Str); |
813 | auto E = ValOrErr.takeError(); |
814 | OS << Msg << "\n" << E; |
815 | Msg = OS.str().c_str(); |
816 | #endif |
817 | llvm_unreachable(Msg)::llvm::llvm_unreachable_internal(Msg, "llvm/include/llvm/Support/Error.h" , 817); |
818 | } |
819 | } |
820 | |
821 | /// Helper for testing applicability of, and applying, handlers for |
822 | /// ErrorInfo types. |
823 | template <typename HandlerT> |
824 | class ErrorHandlerTraits |
825 | : public ErrorHandlerTraits<decltype( |
826 | &std::remove_reference<HandlerT>::type::operator())> {}; |
827 | |
828 | // Specialization functions of the form 'Error (const ErrT&)'. |
829 | template <typename ErrT> class ErrorHandlerTraits<Error (&)(ErrT &)> { |
830 | public: |
831 | static bool appliesTo(const ErrorInfoBase &E) { |
832 | return E.template isA<ErrT>(); |
833 | } |
834 | |
835 | template <typename HandlerT> |
836 | static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { |
837 | assert(appliesTo(*E) && "Applying incorrect handler")(static_cast <bool> (appliesTo(*E) && "Applying incorrect handler" ) ? void (0) : __assert_fail ("appliesTo(*E) && \"Applying incorrect handler\"" , "llvm/include/llvm/Support/Error.h", 837, __extension__ __PRETTY_FUNCTION__ )); |
838 | return H(static_cast<ErrT &>(*E)); |
839 | } |
840 | }; |
841 | |
842 | // Specialization functions of the form 'void (const ErrT&)'. |
843 | template <typename ErrT> class ErrorHandlerTraits<void (&)(ErrT &)> { |
844 | public: |
845 | static bool appliesTo(const ErrorInfoBase &E) { |
846 | return E.template isA<ErrT>(); |
847 | } |
848 | |
849 | template <typename HandlerT> |
850 | static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { |
851 | assert(appliesTo(*E) && "Applying incorrect handler")(static_cast <bool> (appliesTo(*E) && "Applying incorrect handler" ) ? void (0) : __assert_fail ("appliesTo(*E) && \"Applying incorrect handler\"" , "llvm/include/llvm/Support/Error.h", 851, __extension__ __PRETTY_FUNCTION__ )); |
852 | H(static_cast<ErrT &>(*E)); |
853 | return Error::success(); |
854 | } |
855 | }; |
856 | |
857 | /// Specialization for functions of the form 'Error (std::unique_ptr<ErrT>)'. |
858 | template <typename ErrT> |
859 | class ErrorHandlerTraits<Error (&)(std::unique_ptr<ErrT>)> { |
860 | public: |
861 | static bool appliesTo(const ErrorInfoBase &E) { |
862 | return E.template isA<ErrT>(); |
863 | } |
864 | |
865 | template <typename HandlerT> |
866 | static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { |
867 | assert(appliesTo(*E) && "Applying incorrect handler")(static_cast <bool> (appliesTo(*E) && "Applying incorrect handler" ) ? void (0) : __assert_fail ("appliesTo(*E) && \"Applying incorrect handler\"" , "llvm/include/llvm/Support/Error.h", 867, __extension__ __PRETTY_FUNCTION__ )); |
868 | std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release())); |
869 | return H(std::move(SubE)); |
870 | } |
871 | }; |
872 | |
873 | /// Specialization for functions of the form 'void (std::unique_ptr<ErrT>)'. |
874 | template <typename ErrT> |
875 | class ErrorHandlerTraits<void (&)(std::unique_ptr<ErrT>)> { |
876 | public: |
877 | static bool appliesTo(const ErrorInfoBase &E) { |
878 | return E.template isA<ErrT>(); |
879 | } |
880 | |
881 | template <typename HandlerT> |
882 | static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { |
883 | assert(appliesTo(*E) && "Applying incorrect handler")(static_cast <bool> (appliesTo(*E) && "Applying incorrect handler" ) ? void (0) : __assert_fail ("appliesTo(*E) && \"Applying incorrect handler\"" , "llvm/include/llvm/Support/Error.h", 883, __extension__ __PRETTY_FUNCTION__ )); |
884 | std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release())); |
885 | H(std::move(SubE)); |
886 | return Error::success(); |
887 | } |
888 | }; |
889 | |
890 | // Specialization for member functions of the form 'RetT (const ErrT&)'. |
891 | template <typename C, typename RetT, typename ErrT> |
892 | class ErrorHandlerTraits<RetT (C::*)(ErrT &)> |
893 | : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; |
894 | |
895 | // Specialization for member functions of the form 'RetT (const ErrT&) const'. |
896 | template <typename C, typename RetT, typename ErrT> |
897 | class ErrorHandlerTraits<RetT (C::*)(ErrT &) const> |
898 | : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; |
899 | |
900 | // Specialization for member functions of the form 'RetT (const ErrT&)'. |
901 | template <typename C, typename RetT, typename ErrT> |
902 | class ErrorHandlerTraits<RetT (C::*)(const ErrT &)> |
903 | : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; |
904 | |
905 | // Specialization for member functions of the form 'RetT (const ErrT&) const'. |
906 | template <typename C, typename RetT, typename ErrT> |
907 | class ErrorHandlerTraits<RetT (C::*)(const ErrT &) const> |
908 | : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; |
909 | |
910 | /// Specialization for member functions of the form |
911 | /// 'RetT (std::unique_ptr<ErrT>)'. |
912 | template <typename C, typename RetT, typename ErrT> |
913 | class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>)> |
914 | : public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {}; |
915 | |
916 | /// Specialization for member functions of the form |
917 | /// 'RetT (std::unique_ptr<ErrT>) const'. |
918 | template <typename C, typename RetT, typename ErrT> |
919 | class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>) const> |
920 | : public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {}; |
921 | |
922 | inline Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload) { |
923 | return Error(std::move(Payload)); |
924 | } |
925 | |
926 | template <typename HandlerT, typename... HandlerTs> |
927 | Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload, |
928 | HandlerT &&Handler, HandlerTs &&... Handlers) { |
929 | if (ErrorHandlerTraits<HandlerT>::appliesTo(*Payload)) |
930 | return ErrorHandlerTraits<HandlerT>::apply(std::forward<HandlerT>(Handler), |
931 | std::move(Payload)); |
932 | return handleErrorImpl(std::move(Payload), |
933 | std::forward<HandlerTs>(Handlers)...); |
934 | } |
935 | |
936 | /// Pass the ErrorInfo(s) contained in E to their respective handlers. Any |
937 | /// unhandled errors (or Errors returned by handlers) are re-concatenated and |
938 | /// returned. |
939 | /// Because this function returns an error, its result must also be checked |
940 | /// or returned. If you intend to handle all errors use handleAllErrors |
941 | /// (which returns void, and will abort() on unhandled errors) instead. |
942 | template <typename... HandlerTs> |
943 | Error handleErrors(Error E, HandlerTs &&... Hs) { |
944 | if (!E) |
945 | return Error::success(); |
946 | |
947 | std::unique_ptr<ErrorInfoBase> Payload = E.takePayload(); |
948 | |
949 | if (Payload->isA<ErrorList>()) { |
950 | ErrorList &List = static_cast<ErrorList &>(*Payload); |
951 | Error R; |
952 | for (auto &P : List.Payloads) |
953 | R = ErrorList::join( |
954 | std::move(R), |
955 | handleErrorImpl(std::move(P), std::forward<HandlerTs>(Hs)...)); |
956 | return R; |
957 | } |
958 | |
959 | return handleErrorImpl(std::move(Payload), std::forward<HandlerTs>(Hs)...); |
960 | } |
961 | |
962 | /// Behaves the same as handleErrors, except that by contract all errors |
963 | /// *must* be handled by the given handlers (i.e. there must be no remaining |
964 | /// errors after running the handlers, or llvm_unreachable is called). |
965 | template <typename... HandlerTs> |
966 | void handleAllErrors(Error E, HandlerTs &&... Handlers) { |
967 | cantFail(handleErrors(std::move(E), std::forward<HandlerTs>(Handlers)...)); |
968 | } |
969 | |
970 | /// Check that E is a non-error, then drop it. |
971 | /// If E is an error, llvm_unreachable will be called. |
972 | inline void handleAllErrors(Error E) { |
973 | cantFail(std::move(E)); |
974 | } |
975 | |
976 | /// Handle any errors (if present) in an Expected<T>, then try a recovery path. |
977 | /// |
978 | /// If the incoming value is a success value it is returned unmodified. If it |
979 | /// is a failure value then it the contained error is passed to handleErrors. |
980 | /// If handleErrors is able to handle the error then the RecoveryPath functor |
981 | /// is called to supply the final result. If handleErrors is not able to |
982 | /// handle all errors then the unhandled errors are returned. |
983 | /// |
984 | /// This utility enables the follow pattern: |
985 | /// |
986 | /// @code{.cpp} |
987 | /// enum FooStrategy { Aggressive, Conservative }; |
988 | /// Expected<Foo> foo(FooStrategy S); |
989 | /// |
990 | /// auto ResultOrErr = |
991 | /// handleExpected( |
992 | /// foo(Aggressive), |
993 | /// []() { return foo(Conservative); }, |
994 | /// [](AggressiveStrategyError&) { |
995 | /// // Implicitly conusme this - we'll recover by using a conservative |
996 | /// // strategy. |
997 | /// }); |
998 | /// |
999 | /// @endcode |
1000 | template <typename T, typename RecoveryFtor, typename... HandlerTs> |
1001 | Expected<T> handleExpected(Expected<T> ValOrErr, RecoveryFtor &&RecoveryPath, |
1002 | HandlerTs &&... Handlers) { |
1003 | if (ValOrErr) |
1004 | return ValOrErr; |
1005 | |
1006 | if (auto Err = handleErrors(ValOrErr.takeError(), |
1007 | std::forward<HandlerTs>(Handlers)...)) |
1008 | return std::move(Err); |
1009 | |
1010 | return RecoveryPath(); |
1011 | } |
1012 | |
1013 | /// Log all errors (if any) in E to OS. If there are any errors, ErrorBanner |
1014 | /// will be printed before the first one is logged. A newline will be printed |
1015 | /// after each error. |
1016 | /// |
1017 | /// This function is compatible with the helpers from Support/WithColor.h. You |
1018 | /// can pass any of them as the OS. Please consider using them instead of |
1019 | /// including 'error: ' in the ErrorBanner. |
1020 | /// |
1021 | /// This is useful in the base level of your program to allow clean termination |
1022 | /// (allowing clean deallocation of resources, etc.), while reporting error |
1023 | /// information to the user. |
1024 | void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner = {}); |
1025 | |
1026 | /// Write all error messages (if any) in E to a string. The newline character |
1027 | /// is used to separate error messages. |
1028 | inline std::string toString(Error E) { |
1029 | SmallVector<std::string, 2> Errors; |
1030 | handleAllErrors(std::move(E), [&Errors](const ErrorInfoBase &EI) { |
1031 | Errors.push_back(EI.message()); |
1032 | }); |
1033 | return join(Errors.begin(), Errors.end(), "\n"); |
1034 | } |
1035 | |
1036 | /// Consume a Error without doing anything. This method should be used |
1037 | /// only where an error can be considered a reasonable and expected return |
1038 | /// value. |
1039 | /// |
1040 | /// Uses of this method are potentially indicative of design problems: If it's |
1041 | /// legitimate to do nothing while processing an "error", the error-producer |
1042 | /// might be more clearly refactored to return an Optional<T>. |
1043 | inline void consumeError(Error Err) { |
1044 | handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {}); |
1045 | } |
1046 | |
1047 | /// Convert an Expected to an Optional without doing anything. This method |
1048 | /// should be used only where an error can be considered a reasonable and |
1049 | /// expected return value. |
1050 | /// |
1051 | /// Uses of this method are potentially indicative of problems: perhaps the |
1052 | /// error should be propagated further, or the error-producer should just |
1053 | /// return an Optional in the first place. |
1054 | template <typename T> Optional<T> expectedToOptional(Expected<T> &&E) { |
1055 | if (E) |
1056 | return std::move(*E); |
1057 | consumeError(E.takeError()); |
1058 | return None; |
1059 | } |
1060 | |
1061 | /// Helper for converting an Error to a bool. |
1062 | /// |
1063 | /// This method returns true if Err is in an error state, or false if it is |
1064 | /// in a success state. Puts Err in a checked state in both cases (unlike |
1065 | /// Error::operator bool(), which only does this for success states). |
1066 | inline bool errorToBool(Error Err) { |
1067 | bool IsError = static_cast<bool>(Err); |
1068 | if (IsError) |
1069 | consumeError(std::move(Err)); |
1070 | return IsError; |
1071 | } |
1072 | |
1073 | /// Helper for Errors used as out-parameters. |
1074 | /// |
1075 | /// This helper is for use with the Error-as-out-parameter idiom, where an error |
1076 | /// is passed to a function or method by reference, rather than being returned. |
1077 | /// In such cases it is helpful to set the checked bit on entry to the function |
1078 | /// so that the error can be written to (unchecked Errors abort on assignment) |
1079 | /// and clear the checked bit on exit so that clients cannot accidentally forget |
1080 | /// to check the result. This helper performs these actions automatically using |
1081 | /// RAII: |
1082 | /// |
1083 | /// @code{.cpp} |
1084 | /// Result foo(Error &Err) { |
1085 | /// ErrorAsOutParameter ErrAsOutParam(&Err); // 'Checked' flag set |
1086 | /// // <body of foo> |
1087 | /// // <- 'Checked' flag auto-cleared when ErrAsOutParam is destructed. |
1088 | /// } |
1089 | /// @endcode |
1090 | /// |
1091 | /// ErrorAsOutParameter takes an Error* rather than Error& so that it can be |
1092 | /// used with optional Errors (Error pointers that are allowed to be null). If |
1093 | /// ErrorAsOutParameter took an Error reference, an instance would have to be |
1094 | /// created inside every condition that verified that Error was non-null. By |
1095 | /// taking an Error pointer we can just create one instance at the top of the |
1096 | /// function. |
1097 | class ErrorAsOutParameter { |
1098 | public: |
1099 | ErrorAsOutParameter(Error *Err) : Err(Err) { |
1100 | // Raise the checked bit if Err is success. |
1101 | if (Err) |
1102 | (void)!!*Err; |
1103 | } |
1104 | |
1105 | ~ErrorAsOutParameter() { |
1106 | // Clear the checked bit. |
1107 | if (Err && !*Err) |
1108 | *Err = Error::success(); |
1109 | } |
1110 | |
1111 | private: |
1112 | Error *Err; |
1113 | }; |
1114 | |
1115 | /// Helper for Expected<T>s used as out-parameters. |
1116 | /// |
1117 | /// See ErrorAsOutParameter. |
1118 | template <typename T> |
1119 | class ExpectedAsOutParameter { |
1120 | public: |
1121 | ExpectedAsOutParameter(Expected<T> *ValOrErr) |
1122 | : ValOrErr(ValOrErr) { |
1123 | if (ValOrErr) |
1124 | (void)!!*ValOrErr; |
1125 | } |
1126 | |
1127 | ~ExpectedAsOutParameter() { |
1128 | if (ValOrErr) |
1129 | ValOrErr->setUnchecked(); |
1130 | } |
1131 | |
1132 | private: |
1133 | Expected<T> *ValOrErr; |
1134 | }; |
1135 | |
1136 | /// This class wraps a std::error_code in a Error. |
1137 | /// |
1138 | /// This is useful if you're writing an interface that returns a Error |
1139 | /// (or Expected) and you want to call code that still returns |
1140 | /// std::error_codes. |
1141 | class ECError : public ErrorInfo<ECError> { |
1142 | friend Error errorCodeToError(std::error_code); |
1143 | |
1144 | virtual void anchor() override; |
1145 | |
1146 | public: |
1147 | void setErrorCode(std::error_code EC) { this->EC = EC; } |
1148 | std::error_code convertToErrorCode() const override { return EC; } |
1149 | void log(raw_ostream &OS) const override { OS << EC.message(); } |
1150 | |
1151 | // Used by ErrorInfo::classID. |
1152 | static char ID; |
1153 | |
1154 | protected: |
1155 | ECError() = default; |
1156 | ECError(std::error_code EC) : EC(EC) {} |
1157 | |
1158 | std::error_code EC; |
1159 | }; |
1160 | |
1161 | /// The value returned by this function can be returned from convertToErrorCode |
1162 | /// for Error values where no sensible translation to std::error_code exists. |
1163 | /// It should only be used in this situation, and should never be used where a |
1164 | /// sensible conversion to std::error_code is available, as attempts to convert |
1165 | /// to/from this error will result in a fatal error. (i.e. it is a programmatic |
1166 | /// error to try to convert such a value). |
1167 | std::error_code inconvertibleErrorCode(); |
1168 | |
1169 | /// Helper for converting an std::error_code to a Error. |
1170 | Error errorCodeToError(std::error_code EC); |
1171 | |
1172 | /// Helper for converting an ECError to a std::error_code. |
1173 | /// |
1174 | /// This method requires that Err be Error() or an ECError, otherwise it |
1175 | /// will trigger a call to abort(). |
1176 | std::error_code errorToErrorCode(Error Err); |
1177 | |
1178 | /// Convert an ErrorOr<T> to an Expected<T>. |
1179 | template <typename T> Expected<T> errorOrToExpected(ErrorOr<T> &&EO) { |
1180 | if (auto EC = EO.getError()) |
1181 | return errorCodeToError(EC); |
1182 | return std::move(*EO); |
1183 | } |
1184 | |
1185 | /// Convert an Expected<T> to an ErrorOr<T>. |
1186 | template <typename T> ErrorOr<T> expectedToErrorOr(Expected<T> &&E) { |
1187 | if (auto Err = E.takeError()) |
1188 | return errorToErrorCode(std::move(Err)); |
1189 | return std::move(*E); |
1190 | } |
1191 | |
1192 | /// This class wraps a string in an Error. |
1193 | /// |
1194 | /// StringError is useful in cases where the client is not expected to be able |
1195 | /// to consume the specific error message programmatically (for example, if the |
1196 | /// error message is to be presented to the user). |
1197 | /// |
1198 | /// StringError can also be used when additional information is to be printed |
1199 | /// along with a error_code message. Depending on the constructor called, this |
1200 | /// class can either display: |
1201 | /// 1. the error_code message (ECError behavior) |
1202 | /// 2. a string |
1203 | /// 3. the error_code message and a string |
1204 | /// |
1205 | /// These behaviors are useful when subtyping is required; for example, when a |
1206 | /// specific library needs an explicit error type. In the example below, |
1207 | /// PDBError is derived from StringError: |
1208 | /// |
1209 | /// @code{.cpp} |
1210 | /// Expected<int> foo() { |
1211 | /// return llvm::make_error<PDBError>(pdb_error_code::dia_failed_loading, |
1212 | /// "Additional information"); |
1213 | /// } |
1214 | /// @endcode |
1215 | /// |
1216 | class StringError : public ErrorInfo<StringError> { |
1217 | public: |
1218 | static char ID; |
1219 | |
1220 | // Prints EC + S and converts to EC |
1221 | StringError(std::error_code EC, const Twine &S = Twine()); |
1222 | |
1223 | // Prints S and converts to EC |
1224 | StringError(const Twine &S, std::error_code EC); |
1225 | |
1226 | void log(raw_ostream &OS) const override; |
1227 | std::error_code convertToErrorCode() const override; |
1228 | |
1229 | const std::string &getMessage() const { return Msg; } |
1230 | |
1231 | private: |
1232 | std::string Msg; |
1233 | std::error_code EC; |
1234 | const bool PrintMsgOnly = false; |
1235 | }; |
1236 | |
1237 | /// Create formatted StringError object. |
1238 | template <typename... Ts> |
1239 | inline Error createStringError(std::error_code EC, char const *Fmt, |
1240 | const Ts &... Vals) { |
1241 | std::string Buffer; |
1242 | raw_string_ostream Stream(Buffer); |
1243 | Stream << format(Fmt, Vals...); |
1244 | return make_error<StringError>(Stream.str(), EC); |
1245 | } |
1246 | |
1247 | Error createStringError(std::error_code EC, char const *Msg); |
1248 | |
1249 | inline Error createStringError(std::error_code EC, const Twine &S) { |
1250 | return createStringError(EC, S.str().c_str()); |
1251 | } |
1252 | |
1253 | template <typename... Ts> |
1254 | inline Error createStringError(std::errc EC, char const *Fmt, |
1255 | const Ts &... Vals) { |
1256 | return createStringError(std::make_error_code(EC), Fmt, Vals...); |
1257 | } |
1258 | |
1259 | /// This class wraps a filename and another Error. |
1260 | /// |
1261 | /// In some cases, an error needs to live along a 'source' name, in order to |
1262 | /// show more detailed information to the user. |
1263 | class FileError final : public ErrorInfo<FileError> { |
1264 | |
1265 | friend Error createFileError(const Twine &, Error); |
1266 | friend Error createFileError(const Twine &, size_t, Error); |
1267 | |
1268 | public: |
1269 | void log(raw_ostream &OS) const override { |
1270 | assert(Err && "Trying to log after takeError().")(static_cast <bool> (Err && "Trying to log after takeError()." ) ? void (0) : __assert_fail ("Err && \"Trying to log after takeError().\"" , "llvm/include/llvm/Support/Error.h", 1270, __extension__ __PRETTY_FUNCTION__ )); |
1271 | OS << "'" << FileName << "': "; |
1272 | if (Line.hasValue()) |
1273 | OS << "line " << Line.getValue() << ": "; |
1274 | Err->log(OS); |
1275 | } |
1276 | |
1277 | std::string messageWithoutFileInfo() const { |
1278 | std::string Msg; |
1279 | raw_string_ostream OS(Msg); |
1280 | Err->log(OS); |
1281 | return OS.str(); |
1282 | } |
1283 | |
1284 | StringRef getFileName() { return FileName; } |
1285 | |
1286 | Error takeError() { return Error(std::move(Err)); } |
1287 | |
1288 | std::error_code convertToErrorCode() const override; |
1289 | |
1290 | // Used by ErrorInfo::classID. |
1291 | static char ID; |
1292 | |
1293 | private: |
1294 | FileError(const Twine &F, Optional<size_t> LineNum, |
1295 | std::unique_ptr<ErrorInfoBase> E) { |
1296 | assert(E && "Cannot create FileError from Error success value.")(static_cast <bool> (E && "Cannot create FileError from Error success value." ) ? void (0) : __assert_fail ("E && \"Cannot create FileError from Error success value.\"" , "llvm/include/llvm/Support/Error.h", 1296, __extension__ __PRETTY_FUNCTION__ )); |
1297 | FileName = F.str(); |
1298 | Err = std::move(E); |
1299 | Line = std::move(LineNum); |
1300 | } |
1301 | |
1302 | static Error build(const Twine &F, Optional<size_t> Line, Error E) { |
1303 | std::unique_ptr<ErrorInfoBase> Payload; |
1304 | handleAllErrors(std::move(E), |
1305 | [&](std::unique_ptr<ErrorInfoBase> EIB) -> Error { |
1306 | Payload = std::move(EIB); |
1307 | return Error::success(); |
1308 | }); |
1309 | return Error( |
1310 | std::unique_ptr<FileError>(new FileError(F, Line, std::move(Payload)))); |
1311 | } |
1312 | |
1313 | std::string FileName; |
1314 | Optional<size_t> Line; |
1315 | std::unique_ptr<ErrorInfoBase> Err; |
1316 | }; |
1317 | |
1318 | /// Concatenate a source file path and/or name with an Error. The resulting |
1319 | /// Error is unchecked. |
1320 | inline Error createFileError(const Twine &F, Error E) { |
1321 | return FileError::build(F, Optional<size_t>(), std::move(E)); |
1322 | } |
1323 | |
1324 | /// Concatenate a source file path and/or name with line number and an Error. |
1325 | /// The resulting Error is unchecked. |
1326 | inline Error createFileError(const Twine &F, size_t Line, Error E) { |
1327 | return FileError::build(F, Optional<size_t>(Line), std::move(E)); |
1328 | } |
1329 | |
1330 | /// Concatenate a source file path and/or name with a std::error_code |
1331 | /// to form an Error object. |
1332 | inline Error createFileError(const Twine &F, std::error_code EC) { |
1333 | return createFileError(F, errorCodeToError(EC)); |
1334 | } |
1335 | |
1336 | /// Concatenate a source file path and/or name with line number and |
1337 | /// std::error_code to form an Error object. |
1338 | inline Error createFileError(const Twine &F, size_t Line, std::error_code EC) { |
1339 | return createFileError(F, Line, errorCodeToError(EC)); |
1340 | } |
1341 | |
1342 | Error createFileError(const Twine &F, ErrorSuccess) = delete; |
1343 | |
1344 | /// Helper for check-and-exit error handling. |
1345 | /// |
1346 | /// For tool use only. NOT FOR USE IN LIBRARY CODE. |
1347 | /// |
1348 | class ExitOnError { |
1349 | public: |
1350 | /// Create an error on exit helper. |
1351 | ExitOnError(std::string Banner = "", int DefaultErrorExitCode = 1) |
1352 | : Banner(std::move(Banner)), |
1353 | GetExitCode([=](const Error &) { return DefaultErrorExitCode; }) {} |
1354 | |
1355 | /// Set the banner string for any errors caught by operator(). |
1356 | void setBanner(std::string Banner) { this->Banner = std::move(Banner); } |
1357 | |
1358 | /// Set the exit-code mapper function. |
1359 | void setExitCodeMapper(std::function<int(const Error &)> GetExitCode) { |
1360 | this->GetExitCode = std::move(GetExitCode); |
1361 | } |
1362 | |
1363 | /// Check Err. If it's in a failure state log the error(s) and exit. |
1364 | void operator()(Error Err) const { checkError(std::move(Err)); } |
1365 | |
1366 | /// Check E. If it's in a success state then return the contained value. If |
1367 | /// it's in a failure state log the error(s) and exit. |
1368 | template <typename T> T operator()(Expected<T> &&E) const { |
1369 | checkError(E.takeError()); |
1370 | return std::move(*E); |
1371 | } |
1372 | |
1373 | /// Check E. If it's in a success state then return the contained reference. If |
1374 | /// it's in a failure state log the error(s) and exit. |
1375 | template <typename T> T& operator()(Expected<T&> &&E) const { |
1376 | checkError(E.takeError()); |
1377 | return *E; |
1378 | } |
1379 | |
1380 | private: |
1381 | void checkError(Error Err) const { |
1382 | if (Err) { |
1383 | int ExitCode = GetExitCode(Err); |
1384 | logAllUnhandledErrors(std::move(Err), errs(), Banner); |
1385 | exit(ExitCode); |
1386 | } |
1387 | } |
1388 | |
1389 | std::string Banner; |
1390 | std::function<int(const Error &)> GetExitCode; |
1391 | }; |
1392 | |
1393 | /// Conversion from Error to LLVMErrorRef for C error bindings. |
1394 | inline LLVMErrorRef wrap(Error Err) { |
1395 | return reinterpret_cast<LLVMErrorRef>(Err.takePayload().release()); |
1396 | } |
1397 | |
1398 | /// Conversion from LLVMErrorRef to Error for C error bindings. |
1399 | inline Error unwrap(LLVMErrorRef ErrRef) { |
1400 | return Error(std::unique_ptr<ErrorInfoBase>( |
1401 | reinterpret_cast<ErrorInfoBase *>(ErrRef))); |
1402 | } |
1403 | |
1404 | } // end namespace llvm |
1405 | |
1406 | #endif // LLVM_SUPPORT_ERROR_H |