@@ -100,6 +100,34 @@ static cl::opt<bool> ExportToDot("memprof-export-to-dot", cl::init(false),
100
100
cl::Hidden,
101
101
cl::desc(" Export graph to dot files." ));
102
102
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
+
103
131
static cl::opt<bool >
104
132
DumpCCG (" memprof-dump-ccg" , cl::init(false ), cl::Hidden,
105
133
cl::desc(" Dump CallingContextGraph to stdout after each stage." ));
@@ -548,6 +576,10 @@ class CallsiteContextGraph {
548
576
// / Map from callsite node to the enclosing caller function.
549
577
std::map<const ContextNode *, const FuncTy *> NodeToCallingFunc;
550
578
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
+
551
583
private:
552
584
using EdgeIter = typename std::vector<std::shared_ptr<ContextEdge>>::iterator;
553
585
@@ -1324,6 +1356,8 @@ CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::duplicateContextIds(
1324
1356
assert (ContextIdToAllocationType.count (OldId));
1325
1357
// The new context has the same allocation type as original.
1326
1358
ContextIdToAllocationType[LastContextId] = ContextIdToAllocationType[OldId];
1359
+ if (DotAllocContextIds.contains (OldId))
1360
+ DotAllocContextIds.insert (LastContextId);
1327
1361
}
1328
1362
return NewContextIds;
1329
1363
}
@@ -2084,6 +2118,10 @@ ModuleCallsiteContextGraph::ModuleCallsiteContextGraph(
2084
2118
AllocNode, StackContext, CallsiteContext,
2085
2119
getMIBAllocType (MIBMD), ContextSizeInfo);
2086
2120
}
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 ();
2087
2125
assert (AllocNode->AllocTypes != (uint8_t )AllocationType::None);
2088
2126
// Memprof and callsite metadata on memory allocations no longer
2089
2127
// needed.
@@ -2176,6 +2214,10 @@ IndexCallsiteContextGraph::IndexCallsiteContextGraph(
2176
2214
ContextSizeInfo);
2177
2215
I++;
2178
2216
}
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 ();
2179
2221
assert (AllocNode->AllocTypes != (uint8_t )AllocationType::None);
2180
2222
// Initialize version 0 on the summary alloc node to the current alloc
2181
2223
// type, unless it has both types in which case make it default, so
@@ -3023,7 +3065,16 @@ struct GraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *> {
3023
3065
template <typename DerivedCCG, typename FuncTy, typename CallTy>
3024
3066
struct DOTGraphTraits <const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
3025
3067
: 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
+ }
3027
3078
3028
3079
using GraphType = const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3029
3080
using GTraits = GraphTraits<GraphType>;
@@ -3051,12 +3102,29 @@ struct DOTGraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
3051
3102
return LabelString;
3052
3103
}
3053
3104
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
+ }
3055
3119
std::string AttributeString = (Twine (" tooltip=\" " ) + getNodeId (Node) + " " +
3056
- getContextIds (Node-> getContextIds () ) + " \" " )
3120
+ getContextIds (ContextIds ) + " \" " )
3057
3121
.str ();
3122
+ // Default fontsize is 14
3123
+ if (Highlight)
3124
+ AttributeString += " ,fontsize=\" 30\" " ;
3058
3125
AttributeString +=
3059
- (Twine (" ,fillcolor=\" " ) + getColor (Node->AllocTypes ) + " \" " ).str ();
3126
+ (Twine (" ,fillcolor=\" " ) + getColor (Node->AllocTypes , Highlight) + " \" " )
3127
+ .str ();
3060
3128
if (Node->CloneOf ) {
3061
3129
AttributeString += " ,color=\" blue\" " ;
3062
3130
AttributeString += " ,style=\" filled,bold,dashed\" " ;
@@ -3066,9 +3134,22 @@ struct DOTGraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
3066
3134
}
3067
3135
3068
3136
static std::string getEdgeAttributes (NodeRef, ChildIteratorType ChildIter,
3069
- GraphType) {
3137
+ GraphType G ) {
3070
3138
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);
3072
3153
std::string AttributeString =
3073
3154
(Twine (" tooltip=\" " ) + getContextIds (Edge->ContextIds ) + " \" " +
3074
3155
// fillcolor is the arrow head and color is the line
@@ -3077,13 +3158,24 @@ struct DOTGraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
3077
3158
.str ();
3078
3159
if (Edge->IsBackedge )
3079
3160
AttributeString += " ,style=\" dotted\" " ;
3161
+ // Default penwidth and weight are both 1.
3162
+ if (Highlight)
3163
+ AttributeString += " ,penwidth=\" 2.0\" ,weight=\" 2\" " ;
3080
3164
return AttributeString;
3081
3165
}
3082
3166
3083
3167
// Since the NodeOwners list includes nodes that are no longer connected to
3084
3168
// 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 ;
3087
3179
}
3088
3180
3089
3181
private:
@@ -3100,16 +3192,20 @@ struct DOTGraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
3100
3192
return IdString;
3101
3193
}
3102
3194
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.
3104
3201
if (AllocTypes == (uint8_t )AllocationType::NotCold)
3105
3202
// Color "brown1" actually looks like a lighter red.
3106
- return " brown1" ;
3203
+ return !DoHighlight || Highlight ? " brown1" : " lightpink " ;
3107
3204
if (AllocTypes == (uint8_t )AllocationType::Cold)
3108
- return " cyan" ;
3205
+ return !DoHighlight || Highlight ? " cyan" : " lightskyblue " ;
3109
3206
if (AllocTypes ==
3110
3207
((uint8_t )AllocationType::NotCold | (uint8_t )AllocationType::Cold))
3111
- // Lighter purple.
3112
- return " mediumorchid1" ;
3208
+ return Highlight ? " magenta" : " mediumorchid1" ;
3113
3209
return " gray" ;
3114
3210
}
3115
3211
@@ -3119,8 +3215,17 @@ struct DOTGraphTraits<const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>
3119
3215
std::string Result = SStream.str ();
3120
3216
return Result;
3121
3217
}
3218
+
3219
+ // True if we should highlight a specific context or allocation's contexts in
3220
+ // the emitted graph.
3221
+ static bool DoHighlight;
3122
3222
};
3123
3223
3224
+ template <typename DerivedCCG, typename FuncTy, typename CallTy>
3225
+ bool DOTGraphTraits<
3226
+ const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>::DoHighlight =
3227
+ false ;
3228
+
3124
3229
template <typename DerivedCCG, typename FuncTy, typename CallTy>
3125
3230
void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::exportToDot(
3126
3231
std::string Label) const {
@@ -5183,6 +5288,20 @@ bool MemProfContextDisambiguation::processModule(
5183
5288
MemProfContextDisambiguation::MemProfContextDisambiguation (
5184
5289
const ModuleSummaryIndex *Summary, bool isSamplePGO)
5185
5290
: 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" );
5186
5305
if (ImportSummary) {
5187
5306
// The MemProfImportSummary should only be used for testing ThinLTO
5188
5307
// distributed backend handling via opt, in which case we don't have a
0 commit comments