36#define DEBUG_TYPE "memprof"
41template <
class T = u
int64_t>
inline T alignedRead(
const char *
Ptr) {
42 static_assert(std::is_pod<T>::value,
"Not a pod type.");
43 assert(
reinterpret_cast<size_t>(
Ptr) %
sizeof(
T) == 0 &&
"Unaligned Read");
44 return *
reinterpret_cast<const T *
>(
Ptr);
47Error checkBuffer(
const MemoryBuffer &Buffer) {
51 if (Buffer.getBufferSize() == 0)
54 if (Buffer.getBufferSize() <
sizeof(Header)) {
61 const char *Next = Buffer.getBufferStart();
62 while (Next < Buffer.getBufferEnd()) {
63 auto *
H =
reinterpret_cast<const Header *
>(Next);
64 if (
H->Version != MEMPROF_RAW_VERSION) {
68 TotalSize +=
H->TotalSize;
72 if (Buffer.getBufferSize() != TotalSize) {
79 using namespace support;
82 endian::readNext<uint64_t, little, unaligned>(
Ptr);
85 Items.
push_back(*
reinterpret_cast<const SegmentEntry *
>(
86 Ptr +
I *
sizeof(SegmentEntry)));
92readMemInfoBlocks(
const char *
Ptr) {
93 using namespace support;
96 endian::readNext<uint64_t, little, unaligned>(
Ptr);
99 const uint64_t Id = endian::readNext<uint64_t, little, unaligned>(
Ptr);
100 const MemInfoBlock MIB = *
reinterpret_cast<const MemInfoBlock *
>(
Ptr);
103 Ptr +=
sizeof(MemInfoBlock);
109 using namespace support;
112 endian::readNext<uint64_t, little, unaligned>(
Ptr);
116 const uint64_t StackId = endian::readNext<uint64_t, little, unaligned>(
Ptr);
117 const uint64_t NumPCs = endian::readNext<uint64_t, little, unaligned>(
Ptr);
119 SmallVector<uint64_t> CallStack;
120 for (
uint64_t J = 0; J < NumPCs; J++) {
121 CallStack.push_back(endian::readNext<uint64_t, little, unaligned>(
Ptr));
124 Items[StackId] = CallStack;
133 for (
const auto &IdStack :
From) {
134 auto I = To.find(IdStack.first);
136 To[IdStack.first] = IdStack.second;
139 if (IdStack.second !=
I->second)
146Error report(Error
E,
const StringRef
Context) {
151bool isRuntimePath(
const StringRef Path) {
155 return Filename.equals(
"memprof_malloc_linux.cpp") ||
156 Filename.equals(
"memprof_interceptors.cpp") ||
157 Filename.equals(
"memprof_new_delete.cpp");
160std::string getBuildIdString(
const SegmentEntry &Entry) {
162 if (Entry.BuildIdSize == 0)
166 raw_string_ostream
OS(Str);
167 for (
size_t I = 0;
I < Entry.BuildIdSize;
I++) {
174Expected<std::unique_ptr<RawMemProfReader>>
178 if (std::error_code EC = BufferOr.getError())
181 std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
182 if (
Error E = checkBuffer(*Buffer))
183 return report(std::move(
E), Path.getSingleStringRef());
185 if (ProfiledBinary.
empty())
188 "Path to profiled binary is empty!");
192 return report(BinaryOr.takeError(), ProfiledBinary);
196 std::unique_ptr<RawMemProfReader> Reader(
198 if (
Error E = Reader->initialize(std::move(Buffer))) {
201 return std::move(Reader);
209 std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
219 return Magic == MEMPROF_RAW_MAGIC_64;
223 uint64_t NumAllocFunctions = 0, NumMibInfo = 0;
224 for (
const auto &KV : FunctionProfileData) {
225 const size_t NumAllocSites = KV.second.AllocSites.size();
226 if (NumAllocSites > 0) {
228 NumMibInfo += NumAllocSites;
232 OS <<
"MemprofProfile:\n";
234 OS <<
" Version: " << MEMPROF_RAW_VERSION <<
"\n";
235 OS <<
" NumSegments: " << SegmentInfo.
size() <<
"\n";
236 OS <<
" NumMibInfo: " << NumMibInfo <<
"\n";
237 OS <<
" NumAllocFunctions: " << NumAllocFunctions <<
"\n";
238 OS <<
" NumStackOffsets: " << StackMap.
size() <<
"\n";
240 OS <<
" Segments:\n";
241 for (
const auto &Entry : SegmentInfo) {
243 OS <<
" BuildId: " << getBuildIdString(Entry) <<
"\n";
244 OS <<
" Start: 0x" << llvm::utohexstr(Entry.Start) <<
"\n";
245 OS <<
" End: 0x" << llvm::utohexstr(Entry.End) <<
"\n";
246 OS <<
" Offset: 0x" << llvm::utohexstr(Entry.Offset) <<
"\n";
250 for (
const auto &Entry : *
this) {
252 OS <<
" FunctionGUID: " << Entry.first <<
"\n";
253 Entry.second.print(
OS);
257Error RawMemProfReader::initialize(std::unique_ptr<MemoryBuffer> DataBuffer) {
258 const StringRef FileName = Binary.getBinary()->getFileName();
260 auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Binary.getBinary());
262 return report(make_error<StringError>(
Twine(
"Not an ELF file: "),
270 auto* Elf64LEObject = llvm::cast<llvm::object::ELF64LEObjectFile>(ElfObject);
272 auto PHdrsOr = ElfFile.program_headers();
274 return report(make_error<StringError>(
Twine(
"Could not read program headers: "),
277 auto FirstLoadHeader = PHdrsOr->begin();
280 if(FirstLoadHeader->p_vaddr == 0)
281 return report(make_error<StringError>(
Twine(
"Unsupported position independent code"),
285 auto Triple = ElfObject->makeTriple();
287 return report(make_error<StringError>(
Twine(
"Unsupported target: ") +
292 auto *
Object = cast<object::ObjectFile>(Binary.getBinary());
297 Object, std::move(Context),
false);
299 return report(SOFOr.takeError(), FileName);
300 Symbolizer = std::move(SOFOr.get());
302 if (
Error E = readRawProfile(std::move(DataBuffer)))
305 if (
Error E = symbolizeAndFilterStackFrames())
308 return mapRawProfileToRecords();
311Error RawMemProfReader::mapRawProfileToRecords() {
317 PerFunctionCallSites;
321 for (
const auto &Entry : CallstackProfileData) {
322 const uint64_t StackId = Entry.first;
324 auto It = StackMap.
find(StackId);
325 if (It == StackMap.
end())
326 return make_error<InstrProfError>(
328 "memprof callstack record does not contain id: " + Twine(StackId));
332 Callstack.
reserve(It->getSecond().size());
335 for (
size_t I = 0;
I < Addresses.
size();
I++) {
338 "Address not found in SymbolizedFrame map");
339 const SmallVector<FrameId> &Frames = SymbolizedFrame[
Address];
342 "The last frame should not be inlined");
347 for (
size_t J = 0; J < Frames.size(); J++) {
348 if (
I == 0 && J == 0)
355 PerFunctionCallSites[Guid].
insert(&Frames);
359 Callstack.
append(Frames.begin(), Frames.end());
364 for (
size_t I = 0; ;
I++) {
365 const Frame &
F = idToFrame(Callstack[
I]);
367 FunctionProfileData.insert({
F.Function, IndexedMemProfRecord()});
368 IndexedMemProfRecord &Record =
Result.first->second;
369 Record.AllocSites.emplace_back(Callstack, Entry.second);
371 if (!
F.IsInlineFrame)
377 for (
const auto &[Id, Locs] : PerFunctionCallSites) {
380 auto Result = FunctionProfileData.insert({
Id, IndexedMemProfRecord()});
381 IndexedMemProfRecord &Record =
Result.first->second;
382 for (LocationPtr Loc : Locs) {
383 Record.CallSites.push_back(*Loc);
390Error RawMemProfReader::symbolizeAndFilterStackFrames() {
392 const DILineInfoSpecifier Specifier(
393 DILineInfoSpecifier::FileLineInfoKind::RawValue,
394 DILineInfoSpecifier::FunctionNameKind::LinkageName);
402 for (
auto &Entry : StackMap) {
403 for (
const uint64_t VAddr : Entry.getSecond()) {
407 if (SymbolizedFrame.count(VAddr) > 0 ||
411 Expected<DIInliningInfo> DIOr = Symbolizer->symbolizeInlinedCode(
412 getModuleOffset(VAddr), Specifier,
false);
414 return DIOr.takeError();
415 DIInliningInfo DI = DIOr.get();
419 isRuntimePath(DI.getFrame(0).FileName)) {
420 AllVAddrsToDiscard.
insert(VAddr);
424 for (
size_t I = 0, NumFrames = DI.getNumberOfFrames();
I < NumFrames;
426 const auto &DIFrame = DI.getFrame(
I);
429 const Frame
F(Guid, DIFrame.Line - DIFrame.StartLine, DIFrame.Column,
437 GuidToSymbolName.
insert({Guid, DIFrame.FunctionName});
440 IdToFrame.insert({Hash,
F});
441 SymbolizedFrame[VAddr].push_back(Hash);
445 auto &CallStack = Entry.getSecond();
449 if (CallStack.empty())
450 EntriesToErase.
push_back(Entry.getFirst());
454 for (
const uint64_t Id : EntriesToErase) {
456 CallstackProfileData.erase(Id);
459 if (StackMap.empty())
460 return make_error<InstrProfError>(
462 "no entries in callstack map after symbolization");
467Error RawMemProfReader::readRawProfile(
468 std::unique_ptr<MemoryBuffer> DataBuffer) {
469 const char *Next = DataBuffer->getBufferStart();
471 while (Next < DataBuffer->getBufferEnd()) {
472 auto *Header =
reinterpret_cast<const memprof::Header *
>(Next);
477 readSegmentEntries(Next + Header->SegmentOffset);
478 if (!SegmentInfo.
empty() && SegmentInfo != Entries) {
482 return make_error<InstrProfError>(
484 "memprof raw profile has different segment information");
486 SegmentInfo.
assign(Entries.begin(), Entries.end());
491 for (
const auto &Value : readMemInfoBlocks(Next + Header->MIBOffset)) {
492 if (CallstackProfileData.count(
Value.first)) {
493 CallstackProfileData[
Value.first].Merge(
Value.second);
495 CallstackProfileData[
Value.first] =
Value.second;
501 const CallStackMap CSM = readStackInfo(Next + Header->StackOffset);
502 if (StackMap.empty()) {
505 if (mergeStackMap(CSM, StackMap))
506 return make_error<InstrProfError>(
508 "memprof raw profile got different call stack for same id");
511 Next += Header->TotalSize;
517object::SectionedAddress
518RawMemProfReader::getModuleOffset(
const uint64_t VirtualAddress) {
520 SegmentEntry *ContainingSegment =
nullptr;
521 for (
auto &SE : SegmentInfo) {
522 if (VirtualAddress > SE.Start && VirtualAddress <= SE.End) {
523 ContainingSegment = &SE;
528 assert(ContainingSegment &&
"Could not find a segment entry");
533 return object::SectionedAddress{VirtualAddress};
537 if (FunctionProfileData.empty())
540 if (Iter == FunctionProfileData.end())
543 auto IdToFrameCallback = [
this](
const FrameId Id) {
544 Frame F = this->idToFrame(Id);
545 if (!this->KeepSymbolName)
547 auto Iter = this->GuidToSymbolName.
find(
F.Function);
548 assert(Iter != this->GuidToSymbolName.
end());
549 F.SymbolName = Iter->getSecond();
554 GuidRecord = {Iter->first,
MemProfRecord(IndexedRecord, IdToFrameCallback)};
BlockVerifier::State From
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.
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file implements a set that has insertion order iteration characteristics.
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)
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.
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
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
void assign(size_type NumElts, ValueParamT Elt)
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.
constexpr bool empty() const
empty - Check if the string is empty.
Triple - Helper class for working with autoconf configuration names.
bool isX86() const
Tests whether the target is x86 (32- or 64-bit).
StringRef getArchName() const
Get the architecture (first) component of the triple.
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.
void printYAML(raw_ostream &OS)
std::pair< GlobalValue::GUID, MemProfRecord > GuidMemProfRecordPair
static Expected< std::unique_ptr< RawMemProfReader > > create(const Twine &Path, const StringRef ProfiledBinary, bool KeepName=false)
Error readNextRecord(GuidMemProfRecordPair &GuidRecord)
static bool hasFormat(const MemoryBuffer &DataBuffer)
This class implements an extremely fast bulk output stream that can only output to a stream.
static Expected< std::unique_ptr< SymbolizableObjectFile > > create(const object::ObjectFile *Obj, std::unique_ptr< DIContext > DICtx, bool UntagAddresses)
llvm::DenseMap< uint64_t, llvm::SmallVector< uint64_t > > CallStackMap
Expected< std::unique_ptr< Binary > > createBinary(MemoryBufferRef Source, LLVMContext *Context=nullptr, bool InitContent=true)
Create a Binary from Source, autodetecting the file type.
StringRef filename(StringRef path, Style style=Style::native)
Get filename.
This is an optimization pass for GlobalISel generic memory operations.
std::error_code make_error_code(BitcodeError E)
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.
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...
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
static constexpr const char *const BadString
GlobalValue::GUID Function
static GlobalValue::GUID getGUID(const StringRef FunctionName)