1*0fca6ea1SDimitry Andric //===- RawMemProfReader.cpp - Instrumented memory profiling reader --------===// 2*0fca6ea1SDimitry Andric // 3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0fca6ea1SDimitry Andric // 7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 8*0fca6ea1SDimitry Andric // 9*0fca6ea1SDimitry Andric // This file contains support for reading MemProf profiling data. 10*0fca6ea1SDimitry Andric // 11*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===// 12*0fca6ea1SDimitry Andric 13*0fca6ea1SDimitry Andric #include <algorithm> 14*0fca6ea1SDimitry Andric #include <cstdint> 15*0fca6ea1SDimitry Andric #include <memory> 16*0fca6ea1SDimitry Andric #include <type_traits> 17*0fca6ea1SDimitry Andric 18*0fca6ea1SDimitry Andric #include "llvm/ADT/ArrayRef.h" 19*0fca6ea1SDimitry Andric #include "llvm/ADT/DenseMap.h" 20*0fca6ea1SDimitry Andric #include "llvm/ADT/SetVector.h" 21*0fca6ea1SDimitry Andric #include "llvm/ADT/SmallSet.h" 22*0fca6ea1SDimitry Andric #include "llvm/ADT/SmallVector.h" 23*0fca6ea1SDimitry Andric #include "llvm/ADT/StringExtras.h" 24*0fca6ea1SDimitry Andric #include "llvm/ADT/Twine.h" 25*0fca6ea1SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFContext.h" 26*0fca6ea1SDimitry Andric #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" 27*0fca6ea1SDimitry Andric #include "llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h" 28*0fca6ea1SDimitry Andric #include "llvm/Object/Binary.h" 29*0fca6ea1SDimitry Andric #include "llvm/Object/BuildID.h" 30*0fca6ea1SDimitry Andric #include "llvm/Object/ELFObjectFile.h" 31*0fca6ea1SDimitry Andric #include "llvm/Object/ObjectFile.h" 32*0fca6ea1SDimitry Andric #include "llvm/ProfileData/InstrProf.h" 33*0fca6ea1SDimitry Andric #include "llvm/ProfileData/MemProf.h" 34*0fca6ea1SDimitry Andric #include "llvm/ProfileData/MemProfData.inc" 35*0fca6ea1SDimitry Andric #include "llvm/ProfileData/MemProfReader.h" 36*0fca6ea1SDimitry Andric #include "llvm/ProfileData/SampleProf.h" 37*0fca6ea1SDimitry Andric #include "llvm/Support/Debug.h" 38*0fca6ea1SDimitry Andric #include "llvm/Support/Endian.h" 39*0fca6ea1SDimitry Andric #include "llvm/Support/Error.h" 40*0fca6ea1SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 41*0fca6ea1SDimitry Andric #include "llvm/Support/Path.h" 42*0fca6ea1SDimitry Andric 43*0fca6ea1SDimitry Andric #define DEBUG_TYPE "memprof" 44*0fca6ea1SDimitry Andric namespace llvm { 45*0fca6ea1SDimitry Andric namespace memprof { 46*0fca6ea1SDimitry Andric namespace { 47*0fca6ea1SDimitry Andric template <class T = uint64_t> inline T alignedRead(const char *Ptr) { 48*0fca6ea1SDimitry Andric static_assert(std::is_pod<T>::value, "Not a pod type."); 49*0fca6ea1SDimitry Andric assert(reinterpret_cast<size_t>(Ptr) % sizeof(T) == 0 && "Unaligned Read"); 50*0fca6ea1SDimitry Andric return *reinterpret_cast<const T *>(Ptr); 51*0fca6ea1SDimitry Andric } 52*0fca6ea1SDimitry Andric 53*0fca6ea1SDimitry Andric Error checkBuffer(const MemoryBuffer &Buffer) { 54*0fca6ea1SDimitry Andric if (!RawMemProfReader::hasFormat(Buffer)) 55*0fca6ea1SDimitry Andric return make_error<InstrProfError>(instrprof_error::bad_magic); 56*0fca6ea1SDimitry Andric 57*0fca6ea1SDimitry Andric if (Buffer.getBufferSize() == 0) 58*0fca6ea1SDimitry Andric return make_error<InstrProfError>(instrprof_error::empty_raw_profile); 59*0fca6ea1SDimitry Andric 60*0fca6ea1SDimitry Andric if (Buffer.getBufferSize() < sizeof(Header)) { 61*0fca6ea1SDimitry Andric return make_error<InstrProfError>(instrprof_error::truncated); 62*0fca6ea1SDimitry Andric } 63*0fca6ea1SDimitry Andric 64*0fca6ea1SDimitry Andric // The size of the buffer can be > header total size since we allow repeated 65*0fca6ea1SDimitry Andric // serialization of memprof profiles to the same file. 66*0fca6ea1SDimitry Andric uint64_t TotalSize = 0; 67*0fca6ea1SDimitry Andric const char *Next = Buffer.getBufferStart(); 68*0fca6ea1SDimitry Andric while (Next < Buffer.getBufferEnd()) { 69*0fca6ea1SDimitry Andric const auto *H = reinterpret_cast<const Header *>(Next); 70*0fca6ea1SDimitry Andric 71*0fca6ea1SDimitry Andric // Check if the version in header is among the supported versions. 72*0fca6ea1SDimitry Andric bool IsSupported = false; 73*0fca6ea1SDimitry Andric for (auto SupportedVersion : MEMPROF_RAW_SUPPORTED_VERSIONS) { 74*0fca6ea1SDimitry Andric if (H->Version == SupportedVersion) 75*0fca6ea1SDimitry Andric IsSupported = true; 76*0fca6ea1SDimitry Andric } 77*0fca6ea1SDimitry Andric if (!IsSupported) { 78*0fca6ea1SDimitry Andric return make_error<InstrProfError>(instrprof_error::unsupported_version); 79*0fca6ea1SDimitry Andric } 80*0fca6ea1SDimitry Andric 81*0fca6ea1SDimitry Andric TotalSize += H->TotalSize; 82*0fca6ea1SDimitry Andric Next += H->TotalSize; 83*0fca6ea1SDimitry Andric } 84*0fca6ea1SDimitry Andric 85*0fca6ea1SDimitry Andric if (Buffer.getBufferSize() != TotalSize) { 86*0fca6ea1SDimitry Andric return make_error<InstrProfError>(instrprof_error::malformed); 87*0fca6ea1SDimitry Andric } 88*0fca6ea1SDimitry Andric return Error::success(); 89*0fca6ea1SDimitry Andric } 90*0fca6ea1SDimitry Andric 91*0fca6ea1SDimitry Andric llvm::SmallVector<SegmentEntry> readSegmentEntries(const char *Ptr) { 92*0fca6ea1SDimitry Andric using namespace support; 93*0fca6ea1SDimitry Andric 94*0fca6ea1SDimitry Andric const uint64_t NumItemsToRead = 95*0fca6ea1SDimitry Andric endian::readNext<uint64_t, llvm::endianness::little>(Ptr); 96*0fca6ea1SDimitry Andric llvm::SmallVector<SegmentEntry> Items; 97*0fca6ea1SDimitry Andric for (uint64_t I = 0; I < NumItemsToRead; I++) { 98*0fca6ea1SDimitry Andric Items.push_back(*reinterpret_cast<const SegmentEntry *>( 99*0fca6ea1SDimitry Andric Ptr + I * sizeof(SegmentEntry))); 100*0fca6ea1SDimitry Andric } 101*0fca6ea1SDimitry Andric return Items; 102*0fca6ea1SDimitry Andric } 103*0fca6ea1SDimitry Andric 104*0fca6ea1SDimitry Andric llvm::SmallVector<std::pair<uint64_t, MemInfoBlock>> 105*0fca6ea1SDimitry Andric readMemInfoBlocksV3(const char *Ptr) { 106*0fca6ea1SDimitry Andric using namespace support; 107*0fca6ea1SDimitry Andric 108*0fca6ea1SDimitry Andric const uint64_t NumItemsToRead = 109*0fca6ea1SDimitry Andric endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr); 110*0fca6ea1SDimitry Andric 111*0fca6ea1SDimitry Andric llvm::SmallVector<std::pair<uint64_t, MemInfoBlock>> Items; 112*0fca6ea1SDimitry Andric for (uint64_t I = 0; I < NumItemsToRead; I++) { 113*0fca6ea1SDimitry Andric const uint64_t Id = 114*0fca6ea1SDimitry Andric endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr); 115*0fca6ea1SDimitry Andric 116*0fca6ea1SDimitry Andric // We cheat a bit here and remove the const from cast to set the 117*0fca6ea1SDimitry Andric // Histogram Pointer to newly allocated buffer. We also cheat, since V3 and 118*0fca6ea1SDimitry Andric // V4 do not have the same fields. V3 is missing AccessHistogramSize and 119*0fca6ea1SDimitry Andric // AccessHistogram. This means we read "dirty" data in here, but it should 120*0fca6ea1SDimitry Andric // not segfault, since there will be callstack data placed after this in the 121*0fca6ea1SDimitry Andric // binary format. 122*0fca6ea1SDimitry Andric MemInfoBlock MIB = *reinterpret_cast<const MemInfoBlock *>(Ptr); 123*0fca6ea1SDimitry Andric // Overwrite dirty data. 124*0fca6ea1SDimitry Andric MIB.AccessHistogramSize = 0; 125*0fca6ea1SDimitry Andric MIB.AccessHistogram = 0; 126*0fca6ea1SDimitry Andric 127*0fca6ea1SDimitry Andric Items.push_back({Id, MIB}); 128*0fca6ea1SDimitry Andric // Only increment by the size of MIB in V3. 129*0fca6ea1SDimitry Andric Ptr += MEMPROF_V3_MIB_SIZE; 130*0fca6ea1SDimitry Andric } 131*0fca6ea1SDimitry Andric return Items; 132*0fca6ea1SDimitry Andric } 133*0fca6ea1SDimitry Andric 134*0fca6ea1SDimitry Andric llvm::SmallVector<std::pair<uint64_t, MemInfoBlock>> 135*0fca6ea1SDimitry Andric readMemInfoBlocksV4(const char *Ptr) { 136*0fca6ea1SDimitry Andric using namespace support; 137*0fca6ea1SDimitry Andric 138*0fca6ea1SDimitry Andric const uint64_t NumItemsToRead = 139*0fca6ea1SDimitry Andric endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr); 140*0fca6ea1SDimitry Andric 141*0fca6ea1SDimitry Andric llvm::SmallVector<std::pair<uint64_t, MemInfoBlock>> Items; 142*0fca6ea1SDimitry Andric for (uint64_t I = 0; I < NumItemsToRead; I++) { 143*0fca6ea1SDimitry Andric const uint64_t Id = 144*0fca6ea1SDimitry Andric endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr); 145*0fca6ea1SDimitry Andric // We cheat a bit here and remove the const from cast to set the 146*0fca6ea1SDimitry Andric // Histogram Pointer to newly allocated buffer. 147*0fca6ea1SDimitry Andric MemInfoBlock MIB = *reinterpret_cast<const MemInfoBlock *>(Ptr); 148*0fca6ea1SDimitry Andric 149*0fca6ea1SDimitry Andric // Only increment by size of MIB since readNext implicitly increments. 150*0fca6ea1SDimitry Andric Ptr += sizeof(MemInfoBlock); 151*0fca6ea1SDimitry Andric 152*0fca6ea1SDimitry Andric if (MIB.AccessHistogramSize > 0) { 153*0fca6ea1SDimitry Andric MIB.AccessHistogram = 154*0fca6ea1SDimitry Andric (uintptr_t)malloc(MIB.AccessHistogramSize * sizeof(uint64_t)); 155*0fca6ea1SDimitry Andric } 156*0fca6ea1SDimitry Andric 157*0fca6ea1SDimitry Andric for (uint64_t J = 0; J < MIB.AccessHistogramSize; J++) { 158*0fca6ea1SDimitry Andric ((uint64_t *)MIB.AccessHistogram)[J] = 159*0fca6ea1SDimitry Andric endian::readNext<uint64_t, llvm::endianness::little, unaligned>(Ptr); 160*0fca6ea1SDimitry Andric } 161*0fca6ea1SDimitry Andric Items.push_back({Id, MIB}); 162*0fca6ea1SDimitry Andric } 163*0fca6ea1SDimitry Andric return Items; 164*0fca6ea1SDimitry Andric } 165*0fca6ea1SDimitry Andric 166*0fca6ea1SDimitry Andric CallStackMap readStackInfo(const char *Ptr) { 167*0fca6ea1SDimitry Andric using namespace support; 168*0fca6ea1SDimitry Andric 169*0fca6ea1SDimitry Andric const uint64_t NumItemsToRead = 170*0fca6ea1SDimitry Andric endian::readNext<uint64_t, llvm::endianness::little>(Ptr); 171*0fca6ea1SDimitry Andric CallStackMap Items; 172*0fca6ea1SDimitry Andric 173*0fca6ea1SDimitry Andric for (uint64_t I = 0; I < NumItemsToRead; I++) { 174*0fca6ea1SDimitry Andric const uint64_t StackId = 175*0fca6ea1SDimitry Andric endian::readNext<uint64_t, llvm::endianness::little>(Ptr); 176*0fca6ea1SDimitry Andric const uint64_t NumPCs = 177*0fca6ea1SDimitry Andric endian::readNext<uint64_t, llvm::endianness::little>(Ptr); 178*0fca6ea1SDimitry Andric 179*0fca6ea1SDimitry Andric SmallVector<uint64_t> CallStack; 180*0fca6ea1SDimitry Andric CallStack.reserve(NumPCs); 181*0fca6ea1SDimitry Andric for (uint64_t J = 0; J < NumPCs; J++) { 182*0fca6ea1SDimitry Andric CallStack.push_back( 183*0fca6ea1SDimitry Andric endian::readNext<uint64_t, llvm::endianness::little>(Ptr)); 184*0fca6ea1SDimitry Andric } 185*0fca6ea1SDimitry Andric 186*0fca6ea1SDimitry Andric Items[StackId] = CallStack; 187*0fca6ea1SDimitry Andric } 188*0fca6ea1SDimitry Andric return Items; 189*0fca6ea1SDimitry Andric } 190*0fca6ea1SDimitry Andric 191*0fca6ea1SDimitry Andric // Merges the contents of stack information in \p From to \p To. Returns true if 192*0fca6ea1SDimitry Andric // any stack ids observed previously map to a different set of program counter 193*0fca6ea1SDimitry Andric // addresses. 194*0fca6ea1SDimitry Andric bool mergeStackMap(const CallStackMap &From, CallStackMap &To) { 195*0fca6ea1SDimitry Andric for (const auto &[Id, Stack] : From) { 196*0fca6ea1SDimitry Andric auto I = To.find(Id); 197*0fca6ea1SDimitry Andric if (I == To.end()) { 198*0fca6ea1SDimitry Andric To[Id] = Stack; 199*0fca6ea1SDimitry Andric } else { 200*0fca6ea1SDimitry Andric // Check that the PCs are the same (in order). 201*0fca6ea1SDimitry Andric if (Stack != I->second) 202*0fca6ea1SDimitry Andric return true; 203*0fca6ea1SDimitry Andric } 204*0fca6ea1SDimitry Andric } 205*0fca6ea1SDimitry Andric return false; 206*0fca6ea1SDimitry Andric } 207*0fca6ea1SDimitry Andric 208*0fca6ea1SDimitry Andric Error report(Error E, const StringRef Context) { 209*0fca6ea1SDimitry Andric return joinErrors(createStringError(inconvertibleErrorCode(), Context), 210*0fca6ea1SDimitry Andric std::move(E)); 211*0fca6ea1SDimitry Andric } 212*0fca6ea1SDimitry Andric 213*0fca6ea1SDimitry Andric bool isRuntimePath(const StringRef Path) { 214*0fca6ea1SDimitry Andric const StringRef Filename = llvm::sys::path::filename(Path); 215*0fca6ea1SDimitry Andric // This list should be updated in case new files with additional interceptors 216*0fca6ea1SDimitry Andric // are added to the memprof runtime. 217*0fca6ea1SDimitry Andric return Filename == "memprof_malloc_linux.cpp" || 218*0fca6ea1SDimitry Andric Filename == "memprof_interceptors.cpp" || 219*0fca6ea1SDimitry Andric Filename == "memprof_new_delete.cpp"; 220*0fca6ea1SDimitry Andric } 221*0fca6ea1SDimitry Andric 222*0fca6ea1SDimitry Andric std::string getBuildIdString(const SegmentEntry &Entry) { 223*0fca6ea1SDimitry Andric // If the build id is unset print a helpful string instead of all zeros. 224*0fca6ea1SDimitry Andric if (Entry.BuildIdSize == 0) 225*0fca6ea1SDimitry Andric return "<None>"; 226*0fca6ea1SDimitry Andric 227*0fca6ea1SDimitry Andric std::string Str; 228*0fca6ea1SDimitry Andric raw_string_ostream OS(Str); 229*0fca6ea1SDimitry Andric for (size_t I = 0; I < Entry.BuildIdSize; I++) { 230*0fca6ea1SDimitry Andric OS << format_hex_no_prefix(Entry.BuildId[I], 2); 231*0fca6ea1SDimitry Andric } 232*0fca6ea1SDimitry Andric return OS.str(); 233*0fca6ea1SDimitry Andric } 234*0fca6ea1SDimitry Andric } // namespace 235*0fca6ea1SDimitry Andric 236*0fca6ea1SDimitry Andric MemProfReader::MemProfReader( 237*0fca6ea1SDimitry Andric llvm::DenseMap<FrameId, Frame> FrameIdMap, 238*0fca6ea1SDimitry Andric llvm::MapVector<GlobalValue::GUID, IndexedMemProfRecord> ProfData) 239*0fca6ea1SDimitry Andric : IdToFrame(std::move(FrameIdMap)), 240*0fca6ea1SDimitry Andric FunctionProfileData(std::move(ProfData)) { 241*0fca6ea1SDimitry Andric // Populate CSId in each IndexedAllocationInfo and IndexedMemProfRecord 242*0fca6ea1SDimitry Andric // while storing CallStack in CSIdToCallStack. 243*0fca6ea1SDimitry Andric for (auto &KV : FunctionProfileData) { 244*0fca6ea1SDimitry Andric IndexedMemProfRecord &Record = KV.second; 245*0fca6ea1SDimitry Andric for (auto &AS : Record.AllocSites) { 246*0fca6ea1SDimitry Andric CallStackId CSId = hashCallStack(AS.CallStack); 247*0fca6ea1SDimitry Andric AS.CSId = CSId; 248*0fca6ea1SDimitry Andric CSIdToCallStack.insert({CSId, AS.CallStack}); 249*0fca6ea1SDimitry Andric } 250*0fca6ea1SDimitry Andric for (auto &CS : Record.CallSites) { 251*0fca6ea1SDimitry Andric CallStackId CSId = hashCallStack(CS); 252*0fca6ea1SDimitry Andric Record.CallSiteIds.push_back(CSId); 253*0fca6ea1SDimitry Andric CSIdToCallStack.insert({CSId, CS}); 254*0fca6ea1SDimitry Andric } 255*0fca6ea1SDimitry Andric } 256*0fca6ea1SDimitry Andric } 257*0fca6ea1SDimitry Andric 258*0fca6ea1SDimitry Andric Expected<std::unique_ptr<RawMemProfReader>> 259*0fca6ea1SDimitry Andric RawMemProfReader::create(const Twine &Path, const StringRef ProfiledBinary, 260*0fca6ea1SDimitry Andric bool KeepName) { 261*0fca6ea1SDimitry Andric auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path); 262*0fca6ea1SDimitry Andric if (std::error_code EC = BufferOr.getError()) 263*0fca6ea1SDimitry Andric return report(errorCodeToError(EC), Path.getSingleStringRef()); 264*0fca6ea1SDimitry Andric 265*0fca6ea1SDimitry Andric std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release()); 266*0fca6ea1SDimitry Andric return create(std::move(Buffer), ProfiledBinary, KeepName); 267*0fca6ea1SDimitry Andric } 268*0fca6ea1SDimitry Andric 269*0fca6ea1SDimitry Andric Expected<std::unique_ptr<RawMemProfReader>> 270*0fca6ea1SDimitry Andric RawMemProfReader::create(std::unique_ptr<MemoryBuffer> Buffer, 271*0fca6ea1SDimitry Andric const StringRef ProfiledBinary, bool KeepName) { 272*0fca6ea1SDimitry Andric if (Error E = checkBuffer(*Buffer)) 273*0fca6ea1SDimitry Andric return report(std::move(E), Buffer->getBufferIdentifier()); 274*0fca6ea1SDimitry Andric 275*0fca6ea1SDimitry Andric if (ProfiledBinary.empty()) { 276*0fca6ea1SDimitry Andric // Peek the build ids to print a helpful error message. 277*0fca6ea1SDimitry Andric const std::vector<std::string> BuildIds = peekBuildIds(Buffer.get()); 278*0fca6ea1SDimitry Andric std::string ErrorMessage( 279*0fca6ea1SDimitry Andric R"(Path to profiled binary is empty, expected binary with one of the following build ids: 280*0fca6ea1SDimitry Andric )"); 281*0fca6ea1SDimitry Andric for (const auto &Id : BuildIds) { 282*0fca6ea1SDimitry Andric ErrorMessage += "\n BuildId: "; 283*0fca6ea1SDimitry Andric ErrorMessage += Id; 284*0fca6ea1SDimitry Andric } 285*0fca6ea1SDimitry Andric return report( 286*0fca6ea1SDimitry Andric make_error<StringError>(ErrorMessage, inconvertibleErrorCode()), 287*0fca6ea1SDimitry Andric /*Context=*/""); 288*0fca6ea1SDimitry Andric } 289*0fca6ea1SDimitry Andric 290*0fca6ea1SDimitry Andric auto BinaryOr = llvm::object::createBinary(ProfiledBinary); 291*0fca6ea1SDimitry Andric if (!BinaryOr) { 292*0fca6ea1SDimitry Andric return report(BinaryOr.takeError(), ProfiledBinary); 293*0fca6ea1SDimitry Andric } 294*0fca6ea1SDimitry Andric 295*0fca6ea1SDimitry Andric // Use new here since constructor is private. 296*0fca6ea1SDimitry Andric std::unique_ptr<RawMemProfReader> Reader( 297*0fca6ea1SDimitry Andric new RawMemProfReader(std::move(BinaryOr.get()), KeepName)); 298*0fca6ea1SDimitry Andric if (Error E = Reader->initialize(std::move(Buffer))) { 299*0fca6ea1SDimitry Andric return std::move(E); 300*0fca6ea1SDimitry Andric } 301*0fca6ea1SDimitry Andric return std::move(Reader); 302*0fca6ea1SDimitry Andric } 303*0fca6ea1SDimitry Andric 304*0fca6ea1SDimitry Andric // We need to make sure that all leftover MIB histograms that have not been 305*0fca6ea1SDimitry Andric // freed by merge are freed here. 306*0fca6ea1SDimitry Andric RawMemProfReader::~RawMemProfReader() { 307*0fca6ea1SDimitry Andric for (auto &[_, MIB] : CallstackProfileData) { 308*0fca6ea1SDimitry Andric if (MemprofRawVersion >= 4ULL && MIB.AccessHistogramSize > 0) { 309*0fca6ea1SDimitry Andric free((void *)MIB.AccessHistogram); 310*0fca6ea1SDimitry Andric } 311*0fca6ea1SDimitry Andric } 312*0fca6ea1SDimitry Andric } 313*0fca6ea1SDimitry Andric 314*0fca6ea1SDimitry Andric bool RawMemProfReader::hasFormat(const StringRef Path) { 315*0fca6ea1SDimitry Andric auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path); 316*0fca6ea1SDimitry Andric if (!BufferOr) 317*0fca6ea1SDimitry Andric return false; 318*0fca6ea1SDimitry Andric 319*0fca6ea1SDimitry Andric std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release()); 320*0fca6ea1SDimitry Andric return hasFormat(*Buffer); 321*0fca6ea1SDimitry Andric } 322*0fca6ea1SDimitry Andric 323*0fca6ea1SDimitry Andric bool RawMemProfReader::hasFormat(const MemoryBuffer &Buffer) { 324*0fca6ea1SDimitry Andric if (Buffer.getBufferSize() < sizeof(uint64_t)) 325*0fca6ea1SDimitry Andric return false; 326*0fca6ea1SDimitry Andric // Aligned read to sanity check that the buffer was allocated with at least 8b 327*0fca6ea1SDimitry Andric // alignment. 328*0fca6ea1SDimitry Andric const uint64_t Magic = alignedRead(Buffer.getBufferStart()); 329*0fca6ea1SDimitry Andric return Magic == MEMPROF_RAW_MAGIC_64; 330*0fca6ea1SDimitry Andric } 331*0fca6ea1SDimitry Andric 332*0fca6ea1SDimitry Andric void RawMemProfReader::printYAML(raw_ostream &OS) { 333*0fca6ea1SDimitry Andric uint64_t NumAllocFunctions = 0, NumMibInfo = 0; 334*0fca6ea1SDimitry Andric for (const auto &KV : FunctionProfileData) { 335*0fca6ea1SDimitry Andric const size_t NumAllocSites = KV.second.AllocSites.size(); 336*0fca6ea1SDimitry Andric if (NumAllocSites > 0) { 337*0fca6ea1SDimitry Andric NumAllocFunctions++; 338*0fca6ea1SDimitry Andric NumMibInfo += NumAllocSites; 339*0fca6ea1SDimitry Andric } 340*0fca6ea1SDimitry Andric } 341*0fca6ea1SDimitry Andric 342*0fca6ea1SDimitry Andric OS << "MemprofProfile:\n"; 343*0fca6ea1SDimitry Andric OS << " Summary:\n"; 344*0fca6ea1SDimitry Andric OS << " Version: " << MemprofRawVersion << "\n"; 345*0fca6ea1SDimitry Andric OS << " NumSegments: " << SegmentInfo.size() << "\n"; 346*0fca6ea1SDimitry Andric OS << " NumMibInfo: " << NumMibInfo << "\n"; 347*0fca6ea1SDimitry Andric OS << " NumAllocFunctions: " << NumAllocFunctions << "\n"; 348*0fca6ea1SDimitry Andric OS << " NumStackOffsets: " << StackMap.size() << "\n"; 349*0fca6ea1SDimitry Andric // Print out the segment information. 350*0fca6ea1SDimitry Andric OS << " Segments:\n"; 351*0fca6ea1SDimitry Andric for (const auto &Entry : SegmentInfo) { 352*0fca6ea1SDimitry Andric OS << " -\n"; 353*0fca6ea1SDimitry Andric OS << " BuildId: " << getBuildIdString(Entry) << "\n"; 354*0fca6ea1SDimitry Andric OS << " Start: 0x" << llvm::utohexstr(Entry.Start) << "\n"; 355*0fca6ea1SDimitry Andric OS << " End: 0x" << llvm::utohexstr(Entry.End) << "\n"; 356*0fca6ea1SDimitry Andric OS << " Offset: 0x" << llvm::utohexstr(Entry.Offset) << "\n"; 357*0fca6ea1SDimitry Andric } 358*0fca6ea1SDimitry Andric // Print out the merged contents of the profiles. 359*0fca6ea1SDimitry Andric OS << " Records:\n"; 360*0fca6ea1SDimitry Andric for (const auto &[GUID, Record] : *this) { 361*0fca6ea1SDimitry Andric OS << " -\n"; 362*0fca6ea1SDimitry Andric OS << " FunctionGUID: " << GUID << "\n"; 363*0fca6ea1SDimitry Andric Record.print(OS); 364*0fca6ea1SDimitry Andric } 365*0fca6ea1SDimitry Andric } 366*0fca6ea1SDimitry Andric 367*0fca6ea1SDimitry Andric Error RawMemProfReader::initialize(std::unique_ptr<MemoryBuffer> DataBuffer) { 368*0fca6ea1SDimitry Andric const StringRef FileName = Binary.getBinary()->getFileName(); 369*0fca6ea1SDimitry Andric 370*0fca6ea1SDimitry Andric auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Binary.getBinary()); 371*0fca6ea1SDimitry Andric if (!ElfObject) { 372*0fca6ea1SDimitry Andric return report(make_error<StringError>(Twine("Not an ELF file: "), 373*0fca6ea1SDimitry Andric inconvertibleErrorCode()), 374*0fca6ea1SDimitry Andric FileName); 375*0fca6ea1SDimitry Andric } 376*0fca6ea1SDimitry Andric 377*0fca6ea1SDimitry Andric // Check whether the profiled binary was built with position independent code 378*0fca6ea1SDimitry Andric // (PIC). Perform sanity checks for assumptions we rely on to simplify 379*0fca6ea1SDimitry Andric // symbolization. 380*0fca6ea1SDimitry Andric auto *Elf64LEObject = llvm::cast<llvm::object::ELF64LEObjectFile>(ElfObject); 381*0fca6ea1SDimitry Andric const llvm::object::ELF64LEFile &ElfFile = Elf64LEObject->getELFFile(); 382*0fca6ea1SDimitry Andric auto PHdrsOr = ElfFile.program_headers(); 383*0fca6ea1SDimitry Andric if (!PHdrsOr) 384*0fca6ea1SDimitry Andric return report( 385*0fca6ea1SDimitry Andric make_error<StringError>(Twine("Could not read program headers: "), 386*0fca6ea1SDimitry Andric inconvertibleErrorCode()), 387*0fca6ea1SDimitry Andric FileName); 388*0fca6ea1SDimitry Andric 389*0fca6ea1SDimitry Andric int NumExecutableSegments = 0; 390*0fca6ea1SDimitry Andric for (const auto &Phdr : *PHdrsOr) { 391*0fca6ea1SDimitry Andric if (Phdr.p_type == ELF::PT_LOAD) { 392*0fca6ea1SDimitry Andric if (Phdr.p_flags & ELF::PF_X) { 393*0fca6ea1SDimitry Andric // We assume only one text segment in the main binary for simplicity and 394*0fca6ea1SDimitry Andric // reduce the overhead of checking multiple ranges during symbolization. 395*0fca6ea1SDimitry Andric if (++NumExecutableSegments > 1) { 396*0fca6ea1SDimitry Andric return report( 397*0fca6ea1SDimitry Andric make_error<StringError>( 398*0fca6ea1SDimitry Andric "Expect only one executable load segment in the binary", 399*0fca6ea1SDimitry Andric inconvertibleErrorCode()), 400*0fca6ea1SDimitry Andric FileName); 401*0fca6ea1SDimitry Andric } 402*0fca6ea1SDimitry Andric // Segment will always be loaded at a page boundary, expect it to be 403*0fca6ea1SDimitry Andric // aligned already. Assume 4K pagesize for the machine from which the 404*0fca6ea1SDimitry Andric // profile has been collected. This should be fine for now, in case we 405*0fca6ea1SDimitry Andric // want to support other pagesizes it can be recorded in the raw profile 406*0fca6ea1SDimitry Andric // during collection. 407*0fca6ea1SDimitry Andric PreferredTextSegmentAddress = Phdr.p_vaddr; 408*0fca6ea1SDimitry Andric assert(Phdr.p_vaddr == (Phdr.p_vaddr & ~(0x1000 - 1U)) && 409*0fca6ea1SDimitry Andric "Expect p_vaddr to always be page aligned"); 410*0fca6ea1SDimitry Andric assert(Phdr.p_offset == 0 && "Expect p_offset = 0 for symbolization."); 411*0fca6ea1SDimitry Andric } 412*0fca6ea1SDimitry Andric } 413*0fca6ea1SDimitry Andric } 414*0fca6ea1SDimitry Andric 415*0fca6ea1SDimitry Andric auto Triple = ElfObject->makeTriple(); 416*0fca6ea1SDimitry Andric if (!Triple.isX86()) 417*0fca6ea1SDimitry Andric return report(make_error<StringError>(Twine("Unsupported target: ") + 418*0fca6ea1SDimitry Andric Triple.getArchName(), 419*0fca6ea1SDimitry Andric inconvertibleErrorCode()), 420*0fca6ea1SDimitry Andric FileName); 421*0fca6ea1SDimitry Andric 422*0fca6ea1SDimitry Andric // Process the raw profile. 423*0fca6ea1SDimitry Andric if (Error E = readRawProfile(std::move(DataBuffer))) 424*0fca6ea1SDimitry Andric return E; 425*0fca6ea1SDimitry Andric 426*0fca6ea1SDimitry Andric if (Error E = setupForSymbolization()) 427*0fca6ea1SDimitry Andric return E; 428*0fca6ea1SDimitry Andric 429*0fca6ea1SDimitry Andric auto *Object = cast<object::ObjectFile>(Binary.getBinary()); 430*0fca6ea1SDimitry Andric std::unique_ptr<DIContext> Context = DWARFContext::create( 431*0fca6ea1SDimitry Andric *Object, DWARFContext::ProcessDebugRelocations::Process); 432*0fca6ea1SDimitry Andric 433*0fca6ea1SDimitry Andric auto SOFOr = symbolize::SymbolizableObjectFile::create( 434*0fca6ea1SDimitry Andric Object, std::move(Context), /*UntagAddresses=*/false); 435*0fca6ea1SDimitry Andric if (!SOFOr) 436*0fca6ea1SDimitry Andric return report(SOFOr.takeError(), FileName); 437*0fca6ea1SDimitry Andric auto Symbolizer = std::move(SOFOr.get()); 438*0fca6ea1SDimitry Andric 439*0fca6ea1SDimitry Andric // The symbolizer ownership is moved into symbolizeAndFilterStackFrames so 440*0fca6ea1SDimitry Andric // that it is freed automatically at the end, when it is no longer used. This 441*0fca6ea1SDimitry Andric // reduces peak memory since it won't be live while also mapping the raw 442*0fca6ea1SDimitry Andric // profile into records afterwards. 443*0fca6ea1SDimitry Andric if (Error E = symbolizeAndFilterStackFrames(std::move(Symbolizer))) 444*0fca6ea1SDimitry Andric return E; 445*0fca6ea1SDimitry Andric 446*0fca6ea1SDimitry Andric return mapRawProfileToRecords(); 447*0fca6ea1SDimitry Andric } 448*0fca6ea1SDimitry Andric 449*0fca6ea1SDimitry Andric Error RawMemProfReader::setupForSymbolization() { 450*0fca6ea1SDimitry Andric auto *Object = cast<object::ObjectFile>(Binary.getBinary()); 451*0fca6ea1SDimitry Andric object::BuildIDRef BinaryId = object::getBuildID(Object); 452*0fca6ea1SDimitry Andric if (BinaryId.empty()) 453*0fca6ea1SDimitry Andric return make_error<StringError>(Twine("No build id found in binary ") + 454*0fca6ea1SDimitry Andric Binary.getBinary()->getFileName(), 455*0fca6ea1SDimitry Andric inconvertibleErrorCode()); 456*0fca6ea1SDimitry Andric 457*0fca6ea1SDimitry Andric int NumMatched = 0; 458*0fca6ea1SDimitry Andric for (const auto &Entry : SegmentInfo) { 459*0fca6ea1SDimitry Andric llvm::ArrayRef<uint8_t> SegmentId(Entry.BuildId, Entry.BuildIdSize); 460*0fca6ea1SDimitry Andric if (BinaryId == SegmentId) { 461*0fca6ea1SDimitry Andric // We assume only one text segment in the main binary for simplicity and 462*0fca6ea1SDimitry Andric // reduce the overhead of checking multiple ranges during symbolization. 463*0fca6ea1SDimitry Andric if (++NumMatched > 1) { 464*0fca6ea1SDimitry Andric return make_error<StringError>( 465*0fca6ea1SDimitry Andric "We expect only one executable segment in the profiled binary", 466*0fca6ea1SDimitry Andric inconvertibleErrorCode()); 467*0fca6ea1SDimitry Andric } 468*0fca6ea1SDimitry Andric ProfiledTextSegmentStart = Entry.Start; 469*0fca6ea1SDimitry Andric ProfiledTextSegmentEnd = Entry.End; 470*0fca6ea1SDimitry Andric } 471*0fca6ea1SDimitry Andric } 472*0fca6ea1SDimitry Andric assert(NumMatched != 0 && "No matching executable segments in segment info."); 473*0fca6ea1SDimitry Andric assert((PreferredTextSegmentAddress == 0 || 474*0fca6ea1SDimitry Andric (PreferredTextSegmentAddress == ProfiledTextSegmentStart)) && 475*0fca6ea1SDimitry Andric "Expect text segment address to be 0 or equal to profiled text " 476*0fca6ea1SDimitry Andric "segment start."); 477*0fca6ea1SDimitry Andric return Error::success(); 478*0fca6ea1SDimitry Andric } 479*0fca6ea1SDimitry Andric 480*0fca6ea1SDimitry Andric Error RawMemProfReader::mapRawProfileToRecords() { 481*0fca6ea1SDimitry Andric // Hold a mapping from function to each callsite location we encounter within 482*0fca6ea1SDimitry Andric // it that is part of some dynamic allocation context. The location is stored 483*0fca6ea1SDimitry Andric // as a pointer to a symbolized list of inline frames. 484*0fca6ea1SDimitry Andric using LocationPtr = const llvm::SmallVector<FrameId> *; 485*0fca6ea1SDimitry Andric llvm::MapVector<GlobalValue::GUID, llvm::SetVector<LocationPtr>> 486*0fca6ea1SDimitry Andric PerFunctionCallSites; 487*0fca6ea1SDimitry Andric 488*0fca6ea1SDimitry Andric // Convert the raw profile callstack data into memprof records. While doing so 489*0fca6ea1SDimitry Andric // keep track of related contexts so that we can fill these in later. 490*0fca6ea1SDimitry Andric for (const auto &[StackId, MIB] : CallstackProfileData) { 491*0fca6ea1SDimitry Andric auto It = StackMap.find(StackId); 492*0fca6ea1SDimitry Andric if (It == StackMap.end()) 493*0fca6ea1SDimitry Andric return make_error<InstrProfError>( 494*0fca6ea1SDimitry Andric instrprof_error::malformed, 495*0fca6ea1SDimitry Andric "memprof callstack record does not contain id: " + Twine(StackId)); 496*0fca6ea1SDimitry Andric 497*0fca6ea1SDimitry Andric // Construct the symbolized callstack. 498*0fca6ea1SDimitry Andric llvm::SmallVector<FrameId> Callstack; 499*0fca6ea1SDimitry Andric Callstack.reserve(It->getSecond().size()); 500*0fca6ea1SDimitry Andric 501*0fca6ea1SDimitry Andric llvm::ArrayRef<uint64_t> Addresses = It->getSecond(); 502*0fca6ea1SDimitry Andric for (size_t I = 0; I < Addresses.size(); I++) { 503*0fca6ea1SDimitry Andric const uint64_t Address = Addresses[I]; 504*0fca6ea1SDimitry Andric assert(SymbolizedFrame.count(Address) > 0 && 505*0fca6ea1SDimitry Andric "Address not found in SymbolizedFrame map"); 506*0fca6ea1SDimitry Andric const SmallVector<FrameId> &Frames = SymbolizedFrame[Address]; 507*0fca6ea1SDimitry Andric 508*0fca6ea1SDimitry Andric assert(!idToFrame(Frames.back()).IsInlineFrame && 509*0fca6ea1SDimitry Andric "The last frame should not be inlined"); 510*0fca6ea1SDimitry Andric 511*0fca6ea1SDimitry Andric // Record the callsites for each function. Skip the first frame of the 512*0fca6ea1SDimitry Andric // first address since it is the allocation site itself that is recorded 513*0fca6ea1SDimitry Andric // as an alloc site. 514*0fca6ea1SDimitry Andric for (size_t J = 0; J < Frames.size(); J++) { 515*0fca6ea1SDimitry Andric if (I == 0 && J == 0) 516*0fca6ea1SDimitry Andric continue; 517*0fca6ea1SDimitry Andric // We attach the entire bottom-up frame here for the callsite even 518*0fca6ea1SDimitry Andric // though we only need the frames up to and including the frame for 519*0fca6ea1SDimitry Andric // Frames[J].Function. This will enable better deduplication for 520*0fca6ea1SDimitry Andric // compression in the future. 521*0fca6ea1SDimitry Andric const GlobalValue::GUID Guid = idToFrame(Frames[J]).Function; 522*0fca6ea1SDimitry Andric PerFunctionCallSites[Guid].insert(&Frames); 523*0fca6ea1SDimitry Andric } 524*0fca6ea1SDimitry Andric 525*0fca6ea1SDimitry Andric // Add all the frames to the current allocation callstack. 526*0fca6ea1SDimitry Andric Callstack.append(Frames.begin(), Frames.end()); 527*0fca6ea1SDimitry Andric } 528*0fca6ea1SDimitry Andric 529*0fca6ea1SDimitry Andric CallStackId CSId = hashCallStack(Callstack); 530*0fca6ea1SDimitry Andric CSIdToCallStack.insert({CSId, Callstack}); 531*0fca6ea1SDimitry Andric 532*0fca6ea1SDimitry Andric // We attach the memprof record to each function bottom-up including the 533*0fca6ea1SDimitry Andric // first non-inline frame. 534*0fca6ea1SDimitry Andric for (size_t I = 0; /*Break out using the condition below*/; I++) { 535*0fca6ea1SDimitry Andric const Frame &F = idToFrame(Callstack[I]); 536*0fca6ea1SDimitry Andric auto Result = 537*0fca6ea1SDimitry Andric FunctionProfileData.insert({F.Function, IndexedMemProfRecord()}); 538*0fca6ea1SDimitry Andric IndexedMemProfRecord &Record = Result.first->second; 539*0fca6ea1SDimitry Andric Record.AllocSites.emplace_back(Callstack, CSId, MIB); 540*0fca6ea1SDimitry Andric 541*0fca6ea1SDimitry Andric if (!F.IsInlineFrame) 542*0fca6ea1SDimitry Andric break; 543*0fca6ea1SDimitry Andric } 544*0fca6ea1SDimitry Andric } 545*0fca6ea1SDimitry Andric 546*0fca6ea1SDimitry Andric // Fill in the related callsites per function. 547*0fca6ea1SDimitry Andric for (const auto &[Id, Locs] : PerFunctionCallSites) { 548*0fca6ea1SDimitry Andric // Some functions may have only callsite data and no allocation data. Here 549*0fca6ea1SDimitry Andric // we insert a new entry for callsite data if we need to. 550*0fca6ea1SDimitry Andric auto Result = FunctionProfileData.insert({Id, IndexedMemProfRecord()}); 551*0fca6ea1SDimitry Andric IndexedMemProfRecord &Record = Result.first->second; 552*0fca6ea1SDimitry Andric for (LocationPtr Loc : Locs) { 553*0fca6ea1SDimitry Andric CallStackId CSId = hashCallStack(*Loc); 554*0fca6ea1SDimitry Andric CSIdToCallStack.insert({CSId, *Loc}); 555*0fca6ea1SDimitry Andric Record.CallSites.push_back(*Loc); 556*0fca6ea1SDimitry Andric Record.CallSiteIds.push_back(CSId); 557*0fca6ea1SDimitry Andric } 558*0fca6ea1SDimitry Andric } 559*0fca6ea1SDimitry Andric 560*0fca6ea1SDimitry Andric verifyFunctionProfileData(FunctionProfileData); 561*0fca6ea1SDimitry Andric 562*0fca6ea1SDimitry Andric return Error::success(); 563*0fca6ea1SDimitry Andric } 564*0fca6ea1SDimitry Andric 565*0fca6ea1SDimitry Andric Error RawMemProfReader::symbolizeAndFilterStackFrames( 566*0fca6ea1SDimitry Andric std::unique_ptr<llvm::symbolize::SymbolizableModule> Symbolizer) { 567*0fca6ea1SDimitry Andric // The specifier to use when symbolization is requested. 568*0fca6ea1SDimitry Andric const DILineInfoSpecifier Specifier( 569*0fca6ea1SDimitry Andric DILineInfoSpecifier::FileLineInfoKind::RawValue, 570*0fca6ea1SDimitry Andric DILineInfoSpecifier::FunctionNameKind::LinkageName); 571*0fca6ea1SDimitry Andric 572*0fca6ea1SDimitry Andric // For entries where all PCs in the callstack are discarded, we erase the 573*0fca6ea1SDimitry Andric // entry from the stack map. 574*0fca6ea1SDimitry Andric llvm::SmallVector<uint64_t> EntriesToErase; 575*0fca6ea1SDimitry Andric // We keep track of all prior discarded entries so that we can avoid invoking 576*0fca6ea1SDimitry Andric // the symbolizer for such entries. 577*0fca6ea1SDimitry Andric llvm::DenseSet<uint64_t> AllVAddrsToDiscard; 578*0fca6ea1SDimitry Andric for (auto &Entry : StackMap) { 579*0fca6ea1SDimitry Andric for (const uint64_t VAddr : Entry.getSecond()) { 580*0fca6ea1SDimitry Andric // Check if we have already symbolized and cached the result or if we 581*0fca6ea1SDimitry Andric // don't want to attempt symbolization since we know this address is bad. 582*0fca6ea1SDimitry Andric // In this case the address is also removed from the current callstack. 583*0fca6ea1SDimitry Andric if (SymbolizedFrame.count(VAddr) > 0 || 584*0fca6ea1SDimitry Andric AllVAddrsToDiscard.contains(VAddr)) 585*0fca6ea1SDimitry Andric continue; 586*0fca6ea1SDimitry Andric 587*0fca6ea1SDimitry Andric Expected<DIInliningInfo> DIOr = Symbolizer->symbolizeInlinedCode( 588*0fca6ea1SDimitry Andric getModuleOffset(VAddr), Specifier, /*UseSymbolTable=*/false); 589*0fca6ea1SDimitry Andric if (!DIOr) 590*0fca6ea1SDimitry Andric return DIOr.takeError(); 591*0fca6ea1SDimitry Andric DIInliningInfo DI = DIOr.get(); 592*0fca6ea1SDimitry Andric 593*0fca6ea1SDimitry Andric // Drop frames which we can't symbolize or if they belong to the runtime. 594*0fca6ea1SDimitry Andric if (DI.getFrame(0).FunctionName == DILineInfo::BadString || 595*0fca6ea1SDimitry Andric isRuntimePath(DI.getFrame(0).FileName)) { 596*0fca6ea1SDimitry Andric AllVAddrsToDiscard.insert(VAddr); 597*0fca6ea1SDimitry Andric continue; 598*0fca6ea1SDimitry Andric } 599*0fca6ea1SDimitry Andric 600*0fca6ea1SDimitry Andric for (size_t I = 0, NumFrames = DI.getNumberOfFrames(); I < NumFrames; 601*0fca6ea1SDimitry Andric I++) { 602*0fca6ea1SDimitry Andric const auto &DIFrame = DI.getFrame(I); 603*0fca6ea1SDimitry Andric const uint64_t Guid = 604*0fca6ea1SDimitry Andric IndexedMemProfRecord::getGUID(DIFrame.FunctionName); 605*0fca6ea1SDimitry Andric const Frame F(Guid, DIFrame.Line - DIFrame.StartLine, DIFrame.Column, 606*0fca6ea1SDimitry Andric // Only the last entry is not an inlined location. 607*0fca6ea1SDimitry Andric I != NumFrames - 1); 608*0fca6ea1SDimitry Andric // Here we retain a mapping from the GUID to canonical symbol name 609*0fca6ea1SDimitry Andric // instead of adding it to the frame object directly to reduce memory 610*0fca6ea1SDimitry Andric // overhead. This is because there can be many unique frames, 611*0fca6ea1SDimitry Andric // particularly for callsite frames. 612*0fca6ea1SDimitry Andric if (KeepSymbolName) { 613*0fca6ea1SDimitry Andric StringRef CanonicalName = 614*0fca6ea1SDimitry Andric sampleprof::FunctionSamples::getCanonicalFnName( 615*0fca6ea1SDimitry Andric DIFrame.FunctionName); 616*0fca6ea1SDimitry Andric GuidToSymbolName.insert({Guid, CanonicalName.str()}); 617*0fca6ea1SDimitry Andric } 618*0fca6ea1SDimitry Andric 619*0fca6ea1SDimitry Andric const FrameId Hash = F.hash(); 620*0fca6ea1SDimitry Andric IdToFrame.insert({Hash, F}); 621*0fca6ea1SDimitry Andric SymbolizedFrame[VAddr].push_back(Hash); 622*0fca6ea1SDimitry Andric } 623*0fca6ea1SDimitry Andric } 624*0fca6ea1SDimitry Andric 625*0fca6ea1SDimitry Andric auto &CallStack = Entry.getSecond(); 626*0fca6ea1SDimitry Andric llvm::erase_if(CallStack, [&AllVAddrsToDiscard](const uint64_t A) { 627*0fca6ea1SDimitry Andric return AllVAddrsToDiscard.contains(A); 628*0fca6ea1SDimitry Andric }); 629*0fca6ea1SDimitry Andric if (CallStack.empty()) 630*0fca6ea1SDimitry Andric EntriesToErase.push_back(Entry.getFirst()); 631*0fca6ea1SDimitry Andric } 632*0fca6ea1SDimitry Andric 633*0fca6ea1SDimitry Andric // Drop the entries where the callstack is empty. 634*0fca6ea1SDimitry Andric for (const uint64_t Id : EntriesToErase) { 635*0fca6ea1SDimitry Andric StackMap.erase(Id); 636*0fca6ea1SDimitry Andric if(CallstackProfileData[Id].AccessHistogramSize > 0) 637*0fca6ea1SDimitry Andric free((void*) CallstackProfileData[Id].AccessHistogram); 638*0fca6ea1SDimitry Andric CallstackProfileData.erase(Id); 639*0fca6ea1SDimitry Andric } 640*0fca6ea1SDimitry Andric 641*0fca6ea1SDimitry Andric if (StackMap.empty()) 642*0fca6ea1SDimitry Andric return make_error<InstrProfError>( 643*0fca6ea1SDimitry Andric instrprof_error::malformed, 644*0fca6ea1SDimitry Andric "no entries in callstack map after symbolization"); 645*0fca6ea1SDimitry Andric 646*0fca6ea1SDimitry Andric return Error::success(); 647*0fca6ea1SDimitry Andric } 648*0fca6ea1SDimitry Andric 649*0fca6ea1SDimitry Andric std::vector<std::string> 650*0fca6ea1SDimitry Andric RawMemProfReader::peekBuildIds(MemoryBuffer *DataBuffer) { 651*0fca6ea1SDimitry Andric const char *Next = DataBuffer->getBufferStart(); 652*0fca6ea1SDimitry Andric // Use a SetVector since a profile file may contain multiple raw profile 653*0fca6ea1SDimitry Andric // dumps, each with segment information. We want them unique and in order they 654*0fca6ea1SDimitry Andric // were stored in the profile; the profiled binary should be the first entry. 655*0fca6ea1SDimitry Andric // The runtime uses dl_iterate_phdr and the "... first object visited by 656*0fca6ea1SDimitry Andric // callback is the main program." 657*0fca6ea1SDimitry Andric // https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html 658*0fca6ea1SDimitry Andric llvm::SetVector<std::string, std::vector<std::string>, 659*0fca6ea1SDimitry Andric llvm::SmallSet<std::string, 10>> 660*0fca6ea1SDimitry Andric BuildIds; 661*0fca6ea1SDimitry Andric while (Next < DataBuffer->getBufferEnd()) { 662*0fca6ea1SDimitry Andric const auto *Header = reinterpret_cast<const memprof::Header *>(Next); 663*0fca6ea1SDimitry Andric 664*0fca6ea1SDimitry Andric const llvm::SmallVector<SegmentEntry> Entries = 665*0fca6ea1SDimitry Andric readSegmentEntries(Next + Header->SegmentOffset); 666*0fca6ea1SDimitry Andric 667*0fca6ea1SDimitry Andric for (const auto &Entry : Entries) 668*0fca6ea1SDimitry Andric BuildIds.insert(getBuildIdString(Entry)); 669*0fca6ea1SDimitry Andric 670*0fca6ea1SDimitry Andric Next += Header->TotalSize; 671*0fca6ea1SDimitry Andric } 672*0fca6ea1SDimitry Andric return BuildIds.takeVector(); 673*0fca6ea1SDimitry Andric } 674*0fca6ea1SDimitry Andric 675*0fca6ea1SDimitry Andric // FIXME: Add a schema for serializing similiar to IndexedMemprofReader. This 676*0fca6ea1SDimitry Andric // will help being able to deserialize different versions raw memprof versions 677*0fca6ea1SDimitry Andric // more easily. 678*0fca6ea1SDimitry Andric llvm::SmallVector<std::pair<uint64_t, MemInfoBlock>> 679*0fca6ea1SDimitry Andric RawMemProfReader::readMemInfoBlocks(const char *Ptr) { 680*0fca6ea1SDimitry Andric if (MemprofRawVersion == 3ULL) 681*0fca6ea1SDimitry Andric return readMemInfoBlocksV3(Ptr); 682*0fca6ea1SDimitry Andric if (MemprofRawVersion == 4ULL) 683*0fca6ea1SDimitry Andric return readMemInfoBlocksV4(Ptr); 684*0fca6ea1SDimitry Andric llvm_unreachable( 685*0fca6ea1SDimitry Andric "Panic: Unsupported version number when reading MemInfoBlocks"); 686*0fca6ea1SDimitry Andric } 687*0fca6ea1SDimitry Andric 688*0fca6ea1SDimitry Andric Error RawMemProfReader::readRawProfile( 689*0fca6ea1SDimitry Andric std::unique_ptr<MemoryBuffer> DataBuffer) { 690*0fca6ea1SDimitry Andric const char *Next = DataBuffer->getBufferStart(); 691*0fca6ea1SDimitry Andric 692*0fca6ea1SDimitry Andric while (Next < DataBuffer->getBufferEnd()) { 693*0fca6ea1SDimitry Andric const auto *Header = reinterpret_cast<const memprof::Header *>(Next); 694*0fca6ea1SDimitry Andric 695*0fca6ea1SDimitry Andric // Set Reader version to memprof raw version of profile. Checking if version 696*0fca6ea1SDimitry Andric // is supported is checked before creating the reader. 697*0fca6ea1SDimitry Andric MemprofRawVersion = Header->Version; 698*0fca6ea1SDimitry Andric 699*0fca6ea1SDimitry Andric // Read in the segment information, check whether its the same across all 700*0fca6ea1SDimitry Andric // profiles in this binary file. 701*0fca6ea1SDimitry Andric const llvm::SmallVector<SegmentEntry> Entries = 702*0fca6ea1SDimitry Andric readSegmentEntries(Next + Header->SegmentOffset); 703*0fca6ea1SDimitry Andric if (!SegmentInfo.empty() && SegmentInfo != Entries) { 704*0fca6ea1SDimitry Andric // We do not expect segment information to change when deserializing from 705*0fca6ea1SDimitry Andric // the same binary profile file. This can happen if dynamic libraries are 706*0fca6ea1SDimitry Andric // loaded/unloaded between profile dumping. 707*0fca6ea1SDimitry Andric return make_error<InstrProfError>( 708*0fca6ea1SDimitry Andric instrprof_error::malformed, 709*0fca6ea1SDimitry Andric "memprof raw profile has different segment information"); 710*0fca6ea1SDimitry Andric } 711*0fca6ea1SDimitry Andric SegmentInfo.assign(Entries.begin(), Entries.end()); 712*0fca6ea1SDimitry Andric 713*0fca6ea1SDimitry Andric // Read in the MemInfoBlocks. Merge them based on stack id - we assume that 714*0fca6ea1SDimitry Andric // raw profiles in the same binary file are from the same process so the 715*0fca6ea1SDimitry Andric // stackdepot ids are the same. 716*0fca6ea1SDimitry Andric for (const auto &[Id, MIB] : readMemInfoBlocks(Next + Header->MIBOffset)) { 717*0fca6ea1SDimitry Andric if (CallstackProfileData.count(Id)) { 718*0fca6ea1SDimitry Andric 719*0fca6ea1SDimitry Andric if (MemprofRawVersion >= 4ULL && 720*0fca6ea1SDimitry Andric (CallstackProfileData[Id].AccessHistogramSize > 0 || 721*0fca6ea1SDimitry Andric MIB.AccessHistogramSize > 0)) { 722*0fca6ea1SDimitry Andric uintptr_t ShorterHistogram; 723*0fca6ea1SDimitry Andric if (CallstackProfileData[Id].AccessHistogramSize > 724*0fca6ea1SDimitry Andric MIB.AccessHistogramSize) 725*0fca6ea1SDimitry Andric ShorterHistogram = MIB.AccessHistogram; 726*0fca6ea1SDimitry Andric else 727*0fca6ea1SDimitry Andric ShorterHistogram = CallstackProfileData[Id].AccessHistogram; 728*0fca6ea1SDimitry Andric CallstackProfileData[Id].Merge(MIB); 729*0fca6ea1SDimitry Andric free((void *)ShorterHistogram); 730*0fca6ea1SDimitry Andric } else { 731*0fca6ea1SDimitry Andric CallstackProfileData[Id].Merge(MIB); 732*0fca6ea1SDimitry Andric } 733*0fca6ea1SDimitry Andric } else { 734*0fca6ea1SDimitry Andric CallstackProfileData[Id] = MIB; 735*0fca6ea1SDimitry Andric } 736*0fca6ea1SDimitry Andric } 737*0fca6ea1SDimitry Andric 738*0fca6ea1SDimitry Andric // Read in the callstack for each ids. For multiple raw profiles in the same 739*0fca6ea1SDimitry Andric // file, we expect that the callstack is the same for a unique id. 740*0fca6ea1SDimitry Andric const CallStackMap CSM = readStackInfo(Next + Header->StackOffset); 741*0fca6ea1SDimitry Andric if (StackMap.empty()) { 742*0fca6ea1SDimitry Andric StackMap = CSM; 743*0fca6ea1SDimitry Andric } else { 744*0fca6ea1SDimitry Andric if (mergeStackMap(CSM, StackMap)) 745*0fca6ea1SDimitry Andric return make_error<InstrProfError>( 746*0fca6ea1SDimitry Andric instrprof_error::malformed, 747*0fca6ea1SDimitry Andric "memprof raw profile got different call stack for same id"); 748*0fca6ea1SDimitry Andric } 749*0fca6ea1SDimitry Andric 750*0fca6ea1SDimitry Andric Next += Header->TotalSize; 751*0fca6ea1SDimitry Andric } 752*0fca6ea1SDimitry Andric 753*0fca6ea1SDimitry Andric return Error::success(); 754*0fca6ea1SDimitry Andric } 755*0fca6ea1SDimitry Andric 756*0fca6ea1SDimitry Andric object::SectionedAddress 757*0fca6ea1SDimitry Andric RawMemProfReader::getModuleOffset(const uint64_t VirtualAddress) { 758*0fca6ea1SDimitry Andric if (VirtualAddress > ProfiledTextSegmentStart && 759*0fca6ea1SDimitry Andric VirtualAddress <= ProfiledTextSegmentEnd) { 760*0fca6ea1SDimitry Andric // For PIE binaries, the preferred address is zero and we adjust the virtual 761*0fca6ea1SDimitry Andric // address by start of the profiled segment assuming that the offset of the 762*0fca6ea1SDimitry Andric // segment in the binary is zero. For non-PIE binaries the preferred and 763*0fca6ea1SDimitry Andric // profiled segment addresses should be equal and this is a no-op. 764*0fca6ea1SDimitry Andric const uint64_t AdjustedAddress = 765*0fca6ea1SDimitry Andric VirtualAddress + PreferredTextSegmentAddress - ProfiledTextSegmentStart; 766*0fca6ea1SDimitry Andric return object::SectionedAddress{AdjustedAddress}; 767*0fca6ea1SDimitry Andric } 768*0fca6ea1SDimitry Andric // Addresses which do not originate from the profiled text segment in the 769*0fca6ea1SDimitry Andric // binary are not adjusted. These will fail symbolization and be filtered out 770*0fca6ea1SDimitry Andric // during processing. 771*0fca6ea1SDimitry Andric return object::SectionedAddress{VirtualAddress}; 772*0fca6ea1SDimitry Andric } 773*0fca6ea1SDimitry Andric 774*0fca6ea1SDimitry Andric Error RawMemProfReader::readNextRecord( 775*0fca6ea1SDimitry Andric GuidMemProfRecordPair &GuidRecord, 776*0fca6ea1SDimitry Andric std::function<const Frame(const FrameId)> Callback) { 777*0fca6ea1SDimitry Andric // Create a new callback for the RawMemProfRecord iterator so that we can 778*0fca6ea1SDimitry Andric // provide the symbol name if the reader was initialized with KeepSymbolName = 779*0fca6ea1SDimitry Andric // true. This is useful for debugging and testing. 780*0fca6ea1SDimitry Andric auto IdToFrameCallback = [this](const FrameId Id) { 781*0fca6ea1SDimitry Andric Frame F = this->idToFrame(Id); 782*0fca6ea1SDimitry Andric if (!this->KeepSymbolName) 783*0fca6ea1SDimitry Andric return F; 784*0fca6ea1SDimitry Andric auto Iter = this->GuidToSymbolName.find(F.Function); 785*0fca6ea1SDimitry Andric assert(Iter != this->GuidToSymbolName.end()); 786*0fca6ea1SDimitry Andric F.SymbolName = std::make_unique<std::string>(Iter->getSecond()); 787*0fca6ea1SDimitry Andric return F; 788*0fca6ea1SDimitry Andric }; 789*0fca6ea1SDimitry Andric return MemProfReader::readNextRecord(GuidRecord, IdToFrameCallback); 790*0fca6ea1SDimitry Andric } 791*0fca6ea1SDimitry Andric } // namespace memprof 792*0fca6ea1SDimitry Andric } // namespace llvm 793