diff options
| author | Daniel Martí <mvdan@mvdan.cc> | 2018-02-12 16:34:48 +0000 |
|---|---|---|
| committer | Daniel Martí <mvdan@mvdan.cc> | 2018-02-15 16:25:43 +0000 |
| commit | e7cbbbe9bb878b6ca4ce04fde645df1c8f1845bd (patch) | |
| tree | f84255198234eb4870c48228cdb4828de1f4f8c5 /src/cmd/vendor/github.com/google/pprof/profile | |
| parent | afb9fc1de922a4ead9d2d787613255a7ba3490f7 (diff) | |
| download | go-e7cbbbe9bb878b6ca4ce04fde645df1c8f1845bd.tar.xz | |
cmd/vendor/github.com/google/pprof: refresh from upstream
Updating to commit 0e0e5b7254e076a62326ab7305ba49e8515f0c91
from github.com/google/pprof
Recent modifications to the vendored pprof, such as skipping
TestWebInterface to avoid starting a web browser, have all been fixed
upstream.
Change-Id: I72e11108c438e1573bf2f9216e76d157378e8d45
Reviewed-on: https://go-review.googlesource.com/93375
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/cmd/vendor/github.com/google/pprof/profile')
5 files changed, 199 insertions, 19 deletions
diff --git a/src/cmd/vendor/github.com/google/pprof/profile/encode.go b/src/cmd/vendor/github.com/google/pprof/profile/encode.go index 622319484a..1e84c72d43 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/encode.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/encode.go @@ -214,7 +214,12 @@ var profileDecoder = []decoder{ // int64 keep_frames = 8 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).keepFramesX) }, // int64 time_nanos = 9 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).TimeNanos) }, + func(b *buffer, m message) error { + if m.(*Profile).TimeNanos != 0 { + return errConcatProfile + } + return decodeInt64(b, &m.(*Profile).TimeNanos) + }, // int64 duration_nanos = 10 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).DurationNanos) }, // ValueType period_type = 11 @@ -480,6 +485,7 @@ func (p *Location) encode(b *buffer) { for i := range p.Line { encodeMessage(b, 4, &p.Line[i]) } + encodeBoolOpt(b, 5, p.IsFolded) } var locationDecoder = []decoder{ @@ -493,6 +499,7 @@ var locationDecoder = []decoder{ pp.Line = append(pp.Line, Line{}) return decodeMessage(b, &pp.Line[n]) }, + func(b *buffer, m message) error { return decodeBool(b, &m.(*Location).IsFolded) }, // optional bool is_folded = 5; } func (p *Line) decoder() []decoder { diff --git a/src/cmd/vendor/github.com/google/pprof/profile/merge.go b/src/cmd/vendor/github.com/google/pprof/profile/merge.go index e00829cc34..a5440b521e 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/merge.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/merge.go @@ -64,7 +64,7 @@ func Merge(srcs []*Profile) (*Profile, error) { // represents the main binary. Take the first Mapping we see, // otherwise the operations below will add mappings in an // arbitrary order. - pm.mapMapping(srcs[0].Mapping[0]) + pm.mapMapping(src.Mapping[0]) } for _, s := range src.Sample { @@ -234,10 +234,11 @@ func (pm *profileMerger) mapLocation(src *Location) *Location { mi := pm.mapMapping(src.Mapping) l := &Location{ - ID: uint64(len(pm.p.Location) + 1), - Mapping: mi.m, - Address: uint64(int64(src.Address) + mi.offset), - Line: make([]Line, len(src.Line)), + ID: uint64(len(pm.p.Location) + 1), + Mapping: mi.m, + Address: uint64(int64(src.Address) + mi.offset), + Line: make([]Line, len(src.Line)), + IsFolded: src.IsFolded, } for i, ln := range src.Line { l.Line[i] = pm.mapLine(ln) @@ -258,7 +259,8 @@ func (pm *profileMerger) mapLocation(src *Location) *Location { // key generates locationKey to be used as a key for maps. func (l *Location) key() locationKey { key := locationKey{ - addr: l.Address, + addr: l.Address, + isFolded: l.IsFolded, } if l.Mapping != nil { // Normalizes address to handle address space randomization. @@ -279,6 +281,7 @@ func (l *Location) key() locationKey { type locationKey struct { addr, mappingID uint64 lines string + isFolded bool } func (pm *profileMerger) mapMapping(src *Mapping) mapInfo { @@ -422,6 +425,7 @@ func combineHeaders(srcs []*Profile) (*Profile, error) { var timeNanos, durationNanos, period int64 var comments []string + seenComments := map[string]bool{} var defaultSampleType string for _, s := range srcs { if timeNanos == 0 || s.TimeNanos < timeNanos { @@ -431,7 +435,12 @@ func combineHeaders(srcs []*Profile) (*Profile, error) { if period == 0 || period < s.Period { period = s.Period } - comments = append(comments, s.Comments...) + for _, c := range s.Comments { + if seen := seenComments[c]; !seen { + comments = append(comments, c) + seenComments[c] = true + } + } if defaultSampleType == "" { defaultSampleType = s.DefaultSampleType } diff --git a/src/cmd/vendor/github.com/google/pprof/profile/profile.go b/src/cmd/vendor/github.com/google/pprof/profile/profile.go index a0f53efe3e..350538bf43 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/profile.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/profile.go @@ -109,10 +109,11 @@ type Mapping struct { // Location corresponds to Profile.Location type Location struct { - ID uint64 - Mapping *Mapping - Address uint64 - Line []Line + ID uint64 + Mapping *Mapping + Address uint64 + Line []Line + IsFolded bool mappingIDX uint64 } @@ -163,7 +164,7 @@ func ParseData(data []byte) (*Profile, error) { return nil, fmt.Errorf("decompressing profile: %v", err) } } - if p, err = ParseUncompressed(data); err != nil && err != errNoData { + if p, err = ParseUncompressed(data); err != nil && err != errNoData && err != errConcatProfile { p, err = parseLegacy(data) } @@ -180,6 +181,7 @@ func ParseData(data []byte) (*Profile, error) { var errUnrecognized = fmt.Errorf("unrecognized profile format") var errMalformed = fmt.Errorf("malformed profile format") var errNoData = fmt.Errorf("empty input file") +var errConcatProfile = fmt.Errorf("concatenated profiles detected") func parseLegacy(data []byte) (*Profile, error) { parsers := []func([]byte) (*Profile, error){ @@ -591,6 +593,9 @@ func (l *Location) string() string { if m := l.Mapping; m != nil { locStr = locStr + fmt.Sprintf("M=%d ", m.ID) } + if l.IsFolded { + locStr = locStr + "[F] " + } if len(l.Line) == 0 { ss = append(ss, locStr) } diff --git a/src/cmd/vendor/github.com/google/pprof/profile/profile_test.go b/src/cmd/vendor/github.com/google/pprof/profile/profile_test.go index bc2ab8bdd1..ab020275cb 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/profile_test.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/profile_test.go @@ -73,11 +73,11 @@ func TestParse(t *testing.T) { } // Reencode and decode. - bw := bytes.NewBuffer(nil) - if err := p.Write(bw); err != nil { + var bw bytes.Buffer + if err := p.Write(&bw); err != nil { t.Fatalf("%s: %v", source, err) } - if p, err = Parse(bw); err != nil { + if p, err = Parse(&bw); err != nil { t.Fatalf("%s: %v", source, err) } js2 := p.String() @@ -108,6 +108,21 @@ func TestParseError(t *testing.T) { } } +func TestParseConcatentated(t *testing.T) { + prof := testProfile1.Copy() + // Write the profile twice to buffer to create concatented profile. + var buf bytes.Buffer + prof.Write(&buf) + prof.Write(&buf) + _, err := Parse(&buf) + if err == nil { + t.Fatalf("got nil, want error") + } + if got, want := err.Error(), "parsing profile: concatenated profiles detected"; want != got { + t.Fatalf("got error %q, want error %q", got, want) + } +} + func TestCheckValid(t *testing.T) { const path = "testdata/java.cpu" @@ -276,6 +291,7 @@ var cpuL = []*Location{ } var testProfile1 = &Profile{ + TimeNanos: 10000, PeriodType: &ValueType{Type: "cpu", Unit: "milliseconds"}, Period: 1, DurationNanos: 10e9, @@ -330,6 +346,60 @@ var testProfile1 = &Profile{ Mapping: cpuM, } +var testProfile1NoMapping = &Profile{ + PeriodType: &ValueType{Type: "cpu", Unit: "milliseconds"}, + Period: 1, + DurationNanos: 10e9, + SampleType: []*ValueType{ + {Type: "samples", Unit: "count"}, + {Type: "cpu", Unit: "milliseconds"}, + }, + Sample: []*Sample{ + { + Location: []*Location{cpuL[0]}, + Value: []int64{1000, 1000}, + Label: map[string][]string{ + "key1": {"tag1"}, + "key2": {"tag1"}, + }, + }, + { + Location: []*Location{cpuL[1], cpuL[0]}, + Value: []int64{100, 100}, + Label: map[string][]string{ + "key1": {"tag2"}, + "key3": {"tag2"}, + }, + }, + { + Location: []*Location{cpuL[2], cpuL[0]}, + Value: []int64{10, 10}, + Label: map[string][]string{ + "key1": {"tag3"}, + "key2": {"tag2"}, + }, + }, + { + Location: []*Location{cpuL[3], cpuL[0]}, + Value: []int64{10000, 10000}, + Label: map[string][]string{ + "key1": {"tag4"}, + "key2": {"tag1"}, + }, + }, + { + Location: []*Location{cpuL[4], cpuL[0]}, + Value: []int64{1, 1}, + Label: map[string][]string{ + "key1": {"tag4"}, + "key2": {"tag1"}, + }, + }, + }, + Location: cpuL, + Function: cpuF, +} + var testProfile2 = &Profile{ PeriodType: &ValueType{Type: "cpu", Unit: "milliseconds"}, Period: 1, @@ -577,6 +647,7 @@ func TestMerge(t *testing.T) { // location should add up to 0). prof := testProfile1.Copy() + prof.Comments = []string{"comment1"} p1, err := Merge([]*Profile{prof, prof}) if err != nil { t.Errorf("merge error: %v", err) @@ -586,6 +657,9 @@ func TestMerge(t *testing.T) { if err != nil { t.Errorf("merge error: %v", err) } + if got, want := len(prof.Comments), 1; got != want { + t.Errorf("len(prof.Comments) = %d, want %d", got, want) + } // Use aggregation to merge locations at function granularity. if err := prof.Aggregate(false, true, false, false, false); err != nil { @@ -627,6 +701,39 @@ func TestMergeAll(t *testing.T) { } } +func TestIsFoldedMerge(t *testing.T) { + testProfile1Folded := testProfile1.Copy() + testProfile1Folded.Location[0].IsFolded = true + testProfile1Folded.Location[1].IsFolded = true + + for _, tc := range []struct { + name string + profs []*Profile + wantLocationLen int + }{ + { + name: "folded and non-folded locations not merged", + profs: []*Profile{testProfile1.Copy(), testProfile1Folded.Copy()}, + wantLocationLen: 7, + }, + { + name: "identical folded locations are merged", + profs: []*Profile{testProfile1Folded.Copy(), testProfile1Folded.Copy()}, + wantLocationLen: 5, + }, + } { + t.Run(tc.name, func(t *testing.T) { + prof, err := Merge(tc.profs) + if err != nil { + t.Fatalf("merge error: %v", err) + } + if got, want := len(prof.Location), tc.wantLocationLen; got != want { + t.Fatalf("got %d locations, want %d locations", got, want) + } + }) + } +} + func TestNumLabelMerge(t *testing.T) { for _, tc := range []struct { name string @@ -684,6 +791,40 @@ func TestNumLabelMerge(t *testing.T) { } } +func TestEmptyMappingMerge(t *testing.T) { + // Aggregate a profile with itself and once again with a factor of + // -2. Should end up with an empty profile (all samples for a + // location should add up to 0). + + prof1 := testProfile1.Copy() + prof2 := testProfile1NoMapping.Copy() + p1, err := Merge([]*Profile{prof2, prof1}) + if err != nil { + t.Errorf("merge error: %v", err) + } + prof2.Scale(-2) + prof, err := Merge([]*Profile{p1, prof2}) + if err != nil { + t.Errorf("merge error: %v", err) + } + + // Use aggregation to merge locations at function granularity. + if err := prof.Aggregate(false, true, false, false, false); err != nil { + t.Errorf("aggregating after merge: %v", err) + } + + samples := make(map[string]int64) + for _, s := range prof.Sample { + tb := locationHash(s) + samples[tb] = samples[tb] + s.Value[0] + } + for s, v := range samples { + if v != 0 { + t.Errorf("nonzero value for sample %s: %d", s, v) + } + } +} + func TestNormalizeBySameProfile(t *testing.T) { pb := testProfile1.Copy() p := testProfile1.Copy() diff --git a/src/cmd/vendor/github.com/google/pprof/profile/proto_test.go b/src/cmd/vendor/github.com/google/pprof/profile/proto_test.go index e0832294ac..38b58c586d 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/proto_test.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/proto_test.go @@ -52,6 +52,8 @@ var testF = []*Function{ {ID: 1, Name: "func1", SystemName: "func1", Filename: "file1"}, {ID: 2, Name: "func2", SystemName: "func2", Filename: "file1"}, {ID: 3, Name: "func3", SystemName: "func3", Filename: "file2"}, + {ID: 4, Name: "func4", SystemName: "func4", Filename: "file3"}, + {ID: 5, Name: "func5", SystemName: "func5", Filename: "file4"}, } var testL = []*Location{ @@ -86,6 +88,22 @@ var testL = []*Location{ Mapping: testM[1], Address: 12, }, + { + ID: 4, + Mapping: testM[1], + Address: 12, + Line: []Line{ + { + Function: testF[4], + Line: 6, + }, + { + Function: testF[4], + Line: 6, + }, + }, + IsFolded: true, + }, } var all = &Profile{ @@ -133,9 +151,9 @@ var all = &Profile{ func TestMarshalUnmarshal(t *testing.T) { // Write the profile, parse it, and ensure they're equal. - buf := bytes.NewBuffer(nil) - all.Write(buf) - all2, err := Parse(buf) + var buf bytes.Buffer + all.Write(&buf) + all2, err := Parse(&buf) if err != nil { t.Fatal(err) } |
