Skip to content

Commit e04df69

Browse files
[memprof] Introduce FrameIdConverter and CallStackIdConverter (#90307)
Currently, we convert FrameId to Frame and CallStackId to a call stack at several places. This patch unifies those into function objects -- FrameIdConverter and CallStackIdConverter. The existing implementation of CallStackIdConverter, being removed in this patch, handles both FrameId and CallStackId conversions. This patch splits it into two phases for flexibility (but make them composable) because some places only require the FrameId conversion.
1 parent d6bf04f commit e04df69

File tree

5 files changed

+97
-117
lines changed

5 files changed

+97
-117
lines changed

llvm/include/llvm/ProfileData/MemProf.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,64 @@ class CallStackLookupTrait {
737737
// Compute a CallStackId for a given call stack.
738738
CallStackId hashCallStack(ArrayRef<FrameId> CS);
739739

740+
namespace detail {
741+
// "Dereference" the iterator from DenseMap or OnDiskChainedHashTable. We have
742+
// to do so in one of two different ways depending on the type of the hash
743+
// table.
744+
template <typename value_type, typename IterTy>
745+
value_type DerefIterator(IterTy Iter) {
746+
using deref_type = llvm::remove_cvref_t<decltype(*Iter)>;
747+
if constexpr (std::is_same_v<deref_type, value_type>)
748+
return *Iter;
749+
else
750+
return Iter->second;
751+
}
752+
} // namespace detail
753+
754+
// A function object that returns a frame for a given FrameId.
755+
template <typename MapTy> struct FrameIdConverter {
756+
std::optional<FrameId> LastUnmappedId;
757+
MapTy &Map;
758+
759+
FrameIdConverter() = delete;
760+
FrameIdConverter(MapTy &Map) : Map(Map) {}
761+
762+
Frame operator()(FrameId Id) {
763+
auto Iter = Map.find(Id);
764+
if (Iter == Map.end()) {
765+
LastUnmappedId = Id;
766+
return Frame(0, 0, 0, false);
767+
}
768+
return detail::DerefIterator<Frame>(Iter);
769+
}
770+
};
771+
772+
// A function object that returns a call stack for a given CallStackId.
773+
template <typename MapTy> struct CallStackIdConverter {
774+
std::optional<CallStackId> LastUnmappedId;
775+
MapTy &Map;
776+
std::function<Frame(FrameId)> FrameIdToFrame;
777+
778+
CallStackIdConverter() = delete;
779+
CallStackIdConverter(MapTy &Map, std::function<Frame(FrameId)> FrameIdToFrame)
780+
: Map(Map), FrameIdToFrame(FrameIdToFrame) {}
781+
782+
llvm::SmallVector<Frame> operator()(CallStackId CSId) {
783+
llvm::SmallVector<Frame> Frames;
784+
auto CSIter = Map.find(CSId);
785+
if (CSIter == Map.end()) {
786+
LastUnmappedId = CSId;
787+
} else {
788+
llvm::SmallVector<FrameId> CS =
789+
detail::DerefIterator<llvm::SmallVector<FrameId>>(CSIter);
790+
Frames.reserve(CS.size());
791+
for (FrameId Id : CS)
792+
Frames.push_back(FrameIdToFrame(Id));
793+
}
794+
return Frames;
795+
}
796+
};
797+
740798
// Verify that each CallStackId is computed with hashCallStack. This function
741799
// is intended to help transition from CallStack to CSId in
742800
// IndexedAllocationInfo.

llvm/include/llvm/ProfileData/MemProfReader.h

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,16 @@ class MemProfReader {
7676
Callback =
7777
std::bind(&MemProfReader::idToFrame, this, std::placeholders::_1);
7878

79-
auto CallStackCallback = [&](CallStackId CSId) {
80-
llvm::SmallVector<Frame> CallStack;
81-
auto Iter = CSIdToCallStack.find(CSId);
82-
assert(Iter != CSIdToCallStack.end());
83-
for (FrameId Id : Iter->second)
84-
CallStack.push_back(Callback(Id));
85-
return CallStack;
86-
};
79+
memprof::CallStackIdConverter<decltype(CSIdToCallStack)> CSIdConv(
80+
CSIdToCallStack, Callback);
8781

8882
const IndexedMemProfRecord &IndexedRecord = Iter->second;
8983
GuidRecord = {
9084
Iter->first,
91-
IndexedRecord.toMemProfRecord(CallStackCallback),
85+
IndexedRecord.toMemProfRecord(CSIdConv),
9286
};
87+
if (CSIdConv.LastUnmappedId)
88+
return make_error<InstrProfError>(instrprof_error::hash_mismatch);
9389
Iter++;
9490
return Error::success();
9591
}

llvm/lib/ProfileData/InstrProfReader.cpp

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,53 +1520,35 @@ IndexedMemProfReader::getMemProfRecord(const uint64_t FuncNameHash) const {
15201520

15211521
// Setup a callback to convert from frame ids to frame using the on-disk
15221522
// FrameData hash table.
1523-
std::optional<memprof::FrameId> LastUnmappedFrameId;
1524-
auto IdToFrameCallback = [&](const memprof::FrameId Id) {
1525-
auto FrIter = MemProfFrameTable->find(Id);
1526-
if (FrIter == MemProfFrameTable->end()) {
1527-
LastUnmappedFrameId = Id;
1528-
return memprof::Frame(0, 0, 0, false);
1529-
}
1530-
return *FrIter;
1531-
};
1523+
memprof::FrameIdConverter<MemProfFrameHashTable> FrameIdConv(
1524+
*MemProfFrameTable.get());
15321525

15331526
// Setup a callback to convert call stack ids to call stacks using the on-disk
15341527
// hash table.
1535-
std::optional<memprof::CallStackId> LastUnmappedCSId;
1536-
auto CSIdToCallStackCallback = [&](memprof::CallStackId CSId) {
1537-
llvm::SmallVector<memprof::Frame> Frames;
1538-
auto CSIter = MemProfCallStackTable->find(CSId);
1539-
if (CSIter == MemProfCallStackTable->end()) {
1540-
LastUnmappedCSId = CSId;
1541-
} else {
1542-
const llvm::SmallVector<memprof::FrameId> &CS = *CSIter;
1543-
Frames.reserve(CS.size());
1544-
for (memprof::FrameId Id : CS)
1545-
Frames.push_back(IdToFrameCallback(Id));
1546-
}
1547-
return Frames;
1548-
};
1528+
memprof::CallStackIdConverter<MemProfCallStackHashTable> CSIdConv(
1529+
*MemProfCallStackTable.get(), FrameIdConv);
15491530

15501531
const memprof::IndexedMemProfRecord IndexedRecord = *Iter;
15511532
memprof::MemProfRecord Record;
15521533
if (MemProfCallStackTable)
1553-
Record = IndexedRecord.toMemProfRecord(CSIdToCallStackCallback);
1534+
Record = IndexedRecord.toMemProfRecord(CSIdConv);
15541535
else
1555-
Record = memprof::MemProfRecord(IndexedRecord, IdToFrameCallback);
1536+
Record = memprof::MemProfRecord(IndexedRecord, FrameIdConv);
15561537

15571538
// Check that all frame ids were successfully converted to frames.
1558-
if (LastUnmappedFrameId) {
1559-
return make_error<InstrProfError>(instrprof_error::hash_mismatch,
1560-
"memprof frame not found for frame id " +
1561-
Twine(*LastUnmappedFrameId));
1539+
if (FrameIdConv.LastUnmappedId) {
1540+
return make_error<InstrProfError>(
1541+
instrprof_error::hash_mismatch,
1542+
"memprof frame not found for frame id " +
1543+
Twine(*FrameIdConv.LastUnmappedId));
15621544
}
15631545

15641546
// Check that all call stack ids were successfully converted to call stacks.
1565-
if (LastUnmappedCSId) {
1547+
if (CSIdConv.LastUnmappedId) {
15661548
return make_error<InstrProfError>(
15671549
instrprof_error::hash_mismatch,
15681550
"memprof call stack not found for call stack id " +
1569-
Twine(*LastUnmappedCSId));
1551+
Twine(*CSIdConv.LastUnmappedId));
15701552
}
15711553
return Record;
15721554
}

llvm/unittests/ProfileData/InstrProfTest.cpp

Lines changed: 14 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -495,44 +495,6 @@ TEST_F(InstrProfTest, test_memprof_v0) {
495495
EXPECT_THAT(WantRecord, EqualsRecord(Record));
496496
}
497497

498-
struct CallStackIdConverter {
499-
std::optional<memprof::FrameId> LastUnmappedFrameId;
500-
std::optional<memprof::CallStackId> LastUnmappedCSId;
501-
502-
const FrameIdMapTy &IdToFrameMap;
503-
const CallStackIdMapTy &CSIdToCallStackMap;
504-
505-
CallStackIdConverter() = delete;
506-
CallStackIdConverter(const FrameIdMapTy &IdToFrameMap,
507-
const CallStackIdMapTy &CSIdToCallStackMap)
508-
: IdToFrameMap(IdToFrameMap), CSIdToCallStackMap(CSIdToCallStackMap) {}
509-
510-
llvm::SmallVector<memprof::Frame>
511-
operator()(::llvm::memprof::CallStackId CSId) {
512-
auto IdToFrameCallback = [&](const memprof::FrameId Id) {
513-
auto Iter = IdToFrameMap.find(Id);
514-
if (Iter == IdToFrameMap.end()) {
515-
LastUnmappedFrameId = Id;
516-
return memprof::Frame(0, 0, 0, false);
517-
}
518-
return Iter->second;
519-
};
520-
521-
llvm::SmallVector<memprof::Frame> Frames;
522-
auto CSIter = CSIdToCallStackMap.find(CSId);
523-
if (CSIter == CSIdToCallStackMap.end()) {
524-
LastUnmappedCSId = CSId;
525-
} else {
526-
const ::llvm::SmallVector<::llvm::memprof::FrameId> &CS =
527-
CSIter->getSecond();
528-
Frames.reserve(CS.size());
529-
for (::llvm::memprof::FrameId Id : CS)
530-
Frames.push_back(IdToFrameCallback(Id));
531-
}
532-
return Frames;
533-
}
534-
};
535-
536498
TEST_F(InstrProfTest, test_memprof_v2_full_schema) {
537499
const MemInfoBlock MIB = makeFullMIB();
538500

@@ -562,14 +524,16 @@ TEST_F(InstrProfTest, test_memprof_v2_full_schema) {
562524
ASSERT_THAT_ERROR(RecordOr.takeError(), Succeeded());
563525
const memprof::MemProfRecord &Record = RecordOr.get();
564526

565-
CallStackIdConverter CSIdConv(IdToFrameMap, CSIdToCallStackMap);
527+
memprof::FrameIdConverter<decltype(IdToFrameMap)> FrameIdConv(IdToFrameMap);
528+
memprof::CallStackIdConverter<decltype(CSIdToCallStackMap)> CSIdConv(
529+
CSIdToCallStackMap, FrameIdConv);
566530

567531
const ::llvm::memprof::MemProfRecord WantRecord =
568532
IndexedMR.toMemProfRecord(CSIdConv);
569-
ASSERT_EQ(CSIdConv.LastUnmappedFrameId, std::nullopt)
570-
<< "could not map frame id: " << *CSIdConv.LastUnmappedFrameId;
571-
ASSERT_EQ(CSIdConv.LastUnmappedCSId, std::nullopt)
572-
<< "could not map call stack id: " << *CSIdConv.LastUnmappedCSId;
533+
ASSERT_EQ(FrameIdConv.LastUnmappedId, std::nullopt)
534+
<< "could not map frame id: " << *FrameIdConv.LastUnmappedId;
535+
ASSERT_EQ(CSIdConv.LastUnmappedId, std::nullopt)
536+
<< "could not map call stack id: " << *CSIdConv.LastUnmappedId;
573537
EXPECT_THAT(WantRecord, EqualsRecord(Record));
574538
}
575539

@@ -602,14 +566,16 @@ TEST_F(InstrProfTest, test_memprof_v2_partial_schema) {
602566
ASSERT_THAT_ERROR(RecordOr.takeError(), Succeeded());
603567
const memprof::MemProfRecord &Record = RecordOr.get();
604568

605-
CallStackIdConverter CSIdConv(IdToFrameMap, CSIdToCallStackMap);
569+
memprof::FrameIdConverter<decltype(IdToFrameMap)> FrameIdConv(IdToFrameMap);
570+
memprof::CallStackIdConverter<decltype(CSIdToCallStackMap)> CSIdConv(
571+
CSIdToCallStackMap, FrameIdConv);
606572

607573
const ::llvm::memprof::MemProfRecord WantRecord =
608574
IndexedMR.toMemProfRecord(CSIdConv);
609-
ASSERT_EQ(CSIdConv.LastUnmappedFrameId, std::nullopt)
610-
<< "could not map frame id: " << *CSIdConv.LastUnmappedFrameId;
611-
ASSERT_EQ(CSIdConv.LastUnmappedCSId, std::nullopt)
612-
<< "could not map call stack id: " << *CSIdConv.LastUnmappedCSId;
575+
ASSERT_EQ(FrameIdConv.LastUnmappedId, std::nullopt)
576+
<< "could not map frame id: " << *FrameIdConv.LastUnmappedId;
577+
ASSERT_EQ(CSIdConv.LastUnmappedId, std::nullopt)
578+
<< "could not map call stack id: " << *CSIdConv.LastUnmappedId;
613579
EXPECT_THAT(WantRecord, EqualsRecord(Record));
614580
}
615581

llvm/unittests/ProfileData/MemProfTest.cpp

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -502,37 +502,15 @@ TEST(MemProf, IndexedMemProfRecordToMemProfRecord) {
502502
IndexedRecord.CallSiteIds.push_back(llvm::memprof::hashCallStack(CS3));
503503
IndexedRecord.CallSiteIds.push_back(llvm::memprof::hashCallStack(CS4));
504504

505-
bool CSIdMissing = false;
506-
bool FrameIdMissing = false;
507-
508-
auto Callback = [&](CallStackId CSId) -> llvm::SmallVector<Frame> {
509-
llvm::SmallVector<Frame> CallStack;
510-
llvm::SmallVector<FrameId> FrameIds;
511-
512-
auto Iter = CallStackIdMap.find(CSId);
513-
if (Iter == CallStackIdMap.end())
514-
CSIdMissing = true;
515-
else
516-
FrameIds = Iter->second;
517-
518-
for (FrameId Id : FrameIds) {
519-
Frame F(0, 0, 0, false);
520-
auto Iter = FrameIdMap.find(Id);
521-
if (Iter == FrameIdMap.end())
522-
FrameIdMissing = true;
523-
else
524-
F = Iter->second;
525-
CallStack.push_back(F);
526-
}
527-
528-
return CallStack;
529-
};
530-
531-
MemProfRecord Record = IndexedRecord.toMemProfRecord(Callback);
505+
llvm::memprof::FrameIdConverter<decltype(FrameIdMap)> FrameIdConv(FrameIdMap);
506+
llvm::memprof::CallStackIdConverter<decltype(CallStackIdMap)> CSIdConv(
507+
CallStackIdMap, FrameIdConv);
508+
509+
MemProfRecord Record = IndexedRecord.toMemProfRecord(CSIdConv);
532510

533511
// Make sure that all lookups are successful.
534-
ASSERT_FALSE(CSIdMissing);
535-
ASSERT_FALSE(FrameIdMissing);
512+
ASSERT_EQ(FrameIdConv.LastUnmappedId, std::nullopt);
513+
ASSERT_EQ(CSIdConv.LastUnmappedId, std::nullopt);
536514

537515
// Verify the contents of Record.
538516
ASSERT_THAT(Record.AllocSites, SizeIs(2));

0 commit comments

Comments
 (0)