Skip to content

Commit 684e79f

Browse files
[memprof] Add YAML read/write support to llvm-profdata (llvm#118915)
This patch adds YAML read/write support to llvm-profdata. The primary intent is to accommodate MemProf profiles in test cases, thereby avoiding the binary format. The read support is via llvm-profdata merge. This is useful when we want to verify that the compiler does the right thing on a given .ll file and a MemProf profile in a test case. In the test case, we would convert the MemProf profile in YAML to an indexed profile and invoke the compiler on the .ll file along with the indexed profile. The write support is via llvm-profdata show --memory. This is useful when we wish to convert an indexed MemProf profile to YAML while writing tests. We would compile a test case in C++, run it for an indexed MemProf profile, and then convert it to the text format.
1 parent 9a156f6 commit 684e79f

File tree

7 files changed

+176
-12
lines changed

7 files changed

+176
-12
lines changed

llvm/include/llvm/ProfileData/InstrProfReader.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,9 @@ class IndexedMemProfReader {
716716

717717
DenseMap<uint64_t, SmallVector<memprof::CallEdgeTy, 0>>
718718
getMemProfCallerCalleePairs() const;
719+
720+
// Return the entire MemProf profile.
721+
memprof::AllMemProfData getAllMemProfData() const;
719722
};
720723

721724
/// Reader for the indexed binary instrprof format.
@@ -823,6 +826,10 @@ class IndexedInstrProfReader : public InstrProfReader {
823826
return MemProfReader.getMemProfCallerCalleePairs();
824827
}
825828

829+
memprof::AllMemProfData getAllMemProfData() const {
830+
return MemProfReader.getAllMemProfData();
831+
}
832+
826833
/// Fill Counts with the profile data for the given function name.
827834
Error getFunctionCounts(StringRef FuncName, uint64_t FuncHash,
828835
std::vector<uint64_t> &Counts);

llvm/include/llvm/ProfileData/MemProfReader.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,20 @@ class RawMemProfReader final : public MemProfReader {
213213
class YAMLMemProfReader final : public MemProfReader {
214214
public:
215215
YAMLMemProfReader() = default;
216+
217+
// Return true if the \p DataBuffer starts with "---" indicating it is a YAML
218+
// file.
219+
static bool hasFormat(const MemoryBuffer &DataBuffer);
220+
// Wrapper around hasFormat above, reading the file instead of the memory
221+
// buffer.
222+
static bool hasFormat(const StringRef Path);
223+
224+
// Create a YAMLMemProfReader after sanity checking the contents of the file
225+
// at \p Path or the \p Buffer.
226+
static Expected<std::unique_ptr<YAMLMemProfReader>> create(const Twine &Path);
227+
static Expected<std::unique_ptr<YAMLMemProfReader>>
228+
create(std::unique_ptr<MemoryBuffer> Buffer);
229+
216230
void parse(StringRef YAMLData);
217231
};
218232
} // namespace memprof

llvm/lib/ProfileData/InstrProfReader.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,22 @@ IndexedMemProfReader::getMemProfCallerCalleePairs() const {
16641664
return Pairs;
16651665
}
16661666

1667+
memprof::AllMemProfData IndexedMemProfReader::getAllMemProfData() const {
1668+
memprof::AllMemProfData AllMemProfData;
1669+
AllMemProfData.HeapProfileRecords.reserve(
1670+
MemProfRecordTable->getNumEntries());
1671+
for (uint64_t Key : MemProfRecordTable->keys()) {
1672+
auto Record = getMemProfRecord(Key);
1673+
if (Record.takeError())
1674+
continue;
1675+
memprof::GUIDMemProfRecordPair Pair;
1676+
Pair.GUID = Key;
1677+
Pair.Record = std::move(*Record);
1678+
AllMemProfData.HeapProfileRecords.push_back(std::move(Pair));
1679+
}
1680+
return AllMemProfData;
1681+
}
1682+
16671683
Error IndexedInstrProfReader::getFunctionCounts(StringRef FuncName,
16681684
uint64_t FuncHash,
16691685
std::vector<uint64_t> &Counts) {

llvm/lib/ProfileData/MemProfReader.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,36 @@ Error RawMemProfReader::readNextRecord(
751751
return MemProfReader::readNextRecord(GuidRecord, IdToFrameCallback);
752752
}
753753

754+
Expected<std::unique_ptr<YAMLMemProfReader>>
755+
YAMLMemProfReader::create(const Twine &Path) {
756+
auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path);
757+
if (std::error_code EC = BufferOr.getError())
758+
return report(errorCodeToError(EC), Path.getSingleStringRef());
759+
760+
std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
761+
return create(std::move(Buffer));
762+
}
763+
764+
Expected<std::unique_ptr<YAMLMemProfReader>>
765+
YAMLMemProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) {
766+
auto Reader = std::make_unique<YAMLMemProfReader>();
767+
Reader->parse(Buffer->getBuffer());
768+
return std::move(Reader);
769+
}
770+
771+
bool YAMLMemProfReader::hasFormat(const StringRef Path) {
772+
auto BufferOr = MemoryBuffer::getFileOrSTDIN(Path);
773+
if (!BufferOr)
774+
return false;
775+
776+
std::unique_ptr<MemoryBuffer> Buffer(BufferOr.get().release());
777+
return hasFormat(*Buffer);
778+
}
779+
780+
bool YAMLMemProfReader::hasFormat(const MemoryBuffer &Buffer) {
781+
return Buffer.getBuffer().starts_with("---");
782+
}
783+
754784
void YAMLMemProfReader::parse(StringRef YAMLData) {
755785
memprof::AllMemProfData Doc;
756786
yaml::Input Yin(YAMLData);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
; REQUIRES: x86_64-linux
2+
; RUN: split-file %s %t
3+
; RUN: not llvm-profdata merge %t/memprof-invalid.yaml -o %t/memprof-invalid.indexed
4+
5+
; Verify that the invalid YAML input results in an error.
6+
;--- memprof-invalid.yaml
7+
---
8+
...
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
; REQUIRES: x86_64-linux
2+
; RUN: split-file %s %t
3+
; RUN: llvm-profdata merge %t/memprof-in.yaml -o %t/memprof-out.indexed
4+
; RUN: llvm-profdata show --memory %t/memprof-out.indexed > %t/memprof-out.yaml
5+
; RUN: cmp %t/memprof-in.yaml %t/memprof-out.yaml
6+
7+
; Verify that the YAML output is identical to the YAML input.
8+
;--- memprof-in.yaml
9+
---
10+
HeapProfileRecords:
11+
- GUID: 16045690981402826360
12+
AllocSites:
13+
- Callstack:
14+
- { Function: 100, LineOffset: 11, Column: 10, IsInlineFrame: true }
15+
- { Function: 200, LineOffset: 22, Column: 20, IsInlineFrame: false }
16+
MemInfoBlock:
17+
AllocCount: 111
18+
TotalSize: 222
19+
TotalLifetime: 333
20+
TotalLifetimeAccessDensity: 444
21+
- Callstack:
22+
- { Function: 300, LineOffset: 33, Column: 30, IsInlineFrame: false }
23+
- { Function: 400, LineOffset: 44, Column: 40, IsInlineFrame: true }
24+
MemInfoBlock:
25+
AllocCount: 555
26+
TotalSize: 666
27+
TotalLifetime: 777
28+
TotalLifetimeAccessDensity: 888
29+
CallSites:
30+
- - { Function: 500, LineOffset: 55, Column: 50, IsInlineFrame: true }
31+
- { Function: 600, LineOffset: 66, Column: 60, IsInlineFrame: false }
32+
- - { Function: 700, LineOffset: 77, Column: 70, IsInlineFrame: true }
33+
- { Function: 800, LineOffset: 88, Column: 80, IsInlineFrame: false }
34+
...

llvm/tools/llvm-profdata/llvm-profdata.cpp

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,43 @@ loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
723723
return;
724724
}
725725

