[memprof] Move the meminfo block struct to MemProfData.inc.
authorSnehasish Kumar <snehasishk@google.com>
Wed, 29 Dec 2021 23:31:11 +0000 (15:31 -0800)
committerSnehasish Kumar <snehasishk@google.com>
Mon, 31 Jan 2022 23:04:41 +0000 (15:04 -0800)
The definition of the MemInfoBlock is shared between the memprof
compiler-rt runtime and llvm/lib/ProfileData/. This change removes the
memprof_meminfoblock header and moves the struct to the shared include
file. To enable this sharing, the Print method is moved to the
memprof_allocator (the only place it is used) and the remaining uses are
updated to refer to the MemInfoBlock defined in the MemProfData.inc
file.

Also a couple of other minor changes which improve usability of the
types in MemProfData.inc.
* Update the PACKED macro to handle commas.
* Add constructors and equality operators.
* Don't initialize the buildid field.

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

compiler-rt/include/profile/MemProfData.inc
compiler-rt/lib/memprof/memprof_allocator.cpp
compiler-rt/lib/memprof/memprof_meminfoblock.h [deleted file]
compiler-rt/lib/memprof/memprof_mibmap.cpp
compiler-rt/lib/memprof/memprof_mibmap.h
compiler-rt/lib/memprof/memprof_rawprofile.cpp
compiler-rt/lib/memprof/tests/rawprofile.cpp
llvm/include/llvm/ProfileData/MemProfData.inc

index d64227e..20f8308 100644 (file)
  *
 \*===----------------------------------------------------------------------===*/
 
-
 #ifdef _MSC_VER
-#define PACKED(__decl__) __pragma(pack(push,1)) __decl__ __pragma(pack(pop))
+#define PACKED(...) __pragma(pack(push,1)) __VA_ARGS__ __pragma(pack(pop))
 #else
-#define PACKED(__decl__) __decl__ __attribute__((__packed__))
+#define PACKED(...) __VA_ARGS__ __attribute__((__packed__))
 #endif
 
 // A 64-bit magic number to uniquely identify the raw binary memprof profile file.
@@ -47,14 +46,106 @@ PACKED(struct Header {
   uint64_t StackOffset;
 });
 
+
 // A struct describing the information necessary to describe a /proc/maps
 // segment entry for a particular binary/library identified by its build id.
 PACKED(struct SegmentEntry {
   uint64_t Start;
   uint64_t End;
   uint64_t Offset;
-  uint8_t BuildId[32];
+  // This field is unused until sanitizer procmaps support for build ids for
+  // Linux-Elf is implemented.
+  uint8_t BuildId[32] = {0};
+
+  SegmentEntry(uint64_t S, uint64_t E, uint64_t O) :
+    Start(S), End(E), Offset(O) {}
+
+  SegmentEntry(const SegmentEntry& S) {
+    Start = S.Start;
+    End = S.End;
+    Offset = S.Offset;
+  }
+
+  SegmentEntry& operator=(const SegmentEntry& S) {
+    Start = S.Start;
+    End = S.End;
+    Offset = S.Offset;
+    return *this;
+  }
+
+  bool operator==(const SegmentEntry& S) const {
+    return Start == S.Start &&
+           End == S.End &&
+           Offset == S.Offset;
+  }
 });
