Skip to content

Commit 76b4931

Browse files
[memprof] Accept a function name in YAML (#119453)
This patch does two things: - During deserialization, we accept a function name as an alternative to the usual GUID represented as a hexadecimal number. - During serialization, we print a GUID as a 16-digit hexadecimal number prefixed with 0x in the usual way. (Without this patch, we print a decimal number, which is not customary.) In YAML, the MemProf profile is a vector of pairs of GUID and MemProfRecord. This patch accepts a function name for the GUID, but it does not accept a function name for the GUID used in Frames yet. That will be addressed in a subsequent patch.
1 parent 3146d57 commit 76b4931

File tree

3 files changed

+70
-2
lines changed

3 files changed

+70
-2
lines changed

llvm/include/llvm/ProfileData/MemProf.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "llvm/Support/BLAKE3.h"
1212
#include "llvm/Support/Endian.h"
1313
#include "llvm/Support/EndianStream.h"
14+
#include "llvm/Support/Format.h"
1415
#include "llvm/Support/HashBuilder.h"
1516
#include "llvm/Support/YAMLTraits.h"
1617
#include "llvm/Support/raw_ostream.h"
@@ -491,11 +492,15 @@ struct MemProfRecord {
491492
}
492493
};
493494

495+
// A "typedef" for GUID. See ScalarTraits<memprof::GUIDHex64> for how a GUID is
496+
// serialized and deserialized in YAML.
497+
LLVM_YAML_STRONG_TYPEDEF(uint64_t, GUIDHex64)
498+
494499
// Helper struct for AllMemProfData. In YAML, we treat the GUID and the fields
495500
// within MemProfRecord at the same level as if the GUID were part of
496501
// MemProfRecord.
497502
struct GUIDMemProfRecordPair {
498-
GlobalValue::GUID GUID;
503+
GUIDHex64 GUID;
499504
MemProfRecord Record;
500505
};
501506

@@ -1166,6 +1171,31 @@ template <typename FrameIdTy> class CallStackRadixTreeBuilder {
11661171
} // namespace memprof
11671172

11681173
namespace yaml {
1174+
template <> struct ScalarTraits<memprof::GUIDHex64> {
1175+
static void output(const memprof::GUIDHex64 &Val, void *, raw_ostream &Out) {
1176+
// Print GUID as a 16-digit hexadecimal number.
1177+
Out << format("0x%016" PRIx64, (uint64_t)Val);
1178+
}
1179+
static StringRef input(StringRef Scalar, void *, memprof::GUIDHex64 &Val) {
1180+
// Reject decimal GUIDs.
1181+
if (all_of(Scalar, [](char C) { return std::isdigit(C); }))
1182+
return "use a hexadecimal GUID or a function instead";
1183+
1184+
uint64_t Num;
1185+
if (Scalar.starts_with_insensitive("0x")) {
1186+
// Accept hexadecimal numbers starting with 0x or 0X.
1187+
if (Scalar.getAsInteger(0, Num))
1188+
return "invalid hex64 number";
1189+
Val = Num;
1190+
} else {
1191+
// Otherwise, treat the input as a string containing a function name.
1192+
Val = memprof::IndexedMemProfRecord::getGUID(Scalar);
1193+
}
1194+
return StringRef();
1195+
}
1196+
static QuotingType mustQuote(StringRef) { return QuotingType::None; }
1197+
};
1198+
11691199
template <> struct MappingTraits<memprof::Frame> {
11701200
static void mapping(IO &Io, memprof::Frame &F) {
11711201
Io.mapRequired("Function", F.Function);

llvm/test/tools/llvm-profdata/memprof-yaml.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
;--- memprof-in.yaml
99
---
1010
HeapProfileRecords:
11-
- GUID: 16045690981402826360
11+
- GUID: 0xdeadbeef12345678
1212
AllocSites:
1313
- Callstack:
1414
- { Function: 100, LineOffset: 11, Column: 10, IsInlineFrame: true }

llvm/unittests/ProfileData/MemProfTest.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ using ::llvm::StringRef;
3535
using ::llvm::object::SectionedAddress;
3636
using ::llvm::symbolize::SymbolizableModule;
3737
using ::testing::ElementsAre;
38+
using ::testing::IsEmpty;
3839
using ::testing::Pair;
3940
using ::testing::Return;
4041
using ::testing::SizeIs;
@@ -760,6 +761,43 @@ TEST(MemProf, YAMLParser) {
760761
ElementsAre(hashCallStack(CS3), hashCallStack(CS4)));
761762
}
762763

764+
// Verify that the YAML parser accepts a GUID expressed as a function name.
765+
TEST(MemProf, YAMLParserGUID) {
766+
StringRef YAMLData = R"YAML(
767+
---
768+
HeapProfileRecords:
769+
- GUID: _Z3fooi
770+
AllocSites:
771+
- Callstack:
772+
- {Function: 0x100, LineOffset: 11, Column: 10, IsInlineFrame: true}
773+
MemInfoBlock: {}
774+
CallSites: []
775+
)YAML";
776+
777+
YAMLMemProfReader YAMLReader;
778+
YAMLReader.parse(YAMLData);
779+
IndexedMemProfData MemProfData = YAMLReader.takeMemProfData();
780+
781+
Frame F1(0x100, 11, 10, true);
782+
783+
llvm::SmallVector<FrameId> CS1 = {F1.hash()};
784+
785+
// Verify the entire contents of MemProfData.Frames.
786+
EXPECT_THAT(MemProfData.Frames, UnorderedElementsAre(Pair(F1.hash(), F1)));
787+
788+
// Verify the entire contents of MemProfData.Frames.
789+
EXPECT_THAT(MemProfData.CallStacks,
790+
UnorderedElementsAre(Pair(hashCallStack(CS1), CS1)));
791+
792+
// Verify the entire contents of MemProfData.Records.
793+
ASSERT_THAT(MemProfData.Records, SizeIs(1));
794+
const auto &[GUID, Record] = MemProfData.Records.front();
795+
EXPECT_EQ(GUID, IndexedMemProfRecord::getGUID("_Z3fooi"));
796+
ASSERT_THAT(Record.AllocSites, SizeIs(1));
797+
EXPECT_EQ(Record.AllocSites[0].CSId, hashCallStack(CS1));
798+
EXPECT_THAT(Record.CallSiteIds, IsEmpty());
799+
}
800+
763801
template <typename T> std::string serializeInYAML(T &Val) {
764802
std::string Out;
765803
llvm::raw_string_ostream OS(Out);

0 commit comments

Comments
 (0)