forked from OSchip/llvm-project
[memprof] Move the meminfo block struct to MemProfData.inc.
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
This commit is contained in:
parent
702c0cfa07
commit
8306968b59
|
@ -20,11 +20,10 @@
|
|||
*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
|
||||
#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
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
|
||||
#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"
|
||||
|
@ -36,6 +36,42 @@
|
|||
#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() {
|
||||
|
|
|
@ -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_
|
|
@ -11,10 +11,12 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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"
|
||||
|
@ -14,13 +13,13 @@
|
|||
|
||||
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],
|
||||
|
|
|
@ -20,11 +20,10 @@
|
|||
*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
|
||||
#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
|
||||
|
||||
|
|
Loading…
Reference in New Issue