167c32a98SDimitry Andric //=-- SampleProf.cpp - Sample profiling format support --------------------===//
267c32a98SDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
667c32a98SDimitry Andric //
767c32a98SDimitry Andric //===----------------------------------------------------------------------===//
867c32a98SDimitry Andric //
967c32a98SDimitry Andric // This file contains common definitions used in the reading and writing of
1067c32a98SDimitry Andric // sample profile data.
1167c32a98SDimitry Andric //
1267c32a98SDimitry Andric //===----------------------------------------------------------------------===//
1367c32a98SDimitry Andric
1467c32a98SDimitry Andric #include "llvm/ProfileData/SampleProf.h"
15eb11fae6SDimitry Andric #include "llvm/Config/llvm-config.h"
16eb11fae6SDimitry Andric #include "llvm/IR/DebugInfoMetadata.h"
17b60736ecSDimitry Andric #include "llvm/IR/PseudoProbe.h"
18b60736ecSDimitry Andric #include "llvm/ProfileData/SampleProfReader.h"
19344a3780SDimitry Andric #include "llvm/Support/CommandLine.h"
2071d5a254SDimitry Andric #include "llvm/Support/Compiler.h"
2171d5a254SDimitry Andric #include "llvm/Support/Debug.h"
2267c32a98SDimitry Andric #include "llvm/Support/ErrorHandling.h"
2371d5a254SDimitry Andric #include "llvm/Support/raw_ostream.h"
2471d5a254SDimitry Andric #include <string>
2571d5a254SDimitry Andric #include <system_error>
2667c32a98SDimitry Andric
2767c32a98SDimitry Andric using namespace llvm;
2871d5a254SDimitry Andric using namespace sampleprof;
2967c32a98SDimitry Andric
30344a3780SDimitry Andric static cl::opt<uint64_t> ProfileSymbolListCutOff(
31145449b1SDimitry Andric "profile-symbol-list-cutoff", cl::Hidden, cl::init(-1),
32344a3780SDimitry Andric cl::desc("Cutoff value about how many symbols in profile symbol list "
33344a3780SDimitry Andric "will be used. This is very useful for performance debugging"));
34344a3780SDimitry Andric
357fa27ce4SDimitry Andric static cl::opt<bool> GenerateMergedBaseProfiles(
36145449b1SDimitry Andric "generate-merged-base-profiles",
3777fc4c14SDimitry Andric cl::desc("When generating nested context-sensitive profiles, always "
3877fc4c14SDimitry Andric "generate extra base profile for function with all its context "
3977fc4c14SDimitry Andric "profiles merged into it."));
4077fc4c14SDimitry Andric
41d8e91e46SDimitry Andric namespace llvm {
42d8e91e46SDimitry Andric namespace sampleprof {
43b60736ecSDimitry Andric bool FunctionSamples::ProfileIsProbeBased = false;
44145449b1SDimitry Andric bool FunctionSamples::ProfileIsCS = false;
45145449b1SDimitry Andric bool FunctionSamples::ProfileIsPreInlined = false;
46344a3780SDimitry Andric bool FunctionSamples::UseMD5 = false;
47344a3780SDimitry Andric bool FunctionSamples::HasUniqSuffix = true;
48344a3780SDimitry Andric bool FunctionSamples::ProfileIsFS = false;
49d8e91e46SDimitry Andric } // namespace sampleprof
50d8e91e46SDimitry Andric } // namespace llvm
51d8e91e46SDimitry Andric
5267c32a98SDimitry Andric namespace {
5371d5a254SDimitry Andric
5401095a5dSDimitry Andric // FIXME: This class is only here to support the transition to llvm::Error. It
5501095a5dSDimitry Andric // will be removed once this transition is complete. Clients should prefer to
5601095a5dSDimitry Andric // deal with the Error value directly, rather than converting to error_code.
5767c32a98SDimitry Andric class SampleProfErrorCategoryType : public std::error_category {
name() const58b915e9e0SDimitry Andric const char *name() const noexcept override { return "llvm.sampleprof"; }
5971d5a254SDimitry Andric
message(int IE) const6067c32a98SDimitry Andric std::string message(int IE) const override {
6167c32a98SDimitry Andric sampleprof_error E = static_cast<sampleprof_error>(IE);
6267c32a98SDimitry Andric switch (E) {
6367c32a98SDimitry Andric case sampleprof_error::success:
6467c32a98SDimitry Andric return "Success";
6567c32a98SDimitry Andric case sampleprof_error::bad_magic:
66dd58ef01SDimitry Andric return "Invalid sample profile data (bad magic)";
6767c32a98SDimitry Andric case sampleprof_error::unsupported_version:
68dd58ef01SDimitry Andric return "Unsupported sample profile format version";
6967c32a98SDimitry Andric case sampleprof_error::too_large:
7067c32a98SDimitry Andric return "Too much profile data";
7167c32a98SDimitry Andric case sampleprof_error::truncated:
7267c32a98SDimitry Andric return "Truncated profile data";
7367c32a98SDimitry Andric case sampleprof_error::malformed:
74dd58ef01SDimitry Andric return "Malformed sample profile data";
7567c32a98SDimitry Andric case sampleprof_error::unrecognized_format:
76dd58ef01SDimitry Andric return "Unrecognized sample profile encoding format";
77dd58ef01SDimitry Andric case sampleprof_error::unsupported_writing_format:
78dd58ef01SDimitry Andric return "Profile encoding format unsupported for writing operations";
79dd58ef01SDimitry Andric case sampleprof_error::truncated_name_table:
80dd58ef01SDimitry Andric return "Truncated function name table";
81dd58ef01SDimitry Andric case sampleprof_error::not_implemented:
82dd58ef01SDimitry Andric return "Unimplemented feature";
83dd58ef01SDimitry Andric case sampleprof_error::counter_overflow:
84dd58ef01SDimitry Andric return "Counter overflow";
85d8e91e46SDimitry Andric case sampleprof_error::ostream_seek_unsupported:
86d8e91e46SDimitry Andric return "Ostream does not support seek";
871d5ae102SDimitry Andric case sampleprof_error::uncompress_failed:
881d5ae102SDimitry Andric return "Uncompress failure";
891d5ae102SDimitry Andric case sampleprof_error::zlib_unavailable:
901d5ae102SDimitry Andric return "Zlib is unavailable";
91b60736ecSDimitry Andric case sampleprof_error::hash_mismatch:
92b60736ecSDimitry Andric return "Function hash mismatch";
9367c32a98SDimitry Andric }
9467c32a98SDimitry Andric llvm_unreachable("A value of sampleprof_error has no message.");
9567c32a98SDimitry Andric }
9667c32a98SDimitry Andric };
9771d5a254SDimitry Andric
9871d5a254SDimitry Andric } // end anonymous namespace
9967c32a98SDimitry Andric
sampleprof_category()10067c32a98SDimitry Andric const std::error_category &llvm::sampleprof_category() {
1011f917f69SDimitry Andric static SampleProfErrorCategoryType ErrorCategory;
1021f917f69SDimitry Andric return ErrorCategory;
10367c32a98SDimitry Andric }
104dd58ef01SDimitry Andric
print(raw_ostream & OS) const105dd58ef01SDimitry Andric void LineLocation::print(raw_ostream &OS) const {
106dd58ef01SDimitry Andric OS << LineOffset;
107dd58ef01SDimitry Andric if (Discriminator > 0)
108dd58ef01SDimitry Andric OS << "." << Discriminator;
109dd58ef01SDimitry Andric }
110dd58ef01SDimitry Andric
operator <<(raw_ostream & OS,const LineLocation & Loc)111dd58ef01SDimitry Andric raw_ostream &llvm::sampleprof::operator<<(raw_ostream &OS,
112dd58ef01SDimitry Andric const LineLocation &Loc) {
113dd58ef01SDimitry Andric Loc.print(OS);
114dd58ef01SDimitry Andric return OS;
115dd58ef01SDimitry Andric }
116dd58ef01SDimitry Andric
117344a3780SDimitry Andric /// Merge the samples in \p Other into this record.
118344a3780SDimitry Andric /// Optionally scale sample counts by \p Weight.
merge(const SampleRecord & Other,uint64_t Weight)119344a3780SDimitry Andric sampleprof_error SampleRecord::merge(const SampleRecord &Other,
120344a3780SDimitry Andric uint64_t Weight) {
121344a3780SDimitry Andric sampleprof_error Result;
122344a3780SDimitry Andric Result = addSamples(Other.getSamples(), Weight);
123344a3780SDimitry Andric for (const auto &I : Other.getCallTargets()) {
124ac9a064cSDimitry Andric mergeSampleProfErrors(Result, addCalledTarget(I.first, I.second, Weight));
125344a3780SDimitry Andric }
126344a3780SDimitry Andric return Result;
127344a3780SDimitry Andric }
128344a3780SDimitry Andric
12971d5a254SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
dump() const13001095a5dSDimitry Andric LLVM_DUMP_METHOD void LineLocation::dump() const { print(dbgs()); }
13171d5a254SDimitry Andric #endif
132dd58ef01SDimitry Andric
133eb11fae6SDimitry Andric /// Print the sample record to the stream \p OS indented by \p Indent.
print(raw_ostream & OS,unsigned Indent) const134dd58ef01SDimitry Andric void SampleRecord::print(raw_ostream &OS, unsigned Indent) const {
135dd58ef01SDimitry Andric OS << NumSamples;
136dd58ef01SDimitry Andric if (hasCalls()) {
137dd58ef01SDimitry Andric OS << ", calls:";
1381d5ae102SDimitry Andric for (const auto &I : getSortedCallTargets())
1391d5ae102SDimitry Andric OS << " " << I.first << ":" << I.second;
140dd58ef01SDimitry Andric }
141dd58ef01SDimitry Andric OS << "\n";
142dd58ef01SDimitry Andric }
143dd58ef01SDimitry Andric
14471d5a254SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
dump() const14501095a5dSDimitry Andric LLVM_DUMP_METHOD void SampleRecord::dump() const { print(dbgs(), 0); }
14671d5a254SDimitry Andric #endif
147dd58ef01SDimitry Andric
operator <<(raw_ostream & OS,const SampleRecord & Sample)148dd58ef01SDimitry Andric raw_ostream &llvm::sampleprof::operator<<(raw_ostream &OS,
149dd58ef01SDimitry Andric const SampleRecord &Sample) {
150dd58ef01SDimitry Andric Sample.print(OS, 0);
151dd58ef01SDimitry Andric return OS;
152dd58ef01SDimitry Andric }
153dd58ef01SDimitry Andric
154eb11fae6SDimitry Andric /// Print the samples collected for a function on stream \p OS.
print(raw_ostream & OS,unsigned Indent) const155dd58ef01SDimitry Andric void FunctionSamples::print(raw_ostream &OS, unsigned Indent) const {
156b60736ecSDimitry Andric if (getFunctionHash())
157b60736ecSDimitry Andric OS << "CFG checksum " << getFunctionHash() << "\n";
158b60736ecSDimitry Andric
159dd58ef01SDimitry Andric OS << TotalSamples << ", " << TotalHeadSamples << ", " << BodySamples.size()
160dd58ef01SDimitry Andric << " sampled lines\n";
161dd58ef01SDimitry Andric
162dd58ef01SDimitry Andric OS.indent(Indent);
16371d5a254SDimitry Andric if (!BodySamples.empty()) {
164dd58ef01SDimitry Andric OS << "Samples collected in the function's body {\n";
165dd58ef01SDimitry Andric SampleSorter<LineLocation, SampleRecord> SortedBodySamples(BodySamples);
166dd58ef01SDimitry Andric for (const auto &SI : SortedBodySamples.get()) {
167dd58ef01SDimitry Andric OS.indent(Indent + 2);
168dd58ef01SDimitry Andric OS << SI->first << ": " << SI->second;
169dd58ef01SDimitry Andric }
170dd58ef01SDimitry Andric OS.indent(Indent);
171dd58ef01SDimitry Andric OS << "}\n";
172dd58ef01SDimitry Andric } else {
173dd58ef01SDimitry Andric OS << "No samples collected in the function's body\n";
174dd58ef01SDimitry Andric }
175dd58ef01SDimitry Andric
176dd58ef01SDimitry Andric OS.indent(Indent);
17771d5a254SDimitry Andric if (!CallsiteSamples.empty()) {
178dd58ef01SDimitry Andric OS << "Samples collected in inlined callsites {\n";
17971d5a254SDimitry Andric SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
180dd58ef01SDimitry Andric CallsiteSamples);
181dd58ef01SDimitry Andric for (const auto &CS : SortedCallsiteSamples.get()) {
18271d5a254SDimitry Andric for (const auto &FS : CS->second) {
183dd58ef01SDimitry Andric OS.indent(Indent + 2);
184b1c73532SDimitry Andric OS << CS->first << ": inlined callee: " << FS.second.getFunction()
185b1c73532SDimitry Andric << ": ";
18671d5a254SDimitry Andric FS.second.print(OS, Indent + 4);
18771d5a254SDimitry Andric }
188dd58ef01SDimitry Andric }
1891d5ae102SDimitry Andric OS.indent(Indent);
190dd58ef01SDimitry Andric OS << "}\n";
191dd58ef01SDimitry Andric } else {
192dd58ef01SDimitry Andric OS << "No inlined callsites in this function\n";
193dd58ef01SDimitry Andric }
194dd58ef01SDimitry Andric }
195dd58ef01SDimitry Andric
operator <<(raw_ostream & OS,const FunctionSamples & FS)196dd58ef01SDimitry Andric raw_ostream &llvm::sampleprof::operator<<(raw_ostream &OS,
197dd58ef01SDimitry Andric const FunctionSamples &FS) {
198dd58ef01SDimitry Andric FS.print(OS);
199dd58ef01SDimitry Andric return OS;
200dd58ef01SDimitry Andric }
201dd58ef01SDimitry Andric
sortFuncProfiles(const SampleProfileMap & ProfileMap,std::vector<NameFunctionSamples> & SortedProfiles)202c0981da4SDimitry Andric void sampleprof::sortFuncProfiles(
203c0981da4SDimitry Andric const SampleProfileMap &ProfileMap,
204c0981da4SDimitry Andric std::vector<NameFunctionSamples> &SortedProfiles) {
205c0981da4SDimitry Andric for (const auto &I : ProfileMap) {
206b1c73532SDimitry Andric SortedProfiles.push_back(std::make_pair(I.first, &I.second));
207c0981da4SDimitry Andric }
208c0981da4SDimitry Andric llvm::stable_sort(SortedProfiles, [](const NameFunctionSamples &A,
209c0981da4SDimitry Andric const NameFunctionSamples &B) {
210c0981da4SDimitry Andric if (A.second->getTotalSamples() == B.second->getTotalSamples())
211b1c73532SDimitry Andric return A.second->getContext() < B.second->getContext();
212c0981da4SDimitry Andric return A.second->getTotalSamples() > B.second->getTotalSamples();
213c0981da4SDimitry Andric });
214c0981da4SDimitry Andric }
215c0981da4SDimitry Andric
getOffset(const DILocation * DIL)216eb11fae6SDimitry Andric unsigned FunctionSamples::getOffset(const DILocation *DIL) {
217eb11fae6SDimitry Andric return (DIL->getLine() - DIL->getScope()->getSubprogram()->getLine()) &
218eb11fae6SDimitry Andric 0xffff;
219eb11fae6SDimitry Andric }
220eb11fae6SDimitry Andric
getCallSiteIdentifier(const DILocation * DIL,bool ProfileIsFS)22177fc4c14SDimitry Andric LineLocation FunctionSamples::getCallSiteIdentifier(const DILocation *DIL,
22277fc4c14SDimitry Andric bool ProfileIsFS) {
22377fc4c14SDimitry Andric if (FunctionSamples::ProfileIsProbeBased) {
224b60736ecSDimitry Andric // In a pseudo-probe based profile, a callsite is simply represented by the
225b60736ecSDimitry Andric // ID of the probe associated with the call instruction. The probe ID is
226b60736ecSDimitry Andric // encoded in the Discriminator field of the call instruction's debug
227b60736ecSDimitry Andric // metadata.
228b60736ecSDimitry Andric return LineLocation(PseudoProbeDwarfDiscriminator::extractProbeIndex(
229b60736ecSDimitry Andric DIL->getDiscriminator()),
230b60736ecSDimitry Andric 0);
23177fc4c14SDimitry Andric } else {
23277fc4c14SDimitry Andric unsigned Discriminator =
23377fc4c14SDimitry Andric ProfileIsFS ? DIL->getDiscriminator() : DIL->getBaseDiscriminator();
23477fc4c14SDimitry Andric return LineLocation(FunctionSamples::getOffset(DIL), Discriminator);
23577fc4c14SDimitry Andric }
23677fc4c14SDimitry Andric }
23777fc4c14SDimitry Andric
findFunctionSamples(const DILocation * DIL,SampleProfileReaderItaniumRemapper * Remapper,const HashKeyMap<std::unordered_map,FunctionId,FunctionId> * FuncNameToProfNameMap) const238b60736ecSDimitry Andric const FunctionSamples *FunctionSamples::findFunctionSamples(
239ac9a064cSDimitry Andric const DILocation *DIL, SampleProfileReaderItaniumRemapper *Remapper,
240ac9a064cSDimitry Andric const HashKeyMap<std::unordered_map, FunctionId, FunctionId>
241ac9a064cSDimitry Andric *FuncNameToProfNameMap) const {
242eb11fae6SDimitry Andric assert(DIL);
243eb11fae6SDimitry Andric SmallVector<std::pair<LineLocation, StringRef>, 10> S;
244eb11fae6SDimitry Andric
245eb11fae6SDimitry Andric const DILocation *PrevDIL = DIL;
246eb11fae6SDimitry Andric for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) {
247c0981da4SDimitry Andric // Use C++ linkage name if possible.
248c0981da4SDimitry Andric StringRef Name = PrevDIL->getScope()->getSubprogram()->getLinkageName();
249c0981da4SDimitry Andric if (Name.empty())
250c0981da4SDimitry Andric Name = PrevDIL->getScope()->getSubprogram()->getName();
25177fc4c14SDimitry Andric S.emplace_back(FunctionSamples::getCallSiteIdentifier(
25277fc4c14SDimitry Andric DIL, FunctionSamples::ProfileIsFS),
25377fc4c14SDimitry Andric Name);
254eb11fae6SDimitry Andric PrevDIL = DIL;
255eb11fae6SDimitry Andric }
25677fc4c14SDimitry Andric
257eb11fae6SDimitry Andric if (S.size() == 0)
258eb11fae6SDimitry Andric return this;
259eb11fae6SDimitry Andric const FunctionSamples *FS = this;
260eb11fae6SDimitry Andric for (int i = S.size() - 1; i >= 0 && FS != nullptr; i--) {
261ac9a064cSDimitry Andric FS = FS->findFunctionSamplesAt(S[i].first, S[i].second, Remapper,
262ac9a064cSDimitry Andric FuncNameToProfNameMap);
263eb11fae6SDimitry Andric }
264eb11fae6SDimitry Andric return FS;
265eb11fae6SDimitry Andric }
266eb11fae6SDimitry Andric
findAllNames(DenseSet<FunctionId> & NameSet) const267b1c73532SDimitry Andric void FunctionSamples::findAllNames(DenseSet<FunctionId> &NameSet) const {
268b1c73532SDimitry Andric NameSet.insert(getFunction());
269b60736ecSDimitry Andric for (const auto &BS : BodySamples)
270b60736ecSDimitry Andric for (const auto &TS : BS.second.getCallTargets())
271b1c73532SDimitry Andric NameSet.insert(TS.first);
272b60736ecSDimitry Andric
273b60736ecSDimitry Andric for (const auto &CS : CallsiteSamples) {
274b60736ecSDimitry Andric for (const auto &NameFS : CS.second) {
275b60736ecSDimitry Andric NameSet.insert(NameFS.first);
276b60736ecSDimitry Andric NameFS.second.findAllNames(NameSet);
277b60736ecSDimitry Andric }
278b60736ecSDimitry Andric }
279b60736ecSDimitry Andric }
280b60736ecSDimitry Andric
findFunctionSamplesAt(const LineLocation & Loc,StringRef CalleeName,SampleProfileReaderItaniumRemapper * Remapper,const HashKeyMap<std::unordered_map,FunctionId,FunctionId> * FuncNameToProfNameMap) const281b60736ecSDimitry Andric const FunctionSamples *FunctionSamples::findFunctionSamplesAt(
282b60736ecSDimitry Andric const LineLocation &Loc, StringRef CalleeName,
283ac9a064cSDimitry Andric SampleProfileReaderItaniumRemapper *Remapper,
284ac9a064cSDimitry Andric const HashKeyMap<std::unordered_map, FunctionId, FunctionId>
285ac9a064cSDimitry Andric *FuncNameToProfNameMap) const {
286344a3780SDimitry Andric CalleeName = getCanonicalFnName(CalleeName);
287344a3780SDimitry Andric
288ac9a064cSDimitry Andric auto I = CallsiteSamples.find(mapIRLocToProfileLoc(Loc));
289ac9a064cSDimitry Andric if (I == CallsiteSamples.end())
290b60736ecSDimitry Andric return nullptr;
291ac9a064cSDimitry Andric auto FS = I->second.find(getRepInFormat(CalleeName));
292ac9a064cSDimitry Andric if (FS != I->second.end())
293b60736ecSDimitry Andric return &FS->second;
294ac9a064cSDimitry Andric
295ac9a064cSDimitry Andric if (FuncNameToProfNameMap && !FuncNameToProfNameMap->empty()) {
296ac9a064cSDimitry Andric auto R = FuncNameToProfNameMap->find(FunctionId(CalleeName));
297ac9a064cSDimitry Andric if (R != FuncNameToProfNameMap->end()) {
298ac9a064cSDimitry Andric CalleeName = R->second.stringRef();
299ac9a064cSDimitry Andric auto FS = I->second.find(getRepInFormat(CalleeName));
300ac9a064cSDimitry Andric if (FS != I->second.end())
301ac9a064cSDimitry Andric return &FS->second;
302ac9a064cSDimitry Andric }
303ac9a064cSDimitry Andric }
304ac9a064cSDimitry Andric
305b60736ecSDimitry Andric if (Remapper) {
306b60736ecSDimitry Andric if (auto NameInProfile = Remapper->lookUpNameInProfile(CalleeName)) {
307ac9a064cSDimitry Andric auto FS = I->second.find(getRepInFormat(*NameInProfile));
308ac9a064cSDimitry Andric if (FS != I->second.end())
309b60736ecSDimitry Andric return &FS->second;
310b60736ecSDimitry Andric }
311b60736ecSDimitry Andric }
312b60736ecSDimitry Andric // If we cannot find exact match of the callee name, return the FS with
313b60736ecSDimitry Andric // the max total count. Only do this when CalleeName is not provided,
314b60736ecSDimitry Andric // i.e., only for indirect calls.
315b60736ecSDimitry Andric if (!CalleeName.empty())
316b60736ecSDimitry Andric return nullptr;
317b60736ecSDimitry Andric uint64_t MaxTotalSamples = 0;
318b60736ecSDimitry Andric const FunctionSamples *R = nullptr;
319ac9a064cSDimitry Andric for (const auto &NameFS : I->second)
320b60736ecSDimitry Andric if (NameFS.second.getTotalSamples() >= MaxTotalSamples) {
321b60736ecSDimitry Andric MaxTotalSamples = NameFS.second.getTotalSamples();
322b60736ecSDimitry Andric R = &NameFS.second;
323b60736ecSDimitry Andric }
324b60736ecSDimitry Andric return R;
325b60736ecSDimitry Andric }
326b60736ecSDimitry Andric
32771d5a254SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
dump() const32871d5a254SDimitry Andric LLVM_DUMP_METHOD void FunctionSamples::dump() const { print(dbgs(), 0); }
32971d5a254SDimitry Andric #endif
3301d5ae102SDimitry Andric
read(const uint8_t * Data,uint64_t ListSize)3311d5ae102SDimitry Andric std::error_code ProfileSymbolList::read(const uint8_t *Data,
3321d5ae102SDimitry Andric uint64_t ListSize) {
3331d5ae102SDimitry Andric const char *ListStart = reinterpret_cast<const char *>(Data);
3341d5ae102SDimitry Andric uint64_t Size = 0;
335344a3780SDimitry Andric uint64_t StrNum = 0;
336344a3780SDimitry Andric while (Size < ListSize && StrNum < ProfileSymbolListCutOff) {
3371d5ae102SDimitry Andric StringRef Str(ListStart + Size);
3381d5ae102SDimitry Andric add(Str);
3391d5ae102SDimitry Andric Size += Str.size() + 1;
340344a3780SDimitry Andric StrNum++;
3411d5ae102SDimitry Andric }
342344a3780SDimitry Andric if (Size != ListSize && StrNum != ProfileSymbolListCutOff)
3431d5ae102SDimitry Andric return sampleprof_error::malformed;
3441d5ae102SDimitry Andric return sampleprof_error::success;
3451d5ae102SDimitry Andric }
3461d5ae102SDimitry Andric
trimAndMergeColdContextProfiles(uint64_t ColdCountThreshold,bool TrimColdContext,bool MergeColdContext,uint32_t ColdContextFrameLength,bool TrimBaseProfileOnly)347344a3780SDimitry Andric void SampleContextTrimmer::trimAndMergeColdContextProfiles(
348344a3780SDimitry Andric uint64_t ColdCountThreshold, bool TrimColdContext, bool MergeColdContext,
349c0981da4SDimitry Andric uint32_t ColdContextFrameLength, bool TrimBaseProfileOnly) {
350344a3780SDimitry Andric if (!TrimColdContext && !MergeColdContext)
351344a3780SDimitry Andric return;
352344a3780SDimitry Andric
353344a3780SDimitry Andric // Nothing to merge if sample threshold is zero
354344a3780SDimitry Andric if (ColdCountThreshold == 0)
355344a3780SDimitry Andric return;
356344a3780SDimitry Andric
357c0981da4SDimitry Andric // Trimming base profiles only is mainly to honor the preinliner decsion. When
358c0981da4SDimitry Andric // MergeColdContext is true preinliner decsion is not honored anyway so turn
359c0981da4SDimitry Andric // off TrimBaseProfileOnly.
360c0981da4SDimitry Andric if (MergeColdContext)
361c0981da4SDimitry Andric TrimBaseProfileOnly = false;
362c0981da4SDimitry Andric
363344a3780SDimitry Andric // Filter the cold profiles from ProfileMap and move them into a tmp
364344a3780SDimitry Andric // container
365b1c73532SDimitry Andric std::vector<std::pair<hash_code, const FunctionSamples *>> ColdProfiles;
366344a3780SDimitry Andric for (const auto &I : ProfileMap) {
367b1c73532SDimitry Andric const SampleContext &Context = I.second.getContext();
368344a3780SDimitry Andric const FunctionSamples &FunctionProfile = I.second;
369c0981da4SDimitry Andric if (FunctionProfile.getTotalSamples() < ColdCountThreshold &&
370c0981da4SDimitry Andric (!TrimBaseProfileOnly || Context.isBaseContext()))
371b1c73532SDimitry Andric ColdProfiles.emplace_back(I.first, &I.second);
372344a3780SDimitry Andric }
373344a3780SDimitry Andric
374344a3780SDimitry Andric // Remove the cold profile from ProfileMap and merge them into
375344a3780SDimitry Andric // MergedProfileMap by the last K frames of context
376c0981da4SDimitry Andric SampleProfileMap MergedProfileMap;
377344a3780SDimitry Andric for (const auto &I : ColdProfiles) {
378344a3780SDimitry Andric if (MergeColdContext) {
379c0981da4SDimitry Andric auto MergedContext = I.second->getContext().getContextFrames();
380c0981da4SDimitry Andric if (ColdContextFrameLength < MergedContext.size())
381c0981da4SDimitry Andric MergedContext = MergedContext.take_back(ColdContextFrameLength);
382b1c73532SDimitry Andric // Need to set MergedProfile's context here otherwise it will be lost.
383ac9a064cSDimitry Andric FunctionSamples &MergedProfile = MergedProfileMap.create(MergedContext);
384344a3780SDimitry Andric MergedProfile.merge(*I.second);
385344a3780SDimitry Andric }
386344a3780SDimitry Andric ProfileMap.erase(I.first);
387344a3780SDimitry Andric }
388344a3780SDimitry Andric
389344a3780SDimitry Andric // Move the merged profiles into ProfileMap;
390344a3780SDimitry Andric for (const auto &I : MergedProfileMap) {
391344a3780SDimitry Andric // Filter the cold merged profile
392344a3780SDimitry Andric if (TrimColdContext && I.second.getTotalSamples() < ColdCountThreshold &&
393b1c73532SDimitry Andric ProfileMap.find(I.second.getContext()) == ProfileMap.end())
394344a3780SDimitry Andric continue;
395344a3780SDimitry Andric // Merge the profile if the original profile exists, otherwise just insert
396b1c73532SDimitry Andric // as a new profile. If inserted as a new profile from MergedProfileMap, it
397b1c73532SDimitry Andric // already has the right context.
398b1c73532SDimitry Andric auto Ret = ProfileMap.emplace(I.second.getContext(), FunctionSamples());
399344a3780SDimitry Andric FunctionSamples &OrigProfile = Ret.first->second;
400344a3780SDimitry Andric OrigProfile.merge(I.second);
401344a3780SDimitry Andric }
402344a3780SDimitry Andric }
403344a3780SDimitry Andric
write(raw_ostream & OS)4041d5ae102SDimitry Andric std::error_code ProfileSymbolList::write(raw_ostream &OS) {
4051d5ae102SDimitry Andric // Sort the symbols before output. If doing compression.
4061d5ae102SDimitry Andric // It will make the compression much more effective.
407b60736ecSDimitry Andric std::vector<StringRef> SortedList(Syms.begin(), Syms.end());
4081d5ae102SDimitry Andric llvm::sort(SortedList);
4091d5ae102SDimitry Andric
4101d5ae102SDimitry Andric std::string OutputString;
4111d5ae102SDimitry Andric for (auto &Sym : SortedList) {
4121d5ae102SDimitry Andric OutputString.append(Sym.str());
4131d5ae102SDimitry Andric OutputString.append(1, '\0');
4141d5ae102SDimitry Andric }
4151d5ae102SDimitry Andric
4161d5ae102SDimitry Andric OS << OutputString;
4171d5ae102SDimitry Andric return sampleprof_error::success;
4181d5ae102SDimitry Andric }
4191d5ae102SDimitry Andric
dump(raw_ostream & OS) const4201d5ae102SDimitry Andric void ProfileSymbolList::dump(raw_ostream &OS) const {
4211d5ae102SDimitry Andric OS << "======== Dump profile symbol list ========\n";
422b60736ecSDimitry Andric std::vector<StringRef> SortedList(Syms.begin(), Syms.end());
4231d5ae102SDimitry Andric llvm::sort(SortedList);
4241d5ae102SDimitry Andric
4251d5ae102SDimitry Andric for (auto &Sym : SortedList)
4261d5ae102SDimitry Andric OS << Sym << "\n";
4271d5ae102SDimitry Andric }
42877fc4c14SDimitry Andric
4297fa27ce4SDimitry Andric ProfileConverter::FrameNode *
getOrCreateChildFrame(const LineLocation & CallSite,FunctionId CalleeName)4307fa27ce4SDimitry Andric ProfileConverter::FrameNode::getOrCreateChildFrame(const LineLocation &CallSite,
431b1c73532SDimitry Andric FunctionId CalleeName) {
43277fc4c14SDimitry Andric uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite);
43377fc4c14SDimitry Andric auto It = AllChildFrames.find(Hash);
43477fc4c14SDimitry Andric if (It != AllChildFrames.end()) {
43577fc4c14SDimitry Andric assert(It->second.FuncName == CalleeName &&
43677fc4c14SDimitry Andric "Hash collision for child context node");
43777fc4c14SDimitry Andric return &It->second;
43877fc4c14SDimitry Andric }
43977fc4c14SDimitry Andric
44077fc4c14SDimitry Andric AllChildFrames[Hash] = FrameNode(CalleeName, nullptr, CallSite);
44177fc4c14SDimitry Andric return &AllChildFrames[Hash];
44277fc4c14SDimitry Andric }
44377fc4c14SDimitry Andric
ProfileConverter(SampleProfileMap & Profiles)4447fa27ce4SDimitry Andric ProfileConverter::ProfileConverter(SampleProfileMap &Profiles)
44577fc4c14SDimitry Andric : ProfileMap(Profiles) {
44677fc4c14SDimitry Andric for (auto &FuncSample : Profiles) {
44777fc4c14SDimitry Andric FunctionSamples *FSamples = &FuncSample.second;
44877fc4c14SDimitry Andric auto *NewNode = getOrCreateContextPath(FSamples->getContext());
44977fc4c14SDimitry Andric assert(!NewNode->FuncSamples && "New node cannot have sample profile");
45077fc4c14SDimitry Andric NewNode->FuncSamples = FSamples;
45177fc4c14SDimitry Andric }
45277fc4c14SDimitry Andric }
45377fc4c14SDimitry Andric
4547fa27ce4SDimitry Andric ProfileConverter::FrameNode *
getOrCreateContextPath(const SampleContext & Context)4557fa27ce4SDimitry Andric ProfileConverter::getOrCreateContextPath(const SampleContext &Context) {
45677fc4c14SDimitry Andric auto Node = &RootFrame;
45777fc4c14SDimitry Andric LineLocation CallSiteLoc(0, 0);
45877fc4c14SDimitry Andric for (auto &Callsite : Context.getContextFrames()) {
459b1c73532SDimitry Andric Node = Node->getOrCreateChildFrame(CallSiteLoc, Callsite.Func);
46077fc4c14SDimitry Andric CallSiteLoc = Callsite.Location;
46177fc4c14SDimitry Andric }
46277fc4c14SDimitry Andric return Node;
46377fc4c14SDimitry Andric }
46477fc4c14SDimitry Andric
convertCSProfiles(ProfileConverter::FrameNode & Node)4657fa27ce4SDimitry Andric void ProfileConverter::convertCSProfiles(ProfileConverter::FrameNode &Node) {
46677fc4c14SDimitry Andric // Process each child profile. Add each child profile to callsite profile map
46777fc4c14SDimitry Andric // of the current node `Node` if `Node` comes with a profile. Otherwise
46877fc4c14SDimitry Andric // promote the child profile to a standalone profile.
46977fc4c14SDimitry Andric auto *NodeProfile = Node.FuncSamples;
47077fc4c14SDimitry Andric for (auto &It : Node.AllChildFrames) {
47177fc4c14SDimitry Andric auto &ChildNode = It.second;
4727fa27ce4SDimitry Andric convertCSProfiles(ChildNode);
47377fc4c14SDimitry Andric auto *ChildProfile = ChildNode.FuncSamples;
47477fc4c14SDimitry Andric if (!ChildProfile)
47577fc4c14SDimitry Andric continue;
47677fc4c14SDimitry Andric SampleContext OrigChildContext = ChildProfile->getContext();
477b1c73532SDimitry Andric uint64_t OrigChildContextHash = OrigChildContext.getHashCode();
47877fc4c14SDimitry Andric // Reset the child context to be contextless.
479b1c73532SDimitry Andric ChildProfile->getContext().setFunction(OrigChildContext.getFunction());
48077fc4c14SDimitry Andric if (NodeProfile) {
48177fc4c14SDimitry Andric // Add child profile to the callsite profile map.
48277fc4c14SDimitry Andric auto &SamplesMap = NodeProfile->functionSamplesAt(ChildNode.CallSiteLoc);
483b1c73532SDimitry Andric SamplesMap.emplace(OrigChildContext.getFunction(), *ChildProfile);
48477fc4c14SDimitry Andric NodeProfile->addTotalSamples(ChildProfile->getTotalSamples());
485145449b1SDimitry Andric // Remove the corresponding body sample for the callsite and update the
486145449b1SDimitry Andric // total weight.
487145449b1SDimitry Andric auto Count = NodeProfile->removeCalledTargetAndBodySample(
488145449b1SDimitry Andric ChildNode.CallSiteLoc.LineOffset, ChildNode.CallSiteLoc.Discriminator,
489b1c73532SDimitry Andric OrigChildContext.getFunction());
490145449b1SDimitry Andric NodeProfile->removeTotalSamples(Count);
49177fc4c14SDimitry Andric }
49277fc4c14SDimitry Andric
493b1c73532SDimitry Andric uint64_t NewChildProfileHash = 0;
49477fc4c14SDimitry Andric // Separate child profile to be a standalone profile, if the current parent
49577fc4c14SDimitry Andric // profile doesn't exist. This is a duplicating operation when the child
49677fc4c14SDimitry Andric // profile is already incorporated into the parent which is still useful and
49777fc4c14SDimitry Andric // thus done optionally. It is seen that duplicating context profiles into
49877fc4c14SDimitry Andric // base profiles improves the code quality for thinlto build by allowing a
49977fc4c14SDimitry Andric // profile in the prelink phase for to-be-fully-inlined functions.
500145449b1SDimitry Andric if (!NodeProfile) {
50177fc4c14SDimitry Andric ProfileMap[ChildProfile->getContext()].merge(*ChildProfile);
502b1c73532SDimitry Andric NewChildProfileHash = ChildProfile->getContext().getHashCode();
503145449b1SDimitry Andric } else if (GenerateMergedBaseProfiles) {
504145449b1SDimitry Andric ProfileMap[ChildProfile->getContext()].merge(*ChildProfile);
505b1c73532SDimitry Andric NewChildProfileHash = ChildProfile->getContext().getHashCode();
506145449b1SDimitry Andric auto &SamplesMap = NodeProfile->functionSamplesAt(ChildNode.CallSiteLoc);
507b1c73532SDimitry Andric SamplesMap[ChildProfile->getFunction()].getContext().setAttribute(
508145449b1SDimitry Andric ContextDuplicatedIntoBase);
509145449b1SDimitry Andric }
51077fc4c14SDimitry Andric
511b1c73532SDimitry Andric // Remove the original child profile. Check if MD5 of new child profile
512b1c73532SDimitry Andric // collides with old profile, in this case the [] operator already
513b1c73532SDimitry Andric // overwritten it without the need of erase.
514b1c73532SDimitry Andric if (NewChildProfileHash != OrigChildContextHash)
515b1c73532SDimitry Andric ProfileMap.erase(OrigChildContextHash);
51677fc4c14SDimitry Andric }
51777fc4c14SDimitry Andric }
51877fc4c14SDimitry Andric
convertCSProfiles()5197fa27ce4SDimitry Andric void ProfileConverter::convertCSProfiles() { convertCSProfiles(RootFrame); }
520