1*4824e7fdSDimitry Andric #include <stdint.h> 2*4824e7fdSDimitry Andric #include <stdlib.h> 3*4824e7fdSDimitry Andric #include <string.h> 4*4824e7fdSDimitry Andric 5349cc55cSDimitry Andric #include "memprof_meminfoblock.h" 6*4824e7fdSDimitry Andric #include "memprof_rawprofile.h" 7*4824e7fdSDimitry Andric #include "profile/MemProfData.inc" 8349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_allocator_internal.h" 9*4824e7fdSDimitry Andric #include "sanitizer_common/sanitizer_common.h" 10349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_linux.h" 11349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_procmaps.h" 12349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h" 13349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_stackdepotbase.h" 14349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace.h" 15349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_vector.h" 16349cc55cSDimitry Andric 17349cc55cSDimitry Andric namespace __memprof { 18349cc55cSDimitry Andric using ::__sanitizer::Vector; 19*4824e7fdSDimitry Andric using SegmentEntry = ::llvm::memprof::SegmentEntry; 20*4824e7fdSDimitry Andric using Header = ::llvm::memprof::Header; 21349cc55cSDimitry Andric 22349cc55cSDimitry Andric namespace { 23349cc55cSDimitry Andric template <class T> char *WriteBytes(T Pod, char *&Buffer) { 24349cc55cSDimitry Andric *(T *)Buffer = Pod; 25349cc55cSDimitry Andric return Buffer + sizeof(T); 26349cc55cSDimitry Andric } 27349cc55cSDimitry Andric 28349cc55cSDimitry Andric void RecordStackId(const uptr Key, UNUSED LockedMemInfoBlock *const &MIB, 29349cc55cSDimitry Andric void *Arg) { 30349cc55cSDimitry Andric // No need to touch the MIB value here since we are only recording the key. 31349cc55cSDimitry Andric auto *StackIds = reinterpret_cast<Vector<u64> *>(Arg); 32349cc55cSDimitry Andric StackIds->PushBack(Key); 33349cc55cSDimitry Andric } 34349cc55cSDimitry Andric } // namespace 35349cc55cSDimitry Andric 36349cc55cSDimitry Andric u64 SegmentSizeBytes(MemoryMappingLayoutBase &Layout) { 37349cc55cSDimitry Andric u64 NumSegmentsToRecord = 0; 38349cc55cSDimitry Andric MemoryMappedSegment segment; 39349cc55cSDimitry Andric for (Layout.Reset(); Layout.Next(&segment);) 40349cc55cSDimitry Andric if (segment.IsReadable() && segment.IsExecutable()) 41349cc55cSDimitry Andric NumSegmentsToRecord++; 42349cc55cSDimitry Andric 43349cc55cSDimitry Andric return sizeof(u64) // A header which stores the number of records. 44349cc55cSDimitry Andric + sizeof(SegmentEntry) * NumSegmentsToRecord; 45349cc55cSDimitry Andric } 46349cc55cSDimitry Andric 47349cc55cSDimitry Andric // The segment section uses the following format: 48349cc55cSDimitry Andric // ---------- Segment Info 49349cc55cSDimitry Andric // Num Entries 50349cc55cSDimitry Andric // ---------- Segment Entry 51349cc55cSDimitry Andric // Start 52349cc55cSDimitry Andric // End 53349cc55cSDimitry Andric // Offset 54349cc55cSDimitry Andric // BuildID 32B 55349cc55cSDimitry Andric // ---------- 56349cc55cSDimitry Andric // ... 57349cc55cSDimitry Andric void SerializeSegmentsToBuffer(MemoryMappingLayoutBase &Layout, 58349cc55cSDimitry Andric const u64 ExpectedNumBytes, char *&Buffer) { 59349cc55cSDimitry Andric char *Ptr = Buffer; 60349cc55cSDimitry Andric // Reserve space for the final count. 61349cc55cSDimitry Andric Ptr += sizeof(u64); 62349cc55cSDimitry Andric 63349cc55cSDimitry Andric u64 NumSegmentsRecorded = 0; 64349cc55cSDimitry Andric MemoryMappedSegment segment; 65349cc55cSDimitry Andric 66349cc55cSDimitry Andric for (Layout.Reset(); Layout.Next(&segment);) { 67349cc55cSDimitry Andric if (segment.IsReadable() && segment.IsExecutable()) { 68*4824e7fdSDimitry Andric SegmentEntry Entry{}; 69*4824e7fdSDimitry Andric Entry.Start = segment.start; 70*4824e7fdSDimitry Andric Entry.End = segment.end; 71*4824e7fdSDimitry Andric Entry.Offset = segment.offset; 72*4824e7fdSDimitry Andric memcpy(Entry.BuildId, segment.uuid, sizeof(segment.uuid)); 73*4824e7fdSDimitry Andric memcpy(Ptr, &Entry, sizeof(SegmentEntry)); 74349cc55cSDimitry Andric Ptr += sizeof(SegmentEntry); 75349cc55cSDimitry Andric NumSegmentsRecorded++; 76349cc55cSDimitry Andric } 77349cc55cSDimitry Andric } 78349cc55cSDimitry Andric 79349cc55cSDimitry Andric // Store the number of segments we recorded in the space we reserved. 80349cc55cSDimitry Andric *((u64 *)Buffer) = NumSegmentsRecorded; 81*4824e7fdSDimitry Andric CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) && 82349cc55cSDimitry Andric "Expected num bytes != actual bytes written"); 83349cc55cSDimitry Andric } 84349cc55cSDimitry Andric 85349cc55cSDimitry Andric u64 StackSizeBytes(const Vector<u64> &StackIds) { 86349cc55cSDimitry Andric u64 NumBytesToWrite = sizeof(u64); 87349cc55cSDimitry Andric 88349cc55cSDimitry Andric const u64 NumIds = StackIds.Size(); 89349cc55cSDimitry Andric for (unsigned k = 0; k < NumIds; ++k) { 90349cc55cSDimitry Andric const u64 Id = StackIds[k]; 91349cc55cSDimitry Andric // One entry for the id and then one more for the number of stack pcs. 92349cc55cSDimitry Andric NumBytesToWrite += 2 * sizeof(u64); 93349cc55cSDimitry Andric const StackTrace St = StackDepotGet(Id); 94349cc55cSDimitry Andric 95349cc55cSDimitry Andric CHECK(St.trace != nullptr && St.size > 0 && "Empty stack trace"); 96349cc55cSDimitry Andric for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) { 97349cc55cSDimitry Andric NumBytesToWrite += sizeof(u64); 98349cc55cSDimitry Andric } 99349cc55cSDimitry Andric } 100349cc55cSDimitry Andric return NumBytesToWrite; 101349cc55cSDimitry Andric } 102349cc55cSDimitry Andric 103349cc55cSDimitry Andric // The stack info section uses the following format: 104349cc55cSDimitry Andric // 105349cc55cSDimitry Andric // ---------- Stack Info 106349cc55cSDimitry Andric // Num Entries 107349cc55cSDimitry Andric // ---------- Stack Entry 108349cc55cSDimitry Andric // Num Stacks 109349cc55cSDimitry Andric // PC1 110349cc55cSDimitry Andric // PC2 111349cc55cSDimitry Andric // ... 112349cc55cSDimitry Andric // ---------- 113349cc55cSDimitry Andric void SerializeStackToBuffer(const Vector<u64> &StackIds, 114349cc55cSDimitry Andric const u64 ExpectedNumBytes, char *&Buffer) { 115349cc55cSDimitry Andric const u64 NumIds = StackIds.Size(); 116349cc55cSDimitry Andric char *Ptr = Buffer; 117349cc55cSDimitry Andric Ptr = WriteBytes(static_cast<u64>(NumIds), Ptr); 118349cc55cSDimitry Andric 119349cc55cSDimitry Andric for (unsigned k = 0; k < NumIds; ++k) { 120349cc55cSDimitry Andric const u64 Id = StackIds[k]; 121349cc55cSDimitry Andric Ptr = WriteBytes(Id, Ptr); 122349cc55cSDimitry Andric Ptr += sizeof(u64); // Bump it by u64, we will fill this in later. 123349cc55cSDimitry Andric u64 Count = 0; 124349cc55cSDimitry Andric const StackTrace St = StackDepotGet(Id); 125349cc55cSDimitry Andric for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) { 126349cc55cSDimitry Andric // PCs in stack traces are actually the return addresses, that is, 127349cc55cSDimitry Andric // addresses of the next instructions after the call. 128349cc55cSDimitry Andric uptr pc = StackTrace::GetPreviousInstructionPc(St.trace[i]); 129349cc55cSDimitry Andric Ptr = WriteBytes(static_cast<u64>(pc), Ptr); 130349cc55cSDimitry Andric ++Count; 131349cc55cSDimitry Andric } 132349cc55cSDimitry Andric // Store the count in the space we reserved earlier. 133349cc55cSDimitry Andric *(u64 *)(Ptr - (Count + 1) * sizeof(u64)) = Count; 134349cc55cSDimitry Andric } 135349cc55cSDimitry Andric 136*4824e7fdSDimitry Andric CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) && 137349cc55cSDimitry Andric "Expected num bytes != actual bytes written"); 138349cc55cSDimitry Andric } 139349cc55cSDimitry Andric 140349cc55cSDimitry Andric // The MIB section has the following format: 141349cc55cSDimitry Andric // ---------- MIB Info 142349cc55cSDimitry Andric // Num Entries 143349cc55cSDimitry Andric // ---------- MIB Entry 0 144349cc55cSDimitry Andric // Alloc Count 145349cc55cSDimitry Andric // ... 146349cc55cSDimitry Andric // ---------- MIB Entry 1 147349cc55cSDimitry Andric // Alloc Count 148349cc55cSDimitry Andric // ... 149349cc55cSDimitry Andric // ---------- 150349cc55cSDimitry Andric void SerializeMIBInfoToBuffer(MIBMapTy &MIBMap, const Vector<u64> &StackIds, 151349cc55cSDimitry Andric const u64 ExpectedNumBytes, char *&Buffer) { 152349cc55cSDimitry Andric char *Ptr = Buffer; 153349cc55cSDimitry Andric const u64 NumEntries = StackIds.Size(); 154349cc55cSDimitry Andric Ptr = WriteBytes(NumEntries, Ptr); 155349cc55cSDimitry Andric 156349cc55cSDimitry Andric for (u64 i = 0; i < NumEntries; i++) { 157349cc55cSDimitry Andric const u64 Key = StackIds[i]; 158349cc55cSDimitry Andric MIBMapTy::Handle h(&MIBMap, Key, /*remove=*/true, /*create=*/false); 159349cc55cSDimitry Andric CHECK(h.exists()); 160349cc55cSDimitry Andric Ptr = WriteBytes(Key, Ptr); 161349cc55cSDimitry Andric Ptr = WriteBytes((*h)->mib, Ptr); 162349cc55cSDimitry Andric } 163349cc55cSDimitry Andric 164*4824e7fdSDimitry Andric CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) && 165349cc55cSDimitry Andric "Expected num bytes != actual bytes written"); 166349cc55cSDimitry Andric } 167349cc55cSDimitry Andric 168349cc55cSDimitry Andric // Format 169349cc55cSDimitry Andric // ---------- Header 170349cc55cSDimitry Andric // Magic 171349cc55cSDimitry Andric // Version 172349cc55cSDimitry Andric // Total Size 173349cc55cSDimitry Andric // Segment Offset 174349cc55cSDimitry Andric // MIB Info Offset 175349cc55cSDimitry Andric // Stack Offset 176349cc55cSDimitry Andric // ---------- Segment Info 177349cc55cSDimitry Andric // Num Entries 178349cc55cSDimitry Andric // ---------- Segment Entry 179349cc55cSDimitry Andric // Start 180349cc55cSDimitry Andric // End 181349cc55cSDimitry Andric // Offset 182349cc55cSDimitry Andric // BuildID 32B 183349cc55cSDimitry Andric // ---------- 184349cc55cSDimitry Andric // ... 185*4824e7fdSDimitry Andric // ---------- 186*4824e7fdSDimitry Andric // Optional Padding Bytes 187349cc55cSDimitry Andric // ---------- MIB Info 188349cc55cSDimitry Andric // Num Entries 189349cc55cSDimitry Andric // ---------- MIB Entry 190349cc55cSDimitry Andric // Alloc Count 191349cc55cSDimitry Andric // ... 192*4824e7fdSDimitry Andric // ---------- 193*4824e7fdSDimitry Andric // Optional Padding Bytes 194349cc55cSDimitry Andric // ---------- Stack Info 195349cc55cSDimitry Andric // Num Entries 196349cc55cSDimitry Andric // ---------- Stack Entry 197349cc55cSDimitry Andric // Num Stacks 198349cc55cSDimitry Andric // PC1 199349cc55cSDimitry Andric // PC2 200349cc55cSDimitry Andric // ... 201349cc55cSDimitry Andric // ---------- 202*4824e7fdSDimitry Andric // Optional Padding Bytes 203349cc55cSDimitry Andric // ... 204349cc55cSDimitry Andric u64 SerializeToRawProfile(MIBMapTy &MIBMap, MemoryMappingLayoutBase &Layout, 205349cc55cSDimitry Andric char *&Buffer) { 206*4824e7fdSDimitry Andric // Each section size is rounded up to 8b since the first entry in each section 207*4824e7fdSDimitry Andric // is a u64 which holds the number of entries in the section by convention. 208*4824e7fdSDimitry Andric const u64 NumSegmentBytes = RoundUpTo(SegmentSizeBytes(Layout), 8); 209349cc55cSDimitry Andric 210349cc55cSDimitry Andric Vector<u64> StackIds; 211349cc55cSDimitry Andric MIBMap.ForEach(RecordStackId, reinterpret_cast<void *>(&StackIds)); 212349cc55cSDimitry Andric // The first 8b are for the total number of MIB records. Each MIB record is 213349cc55cSDimitry Andric // preceded by a 8b stack id which is associated with stack frames in the next 214349cc55cSDimitry Andric // section. 215*4824e7fdSDimitry Andric const u64 NumMIBInfoBytes = RoundUpTo( 216*4824e7fdSDimitry Andric sizeof(u64) + StackIds.Size() * (sizeof(u64) + sizeof(MemInfoBlock)), 8); 217349cc55cSDimitry Andric 218*4824e7fdSDimitry Andric const u64 NumStackBytes = RoundUpTo(StackSizeBytes(StackIds), 8); 219349cc55cSDimitry Andric 220*4824e7fdSDimitry Andric // Ensure that the profile is 8b aligned. We allow for some optional padding 221*4824e7fdSDimitry Andric // at the end so that any subsequent profile serialized to the same file does 222*4824e7fdSDimitry Andric // not incur unaligned accesses. 223*4824e7fdSDimitry Andric const u64 TotalSizeBytes = RoundUpTo( 224*4824e7fdSDimitry Andric sizeof(Header) + NumSegmentBytes + NumStackBytes + NumMIBInfoBytes, 8); 225349cc55cSDimitry Andric 226349cc55cSDimitry Andric // Allocate the memory for the entire buffer incl. info blocks. 227349cc55cSDimitry Andric Buffer = (char *)InternalAlloc(TotalSizeBytes); 228349cc55cSDimitry Andric char *Ptr = Buffer; 229349cc55cSDimitry Andric 230349cc55cSDimitry Andric Header header{MEMPROF_RAW_MAGIC_64, 231349cc55cSDimitry Andric MEMPROF_RAW_VERSION, 232349cc55cSDimitry Andric static_cast<u64>(TotalSizeBytes), 233349cc55cSDimitry Andric sizeof(Header), 234349cc55cSDimitry Andric sizeof(Header) + NumSegmentBytes, 235349cc55cSDimitry Andric sizeof(Header) + NumSegmentBytes + NumMIBInfoBytes}; 236349cc55cSDimitry Andric Ptr = WriteBytes(header, Ptr); 237349cc55cSDimitry Andric 238349cc55cSDimitry Andric SerializeSegmentsToBuffer(Layout, NumSegmentBytes, Ptr); 239349cc55cSDimitry Andric Ptr += NumSegmentBytes; 240349cc55cSDimitry Andric 241349cc55cSDimitry Andric SerializeMIBInfoToBuffer(MIBMap, StackIds, NumMIBInfoBytes, Ptr); 242349cc55cSDimitry Andric Ptr += NumMIBInfoBytes; 243349cc55cSDimitry Andric 244349cc55cSDimitry Andric SerializeStackToBuffer(StackIds, NumStackBytes, Ptr); 245349cc55cSDimitry Andric 246349cc55cSDimitry Andric return TotalSizeBytes; 247349cc55cSDimitry Andric } 248349cc55cSDimitry Andric 249349cc55cSDimitry Andric } // namespace __memprof 250