aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vendor/github.com/google/pprof/internal
diff options
context:
space:
mode:
authorHana Kim <hakim@google.com>2018-05-30 10:54:20 -0400
committerHyang-Ah Hana Kim <hyangah@gmail.com>2018-05-30 16:31:37 +0000
commitd5bc3b96c6fb758561e6274c8f69232623157ca4 (patch)
tree2d156042108d713fc5118d11f713c620d8eb1a95 /src/cmd/vendor/github.com/google/pprof/internal
parentfb0d6e4bd18da45fdb2b88640d368e919d3b6c7c (diff)
downloadgo-d5bc3b96c6fb758561e6274c8f69232623157ca4.tar.xz
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 <hyangah@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/cmd/vendor/github.com/google/pprof/internal')
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go49
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go27
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go9
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go3
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go240
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go8
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/report/report.go25
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/report/report_test.go118
8 files changed, 415 insertions, 64 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)
+ }
+ })
+ }
+}