[memprof] Align each rawprofile section to 8b.
authorSnehasish Kumar <snehasishk@google.com>
Tue, 30 Nov 2021 23:56:17 +0000 (15:56 -0800)
committerSnehasish Kumar <snehasishk@google.com>
Wed, 1 Dec 2021 04:12:43 +0000 (20:12 -0800)
The first 8b of each raw profile section need to be aligned to 8b since
the first item in each section is a u64 count of the number of items in
the section.
Summary of changes:
* Assert alignment when reading counts.
* Update test to check alignment, relax some size checks to allow padding.
* Update raw binary inputs for llvm-profdata tests.

Differential Revision: https://reviews.llvm.org/D114826

compiler-rt/lib/memprof/memprof_rawprofile.cpp
compiler-rt/lib/memprof/tests/rawprofile.cpp
llvm/lib/ProfileData/RawMemProfReader.cpp
llvm/test/tools/llvm-profdata/Inputs/basic.memprofraw
llvm/test/tools/llvm-profdata/Inputs/multi.memprofraw
llvm/test/tools/llvm-profdata/memprof-basic.test

index 48e579c..c4800a6 100644 (file)
@@ -6,6 +6,7 @@
 #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"
@@ -77,7 +78,7 @@ void SerializeSegmentsToBuffer(MemoryMappingLayoutBase &Layout,
 
   // 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");
 }
 
@@ -132,7 +133,7 @@ void SerializeStackToBuffer(const Vector<u64> &StackIds,
     *(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");
 }
 
@@ -160,7 +161,7 @@ void SerializeMIBInfoToBuffer(MIBMapTy &MIBMap, const Vector<u64> &StackIds,
     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");
 }
 
@@ -181,11 +182,15 @@ void SerializeMIBInfoToBuffer(MIBMapTy &MIBMap, const Vector<u64> &StackIds,
 // BuildID 32B
 // ----------
 // ...
+// ----------
+// Optional Padding Bytes
 // ---------- MIB Info
 // Num Entries
 // ---------- MIB Entry
 // Alloc Count
 // ...
+// ----------
+// Optional Padding Bytes
 // ---------- Stack Info
 // Num Entries
 // ---------- Stack Entry
@@ -194,23 +199,29 @@ void SerializeMIBInfoToBuffer(MIBMapTy &MIBMap, const Vector<u64> &StackIds,
 // 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);
index 29bc3b5..829e183 100644 (file)
@@ -49,6 +49,8 @@ u64 PopulateFakeMap(const MemInfoBlock &FakeMIB, uptr StackPCBegin,
 
 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;
@@ -103,8 +105,9 @@ TEST(MemProf, Basic) {
   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);
@@ -120,8 +123,10 @@ TEST(MemProf, Basic) {
 
   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] = {
index 07c218a..f8d13c7 100644 (file)
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include <cstdint>
+#include <type_traits>
 
 #include "llvm/ProfileData/InstrProf.h"
 #include "llvm/ProfileData/MemProfData.inc"
@@ -28,15 +29,22 @@ struct Summary {
   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),
   };
 }
 
@@ -84,7 +92,9 @@ RawMemProfReader::create(const Twine &Path) {
 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;
 }
 
index b0be02a..42bd6e7 100644 (file)
Binary files a/llvm/test/tools/llvm-profdata/Inputs/basic.memprofraw and b/llvm/test/tools/llvm-profdata/Inputs/basic.memprofraw differ
index e740903..fd8f412 100644 (file)
Binary files a/llvm/test/tools/llvm-profdata/Inputs/multi.memprofraw and b/llvm/test/tools/llvm-profdata/Inputs/multi.memprofraw differ
index 1ef7c71..321119b 100644 (file)
@@ -36,7 +36,7 @@ additional entry from a realloc in glibc/libio/vasprintf.c.
 
 CHECK: MemProf Profile 1
 CHECK:   Version: 1
-CHECK:   TotalSizeBytes: 1012
+CHECK:   TotalSizeBytes: 1016
 CHECK:   NumSegments: 9
 CHECK:   NumMIBInfo: 3
 CHECK:   NumStackOffsets: 3