xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/memprof/memprof_rawprofile.cpp (revision 4824e7fd18a1223177218d4aec1b3c6c5c4a444e)
1*4824e7fdSDimitry Andric #include <stdint.h>
2*4824e7fdSDimitry Andric #include <stdlib.h>
3*4824e7fdSDimitry Andric #include <string.h>
4*4824e7fdSDimitry Andric 
5349cc55cSDimitry Andric #include "memprof_meminfoblock.h"
6*4824e7fdSDimitry Andric #include "memprof_rawprofile.h"
7*4824e7fdSDimitry Andric #include "profile/MemProfData.inc"
8349cc55cSDimitry Andric #include "sanitizer_common/sanitizer_allocator_internal.h"
9*4824e7fdSDimitry 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;
19*4824e7fdSDimitry Andric using SegmentEntry = ::llvm::memprof::SegmentEntry;
20*4824e7fdSDimitry Andric using Header = ::llvm::memprof::Header;
21349cc55cSDimitry Andric 
22349cc55cSDimitry Andric namespace {
23349cc55cSDimitry Andric template <class T> char *WriteBytes(T Pod, char *&Buffer) {
24349cc55cSDimitry Andric   *(T *)Buffer = Pod;
25349cc55cSDimitry Andric   return Buffer + sizeof(T);
26349cc55cSDimitry Andric }
27349cc55cSDimitry Andric 
28349cc55cSDimitry Andric void RecordStackId(const uptr Key, UNUSED LockedMemInfoBlock *const &MIB,
29349cc55cSDimitry Andric                    void *Arg) {
30349cc55cSDimitry Andric   // No need to touch the MIB value here since we are only recording the key.
31349cc55cSDimitry Andric   auto *StackIds = reinterpret_cast<Vector<u64> *>(Arg);
32349cc55cSDimitry Andric   StackIds->PushBack(Key);
33349cc55cSDimitry Andric }
34349cc55cSDimitry Andric } // namespace
35349cc55cSDimitry Andric 
36349cc55cSDimitry Andric u64 SegmentSizeBytes(MemoryMappingLayoutBase &Layout) {
37349cc55cSDimitry Andric   u64 NumSegmentsToRecord = 0;
38349cc55cSDimitry Andric   MemoryMappedSegment segment;
39349cc55cSDimitry Andric   for (Layout.Reset(); Layout.Next(&segment);)
40349cc55cSDimitry Andric     if (segment.IsReadable() && segment.IsExecutable())
41349cc55cSDimitry Andric       NumSegmentsToRecord++;
42349cc55cSDimitry Andric 
43349cc55cSDimitry Andric   return sizeof(u64) // A header which stores the number of records.
44349cc55cSDimitry Andric          + sizeof(SegmentEntry) * NumSegmentsToRecord;
45349cc55cSDimitry Andric }
46349cc55cSDimitry Andric 
47349cc55cSDimitry Andric // The segment section uses the following format:
48349cc55cSDimitry Andric // ---------- Segment Info
49349cc55cSDimitry Andric // Num Entries
50349cc55cSDimitry Andric // ---------- Segment Entry
51349cc55cSDimitry Andric // Start
52349cc55cSDimitry Andric // End
53349cc55cSDimitry Andric // Offset
54349cc55cSDimitry Andric // BuildID 32B
55349cc55cSDimitry Andric // ----------
56349cc55cSDimitry Andric // ...
57349cc55cSDimitry Andric void SerializeSegmentsToBuffer(MemoryMappingLayoutBase &Layout,
58349cc55cSDimitry Andric                                const u64 ExpectedNumBytes, char *&Buffer) {
59349cc55cSDimitry Andric   char *Ptr = Buffer;
60349cc55cSDimitry Andric   // Reserve space for the final count.
61349cc55cSDimitry Andric   Ptr += sizeof(u64);
62349cc55cSDimitry Andric 
63349cc55cSDimitry Andric   u64 NumSegmentsRecorded = 0;
64349cc55cSDimitry Andric   MemoryMappedSegment segment;
65349cc55cSDimitry Andric 
66349cc55cSDimitry Andric   for (Layout.Reset(); Layout.Next(&segment);) {
67349cc55cSDimitry Andric     if (segment.IsReadable() && segment.IsExecutable()) {
68*4824e7fdSDimitry Andric       SegmentEntry Entry{};
69*4824e7fdSDimitry Andric       Entry.Start = segment.start;
70*4824e7fdSDimitry Andric       Entry.End = segment.end;
71*4824e7fdSDimitry Andric       Entry.Offset = segment.offset;
72*4824e7fdSDimitry Andric       memcpy(Entry.BuildId, segment.uuid, sizeof(segment.uuid));
73*4824e7fdSDimitry Andric       memcpy(Ptr, &Entry, sizeof(SegmentEntry));
74349cc55cSDimitry Andric       Ptr += sizeof(SegmentEntry);
75349cc55cSDimitry Andric       NumSegmentsRecorded++;
76349cc55cSDimitry Andric     }
77349cc55cSDimitry Andric   }
78349cc55cSDimitry Andric 
79349cc55cSDimitry Andric   // Store the number of segments we recorded in the space we reserved.
80349cc55cSDimitry Andric   *((u64 *)Buffer) = NumSegmentsRecorded;
81*4824e7fdSDimitry Andric   CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&
82349cc55cSDimitry Andric         "Expected num bytes != actual bytes written");
83349cc55cSDimitry Andric }
84349cc55cSDimitry Andric 
85349cc55cSDimitry Andric u64 StackSizeBytes(const Vector<u64> &StackIds) {
86349cc55cSDimitry Andric   u64 NumBytesToWrite = sizeof(u64);
87349cc55cSDimitry Andric 
88349cc55cSDimitry Andric   const u64 NumIds = StackIds.Size();
89349cc55cSDimitry Andric   for (unsigned k = 0; k < NumIds; ++k) {
90349cc55cSDimitry Andric     const u64 Id = StackIds[k];
91349cc55cSDimitry Andric     // One entry for the id and then one more for the number of stack pcs.
92349cc55cSDimitry Andric     NumBytesToWrite += 2 * sizeof(u64);
93349cc55cSDimitry Andric     const StackTrace St = StackDepotGet(Id);
94349cc55cSDimitry Andric 
95349cc55cSDimitry Andric     CHECK(St.trace != nullptr && St.size > 0 && "Empty stack trace");
96349cc55cSDimitry Andric     for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) {
97349cc55cSDimitry Andric       NumBytesToWrite += sizeof(u64);
98349cc55cSDimitry Andric     }
99349cc55cSDimitry Andric   }
100349cc55cSDimitry Andric   return NumBytesToWrite;
101349cc55cSDimitry Andric }
102349cc55cSDimitry Andric 
103349cc55cSDimitry Andric // The stack info section uses the following format:
104349cc55cSDimitry Andric //
105349cc55cSDimitry Andric // ---------- Stack Info
106349cc55cSDimitry Andric // Num Entries
107349cc55cSDimitry Andric // ---------- Stack Entry
108349cc55cSDimitry Andric // Num Stacks
109349cc55cSDimitry Andric // PC1
110349cc55cSDimitry Andric // PC2
111349cc55cSDimitry Andric // ...
112349cc55cSDimitry Andric // ----------
113349cc55cSDimitry Andric void SerializeStackToBuffer(const Vector<u64> &StackIds,
114349cc55cSDimitry Andric                             const u64 ExpectedNumBytes, char *&Buffer) {
115349cc55cSDimitry Andric   const u64 NumIds = StackIds.Size();
116349cc55cSDimitry Andric   char *Ptr = Buffer;
117349cc55cSDimitry Andric   Ptr = WriteBytes(static_cast<u64>(NumIds), Ptr);
118349cc55cSDimitry Andric 
119349cc55cSDimitry Andric   for (unsigned k = 0; k < NumIds; ++k) {
120349cc55cSDimitry Andric     const u64 Id = StackIds[k];
121349cc55cSDimitry Andric     Ptr = WriteBytes(Id, Ptr);
122349cc55cSDimitry Andric     Ptr += sizeof(u64); // Bump it by u64, we will fill this in later.
123349cc55cSDimitry Andric     u64 Count = 0;
124349cc55cSDimitry Andric     const StackTrace St = StackDepotGet(Id);
125349cc55cSDimitry Andric     for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) {
126349cc55cSDimitry Andric       // PCs in stack traces are actually the return addresses, that is,
127349cc55cSDimitry Andric       // addresses of the next instructions after the call.
128349cc55cSDimitry Andric       uptr pc = StackTrace::GetPreviousInstructionPc(St.trace[i]);
129349cc55cSDimitry Andric       Ptr = WriteBytes(static_cast<u64>(pc), Ptr);
130349cc55cSDimitry Andric       ++Count;
131349cc55cSDimitry Andric     }
132349cc55cSDimitry Andric     // Store the count in the space we reserved earlier.
133349cc55cSDimitry Andric     *(u64 *)(Ptr - (Count + 1) * sizeof(u64)) = Count;
134349cc55cSDimitry Andric   }
135349cc55cSDimitry Andric 
136*4824e7fdSDimitry Andric   CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&
137349cc55cSDimitry Andric         "Expected num bytes != actual bytes written");
138349cc55cSDimitry Andric }
139349cc55cSDimitry Andric 
140349cc55cSDimitry Andric // The MIB section has the following format:
141349cc55cSDimitry Andric // ---------- MIB Info
142349cc55cSDimitry Andric // Num Entries
143349cc55cSDimitry Andric // ---------- MIB Entry 0
144349cc55cSDimitry Andric // Alloc Count
145349cc55cSDimitry Andric // ...
146349cc55cSDimitry Andric // ---------- MIB Entry 1
147349cc55cSDimitry Andric // Alloc Count
148349cc55cSDimitry Andric // ...
149349cc55cSDimitry Andric // ----------
150349cc55cSDimitry Andric void SerializeMIBInfoToBuffer(MIBMapTy &MIBMap, const Vector<u64> &StackIds,
151349cc55cSDimitry Andric                               const u64 ExpectedNumBytes, char *&Buffer) {
152349cc55cSDimitry Andric   char *Ptr = Buffer;
153349cc55cSDimitry Andric   const u64 NumEntries = StackIds.Size();
154349cc55cSDimitry Andric   Ptr = WriteBytes(NumEntries, Ptr);
155349cc55cSDimitry Andric 
156349cc55cSDimitry Andric   for (u64 i = 0; i < NumEntries; i++) {
157349cc55cSDimitry Andric     const u64 Key = StackIds[i];
158349cc55cSDimitry Andric     MIBMapTy::Handle h(&MIBMap, Key, /*remove=*/true, /*create=*/false);
159349cc55cSDimitry Andric     CHECK(h.exists());
160349cc55cSDimitry Andric     Ptr = WriteBytes(Key, Ptr);
161349cc55cSDimitry Andric     Ptr = WriteBytes((*h)->mib, Ptr);
162349cc55cSDimitry Andric   }
163349cc55cSDimitry Andric 
164*4824e7fdSDimitry Andric   CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&
165349cc55cSDimitry Andric         "Expected num bytes != actual bytes written");
166349cc55cSDimitry Andric }
167349cc55cSDimitry Andric 
168349cc55cSDimitry Andric // Format
169349cc55cSDimitry Andric // ---------- Header
170349cc55cSDimitry Andric // Magic
171349cc55cSDimitry Andric // Version
172349cc55cSDimitry Andric // Total Size
173349cc55cSDimitry Andric // Segment Offset
174349cc55cSDimitry Andric // MIB Info Offset
175349cc55cSDimitry Andric // Stack Offset
176349cc55cSDimitry Andric // ---------- Segment Info
177349cc55cSDimitry Andric // Num Entries
178349cc55cSDimitry Andric // ---------- Segment Entry
179349cc55cSDimitry Andric // Start
180349cc55cSDimitry Andric // End
181349cc55cSDimitry Andric // Offset
182349cc55cSDimitry Andric // BuildID 32B
183349cc55cSDimitry Andric // ----------
184349cc55cSDimitry Andric // ...
185*4824e7fdSDimitry Andric // ----------
186*4824e7fdSDimitry Andric // Optional Padding Bytes
187349cc55cSDimitry Andric // ---------- MIB Info
188349cc55cSDimitry Andric // Num Entries
189349cc55cSDimitry Andric // ---------- MIB Entry
190349cc55cSDimitry Andric // Alloc Count
191349cc55cSDimitry Andric // ...
192*4824e7fdSDimitry Andric // ----------
193*4824e7fdSDimitry Andric // Optional Padding Bytes
194349cc55cSDimitry Andric // ---------- Stack Info
195349cc55cSDimitry Andric // Num Entries
196349cc55cSDimitry Andric // ---------- Stack Entry
197349cc55cSDimitry Andric // Num Stacks
198349cc55cSDimitry Andric // PC1
199349cc55cSDimitry Andric // PC2
200349cc55cSDimitry Andric // ...
201349cc55cSDimitry Andric // ----------
202*4824e7fdSDimitry Andric // Optional Padding Bytes
203349cc55cSDimitry Andric // ...
204349cc55cSDimitry Andric u64 SerializeToRawProfile(MIBMapTy &MIBMap, MemoryMappingLayoutBase &Layout,
205349cc55cSDimitry Andric                           char *&Buffer) {
206*4824e7fdSDimitry Andric   // Each section size is rounded up to 8b since the first entry in each section
207*4824e7fdSDimitry Andric   // is a u64 which holds the number of entries in the section by convention.
208*4824e7fdSDimitry Andric   const u64 NumSegmentBytes = RoundUpTo(SegmentSizeBytes(Layout), 8);
209349cc55cSDimitry Andric 
210349cc55cSDimitry Andric   Vector<u64> StackIds;
211349cc55cSDimitry Andric   MIBMap.ForEach(RecordStackId, reinterpret_cast<void *>(&StackIds));
212349cc55cSDimitry Andric   // The first 8b are for the total number of MIB records. Each MIB record is
213349cc55cSDimitry Andric   // preceded by a 8b stack id which is associated with stack frames in the next
214349cc55cSDimitry Andric   // section.
215*4824e7fdSDimitry Andric   const u64 NumMIBInfoBytes = RoundUpTo(
216*4824e7fdSDimitry Andric       sizeof(u64) + StackIds.Size() * (sizeof(u64) + sizeof(MemInfoBlock)), 8);
217349cc55cSDimitry Andric 
218*4824e7fdSDimitry Andric   const u64 NumStackBytes = RoundUpTo(StackSizeBytes(StackIds), 8);
219349cc55cSDimitry Andric 
220*4824e7fdSDimitry Andric   // Ensure that the profile is 8b aligned. We allow for some optional padding
221*4824e7fdSDimitry Andric   // at the end so that any subsequent profile serialized to the same file does
222*4824e7fdSDimitry Andric   // not incur unaligned accesses.
223*4824e7fdSDimitry Andric   const u64 TotalSizeBytes = RoundUpTo(
224*4824e7fdSDimitry Andric       sizeof(Header) + NumSegmentBytes + NumStackBytes + NumMIBInfoBytes, 8);
225349cc55cSDimitry Andric 
226349cc55cSDimitry Andric   // Allocate the memory for the entire buffer incl. info blocks.
227349cc55cSDimitry Andric   Buffer = (char *)InternalAlloc(TotalSizeBytes);
228349cc55cSDimitry Andric   char *Ptr = Buffer;
229349cc55cSDimitry Andric 
230349cc55cSDimitry Andric   Header header{MEMPROF_RAW_MAGIC_64,
231349cc55cSDimitry Andric                 MEMPROF_RAW_VERSION,
232349cc55cSDimitry Andric                 static_cast<u64>(TotalSizeBytes),
233349cc55cSDimitry Andric                 sizeof(Header),
234349cc55cSDimitry Andric                 sizeof(Header) + NumSegmentBytes,
235349cc55cSDimitry Andric                 sizeof(Header) + NumSegmentBytes + NumMIBInfoBytes};
236349cc55cSDimitry Andric   Ptr = WriteBytes(header, Ptr);
237349cc55cSDimitry Andric 
238349cc55cSDimitry Andric   SerializeSegmentsToBuffer(Layout, NumSegmentBytes, Ptr);
239349cc55cSDimitry Andric   Ptr += NumSegmentBytes;
240349cc55cSDimitry Andric 
241349cc55cSDimitry Andric   SerializeMIBInfoToBuffer(MIBMap, StackIds, NumMIBInfoBytes, Ptr);
242349cc55cSDimitry Andric   Ptr += NumMIBInfoBytes;
243349cc55cSDimitry Andric 
244349cc55cSDimitry Andric   SerializeStackToBuffer(StackIds, NumStackBytes, Ptr);
245349cc55cSDimitry Andric 
246349cc55cSDimitry Andric   return TotalSizeBytes;
247349cc55cSDimitry Andric }
248349cc55cSDimitry Andric 
249349cc55cSDimitry Andric } // namespace __memprof
250