-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[MemProf] Print full context hash when reporting hinted bytes #114465
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
11d13e4
cb1c098
8d960cf
0d2f55a
68b327e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -308,7 +308,7 @@ enum GlobalValueSummarySymtabCodes { | |
| FS_PERMODULE_CALLSITE_INFO = 26, | ||
| // Summary of per-module allocation memprof metadata. | ||
| // [nummib, nummib x (alloc type, numstackids, numstackids x stackidindex), | ||
| // [nummib x total size]?] | ||
| // [nummib x (numcontext x contextsizeindex)]?] | ||
| FS_PERMODULE_ALLOC_INFO = 27, | ||
| // Summary of combined index memprof callsite metadata. | ||
| // [valueid, numstackindices, numver, | ||
|
|
@@ -317,9 +317,15 @@ enum GlobalValueSummarySymtabCodes { | |
| // Summary of combined index allocation memprof metadata. | ||
| // [nummib, numver, | ||
| // nummib x (alloc type, numstackids, numstackids x stackidindex), | ||
| // numver x version, [nummib x total size]?] | ||
| // numver x version] | ||
| FS_COMBINED_ALLOC_INFO = 29, | ||
| // List of all stack ids referenced by index in the callsite and alloc infos. | ||
| // [n x stack id] | ||
| FS_STACK_IDS = 30, | ||
| // List of all (full stack id, total size) pairs optionally referenced by | ||
| // index from the alloc info records. | ||
| // [n x (full stack id, total size)] | ||
| FS_CONTEXT_SIZE_INFOS = 31, | ||
|
||
| }; | ||
|
|
||
| enum MetadataCodes { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -302,6 +302,14 @@ template <> struct DenseMapInfo<ValueInfo> { | |
| static unsigned getHashValue(ValueInfo I) { return (uintptr_t)I.getRef(); } | ||
| }; | ||
|
|
||
| // For optional hinted size reporting, holds a pair of the full stack id | ||
| // (pre-trimming, from the full context in the profile), and the associated | ||
| // total profiled size. | ||
| struct ContextTotalSize { | ||
| uint64_t FullStackId; | ||
| uint64_t TotalSize; | ||
| }; | ||
|
|
||
| /// Summary of memprof callsite metadata. | ||
| struct CallsiteInfo { | ||
| // Actual callee function. | ||
|
|
@@ -408,9 +416,15 @@ struct AllocInfo { | |
| // Vector of MIBs in this memprof metadata. | ||
| std::vector<MIBInfo> MIBs; | ||
|
|
||
| // If requested, keep track of total profiled sizes for each MIB. This will be | ||
| // a vector of the same length and order as the MIBs vector, if non-empty. | ||
| std::vector<uint64_t> TotalSizes; | ||
| // If requested, keep track of full stack contexts and total profiled sizes | ||
| // for each MIB. This will be a vector of the same length and order as the | ||
| // MIBs vector, if non-empty. Note that each MIB in the summary can have | ||
| // multiple of these as we trim the contexts when possible during matching. | ||
| // For hinted size reporting we, however, want the original pre-trimmed full | ||
| // stack context id for better correlation with the profile. Note that these | ||
| // are indexes into the ContextSizeInfos list in the index, to enable | ||
| // deduplication. | ||
| std::vector<std::vector<unsigned>> ContextSizeInfoIndices; | ||
|
|
||
| AllocInfo(std::vector<MIBInfo> MIBs) : MIBs(std::move(MIBs)) { | ||
| Versions.push_back(0); | ||
|
|
@@ -432,14 +446,21 @@ inline raw_ostream &operator<<(raw_ostream &OS, const AllocInfo &AE) { | |
| for (auto &M : AE.MIBs) { | ||
| OS << "\t\t" << M << "\n"; | ||
| } | ||
| if (!AE.TotalSizes.empty()) { | ||
| OS << " TotalSizes per MIB:\n\t\t"; | ||
| if (!AE.ContextSizeInfoIndices.empty()) { | ||
| OS << " ContextSizeInfo index per MIB:\n\t\t"; | ||
| First = true; | ||
| for (uint64_t TS : AE.TotalSizes) { | ||
| for (auto Indices : AE.ContextSizeInfoIndices) { | ||
| if (!First) | ||
| OS << ", "; | ||
| First = false; | ||
| OS << TS << "\n"; | ||
| bool FirstIndex = true; | ||
| for (uint64_t Index : Indices) { | ||
| if (!FirstIndex) | ||
| OS << ", "; | ||
| FirstIndex = false; | ||
| OS << Index; | ||
| } | ||
| OS << "\n"; | ||
| } | ||
| } | ||
| return OS; | ||
|
|
@@ -1426,6 +1447,19 @@ class ModuleSummaryIndex { | |
| // built via releaseTemporaryMemory. | ||
| DenseMap<uint64_t, unsigned> StackIdToIndex; | ||
|
|
||
| // List of unique ContextTotalSize structs (pair of the full stack id hash and | ||
| // its associated total profiled size). We use an index into this vector when | ||
| // referencing from the alloc summary to reduce the overall memory and size | ||
| // requirements, since often allocations may be duplicated due to inlining. | ||
| std::vector<ContextTotalSize> ContextSizeInfos; | ||
|
|
||
| // Temporary map while building the ContextSizeInfos list. Clear when index is | ||
| // completely built via releaseTemporaryMemory. | ||
| // Maps from full stack id to a map of total size to the assigned index. | ||
| // We need size in here too because due to stack truncation in the profile we | ||
| // can have the same full stack id and different sizes. | ||
| DenseMap<uint64_t, DenseMap<uint64_t, unsigned>> ContextToTotalSizeAndIndex; | ||
|
|
||
| // YAML I/O support. | ||
| friend yaml::MappingTraits<ModuleSummaryIndex>; | ||
|
|
||
|
|
@@ -1470,6 +1504,9 @@ class ModuleSummaryIndex { | |
| size_t size() const { return GlobalValueMap.size(); } | ||
|
|
||
| const std::vector<uint64_t> &stackIds() const { return StackIds; } | ||
| const std::vector<ContextTotalSize> &contextSizeInfos() const { | ||
| return ContextSizeInfos; | ||
| } | ||
|
|
||
| unsigned addOrGetStackIdIndex(uint64_t StackId) { | ||
| auto Inserted = StackIdToIndex.insert({StackId, StackIds.size()}); | ||
|
|
@@ -1483,15 +1520,36 @@ class ModuleSummaryIndex { | |
| return StackIds[Index]; | ||
| } | ||
|
|
||
| unsigned addOrGetContextSizeIndex(ContextTotalSize ContextSizeInfo) { | ||
| auto &Entry = ContextToTotalSizeAndIndex[ContextSizeInfo.FullStackId]; | ||
| auto Inserted = | ||
| Entry.insert({ContextSizeInfo.TotalSize, ContextSizeInfos.size()}); | ||
| if (Inserted.second) | ||
| ContextSizeInfos.push_back( | ||
| {ContextSizeInfo.FullStackId, ContextSizeInfo.TotalSize}); | ||
| else | ||
| assert(Inserted.first->first == ContextSizeInfo.TotalSize); | ||
| return Inserted.first->second; | ||
| } | ||
|
|
||
| ContextTotalSize getContextSizeInfoAtIndex(unsigned Index) const { | ||
| assert(ContextSizeInfos.size() > Index); | ||
| return ContextSizeInfos[Index]; | ||
| } | ||
|
|
||
| // Facility to release memory from data structures only needed during index | ||
| // construction (including while building combined index). Currently this only | ||
| // construction (including while building combined index). Currently this | ||
| // releases the temporary map used while constructing a correspondence between | ||
| // stack ids and their index in the StackIds vector. Mostly impactful when | ||
| // stack ids and their index in the StackIds vector, and a similar map used | ||
| // while constructing a the ContextSizeInfos vector. Mostly impactful when | ||
|
||
| // building a large combined index. | ||
| void releaseTemporaryMemory() { | ||
| assert(StackIdToIndex.size() == StackIds.size()); | ||
| StackIdToIndex.clear(); | ||
| StackIds.shrink_to_fit(); | ||
| assert(ContextToTotalSizeAndIndex.size() == ContextSizeInfos.size()); | ||
| ContextToTotalSizeAndIndex.clear(); | ||
| ContextSizeInfos.shrink_to_fit(); | ||
| } | ||
|
|
||
| /// Convenience function for doing a DFS on a ValueInfo. Marks the function in | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -99,12 +99,6 @@ AllocationType llvm::memprof::getMIBAllocType(const MDNode *MIB) { | |
| return AllocationType::NotCold; | ||
| } | ||
|
|
||
| uint64_t llvm::memprof::getMIBTotalSize(const MDNode *MIB) { | ||
| if (MIB->getNumOperands() < 3) | ||
| return 0; | ||
| return mdconst::dyn_extract<ConstantInt>(MIB->getOperand(2))->getZExtValue(); | ||
| } | ||
|
|
||
| std::string llvm::memprof::getAllocTypeAttributeString(AllocationType Type) { | ||
| switch (Type) { | ||
| case AllocationType::NotCold: | ||
|
|
@@ -135,22 +129,22 @@ bool llvm::memprof::hasSingleAllocType(uint8_t AllocTypes) { | |
| return NumAllocTypes == 1; | ||
| } | ||
|
|
||
| void CallStackTrie::addCallStack(AllocationType AllocType, | ||
| ArrayRef<uint64_t> StackIds, | ||
| uint64_t TotalSize) { | ||
| void CallStackTrie::addCallStack( | ||
| AllocationType AllocType, ArrayRef<uint64_t> StackIds, | ||
| std::vector<ContextTotalSize> ContextSizeInfo) { | ||
| bool First = true; | ||
| CallStackTrieNode *Curr = nullptr; | ||
| for (auto StackId : StackIds) { | ||
| // If this is the first stack frame, add or update alloc node. | ||
| // errs() << StackId << " "; | ||
|
||
| // If this is the first stack frame, add or update alloc node. | ||
| if (First) { | ||
| First = false; | ||
| if (Alloc) { | ||
| assert(AllocStackId == StackId); | ||
| Alloc->AllocTypes |= static_cast<uint8_t>(AllocType); | ||
| Alloc->TotalSize += TotalSize; | ||
| } else { | ||
| AllocStackId = StackId; | ||
| Alloc = new CallStackTrieNode(AllocType, TotalSize); | ||
| Alloc = new CallStackTrieNode(AllocType); | ||
| } | ||
| Curr = Alloc; | ||
| continue; | ||
|
|
@@ -160,15 +154,18 @@ void CallStackTrie::addCallStack(AllocationType AllocType, | |
| if (Next != Curr->Callers.end()) { | ||
| Curr = Next->second; | ||
| Curr->AllocTypes |= static_cast<uint8_t>(AllocType); | ||
| Curr->TotalSize += TotalSize; | ||
| continue; | ||
| } | ||
| // Otherwise add a new caller node. | ||
| auto *New = new CallStackTrieNode(AllocType, TotalSize); | ||
| auto *New = new CallStackTrieNode(AllocType); | ||
| Curr->Callers[StackId] = New; | ||
| Curr = New; | ||
| } | ||
| assert(Curr); | ||
| Curr->ContextSizeInfo.insert(Curr->ContextSizeInfo.end(), | ||
| ContextSizeInfo.begin(), ContextSizeInfo.end()); | ||
| std::vector<ContextTotalSize> AllContextSizeInfo; | ||
| collectContextSizeInfo(Curr, AllContextSizeInfo); | ||
| } | ||
|
|
||
| void CallStackTrie::addCallStack(MDNode *MIB) { | ||
|
|
@@ -181,21 +178,55 @@ void CallStackTrie::addCallStack(MDNode *MIB) { | |
| assert(StackId); | ||
| CallStack.push_back(StackId->getZExtValue()); | ||
| } | ||
| addCallStack(getMIBAllocType(MIB), CallStack, getMIBTotalSize(MIB)); | ||
| std::vector<ContextTotalSize> ContextSizeInfo; | ||
| // Collect the context size information if it exists. | ||
| if (MIB->getNumOperands() > 2) { | ||
| for (unsigned I = 2; I < MIB->getNumOperands(); I++) { | ||
| MDNode *ContextSizePair = dyn_cast<MDNode>(MIB->getOperand(I)); | ||
| assert(ContextSizePair->getNumOperands() == 2); | ||
| uint64_t FullStackId = | ||
| mdconst::dyn_extract<ConstantInt>(ContextSizePair->getOperand(0)) | ||
| ->getZExtValue(); | ||
| uint64_t TotalSize = | ||
| mdconst::dyn_extract<ConstantInt>(ContextSizePair->getOperand(1)) | ||
| ->getZExtValue(); | ||
| ContextSizeInfo.push_back({FullStackId, TotalSize}); | ||
| } | ||
| } | ||
| addCallStack(getMIBAllocType(MIB), CallStack, std::move(ContextSizeInfo)); | ||
| } | ||
|
|
||
| static MDNode *createMIBNode(LLVMContext &Ctx, ArrayRef<uint64_t> MIBCallStack, | ||
| AllocationType AllocType, uint64_t TotalSize) { | ||
| AllocationType AllocType, | ||
| ArrayRef<ContextTotalSize> ContextSizeInfo) { | ||
| SmallVector<Metadata *> MIBPayload( | ||
| {buildCallstackMetadata(MIBCallStack, Ctx)}); | ||
| MIBPayload.push_back( | ||
| MDString::get(Ctx, getAllocTypeAttributeString(AllocType))); | ||
| if (TotalSize) | ||
| MIBPayload.push_back(ValueAsMetadata::get( | ||
| ConstantInt::get(Type::getInt64Ty(Ctx), TotalSize))); | ||
| if (!ContextSizeInfo.empty()) { | ||
| for (auto Info : ContextSizeInfo) { | ||
|
||
| auto *FullStackIdMD = ValueAsMetadata::get( | ||
| ConstantInt::get(Type::getInt64Ty(Ctx), Info.FullStackId)); | ||
| auto *TotalSizeMD = ValueAsMetadata::get( | ||
| ConstantInt::get(Type::getInt64Ty(Ctx), Info.TotalSize)); | ||
| auto *ContextSizeMD = MDNode::get(Ctx, {FullStackIdMD, TotalSizeMD}); | ||
| MIBPayload.push_back(ContextSizeMD); | ||
| } | ||
| } | ||
| return MDNode::get(Ctx, MIBPayload); | ||
| } | ||
|
|
||
| void CallStackTrie::collectContextSizeInfo( | ||
| CallStackTrieNode *Node, std::vector<ContextTotalSize> &ContextSizeInfo) { | ||
| ContextSizeInfo.insert(ContextSizeInfo.end(), Node->ContextSizeInfo.begin(), | ||
| Node->ContextSizeInfo.end()); | ||
| if (Node->Callers.empty()) | ||
| return; | ||
|
||
| for (auto &Caller : Node->Callers) { | ||
| collectContextSizeInfo(Caller.second, ContextSizeInfo); | ||
| } | ||
| } | ||
|
|
||
| // Recursive helper to trim contexts and create metadata nodes. | ||
| // Caller should have pushed Node's loc to MIBCallStack. Doing this in the | ||
| // caller makes it simpler to handle the many early returns in this method. | ||
|
|
@@ -206,8 +237,10 @@ bool CallStackTrie::buildMIBNodes(CallStackTrieNode *Node, LLVMContext &Ctx, | |
| // Trim context below the first node in a prefix with a single alloc type. | ||
| // Add an MIB record for the current call stack prefix. | ||
| if (hasSingleAllocType(Node->AllocTypes)) { | ||
| std::vector<ContextTotalSize> ContextSizeInfo; | ||
| collectContextSizeInfo(Node, ContextSizeInfo); | ||
| MIBNodes.push_back(createMIBNode( | ||
| Ctx, MIBCallStack, (AllocationType)Node->AllocTypes, Node->TotalSize)); | ||
| Ctx, MIBCallStack, (AllocationType)Node->AllocTypes, ContextSizeInfo)); | ||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -243,8 +276,10 @@ bool CallStackTrie::buildMIBNodes(CallStackTrieNode *Node, LLVMContext &Ctx, | |
| // non-cold allocation type. | ||
| if (!CalleeHasAmbiguousCallerContext) | ||
| return false; | ||
| std::vector<ContextTotalSize> ContextSizeInfo; | ||
| collectContextSizeInfo(Node, ContextSizeInfo); | ||
| MIBNodes.push_back(createMIBNode(Ctx, MIBCallStack, AllocationType::NotCold, | ||
| Node->TotalSize)); | ||
| ContextSizeInfo)); | ||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -256,11 +291,14 @@ bool CallStackTrie::buildAndAttachMIBMetadata(CallBase *CI) { | |
| if (hasSingleAllocType(Alloc->AllocTypes)) { | ||
| addAllocTypeAttribute(Ctx, CI, (AllocationType)Alloc->AllocTypes); | ||
| if (MemProfReportHintedSizes) { | ||
| assert(Alloc->TotalSize); | ||
| errs() << "Total size for allocation with location hash " << AllocStackId | ||
| << " and single alloc type " | ||
| << getAllocTypeAttributeString((AllocationType)Alloc->AllocTypes) | ||
| << ": " << Alloc->TotalSize << "\n"; | ||
| std::vector<ContextTotalSize> ContextSizeInfo; | ||
| collectContextSizeInfo(Alloc, ContextSizeInfo); | ||
| for (const auto &Info : ContextSizeInfo) { | ||
|
||
| errs() << "Total size for full allocation context hash " | ||
|
||
| << Info.FullStackId << " and single alloc type " | ||
| << getAllocTypeAttributeString((AllocationType)Alloc->AllocTypes) | ||
| << ": " << Info.TotalSize << "\n"; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Just
= {}?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done