xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/memprof/memprof_rawprofile.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
14824e7fdSDimitry Andric #include <stdint.h>
24824e7fdSDimitry Andric #include <stdlib.h>
34824e7fdSDimitry Andric #include <string.h>
44824e7fdSDimitry Andric 
54824e7fdSDimitry Andric #include "memprof_rawprofile.h"
64824e7fdSDimitry Andric #include "profile/MemProfData.inc"
7349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_allocator_internal.h"
806c3fb27SDimitry Andric #include "sanitizer_common/sanitizer_array_ref.h"
94824e7fdSDimitry Andric #include "sanitizer_common/sanitizer_common.h"
10349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_linux.h"
11349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_procmaps.h"
12349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h"
13349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_stackdepotbase.h"
14349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace.h"
15349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_vector.h"
16349cc55cSDimitry Andric 
17349cc55cSDimitry Andric namespace __memprof {
18349cc55cSDimitry Andric using ::__sanitizer::Vector;
191fd87a68SDimitry Andric using ::llvm::memprof::MemInfoBlock;
204824e7fdSDimitry Andric using SegmentEntry = ::llvm::memprof::SegmentEntry;
214824e7fdSDimitry Andric using Header = ::llvm::memprof::Header;
22349cc55cSDimitry Andric 
23349cc55cSDimitry Andric namespace {
2406c3fb27SDimitry Andric template <class T> char *WriteBytes(const T &Pod, char *Buffer) {
25349cc55cSDimitry Andric   *(T *)Buffer = Pod;
26349cc55cSDimitry Andric   return Buffer + sizeof(T);
27349cc55cSDimitry Andric }
28349cc55cSDimitry Andric 
29349cc55cSDimitry Andric void RecordStackId(const uptr Key, UNUSED LockedMemInfoBlock *const &MIB,
30349cc55cSDimitry Andric                    void *Arg) {
31349cc55cSDimitry Andric   // No need to touch the MIB value here since we are only recording the key.
32349cc55cSDimitry Andric   auto *StackIds = reinterpret_cast<Vector<u64> *>(Arg);
33349cc55cSDimitry Andric   StackIds->PushBack(Key);
34349cc55cSDimitry Andric }
35349cc55cSDimitry Andric } // namespace
36349cc55cSDimitry Andric 
3706c3fb27SDimitry Andric u64 SegmentSizeBytes(ArrayRef<LoadedModule> Modules) {
38349cc55cSDimitry Andric   u64 NumSegmentsToRecord = 0;
3906c3fb27SDimitry Andric   for (const auto &Module : Modules) {
4006c3fb27SDimitry Andric     for (const auto &Segment : Module.ranges()) {
4106c3fb27SDimitry Andric       if (Segment.executable)
42349cc55cSDimitry Andric         NumSegmentsToRecord++;
4306c3fb27SDimitry Andric     }
4406c3fb27SDimitry Andric   }
45349cc55cSDimitry Andric 
46349cc55cSDimitry Andric   return sizeof(u64) // A header which stores the number of records.
47349cc55cSDimitry Andric          + sizeof(SegmentEntry) * NumSegmentsToRecord;
48349cc55cSDimitry Andric }
49349cc55cSDimitry Andric 
50349cc55cSDimitry Andric // The segment section uses the following format:
51349cc55cSDimitry Andric // ---------- Segment Info
52349cc55cSDimitry Andric // Num Entries
53349cc55cSDimitry Andric // ---------- Segment Entry
54349cc55cSDimitry Andric // Start
55349cc55cSDimitry Andric // End
56349cc55cSDimitry Andric // Offset
5706c3fb27SDimitry Andric // UuidSize
5806c3fb27SDimitry Andric // Uuid 32B
59349cc55cSDimitry Andric // ----------
60349cc55cSDimitry Andric // ...
6106c3fb27SDimitry Andric void SerializeSegmentsToBuffer(ArrayRef<LoadedModule> Modules,
62349cc55cSDimitry Andric                                const u64 ExpectedNumBytes, char *&Buffer) {
63349cc55cSDimitry Andric   char *Ptr = Buffer;
64349cc55cSDimitry Andric   // Reserve space for the final count.
65349cc55cSDimitry Andric   Ptr += sizeof(u64);
66349cc55cSDimitry Andric 
67349cc55cSDimitry Andric   u64 NumSegmentsRecorded = 0;
68349cc55cSDimitry Andric 
6906c3fb27SDimitry Andric   for (const auto &Module : Modules) {
7006c3fb27SDimitry Andric     for (const auto &Segment : Module.ranges()) {
7106c3fb27SDimitry Andric       if (Segment.executable) {
7206c3fb27SDimitry Andric         SegmentEntry Entry(Segment.beg, Segment.end, Module.base_address());
7306c3fb27SDimitry Andric         CHECK(Module.uuid_size() <= MEMPROF_BUILDID_MAX_SIZE);
7406c3fb27SDimitry Andric         Entry.BuildIdSize = Module.uuid_size();
7506c3fb27SDimitry Andric         memcpy(Entry.BuildId, Module.uuid(), Module.uuid_size());
764824e7fdSDimitry Andric         memcpy(Ptr, &Entry, sizeof(SegmentEntry));
77349cc55cSDimitry Andric         Ptr += sizeof(SegmentEntry);
78349cc55cSDimitry Andric         NumSegmentsRecorded++;
79349cc55cSDimitry Andric       }
80349cc55cSDimitry Andric     }
8106c3fb27SDimitry Andric   }
82349cc55cSDimitry Andric   // Store the number of segments we recorded in the space we reserved.
83349cc55cSDimitry Andric   *((u64 *)Buffer) = NumSegmentsRecorded;
844824e7fdSDimitry Andric   CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&
85349cc55cSDimitry Andric         "Expected num bytes != actual bytes written");
86349cc55cSDimitry Andric }
87349cc55cSDimitry Andric 
88349cc55cSDimitry Andric u64 StackSizeBytes(const Vector<u64> &StackIds) {
89349cc55cSDimitry Andric   u64 NumBytesToWrite = sizeof(u64);
90349cc55cSDimitry Andric 
91349cc55cSDimitry Andric   const u64 NumIds = StackIds.Size();
92349cc55cSDimitry Andric   for (unsigned k = 0; k < NumIds; ++k) {
93349cc55cSDimitry Andric     const u64 Id = StackIds[k];
94349cc55cSDimitry Andric     // One entry for the id and then one more for the number of stack pcs.
95349cc55cSDimitry Andric     NumBytesToWrite += 2 * sizeof(u64);
96349cc55cSDimitry Andric     const StackTrace St = StackDepotGet(Id);
97349cc55cSDimitry Andric 
98349cc55cSDimitry Andric     CHECK(St.trace != nullptr && St.size > 0 && "Empty stack trace");
99349cc55cSDimitry Andric     for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) {
100349cc55cSDimitry Andric       NumBytesToWrite += sizeof(u64);
101349cc55cSDimitry Andric     }
102349cc55cSDimitry Andric   }
103349cc55cSDimitry Andric   return NumBytesToWrite;
104349cc55cSDimitry Andric }
105349cc55cSDimitry Andric 
106349cc55cSDimitry Andric // The stack info section uses the following format:
107349cc55cSDimitry Andric //
108349cc55cSDimitry Andric // ---------- Stack Info
109349cc55cSDimitry Andric // Num Entries
110349cc55cSDimitry Andric // ---------- Stack Entry
111349cc55cSDimitry Andric // Num Stacks
112349cc55cSDimitry Andric // PC1
113349cc55cSDimitry Andric // PC2
114349cc55cSDimitry Andric // ...
115349cc55cSDimitry Andric // ----------
116349cc55cSDimitry Andric void SerializeStackToBuffer(const Vector<u64> &StackIds,
117349cc55cSDimitry Andric                             const u64 ExpectedNumBytes, char *&Buffer) {
118349cc55cSDimitry Andric   const u64 NumIds = StackIds.Size();
119349cc55cSDimitry Andric   char *Ptr = Buffer;
120349cc55cSDimitry Andric   Ptr = WriteBytes(static_cast<u64>(NumIds), Ptr);
121349cc55cSDimitry Andric 
122349cc55cSDimitry Andric   for (unsigned k = 0; k < NumIds; ++k) {
123349cc55cSDimitry Andric     const u64 Id = StackIds[k];
124349cc55cSDimitry Andric     Ptr = WriteBytes(Id, Ptr);
125349cc55cSDimitry Andric     Ptr += sizeof(u64); // Bump it by u64, we will fill this in later.
126349cc55cSDimitry Andric     u64 Count = 0;
127349cc55cSDimitry Andric     const StackTrace St = StackDepotGet(Id);
128349cc55cSDimitry Andric     for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) {
129349cc55cSDimitry Andric       // PCs in stack traces are actually the return addresses, that is,
130349cc55cSDimitry Andric       // addresses of the next instructions after the call.
131349cc55cSDimitry Andric       uptr pc = StackTrace::GetPreviousInstructionPc(St.trace[i]);
132349cc55cSDimitry Andric       Ptr = WriteBytes(static_cast<u64>(pc), Ptr);
133349cc55cSDimitry Andric       ++Count;
134349cc55cSDimitry Andric     }
135349cc55cSDimitry Andric     // Store the count in the space we reserved earlier.
136349cc55cSDimitry Andric     *(u64 *)(Ptr - (Count + 1) * sizeof(u64)) = Count;
137349cc55cSDimitry Andric   }
138349cc55cSDimitry Andric 
1394824e7fdSDimitry Andric   CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&
140349cc55cSDimitry Andric         "Expected num bytes != actual bytes written");
141349cc55cSDimitry Andric }
142349cc55cSDimitry Andric 
143349cc55cSDimitry Andric // The MIB section has the following format:
144349cc55cSDimitry Andric // ---------- MIB Info
145349cc55cSDimitry Andric // Num Entries
146349cc55cSDimitry Andric // ---------- MIB Entry 0
147349cc55cSDimitry Andric // Alloc Count
148349cc55cSDimitry Andric // ...
149*0fca6ea1SDimitry Andric //       ---- AccessHistogram Entry 0
150*0fca6ea1SDimitry Andric //            ...
151*0fca6ea1SDimitry Andric //       ---- AccessHistogram Entry AccessHistogramSize - 1
152349cc55cSDimitry Andric // ---------- MIB Entry 1
153349cc55cSDimitry Andric // Alloc Count
154349cc55cSDimitry Andric // ...
155*0fca6ea1SDimitry Andric //       ---- AccessHistogram Entry 0
156*0fca6ea1SDimitry Andric //            ...
157*0fca6ea1SDimitry Andric //       ---- AccessHistogram Entry AccessHistogramSize - 1
158349cc55cSDimitry Andric // ----------
159349cc55cSDimitry Andric void SerializeMIBInfoToBuffer(MIBMapTy &MIBMap, const Vector<u64> &StackIds,
160349cc55cSDimitry Andric                               const u64 ExpectedNumBytes, char *&Buffer) {
161349cc55cSDimitry Andric   char *Ptr = Buffer;
162349cc55cSDimitry Andric   const u64 NumEntries = StackIds.Size();
163349cc55cSDimitry Andric   Ptr = WriteBytes(NumEntries, Ptr);
164349cc55cSDimitry Andric   for (u64 i = 0; i < NumEntries; i++) {
165349cc55cSDimitry Andric     const u64 Key = StackIds[i];
166349cc55cSDimitry Andric     MIBMapTy::Handle h(&MIBMap, Key, /*remove=*/true, /*create=*/false);
167349cc55cSDimitry Andric     CHECK(h.exists());
168349cc55cSDimitry Andric     Ptr = WriteBytes(Key, Ptr);
169*0fca6ea1SDimitry Andric     // FIXME: We unnecessarily serialize the AccessHistogram pointer. Adding a
170*0fca6ea1SDimitry Andric     // serialization schema will fix this issue. See also FIXME in
171*0fca6ea1SDimitry Andric     // deserialization.
172349cc55cSDimitry Andric     Ptr = WriteBytes((*h)->mib, Ptr);
173*0fca6ea1SDimitry Andric     for (u64 j = 0; j < (*h)->mib.AccessHistogramSize; ++j) {
174*0fca6ea1SDimitry Andric       u64 HistogramEntry = ((u64 *)((*h)->mib.AccessHistogram))[j];
175*0fca6ea1SDimitry Andric       Ptr = WriteBytes(HistogramEntry, Ptr);
176349cc55cSDimitry Andric     }
177*0fca6ea1SDimitry Andric     if ((*h)->mib.AccessHistogramSize > 0) {
178*0fca6ea1SDimitry Andric       InternalFree((void *)((*h)->mib.AccessHistogram));
179*0fca6ea1SDimitry Andric     }
180*0fca6ea1SDimitry Andric   }
1814824e7fdSDimitry Andric   CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&
182349cc55cSDimitry Andric         "Expected num bytes != actual bytes written");
183349cc55cSDimitry Andric }
184349cc55cSDimitry Andric 
185349cc55cSDimitry Andric // Format
186349cc55cSDimitry Andric // ---------- Header
187349cc55cSDimitry Andric // Magic
188349cc55cSDimitry Andric // Version
189349cc55cSDimitry Andric // Total Size
190349cc55cSDimitry Andric // Segment Offset
191349cc55cSDimitry Andric // MIB Info Offset
192349cc55cSDimitry Andric // Stack Offset
193349cc55cSDimitry Andric // ---------- Segment Info
194349cc55cSDimitry Andric // Num Entries
195349cc55cSDimitry Andric // ---------- Segment Entry
196349cc55cSDimitry Andric // Start
197349cc55cSDimitry Andric // End
198349cc55cSDimitry Andric // Offset
199349cc55cSDimitry Andric // BuildID 32B
200349cc55cSDimitry Andric // ----------
201349cc55cSDimitry Andric // ...
2024824e7fdSDimitry Andric // ----------
2034824e7fdSDimitry Andric // Optional Padding Bytes
204349cc55cSDimitry Andric // ---------- MIB Info
205349cc55cSDimitry Andric // Num Entries
206349cc55cSDimitry Andric // ---------- MIB Entry
207349cc55cSDimitry Andric // Alloc Count
208349cc55cSDimitry Andric // ...
209*0fca6ea1SDimitry Andric //       ---- AccessHistogram Entry 0
210*0fca6ea1SDimitry Andric //            ...
211*0fca6ea1SDimitry Andric //       ---- AccessHistogram Entry AccessHistogramSize - 1
212*0fca6ea1SDimitry Andric // ---------- MIB Entry 1
213*0fca6ea1SDimitry Andric // Alloc Count
214*0fca6ea1SDimitry Andric // ...
215*0fca6ea1SDimitry Andric //       ---- AccessHistogram Entry 0
216*0fca6ea1SDimitry Andric //            ...
217*0fca6ea1SDimitry Andric //       ---- AccessHistogram Entry AccessHistogramSize - 1
2184824e7fdSDimitry Andric // Optional Padding Bytes
219349cc55cSDimitry Andric // ---------- Stack Info
220349cc55cSDimitry Andric // Num Entries
221349cc55cSDimitry Andric // ---------- Stack Entry
222349cc55cSDimitry Andric // Num Stacks
223349cc55cSDimitry Andric // PC1
224349cc55cSDimitry Andric // PC2
225349cc55cSDimitry Andric // ...
226349cc55cSDimitry Andric // ----------
2274824e7fdSDimitry Andric // Optional Padding Bytes
228349cc55cSDimitry Andric // ...
22906c3fb27SDimitry Andric u64 SerializeToRawProfile(MIBMapTy &MIBMap, ArrayRef<LoadedModule> Modules,
230349cc55cSDimitry Andric                           char *&Buffer) {
2314824e7fdSDimitry Andric   // Each section size is rounded up to 8b since the first entry in each section
2324824e7fdSDimitry Andric   // is a u64 which holds the number of entries in the section by convention.
23306c3fb27SDimitry Andric   const u64 NumSegmentBytes = RoundUpTo(SegmentSizeBytes(Modules), 8);
234349cc55cSDimitry Andric 
235349cc55cSDimitry Andric   Vector<u64> StackIds;
236349cc55cSDimitry Andric   MIBMap.ForEach(RecordStackId, reinterpret_cast<void *>(&StackIds));
237349cc55cSDimitry Andric   // The first 8b are for the total number of MIB records. Each MIB record is
238349cc55cSDimitry Andric   // preceded by a 8b stack id which is associated with stack frames in the next
239349cc55cSDimitry Andric   // section.
2404824e7fdSDimitry Andric   const u64 NumMIBInfoBytes = RoundUpTo(
2414824e7fdSDimitry Andric       sizeof(u64) + StackIds.Size() * (sizeof(u64) + sizeof(MemInfoBlock)), 8);
242349cc55cSDimitry Andric 
243*0fca6ea1SDimitry Andric   // Get Number of AccessHistogram entries in total
244*0fca6ea1SDimitry Andric   u64 TotalAccessHistogramEntries = 0;
245*0fca6ea1SDimitry Andric   MIBMap.ForEach(
246*0fca6ea1SDimitry Andric       [](const uptr Key, UNUSED LockedMemInfoBlock *const &MIB, void *Arg) {
247*0fca6ea1SDimitry Andric         u64 *TotalAccessHistogramEntries = (u64 *)Arg;
248*0fca6ea1SDimitry Andric         *TotalAccessHistogramEntries += MIB->mib.AccessHistogramSize;
249*0fca6ea1SDimitry Andric       },
250*0fca6ea1SDimitry Andric       reinterpret_cast<void *>(&TotalAccessHistogramEntries));
251*0fca6ea1SDimitry Andric   const u64 NumHistogramBytes =
252*0fca6ea1SDimitry Andric       RoundUpTo(TotalAccessHistogramEntries * sizeof(uint64_t), 8);
253*0fca6ea1SDimitry Andric 
2544824e7fdSDimitry Andric   const u64 NumStackBytes = RoundUpTo(StackSizeBytes(StackIds), 8);
255349cc55cSDimitry Andric 
2564824e7fdSDimitry Andric   // Ensure that the profile is 8b aligned. We allow for some optional padding
2574824e7fdSDimitry Andric   // at the end so that any subsequent profile serialized to the same file does
2584824e7fdSDimitry Andric   // not incur unaligned accesses.
259*0fca6ea1SDimitry Andric   const u64 TotalSizeBytes =
260*0fca6ea1SDimitry Andric       RoundUpTo(sizeof(Header) + NumSegmentBytes + NumStackBytes +
261*0fca6ea1SDimitry Andric                     NumMIBInfoBytes + NumHistogramBytes,
262*0fca6ea1SDimitry Andric                 8);
263349cc55cSDimitry Andric 
264349cc55cSDimitry Andric   // Allocate the memory for the entire buffer incl. info blocks.
265349cc55cSDimitry Andric   Buffer = (char *)InternalAlloc(TotalSizeBytes);
266349cc55cSDimitry Andric   char *Ptr = Buffer;
267349cc55cSDimitry Andric 
268349cc55cSDimitry Andric   Header header{MEMPROF_RAW_MAGIC_64,
269349cc55cSDimitry Andric                 MEMPROF_RAW_VERSION,
270349cc55cSDimitry Andric                 static_cast<u64>(TotalSizeBytes),
271349cc55cSDimitry Andric                 sizeof(Header),
272349cc55cSDimitry Andric                 sizeof(Header) + NumSegmentBytes,
273*0fca6ea1SDimitry Andric                 sizeof(Header) + NumSegmentBytes + NumMIBInfoBytes +
274*0fca6ea1SDimitry Andric                     NumHistogramBytes};
275349cc55cSDimitry Andric   Ptr = WriteBytes(header, Ptr);
276349cc55cSDimitry Andric 
27706c3fb27SDimitry Andric   SerializeSegmentsToBuffer(Modules, NumSegmentBytes, Ptr);
278349cc55cSDimitry Andric   Ptr += NumSegmentBytes;
279349cc55cSDimitry Andric 
280*0fca6ea1SDimitry Andric   SerializeMIBInfoToBuffer(MIBMap, StackIds,
281*0fca6ea1SDimitry Andric                            NumMIBInfoBytes + NumHistogramBytes, Ptr);
282*0fca6ea1SDimitry Andric   Ptr += NumMIBInfoBytes + NumHistogramBytes;
283349cc55cSDimitry Andric 
284349cc55cSDimitry Andric   SerializeStackToBuffer(StackIds, NumStackBytes, Ptr);
285349cc55cSDimitry Andric 
286349cc55cSDimitry Andric   return TotalSizeBytes;
287349cc55cSDimitry Andric }
288349cc55cSDimitry Andric 
289349cc55cSDimitry Andric } // namespace __memprof
290