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