Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 1 | #ifndef LLVM_PROFILEDATA_MEMPROF_H_ |
| 2 | #define LLVM_PROFILEDATA_MEMPROF_H_ |
||
| 3 | |||
| 4 | #include "llvm/ADT/DenseMap.h" |
||
| 5 | #include "llvm/ADT/STLFunctionalExtras.h" |
||
| 6 | #include "llvm/ADT/SmallVector.h" |
||
| 7 | #include "llvm/IR/GlobalValue.h" |
||
| 8 | #include "llvm/ProfileData/MemProfData.inc" |
||
| 9 | #include "llvm/Support/Endian.h" |
||
| 10 | #include "llvm/Support/EndianStream.h" |
||
| 11 | #include "llvm/Support/raw_ostream.h" |
||
| 12 | |||
| 13 | #include <cstdint> |
||
| 14 | #include <optional> |
||
| 15 | |||
| 16 | namespace llvm { |
||
| 17 | namespace memprof { |
||
| 18 | |||
| 19 | enum class Meta : uint64_t { |
||
| 20 | Start = 0, |
||
| 21 | #define MIBEntryDef(NameTag, Name, Type) NameTag, |
||
| 22 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
| 23 | #undef MIBEntryDef |
||
| 24 | Size |
||
| 25 | }; |
||
| 26 | |||
| 27 | using MemProfSchema = llvm::SmallVector<Meta, static_cast<int>(Meta::Size)>; |
||
| 28 | |||
| 29 | // Holds the actual MemInfoBlock data with all fields. Contents may be read or |
||
| 30 | // written partially by providing an appropriate schema to the serialize and |
||
| 31 | // deserialize methods. |
||
| 32 | struct PortableMemInfoBlock { |
||
| 33 | PortableMemInfoBlock() = default; |
||
| 34 | explicit PortableMemInfoBlock(const MemInfoBlock &Block) { |
||
| 35 | #define MIBEntryDef(NameTag, Name, Type) Name = Block.Name; |
||
| 36 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
| 37 | #undef MIBEntryDef |
||
| 38 | } |
||
| 39 | |||
| 40 | PortableMemInfoBlock(const MemProfSchema &Schema, const unsigned char *Ptr) { |
||
| 41 | deserialize(Schema, Ptr); |
||
| 42 | } |
||
| 43 | |||
| 44 | // Read the contents of \p Ptr based on the \p Schema to populate the |
||
| 45 | // MemInfoBlock member. |
||
| 46 | void deserialize(const MemProfSchema &Schema, const unsigned char *Ptr) { |
||
| 47 | using namespace support; |
||
| 48 | |||
| 49 | for (const Meta Id : Schema) { |
||
| 50 | switch (Id) { |
||
| 51 | #define MIBEntryDef(NameTag, Name, Type) \ |
||
| 52 | case Meta::Name: { \ |
||
| 53 | Name = endian::readNext<Type, little, unaligned>(Ptr); \ |
||
| 54 | } break; |
||
| 55 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
| 56 | #undef MIBEntryDef |
||
| 57 | default: |
||
| 58 | llvm_unreachable("Unknown meta type id, is the profile collected from " |
||
| 59 | "a newer version of the runtime?"); |
||
| 60 | } |
||
| 61 | } |
||
| 62 | } |
||
| 63 | |||
| 64 | // Write the contents of the MemInfoBlock based on the \p Schema provided to |
||
| 65 | // the raw_ostream \p OS. |
||
| 66 | void serialize(const MemProfSchema &Schema, raw_ostream &OS) const { |
||
| 67 | using namespace support; |
||
| 68 | |||
| 69 | endian::Writer LE(OS, little); |
||
| 70 | for (const Meta Id : Schema) { |
||
| 71 | switch (Id) { |
||
| 72 | #define MIBEntryDef(NameTag, Name, Type) \ |
||
| 73 | case Meta::Name: { \ |
||
| 74 | LE.write<Type>(Name); \ |
||
| 75 | } break; |
||
| 76 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
| 77 | #undef MIBEntryDef |
||
| 78 | default: |
||
| 79 | llvm_unreachable("Unknown meta type id, invalid input?"); |
||
| 80 | } |
||
| 81 | } |
||
| 82 | } |
||
| 83 | |||
| 84 | // Print out the contents of the MemInfoBlock in YAML format. |
||
| 85 | void printYAML(raw_ostream &OS) const { |
||
| 86 | OS << " MemInfoBlock:\n"; |
||
| 87 | #define MIBEntryDef(NameTag, Name, Type) \ |
||
| 88 | OS << " " << #Name << ": " << Name << "\n"; |
||
| 89 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
| 90 | #undef MIBEntryDef |
||
| 91 | } |
||
| 92 | |||
| 93 | // Define getters for each type which can be called by analyses. |
||
| 94 | #define MIBEntryDef(NameTag, Name, Type) \ |
||
| 95 | Type get##Name() const { return Name; } |
||
| 96 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
| 97 | #undef MIBEntryDef |
||
| 98 | |||
| 99 | void clear() { *this = PortableMemInfoBlock(); } |
||
| 100 | |||
| 101 | // Returns the full schema currently in use. |
||
| 102 | static MemProfSchema getSchema() { |
||
| 103 | MemProfSchema List; |
||
| 104 | #define MIBEntryDef(NameTag, Name, Type) List.push_back(Meta::Name); |
||
| 105 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
| 106 | #undef MIBEntryDef |
||
| 107 | return List; |
||
| 108 | } |
||
| 109 | |||
| 110 | bool operator==(const PortableMemInfoBlock &Other) const { |
||
| 111 | #define MIBEntryDef(NameTag, Name, Type) \ |
||
| 112 | if (Other.get##Name() != get##Name()) \ |
||
| 113 | return false; |
||
| 114 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
| 115 | #undef MIBEntryDef |
||
| 116 | return true; |
||
| 117 | } |
||
| 118 | |||
| 119 | bool operator!=(const PortableMemInfoBlock &Other) const { |
||
| 120 | return !operator==(Other); |
||
| 121 | } |
||
| 122 | |||
| 123 | static constexpr size_t serializedSize() { |
||
| 124 | size_t Result = 0; |
||
| 125 | #define MIBEntryDef(NameTag, Name, Type) Result += sizeof(Type); |
||
| 126 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
| 127 | #undef MIBEntryDef |
||
| 128 | return Result; |
||
| 129 | } |
||
| 130 | |||
| 131 | private: |
||
| 132 | #define MIBEntryDef(NameTag, Name, Type) Type Name = Type(); |
||
| 133 | #include "llvm/ProfileData/MIBEntryDef.inc" |
||
| 134 | #undef MIBEntryDef |
||
| 135 | }; |
||
| 136 | |||
| 137 | // A type representing the id generated by hashing the contents of the Frame. |
||
| 138 | using FrameId = uint64_t; |
||
| 139 | // Describes a call frame for a dynamic allocation context. The contents of |
||
| 140 | // the frame are populated by symbolizing the stack depot call frame from the |
||
| 141 | // compiler runtime. |
||
| 142 | struct Frame { |
||
| 143 | // A uuid (uint64_t) identifying the function. It is obtained by |
||
| 144 | // llvm::md5(FunctionName) which returns the lower 64 bits. |
||
| 145 | GlobalValue::GUID Function; |
||
| 146 | // The symbol name for the function. Only populated in the Frame by the reader |
||
| 147 | // if requested during initialization. This field should not be serialized. |
||
| 148 | std::optional<std::string> SymbolName; |
||
| 149 | // The source line offset of the call from the beginning of parent function. |
||
| 150 | uint32_t LineOffset; |
||
| 151 | // The source column number of the call to help distinguish multiple calls |
||
| 152 | // on the same line. |
||
| 153 | uint32_t Column; |
||
| 154 | // Whether the current frame is inlined. |
||
| 155 | bool IsInlineFrame; |
||
| 156 | |||
| 157 | Frame(const Frame &Other) { |
||
| 158 | Function = Other.Function; |
||
| 159 | SymbolName = Other.SymbolName; |
||
| 160 | LineOffset = Other.LineOffset; |
||
| 161 | Column = Other.Column; |
||
| 162 | IsInlineFrame = Other.IsInlineFrame; |
||
| 163 | } |
||
| 164 | |||
| 165 | Frame(uint64_t Hash, uint32_t Off, uint32_t Col, bool Inline) |
||
| 166 | : Function(Hash), LineOffset(Off), Column(Col), IsInlineFrame(Inline) {} |
||
| 167 | |||
| 168 | bool operator==(const Frame &Other) const { |
||
| 169 | // Ignore the SymbolName field to avoid a string compare. Comparing the |
||
| 170 | // function hash serves the same purpose. |
||
| 171 | return Other.Function == Function && Other.LineOffset == LineOffset && |
||
| 172 | Other.Column == Column && Other.IsInlineFrame == IsInlineFrame; |
||
| 173 | } |
||
| 174 | |||
| 175 | Frame &operator=(const Frame &Other) { |
||
| 176 | Function = Other.Function; |
||
| 177 | SymbolName = Other.SymbolName; |
||
| 178 | LineOffset = Other.LineOffset; |
||
| 179 | Column = Other.Column; |
||
| 180 | IsInlineFrame = Other.IsInlineFrame; |
||
| 181 | return *this; |
||
| 182 | } |
||
| 183 | |||
| 184 | bool operator!=(const Frame &Other) const { return !operator==(Other); } |
||
| 185 | |||
| 186 | // Write the contents of the frame to the ostream \p OS. |
||
| 187 | void serialize(raw_ostream &OS) const { |
||
| 188 | using namespace support; |
||
| 189 | |||
| 190 | endian::Writer LE(OS, little); |
||
| 191 | |||
| 192 | // If the type of the GlobalValue::GUID changes, then we need to update |
||
| 193 | // the reader and the writer. |
||
| 194 | static_assert(std::is_same<GlobalValue::GUID, uint64_t>::value, |
||
| 195 | "Expect GUID to be uint64_t."); |
||
| 196 | LE.write<uint64_t>(Function); |
||
| 197 | |||
| 198 | LE.write<uint32_t>(LineOffset); |
||
| 199 | LE.write<uint32_t>(Column); |
||
| 200 | LE.write<bool>(IsInlineFrame); |
||
| 201 | } |
||
| 202 | |||
| 203 | // Read a frame from char data which has been serialized as little endian. |
||
| 204 | static Frame deserialize(const unsigned char *Ptr) { |
||
| 205 | using namespace support; |
||
| 206 | |||
| 207 | const uint64_t F = endian::readNext<uint64_t, little, unaligned>(Ptr); |
||
| 208 | const uint32_t L = endian::readNext<uint32_t, little, unaligned>(Ptr); |
||
| 209 | const uint32_t C = endian::readNext<uint32_t, little, unaligned>(Ptr); |
||
| 210 | const bool I = endian::readNext<bool, little, unaligned>(Ptr); |
||
| 211 | return Frame(/*Function=*/F, /*LineOffset=*/L, /*Column=*/C, |
||
| 212 | /*IsInlineFrame=*/I); |
||
| 213 | } |
||
| 214 | |||
| 215 | // Returns the size of the frame information. |
||
| 216 | static constexpr size_t serializedSize() { |
||
| 217 | return sizeof(Frame::Function) + sizeof(Frame::LineOffset) + |
||
| 218 | sizeof(Frame::Column) + sizeof(Frame::IsInlineFrame); |
||
| 219 | } |
||
| 220 | |||
| 221 | // Print the frame information in YAML format. |
||
| 222 | void printYAML(raw_ostream &OS) const { |
||
| 223 | OS << " -\n" |
||
| 224 | << " Function: " << Function << "\n" |
||
| 225 | << " SymbolName: " << SymbolName.value_or("<None>") << "\n" |
||
| 226 | << " LineOffset: " << LineOffset << "\n" |
||
| 227 | << " Column: " << Column << "\n" |
||
| 228 | << " Inline: " << IsInlineFrame << "\n"; |
||
| 229 | } |
||
| 230 | |||
| 231 | // Return a hash value based on the contents of the frame. Here we don't use |
||
| 232 | // hashing from llvm ADT since we are going to persist the hash id, the hash |
||
| 233 | // combine algorithm in ADT uses a new randomized seed each time. |
||
| 234 | inline FrameId hash() const { |
||
| 235 | auto HashCombine = [](auto Value, size_t Seed) { |
||
| 236 | std::hash<decltype(Value)> Hasher; |
||
| 237 | // The constant used below is the 64 bit representation of the fractional |
||
| 238 | // part of the golden ratio. Used here for the randomness in their bit |
||
| 239 | // pattern. |
||
| 240 | return Hasher(Value) + 0x9e3779b97f4a7c15 + (Seed << 6) + (Seed >> 2); |
||
| 241 | }; |
||
| 242 | |||
| 243 | size_t Result = 0; |
||
| 244 | Result ^= HashCombine(Function, Result); |
||
| 245 | Result ^= HashCombine(LineOffset, Result); |
||
| 246 | Result ^= HashCombine(Column, Result); |
||
| 247 | Result ^= HashCombine(IsInlineFrame, Result); |
||
| 248 | return static_cast<FrameId>(Result); |
||
| 249 | } |
||
| 250 | }; |
||
| 251 | |||
| 252 | // Holds allocation information in a space efficient format where frames are |
||
| 253 | // represented using unique identifiers. |
||
| 254 | struct IndexedAllocationInfo { |
||
| 255 | // The dynamic calling context for the allocation in bottom-up (leaf-to-root) |
||
| 256 | // order. Frame contents are stored out-of-line. |
||
| 257 | llvm::SmallVector<FrameId> CallStack; |
||
| 258 | // The statistics obtained from the runtime for the allocation. |
||
| 259 | PortableMemInfoBlock Info; |
||
| 260 | |||
| 261 | IndexedAllocationInfo() = default; |
||
| 262 | IndexedAllocationInfo(ArrayRef<FrameId> CS, const MemInfoBlock &MB) |
||
| 263 | : CallStack(CS.begin(), CS.end()), Info(MB) {} |
||
| 264 | |||
| 265 | // Returns the size in bytes when this allocation info struct is serialized. |
||
| 266 | size_t serializedSize() const { |
||
| 267 | return sizeof(uint64_t) + // The number of frames to serialize. |
||
| 268 | sizeof(FrameId) * CallStack.size() + // The callstack frame ids. |
||
| 269 | PortableMemInfoBlock::serializedSize(); // The size of the payload. |
||
| 270 | } |
||
| 271 | |||
| 272 | bool operator==(const IndexedAllocationInfo &Other) const { |
||
| 273 | if (Other.Info != Info) |
||
| 274 | return false; |
||
| 275 | |||
| 276 | if (Other.CallStack.size() != CallStack.size()) |
||
| 277 | return false; |
||
| 278 | |||
| 279 | for (size_t J = 0; J < Other.CallStack.size(); J++) { |
||
| 280 | if (Other.CallStack[J] != CallStack[J]) |
||
| 281 | return false; |
||
| 282 | } |
||
| 283 | return true; |
||
| 284 | } |
||
| 285 | |||
| 286 | bool operator!=(const IndexedAllocationInfo &Other) const { |
||
| 287 | return !operator==(Other); |
||
| 288 | } |
||
| 289 | }; |
||
| 290 | |||
| 291 | // Holds allocation information with frame contents inline. The type should |
||
| 292 | // be used for temporary in-memory instances. |
||
| 293 | struct AllocationInfo { |
||
| 294 | // Same as IndexedAllocationInfo::CallStack with the frame contents inline. |
||
| 295 | llvm::SmallVector<Frame> CallStack; |
||
| 296 | // Same as IndexedAllocationInfo::Info; |
||
| 297 | PortableMemInfoBlock Info; |
||
| 298 | |||
| 299 | AllocationInfo() = default; |
||
| 300 | AllocationInfo( |
||
| 301 | const IndexedAllocationInfo &IndexedAI, |
||
| 302 | llvm::function_ref<const Frame(const FrameId)> IdToFrameCallback) { |
||
| 303 | for (const FrameId &Id : IndexedAI.CallStack) { |
||
| 304 | CallStack.push_back(IdToFrameCallback(Id)); |
||
| 305 | } |
||
| 306 | Info = IndexedAI.Info; |
||
| 307 | } |
||
| 308 | |||
| 309 | void printYAML(raw_ostream &OS) const { |
||
| 310 | OS << " -\n"; |
||
| 311 | OS << " Callstack:\n"; |
||
| 312 | // TODO: Print out the frame on one line with to make it easier for deep |
||
| 313 | // callstacks once we have a test to check valid YAML is generated. |
||
| 314 | for (const Frame &F : CallStack) { |
||
| 315 | F.printYAML(OS); |
||
| 316 | } |
||
| 317 | Info.printYAML(OS); |
||
| 318 | } |
||
| 319 | }; |
||
| 320 | |||
| 321 | // Holds the memprof profile information for a function. The internal |
||
| 322 | // representation stores frame ids for efficiency. This representation should |
||
| 323 | // be used in the profile conversion and manipulation tools. |
||
| 324 | struct IndexedMemProfRecord { |
||
| 325 | // Memory allocation sites in this function for which we have memory |
||
| 326 | // profiling data. |
||
| 327 | llvm::SmallVector<IndexedAllocationInfo> AllocSites; |
||
| 328 | // Holds call sites in this function which are part of some memory |
||
| 329 | // allocation context. We store this as a list of locations, each with its |
||
| 330 | // list of inline locations in bottom-up order i.e. from leaf to root. The |
||
| 331 | // inline location list may include additional entries, users should pick |
||
| 332 | // the last entry in the list with the same function GUID. |
||
| 333 | llvm::SmallVector<llvm::SmallVector<FrameId>> CallSites; |
||
| 334 | |||
| 335 | void clear() { |
||
| 336 | AllocSites.clear(); |
||
| 337 | CallSites.clear(); |
||
| 338 | } |
||
| 339 | |||
| 340 | void merge(const IndexedMemProfRecord &Other) { |
||
| 341 | // TODO: Filter out duplicates which may occur if multiple memprof |
||
| 342 | // profiles are merged together using llvm-profdata. |
||
| 343 | AllocSites.append(Other.AllocSites); |
||
| 344 | CallSites.append(Other.CallSites); |
||
| 345 | } |
||
| 346 | |||
| 347 | size_t serializedSize() const { |
||
| 348 | size_t Result = sizeof(GlobalValue::GUID); |
||
| 349 | for (const IndexedAllocationInfo &N : AllocSites) |
||
| 350 | Result += N.serializedSize(); |
||
| 351 | |||
| 352 | // The number of callsites we have information for. |
||
| 353 | Result += sizeof(uint64_t); |
||
| 354 | for (const auto &Frames : CallSites) { |
||
| 355 | // The number of frame ids to serialize. |
||
| 356 | Result += sizeof(uint64_t); |
||
| 357 | Result += Frames.size() * sizeof(FrameId); |
||
| 358 | } |
||
| 359 | return Result; |
||
| 360 | } |
||
| 361 | |||
| 362 | bool operator==(const IndexedMemProfRecord &Other) const { |
||
| 363 | if (Other.AllocSites.size() != AllocSites.size()) |
||
| 364 | return false; |
||
| 365 | |||
| 366 | if (Other.CallSites.size() != CallSites.size()) |
||
| 367 | return false; |
||
| 368 | |||
| 369 | for (size_t I = 0; I < AllocSites.size(); I++) { |
||
| 370 | if (AllocSites[I] != Other.AllocSites[I]) |
||
| 371 | return false; |
||
| 372 | } |
||
| 373 | |||
| 374 | for (size_t I = 0; I < CallSites.size(); I++) { |
||
| 375 | if (CallSites[I] != Other.CallSites[I]) |
||
| 376 | return false; |
||
| 377 | } |
||
| 378 | return true; |
||
| 379 | } |
||
| 380 | |||
| 381 | // Serializes the memprof records in \p Records to the ostream \p OS based |
||
| 382 | // on the schema provided in \p Schema. |
||
| 383 | void serialize(const MemProfSchema &Schema, raw_ostream &OS); |
||
| 384 | |||
| 385 | // Deserializes memprof records from the Buffer. |
||
| 386 | static IndexedMemProfRecord deserialize(const MemProfSchema &Schema, |
||
| 387 | const unsigned char *Buffer); |
||
| 388 | |||
| 389 | // Returns the GUID for the function name after canonicalization. For |
||
| 390 | // memprof, we remove any .llvm suffix added by LTO. MemProfRecords are |
||
| 391 | // mapped to functions using this GUID. |
||
| 392 | static GlobalValue::GUID getGUID(const StringRef FunctionName); |
||
| 393 | }; |
||
| 394 | |||
| 395 | // Holds the memprof profile information for a function. The internal |
||
| 396 | // representation stores frame contents inline. This representation should |
||
| 397 | // be used for small amount of temporary, in memory instances. |
||
| 398 | struct MemProfRecord { |
||
| 399 | // Same as IndexedMemProfRecord::AllocSites with frame contents inline. |
||
| 400 | llvm::SmallVector<AllocationInfo> AllocSites; |
||
| 401 | // Same as IndexedMemProfRecord::CallSites with frame contents inline. |
||
| 402 | llvm::SmallVector<llvm::SmallVector<Frame>> CallSites; |
||
| 403 | |||
| 404 | MemProfRecord() = default; |
||
| 405 | MemProfRecord( |
||
| 406 | const IndexedMemProfRecord &Record, |
||
| 407 | llvm::function_ref<const Frame(const FrameId Id)> IdToFrameCallback) { |
||
| 408 | for (const IndexedAllocationInfo &IndexedAI : Record.AllocSites) { |
||
| 409 | AllocSites.emplace_back(IndexedAI, IdToFrameCallback); |
||
| 410 | } |
||
| 411 | for (const ArrayRef<FrameId> Site : Record.CallSites) { |
||
| 412 | llvm::SmallVector<Frame> Frames; |
||
| 413 | for (const FrameId Id : Site) { |
||
| 414 | Frames.push_back(IdToFrameCallback(Id)); |
||
| 415 | } |
||
| 416 | CallSites.push_back(Frames); |
||
| 417 | } |
||
| 418 | } |
||
| 419 | |||
| 420 | // Prints out the contents of the memprof record in YAML. |
||
| 421 | void print(llvm::raw_ostream &OS) const { |
||
| 422 | if (!AllocSites.empty()) { |
||
| 423 | OS << " AllocSites:\n"; |
||
| 424 | for (const AllocationInfo &N : AllocSites) |
||
| 425 | N.printYAML(OS); |
||
| 426 | } |
||
| 427 | |||
| 428 | if (!CallSites.empty()) { |
||
| 429 | OS << " CallSites:\n"; |
||
| 430 | for (const llvm::SmallVector<Frame> &Frames : CallSites) { |
||
| 431 | for (const Frame &F : Frames) { |
||
| 432 | OS << " -\n"; |
||
| 433 | F.printYAML(OS); |
||
| 434 | } |
||
| 435 | } |
||
| 436 | } |
||
| 437 | } |
||
| 438 | }; |
||
| 439 | |||
| 440 | // Reads a memprof schema from a buffer. All entries in the buffer are |
||
| 441 | // interpreted as uint64_t. The first entry in the buffer denotes the number of |
||
| 442 | // ids in the schema. Subsequent entries are integers which map to memprof::Meta |
||
| 443 | // enum class entries. After successfully reading the schema, the pointer is one |
||
| 444 | // byte past the schema contents. |
||
| 445 | Expected<MemProfSchema> readMemProfSchema(const unsigned char *&Buffer); |
||
| 446 | |||
| 447 | // Trait for reading IndexedMemProfRecord data from the on-disk hash table. |
||
| 448 | class RecordLookupTrait { |
||
| 449 | public: |
||
| 450 | using data_type = const IndexedMemProfRecord &; |
||
| 451 | using internal_key_type = uint64_t; |
||
| 452 | using external_key_type = uint64_t; |
||
| 453 | using hash_value_type = uint64_t; |
||
| 454 | using offset_type = uint64_t; |
||
| 455 | |||
| 456 | RecordLookupTrait() = delete; |
||
| 457 | RecordLookupTrait(const MemProfSchema &S) : Schema(S) {} |
||
| 458 | |||
| 459 | static bool EqualKey(uint64_t A, uint64_t B) { return A == B; } |
||
| 460 | static uint64_t GetInternalKey(uint64_t K) { return K; } |
||
| 461 | static uint64_t GetExternalKey(uint64_t K) { return K; } |
||
| 462 | |||
| 463 | hash_value_type ComputeHash(uint64_t K) { return K; } |
||
| 464 | |||
| 465 | static std::pair<offset_type, offset_type> |
||
| 466 | ReadKeyDataLength(const unsigned char *&D) { |
||
| 467 | using namespace support; |
||
| 468 | |||
| 469 | offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D); |
||
| 470 | offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D); |
||
| 471 | return std::make_pair(KeyLen, DataLen); |
||
| 472 | } |
||
| 473 | |||
| 474 | uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) { |
||
| 475 | using namespace support; |
||
| 476 | return endian::readNext<external_key_type, little, unaligned>(D); |
||
| 477 | } |
||
| 478 | |||
| 479 | data_type ReadData(uint64_t K, const unsigned char *D, |
||
| 480 | offset_type /*Unused*/) { |
||
| 481 | Record = IndexedMemProfRecord::deserialize(Schema, D); |
||
| 482 | return Record; |
||
| 483 | } |
||
| 484 | |||
| 485 | private: |
||
| 486 | // Holds the memprof schema used to deserialize records. |
||
| 487 | MemProfSchema Schema; |
||
| 488 | // Holds the records from one function deserialized from the indexed format. |
||
| 489 | IndexedMemProfRecord Record; |
||
| 490 | }; |
||
| 491 | |||
| 492 | // Trait for writing IndexedMemProfRecord data to the on-disk hash table. |
||
| 493 | class RecordWriterTrait { |
||
| 494 | public: |
||
| 495 | using key_type = uint64_t; |
||
| 496 | using key_type_ref = uint64_t; |
||
| 497 | |||
| 498 | using data_type = IndexedMemProfRecord; |
||
| 499 | using data_type_ref = IndexedMemProfRecord &; |
||
| 500 | |||
| 501 | using hash_value_type = uint64_t; |
||
| 502 | using offset_type = uint64_t; |
||
| 503 | |||
| 504 | // Pointer to the memprof schema to use for the generator. Unlike the reader |
||
| 505 | // we must use a default constructor with no params for the writer trait so we |
||
| 506 | // have a public member which must be initialized by the user. |
||
| 507 | MemProfSchema *Schema = nullptr; |
||
| 508 | |||
| 509 | RecordWriterTrait() = default; |
||
| 510 | |||
| 511 | static hash_value_type ComputeHash(key_type_ref K) { return K; } |
||
| 512 | |||
| 513 | static std::pair<offset_type, offset_type> |
||
| 514 | EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) { |
||
| 515 | using namespace support; |
||
| 516 | |||
| 517 | endian::Writer LE(Out, little); |
||
| 518 | offset_type N = sizeof(K); |
||
| 519 | LE.write<offset_type>(N); |
||
| 520 | offset_type M = V.serializedSize(); |
||
| 521 | LE.write<offset_type>(M); |
||
| 522 | return std::make_pair(N, M); |
||
| 523 | } |
||
| 524 | |||
| 525 | void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) { |
||
| 526 | using namespace support; |
||
| 527 | endian::Writer LE(Out, little); |
||
| 528 | LE.write<uint64_t>(K); |
||
| 529 | } |
||
| 530 | |||
| 531 | void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V, |
||
| 532 | offset_type /*Unused*/) { |
||
| 533 | assert(Schema != nullptr && "MemProf schema is not initialized!"); |
||
| 534 | V.serialize(*Schema, Out); |
||
| 535 | } |
||
| 536 | }; |
||
| 537 | |||
| 538 | // Trait for writing frame mappings to the on-disk hash table. |
||
| 539 | class FrameWriterTrait { |
||
| 540 | public: |
||
| 541 | using key_type = FrameId; |
||
| 542 | using key_type_ref = FrameId; |
||
| 543 | |||
| 544 | using data_type = Frame; |
||
| 545 | using data_type_ref = Frame &; |
||
| 546 | |||
| 547 | using hash_value_type = FrameId; |
||
| 548 | using offset_type = uint64_t; |
||
| 549 | |||
| 550 | static hash_value_type ComputeHash(key_type_ref K) { return K; } |
||
| 551 | |||
| 552 | static std::pair<offset_type, offset_type> |
||
| 553 | EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) { |
||
| 554 | using namespace support; |
||
| 555 | endian::Writer LE(Out, little); |
||
| 556 | offset_type N = sizeof(K); |
||
| 557 | LE.write<offset_type>(N); |
||
| 558 | offset_type M = V.serializedSize(); |
||
| 559 | LE.write<offset_type>(M); |
||
| 560 | return std::make_pair(N, M); |
||
| 561 | } |
||
| 562 | |||
| 563 | void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) { |
||
| 564 | using namespace support; |
||
| 565 | endian::Writer LE(Out, little); |
||
| 566 | LE.write<key_type>(K); |
||
| 567 | } |
||
| 568 | |||
| 569 | void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V, |
||
| 570 | offset_type /*Unused*/) { |
||
| 571 | V.serialize(Out); |
||
| 572 | } |
||
| 573 | }; |
||
| 574 | |||
| 575 | // Trait for reading frame mappings from the on-disk hash table. |
||
| 576 | class FrameLookupTrait { |
||
| 577 | public: |
||
| 578 | using data_type = const Frame; |
||
| 579 | using internal_key_type = FrameId; |
||
| 580 | using external_key_type = FrameId; |
||
| 581 | using hash_value_type = FrameId; |
||
| 582 | using offset_type = uint64_t; |
||
| 583 | |||
| 584 | static bool EqualKey(internal_key_type A, internal_key_type B) { |
||
| 585 | return A == B; |
||
| 586 | } |
||
| 587 | static uint64_t GetInternalKey(internal_key_type K) { return K; } |
||
| 588 | static uint64_t GetExternalKey(external_key_type K) { return K; } |
||
| 589 | |||
| 590 | hash_value_type ComputeHash(internal_key_type K) { return K; } |
||
| 591 | |||
| 592 | static std::pair<offset_type, offset_type> |
||
| 593 | ReadKeyDataLength(const unsigned char *&D) { |
||
| 594 | using namespace support; |
||
| 595 | |||
| 596 | offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D); |
||
| 597 | offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D); |
||
| 598 | return std::make_pair(KeyLen, DataLen); |
||
| 599 | } |
||
| 600 | |||
| 601 | uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) { |
||
| 602 | using namespace support; |
||
| 603 | return endian::readNext<external_key_type, little, unaligned>(D); |
||
| 604 | } |
||
| 605 | |||
| 606 | data_type ReadData(uint64_t K, const unsigned char *D, |
||
| 607 | offset_type /*Unused*/) { |
||
| 608 | return Frame::deserialize(D); |
||
| 609 | } |
||
| 610 | }; |
||
| 611 | } // namespace memprof |
||
| 612 | } // namespace llvm |
||
| 613 | |||
| 614 | #endif // LLVM_PROFILEDATA_MEMPROF_H_ |