From 932d7b9ddd23bfbe9ac1e8a2dabab8e457993ed4 Mon Sep 17 00:00:00 2001 From: Snehasish Kumar Date: Fri, 14 Apr 2023 05:12:09 +0000 Subject: [PATCH] [memprof] Print out profile build ids in the error message. When no --profiled-binary flag is provided we can print out the build ids of the modules in the profile. This can help the user fetch the correct binary from e.g. remote object store. Reviewed By: tejohnson Differential Revision: https://reviews.llvm.org/D148301 --- llvm/include/llvm/ProfileData/RawMemProfReader.h | 3 ++ llvm/lib/ProfileData/RawMemProfReader.cpp | 50 ++++++++++++++++++++-- llvm/test/tools/llvm-profdata/memprof-buildid.test | 6 ++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/llvm/include/llvm/ProfileData/RawMemProfReader.h b/llvm/include/llvm/ProfileData/RawMemProfReader.h index 52d5f12..63bf819 100644 --- a/llvm/include/llvm/ProfileData/RawMemProfReader.h +++ b/llvm/include/llvm/ProfileData/RawMemProfReader.h @@ -57,6 +57,9 @@ public: 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 index 1c7323e..7c519ed 100644 --- 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 @@ RawMemProfReader::create(const Twine &Path, const StringRef ProfiledBinary, 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 @@ Error RawMemProfReader::symbolizeAndFilterStackFrames() { 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 index 9b055d3a..a5abe6e 100644 --- a/llvm/test/tools/llvm-profdata/memprof-buildid.test +++ b/llvm/test/tools/llvm-profdata/memprof-buildid.test @@ -5,8 +5,12 @@ RUN: llvm-readelf --notes %p/Inputs/buildid.memprofexe > %t1.txt 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]] -- 2.7.4