#ifndef MEMPROF_DATA_INC #define MEMPROF_DATA_INC /*===-- MemProfData.inc - MemProf profiling runtime structures -*- C++ -*-=== *\ |* |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |* See https://llvm.org/LICENSE.txt for license information. |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |* \*===----------------------------------------------------------------------===*/ /* * This is the main file that defines all the data structure, signature, * constant literals that are shared across profiling runtime library, * and host tools (reader/writer). * * This file has two identical copies. The primary copy lives in LLVM and * the other one sits in compiler-rt/include/profile directory. To make changes * in this file, first modify the primary copy and copy it over to compiler-rt. * Testing of any change in this file can start only after the two copies are * synced up. * \*===----------------------------------------------------------------------===*/ #include #ifdef _MSC_VER #define PACKED(...) __pragma(pack(push,1)) __VA_ARGS__ __pragma(pack(pop)) #else #define PACKED(...) __VA_ARGS__ __attribute__((__packed__)) #endif // A 64-bit magic number to uniquely identify the raw binary memprof profile file. #define MEMPROF_RAW_MAGIC_64 \ ((uint64_t)255 << 56 | (uint64_t)'m' << 48 | (uint64_t)'p' << 40 | (uint64_t)'r' << 32 | \ (uint64_t)'o' << 24 | (uint64_t)'f' << 16 | (uint64_t)'r' << 8 | (uint64_t)129) // The version number of the raw binary format. #define MEMPROF_RAW_VERSION 4ULL // Currently supported versions. #define MEMPROF_RAW_SUPPORTED_VERSIONS \ { 3ULL, 4ULL } #define MEMPROF_V3_MIB_SIZE 132ULL; #define MEMPROF_BUILDID_MAX_SIZE 32ULL namespace llvm { namespace memprof { // A struct describing the header used for the raw binary memprof profile format. PACKED(struct Header { uint64_t Magic; uint64_t Version; uint64_t TotalSize; uint64_t SegmentOffset; uint64_t MIBOffset; uint64_t StackOffset; }); // A struct describing the information necessary to describe a /proc/maps // segment entry for a particular binary/library identified by its build id. PACKED(struct SegmentEntry { uint64_t Start; uint64_t End; uint64_t Offset; uint64_t BuildIdSize; uint8_t BuildId[MEMPROF_BUILDID_MAX_SIZE] = {0}; // This constructor is only used in tests so don't set the BuildId. SegmentEntry(uint64_t S, uint64_t E, uint64_t O) : Start(S), End(E), Offset(O), BuildIdSize(0) {} SegmentEntry(const SegmentEntry& S) { Start = S.Start; End = S.End; Offset = S.Offset; BuildIdSize = S.BuildIdSize; memcpy(BuildId, S.BuildId, S.BuildIdSize); } SegmentEntry& operator=(const SegmentEntry& S) { Start = S.Start; End = S.End; Offset = S.Offset; BuildIdSize = S.BuildIdSize; memcpy(BuildId, S.BuildId, S.BuildIdSize); return *this; } bool operator==(const SegmentEntry& S) const { return Start == S.Start && End == S.End && Offset == S.Offset && BuildIdSize == S.BuildIdSize && memcmp(BuildId, S.BuildId, S.BuildIdSize) == 0; } }); // Packed struct definition for MSVC. We can't use the PACKED macro defined in // MemProfData.inc since it would mean we are embedding a directive (the // #include for MIBEntryDef) into the macros which is undefined behaviour. #ifdef _MSC_VER __pragma(pack(push,1)) #endif // A struct representing the heap allocation characteristics of a particular // runtime context. This struct is shared between the compiler-rt runtime and // the raw profile reader. The indexed format uses a separate, self-describing // backwards compatible format. struct MemInfoBlock{ #define MIBEntryDef(NameTag, Name, Type) Type Name; #include "MIBEntryDef.inc" #undef MIBEntryDef bool operator==(const MemInfoBlock& Other) const { bool IsEqual = true; #define MIBEntryDef(NameTag, Name, Type) \ IsEqual = (IsEqual && Name == Other.Name); #include "MIBEntryDef.inc" #undef MIBEntryDef return IsEqual; } MemInfoBlock() { #define MIBEntryDef(NameTag, Name, Type) Name = Type(); #include "MIBEntryDef.inc" #undef MIBEntryDef } MemInfoBlock(uint32_t Size, uint64_t AccessCount, uint32_t AllocTs, uint32_t DeallocTs, uint32_t AllocCpu, uint32_t DeallocCpu, uintptr_t Histogram, uint32_t HistogramSize) : MemInfoBlock() { AllocCount = 1U; TotalAccessCount = AccessCount; MinAccessCount = AccessCount; MaxAccessCount = AccessCount; TotalSize = Size; MinSize = Size; MaxSize = Size; AllocTimestamp = AllocTs; DeallocTimestamp = DeallocTs; TotalLifetime = DeallocTimestamp - AllocTimestamp; MinLifetime = TotalLifetime; MaxLifetime = TotalLifetime; // Access density is accesses per byte. Multiply by 100 to include the // fractional part. TotalAccessDensity = AccessCount * 100 / Size; MinAccessDensity = TotalAccessDensity; MaxAccessDensity = TotalAccessDensity; // Lifetime access density is the access density per second of lifetime. // Multiply by 1000 to convert denominator lifetime to seconds (using a // minimum lifetime of 1ms to avoid divide by 0. Do the multiplication first // to reduce truncations to 0. TotalLifetimeAccessDensity = TotalAccessDensity * 1000 / (TotalLifetime ? TotalLifetime : 1); MinLifetimeAccessDensity = TotalLifetimeAccessDensity; MaxLifetimeAccessDensity = TotalLifetimeAccessDensity; AllocCpuId = AllocCpu; DeallocCpuId = DeallocCpu; NumMigratedCpu = AllocCpuId != DeallocCpuId; AccessHistogramSize = HistogramSize; AccessHistogram = Histogram; } void Merge(const MemInfoBlock &newMIB) { AllocCount += newMIB.AllocCount; TotalAccessCount += newMIB.TotalAccessCount; MinAccessCount = newMIB.MinAccessCount < MinAccessCount ? newMIB.MinAccessCount : MinAccessCount; MaxAccessCount = newMIB.MaxAccessCount > MaxAccessCount ? newMIB.MaxAccessCount : MaxAccessCount; TotalSize += newMIB.TotalSize; MinSize = newMIB.MinSize < MinSize ? newMIB.MinSize : MinSize; MaxSize = newMIB.MaxSize > MaxSize ? newMIB.MaxSize : MaxSize; TotalLifetime += newMIB.TotalLifetime; MinLifetime = newMIB.MinLifetime < MinLifetime ? newMIB.MinLifetime : MinLifetime; MaxLifetime = newMIB.MaxLifetime > MaxLifetime ? newMIB.MaxLifetime : MaxLifetime; TotalAccessDensity += newMIB.TotalAccessDensity; MinAccessDensity = newMIB.MinAccessDensity < MinAccessDensity ? newMIB.MinAccessDensity : MinAccessDensity; MaxAccessDensity = newMIB.MaxAccessDensity > MaxAccessDensity ? newMIB.MaxAccessDensity : MaxAccessDensity; TotalLifetimeAccessDensity += newMIB.TotalLifetimeAccessDensity; MinLifetimeAccessDensity = newMIB.MinLifetimeAccessDensity < MinLifetimeAccessDensity ? newMIB.MinLifetimeAccessDensity : MinLifetimeAccessDensity; MaxLifetimeAccessDensity = newMIB.MaxLifetimeAccessDensity > MaxLifetimeAccessDensity ? newMIB.MaxLifetimeAccessDensity : MaxLifetimeAccessDensity; // We know newMIB was deallocated later, so just need to check if it was // allocated before last one deallocated. NumLifetimeOverlaps += newMIB.AllocTimestamp < DeallocTimestamp; AllocTimestamp = newMIB.AllocTimestamp; DeallocTimestamp = newMIB.DeallocTimestamp; NumSameAllocCpu += AllocCpuId == newMIB.AllocCpuId; NumSameDeallocCpu += DeallocCpuId == newMIB.DeallocCpuId; AllocCpuId = newMIB.AllocCpuId; DeallocCpuId = newMIB.DeallocCpuId; // For merging histograms, we always keep the longer histogram, and add // values of shorter histogram to larger one. uintptr_t ShorterHistogram; uint32_t ShorterHistogramSize; if (newMIB.AccessHistogramSize > AccessHistogramSize) { ShorterHistogram = AccessHistogram; ShorterHistogramSize = AccessHistogramSize; // Swap histogram of current to larger histogram AccessHistogram = newMIB.AccessHistogram; AccessHistogramSize = newMIB.AccessHistogramSize; } else { ShorterHistogram = newMIB.AccessHistogram; ShorterHistogramSize = newMIB.AccessHistogramSize; } for (size_t i = 0; i < ShorterHistogramSize; ++i) { ((uint64_t *)AccessHistogram)[i] += ((uint64_t *)ShorterHistogram)[i]; } } #ifdef _MSC_VER } __pragma(pack(pop)); #else } __attribute__((__packed__)); #endif } // namespace memprof } // namespace llvm #endif