14824e7fdSDimitry Andric #include <stdint.h> 24824e7fdSDimitry Andric #include <stdlib.h> 34824e7fdSDimitry Andric #include <string.h> 44824e7fdSDimitry Andric 54824e7fdSDimitry Andric #include "memprof_rawprofile.h" 64824e7fdSDimitry Andric #include "profile/MemProfData.inc" 7349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_allocator_internal.h" 806c3fb27SDimitry Andric #include "sanitizer_common/sanitizer_array_ref.h" 94824e7fdSDimitry 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; 191fd87a68SDimitry Andric using ::llvm::memprof::MemInfoBlock; 204824e7fdSDimitry Andric using SegmentEntry = ::llvm::memprof::SegmentEntry; 214824e7fdSDimitry Andric using Header = ::llvm::memprof::Header; 22349cc55cSDimitry Andric 23349cc55cSDimitry Andric namespace { 2406c3fb27SDimitry Andric template <class T> char *WriteBytes(const T &Pod, char *Buffer) { 25349cc55cSDimitry Andric *(T *)Buffer = Pod; 26349cc55cSDimitry Andric return Buffer + sizeof(T); 27349cc55cSDimitry Andric } 28349cc55cSDimitry Andric 29349cc55cSDimitry Andric void RecordStackId(const uptr Key, UNUSED LockedMemInfoBlock *const &MIB, 30349cc55cSDimitry Andric void *Arg) { 31349cc55cSDimitry Andric // No need to touch the MIB value here since we are only recording the key. 32349cc55cSDimitry Andric auto *StackIds = reinterpret_cast<Vector<u64> *>(Arg); 33349cc55cSDimitry Andric StackIds->PushBack(Key); 34349cc55cSDimitry Andric } 35349cc55cSDimitry Andric } // namespace 36349cc55cSDimitry Andric 3706c3fb27SDimitry Andric u64 SegmentSizeBytes(ArrayRef<LoadedModule> Modules) { 38349cc55cSDimitry Andric u64 NumSegmentsToRecord = 0; 3906c3fb27SDimitry Andric for (const auto &Module : Modules) { 4006c3fb27SDimitry Andric for (const auto &Segment : Module.ranges()) { 4106c3fb27SDimitry Andric if (Segment.executable) 42349cc55cSDimitry Andric NumSegmentsToRecord++; 4306c3fb27SDimitry Andric } 4406c3fb27SDimitry Andric } 45349cc55cSDimitry Andric 46349cc55cSDimitry Andric return sizeof(u64) // A header which stores the number of records. 47349cc55cSDimitry Andric + sizeof(SegmentEntry) * NumSegmentsToRecord; 48349cc55cSDimitry Andric } 49349cc55cSDimitry Andric 50349cc55cSDimitry Andric // The segment section uses the following format: 51349cc55cSDimitry Andric // ---------- Segment Info 52349cc55cSDimitry Andric // Num Entries 53349cc55cSDimitry Andric // ---------- Segment Entry 54349cc55cSDimitry Andric // Start 55349cc55cSDimitry Andric // End 56349cc55cSDimitry Andric // Offset 5706c3fb27SDimitry Andric // UuidSize 5806c3fb27SDimitry Andric // Uuid 32B 59349cc55cSDimitry Andric // ---------- 60349cc55cSDimitry Andric // ... 6106c3fb27SDimitry Andric void SerializeSegmentsToBuffer(ArrayRef<LoadedModule> Modules, 62349cc55cSDimitry Andric const u64 ExpectedNumBytes, char *&Buffer) { 63349cc55cSDimitry Andric char *Ptr = Buffer; 64349cc55cSDimitry Andric // Reserve space for the final count. 65349cc55cSDimitry Andric Ptr += sizeof(u64); 66349cc55cSDimitry Andric 67349cc55cSDimitry Andric u64 NumSegmentsRecorded = 0; 68349cc55cSDimitry Andric 6906c3fb27SDimitry Andric for (const auto &Module : Modules) { 7006c3fb27SDimitry Andric for (const auto &Segment : Module.ranges()) { 7106c3fb27SDimitry Andric if (Segment.executable) { 7206c3fb27SDimitry Andric SegmentEntry Entry(Segment.beg, Segment.end, Module.base_address()); 7306c3fb27SDimitry Andric CHECK(Module.uuid_size() <= MEMPROF_BUILDID_MAX_SIZE); 7406c3fb27SDimitry Andric Entry.BuildIdSize = Module.uuid_size(); 7506c3fb27SDimitry Andric memcpy(Entry.BuildId, Module.uuid(), Module.uuid_size()); 764824e7fdSDimitry Andric memcpy(Ptr, &Entry, sizeof(SegmentEntry)); 77349cc55cSDimitry Andric Ptr += sizeof(SegmentEntry); 78349cc55cSDimitry Andric NumSegmentsRecorded++; 79349cc55cSDimitry Andric } 80349cc55cSDimitry Andric } 8106c3fb27SDimitry Andric } 82349cc55cSDimitry Andric // Store the number of segments we recorded in the space we reserved. 83349cc55cSDimitry Andric *((u64 *)Buffer) = NumSegmentsRecorded; 844824e7fdSDimitry Andric CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) && 85349cc55cSDimitry Andric "Expected num bytes != actual bytes written"); 86349cc55cSDimitry Andric } 87349cc55cSDimitry Andric 88349cc55cSDimitry Andric u64 StackSizeBytes(const Vector<u64> &StackIds) { 89349cc55cSDimitry Andric u64 NumBytesToWrite = sizeof(u64); 90349cc55cSDimitry Andric 91349cc55cSDimitry Andric const u64 NumIds = StackIds.Size(); 92349cc55cSDimitry Andric for (unsigned k = 0; k < NumIds; ++k) { 93349cc55cSDimitry Andric const u64 Id = StackIds[k]; 94349cc55cSDimitry Andric // One entry for the id and then one more for the number of stack pcs. 95349cc55cSDimitry Andric NumBytesToWrite += 2 * sizeof(u64); 96349cc55cSDimitry Andric const StackTrace St = StackDepotGet(Id); 97349cc55cSDimitry Andric 98349cc55cSDimitry Andric CHECK(St.trace != nullptr && St.size > 0 && "Empty stack trace"); 99349cc55cSDimitry Andric for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) { 100349cc55cSDimitry Andric NumBytesToWrite += sizeof(u64); 101349cc55cSDimitry Andric } 102349cc55cSDimitry Andric } 103349cc55cSDimitry Andric return NumBytesToWrite; 104349cc55cSDimitry Andric } 105349cc55cSDimitry Andric 106349cc55cSDimitry Andric // The stack info section uses the following format: 107349cc55cSDimitry Andric // 108349cc55cSDimitry Andric // ---------- Stack Info 109349cc55cSDimitry Andric // Num Entries 110349cc55cSDimitry Andric // ---------- Stack Entry 111349cc55cSDimitry Andric // Num Stacks 112349cc55cSDimitry Andric // PC1 113349cc55cSDimitry Andric // PC2 114349cc55cSDimitry Andric // ... 115349cc55cSDimitry Andric // ---------- 116349cc55cSDimitry Andric void SerializeStackToBuffer(const Vector<u64> &StackIds, 117349cc55cSDimitry Andric const u64 ExpectedNumBytes, char *&Buffer) { 118349cc55cSDimitry Andric const u64 NumIds = StackIds.Size(); 119349cc55cSDimitry Andric char *Ptr = Buffer; 120349cc55cSDimitry Andric Ptr = WriteBytes(static_cast<u64>(NumIds), Ptr); 121349cc55cSDimitry Andric 122349cc55cSDimitry Andric for (unsigned k = 0; k < NumIds; ++k) { 123349cc55cSDimitry Andric const u64 Id = StackIds[k]; 124349cc55cSDimitry Andric Ptr = WriteBytes(Id, Ptr); 125349cc55cSDimitry Andric Ptr += sizeof(u64); // Bump it by u64, we will fill this in later. 126349cc55cSDimitry Andric u64 Count = 0; 127349cc55cSDimitry Andric const StackTrace St = StackDepotGet(Id); 128349cc55cSDimitry Andric for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) { 129349cc55cSDimitry Andric // PCs in stack traces are actually the return addresses, that is, 130349cc55cSDimitry Andric // addresses of the next instructions after the call. 131349cc55cSDimitry Andric uptr pc = StackTrace::GetPreviousInstructionPc(St.trace[i]); 132349cc55cSDimitry Andric Ptr = WriteBytes(static_cast<u64>(pc), Ptr); 133349cc55cSDimitry Andric ++Count; 134349cc55cSDimitry Andric } 135349cc55cSDimitry Andric // Store the count in the space we reserved earlier. 136349cc55cSDimitry Andric *(u64 *)(Ptr - (Count + 1) * sizeof(u64)) = Count; 137349cc55cSDimitry Andric } 138349cc55cSDimitry Andric 1394824e7fdSDimitry Andric CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) && 140349cc55cSDimitry Andric "Expected num bytes != actual bytes written"); 141349cc55cSDimitry Andric } 142349cc55cSDimitry Andric 143349cc55cSDimitry Andric // The MIB section has the following format: 144349cc55cSDimitry Andric // ---------- MIB Info 145349cc55cSDimitry Andric // Num Entries 146349cc55cSDimitry Andric // ---------- MIB Entry 0 147349cc55cSDimitry Andric // Alloc Count 148349cc55cSDimitry Andric // ... 149*0fca6ea1SDimitry Andric // ---- AccessHistogram Entry 0 150*0fca6ea1SDimitry Andric // ... 151*0fca6ea1SDimitry Andric // ---- AccessHistogram Entry AccessHistogramSize - 1 152349cc55cSDimitry Andric // ---------- MIB Entry 1 153349cc55cSDimitry Andric // Alloc Count 154349cc55cSDimitry Andric // ... 155*0fca6ea1SDimitry Andric // ---- AccessHistogram Entry 0 156*0fca6ea1SDimitry Andric // ... 157*0fca6ea1SDimitry Andric // ---- AccessHistogram Entry AccessHistogramSize - 1 158349cc55cSDimitry Andric // ---------- 159349cc55cSDimitry Andric void SerializeMIBInfoToBuffer(MIBMapTy &MIBMap, const Vector<u64> &StackIds, 160349cc55cSDimitry Andric const u64 ExpectedNumBytes, char *&Buffer) { 161349cc55cSDimitry Andric char *Ptr = Buffer; 162349cc55cSDimitry Andric const u64 NumEntries = StackIds.Size(); 163349cc55cSDimitry Andric Ptr = WriteBytes(NumEntries, Ptr); 164349cc55cSDimitry Andric for (u64 i = 0; i < NumEntries; i++) { 165349cc55cSDimitry Andric const u64 Key = StackIds[i]; 166349cc55cSDimitry Andric MIBMapTy::Handle h(&MIBMap, Key, /*remove=*/true, /*create=*/false); 167349cc55cSDimitry Andric CHECK(h.exists()); 168349cc55cSDimitry Andric Ptr = WriteBytes(Key, Ptr); 169*0fca6ea1SDimitry Andric // FIXME: We unnecessarily serialize the AccessHistogram pointer. Adding a 170*0fca6ea1SDimitry Andric // serialization schema will fix this issue. See also FIXME in 171*0fca6ea1SDimitry Andric // deserialization. 172349cc55cSDimitry Andric Ptr = WriteBytes((*h)->mib, Ptr); 173*0fca6ea1SDimitry Andric for (u64 j = 0; j < (*h)->mib.AccessHistogramSize; ++j) { 174*0fca6ea1SDimitry Andric u64 HistogramEntry = ((u64 *)((*h)->mib.AccessHistogram))[j]; 175*0fca6ea1SDimitry Andric Ptr = WriteBytes(HistogramEntry, Ptr); 176349cc55cSDimitry Andric } 177*0fca6ea1SDimitry Andric if ((*h)->mib.AccessHistogramSize > 0) { 178*0fca6ea1SDimitry Andric InternalFree((void *)((*h)->mib.AccessHistogram)); 179*0fca6ea1SDimitry Andric } 180*0fca6ea1SDimitry Andric } 1814824e7fdSDimitry Andric CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) && 182349cc55cSDimitry Andric "Expected num bytes != actual bytes written"); 183349cc55cSDimitry Andric } 184349cc55cSDimitry Andric 185349cc55cSDimitry Andric // Format 186349cc55cSDimitry Andric // ---------- Header 187349cc55cSDimitry Andric // Magic 188349cc55cSDimitry Andric // Version 189349cc55cSDimitry Andric // Total Size 190349cc55cSDimitry Andric // Segment Offset 191349cc55cSDimitry Andric // MIB Info Offset 192349cc55cSDimitry Andric // Stack Offset 193349cc55cSDimitry Andric // ---------- Segment Info 194349cc55cSDimitry Andric // Num Entries 195349cc55cSDimitry Andric // ---------- Segment Entry 196349cc55cSDimitry Andric // Start 197349cc55cSDimitry Andric // End 198349cc55cSDimitry Andric // Offset 199349cc55cSDimitry Andric // BuildID 32B 200349cc55cSDimitry Andric // ---------- 201349cc55cSDimitry Andric // ... 2024824e7fdSDimitry Andric // ---------- 2034824e7fdSDimitry Andric // Optional Padding Bytes 204349cc55cSDimitry Andric // ---------- MIB Info 205349cc55cSDimitry Andric // Num Entries 206349cc55cSDimitry Andric // ---------- MIB Entry 207349cc55cSDimitry Andric // Alloc Count 208349cc55cSDimitry Andric // ... 209*0fca6ea1SDimitry Andric // ---- AccessHistogram Entry 0 210*0fca6ea1SDimitry Andric // ... 211*0fca6ea1SDimitry Andric // ---- AccessHistogram Entry AccessHistogramSize - 1 212*0fca6ea1SDimitry Andric // ---------- MIB Entry 1 213*0fca6ea1SDimitry Andric // Alloc Count 214*0fca6ea1SDimitry Andric // ... 215*0fca6ea1SDimitry Andric // ---- AccessHistogram Entry 0 216*0fca6ea1SDimitry Andric // ... 217*0fca6ea1SDimitry Andric // ---- AccessHistogram Entry AccessHistogramSize - 1 2184824e7fdSDimitry Andric // Optional Padding Bytes 219349cc55cSDimitry Andric // ---------- Stack Info 220349cc55cSDimitry Andric // Num Entries 221349cc55cSDimitry Andric // ---------- Stack Entry 222349cc55cSDimitry Andric // Num Stacks 223349cc55cSDimitry Andric // PC1 224349cc55cSDimitry Andric // PC2 225349cc55cSDimitry Andric // ... 226349cc55cSDimitry Andric // ---------- 2274824e7fdSDimitry Andric // Optional Padding Bytes 228349cc55cSDimitry Andric // ... 22906c3fb27SDimitry Andric u64 SerializeToRawProfile(MIBMapTy &MIBMap, ArrayRef<LoadedModule> Modules, 230349cc55cSDimitry Andric char *&Buffer) { 2314824e7fdSDimitry Andric // Each section size is rounded up to 8b since the first entry in each section 2324824e7fdSDimitry Andric // is a u64 which holds the number of entries in the section by convention. 23306c3fb27SDimitry Andric const u64 NumSegmentBytes = RoundUpTo(SegmentSizeBytes(Modules), 8); 234349cc55cSDimitry Andric 235349cc55cSDimitry Andric Vector<u64> StackIds; 236349cc55cSDimitry Andric MIBMap.ForEach(RecordStackId, reinterpret_cast<void *>(&StackIds)); 237349cc55cSDimitry Andric // The first 8b are for the total number of MIB records. Each MIB record is 238349cc55cSDimitry Andric // preceded by a 8b stack id which is associated with stack frames in the next 239349cc55cSDimitry Andric // section. 2404824e7fdSDimitry Andric const u64 NumMIBInfoBytes = RoundUpTo( 2414824e7fdSDimitry Andric sizeof(u64) + StackIds.Size() * (sizeof(u64) + sizeof(MemInfoBlock)), 8); 242349cc55cSDimitry Andric 243*0fca6ea1SDimitry Andric // Get Number of AccessHistogram entries in total 244*0fca6ea1SDimitry Andric u64 TotalAccessHistogramEntries = 0; 245*0fca6ea1SDimitry Andric MIBMap.ForEach( 246*0fca6ea1SDimitry Andric [](const uptr Key, UNUSED LockedMemInfoBlock *const &MIB, void *Arg) { 247*0fca6ea1SDimitry Andric u64 *TotalAccessHistogramEntries = (u64 *)Arg; 248*0fca6ea1SDimitry Andric *TotalAccessHistogramEntries += MIB->mib.AccessHistogramSize; 249*0fca6ea1SDimitry Andric }, 250*0fca6ea1SDimitry Andric reinterpret_cast<void *>(&TotalAccessHistogramEntries)); 251*0fca6ea1SDimitry Andric const u64 NumHistogramBytes = 252*0fca6ea1SDimitry Andric RoundUpTo(TotalAccessHistogramEntries * sizeof(uint64_t), 8); 253*0fca6ea1SDimitry Andric 2544824e7fdSDimitry Andric const u64 NumStackBytes = RoundUpTo(StackSizeBytes(StackIds), 8); 255349cc55cSDimitry Andric 2564824e7fdSDimitry Andric // Ensure that the profile is 8b aligned. We allow for some optional padding 2574824e7fdSDimitry Andric // at the end so that any subsequent profile serialized to the same file does 2584824e7fdSDimitry Andric // not incur unaligned accesses. 259*0fca6ea1SDimitry Andric const u64 TotalSizeBytes = 260*0fca6ea1SDimitry Andric RoundUpTo(sizeof(Header) + NumSegmentBytes + NumStackBytes + 261*0fca6ea1SDimitry Andric NumMIBInfoBytes + NumHistogramBytes, 262*0fca6ea1SDimitry Andric 8); 263349cc55cSDimitry Andric 264349cc55cSDimitry Andric // Allocate the memory for the entire buffer incl. info blocks. 265349cc55cSDimitry Andric Buffer = (char *)InternalAlloc(TotalSizeBytes); 266349cc55cSDimitry Andric char *Ptr = Buffer; 267349cc55cSDimitry Andric 268349cc55cSDimitry Andric Header header{MEMPROF_RAW_MAGIC_64, 269349cc55cSDimitry Andric MEMPROF_RAW_VERSION, 270349cc55cSDimitry Andric static_cast<u64>(TotalSizeBytes), 271349cc55cSDimitry Andric sizeof(Header), 272349cc55cSDimitry Andric sizeof(Header) + NumSegmentBytes, 273*0fca6ea1SDimitry Andric sizeof(Header) + NumSegmentBytes + NumMIBInfoBytes + 274*0fca6ea1SDimitry Andric NumHistogramBytes}; 275349cc55cSDimitry Andric Ptr = WriteBytes(header, Ptr); 276349cc55cSDimitry Andric 27706c3fb27SDimitry Andric SerializeSegmentsToBuffer(Modules, NumSegmentBytes, Ptr); 278349cc55cSDimitry Andric Ptr += NumSegmentBytes; 279349cc55cSDimitry Andric 280*0fca6ea1SDimitry Andric SerializeMIBInfoToBuffer(MIBMap, StackIds, 281*0fca6ea1SDimitry Andric NumMIBInfoBytes + NumHistogramBytes, Ptr); 282*0fca6ea1SDimitry Andric Ptr += NumMIBInfoBytes + NumHistogramBytes; 283349cc55cSDimitry Andric 284349cc55cSDimitry Andric SerializeStackToBuffer(StackIds, NumStackBytes, Ptr); 285349cc55cSDimitry Andric 286349cc55cSDimitry Andric return TotalSizeBytes; 287349cc55cSDimitry Andric } 288349cc55cSDimitry Andric 289349cc55cSDimitry Andric } // namespace __memprof 290