+
+// A struct representing the heap allocation characteristics of a particular
+// runtime context. This struct is shared between the compiler-rt runtime and
+// the raw profile reader. The indexed format uses a separate, self-describing
+// backwards compatible format.
+PACKED(struct MemInfoBlock {
+  uint32_t alloc_count;
+  uint64_t total_access_count, min_access_count, max_access_count;
+  uint64_t total_size;
+  uint32_t min_size, max_size;
+  uint32_t alloc_timestamp, dealloc_timestamp;
+  uint64_t total_lifetime;
+  uint32_t min_lifetime, max_lifetime;
+  uint32_t alloc_cpu_id, dealloc_cpu_id;
+  uint32_t num_migrated_cpu;
+
+  // Only compared to prior deallocated object currently.
+  uint32_t num_lifetime_overlaps;
+  uint32_t num_same_alloc_cpu;
+  uint32_t num_same_dealloc_cpu;
+
+  uint64_t data_type_id; // TODO: hash of type name
+
+  MemInfoBlock() : alloc_count(0) {}
+
+  MemInfoBlock(uint32_t size, uint64_t access_count, uint32_t alloc_timestamp,
+               uint32_t dealloc_timestamp, uint32_t alloc_cpu, uint32_t dealloc_cpu)
+      : alloc_count(1), total_access_count(access_count),
+        min_access_count(access_count), max_access_count(access_count),
+        total_size(size), min_size(size), max_size(size),
+        alloc_timestamp(alloc_timestamp), dealloc_timestamp(dealloc_timestamp),
+        total_lifetime(dealloc_timestamp - alloc_timestamp),
+        min_lifetime(total_lifetime), max_lifetime(total_lifetime),
+        alloc_cpu_id(alloc_cpu), dealloc_cpu_id(dealloc_cpu),
+        num_lifetime_overlaps(0), num_same_alloc_cpu(0),
+        num_same_dealloc_cpu(0) {
+    num_migrated_cpu = alloc_cpu_id != dealloc_cpu_id;
+  }
+
+  void Merge(const MemInfoBlock &newMIB) {
+    alloc_count += newMIB.alloc_count;
+
+    total_access_count += newMIB.total_access_count;
+    min_access_count = newMIB.min_access_count < min_access_count ? newMIB.min_access_count : min_access_count;
+    max_access_count = newMIB.max_access_count < max_access_count ? newMIB.max_access_count : max_access_count;
+
+    total_size += newMIB.total_size;
+    min_size = newMIB.min_size < min_size ? newMIB.min_size : min_size;
+    max_size = newMIB.max_size < max_size ? newMIB.max_size : max_size;
+
+    total_lifetime += newMIB.total_lifetime;
+    min_lifetime = newMIB.min_lifetime < min_lifetime ? newMIB.min_lifetime : min_lifetime;
+    max_lifetime = newMIB.max_lifetime > max_lifetime ? newMIB.max_lifetime : max_lifetime;
+
+    // We know newMIB was deallocated later, so just need to check if it was
+    // allocated before last one deallocated.
+    num_lifetime_overlaps += newMIB.alloc_timestamp < dealloc_timestamp;
+    alloc_timestamp = newMIB.alloc_timestamp;
+    dealloc_timestamp = newMIB.dealloc_timestamp;
+
+    num_same_alloc_cpu += alloc_cpu_id == newMIB.alloc_cpu_id;
+    num_same_dealloc_cpu += dealloc_cpu_id == newMIB.dealloc_cpu_id;
+    alloc_cpu_id = newMIB.alloc_cpu_id;
+    dealloc_cpu_id = newMIB.dealloc_cpu_id;
+  }
+});
+
 } // namespace memprof
 } // namespace llvm
 
index 0974b89..14e7bfe 100644 (file)
 
 #include "memprof_allocator.h"
 #include "memprof_mapping.h"
-#include "memprof_meminfoblock.h"
 #include "memprof_mibmap.h"
 #include "memprof_rawprofile.h"
 #include "memprof_stack.h"
 #include "memprof_thread.h"
