Path: blob/main/contrib/llvm-project/compiler-rt/lib/memprof/memprof_rawprofile.cpp
35236 views
#include <stdint.h>1#include <stdlib.h>2#include <string.h>34#include "memprof_rawprofile.h"5#include "profile/MemProfData.inc"6#include "sanitizer_common/sanitizer_allocator_internal.h"7#include "sanitizer_common/sanitizer_array_ref.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"1516namespace __memprof {17using ::__sanitizer::Vector;18using ::llvm::memprof::MemInfoBlock;19using SegmentEntry = ::llvm::memprof::SegmentEntry;20using Header = ::llvm::memprof::Header;2122namespace {23template <class T> char *WriteBytes(const T &Pod, char *Buffer) {24*(T *)Buffer = Pod;25return Buffer + sizeof(T);26}2728void RecordStackId(const uptr Key, UNUSED LockedMemInfoBlock *const &MIB,29void *Arg) {30// No need to touch the MIB value here since we are only recording the key.31auto *StackIds = reinterpret_cast<Vector<u64> *>(Arg);32StackIds->PushBack(Key);33}34} // namespace3536u64 SegmentSizeBytes(ArrayRef<LoadedModule> Modules) {37u64 NumSegmentsToRecord = 0;38for (const auto &Module : Modules) {39for (const auto &Segment : Module.ranges()) {40if (Segment.executable)41NumSegmentsToRecord++;42}43}4445return sizeof(u64) // A header which stores the number of records.46+ sizeof(SegmentEntry) * NumSegmentsToRecord;47}4849// The segment section uses the following format:50// ---------- Segment Info51// Num Entries52// ---------- Segment Entry53// Start54// End55// Offset56// UuidSize57// Uuid 32B58// ----------59// ...60void SerializeSegmentsToBuffer(ArrayRef<LoadedModule> Modules,61const u64 ExpectedNumBytes, char *&Buffer) {62char *Ptr = Buffer;63// Reserve space for the final count.64Ptr += sizeof(u64);6566u64 NumSegmentsRecorded = 0;6768for (const auto &Module : Modules) {69for (const auto &Segment : Module.ranges()) {70if (Segment.executable) {71SegmentEntry Entry(Segment.beg, Segment.end, Module.base_address());72CHECK(Module.uuid_size() <= MEMPROF_BUILDID_MAX_SIZE);73Entry.BuildIdSize = Module.uuid_size();74memcpy(Entry.BuildId, Module.uuid(), Module.uuid_size());75memcpy(Ptr, &Entry, sizeof(SegmentEntry));76Ptr += sizeof(SegmentEntry);77NumSegmentsRecorded++;78}79}80}81// Store the number of segments we recorded in the space we reserved.82*((u64 *)Buffer) = NumSegmentsRecorded;83CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&84"Expected num bytes != actual bytes written");85}8687u64 StackSizeBytes(const Vector<u64> &StackIds) {88u64 NumBytesToWrite = sizeof(u64);8990const u64 NumIds = StackIds.Size();91for (unsigned k = 0; k < NumIds; ++k) {92const u64 Id = StackIds[k];93// One entry for the id and then one more for the number of stack pcs.94NumBytesToWrite += 2 * sizeof(u64);95const StackTrace St = StackDepotGet(Id);9697CHECK(St.trace != nullptr && St.size > 0 && "Empty stack trace");98for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) {99NumBytesToWrite += sizeof(u64);100}101}102return NumBytesToWrite;103}104105// The stack info section uses the following format:106//107// ---------- Stack Info108// Num Entries109// ---------- Stack Entry110// Num Stacks111// PC1112// PC2113// ...114// ----------115void SerializeStackToBuffer(const Vector<u64> &StackIds,116const u64 ExpectedNumBytes, char *&Buffer) {117const u64 NumIds = StackIds.Size();118char *Ptr = Buffer;119Ptr = WriteBytes(static_cast<u64>(NumIds), Ptr);120121for (unsigned k = 0; k < NumIds; ++k) {122const u64 Id = StackIds[k];123Ptr = WriteBytes(Id, Ptr);124Ptr += sizeof(u64); // Bump it by u64, we will fill this in later.125u64 Count = 0;126const StackTrace St = StackDepotGet(Id);127for (uptr i = 0; i < St.size && St.trace[i] != 0; i++) {128// PCs in stack traces are actually the return addresses, that is,129// addresses of the next instructions after the call.130uptr pc = StackTrace::GetPreviousInstructionPc(St.trace[i]);131Ptr = WriteBytes(static_cast<u64>(pc), Ptr);132++Count;133}134// Store the count in the space we reserved earlier.135*(u64 *)(Ptr - (Count + 1) * sizeof(u64)) = Count;136}137138CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&139"Expected num bytes != actual bytes written");140}141142// The MIB section has the following format:143// ---------- MIB Info144// Num Entries145// ---------- MIB Entry 0146// Alloc Count147// ...148// ---- AccessHistogram Entry 0149// ...150// ---- AccessHistogram Entry AccessHistogramSize - 1151// ---------- MIB Entry 1152// Alloc Count153// ...154// ---- AccessHistogram Entry 0155// ...156// ---- AccessHistogram Entry AccessHistogramSize - 1157// ----------158void SerializeMIBInfoToBuffer(MIBMapTy &MIBMap, const Vector<u64> &StackIds,159const u64 ExpectedNumBytes, char *&Buffer) {160char *Ptr = Buffer;161const u64 NumEntries = StackIds.Size();162Ptr = WriteBytes(NumEntries, Ptr);163for (u64 i = 0; i < NumEntries; i++) {164const u64 Key = StackIds[i];165MIBMapTy::Handle h(&MIBMap, Key, /*remove=*/true, /*create=*/false);166CHECK(h.exists());167Ptr = WriteBytes(Key, Ptr);168// FIXME: We unnecessarily serialize the AccessHistogram pointer. Adding a169// serialization schema will fix this issue. See also FIXME in170// deserialization.171Ptr = WriteBytes((*h)->mib, Ptr);172for (u64 j = 0; j < (*h)->mib.AccessHistogramSize; ++j) {173u64 HistogramEntry = ((u64 *)((*h)->mib.AccessHistogram))[j];174Ptr = WriteBytes(HistogramEntry, Ptr);175}176if ((*h)->mib.AccessHistogramSize > 0) {177InternalFree((void *)((*h)->mib.AccessHistogram));178}179}180CHECK(ExpectedNumBytes >= static_cast<u64>(Ptr - Buffer) &&181"Expected num bytes != actual bytes written");182}183184// Format185// ---------- Header186// Magic187// Version188// Total Size189// Segment Offset190// MIB Info Offset191// Stack Offset192// ---------- Segment Info193// Num Entries194// ---------- Segment Entry195// Start196// End197// Offset198// BuildID 32B199// ----------200// ...201// ----------202// Optional Padding Bytes203// ---------- MIB Info204// Num Entries205// ---------- MIB Entry206// Alloc Count207// ...208// ---- AccessHistogram Entry 0209// ...210// ---- AccessHistogram Entry AccessHistogramSize - 1211// ---------- MIB Entry 1212// Alloc Count213// ...214// ---- AccessHistogram Entry 0215// ...216// ---- AccessHistogram Entry AccessHistogramSize - 1217// Optional Padding Bytes218// ---------- Stack Info219// Num Entries220// ---------- Stack Entry221// Num Stacks222// PC1223// PC2224// ...225// ----------226// Optional Padding Bytes227// ...228u64 SerializeToRawProfile(MIBMapTy &MIBMap, ArrayRef<LoadedModule> Modules,229char *&Buffer) {230// Each section size is rounded up to 8b since the first entry in each section231// is a u64 which holds the number of entries in the section by convention.232const u64 NumSegmentBytes = RoundUpTo(SegmentSizeBytes(Modules), 8);233234Vector<u64> StackIds;235MIBMap.ForEach(RecordStackId, reinterpret_cast<void *>(&StackIds));236// The first 8b are for the total number of MIB records. Each MIB record is237// preceded by a 8b stack id which is associated with stack frames in the next238// section.239const u64 NumMIBInfoBytes = RoundUpTo(240sizeof(u64) + StackIds.Size() * (sizeof(u64) + sizeof(MemInfoBlock)), 8);241242// Get Number of AccessHistogram entries in total243u64 TotalAccessHistogramEntries = 0;244MIBMap.ForEach(245[](const uptr Key, UNUSED LockedMemInfoBlock *const &MIB, void *Arg) {246u64 *TotalAccessHistogramEntries = (u64 *)Arg;247*TotalAccessHistogramEntries += MIB->mib.AccessHistogramSize;248},249reinterpret_cast<void *>(&TotalAccessHistogramEntries));250const u64 NumHistogramBytes =251RoundUpTo(TotalAccessHistogramEntries * sizeof(uint64_t), 8);252253const u64 NumStackBytes = RoundUpTo(StackSizeBytes(StackIds), 8);254255// Ensure that the profile is 8b aligned. We allow for some optional padding256// at the end so that any subsequent profile serialized to the same file does257// not incur unaligned accesses.258const u64 TotalSizeBytes =259RoundUpTo(sizeof(Header) + NumSegmentBytes + NumStackBytes +260NumMIBInfoBytes + NumHistogramBytes,2618);262263// Allocate the memory for the entire buffer incl. info blocks.264Buffer = (char *)InternalAlloc(TotalSizeBytes);265char *Ptr = Buffer;266267Header header{MEMPROF_RAW_MAGIC_64,268MEMPROF_RAW_VERSION,269static_cast<u64>(TotalSizeBytes),270sizeof(Header),271sizeof(Header) + NumSegmentBytes,272sizeof(Header) + NumSegmentBytes + NumMIBInfoBytes +273NumHistogramBytes};274Ptr = WriteBytes(header, Ptr);275276SerializeSegmentsToBuffer(Modules, NumSegmentBytes, Ptr);277Ptr += NumSegmentBytes;278279SerializeMIBInfoToBuffer(MIBMap, StackIds,280NumMIBInfoBytes + NumHistogramBytes, Ptr);281Ptr += NumMIBInfoBytes + NumHistogramBytes;282283SerializeStackToBuffer(StackIds, NumStackBytes, Ptr);284285return TotalSizeBytes;286}287288} // namespace __memprof289290291