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