45#define DEBUG_TYPE "memprof"
50template <
class T = u
int64_t>
inline T alignedRead(
const char *
Ptr) {
51 static_assert(std::is_integral_v<T>,
"Not an integral type");
52 assert(
reinterpret_cast<size_t>(
Ptr) %
sizeof(
T) == 0 &&
"Unaligned Read");
53 return *
reinterpret_cast<const T *
>(
Ptr);
56Error checkBuffer(
const MemoryBuffer &Buffer) {
60 if (Buffer.getBufferSize() == 0)
63 if (Buffer.getBufferSize() <
sizeof(Header)) {
70 const char *Next = Buffer.getBufferStart();
71 while (Next < Buffer.getBufferEnd()) {
72 const auto *
H =
reinterpret_cast<const Header *
>(Next);
75 bool IsSupported =
false;
76 for (
auto SupportedVersion : MEMPROF_RAW_SUPPORTED_VERSIONS) {
77 if (
H->Version == SupportedVersion)
84 TotalSize +=
H->TotalSize;
88 if (Buffer.getBufferSize() != TotalSize) {
95 using namespace support;
98 endian::readNext<uint64_t, llvm::endianness::little>(
Ptr);
101 Items.
push_back(*
reinterpret_cast<const SegmentEntry *
>(
102 Ptr +
I *
sizeof(SegmentEntry)));
108readMemInfoBlocksV3(
const char *
Ptr) {
109 using namespace support;
112 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(
Ptr);
117 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(
Ptr);
125 MemInfoBlock MIB = *
reinterpret_cast<const MemInfoBlock *
>(
Ptr);
127 MIB.AccessHistogramSize = 0;
128 MIB.AccessHistogram = 0;
132 Ptr += MEMPROF_V3_MIB_SIZE;
138readMemInfoBlocksCommon(
const char *
Ptr,
bool IsHistogramEncoded =
false) {
139 using namespace support;
142 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(
Ptr);
147 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(
Ptr);
150#define READ_MIB_FIELD(FIELD) \
151 MIB.FIELD = endian::readNext<decltype(MIB.FIELD), llvm::endianness::little, \
183 if (MIB.AccessHistogramSize > 0) {
185 MIB.AccessHistogram =
186 (uintptr_t)malloc(MIB.AccessHistogramSize *
sizeof(
uint64_t));
187 for (
uint64_t J = 0; J < MIB.AccessHistogramSize; J++) {
188 if (!IsHistogramEncoded) {
189 ((
uint64_t *)MIB.AccessHistogram)[J] =
190 endian::readNext<uint64_t, llvm::endianness::little, unaligned>(
195 endian::readNext<uint16_t, llvm::endianness::little, unaligned>(
197 ((
uint64_t *)MIB.AccessHistogram)[J] = decodeHistogramCount(Val);
207readMemInfoBlocksV4(
const char *
Ptr) {
208 return readMemInfoBlocksCommon(
Ptr);
212readMemInfoBlocksV5(
const char *
Ptr) {
213 return readMemInfoBlocksCommon(
Ptr,
true);
217 using namespace support;
220 endian::readNext<uint64_t, llvm::endianness::little>(
Ptr);
225 endian::readNext<uint64_t, llvm::endianness::little>(
Ptr);
227 endian::readNext<uint64_t, llvm::endianness::little>(
Ptr);
229 SmallVector<uint64_t> CallStack;
230 CallStack.reserve(NumPCs);
231 for (
uint64_t J = 0; J < NumPCs; J++) {
233 endian::readNext<uint64_t, llvm::endianness::little>(
Ptr));
236 Items[StackId] = CallStack;
245 for (
const auto &[Id, Stack] :
From) {
246 auto [It, Inserted] = To.try_emplace(Id, Stack);
248 if (!Inserted && Stack != It->second)
254Error report(Error
E,
const StringRef
Context) {
259bool isRuntimePath(
const StringRef Path) {
263 return Filename ==
"memprof_malloc_linux.cpp" ||
264 Filename ==
"memprof_interceptors.cpp" ||
265 Filename ==
"memprof_new_delete.cpp";
268std::string getBuildIdString(
const SegmentEntry &Entry) {
270 if (Entry.BuildIdSize == 0)
274 raw_string_ostream
OS(Str);
275 for (
size_t I = 0;
I < Entry.BuildIdSize;
I++) {
282Expected<std::unique_ptr<RawMemProfReader>>
286 if (std::error_code EC = BufferOr.getError())
289 std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
290 return create(std::move(Buffer), ProfiledBinary, KeepName);
295 const StringRef ProfiledBinary,
bool KeepName) {
296 if (
Error E = checkBuffer(*Buffer))
297 return report(std::move(
E), Buffer->getBufferIdentifier());
299 if (ProfiledBinary.
empty()) {
301 const std::vector<std::string> BuildIds =
peekBuildIds(Buffer.get());
302 std::string ErrorMessage(
303 R
"(Path to profiled binary is empty, expected binary with one of the following build ids:
305 for (
const auto &Id : BuildIds) {
306 ErrorMessage +=
"\n BuildId: ";
316 return report(BinaryOr.takeError(), ProfiledBinary);
320 std::unique_ptr<RawMemProfReader> Reader(
322 if (
Error E = Reader->initialize(std::move(Buffer))) {
325 return std::move(Reader);
331 for (
auto &[
_, MIB] : CallstackProfileData) {
332 if (MemprofRawVersion >= 4ULL && MIB.AccessHistogramSize > 0) {
333 free((
void *)MIB.AccessHistogram);
343 std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
353 return Magic == MEMPROF_RAW_MAGIC_64;
358 uint64_t NumAllocFunctions = 0, NumMibInfo = 0;
360 MemProfSumBuilder.addRecord(KV.second);
361 const size_t NumAllocSites = KV.second.AllocSites.size();
362 if (NumAllocSites > 0) {
364 NumMibInfo += NumAllocSites;
369 auto MemProfSum = MemProfSumBuilder.
getSummary();
370 MemProfSum->printSummaryYaml(
OS);
372 OS <<
"MemprofProfile:\n";
374 OS <<
" Version: " << MemprofRawVersion <<
"\n";
375 OS <<
" NumSegments: " << SegmentInfo.
size() <<
"\n";
376 OS <<
" NumMibInfo: " << NumMibInfo <<
"\n";
377 OS <<
" NumAllocFunctions: " << NumAllocFunctions <<
"\n";
378 OS <<
" NumStackOffsets: " << StackMap.
size() <<
"\n";
380 OS <<
" Segments:\n";
381 for (
const auto &Entry : SegmentInfo) {
383 OS <<
" BuildId: " << getBuildIdString(Entry) <<
"\n";
384 OS <<
" Start: 0x" << llvm::utohexstr(Entry.Start) <<
"\n";
385 OS <<
" End: 0x" << llvm::utohexstr(Entry.End) <<
"\n";
386 OS <<
" Offset: 0x" << llvm::utohexstr(Entry.Offset) <<
"\n";
390 for (
const auto &[GUID,
Record] : *
this) {
392 OS <<
" FunctionGUID: " << GUID <<
"\n";
397Error RawMemProfReader::initialize(std::unique_ptr<MemoryBuffer> DataBuffer) {
398 const StringRef FileName = Binary.getBinary()->getFileName();
400 auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Binary.getBinary());
402 return report(make_error<StringError>(
Twine(
"Not an ELF file: "),
410 auto *Elf64LEObject = llvm::cast<llvm::object::ELF64LEObjectFile>(ElfObject);
412 auto PHdrsOr = ElfFile.program_headers();
415 make_error<StringError>(
Twine(
"Could not read program headers: "),
419 int NumExecutableSegments = 0;
420 for (
const auto &Phdr : *PHdrsOr) {
425 if (++NumExecutableSegments > 1) {
427 make_error<StringError>(
428 "Expect only one executable load segment in the binary",
437 PreferredTextSegmentAddress = Phdr.p_vaddr;
438 assert(Phdr.p_vaddr == (Phdr.p_vaddr & ~(0x1000 - 1U)) &&
439 "Expect p_vaddr to always be page aligned");
440 assert(Phdr.p_offset == 0 &&
"Expect p_offset = 0 for symbolization.");
445 auto Triple = ElfObject->makeTriple();
447 return report(make_error<StringError>(Twine(
"Unsupported target: ") +
448 Triple.getArchName(),
453 if (Error
E = readRawProfile(std::move(DataBuffer)))
456 if (Error
E = setupForSymbolization())
459 auto *
Object = cast<object::ObjectFile>(Binary.getBinary());
464 Object, std::move(Context),
false);
466 return report(SOFOr.takeError(), FileName);
467 auto Symbolizer = std::move(SOFOr.get());
473 if (Error
E = symbolizeAndFilterStackFrames(std::move(Symbolizer)))
476 return mapRawProfileToRecords();
479Error RawMemProfReader::setupForSymbolization() {
480 auto *
Object = cast<object::ObjectFile>(Binary.getBinary());
482 if (BinaryId.empty())
483 return make_error<StringError>(Twine(
"No build id found in binary ") +
484 Binary.getBinary()->getFileName(),
488 for (
const auto &Entry : SegmentInfo) {
490 if (BinaryId == SegmentId) {
493 if (++NumMatched > 1) {
494 return make_error<StringError>(
495 "We expect only one executable segment in the profiled binary",
498 ProfiledTextSegmentStart =
Entry.Start;
499 ProfiledTextSegmentEnd =
Entry.End;
503 return make_error<StringError>(
504 Twine(
"No matching executable segments found in binary ") +
505 Binary.getBinary()->getFileName(),
507 assert((PreferredTextSegmentAddress == 0 ||
508 (PreferredTextSegmentAddress == ProfiledTextSegmentStart)) &&
509 "Expect text segment address to be 0 or equal to profiled text "
514Error RawMemProfReader::mapRawProfileToRecords() {
520 PerFunctionCallSites;
524 for (
const auto &[StackId, MIB] : CallstackProfileData) {
525 auto It = StackMap.
find(StackId);
526 if (It == StackMap.
end())
527 return make_error<InstrProfError>(
529 "memprof callstack record does not contain id: " + Twine(StackId));
533 Callstack.
reserve(It->getSecond().size());
536 for (
size_t I = 0;
I < Addresses.
size();
I++) {
539 "Address not found in SymbolizedFrame map");
540 const SmallVector<FrameId> &Frames = SymbolizedFrame[
Address];
543 "The last frame should not be inlined");
548 for (
size_t J = 0; J < Frames.size(); J++) {
549 if (
I == 0 && J == 0)
560 Callstack.
append(Frames.begin(), Frames.end());
567 for (
size_t I = 0; ;
I++) {
570 Record.AllocSites.emplace_back(CSId, MIB);
572 if (!
F.IsInlineFrame)
578 for (
const auto &[Id, Locs] : PerFunctionCallSites) {
582 for (LocationPtr Loc : Locs)
589Error RawMemProfReader::symbolizeAndFilterStackFrames(
590 std::unique_ptr<llvm::symbolize::SymbolizableModule> Symbolizer) {
593 DILineInfoSpecifier::FileLineInfoKind::RawValue,
594 DILineInfoSpecifier::FunctionNameKind::LinkageName);
602 for (
auto &Entry : StackMap) {
607 if (SymbolizedFrame.count(VAddr) > 0 ||
611 Expected<DIInliningInfo> DIOr = Symbolizer->symbolizeInlinedCode(
612 getModuleOffset(VAddr), Specifier,
false);
614 return DIOr.takeError();
615 DIInliningInfo DI = DIOr.get();
619 isRuntimePath(DI.getFrame(0).FileName)) {
620 AllVAddrsToDiscard.
insert(VAddr);
624 for (
size_t I = 0, NumFrames = DI.getNumberOfFrames();
I < NumFrames;
626 const auto &DIFrame = DI.getFrame(
I);
628 const Frame
F(
Guid, DIFrame.Line - DIFrame.StartLine, DIFrame.Column,
635 if (KeepSymbolName) {
636 StringRef CanonicalName =
638 DIFrame.FunctionName);
639 GuidToSymbolName.
insert({
Guid, CanonicalName.str()});
646 auto &CallStack =
Entry.getSecond();
650 if (CallStack.empty())
655 for (
const uint64_t Id : EntriesToErase) {
657 if (
auto It = CallstackProfileData.find(Id);
658 It != CallstackProfileData.end()) {
659 if (It->second.AccessHistogramSize > 0)
660 free((
void *)It->second.AccessHistogram);
661 CallstackProfileData.erase(It);
665 if (StackMap.empty())
666 return make_error<InstrProfError>(
668 "no entries in callstack map after symbolization");
673std::vector<std::string>
685 while (Next < DataBuffer->getBufferEnd()) {
686 const auto *Header =
reinterpret_cast<const memprof::Header *
>(Next);
689 readSegmentEntries(Next + Header->SegmentOffset);
691 for (
const auto &Entry : Entries)
692 BuildIds.
insert(getBuildIdString(Entry));
694 Next += Header->TotalSize;
703RawMemProfReader::readMemInfoBlocks(
const char *
Ptr) {
704 if (MemprofRawVersion == 3ULL)
705 return readMemInfoBlocksV3(
Ptr);
706 if (MemprofRawVersion == 4ULL)
707 return readMemInfoBlocksV4(
Ptr);
708 if (MemprofRawVersion == 5ULL)
709 return readMemInfoBlocksV5(
Ptr);
711 "Panic: Unsupported version number when reading MemInfoBlocks");
714Error RawMemProfReader::readRawProfile(
715 std::unique_ptr<MemoryBuffer> DataBuffer) {
716 const char *Next = DataBuffer->getBufferStart();
718 while (Next < DataBuffer->getBufferEnd()) {
719 const auto *Header =
reinterpret_cast<const memprof::Header *
>(Next);
723 MemprofRawVersion = Header->Version;
728 readSegmentEntries(Next + Header->SegmentOffset);
729 if (!SegmentInfo.empty() && SegmentInfo != Entries) {
733 return make_error<InstrProfError>(
735 "memprof raw profile has different segment information");
737 SegmentInfo.assign(Entries.begin(), Entries.end());
742 for (
const auto &[Id, MIB] : readMemInfoBlocks(Next + Header->MIBOffset)) {
743 if (CallstackProfileData.count(Id)) {
745 if (MemprofRawVersion >= 4ULL &&
746 (CallstackProfileData[Id].AccessHistogramSize > 0 ||
747 MIB.AccessHistogramSize > 0)) {
748 uintptr_t ShorterHistogram;
749 if (CallstackProfileData[Id].AccessHistogramSize >
750 MIB.AccessHistogramSize)
751 ShorterHistogram = MIB.AccessHistogram;
753 ShorterHistogram = CallstackProfileData[Id].AccessHistogram;
754 CallstackProfileData[Id].Merge(MIB);
755 free((
void *)ShorterHistogram);
757 CallstackProfileData[
Id].Merge(MIB);
760 CallstackProfileData[
Id] = MIB;
766 const CallStackMap CSM = readStackInfo(Next + Header->StackOffset);
767 if (StackMap.empty()) {
770 if (mergeStackMap(CSM, StackMap))
771 return make_error<InstrProfError>(
773 "memprof raw profile got different call stack for same id");
776 Next += Header->TotalSize;
782object::SectionedAddress
783RawMemProfReader::getModuleOffset(
const uint64_t VirtualAddress) {
784 if (VirtualAddress > ProfiledTextSegmentStart &&
785 VirtualAddress <= ProfiledTextSegmentEnd) {
791 VirtualAddress + PreferredTextSegmentAddress - ProfiledTextSegmentStart;
792 return object::SectionedAddress{AdjustedAddress};
797 return object::SectionedAddress{VirtualAddress};
806 auto IdToFrameCallback = [
this](
const FrameId Id) {
808 if (!this->KeepSymbolName)
810 auto Iter = this->GuidToSymbolName.
find(
F.Function);
812 F.SymbolName = std::make_unique<std::string>(
Iter->getSecond());
821 if (std::error_code EC = BufferOr.getError())
824 std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
825 return create(std::move(Buffer));
830 auto Reader = std::make_unique<YAMLMemProfReader>();
831 Reader->parse(Buffer->getBuffer());
832 return std::move(Reader);
840 std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
850 yaml::Input Yin(YAMLData);
875 for (
const auto &CallSite :
Record.CallSites) {
877 IndexedRecord.
CallSites.emplace_back(CSId, CallSite.CalleeGuids);
886 auto ToSymHandleRef =
888 if (std::holds_alternative<std::string>(Handle))
889 return StringRef(std::get<std::string>(Handle));
890 return std::get<uint64_t>(Handle);
893 auto DataAccessProfileData = std::make_unique<memprof::DataAccessProfData>();
895 if (
Error E = DataAccessProfileData->setDataAccessProfile(
901 if (
Error E = DataAccessProfileData->addKnownSymbolWithoutSamples(Hash))
904 for (
const std::string &
Sym :
906 if (
Error E = DataAccessProfileData->addKnownSymbolWithoutSamples(
Sym))
909 setDataAccessProfileData(std::move(DataAccessProfileData));
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
BlockVerifier::State From
This file declares a library for handling Build IDs and using them to find debug info.
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file defines the DenseMap class.
#define READ_MIB_FIELD(FIELD)
This file implements a set that has insertion order iteration characteristics.
This file defines the SmallSet class.
This file defines the SmallVector class.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
size_t size() const
size - Get the array size.
static std::unique_ptr< DWARFContext > create(const object::ObjectFile &Obj, ProcessDebugRelocations RelocAction=ProcessDebugRelocations::Process, const LoadedObjectInfo *L=nullptr, std::string DWPName="", std::function< void(Error)> RecoverableErrorHandler=WithColor::defaultErrorHandler, std::function< void(Error)> WarningHandler=WithColor::defaultWarningHandler, bool ThreadSafe=false)
iterator find(const_arg_type_t< KeyT > Val)
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Implements a dense probed hash-table based set.
Lightweight error class with error context and mandatory checking.
static ErrorSuccess success()
Create a success value.
Tagged union holding either a T or a Error.
uint64_t GUID
Declare a type to represent a global unique identifier for a global value.
This class implements a map that also provides access to all stored values in a deterministic order.
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
This interface provides simple read-only access to a block of memory, and provides simple methods for...
size_t getBufferSize() const
StringRef getBuffer() const
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFileOrSTDIN(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, or open stdin if the Filename is "-".
const char * getBufferStart() const
A vector that has set insertion semantics.
Vector takeVector()
Clear the SetVector and return the underlying vector.
bool insert(const value_type &X)
Insert a new element into the SetVector.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
void reserve(size_type N)
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
constexpr bool empty() const
empty - Check if the string is empty.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
std::pair< iterator, bool > insert(const ValueT &V)
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
Helper class to iterate through stack ids in both metadata (memprof MIB and callsite) and the corresp...
const Frame & idToFrame(const FrameId Id) const
IndexedMemProfData MemProfData
virtual Error readNextRecord(GuidMemProfRecordPair &GuidRecord, std::function< const Frame(const FrameId)> Callback=nullptr)
llvm::MapVector< GlobalValue::GUID, IndexedMemProfRecord >::iterator Iter
std::pair< GlobalValue::GUID, MemProfRecord > GuidMemProfRecordPair
LLVM_ABI std::unique_ptr< MemProfSummary > getSummary()
void printYAML(raw_ostream &OS)
static Expected< std::unique_ptr< RawMemProfReader > > create(const Twine &Path, StringRef ProfiledBinary, bool KeepName=false)
static std::vector< std::string > peekBuildIds(MemoryBuffer *DataBuffer)
Error readNextRecord(GuidMemProfRecordPair &GuidRecord, std::function< const Frame(const FrameId)> Callback) override
static bool hasFormat(const MemoryBuffer &DataBuffer)
virtual ~RawMemProfReader() override
static LLVM_ABI bool hasFormat(const MemoryBuffer &DataBuffer)
static LLVM_ABI Expected< std::unique_ptr< YAMLMemProfReader > > create(const Twine &Path)
LLVM_ABI void parse(StringRef YAMLData)
This class implements an extremely fast bulk output stream that can only output to a stream.
static StringRef getCanonicalFnName(const Function &F)
Return the canonical name for a function, taking into account suffix elision policy attributes.
static Expected< std::unique_ptr< SymbolizableObjectFile > > create(const object::ObjectFile *Obj, std::unique_ptr< DIContext > DICtx, bool UntagAddresses)
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
llvm::DenseMap< uint64_t, llvm::SmallVector< uint64_t > > CallStackMap
std::variant< StringRef, uint64_t > SymbolHandleRef
LLVM_ABI GlobalValue::GUID getGUID(const StringRef FunctionName)
std::variant< std::string, uint64_t > SymbolHandle
LLVM_ABI BuildIDRef getBuildID(const ObjectFile *Obj)
Returns the build ID, if any, contained in the given object file.
ArrayRef< uint8_t > BuildIDRef
A reference to a BuildID in binary form.
LLVM_ABI Expected< std::unique_ptr< Binary > > createBinary(MemoryBufferRef Source, LLVMContext *Context=nullptr, bool InitContent=true)
Create a Binary from Source, autodetecting the file type.
LLVM_ABI StringRef filename(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get filename.
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
LLVM_ABI void reportFatalInternalError(Error Err)
Report a fatal error that indicates a bug in LLVM.
Error joinErrors(Error E1, Error E2)
Concatenate errors.
FormattedNumber format_hex_no_prefix(uint64_t N, unsigned Width, bool Upper=false)
format_hex_no_prefix - Output N as a fixed width hexadecimal.
void erase_if(Container &C, UnaryPredicate P)
Provide a container algorithm similar to C++ Library Fundamentals v2's erase_if which is equivalent t...
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
static constexpr const char *const BadString
YamlDataAccessProfData YamlifiedDataAccessProfiles
std::vector< GUIDMemProfRecordPair > HeapProfileRecords
std::vector< Frame > CallStack
PortableMemInfoBlock Info
GlobalValue::GUID Function
llvm::MapVector< GlobalValue::GUID, IndexedMemProfRecord > Records
CallStackId addCallStack(ArrayRef< FrameId > CS)
FrameId addFrame(const Frame &F)
llvm::SmallVector< IndexedAllocationInfo > AllocSites
llvm::SmallVector< IndexedCallSiteInfo > CallSites
std::vector< memprof::DataAccessProfRecord > Records
std::vector< uint64_t > KnownColdStrHashes
std::vector< std::string > KnownColdSymbols