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