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