726+
using ::llvm::memprof::YAMLMemProfReader;
727+
if (YAMLMemProfReader::hasFormat(Input.Filename)) {
728+
auto ReaderOrErr = YAMLMemProfReader::create(Input.Filename);
729+
if (!ReaderOrErr)
730+
exitWithError(ReaderOrErr.takeError(), Input.Filename);
731+
std::unique_ptr<YAMLMemProfReader> Reader = std::move(ReaderOrErr.get());
732+
// Check if the profile types can be merged, e.g. clang frontend profiles
733+
// should not be merged with memprof profiles.
734+
if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
735+
consumeError(std::move(E));
736+
WC->Errors.emplace_back(
737+
make_error<StringError>(
738+
"Cannot merge MemProf profile with incompatible profile.",
739+
std::error_code()),
740+
Filename);
741+
return;
742+
}
743+
744+
auto MemProfError = [&](Error E) {
745+
auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
746+
WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),
747+
Filename);
748+
};
749+
750+
auto MemProfData = Reader->takeMemProfData();
751+
752+
// Check for the empty input in case the YAML file is invalid.
753+
if (MemProfData.Records.empty()) {
754+
WC->Errors.emplace_back(
755+
make_error<StringError>("The profile is empty.", std::error_code()),
756+
Filename);
757+
}
758+
759+
WC->Writer.addMemProfData(std::move(MemProfData), MemProfError);
760+
return;
761+
}
762+
726763
auto FS = vfs::getRealFileSystem();
727764
// TODO: This only saves the first non-fatal error from InstrProfReader, and
728765
// then added to WriterContext::Errors. However, this is not extensible, if
@@ -3242,18 +3279,36 @@ static int showSampleProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
32423279
static int showMemProfProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
32433280
if (SFormat == ShowFormat::Json)
32443281
exitWithError("JSON output is not supported for MemProf");
3245-
auto ReaderOr = llvm::memprof::RawMemProfReader::create(
3246-
Filename, ProfiledBinary, /*KeepNames=*/true);
3247-
if (Error E = ReaderOr.takeError())
3248-
// Since the error can be related to the profile or the binary we do not
3249-
// pass whence. Instead additional context is provided where necessary in
3250-
// the error message.
3251-
exitWithError(std::move(E), /*Whence*/ "");
3252-
3253-
std::unique_ptr<llvm::memprof::RawMemProfReader> Reader(
3254-
ReaderOr.get().release());
3255-
3256-
Reader->printYAML(OS);
3282+
3283+
// Show the raw profile in YAML.
3284+
if (memprof::RawMemProfReader::hasFormat(Filename)) {
3285+
auto ReaderOr = llvm::memprof::RawMemProfReader::create(
3286+
Filename, ProfiledBinary, /*KeepNames=*/true);
3287+
if (Error E = ReaderOr.takeError()) {
3288+
// Since the error can be related to the profile or the binary we do not
3289+
// pass whence. Instead additional context is provided where necessary in
3290+
// the error message.
3291+
exitWithError(std::move(E), /*Whence*/ "");
3292+
}
3293+
3294+
std::unique_ptr<llvm::memprof::RawMemProfReader> Reader(
3295+
ReaderOr.get().release());
3296+
3297+
Reader->printYAML(OS);
3298+
return 0;
3299+
}
3300+
3301+
// Show the indexed MemProf profile in YAML.
3302+
auto FS = vfs::getRealFileSystem();
3303+
auto ReaderOrErr = IndexedInstrProfReader::create(Filename, *FS);
3304+
if (Error E = ReaderOrErr.takeError())
3305+
exitWithError(std::move(E), Filename);
3306+
3307+
auto Reader = std::move(ReaderOrErr.get());
3308+
memprof::AllMemProfData Data = Reader->getAllMemProfData();
3309+
yaml::Output Yout(OS);
3310+
Yout << Data;
3311+
32573312
return 0;
32583313
}
32593314

0 commit comments

Comments
 (0)