diff --git a/llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp b/llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp index 0cb71cbd045d..52eb82f1c858 100644 --- a/llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp +++ b/llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp @@ -100,6 +100,34 @@ static cl::opt ExportToDot("memprof-export-to-dot", cl::init(false), cl::Hidden, cl::desc("Export graph to dot files.")); +// How much of the graph to export to dot. +enum DotScope { + All, // The full CCG graph. + Alloc, // Only contexts for the specified allocation. + Context, // Only the specified context. +}; + +static cl::opt DotGraphScope( + "memprof-dot-scope", cl::desc("Scope of graph to export to dot"), + cl::Hidden, cl::init(DotScope::All), + cl::values( + clEnumValN(DotScope::All, "all", "Export full callsite graph"), + clEnumValN(DotScope::Alloc, "alloc", + "Export only nodes with contexts feeding given " + "-memprof-dot-alloc-id"), + clEnumValN(DotScope::Context, "context", + "Export only nodes with given -memprof-dot-context-id"))); + +static cl::opt + AllocIdForDot("memprof-dot-alloc-id", cl::init(0), cl::Hidden, + cl::desc("Id of alloc to export if -memprof-dot-scope=alloc " + "or to highlight if -memprof-dot-scope=all")); + +static cl::opt ContextIdForDot( + "memprof-dot-context-id", cl::init(0), cl::Hidden, + cl::desc("Id of context to export if -memprof-dot-scope=context or to " + "highlight otherwise")); + static cl::opt DumpCCG("memprof-dump-ccg", cl::init(false), cl::Hidden, cl::desc("Dump CallingContextGraph to stdout after each stage.")); @@ -548,6 +576,10 @@ protected: /// Map from callsite node to the enclosing caller function. std::map NodeToCallingFunc; + // When exporting to dot, and an allocation id is specified, contains the + // context ids on that allocation. + DenseSet DotAllocContextIds; + private: using EdgeIter = typename std::vector>::iterator; @@ -1324,6 +1356,8 @@ CallsiteContextGraph::duplicateContextIds( assert(ContextIdToAllocationType.count(OldId)); // The new context has the same allocation type as original. ContextIdToAllocationType[LastContextId] = ContextIdToAllocationType[OldId]; + if (DotAllocContextIds.contains(OldId)) + DotAllocContextIds.insert(LastContextId); } return NewContextIds; } @@ -2084,6 +2118,10 @@ ModuleCallsiteContextGraph::ModuleCallsiteContextGraph( AllocNode, StackContext, CallsiteContext, getMIBAllocType(MIBMD), ContextSizeInfo); } + // If exporting the graph to dot and an allocation id of interest was + // specified, record all the context ids for this allocation node. + if (ExportToDot && AllocNode->OrigStackOrAllocId == AllocIdForDot) + DotAllocContextIds = AllocNode->getContextIds(); assert(AllocNode->AllocTypes != (uint8_t)AllocationType::None); // Memprof and callsite metadata on memory allocations no longer // needed. @@ -2176,6 +2214,10 @@ IndexCallsiteContextGraph::IndexCallsiteContextGraph( ContextSizeInfo); I++; } + // If exporting the graph to dot and an allocation id of interest was + // specified, record all the context ids for this allocation node. + if (ExportToDot && AllocNode->OrigStackOrAllocId == AllocIdForDot) + DotAllocContextIds = AllocNode->getContextIds(); assert(AllocNode->AllocTypes != (uint8_t)AllocationType::None); // Initialize version 0 on the summary alloc node to the current alloc // type, unless it has both types in which case make it default, so @@ -3023,7 +3065,16 @@ struct GraphTraits *> { template struct DOTGraphTraits *> : public DefaultDOTGraphTraits { - DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) {} + DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) { + // If the user requested the full graph to be exported, but provided an + // allocation id, or if the user gave a context id and requested more than + // just a specific context to be exported, note that highlighting is + // enabled. + DoHighlight = + (AllocIdForDot.getNumOccurrences() && DotGraphScope == DotScope::All) || + (ContextIdForDot.getNumOccurrences() && + DotGraphScope != DotScope::Context); + } using GraphType = const CallsiteContextGraph *; using GTraits = GraphTraits; @@ -3051,12 +3102,29 @@ struct DOTGraphTraits *> return LabelString; } - static std::string getNodeAttributes(NodeRef Node, GraphType) { + static std::string getNodeAttributes(NodeRef Node, GraphType G) { + auto ContextIds = Node->getContextIds(); + // If highlighting enabled, see if this node contains any of the context ids + // of interest. If so, it will use a different color and a larger fontsize + // (which makes the node larger as well). + bool Highlight = false; + if (DoHighlight) { + assert(ContextIdForDot.getNumOccurrences() || + AllocIdForDot.getNumOccurrences()); + if (ContextIdForDot.getNumOccurrences()) + Highlight = ContextIds.contains(ContextIdForDot); + else + Highlight = set_intersects(ContextIds, G->DotAllocContextIds); + } std::string AttributeString = (Twine("tooltip=\"") + getNodeId(Node) + " " + - getContextIds(Node->getContextIds()) + "\"") + getContextIds(ContextIds) + "\"") .str(); + // Default fontsize is 14 + if (Highlight) + AttributeString += ",fontsize=\"30\""; AttributeString += - (Twine(",fillcolor=\"") + getColor(Node->AllocTypes) + "\"").str(); + (Twine(",fillcolor=\"") + getColor(Node->AllocTypes, Highlight) + "\"") + .str(); if (Node->CloneOf) { AttributeString += ",color=\"blue\""; AttributeString += ",style=\"filled,bold,dashed\""; @@ -3066,9 +3134,22 @@ struct DOTGraphTraits *> } static std::string getEdgeAttributes(NodeRef, ChildIteratorType ChildIter, - GraphType) { + GraphType G) { auto &Edge = *(ChildIter.getCurrent()); - auto Color = getColor(Edge->AllocTypes); + // If highlighting enabled, see if this edge contains any of the context ids + // of interest. If so, it will use a different color and a heavier arrow + // size and weight (the larger weight makes the highlighted path + // straighter). + bool Highlight = false; + if (DoHighlight) { + assert(ContextIdForDot.getNumOccurrences() || + AllocIdForDot.getNumOccurrences()); + if (ContextIdForDot.getNumOccurrences()) + Highlight = Edge->ContextIds.contains(ContextIdForDot); + else + Highlight = set_intersects(Edge->ContextIds, G->DotAllocContextIds); + } + auto Color = getColor(Edge->AllocTypes, Highlight); std::string AttributeString = (Twine("tooltip=\"") + getContextIds(Edge->ContextIds) + "\"" + // fillcolor is the arrow head and color is the line @@ -3077,13 +3158,24 @@ struct DOTGraphTraits *> .str(); if (Edge->IsBackedge) AttributeString += ",style=\"dotted\""; + // Default penwidth and weight are both 1. + if (Highlight) + AttributeString += ",penwidth=\"2.0\",weight=\"2\""; return AttributeString; } // Since the NodeOwners list includes nodes that are no longer connected to // the graph, skip them here. - static bool isNodeHidden(NodeRef Node, GraphType) { - return Node->isRemoved(); + static bool isNodeHidden(NodeRef Node, GraphType G) { + if (Node->isRemoved()) + return true; + // If a scope smaller than the full graph was requested, see if this node + // contains any of the context ids of interest. + if (DotGraphScope == DotScope::Alloc) + return !set_intersects(Node->getContextIds(), G->DotAllocContextIds); + if (DotGraphScope == DotScope::Context) + return !Node->getContextIds().contains(ContextIdForDot); + return false; } private: @@ -3100,16 +3192,20 @@ private: return IdString; } - static std::string getColor(uint8_t AllocTypes) { + static std::string getColor(uint8_t AllocTypes, bool Highlight) { + // If DoHighlight is not enabled, we want to use the highlight colors for + // NotCold and Cold, and the non-highlight color for NotCold+Cold. This is + // both compatible with the color scheme before highlighting was supported, + // and for the NotCold+Cold color the non-highlight color is a bit more + // readable. if (AllocTypes == (uint8_t)AllocationType::NotCold) // Color "brown1" actually looks like a lighter red. - return "brown1"; + return !DoHighlight || Highlight ? "brown1" : "lightpink"; if (AllocTypes == (uint8_t)AllocationType::Cold) - return "cyan"; + return !DoHighlight || Highlight ? "cyan" : "lightskyblue"; if (AllocTypes == ((uint8_t)AllocationType::NotCold | (uint8_t)AllocationType::Cold)) - // Lighter purple. - return "mediumorchid1"; + return Highlight ? "magenta" : "mediumorchid1"; return "gray"; } @@ -3119,8 +3215,17 @@ private: std::string Result = SStream.str(); return Result; } + + // True if we should highlight a specific context or allocation's contexts in + // the emitted graph. + static bool DoHighlight; }; +template +bool DOTGraphTraits< + const CallsiteContextGraph *>::DoHighlight = + false; + template void CallsiteContextGraph::exportToDot( std::string Label) const { @@ -5183,6 +5288,20 @@ bool MemProfContextDisambiguation::processModule( MemProfContextDisambiguation::MemProfContextDisambiguation( const ModuleSummaryIndex *Summary, bool isSamplePGO) : ImportSummary(Summary), isSamplePGO(isSamplePGO) { + // Check the dot graph printing options once here, to make sure we have valid + // and expected combinations. + if (DotGraphScope == DotScope::Alloc && !AllocIdForDot.getNumOccurrences()) + llvm::report_fatal_error( + "-memprof-dot-scope=alloc requires -memprof-dot-alloc-id"); + if (DotGraphScope == DotScope::Context && + !ContextIdForDot.getNumOccurrences()) + llvm::report_fatal_error( + "-memprof-dot-scope=context requires -memprof-dot-context-id"); + if (DotGraphScope == DotScope::All && AllocIdForDot.getNumOccurrences() && + ContextIdForDot.getNumOccurrences()) + llvm::report_fatal_error( + "-memprof-dot-scope=all can't have both -memprof-dot-alloc-id and " + "-memprof-dot-context-id"); if (ImportSummary) { // The MemProfImportSummary should only be used for testing ThinLTO // distributed backend handling via opt, in which case we don't have a diff --git a/llvm/test/Transforms/MemProfContextDisambiguation/dot.ll b/llvm/test/Transforms/MemProfContextDisambiguation/dot.ll new file mode 100644 index 000000000000..6ffe5038afdb --- /dev/null +++ b/llvm/test/Transforms/MemProfContextDisambiguation/dot.ll @@ -0,0 +1,265 @@ +;; Test callsite context graph exporting to dot with various options. +;; +;; The code is similar to that of basic.ll, but with a second allocation. + +;; Check expected error if alloc scope requested without an alloc id. +; RUN: not --crash opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ +; RUN: -memprof-export-to-dot -memprof-dot-file-path-prefix=%t. \ +; RUN: -memprof-dot-scope=alloc \ +; RUN: %s -S 2>&1 | FileCheck %s --check-prefix=ERRMISSINGALLOCID +; ERRMISSINGALLOCID: -memprof-dot-scope=alloc requires -memprof-dot-alloc-id + +;; Check expected error if context scope requested without a context id. +; RUN: not --crash opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ +; RUN: -memprof-export-to-dot -memprof-dot-file-path-prefix=%t. \ +; RUN: -memprof-dot-scope=context \ +; RUN: %s -S 2>&1 | FileCheck %s --check-prefix=ERRMISSINGCONTEXTID +; ERRMISSINGCONTEXTID: -memprof-dot-scope=context requires -memprof-dot-context-id + +;; Check expected error if both alloc and context ids are specified when we are +;; exporting the full graph to dot. It is unclear which scope to highlight. +; RUN: not --crash opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ +; RUN: -memprof-export-to-dot -memprof-dot-file-path-prefix=%t. \ +; RUN: -memprof-dot-alloc-id=0 -memprof-dot-context-id=2 \ +; RUN: %s -S 2>&1 | FileCheck %s --check-prefix=ERRBOTH +; ERRBOTH: -memprof-dot-scope=all can't have both -memprof-dot-alloc-id and -memprof-dot-context-id + +;; Export full graph (default), without any highlight. +; RUN: opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ +; RUN: -memprof-export-to-dot -memprof-dot-file-path-prefix=%t. \ +; RUN: %s -S 2>&1 | FileCheck %s --check-prefix=IR +; RUN: cat %t.ccg.postbuild.dot | FileCheck %s -check-prefix=DOTCOMMON --check-prefix=DOTALLANDALLOC0 --check-prefix=DOTALL --check-prefix=DOTALLNONE + +;; Export full graph (default), with highlight of alloc 0. +; RUN: opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ +; RUN: -memprof-export-to-dot -memprof-dot-file-path-prefix=%t. \ +; RUN: -memprof-dot-alloc-id=0 \ +; RUN: %s -S 2>&1 | FileCheck %s --check-prefix=IR +; RUN: cat %t.ccg.postbuild.dot | FileCheck %s --check-prefix=DOTCOMMON --check-prefix=DOTALLANDALLOC0 --check-prefix=DOTALL --check-prefix=DOTALLALLOC0 + +;; Export full graph (default), with highlight of context 1. +; RUN: opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ +; RUN: -memprof-export-to-dot -memprof-dot-file-path-prefix=%t. \ +; RUN: -memprof-dot-context-id=1 \ +; RUN: %s -S 2>&1 | FileCheck %s --check-prefix=IR +; RUN: cat %t.ccg.postbuild.dot | FileCheck %s --check-prefix=DOTCOMMON --check-prefix=DOTALLANDALLOC0 --check-prefix=DOTALL --check-prefix=DOTALLCONTEXT1 + +;; Export alloc 0 only, without any highlight. +; RUN: opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ +; RUN: -memprof-export-to-dot -memprof-dot-file-path-prefix=%t. \ +; RUN: -memprof-dot-scope=alloc -memprof-dot-alloc-id=0 \ +; RUN: %s -S 2>&1 | FileCheck %s --check-prefix=IR +; RUN: cat %t.ccg.postbuild.dot | FileCheck %s --check-prefix=DOTCOMMON --check-prefix=DOTALLANDALLOC0 --check-prefix=DOTALLOC0NONE + +;; Export alloc 0 only, with highlight of context 1. +; RUN: opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ +; RUN: -memprof-export-to-dot -memprof-dot-file-path-prefix=%t. \ +; RUN: -memprof-dot-scope=alloc -memprof-dot-alloc-id=0 \ +; RUN: -memprof-dot-context-id=1 \ +; RUN: %s -S 2>&1 | FileCheck %s --check-prefix=IR +; RUN: cat %t.ccg.postbuild.dot | FileCheck %s --check-prefix=DOTCOMMON --check-prefix=DOTALLANDALLOC0 --check-prefix=DOTALLOC0CONTEXT1 + +;; Export context 1 only (which means no highlighting). +; RUN: opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ +; RUN: -memprof-export-to-dot -memprof-dot-file-path-prefix=%t. \ +; RUN: -memprof-dot-scope=context -memprof-dot-context-id=1 \ +; RUN: %s -S 2>&1 | FileCheck %s --check-prefix=IR +; RUN: cat %t.ccg.postbuild.dot | FileCheck %s --check-prefix=DOTCOMMON --check-prefix=DOTCONTEXT1 + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i32 @main() #0 { +entry: + %call = call noundef ptr @_Z3foov(), !callsite !0 + %call1 = call noundef ptr @_Z3foov(), !callsite !1 + ret i32 0 +} + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) +declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #1 + +; Function Attrs: nobuiltin +declare void @_ZdaPv() #2 + +define internal ptr @_Z3barv() #3 { +entry: + %call = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #6, !memprof !2, !callsite !7 + %call2 = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #6, !memprof !13, !callsite !18 + ret ptr null +} + +declare ptr @_Znam(i64) + +define internal ptr @_Z3bazv() #4 { +entry: + %call = call noundef ptr @_Z3barv(), !callsite !8 + ret ptr null +} + +; Function Attrs: noinline +define internal ptr @_Z3foov() #5 { +entry: + %call = call noundef ptr @_Z3bazv(), !callsite !9 + ret ptr null +} + +; uselistorder directives +uselistorder ptr @_Z3foov, { 1, 0 } + +attributes #0 = { "tune-cpu"="generic" } +attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) } +attributes #2 = { nobuiltin } +attributes #3 = { "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" } +attributes #4 = { "stack-protector-buffer-size"="8" } +attributes #5 = { noinline } +attributes #6 = { builtin } + +!0 = !{i64 8632435727821051414} +!1 = !{i64 -3421689549917153178} +!2 = !{!3, !5} +!3 = !{!4, !"notcold", !10} +!4 = !{i64 9086428284934609951, i64 -5964873800580613432, i64 2732490490862098848, i64 8632435727821051414} +!5 = !{!6, !"cold", !11, !12} +!6 = !{i64 9086428284934609951, i64 -5964873800580613432, i64 2732490490862098848, i64 -3421689549917153178} +!7 = !{i64 9086428284934609951} +!8 = !{i64 -5964873800580613432} +!9 = !{i64 2732490490862098848} +!10 = !{i64 123, i64 100} +!11 = !{i64 456, i64 200} +!12 = !{i64 789, i64 300} +!13 = !{!14, !16} +!14 = !{!15, !"notcold", !10} +!15 = !{i64 123, i64 -5964873800580613432, i64 2732490490862098848, i64 8632435727821051414} +!16 = !{!17, !"cold", !11, !12} +!17 = !{i64 123, i64 -5964873800580613432, i64 2732490490862098848, i64 -3421689549917153178} +!18 = !{i64 123} + +; IR: define {{.*}} @main +;; The first call to foo does not allocate cold memory. It should call the +;; original functions, which ultimately call the original allocations decorated +;; with a "notcold" attribute. +; IR: call {{.*}} @_Z3foov() +;; The second call to foo allocates cold memory. It should call cloned functions +;; which ultimately call acloned allocations decorated with a "cold" attribute. +; IR: call {{.*}} @_Z3foov.memprof.1() +; IR: define internal {{.*}} @_Z3barv() +; IR: call {{.*}} @_Znam(i64 noundef 10) #[[NOTCOLD:[0-9]+]] +; IR: call {{.*}} @_Znam(i64 noundef 10) #[[NOTCOLD]] +; IR: define internal {{.*}} @_Z3bazv() +; IR: call {{.*}} @_Z3barv() +; IR: define internal {{.*}} @_Z3foov() +; IR: call {{.*}} @_Z3bazv() +; IR: define internal {{.*}} @_Z3barv.memprof.1() +; IR: call {{.*}} @_Znam(i64 noundef 10) #[[COLD:[0-9]+]] +; IR: call {{.*}} @_Znam(i64 noundef 10) #[[COLD]] +; IR: define internal {{.*}} @_Z3bazv.memprof.1() +; IR: call {{.*}} @_Z3barv.memprof.1() +; IR: define internal {{.*}} @_Z3foov.memprof.1() +; IR: call {{.*}} @_Z3bazv.memprof.1() +; IR: attributes #[[NOTCOLD]] = { builtin "memprof"="notcold" } +; IR: attributes #[[COLD]] = { builtin "memprof"="cold" } + + +;; Check highlighting. Alloc 0 includes context ids 1 and 2. + +; DOTCOMMON: Node[[BAR:0x[a-z0-9]+]] [shape=record,tooltip="N[[BAR]] ContextIds: 1 2", +;; This node is highlighted when dumping the whole graph and specifying +;; alloc 0 or context 1, or if dumping alloc 0 and specifying context 1. +; DOTALLNONE-SAME: fillcolor="mediumorchid1", +; DOTALLALLOC0-SAME: fontsize="30",fillcolor="magenta", +; DOTALLCONTEXT1-SAME: fontsize="30",fillcolor="magenta", +; DOTALLOC0NONE-SAME: fillcolor="mediumorchid1", +; DOTALLOC0CONTEXT1-SAME: fontsize="30",fillcolor="magenta", +; DOTCONTEXT1-SAME: fillcolor="mediumorchid1", +; DOTCOMMON-SAME: style="filled",label="{OrigId: Alloc0\n_Z3barv -\> _Znam}"]; + +; DOTCOMMON: Node[[BAZ:0x[a-z0-9]+]] [shape=record,tooltip="N[[BAZ]] ContextIds: 1 2 3 4", +;; This node is highlighted when dumping the whole graph and specifying +;; alloc 0 or context 1, or if dumping alloc 0 and specifying context 1. +; DOTALLNONE-SAME: fillcolor="mediumorchid1", +; DOTALLALLOC0-SAME: fontsize="30",fillcolor="magenta", +; DOTALLCONTEXT1-SAME: fontsize="30",fillcolor="magenta", +; DOTALLOC0NONE-SAME: fillcolor="mediumorchid1", +; DOTALLOC0CONTEXT1-SAME: fontsize="30",fillcolor="magenta", +; DOTCONTEXT1-SAME: fillcolor="mediumorchid1", +; DOTCOMMON-SAME: style="filled",label="{OrigId: 12481870273128938184\n_Z3bazv -\> _Z3barv}"]; + +; DOTCOMMON: Node[[BAZ]] -> Node[[BAR]][tooltip="ContextIds: 1 2", +;; This edge is highlighted when dumping the whole graph and specifying +;; alloc 0 or context 1, or if dumping alloc 0 and specifying context 1. +; DOTALLNONE-SAME: fillcolor="mediumorchid1",color="mediumorchid1"]; +; DOTALLALLOC0-SAME: fillcolor="magenta",color="magenta",penwidth="2.0",weight="2"]; +; DOTALLCONTEXT1-SAME: fillcolor="magenta",color="magenta",penwidth="2.0",weight="2"]; +; DOTALLOC0NONE-SAME: fillcolor="mediumorchid1",color="mediumorchid1"]; +; DOTALLOC0CONTEXT1-SAME: fillcolor="magenta",color="magenta",penwidth="2.0",weight="2"]; +; DOTCONTEXT1-SAME: fillcolor="mediumorchid1",color="mediumorchid1"]; + +;; This edge is not in alloc 0 or context 0, so only included when exporting +;; the whole graph (and never highlighted). +; DOTALL: Node[[BAZ]] -> Node[[BAR2:0x[a-z0-9]+]][tooltip="ContextIds: 3 4",fillcolor="mediumorchid1",color="mediumorchid1"]; + +; DOTCOMMON: Node[[FOO:0x[a-z0-9]+]] [shape=record,tooltip="N[[FOO]] ContextIds: 1 2 3 4", +;; This node is highlighted when dumping the whole graph and specifying +;; alloc 0 or context 1, or if dumping alloc 0 and specifying context 1. +; DOTALLNONE-SAME: fillcolor="mediumorchid1", +; DOTALLALLOC0-SAME: fontsize="30",fillcolor="magenta", +; DOTALLCONTEXT1-SAME: fontsize="30",fillcolor="magenta", +; DOTALLOC0NONE-SAME: fillcolor="mediumorchid1", +; DOTALLOC0CONTEXT1-SAME: fontsize="30",fillcolor="magenta", +; DOTCONTEXT1-SAME: fillcolor="mediumorchid1", +; DOTCOMMON-SAME: style="filled",label="{OrigId: 2732490490862098848\n_Z3foov -\> _Z3bazv}"]; + +; DOTCOMMON: Node[[FOO]] -> Node[[BAZ]][tooltip="ContextIds: 1 2 3 4", +;; This edge is highlighted when dumping the whole graph and specifying +;; alloc 0 or context 1, or if dumping alloc 0 and specifying context 1. +; DOTALLNONE-SAME: fillcolor="mediumorchid1",color="mediumorchid1"]; +; DOTALLALLOC0-SAME: fillcolor="magenta",color="magenta",penwidth="2.0",weight="2"]; +; DOTALLCONTEXT1-SAME: fillcolor="magenta",color="magenta",penwidth="2.0",weight="2"]; +; DOTALLOC0NONE-SAME: fillcolor="mediumorchid1",color="mediumorchid1"]; +; DOTALLOC0CONTEXT1-SAME: fillcolor="magenta",color="magenta",penwidth="2.0",weight="2"]; +; DOTCONTEXT1-SAME: fillcolor="mediumorchid1",color="mediumorchid1"]; + +; DOTCOMMON: Node[[MAIN1:0x[a-z0-9]+]] [shape=record,tooltip="N[[MAIN1]] ContextIds: 1 3", +;; This node is highlighted when dumping the whole graph and specifying +;; alloc 0 or context 1, or if dumping alloc 0 and specifying context 1. +;; Note that the highlight color is the same as when there is no highlighting. +; DOTALLALLOC0-SAME: fontsize="30", +; DOTALLCONTEXT1-SAME: fontsize="30", +; DOTALLOC0CONTEXT1-SAME: fontsize="30", +; DOTCOMMON-SAME: fillcolor="brown1",style="filled",label="{OrigId: 8632435727821051414\nmain -\> _Z3foov}"]; + +; DOTCOMMON: Node[[MAIN1]] -> Node[[FOO]][tooltip="ContextIds: 1 3",fillcolor="brown1",color="brown1" +;; This edge is highlighted when dumping the whole graph and specifying +;; alloc 0 or context 1, or if dumping alloc 0 and specifying context 1. +; DOTALLNONE-SAME: ]; +; DOTALLALLOC0-SAME: penwidth="2.0",weight="2"]; +; DOTALLCONTEXT1-SAME: penwidth="2.0",weight="2"]; +; DOTALLOC0NONE-SAME: ]; +; DOTALLOC0CONTEXT1-SAME: penwidth="2.0",weight="2"]; +; DOTCONTEXT1-SAME: ]; + +; DOTALLANDALLOC0: Node[[MAIN2:0x[a-z0-9]+]] [shape=record,tooltip="N[[MAIN2]] ContextIds: 2 4", +;; This node is highlighted when dumping the whole graph and specifying +;; alloc 0. Note that the unhighlighted color is different when there is any +;; highlighting (lightskyblue) vs no highlighting (cyan). +; DOTALLNONE-SAME: fillcolor="cyan", +; DOTALLALLOC0-SAME: fontsize="30",fillcolor="cyan", +; DOTALLCONTEXT1-SAME: fillcolor="lightskyblue", +; DOTALLOC0NONE-SAME: fillcolor="cyan", +; DOTALLOC0CONTEXT1-SAME: fillcolor="lightskyblue", +; DOTALLANDALLOC0-SAME: style="filled",label="{OrigId: 15025054523792398438\nmain -\> _Z3foov}"]; + +; DOTALLANDALLOC0: Node[[MAIN2]] -> Node[[FOO]][tooltip="ContextIds: 2 4", +;; This edge is highlighted when dumping the whole graph and specifying +;; alloc 0. Note that the unhighlighted color is different when there is any +;; highlighting (lightskyblue) vs no highlighting (cyan). +; DOTALLNONE-SAME: fillcolor="cyan",color="cyan"]; +; DOTALLALLOC0-SAME: fillcolor="cyan",color="cyan",penwidth="2.0",weight="2"]; +; DOTALLCONTEXT1-SAME: fillcolor="lightskyblue",color="lightskyblue"]; +; DOTALLOC0NONE-SAME: fillcolor="cyan",color="cyan"]; +; DOTALLOC0CONTEXT1-SAME: fillcolor="lightskyblue",color="lightskyblue"]; + +;; This edge is not in alloc 0 or context 0, so only included when exporting +;; the whole graph (and never highlighted). +; DOTALL: Node[[BAR2]] [shape=record,tooltip="N[[BAR2]] ContextIds: 3 4",fillcolor="mediumorchid1",style="filled",label="{OrigId: Alloc2\n_Z3barv -\> _Znam}"];