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