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