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