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