mirror of
https://github.com/golang/go.git
synced 2024-09-21 18:38:37 +00:00
internal/profile: remove legacy profile support
internal/profile.Parse is only used in two places: cmd/compile for parsing PGO profiles, and net/http/pprof for parsing runtime/pprof profiles for delta profiles. Neither case ever encounters legacy profiles, so we can remove support entirely from the package. Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Change-Id: Ic5f85b3fc1e1367131b6039dac9378913cbf9f2c Reviewed-on: https://go-review.googlesource.com/c/go/+/548035 Auto-Submit: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
parent
e069587c8d
commit
2f6a25f447
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,6 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"internal/lazyregexp"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
@ -120,16 +119,14 @@ type Function struct {
|
||||
filenameX int64
|
||||
}
|
||||
|
||||
// Parse parses a profile and checks for its validity. The input
|
||||
// may be a gzip-compressed encoded protobuf or one of many legacy
|
||||
// profile formats which may be unsupported in the future.
|
||||
// Parse parses a profile and checks for its validity. The input must be an
|
||||
// encoded pprof protobuf, which may optionally be gzip-compressed.
|
||||
func Parse(r io.Reader) (*Profile, error) {
|
||||
orig, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var p *Profile
|
||||
if len(orig) >= 2 && orig[0] == 0x1f && orig[1] == 0x8b {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(orig))
|
||||
if err != nil {
|
||||
@ -142,13 +139,9 @@ func Parse(r io.Reader) (*Profile, error) {
|
||||
orig = data
|
||||
}
|
||||
|
||||
var lErr error
|
||||
p, pErr := parseUncompressed(orig)
|
||||
if pErr != nil {
|
||||
p, lErr = parseLegacy(orig)
|
||||
}
|
||||
if pErr != nil && lErr != nil {
|
||||
return nil, fmt.Errorf("parsing profile: not a valid proto profile (%w) or legacy profile (%w)", pErr, lErr)
|
||||
p, err := parseUncompressed(orig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing profile: %w", err)
|
||||
}
|
||||
|
||||
if err := p.CheckValid(); err != nil {
|
||||
@ -161,29 +154,6 @@ var errUnrecognized = fmt.Errorf("unrecognized profile format")
|
||||
var errMalformed = fmt.Errorf("malformed profile format")
|
||||
var ErrNoData = fmt.Errorf("empty input file")
|
||||
|
||||
func parseLegacy(data []byte) (*Profile, error) {
|
||||
parsers := []func([]byte) (*Profile, error){
|
||||
parseCPU,
|
||||
parseHeap,
|
||||
parseGoCount, // goroutine, threadcreate
|
||||
parseThread,
|
||||
parseContention,
|
||||
}
|
||||
|
||||
for _, parser := range parsers {
|
||||
p, err := parser(data)
|
||||
if err == nil {
|
||||
p.setMain()
|
||||
p.addLegacyFrameInfo()
|
||||
return p, nil
|
||||
}
|
||||
if err != errUnrecognized {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, errUnrecognized
|
||||
}
|
||||
|
||||
func parseUncompressed(data []byte) (*Profile, error) {
|
||||
if len(data) == 0 {
|
||||
return nil, ErrNoData
|
||||
@ -201,29 +171,6 @@ func parseUncompressed(data []byte) (*Profile, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
var libRx = lazyregexp.New(`([.]so$|[.]so[._][0-9]+)`)
|
||||
|
||||
// setMain scans Mapping entries and guesses which entry is main
|
||||
// because legacy profiles don't obey the convention of putting main
|
||||
// first.
|
||||
func (p *Profile) setMain() {
|
||||
for i := 0; i < len(p.Mapping); i++ {
|
||||
file := strings.TrimSpace(strings.ReplaceAll(p.Mapping[i].File, "(deleted)", ""))
|
||||
if len(file) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(libRx.FindStringSubmatch(file)) > 0 {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(file, "[") {
|
||||
continue
|
||||
}
|
||||
// Swap what we guess is main to position 0.
|
||||
p.Mapping[i], p.Mapping[0] = p.Mapping[0], p.Mapping[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes the profile as a gzip-compressed marshaled protobuf.
|
||||
func (p *Profile) Write(w io.Writer) error {
|
||||
p.preEncode()
|
||||
|
@ -1,64 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package profile
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseContention(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
in: `--- mutex:
|
||||
cycles/second=3491920901
|
||||
sampling period=1
|
||||
43227965305 1659640 @ 0x45e851 0x45f764 0x4a2be1 0x44ea31
|
||||
34035731690 15760 @ 0x45e851 0x45f764 0x4a2b17 0x44ea31
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "valid with comment",
|
||||
in: `--- mutex:
|
||||
cycles/second=3491920901
|
||||
sampling period=1
|
||||
43227965305 1659640 @ 0x45e851 0x45f764 0x4a2be1 0x44ea31
|
||||
# 0x45e850 sync.(*Mutex).Unlock+0x80 /go/src/sync/mutex.go:126
|
||||
# 0x45f763 sync.(*RWMutex).Unlock+0x83 /go/src/sync/rwmutex.go:125
|
||||
# 0x4a2be0 main.main.func3+0x70 /go/src/internal/pprof/profile/a_binary.go:58
|
||||
|
||||
34035731690 15760 @ 0x45e851 0x45f764 0x4a2b17 0x44ea31
|
||||
# 0x45e850 sync.(*Mutex).Unlock+0x80 /go/src/sync/mutex.go:126
|
||||
# 0x45f763 sync.(*RWMutex).Unlock+0x83 /go/src/sync/rwmutex.go:125
|
||||
# 0x4a2b16 main.main.func2+0xd6 /go/src/internal/pprof/profile/a_binary.go:48
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
in: `--- mutex:`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid header",
|
||||
in: `--- channel:
|
||||
43227965305 1659640 @ 0x45e851 0x45f764 0x4a2be1 0x44ea31`,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
_, err := parseContention([]byte(tc.in))
|
||||
if tc.wantErr && err == nil {
|
||||
t.Errorf("parseContention(%q) succeeded unexpectedly", tc.name)
|
||||
}
|
||||
if !tc.wantErr && err != nil {
|
||||
t.Errorf("parseContention(%q) failed unexpectedly: %v", tc.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -86,21 +86,28 @@ func testPCs(t *testing.T) (addr1, addr2 uint64, map1, map2 *profile.Mapping) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mprof := &profile.Profile{}
|
||||
if err = mprof.ParseMemoryMap(bytes.NewReader(mmap)); err != nil {
|
||||
t.Fatalf("parsing /proc/self/maps: %v", err)
|
||||
}
|
||||
if len(mprof.Mapping) < 2 {
|
||||
var mappings []*profile.Mapping
|
||||
id := uint64(1)
|
||||
parseProcSelfMaps(mmap, func(lo, hi, offset uint64, file, buildID string) {
|
||||
mappings = append(mappings, &profile.Mapping{
|
||||
ID: id,
|
||||
Start: lo,
|
||||
Limit: hi,
|
||||
Offset: offset,
|
||||
File: file,
|
||||
BuildID: buildID,
|
||||
})
|
||||
id++
|
||||
})
|
||||
if len(mappings) < 2 {
|
||||
// It is possible for a binary to only have 1 executable
|
||||
// region of memory.
|
||||
t.Skipf("need 2 or more mappings, got %v", len(mprof.Mapping))
|
||||
t.Skipf("need 2 or more mappings, got %v", len(mappings))
|
||||
}
|
||||
addr1 = mprof.Mapping[0].Start
|
||||
map1 = mprof.Mapping[0]
|
||||
map1.BuildID, _ = elfBuildID(map1.File)
|
||||
addr2 = mprof.Mapping[1].Start
|
||||
map2 = mprof.Mapping[1]
|
||||
map2.BuildID, _ = elfBuildID(map2.File)
|
||||
addr1 = mappings[0].Start
|
||||
map1 = mappings[0]
|
||||
addr2 = mappings[1].Start
|
||||
map2 = mappings[1]
|
||||
case "windows", "darwin", "ios":
|
||||
addr1 = uint64(abi.FuncPCABIInternal(f1))
|
||||
addr2 = uint64(abi.FuncPCABIInternal(f2))
|
||||
|
Loading…
Reference in New Issue
Block a user