From d5bc3b96c6fb758561e6274c8f69232623157ca4 Mon Sep 17 00:00:00 2001 From: Hana Kim Date: Wed, 30 May 2018 10:54:20 -0400 Subject: [PATCH] cmd/vendor/.../pprof: sync at rev 1ddc9e2 This includes changes in pprof to support - the new -diff_base flag - fix for a bug in handling of legacy Go heap profiles Update #25096 Change-Id: I826ac9244f31cc2c4415388c44a0cbe77303e460 Reviewed-on: https://go-review.googlesource.com/115295 Run-TryBot: Hyang-Ah Hana Kim TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- .../google/pprof/internal/driver/cli.go | 49 +++- .../google/pprof/internal/driver/driver.go | 27 +- .../pprof/internal/driver/driver_test.go | 9 +- .../google/pprof/internal/driver/fetch.go | 3 + .../pprof/internal/driver/fetch_test.go | 240 +++++++++++++--- .../pprof/internal/driver/interactive_test.go | 8 +- .../google/pprof/internal/report/report.go | 25 +- .../pprof/internal/report/report_test.go | 118 ++++++++ .../google/pprof/profile/legacy_profile.go | 1 + .../pprof/profile/legacy_profile_test.go | 2 + .../google/pprof/profile/profile.go | 30 ++ .../google/pprof/profile/profile_test.go | 267 +++++++++++++++++- src/cmd/vendor/vendor.json | 72 ++--- 13 files changed, 750 insertions(+), 101 deletions(-) diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go index c3c22e7c96..a5153e1511 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go @@ -15,6 +15,7 @@ package driver import ( + "errors" "fmt" "os" "strings" @@ -28,6 +29,7 @@ type source struct { ExecName string BuildID string Base []string + DiffBase bool Normalize bool Seconds int @@ -43,7 +45,8 @@ type source struct { func parseFlags(o *plugin.Options) (*source, []string, error) { flag := o.Flagset // Comparisons. - flagBase := flag.StringList("base", "", "Source for base profile for comparison") + flagBase := flag.StringList("base", "", "Source for base profile for profile subtraction") + flagDiffBase := flag.StringList("diff_base", "", "Source for diff base profile for comparison") // Source options. flagSymbolize := flag.String("symbolize", "", "Options for profile symbolization") flagBuildID := flag.String("buildid", "", "Override build id for first mapping") @@ -85,7 +88,7 @@ func parseFlags(o *plugin.Options) (*source, []string, error) { usageMsgVars) }) if len(args) == 0 { - return nil, nil, fmt.Errorf("no profile source specified") + return nil, nil, errors.New("no profile source specified") } var execName string @@ -112,7 +115,7 @@ func parseFlags(o *plugin.Options) (*source, []string, error) { return nil, nil, err } if cmd != nil && *flagHTTP != "" { - return nil, nil, fmt.Errorf("-http is not compatible with an output format on the command line") + return nil, nil, errors.New("-http is not compatible with an output format on the command line") } si := pprofVariables["sample_index"].value @@ -140,15 +143,13 @@ func parseFlags(o *plugin.Options) (*source, []string, error) { Comment: *flagAddComment, } - for _, s := range *flagBase { - if *s != "" { - source.Base = append(source.Base, *s) - } + if err := source.addBaseProfiles(*flagBase, *flagDiffBase); err != nil { + return nil, nil, err } normalize := pprofVariables["normalize"].boolValue() if normalize && len(source.Base) == 0 { - return nil, nil, fmt.Errorf("Must have base profile to normalize by") + return nil, nil, errors.New("must have base profile to normalize by") } source.Normalize = normalize @@ -158,6 +159,34 @@ func parseFlags(o *plugin.Options) (*source, []string, error) { return source, cmd, nil } +// addBaseProfiles adds the list of base profiles or diff base profiles to +// the source. This function will return an error if both base and diff base +// profiles are specified. +func (source *source) addBaseProfiles(flagBase, flagDiffBase []*string) error { + base, diffBase := dropEmpty(flagBase), dropEmpty(flagDiffBase) + if len(base) > 0 && len(diffBase) > 0 { + return errors.New("-base and -diff_base flags cannot both be specified") + } + + source.Base = base + if len(diffBase) > 0 { + source.Base, source.DiffBase = diffBase, true + } + return nil +} + +// dropEmpty list takes a slice of string pointers, and outputs a slice of +// non-empty strings associated with the flag. +func dropEmpty(list []*string) []string { + var l []string + for _, s := range list { + if *s != "" { + l = append(l, *s) + } + } + return l +} + // installFlags creates command line flags for pprof variables. func installFlags(flag plugin.FlagSet) flagsInstalled { f := flagsInstalled{ @@ -240,7 +269,7 @@ func outputFormat(bcmd map[string]*bool, acmd map[string]*string) (cmd []string, for n, b := range bcmd { if *b { if cmd != nil { - return nil, fmt.Errorf("must set at most one output format") + return nil, errors.New("must set at most one output format") } cmd = []string{n} } @@ -248,7 +277,7 @@ func outputFormat(bcmd map[string]*bool, acmd map[string]*string) (cmd []string, for n, s := range acmd { if *s != "" { if cmd != nil { - return nil, fmt.Errorf("must set at most one output format") + return nil, errors.New("must set at most one output format") } cmd = []string{n, *s} } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go index acc0b4ad8a..2dabc3017b 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go @@ -65,7 +65,13 @@ func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plug // Identify units of numeric tags in profile. numLabelUnits := identifyNumLabelUnits(p, o.UI) - vars = applyCommandOverrides(cmd, vars) + // Get report output format + c := pprofCommands[cmd[0]] + if c == nil { + panic("unexpected nil command") + } + + vars = applyCommandOverrides(cmd[0], c.format, vars) // Delay focus after configuring report to get percentages on all samples. relative := vars["relative_percentages"].boolValue() @@ -78,10 +84,6 @@ func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plug if err != nil { return nil, nil, err } - c := pprofCommands[cmd[0]] - if c == nil { - panic("unexpected nil command") - } ropt.OutputFormat = c.format if len(cmd) == 2 { s, err := regexp.Compile(cmd[1]) @@ -149,13 +151,10 @@ func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin. return out.Close() } -func applyCommandOverrides(cmd []string, v variables) variables { +func applyCommandOverrides(cmd string, outputFormat int, v variables) variables { trim, tagfilter, filter := v["trim"].boolValue(), true, true - switch cmd[0] { - case "proto", "raw": - trim, tagfilter, filter = false, false, false - v.set("addresses", "t") + switch cmd { case "callgrind", "kcachegrind": trim = false v.set("addresses", "t") @@ -163,7 +162,7 @@ func applyCommandOverrides(cmd []string, v variables) variables { trim = false v.set("addressnoinlines", "t") case "peek": - trim, filter = false, false + trim, tagfilter, filter = false, false, false case "list": v.set("nodecount", "0") v.set("lines", "t") @@ -176,6 +175,12 @@ func applyCommandOverrides(cmd []string, v variables) variables { v.set("nodecount", "80") } } + + if outputFormat == report.Proto || outputFormat == report.Raw { + trim, tagfilter, filter = false, false, false + v.set("addresses", "t") + } + if !trim { v.set("nodecount", "0") v.set("nodefraction", "0") diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go index 309e9950b6..ff6afe9cff 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go @@ -288,7 +288,7 @@ type testFlags struct { floats map[string]float64 strings map[string]string args []string - stringLists map[string][]*string + stringLists map[string][]string } func (testFlags) ExtraUsage() string { return "" } @@ -355,7 +355,12 @@ func (f testFlags) StringVar(p *string, s, d, c string) { func (f testFlags) StringList(s, d, c string) *[]*string { if t, ok := f.stringLists[s]; ok { - return &t + // convert slice of strings to slice of string pointers before returning. + tp := make([]*string, len(t)) + for i, v := range t { + tp[i] = &v + } + return &tp } return &[]*string{} } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go index 7c576de614..7a7a1a20f2 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go @@ -63,6 +63,9 @@ func fetchProfiles(s *source, o *plugin.Options) (*profile.Profile, error) { } if pbase != nil { + if s.DiffBase { + pbase.SetLabel("pprof::base", []string{"true"}) + } if s.Normalize { err := p.Normalize(pbase) if err != nil { diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go index afb135b7cd..e67b2e9f87 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go @@ -210,13 +210,20 @@ func TestFetchWithBase(t *testing.T) { baseVars := pprofVariables defer func() { pprofVariables = baseVars }() + type WantSample struct { + values []int64 + labels map[string][]string + } + const path = "testdata/" type testcase struct { - desc string - sources []string - bases []string - normalize bool - expectedSamples [][]int64 + desc string + sources []string + bases []string + diffBases []string + normalize bool + wantSamples []WantSample + wantErrorMsg string } testcases := []testcase{ @@ -224,58 +231,216 @@ func TestFetchWithBase(t *testing.T) { "not normalized base is same as source", []string{path + "cppbench.contention"}, []string{path + "cppbench.contention"}, + nil, + false, + nil, + "", + }, + { + "not normalized base is same as source", + []string{path + "cppbench.contention"}, + []string{path + "cppbench.contention"}, + nil, false, - [][]int64{}, + nil, + "", }, { "not normalized single source, multiple base (all profiles same)", []string{path + "cppbench.contention"}, []string{path + "cppbench.contention", path + "cppbench.contention"}, + nil, false, - [][]int64{{-2700, -608881724}, {-100, -23992}, {-200, -179943}, {-100, -17778444}, {-100, -75976}, {-300, -63568134}}, + []WantSample{ + { + values: []int64{-2700, -608881724}, + labels: map[string][]string{}, + }, + { + values: []int64{-100, -23992}, + labels: map[string][]string{}, + }, + { + values: []int64{-200, -179943}, + labels: map[string][]string{}, + }, + { + values: []int64{-100, -17778444}, + labels: map[string][]string{}, + }, + { + values: []int64{-100, -75976}, + labels: map[string][]string{}, + }, + { + values: []int64{-300, -63568134}, + labels: map[string][]string{}, + }, + }, + "", }, { "not normalized, different base and source", []string{path + "cppbench.contention"}, []string{path + "cppbench.small.contention"}, + nil, false, - [][]int64{{1700, 608878600}, {100, 23992}, {200, 179943}, {100, 17778444}, {100, 75976}, {300, 63568134}}, + []WantSample{ + { + values: []int64{1700, 608878600}, + labels: map[string][]string{}, + }, + { + values: []int64{100, 23992}, + labels: map[string][]string{}, + }, + { + values: []int64{200, 179943}, + labels: map[string][]string{}, + }, + { + values: []int64{100, 17778444}, + labels: map[string][]string{}, + }, + { + values: []int64{100, 75976}, + labels: map[string][]string{}, + }, + { + values: []int64{300, 63568134}, + labels: map[string][]string{}, + }, + }, + "", }, { "normalized base is same as source", []string{path + "cppbench.contention"}, []string{path + "cppbench.contention"}, + nil, true, - [][]int64{}, + nil, + "", }, { "normalized single source, multiple base (all profiles same)", []string{path + "cppbench.contention"}, []string{path + "cppbench.contention", path + "cppbench.contention"}, + nil, true, - [][]int64{}, + nil, + "", }, { "normalized different base and source", []string{path + "cppbench.contention"}, []string{path + "cppbench.small.contention"}, + nil, true, - [][]int64{{-229, -370}, {28, 0}, {57, 0}, {28, 80}, {28, 0}, {85, 287}}, + []WantSample{ + { + values: []int64{-229, -370}, + labels: map[string][]string{}, + }, + { + values: []int64{28, 0}, + labels: map[string][]string{}, + }, + { + values: []int64{57, 0}, + labels: map[string][]string{}, + }, + { + values: []int64{28, 80}, + labels: map[string][]string{}, + }, + { + values: []int64{28, 0}, + labels: map[string][]string{}, + }, + { + values: []int64{85, 287}, + labels: map[string][]string{}, + }, + }, + "", + }, + { + "not normalized diff base is same as source", + []string{path + "cppbench.contention"}, + nil, + []string{path + "cppbench.contention"}, + false, + []WantSample{ + { + values: []int64{2700, 608881724}, + labels: map[string][]string{}, + }, + { + values: []int64{100, 23992}, + labels: map[string][]string{}, + }, + { + values: []int64{200, 179943}, + labels: map[string][]string{}, + }, + { + values: []int64{100, 17778444}, + labels: map[string][]string{}, + }, + { + values: []int64{100, 75976}, + labels: map[string][]string{}, + }, + { + values: []int64{300, 63568134}, + labels: map[string][]string{}, + }, + { + values: []int64{-2700, -608881724}, + labels: map[string][]string{"pprof::base": {"true"}}, + }, + { + values: []int64{-100, -23992}, + labels: map[string][]string{"pprof::base": {"true"}}, + }, + { + values: []int64{-200, -179943}, + labels: map[string][]string{"pprof::base": {"true"}}, + }, + { + values: []int64{-100, -17778444}, + labels: map[string][]string{"pprof::base": {"true"}}, + }, + { + values: []int64{-100, -75976}, + labels: map[string][]string{"pprof::base": {"true"}}, + }, + { + values: []int64{-300, -63568134}, + labels: map[string][]string{"pprof::base": {"true"}}, + }, + }, + "", + }, + { + "diff_base and base both specified", + []string{path + "cppbench.contention"}, + []string{path + "cppbench.contention"}, + []string{path + "cppbench.contention"}, + false, + nil, + "-base and -diff_base flags cannot both be specified", }, } for _, tc := range testcases { t.Run(tc.desc, func(t *testing.T) { pprofVariables = baseVars.makeCopy() - - base := make([]*string, len(tc.bases)) - for i, s := range tc.bases { - base[i] = &s - } - f := testFlags{ - stringLists: map[string][]*string{ - "base": base, + stringLists: map[string][]string{ + "base": tc.bases, + "diff_base": tc.diffBases, }, bools: map[string]bool{ "normalize": tc.normalize, @@ -289,30 +454,37 @@ func TestFetchWithBase(t *testing.T) { }) src, _, err := parseFlags(o) + if tc.wantErrorMsg != "" { + if err == nil { + t.Fatalf("got nil, want error %q", tc.wantErrorMsg) + } + + if gotErrMsg := err.Error(); gotErrMsg != tc.wantErrorMsg { + t.Fatalf("got error %q, want error %q", gotErrMsg, tc.wantErrorMsg) + } + return + } + if err != nil { - t.Fatalf("%s: %v", tc.desc, err) + t.Fatalf("got error %q, want no error", err) } p, err := fetchProfiles(src, o) - pprofVariables = baseVars + if err != nil { - t.Fatal(err) + t.Fatalf("got error %q, want no error", err) } - if want, got := len(tc.expectedSamples), len(p.Sample); want != got { - t.Fatalf("want %d samples got %d", want, got) + if got, want := len(p.Sample), len(tc.wantSamples); got != want { + t.Fatalf("got %d samples want %d", got, want) } - if len(p.Sample) > 0 { - for i, sample := range p.Sample { - if want, got := len(tc.expectedSamples[i]), len(sample.Value); want != got { - t.Errorf("want %d values for sample %d, got %d", want, i, got) - } - for j, value := range sample.Value { - if want, got := tc.expectedSamples[i][j], value; want != got { - t.Errorf("want value of %d for value %d of sample %d, got %d", want, j, i, got) - } - } + for i, sample := range p.Sample { + if !reflect.DeepEqual(tc.wantSamples[i].values, sample.Value) { + t.Errorf("for sample %d got values %v, want %v", i, sample.Value, tc.wantSamples[i]) + } + if !reflect.DeepEqual(tc.wantSamples[i].labels, sample.Label) { + t.Errorf("for sample %d got labels %v, want %v", i, sample.Label, tc.wantSamples[i].labels) } } }) diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go index db26862c7d..8d775e16bd 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go @@ -294,7 +294,13 @@ func TestInteractiveCommands(t *testing.T) { t.Errorf("failed on %q: %v", tc.input, err) continue } - vars = applyCommandOverrides(cmd, vars) + + // Get report output format + c := pprofCommands[cmd[0]] + if c == nil { + t.Errorf("unexpected nil command") + } + vars = applyCommandOverrides(cmd[0], c.format, vars) for n, want := range tc.want { if got := vars[n].stringValue(); got != want { diff --git a/src/cmd/vendor/github.com/google/pprof/internal/report/report.go b/src/cmd/vendor/github.com/google/pprof/internal/report/report.go index 15cadfb548..76db9cbf99 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/report/report.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/report/report.go @@ -264,6 +264,10 @@ func (rpt *Report) newGraph(nodes graph.NodeSet) *graph.Graph { s.NumUnit = numUnits } + // Remove label marking samples from the base profiles, so it does not appear + // as a nodelet in the graph view. + prof.RemoveLabel("pprof::base") + formatTag := func(v int64, key string) string { return measurement.ScaledLabel(v, key, o.OutputUnit) } @@ -1212,10 +1216,11 @@ func NewDefault(prof *profile.Profile, options Options) *Report { return New(prof, o) } -// computeTotal computes the sum of all sample values. This will be -// used to compute percentages. +// computeTotal computes the sum of the absolute value of all sample values. +// If any samples have the label "pprof::base" with value "true", then the total +// will only include samples with that label. func computeTotal(prof *profile.Profile, value, meanDiv func(v []int64) int64) int64 { - var div, ret int64 + var div, total, diffDiv, diffTotal int64 for _, sample := range prof.Sample { var d, v int64 v = value(sample.Value) @@ -1225,13 +1230,21 @@ func computeTotal(prof *profile.Profile, value, meanDiv func(v []int64) int64) i if v < 0 { v = -v } - ret += v + total += v div += d + if sample.HasLabel("pprof::base", "true") { + diffTotal += v + diffDiv += d + } + } + if diffTotal > 0 { + total = diffTotal + div = diffDiv } if div != 0 { - return ret / div + return total / div } - return ret + return total } // Report contains the data and associated routines to extract a diff --git a/src/cmd/vendor/github.com/google/pprof/internal/report/report_test.go b/src/cmd/vendor/github.com/google/pprof/internal/report/report_test.go index 49c6e4934f..9eb435bbb8 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/report/report_test.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/report/report_test.go @@ -287,3 +287,121 @@ func TestLegendActiveFilters(t *testing.T) { } } } + +func TestComputeTotal(t *testing.T) { + p1 := testProfile.Copy() + p1.Sample = []*profile.Sample{ + { + Location: []*profile.Location{testL[0]}, + Value: []int64{1, 1}, + }, + { + Location: []*profile.Location{testL[2], testL[1], testL[0]}, + Value: []int64{1, 10}, + }, + { + Location: []*profile.Location{testL[4], testL[2], testL[0]}, + Value: []int64{1, 100}, + }, + } + + p2 := testProfile.Copy() + p2.Sample = []*profile.Sample{ + { + Location: []*profile.Location{testL[0]}, + Value: []int64{1, 1}, + }, + { + Location: []*profile.Location{testL[2], testL[1], testL[0]}, + Value: []int64{1, -10}, + }, + { + Location: []*profile.Location{testL[4], testL[2], testL[0]}, + Value: []int64{1, 100}, + }, + } + + p3 := testProfile.Copy() + p3.Sample = []*profile.Sample{ + { + Location: []*profile.Location{testL[0]}, + Value: []int64{10000, 1}, + }, + { + Location: []*profile.Location{testL[2], testL[1], testL[0]}, + Value: []int64{-10, 3}, + Label: map[string][]string{"pprof::base": {"true"}}, + }, + { + Location: []*profile.Location{testL[2], testL[1], testL[0]}, + Value: []int64{1000, -10}, + }, + { + Location: []*profile.Location{testL[2], testL[1], testL[0]}, + Value: []int64{-9000, 3}, + Label: map[string][]string{"pprof::base": {"true"}}, + }, + { + Location: []*profile.Location{testL[2], testL[1], testL[0]}, + Value: []int64{-1, 3}, + Label: map[string][]string{"pprof::base": {"true"}}, + }, + { + Location: []*profile.Location{testL[4], testL[2], testL[0]}, + Value: []int64{100, 100}, + }, + { + Location: []*profile.Location{testL[2], testL[1], testL[0]}, + Value: []int64{100, 3}, + Label: map[string][]string{"pprof::base": {"true"}}, + }, + } + + testcases := []struct { + desc string + prof *profile.Profile + value, meanDiv func(v []int64) int64 + wantTotal int64 + }{ + { + desc: "no diff base, all positive values, index 1", + prof: p1, + value: func(v []int64) int64 { + return v[0] + }, + wantTotal: 3, + }, + { + desc: "no diff base, all positive values, index 2", + prof: p1, + value: func(v []int64) int64 { + return v[1] + }, + wantTotal: 111, + }, + { + desc: "no diff base, some negative values", + prof: p2, + value: func(v []int64) int64 { + return v[1] + }, + wantTotal: 111, + }, + { + desc: "diff base, some negative values", + prof: p3, + value: func(v []int64) int64 { + return v[0] + }, + wantTotal: 9111, + }, + } + + for _, tc := range testcases { + t.Run(tc.desc, func(t *testing.T) { + if gotTotal := computeTotal(tc.prof, tc.value, tc.meanDiv); gotTotal != tc.wantTotal { + t.Errorf("got total %d, want %v", gotTotal, tc.wantTotal) + } + }) + } +} diff --git a/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile.go b/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile.go index 096890d9b4..0c8f3bb5b7 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile.go @@ -1103,6 +1103,7 @@ var heapzSampleTypes = [][]string{ {"objects", "space"}, {"inuse_objects", "inuse_space"}, {"alloc_objects", "alloc_space"}, + {"alloc_objects", "alloc_space", "inuse_objects", "inuse_space"}, // Go pprof legacy profiles } var contentionzSampleTypes = [][]string{ {"contentions", "delay"}, diff --git a/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile_test.go b/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile_test.go index 5f63453e09..6ba0e338c9 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile_test.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile_test.go @@ -39,10 +39,12 @@ func TestLegacyProfileType(t *testing.T) { {[]string{"objects", "space"}, heap, true, "heapzSampleTypes"}, {[]string{"inuse_objects", "inuse_space"}, heap, true, "heapzSampleTypes"}, {[]string{"alloc_objects", "alloc_space"}, heap, true, "heapzSampleTypes"}, + {[]string{"alloc_objects", "alloc_space", "inuse_objects", "inuse_space"}, heap, true, "heapzSampleTypes"}, {[]string{"contentions", "delay"}, cont, true, "contentionzSampleTypes"}, // False cases {[]string{"objects"}, heap, false, "heapzSampleTypes"}, {[]string{"objects", "unknown"}, heap, false, "heapzSampleTypes"}, + {[]string{"inuse_objects", "inuse_space", "alloc_objects", "alloc_space"}, heap, false, "heapzSampleTypes"}, {[]string{"contentions", "delay"}, heap, false, "heapzSampleTypes"}, {[]string{"samples", "cpu"}, heap, false, "heapzSampleTypes"}, {[]string{"samples", "cpu"}, cont, false, "contentionzSampleTypes"}, diff --git a/src/cmd/vendor/github.com/google/pprof/profile/profile.go b/src/cmd/vendor/github.com/google/pprof/profile/profile.go index 350538bf43..452194b127 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/profile.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/profile.go @@ -674,6 +674,36 @@ func numLabelsToString(numLabels map[string][]int64, numUnits map[string][]strin return strings.Join(ls, " ") } +// SetLabel sets the specified key to the specified value for all samples in the +// profile. +func (p *Profile) SetLabel(key string, value []string) { + for _, sample := range p.Sample { + if sample.Label == nil { + sample.Label = map[string][]string{key: value} + } else { + sample.Label[key] = value + } + } +} + +// RemoveLabel removes all labels associated with the specified key for all +// samples in the profile. +func (p *Profile) RemoveLabel(key string) { + for _, sample := range p.Sample { + delete(sample.Label, key) + } +} + +// HasLabel returns true if a sample has a label with indicated key and value. +func (s *Sample) HasLabel(key, value string) bool { + for _, v := range s.Label[key] { + if v == value { + return true + } + } + return false +} + // Scale multiplies all sample values in a profile by a constant. func (p *Profile) Scale(ratio float64) { if ratio == 1 { diff --git a/src/cmd/vendor/github.com/google/pprof/profile/profile_test.go b/src/cmd/vendor/github.com/google/pprof/profile/profile_test.go index 8ed67b1dd6..5b299b1d55 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/profile_test.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/profile_test.go @@ -741,7 +741,7 @@ func TestNumLabelMerge(t *testing.T) { wantNumUnits []map[string][]string }{ { - name: "different tag units not merged", + name: "different label units not merged", profs: []*Profile{testProfile4.Copy(), testProfile5.Copy()}, wantNumLabels: []map[string][]int64{ { @@ -912,6 +912,271 @@ func locationHash(s *Sample) string { return tb } +func TestHasLabel(t *testing.T) { + var testcases = []struct { + desc string + labels map[string][]string + key string + value string + wantHasLabel bool + }{ + { + desc: "empty label does not have label", + labels: map[string][]string{}, + key: "key", + value: "value", + wantHasLabel: false, + }, + { + desc: "label with one key and value has label", + labels: map[string][]string{"key": {"value"}}, + key: "key", + value: "value", + wantHasLabel: true, + }, + { + desc: "label with one key and value does not have label", + labels: map[string][]string{"key": {"value"}}, + key: "key1", + value: "value1", + wantHasLabel: false, + }, + { + desc: "label with many keys and values has label", + labels: map[string][]string{ + "key1": {"value2", "value1"}, + "key2": {"value1", "value2", "value2"}, + "key3": {"value1", "value2", "value2"}, + }, + key: "key1", + value: "value1", + wantHasLabel: true, + }, + { + desc: "label with many keys and values does not have label", + labels: map[string][]string{ + "key1": {"value2", "value1"}, + "key2": {"value1", "value2", "value2"}, + "key3": {"value1", "value2", "value2"}, + }, + key: "key5", + value: "value5", + wantHasLabel: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.desc, func(t *testing.T) { + sample := &Sample{ + Label: tc.labels, + } + if gotHasLabel := sample.HasLabel(tc.key, tc.value); gotHasLabel != tc.wantHasLabel { + t.Errorf("sample.HasLabel(%q, %q) got %v, want %v", tc.key, tc.value, gotHasLabel, tc.wantHasLabel) + } + }) + } +} + +func TestRemove(t *testing.T) { + var testcases = []struct { + desc string + samples []*Sample + removeKey string + wantLabels []map[string][]string + }{ + { + desc: "some samples have label already", + samples: []*Sample{ + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + }, + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + Label: map[string][]string{ + "key1": {"value1", "value2", "value3"}, + "key2": {"value1"}, + }, + }, + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + Label: map[string][]string{ + "key1": {"value2"}, + }, + }, + }, + removeKey: "key1", + wantLabels: []map[string][]string{ + {}, + {"key2": {"value1"}}, + {}, + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.desc, func(t *testing.T) { + profile := testProfile1.Copy() + profile.Sample = tc.samples + profile.RemoveLabel(tc.removeKey) + if got, want := len(profile.Sample), len(tc.wantLabels); got != want { + t.Fatalf("got %v samples, want %v samples", got, want) + } + for i, sample := range profile.Sample { + wantLabels := tc.wantLabels[i] + if got, want := len(sample.Label), len(wantLabels); got != want { + t.Errorf("got %v label keys for sample %v, want %v", got, i, want) + continue + } + for wantKey, wantValues := range wantLabels { + if gotValues, ok := sample.Label[wantKey]; ok { + if !reflect.DeepEqual(gotValues, wantValues) { + t.Errorf("for key %s, got values %v, want values %v", wantKey, gotValues, wantValues) + } + } else { + t.Errorf("for key %s got no values, want %v", wantKey, wantValues) + } + } + } + }) + } +} + +func TestSetLabel(t *testing.T) { + var testcases = []struct { + desc string + samples []*Sample + setKey string + setVal []string + wantLabels []map[string][]string + }{ + { + desc: "some samples have label already", + samples: []*Sample{ + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + }, + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + Label: map[string][]string{ + "key1": {"value1", "value2", "value3"}, + "key2": {"value1"}, + }, + }, + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + Label: map[string][]string{ + "key1": {"value2"}, + }, + }, + }, + setKey: "key1", + setVal: []string{"value1"}, + wantLabels: []map[string][]string{ + {"key1": {"value1"}}, + {"key1": {"value1"}, "key2": {"value1"}}, + {"key1": {"value1"}}, + }, + }, + { + desc: "no samples have labels", + samples: []*Sample{ + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + }, + }, + setKey: "key1", + setVal: []string{"value1"}, + wantLabels: []map[string][]string{ + {"key1": {"value1"}}, + }, + }, + { + desc: "all samples have some labels, but not key being added", + samples: []*Sample{ + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + Label: map[string][]string{ + "key2": {"value2"}, + }, + }, + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + Label: map[string][]string{ + "key3": {"value3"}, + }, + }, + }, + setKey: "key1", + setVal: []string{"value1"}, + wantLabels: []map[string][]string{ + {"key1": {"value1"}, "key2": {"value2"}}, + {"key1": {"value1"}, "key3": {"value3"}}, + }, + }, + { + desc: "all samples have key being added", + samples: []*Sample{ + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + Label: map[string][]string{ + "key1": {"value1"}, + }, + }, + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000}, + Label: map[string][]string{ + "key1": {"value1"}, + }, + }, + }, + setKey: "key1", + setVal: []string{"value1"}, + wantLabels: []map[string][]string{ + {"key1": {"value1"}}, + {"key1": {"value1"}}, + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.desc, func(t *testing.T) { + profile := testProfile1.Copy() + profile.Sample = tc.samples + profile.SetLabel(tc.setKey, tc.setVal) + if got, want := len(profile.Sample), len(tc.wantLabels); got != want { + t.Fatalf("got %v samples, want %v samples", got, want) + } + for i, sample := range profile.Sample { + wantLabels := tc.wantLabels[i] + if got, want := len(sample.Label), len(wantLabels); got != want { + t.Errorf("got %v label keys for sample %v, want %v", got, i, want) + continue + } + for wantKey, wantValues := range wantLabels { + if gotValues, ok := sample.Label[wantKey]; ok { + if !reflect.DeepEqual(gotValues, wantValues) { + t.Errorf("for key %s, got values %v, want values %v", wantKey, gotValues, wantValues) + } + } else { + t.Errorf("for key %s got no values, want %v", wantKey, wantValues) + } + } + } + }) + } +} + func TestNumLabelUnits(t *testing.T) { var tagFilterTests = []struct { desc string diff --git a/src/cmd/vendor/vendor.json b/src/cmd/vendor/vendor.json index 89506e7fe1..26b32692b2 100644 --- a/src/cmd/vendor/vendor.json +++ b/src/cmd/vendor/vendor.json @@ -5,98 +5,98 @@ { "checksumSHA1": "G9UsR+iruMWxwUefhy+ID+VIFNs=", "path": "github.com/google/pprof/driver", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "LzGfApA19baVJIbQEqziWpRS3zE=", "path": "github.com/google/pprof/internal/binutils", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { - "checksumSHA1": "f7aprpcWR7iZX1PJgKBZrpt++XY=", + "checksumSHA1": "uoKLYk9VTOx2kYV3hU3vOGm4BX8=", "path": "github.com/google/pprof/internal/driver", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "IhuyU2pFSHhQxzadDBw1nHbcsrY=", "path": "github.com/google/pprof/internal/elfexec", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "8vah+aXLGpbtn55JR8MkCAEOMrk=", "path": "github.com/google/pprof/internal/graph", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "QPWfnT5pEU2jOOb8l8hpiFzQJ7Q=", "path": "github.com/google/pprof/internal/measurement", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "PWZdFtGfGz/zbQTfvel9737NZdY=", "path": "github.com/google/pprof/internal/plugin", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "LmDglu/S6vFmgqkxubKDZemFHaY=", "path": "github.com/google/pprof/internal/proftest", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { - "checksumSHA1": "gdyWnzbjgwmqJ2EN/WAp+QPu7d0=", + "checksumSHA1": "qgsLCrPLve6es8A3bA3qv2LPoYk=", "path": "github.com/google/pprof/internal/report", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "rWdirHgJi1+TdRwv5v3zjgFKcJA=", "path": "github.com/google/pprof/internal/symbolizer", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "5lS2AF207MVYyjF+82qHkWK2V64=", "path": "github.com/google/pprof/internal/symbolz", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { - "checksumSHA1": "RvwtpZ+NVtPRCo4EiFvLFFHpoBo=", + "checksumSHA1": "JMf63Fn5hz7JFgz6A2aT9DP/bL0=", "path": "github.com/google/pprof/profile", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "xmqfYca88U2c/I4642r3ps9uIRg=", "path": "github.com/google/pprof/third_party/d3", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "LzWzD56Trzpq+0hLR00Yw5Gpepw=", "path": "github.com/google/pprof/third_party/d3flamegraph", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { "checksumSHA1": "738v1E0v0qRW6oAKdCpBEtyVNnY=", "path": "github.com/google/pprof/third_party/svgpan", - "revision": "4d67f66d7c9469639518a80f378434bb7e9156b7", - "revisionTime": "2018-05-09T15:07:09Z" + "revision": "1ddc9e21322e23449cb6709652bf3583969ca167", + "revisionTime": "2018-05-30T14:24:47Z" }, { - "checksumSHA1": "UDJQBwUTuQYEHHJ/D7nPBv1qNqI=", + "checksumSHA1": "J5yI4NzHbondzccJmummyJR/kQQ=", "path": "github.com/ianlancetaylor/demangle", - "revision": "4883227f66371e02c4948937d3e2be1664d9be38", - "revisionTime": "2016-09-27T19:13:59Z" + "revision": "fc6590592b44fedfff586c5d94647c090fbd6bac", + "revisionTime": "2018-05-24T22:59:00Z" }, { "path": "golang.org/x/arch/arm/armasm", -- 2.50.0