Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 1 | //===- SampleProf.h - Sampling profiling format support ---------*- C++ -*-===// |
| 2 | // |
Andrew Walbran | 16937d0 | 2019-10-22 13:54:20 +0100 | [diff] [blame] | 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 |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file contains common definitions used in the reading and writing of |
| 10 | // sample profile data. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #ifndef LLVM_PROFILEDATA_SAMPLEPROF_H |
| 15 | #define LLVM_PROFILEDATA_SAMPLEPROF_H |
| 16 | |
| 17 | #include "llvm/ADT/DenseSet.h" |
| 18 | #include "llvm/ADT/SmallVector.h" |
| 19 | #include "llvm/ADT/StringMap.h" |
| 20 | #include "llvm/ADT/StringRef.h" |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 21 | #include "llvm/ADT/StringSet.h" |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 22 | #include "llvm/IR/Function.h" |
| 23 | #include "llvm/IR/GlobalValue.h" |
| 24 | #include "llvm/IR/Module.h" |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 25 | #include "llvm/Support/Allocator.h" |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 26 | #include "llvm/Support/Debug.h" |
| 27 | #include "llvm/Support/ErrorOr.h" |
| 28 | #include "llvm/Support/MathExtras.h" |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 29 | #include "llvm/Support/raw_ostream.h" |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 30 | #include <algorithm> |
| 31 | #include <cstdint> |
| 32 | #include <map> |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 33 | #include <set> |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 34 | #include <string> |
| 35 | #include <system_error> |
| 36 | #include <utility> |
| 37 | |
| 38 | namespace llvm { |
| 39 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 40 | const std::error_category &sampleprof_category(); |
| 41 | |
| 42 | enum class sampleprof_error { |
| 43 | success = 0, |
| 44 | bad_magic, |
| 45 | unsupported_version, |
| 46 | too_large, |
| 47 | truncated, |
| 48 | malformed, |
| 49 | unrecognized_format, |
| 50 | unsupported_writing_format, |
| 51 | truncated_name_table, |
| 52 | not_implemented, |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 53 | counter_overflow, |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 54 | ostream_seek_unsupported, |
| 55 | compress_failed, |
| 56 | uncompress_failed, |
| 57 | zlib_unavailable, |
| 58 | hash_mismatch |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 59 | }; |
| 60 | |
| 61 | inline std::error_code make_error_code(sampleprof_error E) { |
| 62 | return std::error_code(static_cast<int>(E), sampleprof_category()); |
| 63 | } |
| 64 | |
| 65 | inline sampleprof_error MergeResult(sampleprof_error &Accumulator, |
| 66 | sampleprof_error Result) { |
| 67 | // Prefer first error encountered as later errors may be secondary effects of |
| 68 | // the initial problem. |
| 69 | if (Accumulator == sampleprof_error::success && |
| 70 | Result != sampleprof_error::success) |
| 71 | Accumulator = Result; |
| 72 | return Accumulator; |
| 73 | } |
| 74 | |
| 75 | } // end namespace llvm |
| 76 | |
| 77 | namespace std { |
| 78 | |
| 79 | template <> |
| 80 | struct is_error_code_enum<llvm::sampleprof_error> : std::true_type {}; |
| 81 | |
| 82 | } // end namespace std |
| 83 | |
| 84 | namespace llvm { |
| 85 | namespace sampleprof { |
| 86 | |
Andrew Scull | cdfcccc | 2018-10-05 20:58:37 +0100 | [diff] [blame] | 87 | enum SampleProfileFormat { |
| 88 | SPF_None = 0, |
| 89 | SPF_Text = 0x1, |
| 90 | SPF_Compact_Binary = 0x2, |
| 91 | SPF_GCC = 0x3, |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 92 | SPF_Ext_Binary = 0x4, |
Andrew Scull | cdfcccc | 2018-10-05 20:58:37 +0100 | [diff] [blame] | 93 | SPF_Binary = 0xff |
| 94 | }; |
| 95 | |
| 96 | static inline uint64_t SPMagic(SampleProfileFormat Format = SPF_Binary) { |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 97 | return uint64_t('S') << (64 - 8) | uint64_t('P') << (64 - 16) | |
| 98 | uint64_t('R') << (64 - 24) | uint64_t('O') << (64 - 32) | |
| 99 | uint64_t('F') << (64 - 40) | uint64_t('4') << (64 - 48) | |
Andrew Scull | cdfcccc | 2018-10-05 20:58:37 +0100 | [diff] [blame] | 100 | uint64_t('2') << (64 - 56) | uint64_t(Format); |
| 101 | } |
| 102 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 103 | /// Get the proper representation of a string according to whether the |
| 104 | /// current Format uses MD5 to represent the string. |
| 105 | static inline StringRef getRepInFormat(StringRef Name, bool UseMD5, |
Andrew Scull | cdfcccc | 2018-10-05 20:58:37 +0100 | [diff] [blame] | 106 | std::string &GUIDBuf) { |
| 107 | if (Name.empty()) |
| 108 | return Name; |
| 109 | GUIDBuf = std::to_string(Function::getGUID(Name)); |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 110 | return UseMD5 ? StringRef(GUIDBuf) : Name; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | static inline uint64_t SPVersion() { return 103; } |
| 114 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 115 | // Section Type used by SampleProfileExtBinaryBaseReader and |
| 116 | // SampleProfileExtBinaryBaseWriter. Never change the existing |
| 117 | // value of enum. Only append new ones. |
| 118 | enum SecType { |
| 119 | SecInValid = 0, |
| 120 | SecProfSummary = 1, |
| 121 | SecNameTable = 2, |
| 122 | SecProfileSymbolList = 3, |
| 123 | SecFuncOffsetTable = 4, |
| 124 | SecFuncMetadata = 5, |
| 125 | // marker for the first type of profile. |
| 126 | SecFuncProfileFirst = 32, |
| 127 | SecLBRProfile = SecFuncProfileFirst |
| 128 | }; |
| 129 | |
| 130 | static inline std::string getSecName(SecType Type) { |
| 131 | switch (Type) { |
| 132 | case SecInValid: |
| 133 | return "InvalidSection"; |
| 134 | case SecProfSummary: |
| 135 | return "ProfileSummarySection"; |
| 136 | case SecNameTable: |
| 137 | return "NameTableSection"; |
| 138 | case SecProfileSymbolList: |
| 139 | return "ProfileSymbolListSection"; |
| 140 | case SecFuncOffsetTable: |
| 141 | return "FuncOffsetTableSection"; |
| 142 | case SecFuncMetadata: |
| 143 | return "FunctionMetadata"; |
| 144 | case SecLBRProfile: |
| 145 | return "LBRProfileSection"; |
| 146 | } |
| 147 | llvm_unreachable("A SecType has no name for output"); |
| 148 | } |
| 149 | |
| 150 | // Entry type of section header table used by SampleProfileExtBinaryBaseReader |
| 151 | // and SampleProfileExtBinaryBaseWriter. |
| 152 | struct SecHdrTableEntry { |
| 153 | SecType Type; |
| 154 | uint64_t Flags; |
| 155 | uint64_t Offset; |
| 156 | uint64_t Size; |
| 157 | // The index indicating the location of the current entry in |
| 158 | // SectionHdrLayout table. |
| 159 | uint32_t LayoutIndex; |
| 160 | }; |
| 161 | |
| 162 | // Flags common for all sections are defined here. In SecHdrTableEntry::Flags, |
| 163 | // common flags will be saved in the lower 32bits and section specific flags |
| 164 | // will be saved in the higher 32 bits. |
| 165 | enum class SecCommonFlags : uint32_t { |
| 166 | SecFlagInValid = 0, |
| 167 | SecFlagCompress = (1 << 0) |
| 168 | }; |
| 169 | |
| 170 | // Section specific flags are defined here. |
| 171 | // !!!Note: Everytime a new enum class is created here, please add |
| 172 | // a new check in verifySecFlag. |
| 173 | enum class SecNameTableFlags : uint32_t { |
| 174 | SecFlagInValid = 0, |
| 175 | SecFlagMD5Name = (1 << 0), |
| 176 | // Store MD5 in fixed length instead of ULEB128 so NameTable can be |
| 177 | // accessed like an array. |
| 178 | SecFlagFixedLengthMD5 = (1 << 1) |
| 179 | }; |
| 180 | enum class SecProfSummaryFlags : uint32_t { |
| 181 | SecFlagInValid = 0, |
| 182 | /// SecFlagPartial means the profile is for common/shared code. |
| 183 | /// The common profile is usually merged from profiles collected |
| 184 | /// from running other targets. |
| 185 | SecFlagPartial = (1 << 0) |
| 186 | }; |
| 187 | |
| 188 | enum class SecFuncMetadataFlags : uint32_t { |
| 189 | SecFlagInvalid = 0, |
| 190 | SecFlagIsProbeBased = (1 << 0), |
| 191 | }; |
| 192 | |
| 193 | // Verify section specific flag is used for the correct section. |
| 194 | template <class SecFlagType> |
| 195 | static inline void verifySecFlag(SecType Type, SecFlagType Flag) { |
| 196 | // No verification is needed for common flags. |
| 197 | if (std::is_same<SecCommonFlags, SecFlagType>()) |
| 198 | return; |
| 199 | |
| 200 | // Verification starts here for section specific flag. |
| 201 | bool IsFlagLegal = false; |
| 202 | switch (Type) { |
| 203 | case SecNameTable: |
| 204 | IsFlagLegal = std::is_same<SecNameTableFlags, SecFlagType>(); |
| 205 | break; |
| 206 | case SecProfSummary: |
| 207 | IsFlagLegal = std::is_same<SecProfSummaryFlags, SecFlagType>(); |
| 208 | break; |
| 209 | case SecFuncMetadata: |
| 210 | IsFlagLegal = std::is_same<SecFuncMetadataFlags, SecFlagType>(); |
| 211 | break; |
| 212 | default: |
| 213 | break; |
| 214 | } |
| 215 | if (!IsFlagLegal) |
| 216 | llvm_unreachable("Misuse of a flag in an incompatible section"); |
| 217 | } |
| 218 | |
| 219 | template <class SecFlagType> |
| 220 | static inline void addSecFlag(SecHdrTableEntry &Entry, SecFlagType Flag) { |
| 221 | verifySecFlag(Entry.Type, Flag); |
| 222 | auto FVal = static_cast<uint64_t>(Flag); |
| 223 | bool IsCommon = std::is_same<SecCommonFlags, SecFlagType>(); |
| 224 | Entry.Flags |= IsCommon ? FVal : (FVal << 32); |
| 225 | } |
| 226 | |
| 227 | template <class SecFlagType> |
| 228 | static inline void removeSecFlag(SecHdrTableEntry &Entry, SecFlagType Flag) { |
| 229 | verifySecFlag(Entry.Type, Flag); |
| 230 | auto FVal = static_cast<uint64_t>(Flag); |
| 231 | bool IsCommon = std::is_same<SecCommonFlags, SecFlagType>(); |
| 232 | Entry.Flags &= ~(IsCommon ? FVal : (FVal << 32)); |
| 233 | } |
| 234 | |
| 235 | template <class SecFlagType> |
| 236 | static inline bool hasSecFlag(const SecHdrTableEntry &Entry, SecFlagType Flag) { |
| 237 | verifySecFlag(Entry.Type, Flag); |
| 238 | auto FVal = static_cast<uint64_t>(Flag); |
| 239 | bool IsCommon = std::is_same<SecCommonFlags, SecFlagType>(); |
| 240 | return Entry.Flags & (IsCommon ? FVal : (FVal << 32)); |
| 241 | } |
| 242 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 243 | /// Represents the relative location of an instruction. |
| 244 | /// |
| 245 | /// Instruction locations are specified by the line offset from the |
| 246 | /// beginning of the function (marked by the line where the function |
| 247 | /// header is) and the discriminator value within that line. |
| 248 | /// |
| 249 | /// The discriminator value is useful to distinguish instructions |
| 250 | /// that are on the same line but belong to different basic blocks |
| 251 | /// (e.g., the two post-increment instructions in "if (p) x++; else y++;"). |
| 252 | struct LineLocation { |
| 253 | LineLocation(uint32_t L, uint32_t D) : LineOffset(L), Discriminator(D) {} |
| 254 | |
| 255 | void print(raw_ostream &OS) const; |
| 256 | void dump() const; |
| 257 | |
| 258 | bool operator<(const LineLocation &O) const { |
| 259 | return LineOffset < O.LineOffset || |
| 260 | (LineOffset == O.LineOffset && Discriminator < O.Discriminator); |
| 261 | } |
| 262 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 263 | bool operator==(const LineLocation &O) const { |
| 264 | return LineOffset == O.LineOffset && Discriminator == O.Discriminator; |
| 265 | } |
| 266 | |
| 267 | bool operator!=(const LineLocation &O) const { |
| 268 | return LineOffset != O.LineOffset || Discriminator != O.Discriminator; |
| 269 | } |
| 270 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 271 | uint32_t LineOffset; |
| 272 | uint32_t Discriminator; |
| 273 | }; |
| 274 | |
| 275 | raw_ostream &operator<<(raw_ostream &OS, const LineLocation &Loc); |
| 276 | |
| 277 | /// Representation of a single sample record. |
| 278 | /// |
| 279 | /// A sample record is represented by a positive integer value, which |
| 280 | /// indicates how frequently was the associated line location executed. |
| 281 | /// |
| 282 | /// Additionally, if the associated location contains a function call, |
| 283 | /// the record will hold a list of all the possible called targets. For |
| 284 | /// direct calls, this will be the exact function being invoked. For |
| 285 | /// indirect calls (function pointers, virtual table dispatch), this |
| 286 | /// will be a list of one or more functions. |
| 287 | class SampleRecord { |
| 288 | public: |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 289 | using CallTarget = std::pair<StringRef, uint64_t>; |
| 290 | struct CallTargetComparator { |
| 291 | bool operator()(const CallTarget &LHS, const CallTarget &RHS) const { |
| 292 | if (LHS.second != RHS.second) |
| 293 | return LHS.second > RHS.second; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 294 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 295 | return LHS.first < RHS.first; |
| 296 | } |
| 297 | }; |
| 298 | |
| 299 | using SortedCallTargetSet = std::set<CallTarget, CallTargetComparator>; |
| 300 | using CallTargetMap = StringMap<uint64_t>; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 301 | SampleRecord() = default; |
| 302 | |
| 303 | /// Increment the number of samples for this record by \p S. |
| 304 | /// Optionally scale sample count \p S by \p Weight. |
| 305 | /// |
| 306 | /// Sample counts accumulate using saturating arithmetic, to avoid wrapping |
| 307 | /// around unsigned integers. |
| 308 | sampleprof_error addSamples(uint64_t S, uint64_t Weight = 1) { |
| 309 | bool Overflowed; |
| 310 | NumSamples = SaturatingMultiplyAdd(S, Weight, NumSamples, &Overflowed); |
| 311 | return Overflowed ? sampleprof_error::counter_overflow |
| 312 | : sampleprof_error::success; |
| 313 | } |
| 314 | |
| 315 | /// Add called function \p F with samples \p S. |
| 316 | /// Optionally scale sample count \p S by \p Weight. |
| 317 | /// |
| 318 | /// Sample counts accumulate using saturating arithmetic, to avoid wrapping |
| 319 | /// around unsigned integers. |
| 320 | sampleprof_error addCalledTarget(StringRef F, uint64_t S, |
| 321 | uint64_t Weight = 1) { |
| 322 | uint64_t &TargetSamples = CallTargets[F]; |
| 323 | bool Overflowed; |
| 324 | TargetSamples = |
| 325 | SaturatingMultiplyAdd(S, Weight, TargetSamples, &Overflowed); |
| 326 | return Overflowed ? sampleprof_error::counter_overflow |
| 327 | : sampleprof_error::success; |
| 328 | } |
| 329 | |
| 330 | /// Return true if this sample record contains function calls. |
| 331 | bool hasCalls() const { return !CallTargets.empty(); } |
| 332 | |
| 333 | uint64_t getSamples() const { return NumSamples; } |
| 334 | const CallTargetMap &getCallTargets() const { return CallTargets; } |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 335 | const SortedCallTargetSet getSortedCallTargets() const { |
| 336 | return SortCallTargets(CallTargets); |
| 337 | } |
| 338 | |
| 339 | /// Sort call targets in descending order of call frequency. |
| 340 | static const SortedCallTargetSet SortCallTargets(const CallTargetMap &Targets) { |
| 341 | SortedCallTargetSet SortedTargets; |
| 342 | for (const auto &I : Targets) { |
| 343 | SortedTargets.emplace(I.first(), I.second); |
| 344 | } |
| 345 | return SortedTargets; |
| 346 | } |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 347 | |
| 348 | /// Merge the samples in \p Other into this record. |
| 349 | /// Optionally scale sample counts by \p Weight. |
| 350 | sampleprof_error merge(const SampleRecord &Other, uint64_t Weight = 1) { |
| 351 | sampleprof_error Result = addSamples(Other.getSamples(), Weight); |
| 352 | for (const auto &I : Other.getCallTargets()) { |
| 353 | MergeResult(Result, addCalledTarget(I.first(), I.second, Weight)); |
| 354 | } |
| 355 | return Result; |
| 356 | } |
| 357 | |
| 358 | void print(raw_ostream &OS, unsigned Indent) const; |
| 359 | void dump() const; |
| 360 | |
| 361 | private: |
| 362 | uint64_t NumSamples = 0; |
| 363 | CallTargetMap CallTargets; |
| 364 | }; |
| 365 | |
| 366 | raw_ostream &operator<<(raw_ostream &OS, const SampleRecord &Sample); |
| 367 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 368 | // State of context associated with FunctionSamples |
| 369 | enum ContextStateMask { |
| 370 | UnknownContext = 0x0, // Profile without context |
| 371 | RawContext = 0x1, // Full context profile from input profile |
| 372 | SyntheticContext = 0x2, // Synthetic context created for context promotion |
| 373 | InlinedContext = 0x4, // Profile for context that is inlined into caller |
| 374 | MergedContext = 0x8 // Profile for context merged into base profile |
| 375 | }; |
| 376 | |
| 377 | // Sample context for FunctionSamples. It consists of the calling context, |
| 378 | // the function name and context state. Internally sample context is represented |
| 379 | // using StringRef, which is also the input for constructing a `SampleContext`. |
| 380 | // It can accept and represent both full context string as well as context-less |
| 381 | // function name. |
| 382 | // Example of full context string (note the wrapping `[]`): |
| 383 | // `[main:3 @ _Z5funcAi:1 @ _Z8funcLeafi]` |
| 384 | // Example of context-less function name (same as AutoFDO): |
| 385 | // `_Z8funcLeafi` |
| 386 | class SampleContext { |
| 387 | public: |
| 388 | SampleContext() : State(UnknownContext) {} |
| 389 | SampleContext(StringRef ContextStr, |
| 390 | ContextStateMask CState = UnknownContext) { |
| 391 | setContext(ContextStr, CState); |
| 392 | } |
| 393 | |
| 394 | // Promote context by removing top frames (represented by `ContextStrToRemove`). |
| 395 | // Note that with string representation of context, the promotion is effectively |
| 396 | // a substr operation with `ContextStrToRemove` removed from left. |
| 397 | void promoteOnPath(StringRef ContextStrToRemove) { |
| 398 | assert(FullContext.startswith(ContextStrToRemove)); |
| 399 | |
| 400 | // Remove leading context and frame separator " @ ". |
| 401 | FullContext = FullContext.substr(ContextStrToRemove.size() + 3); |
| 402 | CallingContext = CallingContext.substr(ContextStrToRemove.size() + 3); |
| 403 | } |
| 404 | |
| 405 | // Split the top context frame (left-most substr) from context. |
| 406 | static std::pair<StringRef, StringRef> |
| 407 | splitContextString(StringRef ContextStr) { |
| 408 | return ContextStr.split(" @ "); |
| 409 | } |
| 410 | |
| 411 | // Decode context string for a frame to get function name and location. |
| 412 | // `ContextStr` is in the form of `FuncName:StartLine.Discriminator`. |
| 413 | static void decodeContextString(StringRef ContextStr, StringRef &FName, |
| 414 | LineLocation &LineLoc) { |
| 415 | // Get function name |
| 416 | auto EntrySplit = ContextStr.split(':'); |
| 417 | FName = EntrySplit.first; |
| 418 | |
| 419 | LineLoc = {0, 0}; |
| 420 | if (!EntrySplit.second.empty()) { |
| 421 | // Get line offset, use signed int for getAsInteger so string will |
| 422 | // be parsed as signed. |
| 423 | int LineOffset = 0; |
| 424 | auto LocSplit = EntrySplit.second.split('.'); |
| 425 | LocSplit.first.getAsInteger(10, LineOffset); |
| 426 | LineLoc.LineOffset = LineOffset; |
| 427 | |
| 428 | // Get discriminator |
| 429 | if (!LocSplit.second.empty()) |
| 430 | LocSplit.second.getAsInteger(10, LineLoc.Discriminator); |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | operator StringRef() const { return FullContext; } |
| 435 | bool hasState(ContextStateMask S) { return State & (uint32_t)S; } |
| 436 | void setState(ContextStateMask S) { State |= (uint32_t)S; } |
| 437 | void clearState(ContextStateMask S) { State &= (uint32_t)~S; } |
| 438 | bool hasContext() const { return State != UnknownContext; } |
| 439 | bool isBaseContext() const { return CallingContext.empty(); } |
| 440 | StringRef getName() const { return Name; } |
| 441 | StringRef getCallingContext() const { return CallingContext; } |
| 442 | StringRef getNameWithContext() const { return FullContext; } |
| 443 | |
| 444 | private: |
| 445 | // Give a context string, decode and populate internal states like |
| 446 | // Function name, Calling context and context state. Example of input |
| 447 | // `ContextStr`: `[main:3 @ _Z5funcAi:1 @ _Z8funcLeafi]` |
| 448 | void setContext(StringRef ContextStr, ContextStateMask CState) { |
| 449 | assert(!ContextStr.empty()); |
| 450 | // Note that `[]` wrapped input indicates a full context string, otherwise |
| 451 | // it's treated as context-less function name only. |
| 452 | bool HasContext = ContextStr.startswith("["); |
| 453 | if (!HasContext && CState == UnknownContext) { |
| 454 | State = UnknownContext; |
| 455 | Name = FullContext = ContextStr; |
| 456 | } else { |
| 457 | // Assume raw context profile if unspecified |
| 458 | if (CState == UnknownContext) |
| 459 | State = RawContext; |
| 460 | else |
| 461 | State = CState; |
| 462 | |
| 463 | // Remove encapsulating '[' and ']' if any |
| 464 | if (HasContext) |
| 465 | FullContext = ContextStr.substr(1, ContextStr.size() - 2); |
| 466 | else |
| 467 | FullContext = ContextStr; |
| 468 | |
| 469 | // Caller is to the left of callee in context string |
| 470 | auto NameContext = FullContext.rsplit(" @ "); |
| 471 | if (NameContext.second.empty()) { |
| 472 | Name = NameContext.first; |
| 473 | CallingContext = NameContext.second; |
| 474 | } else { |
| 475 | Name = NameContext.second; |
| 476 | CallingContext = NameContext.first; |
| 477 | } |
| 478 | } |
| 479 | } |
| 480 | |
| 481 | // Full context string including calling context and leaf function name |
| 482 | StringRef FullContext; |
| 483 | // Function name for the associated sample profile |
| 484 | StringRef Name; |
| 485 | // Calling context (leaf function excluded) for the associated sample profile |
| 486 | StringRef CallingContext; |
| 487 | // State of the associated sample profile |
| 488 | uint32_t State; |
| 489 | }; |
| 490 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 491 | class FunctionSamples; |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 492 | class SampleProfileReaderItaniumRemapper; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 493 | |
| 494 | using BodySampleMap = std::map<LineLocation, SampleRecord>; |
| 495 | // NOTE: Using a StringMap here makes parsed profiles consume around 17% more |
| 496 | // memory, which is *very* significant for large profiles. |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 497 | using FunctionSamplesMap = std::map<std::string, FunctionSamples, std::less<>>; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 498 | using CallsiteSampleMap = std::map<LineLocation, FunctionSamplesMap>; |
| 499 | |
| 500 | /// Representation of the samples collected for a function. |
| 501 | /// |
| 502 | /// This data structure contains all the collected samples for the body |
| 503 | /// of a function. Each sample corresponds to a LineLocation instance |
| 504 | /// within the body of the function. |
| 505 | class FunctionSamples { |
| 506 | public: |
| 507 | FunctionSamples() = default; |
| 508 | |
| 509 | void print(raw_ostream &OS = dbgs(), unsigned Indent = 0) const; |
| 510 | void dump() const; |
| 511 | |
| 512 | sampleprof_error addTotalSamples(uint64_t Num, uint64_t Weight = 1) { |
| 513 | bool Overflowed; |
| 514 | TotalSamples = |
| 515 | SaturatingMultiplyAdd(Num, Weight, TotalSamples, &Overflowed); |
| 516 | return Overflowed ? sampleprof_error::counter_overflow |
| 517 | : sampleprof_error::success; |
| 518 | } |
| 519 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 520 | void setTotalSamples(uint64_t Num) { TotalSamples = Num; } |
| 521 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 522 | sampleprof_error addHeadSamples(uint64_t Num, uint64_t Weight = 1) { |
| 523 | bool Overflowed; |
| 524 | TotalHeadSamples = |
| 525 | SaturatingMultiplyAdd(Num, Weight, TotalHeadSamples, &Overflowed); |
| 526 | return Overflowed ? sampleprof_error::counter_overflow |
| 527 | : sampleprof_error::success; |
| 528 | } |
| 529 | |
| 530 | sampleprof_error addBodySamples(uint32_t LineOffset, uint32_t Discriminator, |
| 531 | uint64_t Num, uint64_t Weight = 1) { |
| 532 | return BodySamples[LineLocation(LineOffset, Discriminator)].addSamples( |
| 533 | Num, Weight); |
| 534 | } |
| 535 | |
| 536 | sampleprof_error addCalledTargetSamples(uint32_t LineOffset, |
| 537 | uint32_t Discriminator, |
| 538 | StringRef FName, uint64_t Num, |
| 539 | uint64_t Weight = 1) { |
| 540 | return BodySamples[LineLocation(LineOffset, Discriminator)].addCalledTarget( |
| 541 | FName, Num, Weight); |
| 542 | } |
| 543 | |
| 544 | /// Return the number of samples collected at the given location. |
| 545 | /// Each location is specified by \p LineOffset and \p Discriminator. |
| 546 | /// If the location is not found in profile, return error. |
| 547 | ErrorOr<uint64_t> findSamplesAt(uint32_t LineOffset, |
| 548 | uint32_t Discriminator) const { |
| 549 | const auto &ret = BodySamples.find(LineLocation(LineOffset, Discriminator)); |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 550 | if (ret == BodySamples.end()) { |
| 551 | // For CSSPGO, in order to conserve profile size, we no longer write out |
| 552 | // locations profile for those not hit during training, so we need to |
| 553 | // treat them as zero instead of error here. |
| 554 | if (ProfileIsCS) |
| 555 | return 0; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 556 | return std::error_code(); |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 557 | // A missing counter for a probe likely means the probe was not executed. |
| 558 | // Treat it as a zero count instead of an unknown count to help edge |
| 559 | // weight inference. |
| 560 | if (FunctionSamples::ProfileIsProbeBased) |
| 561 | return 0; |
| 562 | return std::error_code(); |
| 563 | } else { |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 564 | return ret->second.getSamples(); |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 565 | } |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 566 | } |
| 567 | |
| 568 | /// Returns the call target map collected at a given location. |
| 569 | /// Each location is specified by \p LineOffset and \p Discriminator. |
| 570 | /// If the location is not found in profile, return error. |
| 571 | ErrorOr<SampleRecord::CallTargetMap> |
| 572 | findCallTargetMapAt(uint32_t LineOffset, uint32_t Discriminator) const { |
| 573 | const auto &ret = BodySamples.find(LineLocation(LineOffset, Discriminator)); |
| 574 | if (ret == BodySamples.end()) |
| 575 | return std::error_code(); |
| 576 | return ret->second.getCallTargets(); |
| 577 | } |
| 578 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 579 | /// Returns the call target map collected at a given location specified by \p |
| 580 | /// CallSite. If the location is not found in profile, return error. |
| 581 | ErrorOr<SampleRecord::CallTargetMap> |
| 582 | findCallTargetMapAt(const LineLocation &CallSite) const { |
| 583 | const auto &Ret = BodySamples.find(CallSite); |
| 584 | if (Ret == BodySamples.end()) |
| 585 | return std::error_code(); |
| 586 | return Ret->second.getCallTargets(); |
| 587 | } |
| 588 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 589 | /// Return the function samples at the given callsite location. |
| 590 | FunctionSamplesMap &functionSamplesAt(const LineLocation &Loc) { |
| 591 | return CallsiteSamples[Loc]; |
| 592 | } |
| 593 | |
| 594 | /// Returns the FunctionSamplesMap at the given \p Loc. |
| 595 | const FunctionSamplesMap * |
| 596 | findFunctionSamplesMapAt(const LineLocation &Loc) const { |
| 597 | auto iter = CallsiteSamples.find(Loc); |
| 598 | if (iter == CallsiteSamples.end()) |
| 599 | return nullptr; |
| 600 | return &iter->second; |
| 601 | } |
| 602 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 603 | /// Returns a pointer to FunctionSamples at the given callsite location |
| 604 | /// \p Loc with callee \p CalleeName. If no callsite can be found, relax |
| 605 | /// the restriction to return the FunctionSamples at callsite location |
| 606 | /// \p Loc with the maximum total sample count. If \p Remapper is not |
| 607 | /// nullptr, use \p Remapper to find FunctionSamples with equivalent name |
| 608 | /// as \p CalleeName. |
| 609 | const FunctionSamples * |
| 610 | findFunctionSamplesAt(const LineLocation &Loc, StringRef CalleeName, |
| 611 | SampleProfileReaderItaniumRemapper *Remapper) const; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 612 | |
| 613 | bool empty() const { return TotalSamples == 0; } |
| 614 | |
| 615 | /// Return the total number of samples collected inside the function. |
| 616 | uint64_t getTotalSamples() const { return TotalSamples; } |
| 617 | |
| 618 | /// Return the total number of branch samples that have the function as the |
| 619 | /// branch target. This should be equivalent to the sample of the first |
| 620 | /// instruction of the symbol. But as we directly get this info for raw |
| 621 | /// profile without referring to potentially inaccurate debug info, this |
| 622 | /// gives more accurate profile data and is preferred for standalone symbols. |
| 623 | uint64_t getHeadSamples() const { return TotalHeadSamples; } |
| 624 | |
| 625 | /// Return the sample count of the first instruction of the function. |
| 626 | /// The function can be either a standalone symbol or an inlined function. |
| 627 | uint64_t getEntrySamples() const { |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 628 | if (FunctionSamples::ProfileIsCS && getHeadSamples()) { |
| 629 | // For CS profile, if we already have more accurate head samples |
| 630 | // counted by branch sample from caller, use them as entry samples. |
| 631 | return getHeadSamples(); |
| 632 | } |
| 633 | uint64_t Count = 0; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 634 | // Use either BodySamples or CallsiteSamples which ever has the smaller |
| 635 | // lineno. |
| 636 | if (!BodySamples.empty() && |
| 637 | (CallsiteSamples.empty() || |
| 638 | BodySamples.begin()->first < CallsiteSamples.begin()->first)) |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 639 | Count = BodySamples.begin()->second.getSamples(); |
| 640 | else if (!CallsiteSamples.empty()) { |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 641 | // An indirect callsite may be promoted to several inlined direct calls. |
| 642 | // We need to get the sum of them. |
| 643 | for (const auto &N_FS : CallsiteSamples.begin()->second) |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 644 | Count += N_FS.second.getEntrySamples(); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 645 | } |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 646 | // Return at least 1 if total sample is not 0. |
| 647 | return Count ? Count : TotalSamples > 0; |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 648 | } |
| 649 | |
| 650 | /// Return all the samples collected in the body of the function. |
| 651 | const BodySampleMap &getBodySamples() const { return BodySamples; } |
| 652 | |
| 653 | /// Return all the callsite samples collected in the body of the function. |
| 654 | const CallsiteSampleMap &getCallsiteSamples() const { |
| 655 | return CallsiteSamples; |
| 656 | } |
| 657 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 658 | /// Return the maximum of sample counts in a function body including functions |
| 659 | /// inlined in it. |
| 660 | uint64_t getMaxCountInside() const { |
| 661 | uint64_t MaxCount = 0; |
| 662 | for (const auto &L : getBodySamples()) |
| 663 | MaxCount = std::max(MaxCount, L.second.getSamples()); |
| 664 | for (const auto &C : getCallsiteSamples()) |
| 665 | for (const FunctionSamplesMap::value_type &F : C.second) |
| 666 | MaxCount = std::max(MaxCount, F.second.getMaxCountInside()); |
| 667 | return MaxCount; |
| 668 | } |
| 669 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 670 | /// Merge the samples in \p Other into this one. |
| 671 | /// Optionally scale samples by \p Weight. |
| 672 | sampleprof_error merge(const FunctionSamples &Other, uint64_t Weight = 1) { |
| 673 | sampleprof_error Result = sampleprof_error::success; |
| 674 | Name = Other.getName(); |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 675 | if (!GUIDToFuncNameMap) |
| 676 | GUIDToFuncNameMap = Other.GUIDToFuncNameMap; |
| 677 | |
| 678 | if (FunctionHash == 0) { |
| 679 | // Set the function hash code for the target profile. |
| 680 | FunctionHash = Other.getFunctionHash(); |
| 681 | } else if (FunctionHash != Other.getFunctionHash()) { |
| 682 | // The two profiles coming with different valid hash codes indicates |
| 683 | // either: |
| 684 | // 1. They are same-named static functions from different compilation |
| 685 | // units (without using -unique-internal-linkage-names), or |
| 686 | // 2. They are really the same function but from different compilations. |
| 687 | // Let's bail out in either case for now, which means one profile is |
| 688 | // dropped. |
| 689 | return sampleprof_error::hash_mismatch; |
| 690 | } |
| 691 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 692 | MergeResult(Result, addTotalSamples(Other.getTotalSamples(), Weight)); |
| 693 | MergeResult(Result, addHeadSamples(Other.getHeadSamples(), Weight)); |
| 694 | for (const auto &I : Other.getBodySamples()) { |
| 695 | const LineLocation &Loc = I.first; |
| 696 | const SampleRecord &Rec = I.second; |
| 697 | MergeResult(Result, BodySamples[Loc].merge(Rec, Weight)); |
| 698 | } |
| 699 | for (const auto &I : Other.getCallsiteSamples()) { |
| 700 | const LineLocation &Loc = I.first; |
| 701 | FunctionSamplesMap &FSMap = functionSamplesAt(Loc); |
| 702 | for (const auto &Rec : I.second) |
| 703 | MergeResult(Result, FSMap[Rec.first].merge(Rec.second, Weight)); |
| 704 | } |
| 705 | return Result; |
| 706 | } |
| 707 | |
| 708 | /// Recursively traverses all children, if the total sample count of the |
| 709 | /// corresponding function is no less than \p Threshold, add its corresponding |
| 710 | /// GUID to \p S. Also traverse the BodySamples to add hot CallTarget's GUID |
| 711 | /// to \p S. |
| 712 | void findInlinedFunctions(DenseSet<GlobalValue::GUID> &S, const Module *M, |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 713 | uint64_t Threshold) const { |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 714 | if (TotalSamples <= Threshold) |
| 715 | return; |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 716 | auto isDeclaration = [](const Function *F) { |
| 717 | return !F || F->isDeclaration(); |
| 718 | }; |
| 719 | if (isDeclaration(M->getFunction(getFuncName()))) { |
| 720 | // Add to the import list only when it's defined out of module. |
| 721 | S.insert(getGUID(Name)); |
| 722 | } |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 723 | // Import hot CallTargets, which may not be available in IR because full |
| 724 | // profile annotation cannot be done until backend compilation in ThinLTO. |
| 725 | for (const auto &BS : BodySamples) |
| 726 | for (const auto &TS : BS.second.getCallTargets()) |
| 727 | if (TS.getValue() > Threshold) { |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 728 | const Function *Callee = M->getFunction(getFuncName(TS.getKey())); |
| 729 | if (isDeclaration(Callee)) |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 730 | S.insert(getGUID(TS.getKey())); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 731 | } |
| 732 | for (const auto &CS : CallsiteSamples) |
| 733 | for (const auto &NameFS : CS.second) |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 734 | NameFS.second.findInlinedFunctions(S, M, Threshold); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 735 | } |
| 736 | |
| 737 | /// Set the name of the function. |
| 738 | void setName(StringRef FunctionName) { Name = FunctionName; } |
| 739 | |
| 740 | /// Return the function name. |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 741 | StringRef getName() const { return Name; } |
| 742 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 743 | /// Return function name with context. |
| 744 | StringRef getNameWithContext() const { |
| 745 | return FunctionSamples::ProfileIsCS ? Context.getNameWithContext() : Name; |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 746 | } |
| 747 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 748 | /// Return the original function name. |
| 749 | StringRef getFuncName() const { return getFuncName(Name); } |
| 750 | |
| 751 | void setFunctionHash(uint64_t Hash) { FunctionHash = Hash; } |
| 752 | |
| 753 | uint64_t getFunctionHash() const { return FunctionHash; } |
| 754 | |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 755 | /// Return the canonical name for a function, taking into account |
| 756 | /// suffix elision policy attributes. |
| 757 | static StringRef getCanonicalFnName(const Function &F) { |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 758 | auto AttrName = "sample-profile-suffix-elision-policy"; |
| 759 | auto Attr = F.getFnAttribute(AttrName).getValueAsString(); |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 760 | return getCanonicalFnName(F.getName(), Attr); |
| 761 | } |
| 762 | |
| 763 | static StringRef getCanonicalFnName(StringRef FnName, StringRef Attr = "") { |
| 764 | static const char *knownSuffixes[] = { ".llvm.", ".part." }; |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 765 | if (Attr == "" || Attr == "all") { |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 766 | return FnName.split('.').first; |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 767 | } else if (Attr == "selected") { |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 768 | StringRef Cand(FnName); |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 769 | for (const auto &Suf : knownSuffixes) { |
| 770 | StringRef Suffix(Suf); |
| 771 | auto It = Cand.rfind(Suffix); |
| 772 | if (It == StringRef::npos) |
| 773 | return Cand; |
| 774 | auto Dit = Cand.rfind('.'); |
| 775 | if (Dit == It + Suffix.size() - 1) |
| 776 | Cand = Cand.substr(0, It); |
| 777 | } |
| 778 | return Cand; |
| 779 | } else if (Attr == "none") { |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 780 | return FnName; |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 781 | } else { |
| 782 | assert(false && "internal error: unknown suffix elision policy"); |
| 783 | } |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 784 | return FnName; |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 785 | } |
| 786 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 787 | /// Translate \p Name into its original name. |
| 788 | /// When profile doesn't use MD5, \p Name needs no translation. |
| 789 | /// When profile uses MD5, \p Name in current FunctionSamples |
| 790 | /// is actually GUID of the original function name. getFuncName will |
| 791 | /// translate \p Name in current FunctionSamples into its original name |
| 792 | /// by looking up in the function map GUIDToFuncNameMap. |
| 793 | /// If the original name doesn't exist in the map, return empty StringRef. |
| 794 | StringRef getFuncName(StringRef Name) const { |
| 795 | if (!UseMD5) |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 796 | return Name; |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 797 | |
| 798 | assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be popluated first"); |
| 799 | return GUIDToFuncNameMap->lookup(std::stoull(Name.data())); |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 800 | } |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 801 | |
| 802 | /// Returns the line offset to the start line of the subprogram. |
| 803 | /// We assume that a single function will not exceed 65535 LOC. |
| 804 | static unsigned getOffset(const DILocation *DIL); |
| 805 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 806 | /// Returns a unique call site identifier for a given debug location of a call |
| 807 | /// instruction. This is wrapper of two scenarios, the probe-based profile and |
| 808 | /// regular profile, to hide implementation details from the sample loader and |
| 809 | /// the context tracker. |
| 810 | static LineLocation getCallSiteIdentifier(const DILocation *DIL); |
| 811 | |
Andrew Scull | cdfcccc | 2018-10-05 20:58:37 +0100 | [diff] [blame] | 812 | /// Get the FunctionSamples of the inline instance where DIL originates |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 813 | /// from. |
| 814 | /// |
| 815 | /// The FunctionSamples of the instruction (Machine or IR) associated to |
| 816 | /// \p DIL is the inlined instance in which that instruction is coming from. |
| 817 | /// We traverse the inline stack of that instruction, and match it with the |
| 818 | /// tree nodes in the profile. |
| 819 | /// |
| 820 | /// \returns the FunctionSamples pointer to the inlined instance. |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 821 | /// If \p Remapper is not nullptr, it will be used to find matching |
| 822 | /// FunctionSamples with not exactly the same but equivalent name. |
| 823 | const FunctionSamples *findFunctionSamples( |
| 824 | const DILocation *DIL, |
| 825 | SampleProfileReaderItaniumRemapper *Remapper = nullptr) const; |
| 826 | |
| 827 | static bool ProfileIsProbeBased; |
| 828 | |
| 829 | static bool ProfileIsCS; |
| 830 | |
| 831 | SampleContext &getContext() const { return Context; } |
| 832 | |
| 833 | void setContext(const SampleContext &FContext) { Context = FContext; } |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 834 | |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 835 | static SampleProfileFormat Format; |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 836 | |
| 837 | /// Whether the profile uses MD5 to represent string. |
| 838 | static bool UseMD5; |
| 839 | |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 840 | /// GUIDToFuncNameMap saves the mapping from GUID to the symbol name, for |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 841 | /// all the function symbols defined or declared in current module. |
| 842 | DenseMap<uint64_t, StringRef> *GUIDToFuncNameMap = nullptr; |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 843 | |
| 844 | // Assume the input \p Name is a name coming from FunctionSamples itself. |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 845 | // If UseMD5 is true, the name is already a GUID and we |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 846 | // don't want to return the GUID of GUID. |
| 847 | static uint64_t getGUID(StringRef Name) { |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 848 | return UseMD5 ? std::stoull(Name.data()) : Function::getGUID(Name); |
Andrew Scull | 0372a57 | 2018-11-16 15:47:06 +0000 | [diff] [blame] | 849 | } |
| 850 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 851 | // Find all the names in the current FunctionSamples including names in |
| 852 | // all the inline instances and names of call targets. |
| 853 | void findAllNames(DenseSet<StringRef> &NameSet) const; |
| 854 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 855 | private: |
| 856 | /// Mangled name of the function. |
| 857 | StringRef Name; |
| 858 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 859 | /// CFG hash value for the function. |
| 860 | uint64_t FunctionHash = 0; |
| 861 | |
| 862 | /// Calling context for function profile |
| 863 | mutable SampleContext Context; |
| 864 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 865 | /// Total number of samples collected inside this function. |
| 866 | /// |
| 867 | /// Samples are cumulative, they include all the samples collected |
| 868 | /// inside this function and all its inlined callees. |
| 869 | uint64_t TotalSamples = 0; |
| 870 | |
| 871 | /// Total number of samples collected at the head of the function. |
| 872 | /// This is an approximation of the number of calls made to this function |
| 873 | /// at runtime. |
| 874 | uint64_t TotalHeadSamples = 0; |
| 875 | |
| 876 | /// Map instruction locations to collected samples. |
| 877 | /// |
| 878 | /// Each entry in this map contains the number of samples |
| 879 | /// collected at the corresponding line offset. All line locations |
| 880 | /// are an offset from the start of the function. |
| 881 | BodySampleMap BodySamples; |
| 882 | |
| 883 | /// Map call sites to collected samples for the called function. |
| 884 | /// |
| 885 | /// Each entry in this map corresponds to all the samples |
| 886 | /// collected for the inlined function call at the given |
| 887 | /// location. For example, given: |
| 888 | /// |
| 889 | /// void foo() { |
| 890 | /// 1 bar(); |
| 891 | /// ... |
| 892 | /// 8 baz(); |
| 893 | /// } |
| 894 | /// |
| 895 | /// If the bar() and baz() calls were inlined inside foo(), this |
| 896 | /// map will contain two entries. One for all the samples collected |
| 897 | /// in the call to bar() at line offset 1, the other for all the samples |
| 898 | /// collected in the call to baz() at line offset 8. |
| 899 | CallsiteSampleMap CallsiteSamples; |
| 900 | }; |
| 901 | |
| 902 | raw_ostream &operator<<(raw_ostream &OS, const FunctionSamples &FS); |
| 903 | |
| 904 | /// Sort a LocationT->SampleT map by LocationT. |
| 905 | /// |
| 906 | /// It produces a sorted list of <LocationT, SampleT> records by ascending |
| 907 | /// order of LocationT. |
| 908 | template <class LocationT, class SampleT> class SampleSorter { |
| 909 | public: |
| 910 | using SamplesWithLoc = std::pair<const LocationT, SampleT>; |
| 911 | using SamplesWithLocList = SmallVector<const SamplesWithLoc *, 20>; |
| 912 | |
| 913 | SampleSorter(const std::map<LocationT, SampleT> &Samples) { |
| 914 | for (const auto &I : Samples) |
| 915 | V.push_back(&I); |
Andrew Walbran | 3d2c197 | 2020-04-07 12:24:26 +0100 | [diff] [blame] | 916 | llvm::stable_sort(V, [](const SamplesWithLoc *A, const SamplesWithLoc *B) { |
| 917 | return A->first < B->first; |
| 918 | }); |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 919 | } |
| 920 | |
| 921 | const SamplesWithLocList &get() const { return V; } |
| 922 | |
| 923 | private: |
| 924 | SamplesWithLocList V; |
| 925 | }; |
| 926 | |
Olivier Deprez | f4ef2d0 | 2021-04-20 13:36:24 +0200 | [diff] [blame] | 927 | /// ProfileSymbolList records the list of function symbols shown up |
| 928 | /// in the binary used to generate the profile. It is useful to |
| 929 | /// to discriminate a function being so cold as not to shown up |
| 930 | /// in the profile and a function newly added. |
| 931 | class ProfileSymbolList { |
| 932 | public: |
| 933 | /// copy indicates whether we need to copy the underlying memory |
| 934 | /// for the input Name. |
| 935 | void add(StringRef Name, bool copy = false) { |
| 936 | if (!copy) { |
| 937 | Syms.insert(Name); |
| 938 | return; |
| 939 | } |
| 940 | Syms.insert(Name.copy(Allocator)); |
| 941 | } |
| 942 | |
| 943 | bool contains(StringRef Name) { return Syms.count(Name); } |
| 944 | |
| 945 | void merge(const ProfileSymbolList &List) { |
| 946 | for (auto Sym : List.Syms) |
| 947 | add(Sym, true); |
| 948 | } |
| 949 | |
| 950 | unsigned size() { return Syms.size(); } |
| 951 | |
| 952 | void setToCompress(bool TC) { ToCompress = TC; } |
| 953 | bool toCompress() { return ToCompress; } |
| 954 | |
| 955 | std::error_code read(const uint8_t *Data, uint64_t ListSize); |
| 956 | std::error_code write(raw_ostream &OS); |
| 957 | void dump(raw_ostream &OS = dbgs()) const; |
| 958 | |
| 959 | private: |
| 960 | // Determine whether or not to compress the symbol list when |
| 961 | // writing it into profile. The variable is unused when the symbol |
| 962 | // list is read from an existing profile. |
| 963 | bool ToCompress = false; |
| 964 | DenseSet<StringRef> Syms; |
| 965 | BumpPtrAllocator Allocator; |
| 966 | }; |
| 967 | |
Andrew Scull | 5e1ddfa | 2018-08-14 10:06:54 +0100 | [diff] [blame] | 968 | } // end namespace sampleprof |
| 969 | } // end namespace llvm |
| 970 | |
| 971 | #endif // LLVM_PROFILEDATA_SAMPLEPROF_H |