1 #include <stdint.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #include "memprof_meminfoblock.h" 6 #include "memprof_rawprofile.h" 7 #include "profile/MemProfData.inc" 8 #include "sanitizer_common/sanitizer_allocator_internal.h" 9 #include "sanitizer_common/sanitizer_common.h" 10 #include "sanitizer_common/sanitizer_linux.h" 11 #include "sanitizer_common/sanitizer_procmaps.h" 12 #include "sanitizer_common/sanitizer_stackdepot.h" 13 #include "sanitizer_common/sanitizer_stackdepotbase.h" 14 #include "sanitizer_common/sanitizer_stacktrace.h" 15 #include "sanitizer_common/sanitizer_vector.h" 16 17 namespace __memprof { 18 using ::__sanitizer::Vector; 19 using SegmentEntry = ::llvm::memprof::SegmentEntry; 20 using Header = ::llvm::memprof::Header; 21 22 namespace { 23 template <class T> char *WriteBytes(T Pod, char *&Buffer) { 24 *(T *)Buffer = Pod; 25 return Buffer + sizeof(T); 26 } 27 28 void RecordStackId(const uptr Key, UNUSED LockedMemInfoBlock *const &MIB, 29 void *Arg) { 30 // No need to touch the MIB value here since we are only recording the key. 31 auto *StackIds = reinterpret_cast<Vector<u64> *>(Arg); 32 StackIds->PushBack(Key); 33 } 34 } // namespace 35 36 u64 SegmentSizeBytes(MemoryMappingLayoutBase &Layout) { 37 u64 NumSegmentsToRecord = 0; 38 MemoryMappedSegment segment; 39 for (Layout.Reset(); Layout.Next(&segment);) 40 if (segment.IsReadable() && segment.IsExecutable()) 41 NumSegmentsToRecord++; 42 43 return sizeof(u64) // A header which stores the number of records. 44 + sizeof(SegmentEntry) * NumSegmentsToRecord; 45 } 46 47 // The segment section uses the following format: 48 // ---------- Segment Info 49 // Num Entries 50 // ---------- Segment Entry 51 // Start 52 // End 53 // Offset 54 // BuildID 32B 55 // ---------- 56 // ... 57 void SerializeSegmentsToBuffer(MemoryMappingLayoutBase &Layout, 58 const u64 ExpectedNumBytes, char *&Buffer) { 59 char *Ptr = Buffer; 60 // Reserve space for the final count. 61 Ptr += sizeof(u64); 62 63 u64 NumSegmentsRecorded = 0; 64 MemoryMappedSegment segment; 65 66 for (Layout.Reset(); Layout.Next(&segment);) { 67 if (segment.IsReadable() && segment.IsExecutable()) { 68 SegmentEntry Entry{}; 69 Entry.Start = segment.start; 70 Entry.End = segment.end; 71 Entry.Offset = segment.offset; 72 memcpy(Entry.BuildId, segment.uuid, sizeof(segment.uuid)); 73 memcpy(Ptr, &Entry, sizeof(SegmentEntry)); 74 Ptr += sizeof(SegmentEntry); 75 NumSegmentsRecorded++; 76 } 77 } 78 79 // Store the number of segments we recorded in the space we reserved. 80 *((u64 *)Buffer) = NumSegmentsRecorded; 81 CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) && 82 "Expected num bytes != actual bytes written"); 83 } 84 85 u64 StackSizeBytes(const Vector<u64> &StackIds) { 86 u64 NumBytesToWrite = sizeof(u64); 87 88 const u64 NumIds = StackIds.Size(); 89 for (unsigned k = 0; k < NumIds; ++k) { 90 const u64 Id = StackIds[k]; 91 // One entry for the id and then one more for the number of stack pcs. 92 NumBytesToWrite += 2 * sizeof(u64); 93 const StackTrace St = StackDepotGet(Id); 94 95 CHECK(St.trace != nullptr && St.size > 0 && "Empty stack trace"); 96 for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) { 97 NumBytesToWrite += sizeof(u64); 98 } 99 } 100 return NumBytesToWrite; 101 } 102 103 // The stack info section uses the following format: 104 // 105 // ---------- Stack Info 106 // Num Entries 107 // ---------- Stack Entry 108 // Num Stacks 109 // PC1 110 // PC2 111 // ... 112 // ---------- 113 void SerializeStackToBuffer(const Vector<u64> &StackIds, 114 const u64 ExpectedNumBytes, char *&Buffer) { 115 const u64 NumIds = StackIds.Size(); 116 char *Ptr = Buffer; 117 Ptr = WriteBytes(static_cast<u64>(NumIds), Ptr); 118 119 for (unsigned k = 0; k < NumIds; ++k) { 120 const u64 Id = StackIds[k]; 121 Ptr = WriteBytes(Id, Ptr); 122 Ptr += sizeof(u64); // Bump it by u64, we will fill this in later. 123 u64 Count = 0; 124 const StackTrace St = StackDepotGet(Id); 125 for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) { 126 // PCs in stack traces are actually the return addresses, that is, 127 // addresses of the next instructions after the call. 128 uptr pc = StackTrace::GetPreviousInstructionPc(St.trace[i]); 129 Ptr = WriteBytes(static_cast<u64>(pc), Ptr); 130 ++Count; 131 } 132 // Store the count in the space we reserved earlier. 133 *(u64 *)(Ptr - (Count + 1) * sizeof(u64)) = Count; 134 } 135 136 CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) && 137 "Expected num bytes != actual bytes written"); 138 } 139 140 // The MIB section has the following format: 141 // ---------- MIB Info 142 // Num Entries 143 // ---------- MIB Entry 0 144 // Alloc Count 145 // ... 146 // ---------- MIB Entry 1 147 // Alloc Count 148 // ... 149 // ---------- 150 void SerializeMIBInfoToBuffer(MIBMapTy &MIBMap, const Vector<u64> &StackIds, 151 const u64 ExpectedNumBytes, char *&Buffer) { 152 char *Ptr = Buffer; 153 const u64 NumEntries = StackIds.Size(); 154 Ptr = WriteBytes(NumEntries, Ptr); 155 156 for (u64 i = 0; i < NumEntries; i++) { 157 const u64 Key = StackIds[i]; 158 MIBMapTy::Handle h(&MIBMap, Key, /*remove=*/true, /*create=*/false); 159 CHECK(h.exists()); 160 Ptr = WriteBytes(Key, Ptr); 161 Ptr = WriteBytes((*h)->mib, Ptr); 162 } 163 164 CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) && 165 "Expected num bytes != actual bytes written"); 166 } 167 168 // Format 169 // ---------- Header 170 // Magic 171 // Version 172 // Total Size 173 // Segment Offset 174 // MIB Info Offset 175 // Stack Offset 176 // ---------- Segment Info 177 // Num Entries 178 // ---------- Segment Entry 179 // Start 180 // End 181 // Offset 182 // BuildID 32B 183 // ---------- 184 // ... 185 // ---------- 186 // Optional Padding Bytes 187 // ---------- MIB Info 188 // Num Entries 189 // ---------- MIB Entry 190 // Alloc Count 191 // ... 192 // ---------- 193 // Optional Padding Bytes 194 // ---------- Stack Info 195 // Num Entries 196 // ---------- Stack Entry 197 // Num Stacks 198 // PC1 199 // PC2 200 // ... 201 // ---------- 202 // Optional Padding Bytes 203 // ... 204 u64 SerializeToRawProfile(MIBMapTy &MIBMap, MemoryMappingLayoutBase &Layout, 205 char *&Buffer) { 206 // Each section size is rounded up to 8b since the first entry in each section 207 // is a u64 which holds the number of entries in the section by convention. 208 const u64 NumSegmentBytes = RoundUpTo(SegmentSizeBytes(Layout), 8); 209 210 Vector<u64> StackIds; 211 MIBMap.ForEach(RecordStackId, reinterpret_cast<void *>(&StackIds)); 212 // The first 8b are for the total number of MIB records. Each MIB record is 213 // preceded by a 8b stack id which is associated with stack frames in the next 214 // section. 215 const u64 NumMIBInfoBytes = RoundUpTo( 216 sizeof(u64) + StackIds.Size() * (sizeof(u64) + sizeof(MemInfoBlock)), 8); 217 218 const u64 NumStackBytes = RoundUpTo(StackSizeBytes(StackIds), 8); 219 220 // Ensure that the profile is 8b aligned. We allow for some optional padding 221 // at the end so that any subsequent profile serialized to the same file does 222 // not incur unaligned accesses. 223 const u64 TotalSizeBytes = RoundUpTo( 224 sizeof(Header) + NumSegmentBytes + NumStackBytes + NumMIBInfoBytes, 8); 225 226 // Allocate the memory for the entire buffer incl. info blocks. 227 Buffer = (char *)InternalAlloc(TotalSizeBytes); 228 char *Ptr = Buffer; 229 230 Header header{MEMPROF_RAW_MAGIC_64, 231 MEMPROF_RAW_VERSION, 232 static_cast<u64>(TotalSizeBytes), 233 sizeof(Header), 234 sizeof(Header) + NumSegmentBytes, 235 sizeof(Header) + NumSegmentBytes + NumMIBInfoBytes}; 236 Ptr = WriteBytes(header, Ptr); 237 238 SerializeSegmentsToBuffer(Layout, NumSegmentBytes, Ptr); 239 Ptr += NumSegmentBytes; 240 241 SerializeMIBInfoToBuffer(MIBMap, StackIds, NumMIBInfoBytes, Ptr); 242 Ptr += NumMIBInfoBytes; 243 244 SerializeStackToBuffer(StackIds, NumStackBytes, Ptr); 245 246 return TotalSizeBytes; 247 } 248 249 } // namespace __memprof 250