aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vendor/github.com/google/pprof/internal/graph
diff options
context:
space:
mode:
authorAlberto Donizetti <alb.donizetti@gmail.com>2017-08-20 12:27:32 +0200
committerBrad Fitzpatrick <bradfitz@golang.org>2017-11-02 23:51:45 +0000
commitaec345d638fa624f08b7d758e9e173897edc80e8 (patch)
treed782d951af4f34de34a08c4775a37f869af25b81 /src/cmd/vendor/github.com/google/pprof/internal/graph
parent3039bff9d07ce05dc9af8c155c6929ae5e53a231 (diff)
downloadgo-aec345d638fa624f08b7d758e9e173897edc80e8.tar.xz
cmd/vendor/github.com/google/pprof: refresh from upstream
Update vendored pprof to commit 4fc39a00b6b8c1aad05260f01429ec70e127252c from github.com/google/pprof (2017-11-01). Fixes #19380 Updates #21047 Change-Id: Ib64a94a45209039e5945acbcfa0392790c8ee41e Reviewed-on: https://go-review.googlesource.com/57370 Run-TryBot: Alberto Donizetti <alb.donizetti@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/cmd/vendor/github.com/google/pprof/internal/graph')
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go59
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph_test.go123
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/graph/graph.go41
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/graph/graph_test.go4
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose1.dot6
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose2.dot6
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose3.dot10
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose4.dot2
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose5.dot10
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose6.dot7
10 files changed, 168 insertions, 100 deletions
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go b/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go
index c99e8992de..4e5d12f6cd 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go
@@ -42,15 +42,17 @@ type DotNodeAttributes struct {
// DotConfig contains attributes about how a graph should be
// constructed and how it should look.
type DotConfig struct {
- Title string // The title of the DOT graph
- Labels []string // The labels for the DOT's legend
+ Title string // The title of the DOT graph
+ LegendURL string // The URL to link to from the legend.
+ Labels []string // The labels for the DOT's legend
- FormatValue func(int64) string // A formatting function for values
- FormatTag func(int64, string) string // A formatting function for numeric tags
- Total int64 // The total weight of the graph, used to compute percentages
+ FormatValue func(int64) string // A formatting function for values
+ Total int64 // The total weight of the graph, used to compute percentages
}
-// Compose creates and writes a in the DOT format to the writer, using
+const maxNodelets = 4 // Number of nodelets for labels (both numeric and non)
+
+// ComposeDot creates and writes a in the DOT format to the writer, using
// the configurations given.
func ComposeDot(w io.Writer, g *Graph, a *DotAttributes, c *DotConfig) {
builder := &builder{w, a, c}
@@ -120,11 +122,19 @@ func (b *builder) finish() {
// addLegend generates a legend in DOT format.
func (b *builder) addLegend() {
labels := b.config.Labels
- var title string
- if len(labels) > 0 {
- title = labels[0]
+ if len(labels) == 0 {
+ return
+ }
+ title := labels[0]
+ fmt.Fprintf(b, `subgraph cluster_L { "%s" [shape=box fontsize=16`, title)
+ fmt.Fprintf(b, ` label="%s\l"`, strings.Join(labels, `\l`))
+ if b.config.LegendURL != "" {
+ fmt.Fprintf(b, ` URL="%s" target="_blank"`, b.config.LegendURL)
+ }
+ if b.config.Title != "" {
+ fmt.Fprintf(b, ` tooltip="%s"`, b.config.Title)
}
- fmt.Fprintf(b, `subgraph cluster_L { "%s" [shape=box fontsize=16 label="%s\l"] }`+"\n", title, strings.Join(labels, `\l`))
+ fmt.Fprintf(b, "] }\n")
}
// addNode generates a graph node in DOT format.
@@ -176,8 +186,8 @@ func (b *builder) addNode(node *Node, nodeID int, maxFlat float64) {
}
// Create DOT attribute for node.
- attr := fmt.Sprintf(`label="%s" fontsize=%d shape=%s tooltip="%s (%s)" color="%s" fillcolor="%s"`,
- label, fontSize, shape, node.Info.PrintableName(), cumValue,
+ attr := fmt.Sprintf(`label="%s" id="node%d" fontsize=%d shape=%s tooltip="%s (%s)" color="%s" fillcolor="%s"`,
+ label, nodeID, fontSize, shape, node.Info.PrintableName(), cumValue,
dotColor(float64(node.CumValue())/float64(abs64(b.config.Total)), false),
dotColor(float64(node.CumValue())/float64(abs64(b.config.Total)), true))
@@ -204,13 +214,11 @@ func (b *builder) addNode(node *Node, nodeID int, maxFlat float64) {
// addNodelets generates the DOT boxes for the node tags if they exist.
func (b *builder) addNodelets(node *Node, nodeID int) bool {
- const maxNodelets = 4 // Number of nodelets for alphanumeric labels
- const maxNumNodelets = 4 // Number of nodelets for numeric labels
var nodelets string
// Populate two Tag slices, one for LabelTags and one for NumericTags.
var ts []*Tag
- lnts := make(map[string][]*Tag, 0)
+ lnts := make(map[string][]*Tag)
for _, t := range node.LabelTags {
ts = append(ts, t)
}
@@ -239,15 +247,15 @@ func (b *builder) addNodelets(node *Node, nodeID int) bool {
continue
}
weight := b.config.FormatValue(w)
- nodelets += fmt.Sprintf(`N%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", nodeID, i, t.Name, weight)
+ nodelets += fmt.Sprintf(`N%d_%d [label = "%s" id="N%d_%d" fontsize=8 shape=box3d tooltip="%s"]`+"\n", nodeID, i, t.Name, nodeID, i, weight)
nodelets += fmt.Sprintf(`N%d -> N%d_%d [label=" %s" weight=100 tooltip="%s" labeltooltip="%s"]`+"\n", nodeID, nodeID, i, weight, weight, weight)
if nts := lnts[t.Name]; nts != nil {
- nodelets += b.numericNodelets(nts, maxNumNodelets, flatTags, fmt.Sprintf(`N%d_%d`, nodeID, i))
+ nodelets += b.numericNodelets(nts, maxNodelets, flatTags, fmt.Sprintf(`N%d_%d`, nodeID, i))
}
}
if nts := lnts[""]; nts != nil {
- nodelets += b.numericNodelets(nts, maxNumNodelets, flatTags, fmt.Sprintf(`N%d`, nodeID))
+ nodelets += b.numericNodelets(nts, maxNodelets, flatTags, fmt.Sprintf(`N%d`, nodeID))
}
fmt.Fprint(b, nodelets)
@@ -266,7 +274,7 @@ func (b *builder) numericNodelets(nts []*Tag, maxNumNodelets int, flatTags bool,
}
if w != 0 {
weight := b.config.FormatValue(w)
- nodelets += fmt.Sprintf(`N%s_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", source, j, t.Name, weight)
+ nodelets += fmt.Sprintf(`N%s_%d [label = "%s" id="N%s_%d" fontsize=8 shape=box3d tooltip="%s"]`+"\n", source, j, t.Name, source, j, weight)
nodelets += fmt.Sprintf(`%s -> N%s_%d [label=" %s" weight=100 tooltip="%s" labeltooltip="%s"%s]`+"\n", source, source, j, weight, weight, weight, attr)
}
}
@@ -441,14 +449,9 @@ func tagDistance(t, u *Tag) float64 {
}
func (b *builder) tagGroupLabel(g []*Tag) (label string, flat, cum int64) {
- formatTag := b.config.FormatTag
- if formatTag == nil {
- formatTag = measurement.Label
- }
-
if len(g) == 1 {
t := g[0]
- return formatTag(t.Value, t.Unit), t.FlatValue(), t.CumValue()
+ return measurement.Label(t.Value, t.Unit), t.FlatValue(), t.CumValue()
}
min := g[0]
max := g[0]
@@ -472,7 +475,11 @@ func (b *builder) tagGroupLabel(g []*Tag) (label string, flat, cum int64) {
if dc != 0 {
c = c / dc
}
- return formatTag(min.Value, min.Unit) + ".." + formatTag(max.Value, max.Unit), f, c
+
+ // Tags are not scaled with the selected output unit because tags are often
+ // much smaller than other values which appear, so the range of tag sizes
+ // sometimes would appear to be "0..0" when scaled to the selected output unit.
+ return measurement.Label(min.Value, min.Unit) + ".." + measurement.Label(max.Value, max.Unit), f, c
}
func min64(a, b int64) int64 {
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph_test.go b/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph_test.go
index 7f51269769..b8368b8fa4 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph_test.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph_test.go
@@ -16,8 +16,10 @@ package graph
import (
"bytes"
+ "flag"
"fmt"
"io/ioutil"
+ "path/filepath"
"reflect"
"strconv"
"strings"
@@ -26,7 +28,7 @@ import (
"github.com/google/pprof/internal/proftest"
)
-const path = "testdata/"
+var updateFlag = flag.Bool("update", false, "Update the golden files")
func TestComposeWithStandardGraph(t *testing.T) {
g := baseGraph()
@@ -35,12 +37,7 @@ func TestComposeWithStandardGraph(t *testing.T) {
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
- want, err := ioutil.ReadFile(path + "compose1.dot")
- if err != nil {
- t.Fatalf("error reading test file: %v", err)
- }
-
- compareGraphs(t, buf.Bytes(), want)
+ compareGraphs(t, buf.Bytes(), "compose1.dot")
}
func TestComposeWithNodeAttributesAndZeroFlat(t *testing.T) {
@@ -64,12 +61,7 @@ func TestComposeWithNodeAttributesAndZeroFlat(t *testing.T) {
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
- want, err := ioutil.ReadFile(path + "compose2.dot")
- if err != nil {
- t.Fatalf("error reading test file: %v", err)
- }
-
- compareGraphs(t, buf.Bytes(), want)
+ compareGraphs(t, buf.Bytes(), "compose2.dot")
}
func TestComposeWithTagsAndResidualEdge(t *testing.T) {
@@ -97,12 +89,7 @@ func TestComposeWithTagsAndResidualEdge(t *testing.T) {
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
- want, err := ioutil.ReadFile(path + "compose3.dot")
- if err != nil {
- t.Fatalf("error reading test file: %v", err)
- }
-
- compareGraphs(t, buf.Bytes(), want)
+ compareGraphs(t, buf.Bytes(), "compose3.dot")
}
func TestComposeWithNestedTags(t *testing.T) {
@@ -127,12 +114,7 @@ func TestComposeWithNestedTags(t *testing.T) {
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
- want, err := ioutil.ReadFile(path + "compose5.dot")
- if err != nil {
- t.Fatalf("error reading test file: %v", err)
- }
-
- compareGraphs(t, buf.Bytes(), want)
+ compareGraphs(t, buf.Bytes(), "compose5.dot")
}
func TestComposeWithEmptyGraph(t *testing.T) {
@@ -142,12 +124,18 @@ func TestComposeWithEmptyGraph(t *testing.T) {
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
- want, err := ioutil.ReadFile(path + "compose4.dot")
- if err != nil {
- t.Fatalf("error reading test file: %v", err)
- }
+ compareGraphs(t, buf.Bytes(), "compose4.dot")
+}
+
+func TestComposeWithStandardGraphAndURL(t *testing.T) {
+ g := baseGraph()
+ a, c := baseAttrsAndConfig()
+ c.LegendURL = "http://example.com"
+
+ var buf bytes.Buffer
+ ComposeDot(&buf, g, a, c)
- compareGraphs(t, buf.Bytes(), want)
+ compareGraphs(t, buf.Bytes(), "compose6.dot")
}
func baseGraph() *Graph {
@@ -199,13 +187,78 @@ func baseAttrsAndConfig() (*DotAttributes, *DotConfig) {
return a, c
}
-func compareGraphs(t *testing.T, got, want []byte) {
+func compareGraphs(t *testing.T, got []byte, wantFile string) {
+ wantFile = filepath.Join("testdata", wantFile)
+ want, err := ioutil.ReadFile(wantFile)
+ if err != nil {
+ t.Fatalf("error reading test file %s: %v", wantFile, err)
+ }
+
if string(got) != string(want) {
d, err := proftest.Diff(got, want)
if err != nil {
t.Fatalf("error finding diff: %v", err)
}
t.Errorf("Compose incorrectly wrote %s", string(d))
+ if *updateFlag {
+ err := ioutil.WriteFile(wantFile, got, 0644)
+ if err != nil {
+ t.Errorf("failed to update the golden file %q: %v", wantFile, err)
+ }
+ }
+ }
+}
+
+func TestNodeletCountCapping(t *testing.T) {
+ labelTags := make(TagMap)
+ for i := 0; i < 10; i++ {
+ name := fmt.Sprintf("tag-%d", i)
+ labelTags[name] = &Tag{
+ Name: name,
+ Flat: 10,
+ Cum: 10,
+ }
+ }
+ numTags := make(TagMap)
+ for i := 0; i < 10; i++ {
+ name := fmt.Sprintf("num-tag-%d", i)
+ numTags[name] = &Tag{
+ Name: name,
+ Unit: "mb",
+ Value: 16,
+ Flat: 10,
+ Cum: 10,
+ }
+ }
+ node1 := &Node{
+ Info: NodeInfo{Name: "node1-with-tags"},
+ Flat: 10,
+ Cum: 10,
+ NumericTags: map[string]TagMap{"": numTags},
+ LabelTags: labelTags,
+ }
+ node2 := &Node{
+ Info: NodeInfo{Name: "node2"},
+ Flat: 15,
+ Cum: 15,
+ }
+ node3 := &Node{
+ Info: NodeInfo{Name: "node3"},
+ Flat: 15,
+ Cum: 15,
+ }
+ g := &Graph{
+ Nodes: Nodes{
+ node1,
+ node2,
+ node3,
+ },
+ }
+ for n := 1; n <= 3; n++ {
+ input := maxNodelets + n
+ if got, want := len(g.SelectTopNodes(input, true)), n; got != want {
+ t.Errorf("SelectTopNodes(%d): got %d nodes, want %d", input, got, want)
+ }
}
}
@@ -240,19 +293,19 @@ func TestTagCollapse(t *testing.T) {
}
tagWant := [][]*Tag{
- []*Tag{
+ {
makeTag("1B..2GB", "", 0, 2401, 2401),
},
- []*Tag{
+ {
makeTag("2GB", "", 0, 1000, 1000),
makeTag("1B..12MB", "", 0, 1401, 1401),
},
- []*Tag{
+ {
makeTag("2GB", "", 0, 1000, 1000),
makeTag("12MB", "", 0, 100, 100),
makeTag("1B..1MB", "", 0, 1301, 1301),
},
- []*Tag{
+ {
makeTag("2GB", "", 0, 1000, 1000),
makeTag("1MB", "", 0, 1000, 1000),
makeTag("2B..1kB", "", 0, 201, 201),
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/graph.go b/src/cmd/vendor/github.com/google/pprof/internal/graph/graph.go
index 428e6257c7..cd72bf2ab1 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/graph/graph.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/graph.go
@@ -240,6 +240,8 @@ type Edge struct {
Inline bool
}
+// WeightValue returns the weight value for this edge, normalizing if a
+// divisor is available.
func (e *Edge) WeightValue() int64 {
if e.WeightDiv == 0 {
return e.Weight
@@ -327,7 +329,7 @@ func newGraph(prof *profile.Profile, o *Options) (*Graph, map[uint64]Nodes) {
// Add cum weight to all nodes in stack, avoiding double counting.
if _, ok := seenNode[n]; !ok {
seenNode[n] = true
- n.addSample(dw, w, labels, sample.NumLabel, o.FormatTag, false)
+ n.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, false)
}
// Update edge weights for all edges in stack, avoiding double counting.
if _, ok := seenEdge[nodePair{n, parent}]; !ok && parent != nil && n != parent {
@@ -340,7 +342,7 @@ func newGraph(prof *profile.Profile, o *Options) (*Graph, map[uint64]Nodes) {
}
if parent != nil && !residual {
// Add flat weight to leaf node.
- parent.addSample(dw, w, labels, sample.NumLabel, o.FormatTag, true)
+ parent.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, true)
}
}
@@ -399,7 +401,7 @@ func newTree(prof *profile.Profile, o *Options) (g *Graph) {
if n == nil {
continue
}
- n.addSample(dw, w, labels, sample.NumLabel, o.FormatTag, false)
+ n.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, false)
if parent != nil {
parent.AddToEdgeDiv(n, dw, w, false, lidx != len(lines)-1)
}
@@ -407,7 +409,7 @@ func newTree(prof *profile.Profile, o *Options) (g *Graph) {
}
}
if parent != nil {
- parent.addSample(dw, w, labels, sample.NumLabel, o.FormatTag, true)
+ parent.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, true)
}
}
@@ -600,7 +602,7 @@ func (ns Nodes) Sum() (flat int64, cum int64) {
return
}
-func (n *Node) addSample(dw, w int64, labels string, numLabel map[string][]int64, format func(int64, string) string, flat bool) {
+func (n *Node) addSample(dw, w int64, labels string, numLabel map[string][]int64, numUnit map[string][]string, format func(int64, string) string, flat bool) {
// Update sample value
if flat {
n.FlatDiv += dw
@@ -631,9 +633,15 @@ func (n *Node) addSample(dw, w int64, labels string, numLabel map[string][]int64
if format == nil {
format = defaultLabelFormat
}
- for key, nvals := range numLabel {
- for _, v := range nvals {
- t := numericTags.findOrAddTag(format(v, key), key, v)
+ for k, nvals := range numLabel {
+ units := numUnit[k]
+ for i, v := range nvals {
+ var t *Tag
+ if len(units) > 0 {
+ t = numericTags.findOrAddTag(format(v, units[i]), units[i], v)
+ } else {
+ t = numericTags.findOrAddTag(format(v, k), k, v)
+ }
if flat {
t.FlatDiv += dw
t.Flat += w
@@ -800,7 +808,11 @@ func (g *Graph) selectTopNodes(maxNodes int, visualMode bool) Nodes {
// If generating a visual graph, count tags as nodes. Update
// maxNodes to account for them.
for i, n := range g.Nodes {
- if count += countTags(n) + 1; count >= maxNodes {
+ tags := countTags(n)
+ if tags > maxNodelets {
+ tags = maxNodelets
+ }
+ if count += tags + 1; count >= maxNodes {
maxNodes = i + 1
break
}
@@ -832,17 +844,6 @@ func countTags(n *Node) int {
return count
}
-// countEdges counts the number of edges below the specified cutoff.
-func countEdges(el EdgeMap, cutoff int64) int {
- count := 0
- for _, e := range el {
- if e.Weight > cutoff {
- count++
- }
- }
- return count
-}
-
// RemoveRedundantEdges removes residual edges if the destination can
// be reached through another path. This is done to simplify the graph
// while preserving connectivity.
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/graph_test.go b/src/cmd/vendor/github.com/google/pprof/internal/graph/graph_test.go
index c2848f8cf2..5657084cac 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/graph/graph_test.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/graph_test.go
@@ -171,7 +171,7 @@ func createExpectedEdges(parent expectedNode, children ...expectedNode) {
}
}
-// createTestCase1 creates a test case that initally looks like:
+// createTestCase1 creates a test case that initially looks like:
// 0
// |(5)
// 1
@@ -255,7 +255,7 @@ func createTestCase2() trimTreeTestcase {
}
}
-// createTestCase3 creates an initally empty graph and expects an empty graph
+// createTestCase3 creates an initially empty graph and expects an empty graph
// after trimming.
func createTestCase3() trimTreeTestcase {
graph := &Graph{make(Nodes, 0)}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose1.dot b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose1.dot
index ceed025318..da349a40a8 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose1.dot
+++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose1.dot
@@ -1,7 +1,7 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
-subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l"] }
-N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
-N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
+subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" tooltip="testtitle"] }
+N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
+N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" id="node2" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src -> dest (10)" labeltooltip="src -> dest (10)"]
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose2.dot b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose2.dot
index ee951fe3b1..0c1a6ebaf1 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose2.dot
+++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose2.dot
@@ -1,7 +1,7 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
-subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l"] }
-N1 [label="SRC10 (10.00%)\nof 25 (25.00%)" fontsize=24 shape=folder tooltip="src (25)" color="#b23c00" fillcolor="#edddd5" style="bold,filled" peripheries=2 URL="www.google.com" target="_blank"]
-N2 [label="dest\n0 of 25 (25.00%)" fontsize=8 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
+subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" tooltip="testtitle"] }
+N1 [label="SRC10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=24 shape=folder tooltip="src (25)" color="#b23c00" fillcolor="#edddd5" style="bold,filled" peripheries=2 URL="www.google.com" target="_blank"]
+N2 [label="dest\n0 of 25 (25.00%)" id="node2" fontsize=8 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src -> dest (10)" labeltooltip="src -> dest (10)"]
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose3.dot b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose3.dot
index 99a3119b82..1b878b79df 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose3.dot
+++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose3.dot
@@ -1,11 +1,11 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
-subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l"] }
-N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
-N1_0 [label = "tag1" fontsize=8 shape=box3d tooltip="10"]
+subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" tooltip="testtitle"] }
+N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
+N1_0 [label = "tag1" id="N1_0" fontsize=8 shape=box3d tooltip="10"]
N1 -> N1_0 [label=" 10" weight=100 tooltip="10" labeltooltip="10"]
-NN1_0 [label = "tag2" fontsize=8 shape=box3d tooltip="20"]
+NN1_0 [label = "tag2" id="NN1_0" fontsize=8 shape=box3d tooltip="20"]
N1 -> NN1_0 [label=" 20" weight=100 tooltip="20" labeltooltip="20"]
-N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
+N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" id="node2" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src ... dest (10)" labeltooltip="src ... dest (10)" style="dotted" minlen=2]
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose4.dot b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose4.dot
index adc9cc6f68..302da8ce94 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose4.dot
+++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose4.dot
@@ -1,4 +1,4 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
-subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l"] }
+subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" tooltip="testtitle"] }
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose5.dot b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose5.dot
index 352975f587..8876e337e6 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose5.dot
+++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose5.dot
@@ -1,11 +1,11 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
-subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l"] }
-N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
-N1_0 [label = "tag1" fontsize=8 shape=box3d tooltip="10"]
+subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" tooltip="testtitle"] }
+N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
+N1_0 [label = "tag1" id="N1_0" fontsize=8 shape=box3d tooltip="10"]
N1 -> N1_0 [label=" 10" weight=100 tooltip="10" labeltooltip="10"]
-NN1_0_0 [label = "tag2" fontsize=8 shape=box3d tooltip="20"]
+NN1_0_0 [label = "tag2" id="NN1_0_0" fontsize=8 shape=box3d tooltip="20"]
N1_0 -> NN1_0_0 [label=" 20" weight=100 tooltip="20" labeltooltip="20"]
-N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
+N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" id="node2" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src -> dest (10)" labeltooltip="src -> dest (10)" minlen=2]
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose6.dot b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose6.dot
new file mode 100644
index 0000000000..cf884394c7
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/testdata/compose6.dot
@@ -0,0 +1,7 @@
+digraph "testtitle" {
+node [style=filled fillcolor="#f8f8f8"]
+subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" URL="http://example.com" target="_blank" tooltip="testtitle"] }
+N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
+N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" id="node2" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
+N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src -> dest (10)" labeltooltip="src -> dest (10)"]
+}