diff --git a/llvm/include/llvm/ProfileData/InstrProfReader.h b/llvm/include/llvm/ProfileData/InstrProfReader.h index 330cf540c099b..25e13f575296d 100644 --- a/llvm/include/llvm/ProfileData/InstrProfReader.h +++ b/llvm/include/llvm/ProfileData/InstrProfReader.h @@ -716,6 +716,9 @@ class IndexedMemProfReader { DenseMap> getMemProfCallerCalleePairs() const; + + // Return the entire MemProf profile. + memprof::AllMemProfData getAllMemProfData() const; }; /// Reader for the indexed binary instrprof format. @@ -823,6 +826,10 @@ class IndexedInstrProfReader : public InstrProfReader { return MemProfReader.getMemProfCallerCalleePairs(); } + memprof::AllMemProfData getAllMemProfData() const { + return MemProfReader.getAllMemProfData(); + } + /// Fill Counts with the profile data for the given function name. Error getFunctionCounts(StringRef FuncName, uint64_t FuncHash, std::vector &Counts); diff --git a/llvm/include/llvm/ProfileData/MemProfReader.h b/llvm/include/llvm/ProfileData/MemProfReader.h index 0529f79460646..29d9e57cae3e3 100644 --- a/llvm/include/llvm/ProfileData/MemProfReader.h +++ b/llvm/include/llvm/ProfileData/MemProfReader.h @@ -213,6 +213,20 @@ class RawMemProfReader final : public MemProfReader { class YAMLMemProfReader final : public MemProfReader { public: YAMLMemProfReader() = default; + + // Return true if the \p DataBuffer starts with "---" indicating it is a YAML + // file. + static bool hasFormat(const MemoryBuffer &DataBuffer); + // Wrapper around hasFormat above, reading the file instead of the memory + // buffer. + static bool hasFormat(const StringRef Path); + + // Create a YAMLMemProfReader after sanity checking the contents of the file + // at \p Path or the \p Buffer. + static Expected> create(const Twine &Path); + static Expected> + create(std::unique_ptr Buffer); + void parse(StringRef YAMLData); }; } // namespace memprof diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp index dad79b2c1761e..cac1760d3ef80 100644 --- a/llvm/lib/ProfileData/InstrProfReader.cpp +++ b/llvm/lib/ProfileData/InstrProfReader.cpp @@ -1664,6 +1664,22 @@ IndexedMemProfReader::getMemProfCallerCalleePairs() const { return Pairs; } +memprof::AllMemProfData IndexedMemProfReader::getAllMemProfData() const { + memprof::AllMemProfData AllMemProfData; + AllMemProfData.HeapProfileRecords.reserve( + MemProfRecordTable->getNumEntries()); + for (uint64_t Key : MemProfRecordTable->keys()) { + auto Record = getMemProfRecord(Key); + if (Record.takeError()) + continue; + memprof::GUIDMemProfRecordPair Pair; + Pair.GUID = Key; + Pair.Record = std::move(*Record); + AllMemProfData.HeapProfileRecords.push_back(std::move(Pair)); + } + return AllMemProfData; +} + Error IndexedInstrProfReader::getFunctionCounts(StringRef FuncName, uint64_t FuncHash, std::vector &Counts) { diff --git a/llvm/lib/ProfileData/MemProfReader.cpp b/llvm/lib/ProfileData/MemProfReader.cpp index 9dacf29898593..e4082fff41686 100644 --- a/llvm/lib/ProfileData/MemProfReader.cpp +++ b/llvm/lib/ProfileData/MemProfReader.cpp @@ -755,6 +755,36 @@ Error RawMemProfReader::readNextRecord( return MemProfReader::readNextRecord(GuidRecord, IdToFrameCallback); } +Expected> +YAMLMemProfReader::create(const Twine &Path) { + auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path); + if (std::error_code EC = BufferOr.getError()) + return report(errorCodeToError(EC), Path.getSingleStringRef()); + + std::unique_ptr Buffer(BufferOr.get().release()); + return create(std::move(Buffer)); +} + +Expected> +YAMLMemProfReader::create(std::unique_ptr Buffer) { + auto Reader = std::make_unique(); + Reader->parse(Buffer->getBuffer()); + return std::move(Reader); +} + +bool YAMLMemProfReader::hasFormat(const StringRef Path) { + auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path); + if (!BufferOr) + return false; + + std::unique_ptr Buffer(BufferOr.get().release()); + return hasFormat(*Buffer); +} + +bool YAMLMemProfReader::hasFormat(const MemoryBuffer &Buffer) { + return Buffer.getBuffer().starts_with("---"); +} + void YAMLMemProfReader::parse(StringRef YAMLData) { memprof::AllMemProfData Doc; yaml::Input Yin(YAMLData); diff --git a/llvm/test/tools/llvm-profdata/memprof-yaml-invalid.test b/llvm/test/tools/llvm-profdata/memprof-yaml-invalid.test new file mode 100644 index 0000000000000..a13451cee5116 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/memprof-yaml-invalid.test @@ -0,0 +1,8 @@ +; REQUIRES: x86_64-linux +; RUN: split-file %s %t +; RUN: not llvm-profdata merge %t/memprof-invalid.yaml -o %t/memprof-invalid.indexed + +; Verify that the invalid YAML input results in an error. +;--- memprof-invalid.yaml +--- +... diff --git a/llvm/test/tools/llvm-profdata/memprof-yaml.test b/llvm/test/tools/llvm-profdata/memprof-yaml.test new file mode 100644 index 0000000000000..fbbf8a6e7e5b5 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/memprof-yaml.test @@ -0,0 +1,34 @@ +; REQUIRES: x86_64-linux +; RUN: split-file %s %t +; RUN: llvm-profdata merge %t/memprof-in.yaml -o %t/memprof-out.indexed +; RUN: llvm-profdata show --memory %t/memprof-out.indexed > %t/memprof-out.yaml +; RUN: cmp %t/memprof-in.yaml %t/memprof-out.yaml + +; Verify that the YAML output is identical to the YAML input. +;--- memprof-in.yaml +--- +HeapProfileRecords: + - GUID: 16045690981402826360 + AllocSites: + - Callstack: + - { Function: 100, LineOffset: 11, Column: 10, IsInlineFrame: true } + - { Function: 200, LineOffset: 22, Column: 20, IsInlineFrame: false } + MemInfoBlock: + AllocCount: 111 + TotalSize: 222 + TotalLifetime: 333 + TotalLifetimeAccessDensity: 444 + - Callstack: + - { Function: 300, LineOffset: 33, Column: 30, IsInlineFrame: false } + - { Function: 400, LineOffset: 44, Column: 40, IsInlineFrame: true } + MemInfoBlock: + AllocCount: 555 + TotalSize: 666 + TotalLifetime: 777 + TotalLifetimeAccessDensity: 888 + CallSites: + - - { Function: 500, LineOffset: 55, Column: 50, IsInlineFrame: true } + - { Function: 600, LineOffset: 66, Column: 60, IsInlineFrame: false } + - - { Function: 700, LineOffset: 77, Column: 70, IsInlineFrame: true } + - { Function: 800, LineOffset: 88, Column: 80, IsInlineFrame: false } +... diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index 1d9d7bcf76549..f34d99e10f316 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -723,6 +723,43 @@ loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, return; } + using ::llvm::memprof::YAMLMemProfReader; + if (YAMLMemProfReader::hasFormat(Input.Filename)) { + auto ReaderOrErr = YAMLMemProfReader::create(Input.Filename); + if (!ReaderOrErr) + exitWithError(ReaderOrErr.takeError(), Input.Filename); + std::unique_ptr Reader = std::move(ReaderOrErr.get()); + // Check if the profile types can be merged, e.g. clang frontend profiles + // should not be merged with memprof profiles. + if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) { + consumeError(std::move(E)); + WC->Errors.emplace_back( + make_error( + "Cannot merge MemProf profile with incompatible profile.", + std::error_code()), + Filename); + return; + } + + auto MemProfError = [&](Error E) { + auto [ErrorCode, Msg] = InstrProfError::take(std::move(E)); + WC->Errors.emplace_back(make_error(ErrorCode, Msg), + Filename); + }; + + auto MemProfData = Reader->takeMemProfData(); + + // Check for the empty input in case the YAML file is invalid. + if (MemProfData.Records.empty()) { + WC->Errors.emplace_back( + make_error("The profile is empty.", std::error_code()), + Filename); + } + + WC->Writer.addMemProfData(std::move(MemProfData), MemProfError); + return; + } + auto FS = vfs::getRealFileSystem(); // TODO: This only saves the first non-fatal error from InstrProfReader, and // then added to WriterContext::Errors. However, this is not extensible, if @@ -3242,18 +3279,36 @@ static int showSampleProfile(ShowFormat SFormat, raw_fd_ostream &OS) { static int showMemProfProfile(ShowFormat SFormat, raw_fd_ostream &OS) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for MemProf"); - auto ReaderOr = llvm::memprof::RawMemProfReader::create( - Filename, ProfiledBinary, /*KeepNames=*/true); - if (Error E = ReaderOr.takeError()) - // Since the error can be related to the profile or the binary we do not - // pass whence. Instead additional context is provided where necessary in - // the error message. - exitWithError(std::move(E), /*Whence*/ ""); - - std::unique_ptr Reader( - ReaderOr.get().release()); - - Reader->printYAML(OS); + + // Show the raw profile in YAML. + if (memprof::RawMemProfReader::hasFormat(Filename)) { + auto ReaderOr = llvm::memprof::RawMemProfReader::create( + Filename, ProfiledBinary, /*KeepNames=*/true); + if (Error E = ReaderOr.takeError()) { + // Since the error can be related to the profile or the binary we do not + // pass whence. Instead additional context is provided where necessary in + // the error message. + exitWithError(std::move(E), /*Whence*/ ""); + } + + std::unique_ptr Reader( + ReaderOr.get().release()); + + Reader->printYAML(OS); + return 0; + } + + // Show the indexed MemProf profile in YAML. + auto FS = vfs::getRealFileSystem(); + auto ReaderOrErr = IndexedInstrProfReader::create(Filename, *FS); + if (Error E = ReaderOrErr.takeError()) + exitWithError(std::move(E), Filename); + + auto Reader = std::move(ReaderOrErr.get()); + memprof::AllMemProfData Data = Reader->getAllMemProfData(); + yaml::Output Yout(OS); + Yout << Data; + return 0; }