1 //=-- SampleProf.cpp - Sample profiling format support --------------------===//
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 contains common definitions used in the reading and writing of
10 // sample profile data.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/ProfileData/SampleProf.h"
15 #include "llvm/Config/llvm-config.h"
16 #include "llvm/IR/DebugInfoMetadata.h"
17 #include "llvm/ProfileData/SampleProfReader.h"
18 #include "llvm/Support/Compiler.h"
19 #include "llvm/Support/Debug.h"
20 #include "llvm/Support/Error.h"
21 #include "llvm/Support/ErrorHandling.h"
22 #include "llvm/Support/LEB128.h"
23 #include "llvm/Support/ManagedStatic.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include <string>
26 #include <system_error>
27 
28 using namespace llvm;
29 using namespace sampleprof;
30 
31 namespace llvm {
32 namespace sampleprof {
33 SampleProfileFormat FunctionSamples::Format;
34 bool FunctionSamples::UseMD5;
35 } // namespace sampleprof
36 } // namespace llvm
37 
38 namespace {
39 
40 // FIXME: This class is only here to support the transition to llvm::Error. It
41 // will be removed once this transition is complete. Clients should prefer to
42 // deal with the Error value directly, rather than converting to error_code.
43 class SampleProfErrorCategoryType : public std::error_category {
44   const char *name() const noexcept override { return "llvm.sampleprof"; }
45 
46   std::string message(int IE) const override {
47     sampleprof_error E = static_cast<sampleprof_error>(IE);
48     switch (E) {
49     case sampleprof_error::success:
50       return "Success";
51     case sampleprof_error::bad_magic:
52       return "Invalid sample profile data (bad magic)";
53     case sampleprof_error::unsupported_version:
54       return "Unsupported sample profile format version";
55     case sampleprof_error::too_large:
56       return "Too much profile data";
57     case sampleprof_error::truncated:
58       return "Truncated profile data";
59     case sampleprof_error::malformed:
60       return "Malformed sample profile data";
61     case sampleprof_error::unrecognized_format:
62       return "Unrecognized sample profile encoding format";
63     case sampleprof_error::unsupported_writing_format:
64       return "Profile encoding format unsupported for writing operations";
65     case sampleprof_error::truncated_name_table:
66       return "Truncated function name table";
67     case sampleprof_error::not_implemented:
68       return "Unimplemented feature";
69     case sampleprof_error::counter_overflow:
70       return "Counter overflow";
71     case sampleprof_error::ostream_seek_unsupported:
72       return "Ostream does not support seek";
73     case sampleprof_error::compress_failed:
74       return "Compress failure";
75     case sampleprof_error::uncompress_failed:
76       return "Uncompress failure";
77     case sampleprof_error::zlib_unavailable:
78       return "Zlib is unavailable";
79     }
80     llvm_unreachable("A value of sampleprof_error has no message.");
81   }
82 };
83 
84 } // end anonymous namespace
85 
86 static ManagedStatic<SampleProfErrorCategoryType> ErrorCategory;
87 
88 const std::error_category &llvm::sampleprof_category() {
89   return *ErrorCategory;
90 }
91 
92 void LineLocation::print(raw_ostream &OS) const {
93   OS << LineOffset;
94   if (Discriminator > 0)
95     OS << "." << Discriminator;
96 }
97 
98 raw_ostream &llvm::sampleprof::operator<<(raw_ostream &OS,
99                                           const LineLocation &Loc) {
100   Loc.print(OS);
101   return OS;
102 }
103 
104 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
105 LLVM_DUMP_METHOD void LineLocation::dump() const { print(dbgs()); }
106 #endif
107 
108 /// Print the sample record to the stream \p OS indented by \p Indent.
109 void SampleRecord::print(raw_ostream &OS, unsigned Indent) const {
110   OS << NumSamples;
111   if (hasCalls()) {
112     OS << ", calls:";
113     for (const auto &I : getSortedCallTargets())
114       OS << " " << I.first << ":" << I.second;
115   }
116   OS << "\n";
117 }
118 
119 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
120 LLVM_DUMP_METHOD void SampleRecord::dump() const { print(dbgs(), 0); }
121 #endif
122 
123 raw_ostream &llvm::sampleprof::operator<<(raw_ostream &OS,
124                                           const SampleRecord &Sample) {
125   Sample.print(OS, 0);
126   return OS;
127 }
128 
129 /// Print the samples collected for a function on stream \p OS.
130 void FunctionSamples::print(raw_ostream &OS, unsigned Indent) const {
131   OS << TotalSamples << ", " << TotalHeadSamples << ", " << BodySamples.size()
132      << " sampled lines\n";
133 
134   OS.indent(Indent);
135   if (!BodySamples.empty()) {
136     OS << "Samples collected in the function's body {\n";
137     SampleSorter<LineLocation, SampleRecord> SortedBodySamples(BodySamples);
138     for (const auto &SI : SortedBodySamples.get()) {
139       OS.indent(Indent + 2);
140       OS << SI->first << ": " << SI->second;
141     }
142     OS.indent(Indent);
143     OS << "}\n";
144   } else {
145     OS << "No samples collected in the function's body\n";
146   }
147 
148   OS.indent(Indent);
149   if (!CallsiteSamples.empty()) {
150     OS << "Samples collected in inlined callsites {\n";
151     SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
152         CallsiteSamples);
153     for (const auto &CS : SortedCallsiteSamples.get()) {
154       for (const auto &FS : CS->second) {
155         OS.indent(Indent + 2);
156         OS << CS->first << ": inlined callee: " << FS.second.getName() << ": ";
157         FS.second.print(OS, Indent + 4);
158       }
159     }
160     OS.indent(Indent);
161     OS << "}\n";
162   } else {
163     OS << "No inlined callsites in this function\n";
164   }
165 }
166 
167 raw_ostream &llvm::sampleprof::operator<<(raw_ostream &OS,
168                                           const FunctionSamples &FS) {
169   FS.print(OS);
170   return OS;
171 }
172 
173 unsigned FunctionSamples::getOffset(const DILocation *DIL) {
174   return (DIL->getLine() - DIL->getScope()->getSubprogram()->getLine()) &
175       0xffff;
176 }
177 
178 const FunctionSamples *FunctionSamples::findFunctionSamples(
179     const DILocation *DIL, SampleProfileReaderItaniumRemapper *Remapper) const {
180   assert(DIL);
181   SmallVector<std::pair<LineLocation, StringRef>, 10> S;
182 
183   const DILocation *PrevDIL = DIL;
184   for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) {
185     S.push_back(std::make_pair(
186         LineLocation(getOffset(DIL), DIL->getBaseDiscriminator()),
187         PrevDIL->getScope()->getSubprogram()->getLinkageName()));
188     PrevDIL = DIL;
189   }
190   if (S.size() == 0)
191     return this;
192   const FunctionSamples *FS = this;
193   for (int i = S.size() - 1; i >= 0 && FS != nullptr; i--) {
194     FS = FS->findFunctionSamplesAt(S[i].first, S[i].second, Remapper);
195   }
196   return FS;
197 }
198 
199 void FunctionSamples::findAllNames(DenseSet<StringRef> &NameSet) const {
200   NameSet.insert(Name);
201   for (const auto &BS : BodySamples)
202     for (const auto &TS : BS.second.getCallTargets())
203       NameSet.insert(TS.getKey());
204 
205   for (const auto &CS : CallsiteSamples) {
206     for (const auto &NameFS : CS.second) {
207       NameSet.insert(NameFS.first);
208       NameFS.second.findAllNames(NameSet);
209     }
210   }
211 }
212 
213 const FunctionSamples *FunctionSamples::findFunctionSamplesAt(
214     const LineLocation &Loc, StringRef CalleeName,
215     SampleProfileReaderItaniumRemapper *Remapper) const {
216   std::string CalleeGUID;
217   CalleeName = getRepInFormat(CalleeName, UseMD5, CalleeGUID);
218 
219   auto iter = CallsiteSamples.find(Loc);
220   if (iter == CallsiteSamples.end())
221     return nullptr;
222   auto FS = iter->second.find(CalleeName);
223   if (FS != iter->second.end())
224     return &FS->second;
225   if (Remapper) {
226     if (auto NameInProfile = Remapper->lookUpNameInProfile(CalleeName)) {
227       auto FS = iter->second.find(*NameInProfile);
228       if (FS != iter->second.end())
229         return &FS->second;
230     }
231   }
232   // If we cannot find exact match of the callee name, return the FS with
233   // the max total count. Only do this when CalleeName is not provided,
234   // i.e., only for indirect calls.
235   if (!CalleeName.empty())
236     return nullptr;
237   uint64_t MaxTotalSamples = 0;
238   const FunctionSamples *R = nullptr;
239   for (const auto &NameFS : iter->second)
240     if (NameFS.second.getTotalSamples() >= MaxTotalSamples) {
241       MaxTotalSamples = NameFS.second.getTotalSamples();
242       R = &NameFS.second;
243     }
244   return R;
245 }
246 
247 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
248 LLVM_DUMP_METHOD void FunctionSamples::dump() const { print(dbgs(), 0); }
249 #endif
250 
251 std::error_code ProfileSymbolList::read(const uint8_t *Data,
252                                         uint64_t ListSize) {
253   const char *ListStart = reinterpret_cast<const char *>(Data);
254   uint64_t Size = 0;
255   while (Size < ListSize) {
256     StringRef Str(ListStart + Size);
257     add(Str);
258     Size += Str.size() + 1;
259   }
260   if (Size != ListSize)
261     return sampleprof_error::malformed;
262   return sampleprof_error::success;
263 }
264 
265 std::error_code ProfileSymbolList::write(raw_ostream &OS) {
266   // Sort the symbols before output. If doing compression.
267   // It will make the compression much more effective.
268   std::vector<StringRef> SortedList;
269   SortedList.insert(SortedList.begin(), Syms.begin(), Syms.end());
270   llvm::sort(SortedList);
271 
272   std::string OutputString;
273   for (auto &Sym : SortedList) {
274     OutputString.append(Sym.str());
275     OutputString.append(1, '\0');
276   }
277 
278   OS << OutputString;
279   return sampleprof_error::success;
280 }
281 
282 void ProfileSymbolList::dump(raw_ostream &OS) const {
283   OS << "======== Dump profile symbol list ========\n";
284   std::vector<StringRef> SortedList;
285   SortedList.insert(SortedList.begin(), Syms.begin(), Syms.end());
286   llvm::sort(SortedList);
287 
288   for (auto &Sym : SortedList)
289     OS << Sym << "\n";
290 }
291