#include "memprof_rawprofile.h"
#include "profile/MemProfData.inc"
#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_linux.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
// Store the number of segments we recorded in the space we reserved.
*((u64 *)Buffer) = NumSegmentsRecorded;
- CHECK(ExpectedNumBytes == static_cast<u64>(Ptr - Buffer) &&
+ CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&
"Expected num bytes != actual bytes written");
}
*(u64 *)(Ptr - (Count + 1) * sizeof(u64)) = Count;
}
- CHECK(ExpectedNumBytes == static_cast<u64>(Ptr - Buffer) &&
+ CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&
"Expected num bytes != actual bytes written");
}
Ptr = WriteBytes((*h)->mib, Ptr);
}
- CHECK(ExpectedNumBytes == static_cast<u64>(Ptr - Buffer) &&
+ CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&
"Expected num bytes != actual bytes written");
}
// BuildID 32B
// ----------
// ...
+// ----------
+// Optional Padding Bytes
// ---------- MIB Info
// Num Entries
// ---------- MIB Entry
// Alloc Count
// ...
+// ----------
+// Optional Padding Bytes
// ---------- Stack Info
// Num Entries
// ---------- Stack Entry
// PC2
// ...
// ----------
+// Optional Padding Bytes
// ...
u64 SerializeToRawProfile(MIBMapTy &MIBMap, MemoryMappingLayoutBase &Layout,
char *&Buffer) {
- const u64 NumSegmentBytes = SegmentSizeBytes(Layout);
+ // Each section size is rounded up to 8b since the first entry in each section
+ // is a u64 which holds the number of entries in the section by convention.
+ const u64 NumSegmentBytes = RoundUpTo(SegmentSizeBytes(Layout), 8);
Vector<u64> StackIds;
MIBMap.ForEach(RecordStackId, reinterpret_cast<void *>(&StackIds));
// The first 8b are for the total number of MIB records. Each MIB record is
// preceded by a 8b stack id which is associated with stack frames in the next
// section.
- const u64 NumMIBInfoBytes =
- sizeof(u64) + StackIds.Size() * (sizeof(u64) + sizeof(MemInfoBlock));
+ const u64 NumMIBInfoBytes = RoundUpTo(
+ sizeof(u64) + StackIds.Size() * (sizeof(u64) + sizeof(MemInfoBlock)), 8);
- const u64 NumStackBytes = StackSizeBytes(StackIds);
+ const u64 NumStackBytes = RoundUpTo(StackSizeBytes(StackIds), 8);
- const u64 TotalSizeBytes =
- sizeof(Header) + NumSegmentBytes + NumStackBytes + NumMIBInfoBytes;
+ // Ensure that the profile is 8b aligned. We allow for some optional padding
+ // at the end so that any subsequent profile serialized to the same file does
+ // not incur unaligned accesses.
+ const u64 TotalSizeBytes = RoundUpTo(
+ sizeof(Header) + NumSegmentBytes + NumStackBytes + NumMIBInfoBytes, 8);
// Allocate the memory for the entire buffer incl. info blocks.
Buffer = (char *)InternalAlloc(TotalSizeBytes);
template <class T = u64> T Read(char *&Buffer) {
static_assert(std::is_pod<T>::value, "Must be a POD type.");
+ assert(reinterpret_cast<size_t>(Buffer) % sizeof(T) == 0 &&
+ "Unaligned read!");
T t = *reinterpret_cast<T *>(Buffer);
Buffer += sizeof(T);
return t;
const u64 MIBOffset = Read(Ptr);
const u64 StackOffset = Read(Ptr);
- // ============= Check sizes.
+ // ============= Check sizes and padding.
EXPECT_EQ(TotalSize, NumBytes);
+ EXPECT_EQ(TotalSize % 8, 0ULL);
// Should be equal to the size of the raw profile header.
EXPECT_EQ(SegmentOffset, 48ULL);
EXPECT_EQ(StackOffset, 336ULL);
// We expect 2 stack entries, with 5 frames - 8b for total count,
- // 2 * (8b for id, 8b for frame count and 5*8b for fake frames)
- EXPECT_EQ(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8));
+ // 2 * (8b for id, 8b for frame count and 5*8b for fake frames).
+ // Since this is the last section, there may be additional padding at the end
+ // to make the total profile size 8b aligned.
+ EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8));
// ============= Check contents.
unsigned char ExpectedSegmentBytes[64] = {
//===----------------------------------------------------------------------===//
#include <cstdint>
+#include <type_traits>
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/ProfileData/MemProfData.inc"
uint64_t NumStackOffsets;
};
+template <class T = uint64_t> inline T alignedRead(const char *Ptr) {
+ static_assert(std::is_pod<T>::value, "Not a pod type.");
+ assert(reinterpret_cast<size_t>(Ptr) % sizeof(T) == 0 && "Unaligned Read");
+ return *reinterpret_cast<const T *>(Ptr);
+}
+
Summary computeSummary(const char *Start) {
auto *H = reinterpret_cast<const Header *>(Start);
+ // Check alignment while reading the number of items in each section.
return Summary{
H->Version,
H->TotalSize,
- *reinterpret_cast<const uint64_t *>(Start + H->SegmentOffset),
- *reinterpret_cast<const uint64_t *>(Start + H->MIBOffset),
- *reinterpret_cast<const uint64_t *>(Start + H->StackOffset),
+ alignedRead(Start + H->SegmentOffset),
+ alignedRead(Start + H->MIBOffset),
+ alignedRead(Start + H->StackOffset),
};
}
bool RawMemProfReader::hasFormat(const MemoryBuffer &Buffer) {
if (Buffer.getBufferSize() < sizeof(uint64_t))
return false;
- uint64_t Magic = *reinterpret_cast<const uint64_t *>(Buffer.getBufferStart());
+ // Aligned read to sanity check that the buffer was allocated with at least 8b
+ // alignment.
+ const uint64_t Magic = alignedRead(Buffer.getBufferStart());
return Magic == MEMPROF_RAW_MAGIC_64;
}
CHECK: MemProf Profile 1
CHECK: Version: 1
-CHECK: TotalSizeBytes: 1012
+CHECK: TotalSizeBytes: 1016
CHECK: NumSegments: 9
CHECK: NumMIBInfo: 3
CHECK: NumStackOffsets: 3