Commit d5bc3b96 authored by Hana Kim's avatar Hana Kim Committed by Hyang-Ah Hana Kim

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: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent fb0d6e4b
......@@ -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}
}
......
......@@ -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")
......
......@@ -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{}
}
......
......@@ -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 {
......
......@@ -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)
}
}
})
......
......@@ -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 {
......
......@@ -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
......
......@@ -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)
}
})
}
}
......@@ -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"},
......
......@@ -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"},
......
......@@ -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 {
......
......@@ -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
......
......@@ -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",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment