]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/vendor/.../pprof: sync at rev 1ddc9e2
authorHana Kim <hakim@google.com>
Wed, 30 May 2018 14:54:20 +0000 (10:54 -0400)
committerHyang-Ah Hana Kim <hyangah@gmail.com>
Wed, 30 May 2018 16:31:37 +0000 (16:31 +0000)
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 <hyangah@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
13 files changed:
src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go
src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go
src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go
src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go
src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go
src/cmd/vendor/github.com/google/pprof/internal/report/report.go
src/cmd/vendor/github.com/google/pprof/internal/report/report_test.go
src/cmd/vendor/github.com/google/pprof/profile/legacy_profile.go
src/cmd/vendor/github.com/google/pprof/profile/legacy_profile_test.go
src/cmd/vendor/github.com/google/pprof/profile/profile.go
src/cmd/vendor/github.com/google/pprof/profile/profile_test.go
src/cmd/vendor/vendor.json

index c3c22e7c96b4336f4ff21da9f96b38476fda9646..a5153e151132b3058f53602500c072c82cfd99a0 100644 (file)
@@ -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}
                }
index acc0b4ad8ac993f7da99736abfe27751f36c9030..2dabc3017b57e463b12e4cce18bf3139a349203c 100644 (file)
@@ -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")
index 309e9950b667a2cd7bf4e1b2c4198391462b4faf..ff6afe9cff7e52784b350ffde68fd6f33da480e4 100644 (file)
@@ -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{}
 }
index 7c576de61498b57af9ea54e1e1bb741cb1ce35e0..7a7a1a20f2a2eb1a763cad3b4da77bd1a9be1a68 100644 (file)
@@ -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 {
index afb135b7cdda13d028812e65b785e5d1ae67d954..e67b2e9f87108bea8083d460bd0e537e7cdd91de 100644 (file)
@@ -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)
                                }
                        }
                })
index db26862c7d1cc8c4d5ef5d055c525097d1642b93..8d775e16bdbc0448d5d47eb7ab0ee612d7ddaee3 100644 (file)
@@ -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 {
index 15cadfb5480693eadbfc5366203bb82060808b42..76db9cbf991e516f06af3773425cf758427bce16 100644 (file)
@@ -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
index 49c6e4934f5c20f64733ac18ca256a514b72bf46..9eb435bbb816dfa4382bd12dabdebc77528ecd5e 100644 (file)
@@ -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)
+                       }
+               })
+       }
+}
index 096890d9b48783366120d85f2e2b3f1753cd13b4..0c8f3bb5b71f45f6c9edc402c4a9fada3690b9f8 100644 (file)
@@ -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"},
index 5f63453e09d0f2a477ec915df84f58732c0b7f73..6ba0e338c9bd48676a8040a4b5ce8c660faf3bc8 100644 (file)
@@ -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"},
index 350538bf432b8365939e6f4568be7e9b2d8a2fec..452194b12788a29b30a09c26e86175c9e6b238d6 100644 (file)
@@ -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 {
index 8ed67b1dd6c0ec8445fa4b5252a41c4e1be4bf32..5b299b1d55263abe44aab45b2e954bc3c97e810e 100644 (file)
@@ -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
index 89506e7fe1724a1c85ebbacbf98ef4bb5de25037..26b32692b28b16ccc69cee6fa4576b2f1aa80e23 100644 (file)
@@ -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",