+#include "profile/MemProfData.inc"
 #include "sanitizer_common/sanitizer_allocator_checks.h"
 #include "sanitizer_common/sanitizer_allocator_interface.h"
 #include "sanitizer_common/sanitizer_allocator_report.h"
 #include <time.h>
 
 namespace __memprof {
+namespace {
+using ::llvm::memprof::MemInfoBlock;
+
+void Print(const MemInfoBlock &M, const u64 id, bool print_terse) {
+  u64 p;
+
+  if (print_terse) {
+    p = M.total_size * 100 / M.alloc_count;
+    Printf("MIB:%llu/%u/%llu.%02llu/%u/%u/", id, M.alloc_count, p / 100,
+           p % 100, M.min_size, M.max_size);
+    p = M.total_access_count * 100 / M.alloc_count;
+    Printf("%llu.%02llu/%llu/%llu/", p / 100, p % 100, M.min_access_count,
+           M.max_access_count);
+    p = M.total_lifetime * 100 / M.alloc_count;
+    Printf("%llu.%02llu/%u/%u/", p / 100, p % 100, M.min_lifetime,
+           M.max_lifetime);
+    Printf("%u/%u/%u/%u\n", M.num_migrated_cpu, M.num_lifetime_overlaps,
+           M.num_same_alloc_cpu, M.num_same_dealloc_cpu);
+  } else {
+    p = M.total_size * 100 / M.alloc_count;
+    Printf("Memory allocation stack id = %llu\n", id);
+    Printf("\talloc_count %u, size (ave/min/max) %llu.%02llu / %u / %u\n",
+           M.alloc_count, p / 100, p % 100, M.min_size, M.max_size);
+    p = M.total_access_count * 100 / M.alloc_count;
+    Printf("\taccess_count (ave/min/max): %llu.%02llu / %llu / %llu\n", p / 100,
+           p % 100, M.min_access_count, M.max_access_count);
+    p = M.total_lifetime * 100 / M.alloc_count;
+    Printf("\tlifetime (ave/min/max): %llu.%02llu / %u / %u\n", p / 100,
+           p % 100, M.min_lifetime, M.max_lifetime);
+    Printf("\tnum migrated: %u, num lifetime overlaps: %u, num same alloc "
+           "cpu: %u, num same dealloc_cpu: %u\n",
+           M.num_migrated_cpu, M.num_lifetime_overlaps, M.num_same_alloc_cpu,
+           M.num_same_dealloc_cpu);
+  }
+}
+} // namespace
 
 static int GetCpuId(void) {
   // _memprof_preinit is called via the preinit_array, which subsequently calls
@@ -240,7 +276,7 @@ struct Allocator {
   static void PrintCallback(const uptr Key, LockedMemInfoBlock *const &Value,
                             void *Arg) {
     SpinMutexLock(&Value->mutex);
-    Value->mib.Print(Key, bool(Arg));
+    Print(Value->mib, Key, bool(Arg));
   }
 
   void FinishAndWrite() {
diff --git a/compiler-rt/lib/memprof/memprof_meminfoblock.h b/compiler-rt/lib/memprof/memprof_meminfoblock.h
deleted file mode 100644 (file)
index 19e4244..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-#ifndef MEMPROF_MEMINFOBLOCK_H_
-#define MEMPROF_MEMINFOBLOCK_H_
-
-#include "memprof_interface_internal.h" // For u32, u64 TODO: Move these out of the internal header.
-#include "sanitizer_common/sanitizer_common.h"
-
-namespace __memprof {
-
-using __sanitizer::Printf;
-
-struct MemInfoBlock {
-  u32 alloc_count;
-  u64 total_access_count, min_access_count, max_access_count;
-  u64 total_size;
-  u32 min_size, max_size;
-  u32 alloc_timestamp, dealloc_timestamp;
-  u64 total_lifetime;
-  u32 min_lifetime, max_lifetime;
-  u32 alloc_cpu_id, dealloc_cpu_id;
-  u32 num_migrated_cpu;
-
-  // Only compared to prior deallocated object currently.
-  u32 num_lifetime_overlaps;
-  u32 num_same_alloc_cpu;
-  u32 num_same_dealloc_cpu;
-
-  u64 data_type_id; // TODO: hash of type name
-
-  MemInfoBlock() : alloc_count(0) {}
-
-  MemInfoBlock(u32 size, u64 access_count, u32 alloc_timestamp,
-               u32 dealloc_timestamp, u32 alloc_cpu, u32 dealloc_cpu)
-      : alloc_count(1), total_access_count(access_count),
-        min_access_count(access_count), max_access_count(access_count),
-        total_size(size), min_size(size), max_size(size),
-        alloc_timestamp(alloc_timestamp), dealloc_timestamp(dealloc_timestamp),
-        total_lifetime(dealloc_timestamp - alloc_timestamp),
-        min_lifetime(total_lifetime), max_lifetime(total_lifetime),
-        alloc_cpu_id(alloc_cpu), dealloc_cpu_id(dealloc_cpu),
-        num_lifetime_overlaps(0), num_same_alloc_cpu(0),
-        num_same_dealloc_cpu(0) {
-    num_migrated_cpu = alloc_cpu_id != dealloc_cpu_id;
-  }
-
-  void Print(u64 id, bool print_terse) const {
-    u64 p;
-
-    if (print_terse) {
-      p = total_size * 100 / alloc_count;
-      Printf("MIB:%llu/%u/%llu.%02llu/%u/%u/", id, alloc_count, p / 100,
-             p % 100, min_size, max_size);
-      p = total_access_count * 100 / alloc_count;
-      Printf("%llu.%02llu/%llu/%llu/", p / 100, p % 100, min_access_count,
-             max_access_count);
-      p = total_lifetime * 100 / alloc_count;
-      Printf("%llu.%02llu/%u/%u/", p / 100, p % 100, min_lifetime,
-             max_lifetime);
-      Printf("%u/%u/%u/%u\n", num_migrated_cpu, num_lifetime_overlaps,
-             num_same_alloc_cpu, num_same_dealloc_cpu);
-    } else {
-      p = total_size * 100 / alloc_count;
-      Printf("Memory allocation stack id = %llu\n", id);
-      Printf("\talloc_count %u, size (ave/min/max) %llu.%02llu / %u / %u\n",
-             alloc_count, p / 100, p % 100, min_size, max_size);
-      p = total_access_count * 100 / alloc_count;
-      Printf("\taccess_count (ave/min/max): %llu.%02llu / %llu / %llu\n",
-             p / 100, p % 100, min_access_count, max_access_count);
-      p = total_lifetime * 100 / alloc_count;
-      Printf("\tlifetime (ave/min/max): %llu.%02llu / %u / %u\n", p / 100,
-             p % 100, min_lifetime, max_lifetime);
-      Printf("\tnum migrated: %u, num lifetime overlaps: %u, num same alloc "
-             "cpu: %u, num same dealloc_cpu: %u\n",
-             num_migrated_cpu, num_lifetime_overlaps, num_same_alloc_cpu,
-             num_same_dealloc_cpu);
-    }
-  }
-
-  static void printHeader() {
-    Printf("MIB:StackID/AllocCount/AveSize/MinSize/MaxSize/AveAccessCount/"
-           "MinAccessCount/MaxAccessCount/AveLifetime/MinLifetime/MaxLifetime/"
-           "NumMigratedCpu/NumLifetimeOverlaps/NumSameAllocCpu/"
-           "NumSameDeallocCpu\n");
-  }
-
-  void Merge(const MemInfoBlock &newMIB) {
-    alloc_count += newMIB.alloc_count;
-
-    total_access_count += newMIB.total_access_count;
-    min_access_count = Min(min_access_count, newMIB.min_access_count);
-    max_access_count = Max(max_access_count, newMIB.max_access_count);
-
-    total_size += newMIB.total_size;
-    min_size = Min(min_size, newMIB.min_size);
-    max_size = Max(max_size, newMIB.max_size);
-
-    total_lifetime += newMIB.total_lifetime;
-    min_lifetime = Min(min_lifetime, newMIB.min_lifetime);
-    max_lifetime = Max(max_lifetime, newMIB.max_lifetime);
-
-    // We know newMIB was deallocated later, so just need to check if it was
-    // allocated before last one deallocated.
-    num_lifetime_overlaps += newMIB.alloc_timestamp < dealloc_timestamp;
-    alloc_timestamp = newMIB.alloc_timestamp;
-    dealloc_timestamp = newMIB.dealloc_timestamp;
-
-    num_same_alloc_cpu += alloc_cpu_id == newMIB.alloc_cpu_id;
-    num_same_dealloc_cpu += dealloc_cpu_id == newMIB.dealloc_cpu_id;
-    alloc_cpu_id = newMIB.alloc_cpu_id;
-    dealloc_cpu_id = newMIB.dealloc_cpu_id;
-  }
-
-} __attribute__((packed));
-
-} // namespace __memprof
-
-#endif // MEMPROF_MEMINFOBLOCK_H_
index 47449cf..32f0796 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "memprof_mibmap.h"
+#include "profile/MemProfData.inc"
 #include "sanitizer_common/sanitizer_allocator_internal.h"
 #include "sanitizer_common/sanitizer_mutex.h"
 
 namespace __memprof {
+using ::llvm::memprof::MemInfoBlock;
 
 void InsertOrMerge(const uptr Id, const MemInfoBlock &Block, MIBMapTy &Map) {
   MIBMapTy::Handle h(&Map, static_cast<uptr>(Id), /*remove=*/false,
index ed5dda1..a7cd420 100644 (file)
@@ -1,7 +1,9 @@
 #ifndef MEMPROF_MIBMAP_H_
 #define MEMPROF_MIBMAP_H_
 
-#include "memprof_meminfoblock.h"
+#include <stdint.h>
+
+#include "profile/MemProfData.inc"
 #include "sanitizer_common/sanitizer_addrhashmap.h"
 #include "sanitizer_common/sanitizer_mutex.h"
 
@@ -9,7 +11,7 @@ namespace __memprof {
 
 struct LockedMemInfoBlock {
   __sanitizer::StaticSpinMutex mutex;
-  MemInfoBlock mib;
+  ::llvm::memprof::MemInfoBlock mib;
 };
 
 // The MIB map stores a mapping from stack ids to MemInfoBlocks.
@@ -17,7 +19,8 @@ typedef __sanitizer::AddrHashMap<LockedMemInfoBlock *, 200003> MIBMapTy;
 
 // Insert a new MemInfoBlock or merge with an existing block identified by the
 // stack id.
-void InsertOrMerge(const uptr Id, const MemInfoBlock &Block, MIBMapTy &Map);
+void InsertOrMerge(const uptr Id, const ::llvm::memprof::MemInfoBlock &Block,
+                   MIBMapTy &Map);
 
 } // namespace __memprof
 
index c4800a6..f065e8d 100644 (file)
@@ -2,7 +2,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "memprof_meminfoblock.h"
 #include "memprof_rawprofile.h"
 #include "profile/MemProfData.inc"
 #include "sanitizer_common/sanitizer_allocator_internal.h"
@@ -16,6 +15,7 @@
 
 namespace __memprof {
 using ::__sanitizer::Vector;
+using ::llvm::memprof::MemInfoBlock;
 using SegmentEntry = ::llvm::memprof::SegmentEntry;
 using Header = ::llvm::memprof::Header;
 
@@ -65,11 +65,8 @@ void SerializeSegmentsToBuffer(MemoryMappingLayoutBase &Layout,
 
   for (Layout.Reset(); Layout.Next(&segment);) {
     if (segment.IsReadable() && segment.IsExecutable()) {
-      SegmentEntry Entry{};
-      Entry.Start = segment.start;
-      Entry.End = segment.end;
-      Entry.Offset = segment.offset;
-      memcpy(Entry.BuildId, segment.uuid, sizeof(segment.uuid));
+      // TODO: Record segment.uuid when it is implemented for Linux-Elf.
+      SegmentEntry Entry(segment.start, segment.end, segment.offset);
       memcpy(Ptr, &Entry, sizeof(SegmentEntry));
       Ptr += sizeof(SegmentEntry);
       NumSegmentsRecorded++;
index 829e183..6181d80 100644 (file)
@@ -3,7 +3,6 @@
 #include <cstdint>
 #include <memory>
 
-#include "memprof/memprof_meminfoblock.h"
 #include "profile/MemProfData.inc"
 #include "sanitizer_common/sanitizer_common.h"
 #include "sanitizer_common/sanitizer_procmaps.h"
 
 namespace {
 
-using ::__memprof::MemInfoBlock;
 using ::__memprof::MIBMapTy;
 using ::__memprof::SerializeToRawProfile;
 using ::__sanitizer::MemoryMappedSegment;
 using ::__sanitizer::MemoryMappingLayoutBase;
 using ::__sanitizer::StackDepotPut;
 using ::__sanitizer::StackTrace;
+using ::llvm::memprof::MemInfoBlock;
 using ::testing::_;
 using ::testing::Action;
 using ::testing::DoAll;
@@ -33,21 +32,21 @@ public:
   MOCK_METHOD(void, Reset, (), (override));
 };
 
-u64 PopulateFakeMap(const MemInfoBlock &FakeMIB, uptr StackPCBegin,
-                    MIBMapTy &FakeMap) {
+uint64_t PopulateFakeMap(const MemInfoBlock &FakeMIB, uint64_t StackPCBegin,
+                         MIBMapTy &FakeMap) {
   constexpr int kSize = 5;
-  uptr array[kSize];
+  uint64_t array[kSize];
   for (int i = 0; i < kSize; i++) {
     array[i] = StackPCBegin + i;
   }
   StackTrace St(array, kSize);
-  u32 Id = StackDepotPut(St);
+  uint32_t Id = StackDepotPut(St);
 
   InsertOrMerge(Id, FakeMIB, FakeMap);
   return Id;
 }
 
-template <class T = u64> T Read(char *&Buffer) {
+template <class T = uint64_t> 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!");
@@ -86,12 +85,12 @@ TEST(MemProf, Basic) {
   FakeMIB.alloc_count = 0x1;
   FakeMIB.total_access_count = 0x2;
 
-  u64 FakeIds[2];
+  uint64_t FakeIds[2];
   FakeIds[0] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/2, FakeMap);
   FakeIds[1] = PopulateFakeMap(FakeMIB, /*StackPCBegin=*/3, FakeMap);
 
   char *Ptr = nullptr;
-  u64 NumBytes = SerializeToRawProfile(FakeMap, Layout, Ptr);
+  uint64_t NumBytes = SerializeToRawProfile(FakeMap, Layout, Ptr);
   const char *Buffer = Ptr;
 
   ASSERT_GT(NumBytes, 0ULL);
@@ -100,10 +99,10 @@ TEST(MemProf, Basic) {
   // Check the header.
   EXPECT_THAT(Read(Ptr), MEMPROF_RAW_MAGIC_64);
   EXPECT_THAT(Read(Ptr), MEMPROF_RAW_VERSION);
-  const u64 TotalSize = Read(Ptr);
-  const u64 SegmentOffset = Read(Ptr);
-  const u64 MIBOffset = Read(Ptr);
-  const u64 StackOffset = Read(Ptr);
+  const uint64_t TotalSize = Read(Ptr);
+  const uint64_t SegmentOffset = Read(Ptr);
+  const uint64_t MIBOffset = Read(Ptr);
+  const uint64_t StackOffset = Read(Ptr);
 
   // ============= Check sizes and padding.
   EXPECT_EQ(TotalSize, NumBytes);
@@ -117,7 +116,7 @@ TEST(MemProf, Basic) {
   EXPECT_EQ(MIBOffset - SegmentOffset, 64ULL);
 
   EXPECT_EQ(MIBOffset, 112ULL);
-  // We expect 2 mib entry, 8b for the count and sizeof(u64) +
+  // We expect 2 mib entry, 8b for the count and sizeof(uint64_t) +
   // sizeof(MemInfoBlock) contains stack id + MeminfoBlock.
   EXPECT_EQ(StackOffset - MIBOffset, 8 + 2 * (8 + sizeof(MemInfoBlock)));
 
@@ -129,19 +128,22 @@ TEST(MemProf, Basic) {
   EXPECT_GE(TotalSize - StackOffset, 8ULL + 2 * (8 + 8 + 5 * 8));
 
   // ============= Check contents.
+  // The Uuid field is not yet populated on Linux-Elf by the sanitizer procmaps
+  // library, so we expect it to be filled with 0 for now.
   unsigned char ExpectedSegmentBytes[64] = {
-      0x01, 0,   0,   0,   0,   0,   0, 0, // Number of entries
-      0x10, 0,   0,   0,   0,   0,   0, 0, // Start
-      0x20, 0,   0,   0,   0,   0,   0, 0, // End
-      0x10, 0,   0,   0,   0,   0,   0, 0, // Offset
-      0x0C, 0x0, 0xF, 0xF, 0xE, 0xE,       // Uuid
+      0x01, 0, 0, 0, 0, 0, 0, 0, // Number of entries
+      0x10, 0, 0, 0, 0, 0, 0, 0, // Start
+      0x20, 0, 0, 0, 0, 0, 0, 0, // End
+      0x10, 0, 0, 0, 0, 0, 0, 0, // Offset
+      0x0,                       // Uuid
   };
   EXPECT_EQ(memcmp(Buffer + SegmentOffset, ExpectedSegmentBytes, 64), 0);
 
   // Check that the number of entries is 2.
-  EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + MIBOffset), 2ULL);
+  EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset), 2ULL);
   // Check that stack id is set.
-  EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + MIBOffset + 8), FakeIds[0]);
+  EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + MIBOffset + 8),
+            FakeIds[0]);
 
   // Only check a few fields of the first MemInfoBlock.
   unsigned char ExpectedMIBBytes[sizeof(MemInfoBlock)] = {
@@ -159,9 +161,9 @@ TEST(MemProf, Basic) {
             0);
 
   // Check that the number of entries is 2.
-  EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + StackOffset), 2ULL);
+  EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset), 2ULL);
   // Check that the 1st stack id is set.
-  EXPECT_EQ(*reinterpret_cast<const u64 *>(Buffer + StackOffset + 8),
+  EXPECT_EQ(*reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8),
             FakeIds[0]);
   // Contents are num pcs, value of each pc - 1.
   unsigned char ExpectedStackBytes[2][6 * 8] = {
@@ -184,7 +186,7 @@ TEST(MemProf, Basic) {
 
   // Check that the 2nd stack id is set.
   EXPECT_EQ(
-      *reinterpret_cast<const u64 *>(Buffer + StackOffset + 8 + 6 * 8 + 8),
+      *reinterpret_cast<const uint64_t *>(Buffer + StackOffset + 8 + 6 * 8 + 8),
       FakeIds[1]);
 
   EXPECT_EQ(memcmp(Buffer + StackOffset + 16 + 6 * 8 + 8, ExpectedStackBytes[1],
index f2cb373..ff22a69 100644 (file)
  *
 \*===----------------------------------------------------------------------===*/
 
-
 #ifdef _MSC_VER
-#define PACKED(__decl__) __pragma(pack(push,1)) __decl__ __pragma(pack(pop))
+#define PACKED(...) __pragma(pack(push,1)) __VA_ARGS__ __pragma(pack(pop))
 #else
-#define PACKED(__decl__) __decl__ __attribute__((__packed__))
+#define PACKED(...) __VA_ARGS__ __attribute__((__packed__))
 #endif
 
 // A 64-bit magic number to uniquely identify the raw binary memprof profile file.
@@ -47,14 +46,106 @@ PACKED(struct Header {
   uint64_t StackOffset;
 });
 
+
 // A struct describing the information necessary to describe a /proc/maps
 // segment entry for a particular binary/library identified by its build id.
 PACKED(struct SegmentEntry {
   uint64_t Start;
   uint64_t End;
   uint64_t Offset;
-  uint8_t BuildId[32];
+  // This field is unused until sanitizer procmaps support for build ids for
+  // Linux-Elf is implemented.
+  uint8_t BuildId[32] = {0};
+
+  SegmentEntry(uint64_t S, uint64_t E, uint64_t O) :
+    Start(S), End(E), Offset(O) {}
+
+  SegmentEntry(const SegmentEntry& S) {
+    Start = S.Start;
+    End = S.End;
+    Offset = S.Offset;
+  }
+
+  SegmentEntry& operator=(const SegmentEntry& S) {
+    Start = S.Start;
+    End = S.End;
+    Offset = S.Offset;
+    return *this;
+  }
+
+  bool operator==(const SegmentEntry& S) const {
+    return Start == S.Start &&
+           End == S.End &&
+           Offset == S.Offset;
+  }
 });
+
+// A struct representing the heap allocation characteristics of a particular
+// runtime context. This struct is shared between the compiler-rt runtime and
+// the raw profile reader. The indexed format uses a separate, self-describing
+// backwards compatible format.
+PACKED(struct MemInfoBlock {
+  uint32_t alloc_count;
+  uint64_t total_access_count, min_access_count, max_access_count;
+  uint64_t total_size;
+  uint32_t min_size, max_size;
+  uint32_t alloc_timestamp, dealloc_timestamp;
+  uint64_t total_lifetime;
+  uint32_t min_lifetime, max_lifetime;
+  uint32_t alloc_cpu_id, dealloc_cpu_id;
+  uint32_t num_migrated_cpu;
+
+  // Only compared to prior deallocated object currently.
+  uint32_t num_lifetime_overlaps;
+  uint32_t num_same_alloc_cpu;
+  uint32_t num_same_dealloc_cpu;
+
+  uint64_t data_type_id; // TODO: hash of type name
+
+  MemInfoBlock() : alloc_count(0) {}
+
+  MemInfoBlock(uint32_t size, uint64_t access_count, uint32_t alloc_timestamp,
+               uint32_t dealloc_timestamp, uint32_t alloc_cpu, uint32_t dealloc_cpu)
+      : alloc_count(1), total_access_count(access_count),
+        min_access_count(access_count), max_access_count(access_count),
+        total_size(size), min_size(size), max_size(size),
+        alloc_timestamp(alloc_timestamp), dealloc_timestamp(dealloc_timestamp),
+        total_lifetime(dealloc_timestamp - alloc_timestamp),
+        min_lifetime(total_lifetime), max_lifetime(total_lifetime),
+        alloc_cpu_id(alloc_cpu), dealloc_cpu_id(dealloc_cpu),
+        num_lifetime_overlaps(0), num_same_alloc_cpu(0),
+        num_same_dealloc_cpu(0) {
+    num_migrated_cpu = alloc_cpu_id != dealloc_cpu_id;
+  }
+
+  void Merge(const MemInfoBlock &newMIB) {
+    alloc_count += newMIB.alloc_count;
+
+    total_access_count += newMIB.total_access_count;
+    min_access_count = newMIB.min_access_count < min_access_count ? newMIB.min_access_count : min_access_count;
+    max_access_count = newMIB.max_access_count < max_access_count ? newMIB.max_access_count : max_access_count;
+
+    total_size += newMIB.total_size;
+    min_size = newMIB.min_size < min_size ? newMIB.min_size : min_size;
+    max_size = newMIB.max_size < max_size ? newMIB.max_size : max_size;
+
+    total_lifetime += newMIB.total_lifetime;
+    min_lifetime = newMIB.min_lifetime < min_lifetime ? newMIB.min_lifetime : min_lifetime;
+    max_lifetime = newMIB.max_lifetime > max_lifetime ? newMIB.max_lifetime : max_lifetime;
+
+    // We know newMIB was deallocated later, so just need to check if it was
+    // allocated before last one deallocated.
+    num_lifetime_overlaps += newMIB.alloc_timestamp < dealloc_timestamp;
+    alloc_timestamp = newMIB.alloc_timestamp;
+    dealloc_timestamp = newMIB.dealloc_timestamp;
+
+    num_same_alloc_cpu += alloc_cpu_id == newMIB.alloc_cpu_id;
+    num_same_dealloc_cpu += dealloc_cpu_id == newMIB.dealloc_cpu_id;
+    alloc_cpu_id = newMIB.alloc_cpu_id;
+    dealloc_cpu_id = newMIB.dealloc_cpu_id;
+  }
+});
+
 } // namespace memprof
 } // namespace llvm