Skip to content

Commit eb92157

Browse files
[MemProf] Add ability to export or highlight only a portion of graph (#128255)
To simplify debugging and analysis, particularly for very large applications with large graphs, this patch adds support for either highlighting a single context id or allocation's context ids, and/or only exporting the nodes/edges for a single context id or allocation's context ids. When highlighting, the specified nodes and edges are a brighter color and larger. This can be controlled by the new -memprof-dot-scope={all,alloc,context} flag which controls how much to export, along with two companion flags: -memprof-dot-alloc-id=ID -memprof-dot-context-id=ID These two are interpreted differently depending on the value of -memprof-dot-scope (where "all" is the default). If exporting all, one of the above flags can optionally be passed to highlight the nodes/edges for the given context id or allocation's context ids. If exporting alloc scope, an alloc id must be provided. A context id can optionally be provided to highlight that context. If exporting context scope, a context id must be provided. The ids to use can be obtained either by looking at the full graph, or a context id can be identified from the -memprof-report-hinted-sizes output after PR128188 is merged.
1 parent 9d6f264 commit eb92157

File tree

2 files changed

+397
-13
lines changed

2 files changed

+397
-13
lines changed

llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp

Lines changed: 132 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
103131
static 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+
551583
private:
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> *> {
30233065
template <typename DerivedCCG, typename FuncTy, typename CallTy>
30243066
struct 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

30893181
private:
@@ -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+
31243229
template <typename DerivedCCG, typename FuncTy, typename CallTy>
31253230
void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::exportToDot(
31263231
std::string Label) const {
@@ -5183,6 +5288,20 @@ bool MemProfContextDisambiguation::processModule(
51835288
MemProfContextDisambiguation::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

Comments
 (0)