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:
Michael Pratt 2023-12-06 14:20:49 -05:00 committed by Gopher Robot
parent e069587c8d
commit 2f6a25f447
4 changed files with 24 additions and 1402 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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()

View File

@ -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)
}
}
}

View File

@ -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))