Skip to content

Commit 3526020

Browse files
Repply [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. This iteration fixes a problem uncovered with ubsan, where we were dereferencing an uninitialized std::unique_ptr.
1 parent 6dd9061 commit 3526020

File tree

5 files changed

+107
-125
lines changed

5 files changed

+107
-125
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: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,54 +1520,38 @@ 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-
};
1532-
1533-
// Setup a callback to convert call stack ids to call stacks using the on-disk
1534-
// 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-
};
1523+
memprof::FrameIdConverter<MemProfFrameHashTable> FrameIdConv(
1524+
*MemProfFrameTable.get());
15491525

15501526
const memprof::IndexedMemProfRecord IndexedRecord = *Iter;
15511527
memprof::MemProfRecord Record;
1552-
if (MemProfCallStackTable)
1553-
Record = IndexedRecord.toMemProfRecord(CSIdToCallStackCallback);
1554-
else
1555-
Record = memprof::MemProfRecord(IndexedRecord, IdToFrameCallback);
1528+
if (MemProfCallStackTable) {
1529+
// Setup a callback to convert call stack ids to call stacks using the
1530+
// on-disk hash table.
1531+
memprof::CallStackIdConverter<MemProfCallStackHashTable> CSIdConv(
1532+
*MemProfCallStackTable.get(), FrameIdConv);
15561533

1557-
// 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));
1534+
Record = IndexedRecord.toMemProfRecord(CSIdConv);
1535+
1536+
// Check that all call stack ids were successfully converted to call stacks.
1537+
if (CSIdConv.LastUnmappedId) {
1538+
return make_error<InstrProfError>(
1539+
instrprof_error::hash_mismatch,
1540+
"memprof call stack not found for call stack id " +
1541+
Twine(*CSIdConv.LastUnmappedId));
1542+
}
1543+
} else {
1544+
Record = memprof::MemProfRecord(IndexedRecord, FrameIdConv);
15621545
}
15631546

1564-
// Check that all call stack ids were successfully converted to call stacks.
1565-
if (LastUnmappedCSId) {
1547+
// Check that all frame ids were successfully converted to frames.
1548+
if (FrameIdConv.LastUnmappedId) {
15661549
return make_error<InstrProfError>(
15671550
instrprof_error::hash_mismatch,
1568-
"memprof call stack not found for call stack id " +
1569-
Twine(*LastUnmappedCSId));
1551+
"memprof frame not found for frame id " +
1552+
Twine(*FrameIdConv.LastUnmappedId));
15701553
}
1554+
15711555
return Record;
15721556
}
15731557

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)