Commit fcd2a06a authored by Raul Silvera's avatar Raul Silvera Committed by David Symonds

cmd/pprof/internal: use and accept packed encoding for repeated fields

Packed encoding is the default on the proto3 format. Profiles generated
in the profile.proto format by third parties cannot be decoded by the
Go pprof tool, since its proto decoder does not recognize packed
encoding for repeated fields.

In particular this issue prevents go tool pprof from reading profiles
generated by the version of pprof in github.com/google/pprof

Profiles generated by go tool pprof after this change will use packed
repeating fields, so older versions of pprof will not be able to read
them. pprof will continue to be able to read profiles generated before
this change.

Change-Id: Ife0b353a535ae1e495515b9bcec588dd967e171b
Reviewed-on: https://go-review.googlesource.com/21240Reviewed-by: default avatarDavid Symonds <dsymonds@golang.org>
Run-TryBot: David Symonds <dsymonds@golang.org>
parent 621aa713
...@@ -64,6 +64,20 @@ func encodeUint64(b *buffer, tag int, x uint64) { ...@@ -64,6 +64,20 @@ func encodeUint64(b *buffer, tag int, x uint64) {
} }
func encodeUint64s(b *buffer, tag int, x []uint64) { func encodeUint64s(b *buffer, tag int, x []uint64) {
if len(x) > 2 {
// Use packed encoding
n1 := len(b.data)
for _, u := range x {
encodeVarint(b, u)
}
n2 := len(b.data)
encodeLength(b, tag, n2-n1)
n3 := len(b.data)
copy(b.tmp[:], b.data[n2:n3])
copy(b.data[n1+(n3-n2):], b.data[n1:n2])
copy(b.data[n1:], b.tmp[:n3-n2])
return
}
for _, u := range x { for _, u := range x {
encodeUint64(b, tag, u) encodeUint64(b, tag, u)
} }
...@@ -88,6 +102,26 @@ func encodeInt64Opt(b *buffer, tag int, x int64) { ...@@ -88,6 +102,26 @@ func encodeInt64Opt(b *buffer, tag int, x int64) {
encodeInt64(b, tag, x) encodeInt64(b, tag, x)
} }
func encodeInt64s(b *buffer, tag int, x []int64) {
if len(x) > 2 {
// Use packed encoding
n1 := len(b.data)
for _, u := range x {
encodeVarint(b, uint64(u))
}
n2 := len(b.data)
encodeLength(b, tag, n2-n1)
n3 := len(b.data)
copy(b.tmp[:], b.data[n2:n3])
copy(b.data[n1+(n3-n2):], b.data[n1:n2])
copy(b.data[n1:], b.tmp[:n3-n2])
return
}
for _, u := range x {
encodeInt64(b, tag, u)
}
}
func encodeString(b *buffer, tag int, x string) { func encodeString(b *buffer, tag int, x string) {
encodeLength(b, tag, len(x)) encodeLength(b, tag, len(x))
b.data = append(b.data, x...) b.data = append(b.data, x...)
...@@ -243,6 +277,20 @@ func decodeInt64(b *buffer, x *int64) error { ...@@ -243,6 +277,20 @@ func decodeInt64(b *buffer, x *int64) error {
} }
func decodeInt64s(b *buffer, x *[]int64) error { func decodeInt64s(b *buffer, x *[]int64) error {
if b.typ == 2 {
// Packed encoding
data := b.data
for len(data) > 0 {
var u uint64
var err error
if u, data, err = decodeVarint(data); err != nil {
return err
}
*x = append(*x, int64(u))
}
return nil
}
var i int64 var i int64
if err := decodeInt64(b, &i); err != nil { if err := decodeInt64(b, &i); err != nil {
return err return err
...@@ -260,6 +308,20 @@ func decodeUint64(b *buffer, x *uint64) error { ...@@ -260,6 +308,20 @@ func decodeUint64(b *buffer, x *uint64) error {
} }
func decodeUint64s(b *buffer, x *[]uint64) error { func decodeUint64s(b *buffer, x *[]uint64) error {
if b.typ == 2 {
data := b.data
// Packed encoding
for len(data) > 0 {
var u uint64
var err error
if u, data, err = decodeVarint(data); err != nil {
return err
}
*x = append(*x, u)
}
return nil
}
var u uint64 var u uint64
if err := decodeUint64(b, &u); err != nil { if err := decodeUint64(b, &u); err != nil {
return err return err
......
package profile
import (
"reflect"
"testing"
)
func TestPackedEncoding(t *testing.T) {
type testcase struct {
uint64s []uint64
int64s []int64
encoded []byte
}
for i, tc := range []testcase{
{
[]uint64{0, 1, 10, 100, 1000, 10000},
[]int64{1000, 0, 1000},
[]byte{10, 8, 0, 1, 10, 100, 232, 7, 144, 78, 18, 5, 232, 7, 0, 232, 7},
},
{
[]uint64{10000},
nil,
[]byte{8, 144, 78},
},
{
nil,
[]int64{-10000},
[]byte{16, 240, 177, 255, 255, 255, 255, 255, 255, 255, 1},
},
} {
source := &packedInts{tc.uint64s, tc.int64s}
if got, want := marshal(source), tc.encoded; !reflect.DeepEqual(got, want) {
t.Errorf("failed encode %d, got %v, want %v", i, got, want)
}
dest := new(packedInts)
if err := unmarshal(tc.encoded, dest); err != nil {
t.Errorf("failed decode %d: %v", i, err)
continue
}
if got, want := dest.uint64s, tc.uint64s; !reflect.DeepEqual(got, want) {
t.Errorf("failed decode uint64s %d, got %v, want %v", i, got, want)
}
if got, want := dest.int64s, tc.int64s; !reflect.DeepEqual(got, want) {
t.Errorf("failed decode int64s %d, got %v, want %v", i, got, want)
}
}
}
type packedInts struct {
uint64s []uint64
int64s []int64
}
func (u *packedInts) decoder() []decoder {
return []decoder{
nil,
func(b *buffer, m message) error { return decodeUint64s(b, &m.(*packedInts).uint64s) },
func(b *buffer, m message) error { return decodeInt64s(b, &m.(*packedInts).int64s) },
}
}
func (u *packedInts) encode(b *buffer) {
encodeUint64s(b, 1, u.uint64s)
encodeInt64s(b, 2, u.int64s)
}
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