1 //===- RawMemProfReader.cpp - Instrumented memory profiling reader --------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file contains support for reading MemProf profiling data.
11 //===----------------------------------------------------------------------===//
16 #include <type_traits>
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/DenseMap.h"
20 #include "llvm/ADT/SetVector.h"
21 #include "llvm/ADT/SmallSet.h"
22 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/ADT/StringExtras.h"
24 #include "llvm/ADT/Twine.h"
25 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
26 #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
27 #include "llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h"
28 #include "llvm/Object/Binary.h"
29 #include "llvm/Object/BuildID.h"
30 #include "llvm/Object/ELFObjectFile.h"
31 #include "llvm/Object/ObjectFile.h"
32 #include "llvm/ProfileData/InstrProf.h"
33 #include "llvm/ProfileData/MemProf.h"
34 #include "llvm/ProfileData/MemProfData.inc"
35 #include "llvm/ProfileData/RawMemProfReader.h"
36 #include "llvm/ProfileData/SampleProf.h"
37 #include "llvm/Support/Debug.h"
38 #include "llvm/Support/Endian.h"
39 #include "llvm/Support/Error.h"
40 #include "llvm/Support/MemoryBuffer.h"
41 #include "llvm/Support/Path.h"
43 #define DEBUG_TYPE "memprof"
48 template <class T
= uint64_t> inline T
alignedRead(const char *Ptr
) {
49 static_assert(std::is_pod
<T
>::value
, "Not a pod type.");
50 assert(reinterpret_cast<size_t>(Ptr
) % sizeof(T
) == 0 && "Unaligned Read");
51 return *reinterpret_cast<const T
*>(Ptr
);
54 Error
checkBuffer(const MemoryBuffer
&Buffer
) {
55 if (!RawMemProfReader::hasFormat(Buffer
))
56 return make_error
<InstrProfError
>(instrprof_error::bad_magic
);
58 if (Buffer
.getBufferSize() == 0)
59 return make_error
<InstrProfError
>(instrprof_error::empty_raw_profile
);
61 if (Buffer
.getBufferSize() < sizeof(Header
)) {
62 return make_error
<InstrProfError
>(instrprof_error::truncated
);
65 // The size of the buffer can be > header total size since we allow repeated
66 // serialization of memprof profiles to the same file.
67 uint64_t TotalSize
= 0;
68 const char *Next
= Buffer
.getBufferStart();
69 while (Next
< Buffer
.getBufferEnd()) {
70 auto *H
= reinterpret_cast<const Header
*>(Next
);
71 if (H
->Version
!= MEMPROF_RAW_VERSION
) {
72 return make_error
<InstrProfError
>(instrprof_error::unsupported_version
);
75 TotalSize
+= H
->TotalSize
;
79 if (Buffer
.getBufferSize() != TotalSize
) {
80 return make_error
<InstrProfError
>(instrprof_error::malformed
);
82 return Error::success();
85 llvm::SmallVector
<SegmentEntry
> readSegmentEntries(const char *Ptr
) {
86 using namespace support
;
88 const uint64_t NumItemsToRead
=
89 endian::readNext
<uint64_t, llvm::endianness::little
, unaligned
>(Ptr
);
90 llvm::SmallVector
<SegmentEntry
> Items
;
91 for (uint64_t I
= 0; I
< NumItemsToRead
; I
++) {
92 Items
.push_back(*reinterpret_cast<const SegmentEntry
*>(
93 Ptr
+ I
* sizeof(SegmentEntry
)));
98 llvm::SmallVector
<std::pair
<uint64_t, MemInfoBlock
>>
99 readMemInfoBlocks(const char *Ptr
) {
100 using namespace support
;
102 const uint64_t NumItemsToRead
=
103 endian::readNext
<uint64_t, llvm::endianness::little
, unaligned
>(Ptr
);
104 llvm::SmallVector
<std::pair
<uint64_t, MemInfoBlock
>> Items
;
105 for (uint64_t I
= 0; I
< NumItemsToRead
; I
++) {
107 endian::readNext
<uint64_t, llvm::endianness::little
, unaligned
>(Ptr
);
108 const MemInfoBlock MIB
= *reinterpret_cast<const MemInfoBlock
*>(Ptr
);
109 Items
.push_back({Id
, MIB
});
110 // Only increment by size of MIB since readNext implicitly increments.
111 Ptr
+= sizeof(MemInfoBlock
);
116 CallStackMap
readStackInfo(const char *Ptr
) {
117 using namespace support
;
119 const uint64_t NumItemsToRead
=
120 endian::readNext
<uint64_t, llvm::endianness::little
, unaligned
>(Ptr
);
123 for (uint64_t I
= 0; I
< NumItemsToRead
; I
++) {
124 const uint64_t StackId
=
125 endian::readNext
<uint64_t, llvm::endianness::little
, unaligned
>(Ptr
);
126 const uint64_t NumPCs
=
127 endian::readNext
<uint64_t, llvm::endianness::little
, unaligned
>(Ptr
);
129 SmallVector
<uint64_t> CallStack
;
130 for (uint64_t J
= 0; J
< NumPCs
; J
++) {
132 endian::readNext
<uint64_t, llvm::endianness::little
, unaligned
>(Ptr
));
135 Items
[StackId
] = CallStack
;
140 // Merges the contents of stack information in \p From to \p To. Returns true if
141 // any stack ids observed previously map to a different set of program counter
143 bool mergeStackMap(const CallStackMap
&From
, CallStackMap
&To
) {
144 for (const auto &IdStack
: From
) {
145 auto I
= To
.find(IdStack
.first
);
147 To
[IdStack
.first
] = IdStack
.second
;
149 // Check that the PCs are the same (in order).
150 if (IdStack
.second
!= I
->second
)
157 Error
report(Error E
, const StringRef Context
) {
158 return joinErrors(createStringError(inconvertibleErrorCode(), Context
),
162 bool isRuntimePath(const StringRef Path
) {
163 const StringRef Filename
= llvm::sys::path::filename(Path
);
164 // This list should be updated in case new files with additional interceptors
165 // are added to the memprof runtime.
166 return Filename
.equals("memprof_malloc_linux.cpp") ||
167 Filename
.equals("memprof_interceptors.cpp") ||
168 Filename
.equals("memprof_new_delete.cpp");
171 std::string
getBuildIdString(const SegmentEntry
&Entry
) {
172 // If the build id is unset print a helpful string instead of all zeros.
173 if (Entry
.BuildIdSize
== 0)
177 raw_string_ostream
OS(Str
);
178 for (size_t I
= 0; I
< Entry
.BuildIdSize
; I
++) {
179 OS
<< format_hex_no_prefix(Entry
.BuildId
[I
], 2);
185 Expected
<std::unique_ptr
<RawMemProfReader
>>
186 RawMemProfReader::create(const Twine
&Path
, const StringRef ProfiledBinary
,
188 auto BufferOr
= MemoryBuffer::getFileOrSTDIN(Path
);
189 if (std::error_code EC
= BufferOr
.getError())
190 return report(errorCodeToError(EC
), Path
.getSingleStringRef());
192 std::unique_ptr
<MemoryBuffer
> Buffer(BufferOr
.get().release());
193 return create(std::move(Buffer
), ProfiledBinary
, KeepName
);
196 Expected
<std::unique_ptr
<RawMemProfReader
>>
197 RawMemProfReader::create(std::unique_ptr
<MemoryBuffer
> Buffer
,
198 const StringRef ProfiledBinary
, bool KeepName
) {
199 if (Error E
= checkBuffer(*Buffer
))
200 return report(std::move(E
), Buffer
->getBufferIdentifier());
202 if (ProfiledBinary
.empty()) {
203 // Peek the build ids to print a helpful error message.
204 const std::vector
<std::string
> BuildIds
= peekBuildIds(Buffer
.get());
205 std::string
ErrorMessage(
206 R
"(Path to profiled binary is empty, expected binary with one of the following build ids:
208 for (const auto &Id
: BuildIds
) {
209 ErrorMessage
+= "\n BuildId: ";
213 make_error
<StringError
>(ErrorMessage
, inconvertibleErrorCode()),
217 auto BinaryOr
= llvm::object::createBinary(ProfiledBinary
);
219 return report(BinaryOr
.takeError(), ProfiledBinary
);
222 // Use new here since constructor is private.
223 std::unique_ptr
<RawMemProfReader
> Reader(
224 new RawMemProfReader(std::move(BinaryOr
.get()), KeepName
));
225 if (Error E
= Reader
->initialize(std::move(Buffer
))) {
228 return std::move(Reader
);
231 bool RawMemProfReader::hasFormat(const StringRef Path
) {
232 auto BufferOr
= MemoryBuffer::getFileOrSTDIN(Path
);
236 std::unique_ptr
<MemoryBuffer
> Buffer(BufferOr
.get().release());
237 return hasFormat(*Buffer
);
240 bool RawMemProfReader::hasFormat(const MemoryBuffer
&Buffer
) {
241 if (Buffer
.getBufferSize() < sizeof(uint64_t))
243 // Aligned read to sanity check that the buffer was allocated with at least 8b
245 const uint64_t Magic
= alignedRead(Buffer
.getBufferStart());
246 return Magic
== MEMPROF_RAW_MAGIC_64
;
249 void RawMemProfReader::printYAML(raw_ostream
&OS
) {
250 uint64_t NumAllocFunctions
= 0, NumMibInfo
= 0;
251 for (const auto &KV
: FunctionProfileData
) {
252 const size_t NumAllocSites
= KV
.second
.AllocSites
.size();
253 if (NumAllocSites
> 0) {
255 NumMibInfo
+= NumAllocSites
;
259 OS
<< "MemprofProfile:\n";
261 OS
<< " Version: " << MEMPROF_RAW_VERSION
<< "\n";
262 OS
<< " NumSegments: " << SegmentInfo
.size() << "\n";
263 OS
<< " NumMibInfo: " << NumMibInfo
<< "\n";
264 OS
<< " NumAllocFunctions: " << NumAllocFunctions
<< "\n";
265 OS
<< " NumStackOffsets: " << StackMap
.size() << "\n";
266 // Print out the segment information.
267 OS
<< " Segments:\n";
268 for (const auto &Entry
: SegmentInfo
) {
270 OS
<< " BuildId: " << getBuildIdString(Entry
) << "\n";
271 OS
<< " Start: 0x" << llvm::utohexstr(Entry
.Start
) << "\n";
272 OS
<< " End: 0x" << llvm::utohexstr(Entry
.End
) << "\n";
273 OS
<< " Offset: 0x" << llvm::utohexstr(Entry
.Offset
) << "\n";
275 // Print out the merged contents of the profiles.
277 for (const auto &Entry
: *this) {
279 OS
<< " FunctionGUID: " << Entry
.first
<< "\n";
280 Entry
.second
.print(OS
);
284 Error
RawMemProfReader::initialize(std::unique_ptr
<MemoryBuffer
> DataBuffer
) {
285 const StringRef FileName
= Binary
.getBinary()->getFileName();
287 auto *ElfObject
= dyn_cast
<object::ELFObjectFileBase
>(Binary
.getBinary());
289 return report(make_error
<StringError
>(Twine("Not an ELF file: "),
290 inconvertibleErrorCode()),
294 // Check whether the profiled binary was built with position independent code
295 // (PIC). Perform sanity checks for assumptions we rely on to simplify
297 auto* Elf64LEObject
= llvm::cast
<llvm::object::ELF64LEObjectFile
>(ElfObject
);
298 const llvm::object::ELF64LEFile
& ElfFile
= Elf64LEObject
->getELFFile();
299 auto PHdrsOr
= ElfFile
.program_headers();
302 make_error
<StringError
>(Twine("Could not read program headers: "),
303 inconvertibleErrorCode()),
306 int NumExecutableSegments
= 0;
307 for (const auto &Phdr
: *PHdrsOr
) {
308 if (Phdr
.p_type
== ELF::PT_LOAD
) {
309 if (Phdr
.p_flags
& ELF::PF_X
) {
310 // We assume only one text segment in the main binary for simplicity and
311 // reduce the overhead of checking multiple ranges during symbolization.
312 if (++NumExecutableSegments
> 1) {
314 make_error
<StringError
>(
315 "Expect only one executable load segment in the binary",
316 inconvertibleErrorCode()),
319 // Segment will always be loaded at a page boundary, expect it to be
320 // aligned already. Assume 4K pagesize for the machine from which the
321 // profile has been collected. This should be fine for now, in case we
322 // want to support other pagesizes it can be recorded in the raw profile
323 // during collection.
324 PreferredTextSegmentAddress
= Phdr
.p_vaddr
;
325 assert(Phdr
.p_vaddr
== (Phdr
.p_vaddr
& ~(0x1000 - 1U)) &&
326 "Expect p_vaddr to always be page aligned");
327 assert(Phdr
.p_offset
== 0 && "Expect p_offset = 0 for symbolization.");
332 auto Triple
= ElfObject
->makeTriple();
334 return report(make_error
<StringError
>(Twine("Unsupported target: ") +
335 Triple
.getArchName(),
336 inconvertibleErrorCode()),
339 // Process the raw profile.
340 if (Error E
= readRawProfile(std::move(DataBuffer
)))
343 if (Error E
= setupForSymbolization())
346 auto *Object
= cast
<object::ObjectFile
>(Binary
.getBinary());
347 std::unique_ptr
<DIContext
> Context
= DWARFContext::create(
348 *Object
, DWARFContext::ProcessDebugRelocations::Process
);
350 auto SOFOr
= symbolize::SymbolizableObjectFile::create(
351 Object
, std::move(Context
), /*UntagAddresses=*/false);
353 return report(SOFOr
.takeError(), FileName
);
354 auto Symbolizer
= std::move(SOFOr
.get());
356 // The symbolizer ownership is moved into symbolizeAndFilterStackFrames so
357 // that it is freed automatically at the end, when it is no longer used. This
358 // reduces peak memory since it won't be live while also mapping the raw
359 // profile into records afterwards.
360 if (Error E
= symbolizeAndFilterStackFrames(std::move(Symbolizer
)))
363 return mapRawProfileToRecords();
366 Error
RawMemProfReader::setupForSymbolization() {
367 auto *Object
= cast
<object::ObjectFile
>(Binary
.getBinary());
368 object::BuildIDRef BinaryId
= object::getBuildID(Object
);
369 if (BinaryId
.empty())
370 return make_error
<StringError
>(Twine("No build id found in binary ") +
371 Binary
.getBinary()->getFileName(),
372 inconvertibleErrorCode());
375 for (const auto &Entry
: SegmentInfo
) {
376 llvm::ArrayRef
<uint8_t> SegmentId(Entry
.BuildId
, Entry
.BuildIdSize
);
377 if (BinaryId
== SegmentId
) {
378 // We assume only one text segment in the main binary for simplicity and
379 // reduce the overhead of checking multiple ranges during symbolization.
380 if (++NumMatched
> 1) {
381 return make_error
<StringError
>(
382 "We expect only one executable segment in the profiled binary",
383 inconvertibleErrorCode());
385 ProfiledTextSegmentStart
= Entry
.Start
;
386 ProfiledTextSegmentEnd
= Entry
.End
;
389 assert(NumMatched
!= 0 && "No matching executable segments in segment info.");
390 assert((PreferredTextSegmentAddress
== 0 ||
391 (PreferredTextSegmentAddress
== ProfiledTextSegmentStart
)) &&
392 "Expect text segment address to be 0 or equal to profiled text "
394 return Error::success();
397 Error
RawMemProfReader::mapRawProfileToRecords() {
398 // Hold a mapping from function to each callsite location we encounter within
399 // it that is part of some dynamic allocation context. The location is stored
400 // as a pointer to a symbolized list of inline frames.
401 using LocationPtr
= const llvm::SmallVector
<FrameId
> *;
402 llvm::MapVector
<GlobalValue::GUID
, llvm::SetVector
<LocationPtr
>>
403 PerFunctionCallSites
;
405 // Convert the raw profile callstack data into memprof records. While doing so
406 // keep track of related contexts so that we can fill these in later.
407 for (const auto &Entry
: CallstackProfileData
) {
408 const uint64_t StackId
= Entry
.first
;
410 auto It
= StackMap
.find(StackId
);
411 if (It
== StackMap
.end())
412 return make_error
<InstrProfError
>(
413 instrprof_error::malformed
,
414 "memprof callstack record does not contain id: " + Twine(StackId
));
416 // Construct the symbolized callstack.
417 llvm::SmallVector
<FrameId
> Callstack
;
418 Callstack
.reserve(It
->getSecond().size());
420 llvm::ArrayRef
<uint64_t> Addresses
= It
->getSecond();
421 for (size_t I
= 0; I
< Addresses
.size(); I
++) {
422 const uint64_t Address
= Addresses
[I
];
423 assert(SymbolizedFrame
.count(Address
) > 0 &&
424 "Address not found in SymbolizedFrame map");
425 const SmallVector
<FrameId
> &Frames
= SymbolizedFrame
[Address
];
427 assert(!idToFrame(Frames
.back()).IsInlineFrame
&&
428 "The last frame should not be inlined");
430 // Record the callsites for each function. Skip the first frame of the
431 // first address since it is the allocation site itself that is recorded
433 for (size_t J
= 0; J
< Frames
.size(); J
++) {
434 if (I
== 0 && J
== 0)
436 // We attach the entire bottom-up frame here for the callsite even
437 // though we only need the frames up to and including the frame for
438 // Frames[J].Function. This will enable better deduplication for
439 // compression in the future.
440 const GlobalValue::GUID Guid
= idToFrame(Frames
[J
]).Function
;
441 PerFunctionCallSites
[Guid
].insert(&Frames
);
444 // Add all the frames to the current allocation callstack.
445 Callstack
.append(Frames
.begin(), Frames
.end());
448 // We attach the memprof record to each function bottom-up including the
449 // first non-inline frame.
450 for (size_t I
= 0; /*Break out using the condition below*/; I
++) {
451 const Frame
&F
= idToFrame(Callstack
[I
]);
453 FunctionProfileData
.insert({F
.Function
, IndexedMemProfRecord()});
454 IndexedMemProfRecord
&Record
= Result
.first
->second
;
455 Record
.AllocSites
.emplace_back(Callstack
, Entry
.second
);
457 if (!F
.IsInlineFrame
)
462 // Fill in the related callsites per function.
463 for (const auto &[Id
, Locs
] : PerFunctionCallSites
) {
464 // Some functions may have only callsite data and no allocation data. Here
465 // we insert a new entry for callsite data if we need to.
466 auto Result
= FunctionProfileData
.insert({Id
, IndexedMemProfRecord()});
467 IndexedMemProfRecord
&Record
= Result
.first
->second
;
468 for (LocationPtr Loc
: Locs
) {
469 Record
.CallSites
.push_back(*Loc
);
473 return Error::success();
476 Error
RawMemProfReader::symbolizeAndFilterStackFrames(
477 std::unique_ptr
<llvm::symbolize::SymbolizableModule
> Symbolizer
) {
478 // The specifier to use when symbolization is requested.
479 const DILineInfoSpecifier
Specifier(
480 DILineInfoSpecifier::FileLineInfoKind::RawValue
,
481 DILineInfoSpecifier::FunctionNameKind::LinkageName
);
483 // For entries where all PCs in the callstack are discarded, we erase the
484 // entry from the stack map.
485 llvm::SmallVector
<uint64_t> EntriesToErase
;
486 // We keep track of all prior discarded entries so that we can avoid invoking
487 // the symbolizer for such entries.
488 llvm::DenseSet
<uint64_t> AllVAddrsToDiscard
;
489 for (auto &Entry
: StackMap
) {
490 for (const uint64_t VAddr
: Entry
.getSecond()) {
491 // Check if we have already symbolized and cached the result or if we
492 // don't want to attempt symbolization since we know this address is bad.
493 // In this case the address is also removed from the current callstack.
494 if (SymbolizedFrame
.count(VAddr
) > 0 ||
495 AllVAddrsToDiscard
.contains(VAddr
))
498 Expected
<DIInliningInfo
> DIOr
= Symbolizer
->symbolizeInlinedCode(
499 getModuleOffset(VAddr
), Specifier
, /*UseSymbolTable=*/false);
501 return DIOr
.takeError();
502 DIInliningInfo DI
= DIOr
.get();
504 // Drop frames which we can't symbolize or if they belong to the runtime.
505 if (DI
.getFrame(0).FunctionName
== DILineInfo::BadString
||
506 isRuntimePath(DI
.getFrame(0).FileName
)) {
507 AllVAddrsToDiscard
.insert(VAddr
);
511 for (size_t I
= 0, NumFrames
= DI
.getNumberOfFrames(); I
< NumFrames
;
513 const auto &DIFrame
= DI
.getFrame(I
);
514 const uint64_t Guid
=
515 IndexedMemProfRecord::getGUID(DIFrame
.FunctionName
);
516 const Frame
F(Guid
, DIFrame
.Line
- DIFrame
.StartLine
, DIFrame
.Column
,
517 // Only the last entry is not an inlined location.
519 // Here we retain a mapping from the GUID to canonical symbol name
520 // instead of adding it to the frame object directly to reduce memory
521 // overhead. This is because there can be many unique frames,
522 // particularly for callsite frames.
523 if (KeepSymbolName
) {
524 StringRef CanonicalName
=
525 sampleprof::FunctionSamples::getCanonicalFnName(
526 DIFrame
.FunctionName
);
527 GuidToSymbolName
.insert({Guid
, CanonicalName
.str()});
530 const FrameId Hash
= F
.hash();
531 IdToFrame
.insert({Hash
, F
});
532 SymbolizedFrame
[VAddr
].push_back(Hash
);
536 auto &CallStack
= Entry
.getSecond();
537 llvm::erase_if(CallStack
, [&AllVAddrsToDiscard
](const uint64_t A
) {
538 return AllVAddrsToDiscard
.contains(A
);
540 if (CallStack
.empty())
541 EntriesToErase
.push_back(Entry
.getFirst());
544 // Drop the entries where the callstack is empty.
545 for (const uint64_t Id
: EntriesToErase
) {
547 CallstackProfileData
.erase(Id
);
550 if (StackMap
.empty())
551 return make_error
<InstrProfError
>(
552 instrprof_error::malformed
,
553 "no entries in callstack map after symbolization");
555 return Error::success();
558 std::vector
<std::string
>
559 RawMemProfReader::peekBuildIds(MemoryBuffer
*DataBuffer
) {
560 const char *Next
= DataBuffer
->getBufferStart();
561 // Use a set + vector since a profile file may contain multiple raw profile
562 // dumps, each with segment information. We want them unique and in order they
563 // were stored in the profile; the profiled binary should be the first entry.
564 // The runtime uses dl_iterate_phdr and the "... first object visited by
565 // callback is the main program."
566 // https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html
567 std::vector
<std::string
> BuildIds
;
568 llvm::SmallSet
<std::string
, 10> BuildIdsSet
;
569 while (Next
< DataBuffer
->getBufferEnd()) {
570 auto *Header
= reinterpret_cast<const memprof::Header
*>(Next
);
572 const llvm::SmallVector
<SegmentEntry
> Entries
=
573 readSegmentEntries(Next
+ Header
->SegmentOffset
);
575 for (const auto &Entry
: Entries
) {
576 const std::string Id
= getBuildIdString(Entry
);
577 if (BuildIdsSet
.contains(Id
))
579 BuildIds
.push_back(Id
);
580 BuildIdsSet
.insert(Id
);
583 Next
+= Header
->TotalSize
;
588 Error
RawMemProfReader::readRawProfile(
589 std::unique_ptr
<MemoryBuffer
> DataBuffer
) {
590 const char *Next
= DataBuffer
->getBufferStart();
592 while (Next
< DataBuffer
->getBufferEnd()) {
593 auto *Header
= reinterpret_cast<const memprof::Header
*>(Next
);
595 // Read in the segment information, check whether its the same across all
596 // profiles in this binary file.
597 const llvm::SmallVector
<SegmentEntry
> Entries
=
598 readSegmentEntries(Next
+ Header
->SegmentOffset
);
599 if (!SegmentInfo
.empty() && SegmentInfo
!= Entries
) {
600 // We do not expect segment information to change when deserializing from
601 // the same binary profile file. This can happen if dynamic libraries are
602 // loaded/unloaded between profile dumping.
603 return make_error
<InstrProfError
>(
604 instrprof_error::malformed
,
605 "memprof raw profile has different segment information");
607 SegmentInfo
.assign(Entries
.begin(), Entries
.end());
609 // Read in the MemInfoBlocks. Merge them based on stack id - we assume that
610 // raw profiles in the same binary file are from the same process so the
611 // stackdepot ids are the same.
612 for (const auto &Value
: readMemInfoBlocks(Next
+ Header
->MIBOffset
)) {
613 if (CallstackProfileData
.count(Value
.first
)) {
614 CallstackProfileData
[Value
.first
].Merge(Value
.second
);
616 CallstackProfileData
[Value
.first
] = Value
.second
;
620 // Read in the callstack for each ids. For multiple raw profiles in the same
621 // file, we expect that the callstack is the same for a unique id.
622 const CallStackMap CSM
= readStackInfo(Next
+ Header
->StackOffset
);
623 if (StackMap
.empty()) {
626 if (mergeStackMap(CSM
, StackMap
))
627 return make_error
<InstrProfError
>(
628 instrprof_error::malformed
,
629 "memprof raw profile got different call stack for same id");
632 Next
+= Header
->TotalSize
;
635 return Error::success();
638 object::SectionedAddress
639 RawMemProfReader::getModuleOffset(const uint64_t VirtualAddress
) {
640 if (VirtualAddress
> ProfiledTextSegmentStart
&&
641 VirtualAddress
<= ProfiledTextSegmentEnd
) {
642 // For PIE binaries, the preferred address is zero and we adjust the virtual
643 // address by start of the profiled segment assuming that the offset of the
644 // segment in the binary is zero. For non-PIE binaries the preferred and
645 // profiled segment addresses should be equal and this is a no-op.
646 const uint64_t AdjustedAddress
=
647 VirtualAddress
+ PreferredTextSegmentAddress
- ProfiledTextSegmentStart
;
648 return object::SectionedAddress
{AdjustedAddress
};
650 // Addresses which do not originate from the profiled text segment in the
651 // binary are not adjusted. These will fail symbolization and be filtered out
652 // during processing.
653 return object::SectionedAddress
{VirtualAddress
};
656 Error
RawMemProfReader::readNextRecord(
657 GuidMemProfRecordPair
&GuidRecord
,
658 std::function
<const Frame(const FrameId
)> Callback
) {
659 // Create a new callback for the RawMemProfRecord iterator so that we can
660 // provide the symbol name if the reader was initialized with KeepSymbolName =
661 // true. This is useful for debugging and testing.
662 auto IdToFrameCallback
= [this](const FrameId Id
) {
663 Frame F
= this->idToFrame(Id
);
664 if (!this->KeepSymbolName
)
666 auto Iter
= this->GuidToSymbolName
.find(F
.Function
);
667 assert(Iter
!= this->GuidToSymbolName
.end());
668 F
.SymbolName
= Iter
->getSecond();
671 return MemProfReader::readNextRecord(GuidRecord
, IdToFrameCallback
);
673 } // namespace memprof