Skip to content

Commit 26fabdd

Browse files
[memprof] Pass FrameIdConverter and CallStackIdConverter by reference (#92327)
CallStackIdConverter sets LastUnmappedId when a mapping failure occurs. Now, since toMemProfRecord takes an instance of CallStackIdConverter by value, namely std::function, the caller of toMemProfRecord never receives the mapping failure that occurs inside toMemProfRecord. The same problem applies to FrameIdConverter. The patch fixes the problem by passing FrameIdConverter and CallStackIdConverter by reference, namely llvm::function_ref. While I am it, this patch deletes the copy constructor and copy assignment operator to avoid accidental copies.
1 parent c6e787f commit 26fabdd

File tree

3 files changed

+85
-6
lines changed

3 files changed

+85
-6
lines changed

llvm/include/llvm/ProfileData/MemProf.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,8 @@ struct IndexedMemProfRecord {
426426
// Convert IndexedMemProfRecord to MemProfRecord. Callback is used to
427427
// translate CallStackId to call stacks with frames inline.
428428
MemProfRecord toMemProfRecord(
429-
std::function<const llvm::SmallVector<Frame>(const CallStackId)> Callback)
430-
const;
429+
llvm::function_ref<const llvm::SmallVector<Frame>(const CallStackId)>
430+
Callback) const;
431431

432432
// Returns the GUID for the function name after canonicalization. For
433433
// memprof, we remove any .llvm suffix added by LTO. MemProfRecords are
@@ -784,6 +784,12 @@ template <typename MapTy> struct FrameIdConverter {
784784
FrameIdConverter() = delete;
785785
FrameIdConverter(MapTy &Map) : Map(Map) {}
786786

787+
// Delete the copy constructor and copy assignment operator to avoid a
788+
// situation where a copy of FrameIdConverter gets an error in LastUnmappedId
789+
// while the original instance doesn't.
790+
FrameIdConverter(const FrameIdConverter &) = delete;
791+
FrameIdConverter &operator=(const FrameIdConverter &) = delete;
792+
787793
Frame operator()(FrameId Id) {
788794
auto Iter = Map.find(Id);
789795
if (Iter == Map.end()) {
@@ -798,12 +804,19 @@ template <typename MapTy> struct FrameIdConverter {
798804
template <typename MapTy> struct CallStackIdConverter {
799805
std::optional<CallStackId> LastUnmappedId;
800806
MapTy &Map;
801-
std::function<Frame(FrameId)> FrameIdToFrame;
807+
llvm::function_ref<Frame(FrameId)> FrameIdToFrame;
802808

803809
CallStackIdConverter() = delete;
804-
CallStackIdConverter(MapTy &Map, std::function<Frame(FrameId)> FrameIdToFrame)
810+
CallStackIdConverter(MapTy &Map,
811+
llvm::function_ref<Frame(FrameId)> FrameIdToFrame)
805812
: Map(Map), FrameIdToFrame(FrameIdToFrame) {}
806813

814+
// Delete the copy constructor and copy assignment operator to avoid a
815+
// situation where a copy of CallStackIdConverter gets an error in
816+
// LastUnmappedId while the original instance doesn't.
817+
CallStackIdConverter(const CallStackIdConverter &) = delete;
818+
CallStackIdConverter &operator=(const CallStackIdConverter &) = delete;
819+
807820
llvm::SmallVector<Frame> operator()(CallStackId CSId) {
808821
llvm::SmallVector<Frame> Frames;
809822
auto CSIter = Map.find(CSId);

llvm/lib/ProfileData/MemProf.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,8 @@ IndexedMemProfRecord::deserialize(const MemProfSchema &Schema,
243243
}
244244

245245
MemProfRecord IndexedMemProfRecord::toMemProfRecord(
246-
std::function<const llvm::SmallVector<Frame>(const CallStackId)> Callback)
247-
const {
246+
llvm::function_ref<const llvm::SmallVector<Frame>(const CallStackId)>
247+
Callback) const {
248248
MemProfRecord Record;
249249

250250
for (const memprof::IndexedAllocationInfo &IndexedAI : AllocSites) {

llvm/unittests/ProfileData/MemProfTest.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,4 +596,70 @@ TEST(MemProf, IndexedMemProfRecordToMemProfRecord) {
596596
EXPECT_EQ(Record.CallSites[1][0].hash(), F2.hash());
597597
EXPECT_EQ(Record.CallSites[1][1].hash(), F4.hash());
598598
}
599+
600+
using FrameIdMapTy =
601+
llvm::DenseMap<::llvm::memprof::FrameId, ::llvm::memprof::Frame>;
602+
using CallStackIdMapTy =
603+
llvm::DenseMap<::llvm::memprof::CallStackId,
604+
::llvm::SmallVector<::llvm::memprof::FrameId>>;
605+
606+
// Populate those fields returned by getHotColdSchema.
607+
MemInfoBlock makePartialMIB() {
608+
MemInfoBlock MIB;
609+
MIB.AllocCount = 1;
610+
MIB.TotalSize = 5;
611+
MIB.TotalLifetime = 10;
612+
MIB.TotalLifetimeAccessDensity = 23;
613+
return MIB;
614+
}
615+
616+
TEST(MemProf, MissingCallStackId) {
617+
// Use a non-existent CallStackId to trigger a mapping error in
618+
// toMemProfRecord.
619+
llvm::memprof::IndexedAllocationInfo AI({}, 0xdeadbeefU, makePartialMIB(),
620+
llvm::memprof::getHotColdSchema());
621+
622+
IndexedMemProfRecord IndexedMR;
623+
IndexedMR.AllocSites.push_back(AI);
624+
625+
// Create empty maps.
626+
const FrameIdMapTy IdToFrameMap;
627+
const CallStackIdMapTy CSIdToCallStackMap;
628+
llvm::memprof::FrameIdConverter<decltype(IdToFrameMap)> FrameIdConv(
629+
IdToFrameMap);
630+
llvm::memprof::CallStackIdConverter<decltype(CSIdToCallStackMap)> CSIdConv(
631+
CSIdToCallStackMap, FrameIdConv);
632+
633+
// We are only interested in errors, not the return value.
634+
(void)IndexedMR.toMemProfRecord(CSIdConv);
635+
636+
ASSERT_TRUE(CSIdConv.LastUnmappedId.has_value());
637+
EXPECT_EQ(*CSIdConv.LastUnmappedId, 0xdeadbeefU);
638+
EXPECT_EQ(FrameIdConv.LastUnmappedId, std::nullopt);
639+
}
640+
641+
TEST(MemProf, MissingFrameId) {
642+
llvm::memprof::IndexedAllocationInfo AI({}, 0x222, makePartialMIB(),
643+
llvm::memprof::getHotColdSchema());
644+
645+
IndexedMemProfRecord IndexedMR;
646+
IndexedMR.AllocSites.push_back(AI);
647+
648+
// An empty map to trigger a mapping error.
649+
const FrameIdMapTy IdToFrameMap;
650+
CallStackIdMapTy CSIdToCallStackMap;
651+
CSIdToCallStackMap.insert({0x222, {2, 3}});
652+
653+
llvm::memprof::FrameIdConverter<decltype(IdToFrameMap)> FrameIdConv(
654+
IdToFrameMap);
655+
llvm::memprof::CallStackIdConverter<decltype(CSIdToCallStackMap)> CSIdConv(
656+
CSIdToCallStackMap, FrameIdConv);
657+
658+
// We are only interested in errors, not the return value.
659+
(void)IndexedMR.toMemProfRecord(CSIdConv);
660+
661+
EXPECT_EQ(CSIdConv.LastUnmappedId, std::nullopt);
662+
ASSERT_TRUE(FrameIdConv.LastUnmappedId.has_value());
663+
EXPECT_EQ(*FrameIdConv.LastUnmappedId, 3U);
664+
}
599665
} // namespace

0 commit comments

Comments
 (0)