diff --git a/llvm/include/llvm/ProfileData/RawMemProfReader.h b/llvm/include/llvm/ProfileData/RawMemProfReader.h --- a/llvm/include/llvm/ProfileData/RawMemProfReader.h +++ b/llvm/include/llvm/ProfileData/RawMemProfReader.h @@ -57,6 +57,9 @@ create(const Twine &Path, const StringRef ProfiledBinary, bool KeepName = false); + // Returns a list of build ids recorded in the segment information. + static std::vector peekBuildIds(MemoryBuffer *DataBuffer); + using GuidMemProfRecordPair = std::pair; using Iterator = InstrProfIterator; Iterator end() { return Iterator(); } diff --git a/llvm/lib/ProfileData/RawMemProfReader.cpp b/llvm/lib/ProfileData/RawMemProfReader.cpp --- a/llvm/lib/ProfileData/RawMemProfReader.cpp +++ b/llvm/lib/ProfileData/RawMemProfReader.cpp @@ -17,9 +17,12 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Twine.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" #include "llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h" @@ -34,6 +37,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #define DEBUG_TYPE "memprof" @@ -185,10 +189,20 @@ if (Error E = checkBuffer(*Buffer)) return report(std::move(E), Path.getSingleStringRef()); - if (ProfiledBinary.empty()) + if (ProfiledBinary.empty()) { + // Peek the build ids to print a helpful error message. + const std::vector BuildIds = peekBuildIds(Buffer.get()); + std::string ErrorMessage( + R"(Path to profiled binary is empty, expected binary with one of the following build ids: +)"); + for (const auto &Id : BuildIds) { + ErrorMessage += "\n BuildId: "; + ErrorMessage += Id; + } return report( - errorCodeToError(make_error_code(std::errc::invalid_argument)), - "Path to profiled binary is empty!"); + make_error(ErrorMessage, inconvertibleErrorCode()), + /*Context=*/""); + } auto BinaryOr = llvm::object::createBinary(ProfiledBinary); if (!BinaryOr) { @@ -522,6 +536,36 @@ return Error::success(); } +std::vector +RawMemProfReader::peekBuildIds(MemoryBuffer *DataBuffer) { + const char *Next = DataBuffer->getBufferStart(); + // Use a set + vector since a profile file may contain multiple raw profile + // dumps, each with segment information. We want them unique and in order they + // were stored in the profile; the profiled binary should be the first entry. + // The runtime uses dl_iterate_phdr and the "... first object visited by + // callback is the main program." + // https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html + std::vector BuildIds; + llvm::SmallSet BuildIdsSet; + while (Next < DataBuffer->getBufferEnd()) { + auto *Header = reinterpret_cast(Next); + + const llvm::SmallVector Entries = + readSegmentEntries(Next + Header->SegmentOffset); + + for (const auto &Entry : Entries) { + const std::string Id = getBuildIdString(Entry); + if (BuildIdsSet.contains(Id)) + continue; + BuildIds.push_back(Id); + BuildIdsSet.insert(BuildIds.back()); + } + + Next += Header->TotalSize; + } + return BuildIds; +} + Error RawMemProfReader::readRawProfile( std::unique_ptr DataBuffer) { const char *Next = DataBuffer->getBufferStart(); diff --git a/llvm/test/tools/llvm-profdata/memprof-buildid.test b/llvm/test/tools/llvm-profdata/memprof-buildid.test --- a/llvm/test/tools/llvm-profdata/memprof-buildid.test +++ b/llvm/test/tools/llvm-profdata/memprof-buildid.test @@ -5,8 +5,12 @@ RUN: llvm-profdata show --memory %p/Inputs/buildid.memprofraw --profiled-binary %p/Inputs/buildid.memprofexe -o - > %t2.txt RUN: cat %t1.txt %t2.txt | FileCheck %s +Test that we print out the profile build ids when --profiled-binary is empty. +RUN: not llvm-profdata show --memory %p/Inputs/buildid.memprofraw -o - 2> %t3.txt +RUN: cat %t1.txt %t3.txt | FileCheck %s + COM: First extract the id from the llvm-readelf output. CHECK: Build ID: [[ID:[[:xdigit:]]+]] COM: Then match it with the profdata output. -CHECK: BuildId: {{.*}}[[ID]] +CHECK-COUNT-1: BuildId: {{.*}}[[ID]]