5 #include "memprof_rawprofile.h"
6 #include "profile/MemProfData.inc"
7 #include "sanitizer_common/sanitizer_allocator_internal.h"
8 #include "sanitizer_common/sanitizer_common.h"
9 #include "sanitizer_common/sanitizer_linux.h"
10 #include "sanitizer_common/sanitizer_procmaps.h"
11 #include "sanitizer_common/sanitizer_stackdepot.h"
12 #include "sanitizer_common/sanitizer_stackdepotbase.h"
13 #include "sanitizer_common/sanitizer_stacktrace.h"
14 #include "sanitizer_common/sanitizer_vector.h"
17 using ::__sanitizer::Vector
;
18 using ::llvm::memprof::MemInfoBlock
;
19 using SegmentEntry
= ::llvm::memprof::SegmentEntry
;
20 using Header
= ::llvm::memprof::Header
;
23 template <class T
> char *WriteBytes(T Pod
, char *&Buffer
) {
25 return Buffer
+ sizeof(T
);
28 void RecordStackId(const uptr Key
, UNUSED LockedMemInfoBlock
*const &MIB
,
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
);
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
++;
43 return sizeof(u64
) // A header which stores the number of records.
44 + sizeof(SegmentEntry
) * NumSegmentsToRecord
;
47 // The segment section uses the following format:
48 // ---------- Segment Info
50 // ---------- Segment Entry
57 void SerializeSegmentsToBuffer(MemoryMappingLayoutBase
&Layout
,
58 const u64 ExpectedNumBytes
, char *&Buffer
) {
60 // Reserve space for the final count.
63 u64 NumSegmentsRecorded
= 0;
64 MemoryMappedSegment segment
;
66 for (Layout
.Reset(); Layout
.Next(&segment
);) {
67 if (segment
.IsReadable() && segment
.IsExecutable()) {
68 // TODO: Record segment.uuid when it is implemented for Linux-Elf.
69 SegmentEntry
Entry(segment
.start
, segment
.end
, segment
.offset
);
70 memcpy(Ptr
, &Entry
, sizeof(SegmentEntry
));
71 Ptr
+= sizeof(SegmentEntry
);
72 NumSegmentsRecorded
++;
76 // Store the number of segments we recorded in the space we reserved.
77 *((u64
*)Buffer
) = NumSegmentsRecorded
;
78 CHECK(ExpectedNumBytes
>= static_cast<u64
>(Ptr
- Buffer
) &&
79 "Expected num bytes != actual bytes written");
82 u64
StackSizeBytes(const Vector
<u64
> &StackIds
) {
83 u64 NumBytesToWrite
= sizeof(u64
);
85 const u64 NumIds
= StackIds
.Size();
86 for (unsigned k
= 0; k
< NumIds
; ++k
) {
87 const u64 Id
= StackIds
[k
];
88 // One entry for the id and then one more for the number of stack pcs.
89 NumBytesToWrite
+= 2 * sizeof(u64
);
90 const StackTrace St
= StackDepotGet(Id
);
92 CHECK(St
.trace
!= nullptr && St
.size
> 0 && "Empty stack trace");
93 for (uptr i
= 0; i
< St
.size
&& St
.trace
[i
] != 0; i
++) {
94 NumBytesToWrite
+= sizeof(u64
);
97 return NumBytesToWrite
;
100 // The stack info section uses the following format:
102 // ---------- Stack Info
104 // ---------- Stack Entry
110 void SerializeStackToBuffer(const Vector
<u64
> &StackIds
,
111 const u64 ExpectedNumBytes
, char *&Buffer
) {
112 const u64 NumIds
= StackIds
.Size();
114 Ptr
= WriteBytes(static_cast<u64
>(NumIds
), Ptr
);
116 for (unsigned k
= 0; k
< NumIds
; ++k
) {
117 const u64 Id
= StackIds
[k
];
118 Ptr
= WriteBytes(Id
, Ptr
);
119 Ptr
+= sizeof(u64
); // Bump it by u64, we will fill this in later.
121 const StackTrace St
= StackDepotGet(Id
);
122 for (uptr i
= 0; i
< St
.size
&& St
.trace
[i
] != 0; i
++) {
123 // PCs in stack traces are actually the return addresses, that is,
124 // addresses of the next instructions after the call.
125 uptr pc
= StackTrace::GetPreviousInstructionPc(St
.trace
[i
]);
126 Ptr
= WriteBytes(static_cast<u64
>(pc
), Ptr
);
129 // Store the count in the space we reserved earlier.
130 *(u64
*)(Ptr
- (Count
+ 1) * sizeof(u64
)) = Count
;
133 CHECK(ExpectedNumBytes
>= static_cast<u64
>(Ptr
- Buffer
) &&
134 "Expected num bytes != actual bytes written");
137 // The MIB section has the following format:
138 // ---------- MIB Info
140 // ---------- MIB Entry 0
143 // ---------- MIB Entry 1
147 void SerializeMIBInfoToBuffer(MIBMapTy
&MIBMap
, const Vector
<u64
> &StackIds
,
148 const u64 ExpectedNumBytes
, char *&Buffer
) {
150 const u64 NumEntries
= StackIds
.Size();
151 Ptr
= WriteBytes(NumEntries
, Ptr
);
153 for (u64 i
= 0; i
< NumEntries
; i
++) {
154 const u64 Key
= StackIds
[i
];
155 MIBMapTy::Handle
h(&MIBMap
, Key
, /*remove=*/true, /*create=*/false);
157 Ptr
= WriteBytes(Key
, Ptr
);
158 Ptr
= WriteBytes((*h
)->mib
, Ptr
);
161 CHECK(ExpectedNumBytes
>= static_cast<u64
>(Ptr
- Buffer
) &&
162 "Expected num bytes != actual bytes written");
173 // ---------- Segment Info
175 // ---------- Segment Entry
183 // Optional Padding Bytes
184 // ---------- MIB Info
186 // ---------- MIB Entry
190 // Optional Padding Bytes
191 // ---------- Stack Info
193 // ---------- Stack Entry
199 // Optional Padding Bytes
201 u64
SerializeToRawProfile(MIBMapTy
&MIBMap
, MemoryMappingLayoutBase
&Layout
,
203 // Each section size is rounded up to 8b since the first entry in each section
204 // is a u64 which holds the number of entries in the section by convention.
205 const u64 NumSegmentBytes
= RoundUpTo(SegmentSizeBytes(Layout
), 8);
207 Vector
<u64
> StackIds
;
208 MIBMap
.ForEach(RecordStackId
, reinterpret_cast<void *>(&StackIds
));
209 // The first 8b are for the total number of MIB records. Each MIB record is
210 // preceded by a 8b stack id which is associated with stack frames in the next
212 const u64 NumMIBInfoBytes
= RoundUpTo(
213 sizeof(u64
) + StackIds
.Size() * (sizeof(u64
) + sizeof(MemInfoBlock
)), 8);
215 const u64 NumStackBytes
= RoundUpTo(StackSizeBytes(StackIds
), 8);
217 // Ensure that the profile is 8b aligned. We allow for some optional padding
218 // at the end so that any subsequent profile serialized to the same file does
219 // not incur unaligned accesses.
220 const u64 TotalSizeBytes
= RoundUpTo(
221 sizeof(Header
) + NumSegmentBytes
+ NumStackBytes
+ NumMIBInfoBytes
, 8);
223 // Allocate the memory for the entire buffer incl. info blocks.
224 Buffer
= (char *)InternalAlloc(TotalSizeBytes
);
227 Header header
{MEMPROF_RAW_MAGIC_64
,
229 static_cast<u64
>(TotalSizeBytes
),
231 sizeof(Header
) + NumSegmentBytes
,
232 sizeof(Header
) + NumSegmentBytes
+ NumMIBInfoBytes
};
233 Ptr
= WriteBytes(header
, Ptr
);
235 SerializeSegmentsToBuffer(Layout
, NumSegmentBytes
, Ptr
);
236 Ptr
+= NumSegmentBytes
;
238 SerializeMIBInfoToBuffer(MIBMap
, StackIds
, NumMIBInfoBytes
, Ptr
);
239 Ptr
+= NumMIBInfoBytes
;
241 SerializeStackToBuffer(StackIds
, NumStackBytes
, Ptr
);
243 return TotalSizeBytes
;
246 } // namespace __memprof