@@ -100,6 +100,34 @@ static cl::opt<bool> ExportToDot("memprof-export-to-dot", cl::init(false),
100100 cl::Hidden,
101101 cl::desc(" Export graph to dot files." ));
102102
103+ // How much of the graph to export to dot.
104+ enum DotScope {
105+ All, // The full CCG graph.
106+ Alloc, // Only contexts for the specified allocation.
107+ Context, // Only the specified context.
108+ };
109+
110+ static cl::opt<DotScope> DotGraphScope (
111+ " memprof-dot-scope" , cl::desc(" Scope of graph to export to dot" ),
112+ cl::Hidden, cl::init(DotScope::All),
113+ cl::values(
114+ clEnumValN (DotScope::All, " all" , " Export full callsite graph" ),
115+ clEnumValN(DotScope::Alloc, " alloc" ,
116+ " Export only nodes with contexts feeding given "
117+ " -memprof-dot-alloc-id" ),
118+ clEnumValN(DotScope::Context, " context" ,
119+ " Export only nodes with given -memprof-dot-context-id" )));
120+
121+ static cl::opt<unsigned >
122+ AllocIdForDot (" memprof-dot-alloc-id" , cl::init(0 ), cl::Hidden,
123+ cl::desc(" Id of alloc to export if -memprof-dot-scope=alloc "
124+ " or to highlight if -memprof-dot-scope=all" ));
125+
126+ static cl::opt<unsigned > ContextIdForDot (
127+ " memprof-dot-context-id" , cl::init(0 ), cl::Hidden,
128+ cl::desc(" Id of context to export if -memprof-dot-scope=context or to "
129+ " highlight otherwise" ));
130+
103131static cl::opt<bool >
104132 DumpCCG (" memprof-dump-ccg" , cl::init(false ), cl::Hidden,
105133 cl::desc(" Dump CallingContextGraph to stdout after each stage." ));
@@ -548,6 +576,10 @@ class CallsiteContextGraph {
548576 // / Map from callsite node to the enclosing caller function.
549577 std::map<const ContextNode *, const FuncTy *> NodeToCallingFunc;
550578
579+ // When exporting to dot, and an allocation id is specified, contains the
580+ // context ids on that allocation.
581+ DenseSet<uint32_t > DotAllocContextIds;
582+
551583private:
552584 using EdgeIter = typename std::vector<std::shared_ptr<ContextEdge>>::iterator;
553585
@@ -1324,6 +1356,8 @@ CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::duplicateContextIds(
13241356 assert (ContextIdToAllocationType.count (OldId));
13251357 // The new context has the same allocation type as original.
13261358 ContextIdToAllocationType[LastContextId] = ContextIdToAllocationType[OldId];
1359+ if (DotAllocContextIds.contains (OldId))
1360+ DotAllocContextIds.insert (LastContextId);
13271361 }
13281362 return NewContextIds;
13291363}
@@ -2084,6 +2118,10 @@ ModuleCallsiteContextGraph::ModuleCallsiteContextGraph(
20842118 AllocNode, StackContext, CallsiteContext,
20852119 getMIBAllocType (MIBMD), ContextSizeInfo);
20862120 }
2121+ // If exporting the graph to dot and an allocation id of interest was
2122+ // specified, record all the context ids for this allocation node.
2123+ if (ExportToDot && AllocNode->OrigStackOrAllocId == AllocIdForDot)
2124+ DotAllocContextIds = AllocNode->getContextIds ();
20872125 assert (AllocNode->AllocTypes != (uint8_t )AllocationType::None);
20882126 // Memprof and callsite metadata on memory allocations no longer
20892127 // needed.
@@ -2176,6 +2214,10 @@ IndexCallsiteContextGraph::IndexCallsiteContextGraph(
21762214 ContextSizeInfo);
21772215 I++;
21782216 }
2217+ // If exporting the graph to dot and an allocation id of interest was
2218+ // specified, record all the context ids for this allocation node.
2219+ if (ExportToDot && AllocNode->OrigStackOrAllocId == AllocIdForDot)
2220+ DotAllocContextIds = AllocNode->getContextIds ();
21792221 assert (AllocNode->AllocTypes != (uint8_t )AllocationType::None);
21802222 // Initialize version 0 on the summary alloc node to the current alloc
21812223 // type, unless it has both types in which case make it default, so
@@ -3023,7 +3065,16 @@ struct GraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *> {
30233065template <typename DerivedCCG, typename FuncTy, typename CallTy>
30243066struct DOTGraphTraits <const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
30253067 : public DefaultDOTGraphTraits {
3026- DOTGraphTraits (bool IsSimple = false ) : DefaultDOTGraphTraits(IsSimple) {}
3068+ DOTGraphTraits (bool IsSimple = false ) : DefaultDOTGraphTraits(IsSimple) {
3069+ // If the user requested the full graph to be exported, but provided an
3070+ // allocation id, or if the user gave a context id and requested more than
3071+ // just a specific context to be exported, note that highlighting is
3072+ // enabled.
3073+ DoHighlight =
3074+ (AllocIdForDot.getNumOccurrences () && DotGraphScope == DotScope::All) ||
3075+ (ContextIdForDot.getNumOccurrences () &&
3076+ DotGraphScope != DotScope::Context);
3077+ }
30273078
30283079 using GraphType = const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
30293080 using GTraits = GraphTraits<GraphType>;
@@ -3051,12 +3102,29 @@ struct DOTGraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
30513102 return LabelString;
30523103 }
30533104
3054- static std::string getNodeAttributes (NodeRef Node, GraphType) {
3105+ static std::string getNodeAttributes (NodeRef Node, GraphType G) {
3106+ auto ContextIds = Node->getContextIds ();
3107+ // If highlighting enabled, see if this node contains any of the context ids
3108+ // of interest. If so, it will use a different color and a larger fontsize
3109+ // (which makes the node larger as well).
3110+ bool Highlight = false ;
3111+ if (DoHighlight) {
3112+ assert (ContextIdForDot.getNumOccurrences () ||
3113+ AllocIdForDot.getNumOccurrences ());
3114+ if (ContextIdForDot.getNumOccurrences ())
3115+ Highlight = ContextIds.contains (ContextIdForDot);
3116+ else
3117+ Highlight = set_intersects (ContextIds, G->DotAllocContextIds );
3118+ }
30553119 std::string AttributeString = (Twine (" tooltip=\" " ) + getNodeId (Node) + " " +
3056- getContextIds (Node-> getContextIds () ) + " \" " )
3120+ getContextIds (ContextIds ) + " \" " )
30573121 .str ();
3122+ // Default fontsize is 14
3123+ if (Highlight)
3124+ AttributeString += " ,fontsize=\" 30\" " ;
30583125 AttributeString +=
3059- (Twine (" ,fillcolor=\" " ) + getColor (Node->AllocTypes ) + " \" " ).str ();
3126+ (Twine (" ,fillcolor=\" " ) + getColor (Node->AllocTypes , Highlight) + " \" " )
3127+ .str ();
30603128 if (Node->CloneOf ) {
30613129 AttributeString += " ,color=\" blue\" " ;
30623130 AttributeString += " ,style=\" filled,bold,dashed\" " ;
@@ -3066,9 +3134,22 @@ struct DOTGraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
30663134 }
30673135
30683136 static std::string getEdgeAttributes (NodeRef, ChildIteratorType ChildIter,
3069- GraphType) {
3137+ GraphType G ) {
30703138 auto &Edge = *(ChildIter.getCurrent ());
3071- auto Color = getColor (Edge->AllocTypes );
3139+ // If highlighting enabled, see if this edge contains any of the context ids
3140+ // of interest. If so, it will use a different color and a heavier arrow
3141+ // size and weight (the larger weight makes the highlighted path
3142+ // straighter).
3143+ bool Highlight = false ;
3144+ if (DoHighlight) {
3145+ assert (ContextIdForDot.getNumOccurrences () ||
3146+ AllocIdForDot.getNumOccurrences ());
3147+ if (ContextIdForDot.getNumOccurrences ())
3148+ Highlight = Edge->ContextIds .contains (ContextIdForDot);
3149+ else
3150+ Highlight = set_intersects (Edge->ContextIds , G->DotAllocContextIds );
3151+ }
3152+ auto Color = getColor (Edge->AllocTypes , Highlight);
30723153 std::string AttributeString =
30733154 (Twine (" tooltip=\" " ) + getContextIds (Edge->ContextIds ) + " \" " +
30743155 // fillcolor is the arrow head and color is the line
@@ -3077,13 +3158,24 @@ struct DOTGraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
30773158 .str ();
30783159 if (Edge->IsBackedge )
30793160 AttributeString += " ,style=\" dotted\" " ;
3161+ // Default penwidth and weight are both 1.
3162+ if (Highlight)
3163+ AttributeString += " ,penwidth=\" 2.0\" ,weight=\" 2\" " ;
30803164 return AttributeString;
30813165 }
30823166
30833167 // Since the NodeOwners list includes nodes that are no longer connected to
30843168 // the graph, skip them here.
3085- static bool isNodeHidden (NodeRef Node, GraphType) {
3086- return Node->isRemoved ();
3169+ static bool isNodeHidden (NodeRef Node, GraphType G) {
3170+ if (Node->isRemoved ())
3171+ return true ;
3172+ // If a scope smaller than the full graph was requested, see if this node
3173+ // contains any of the context ids of interest.
3174+ if (DotGraphScope == DotScope::Alloc)
3175+ return !set_intersects (Node->getContextIds (), G->DotAllocContextIds );
3176+ if (DotGraphScope == DotScope::Context)
3177+ return !Node->getContextIds ().contains (ContextIdForDot);
3178+ return false ;
30873179 }
30883180
30893181private:
@@ -3100,16 +3192,20 @@ struct DOTGraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
31003192 return IdString;
31013193 }
31023194
3103- static std::string getColor (uint8_t AllocTypes) {
3195+ static std::string getColor (uint8_t AllocTypes, bool Highlight) {
3196+ // If DoHighlight is not enabled, we want to use the highlight colors for
3197+ // NotCold and Cold, and the non-highlight color for NotCold+Cold. This is
3198+ // both compatible with the color scheme before highlighting was supported,
3199+ // and for the NotCold+Cold color the non-highlight color is a bit more
3200+ // readable.
31043201 if (AllocTypes == (uint8_t )AllocationType::NotCold)
31053202 // Color "brown1" actually looks like a lighter red.
3106- return " brown1" ;
3203+ return !DoHighlight || Highlight ? " brown1" : " lightpink " ;
31073204 if (AllocTypes == (uint8_t )AllocationType::Cold)
3108- return " cyan" ;
3205+ return !DoHighlight || Highlight ? " cyan" : " lightskyblue " ;
31093206 if (AllocTypes ==
31103207 ((uint8_t )AllocationType::NotCold | (uint8_t )AllocationType::Cold))
3111- // Lighter purple.
3112- return " mediumorchid1" ;
3208+ return Highlight ? " magenta" : " mediumorchid1" ;
31133209 return " gray" ;
31143210 }
31153211
@@ -3119,8 +3215,17 @@ struct DOTGraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
31193215 std::string Result = SStream.str ();
31203216 return Result;
31213217 }
3218+
3219+ // True if we should highlight a specific context or allocation's contexts in
3220+ // the emitted graph.
3221+ static bool DoHighlight;
31223222};
31233223
3224+ template <typename DerivedCCG, typename FuncTy, typename CallTy>
3225+ bool DOTGraphTraits<
3226+ const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>::DoHighlight =
3227+ false ;
3228+
31243229template <typename DerivedCCG, typename FuncTy, typename CallTy>
31253230void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::exportToDot(
31263231 std::string Label) const {
@@ -5183,6 +5288,20 @@ bool MemProfContextDisambiguation::processModule(
51835288MemProfContextDisambiguation::MemProfContextDisambiguation (
51845289 const ModuleSummaryIndex *Summary, bool isSamplePGO)
51855290 : ImportSummary(Summary), isSamplePGO(isSamplePGO) {
5291+ // Check the dot graph printing options once here, to make sure we have valid
5292+ // and expected combinations.
5293+ if (DotGraphScope == DotScope::Alloc && !AllocIdForDot.getNumOccurrences ())
5294+ llvm::report_fatal_error (
5295+ " -memprof-dot-scope=alloc requires -memprof-dot-alloc-id" );
5296+ if (DotGraphScope == DotScope::Context &&
5297+ !ContextIdForDot.getNumOccurrences ())
5298+ llvm::report_fatal_error (
5299+ " -memprof-dot-scope=context requires -memprof-dot-context-id" );
5300+ if (DotGraphScope == DotScope::All && AllocIdForDot.getNumOccurrences () &&
5301+ ContextIdForDot.getNumOccurrences ())
5302+ llvm::report_fatal_error (
5303+ " -memprof-dot-scope=all can't have both -memprof-dot-alloc-id and "
5304+ " -memprof-dot-context-id" );
51865305 if (ImportSummary) {
51875306 // The MemProfImportSummary should only be used for testing ThinLTO
51885307 // distributed backend handling via opt, in which case we don't have a
0 commit comments