mirror of
https://github.com/golang/go
synced 2024-11-12 03:40:21 -07:00
runtime/pprof: generate heap profiles in compressed proto format
When debug is 0, emit the compressed proto format. The debug>0 format stays the same. Updates #16093 Change-Id: I45aa1874a22d34cf44dd4aa78bbff9302381cb34 Reviewed-on: https://go-review.googlesource.com/33422 Run-TryBot: Michael Matloob <matloob@golang.org> Reviewed-by: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
f88a33aeac
commit
86ab09eed5
83
src/runtime/pprof/internal/protopprof/protomemprofile.go
Normal file
83
src/runtime/pprof/internal/protopprof/protomemprofile.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2016 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 protopprof
|
||||
|
||||
import (
|
||||
"internal/pprof/profile"
|
||||
"math"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EncodeMemProfile converts MemProfileRecords to a Profile.
|
||||
func EncodeMemProfile(mr []runtime.MemProfileRecord, rate int64, t time.Time) *profile.Profile {
|
||||
p := &profile.Profile{
|
||||
Period: rate,
|
||||
PeriodType: &profile.ValueType{Type: "space", Unit: "bytes"},
|
||||
SampleType: []*profile.ValueType{
|
||||
{Type: "alloc_objects", Unit: "count"},
|
||||
{Type: "alloc_space", Unit: "bytes"},
|
||||
{Type: "inuse_objects", Unit: "count"},
|
||||
{Type: "inuse_space", Unit: "bytes"},
|
||||
},
|
||||
TimeNanos: int64(t.UnixNano()),
|
||||
}
|
||||
|
||||
locs := make(map[uintptr]*profile.Location)
|
||||
for _, r := range mr {
|
||||
stack := r.Stack()
|
||||
sloc := make([]*profile.Location, len(stack))
|
||||
for i, addr := range stack {
|
||||
loc := locs[addr]
|
||||
if loc == nil {
|
||||
loc = &profile.Location{
|
||||
ID: uint64(len(p.Location) + 1),
|
||||
Address: uint64(addr),
|
||||
}
|
||||
locs[addr] = loc
|
||||
p.Location = append(p.Location, loc)
|
||||
}
|
||||
sloc[i] = loc
|
||||
}
|
||||
|
||||
ao, ab := scaleHeapSample(r.AllocObjects, r.AllocBytes, rate)
|
||||
uo, ub := scaleHeapSample(r.InUseObjects(), r.InUseBytes(), rate)
|
||||
|
||||
p.Sample = append(p.Sample, &profile.Sample{
|
||||
Value: []int64{ao, ab, uo, ub},
|
||||
Location: sloc,
|
||||
})
|
||||
}
|
||||
if runtime.GOOS == "linux" {
|
||||
addMappings(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// scaleHeapSample adjusts the data from a heap Sample to
|
||||
// account for its probability of appearing in the collected
|
||||
// data. heap profiles are a sampling of the memory allocations
|
||||
// requests in a program. We estimate the unsampled value by dividing
|
||||
// each collected sample by its probability of appearing in the
|
||||
// profile. heap profiles rely on a poisson process to determine
|
||||
// which samples to collect, based on the desired average collection
|
||||
// rate R. The probability of a sample of size S to appear in that
|
||||
// profile is 1-exp(-S/R).
|
||||
func scaleHeapSample(count, size, rate int64) (int64, int64) {
|
||||
if count == 0 || size == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
if rate <= 1 {
|
||||
// if rate==1 all samples were collected so no adjustment is needed.
|
||||
// if rate<1 treat as unknown and skip scaling.
|
||||
return count, size
|
||||
}
|
||||
|
||||
avgSize := float64(size) / float64(count)
|
||||
scale := 1 / (1 - math.Exp(-avgSize/float64(rate)))
|
||||
|
||||
return int64(float64(count) * scale), int64(float64(size) * scale)
|
||||
}
|
102
src/runtime/pprof/internal/protopprof/protomemprofile_test.go
Normal file
102
src/runtime/pprof/internal/protopprof/protomemprofile_test.go
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2016 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 protopprof
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"internal/pprof/profile"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestSampledHeapAllocProfile tests encoding of a memory profile from
|
||||
// runtime.MemProfileRecord data.
|
||||
func TestSampledHeapAllocProfile(t *testing.T) {
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skip("Test requires a system with /proc/self/maps")
|
||||
}
|
||||
|
||||
// Figure out two addresses from /proc/self/maps.
|
||||
mmap, err := ioutil.ReadFile("/proc/self/maps")
|
||||
if err != nil {
|
||||
t.Fatal("Cannot read /proc/self/maps")
|
||||
}
|
||||
rd := bytes.NewReader(mmap)
|
||||
mprof := &profile.Profile{}
|
||||
if err = mprof.ParseMemoryMap(rd); err != nil {
|
||||
t.Fatalf("Cannot parse /proc/self/maps")
|
||||
}
|
||||
if len(mprof.Mapping) < 2 {
|
||||
t.Fatalf("Less than two mappings")
|
||||
}
|
||||
address1 := mprof.Mapping[0].Start
|
||||
address2 := mprof.Mapping[1].Start
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
rec, rate := testMemRecords(address1, address2)
|
||||
p := EncodeMemProfile(rec, rate, time.Now())
|
||||
if err := p.Write(&buf); err != nil {
|
||||
t.Fatalf("Failed to write profile: %v", err)
|
||||
}
|
||||
|
||||
p, err = profile.Parse(&buf)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not parse Profile profile: %v", err)
|
||||
}
|
||||
|
||||
// Expected PeriodType, SampleType and Sample.
|
||||
expectedPeriodType := &profile.ValueType{Type: "space", Unit: "bytes"}
|
||||
expectedSampleType := []*profile.ValueType{
|
||||
{Type: "alloc_objects", Unit: "count"},
|
||||
{Type: "alloc_space", Unit: "bytes"},
|
||||
{Type: "inuse_objects", Unit: "count"},
|
||||
{Type: "inuse_space", Unit: "bytes"},
|
||||
}
|
||||
// Expected samples, with values unsampled according to the profiling rate.
|
||||
expectedSample := []*profile.Sample{
|
||||
{Value: []int64{2050, 2099200, 1537, 1574400}, Location: []*profile.Location{
|
||||
{ID: 1, Mapping: mprof.Mapping[0], Address: address1},
|
||||
{ID: 2, Mapping: mprof.Mapping[1], Address: address2},
|
||||
}},
|
||||
{Value: []int64{1, 829411, 1, 829411}, Location: []*profile.Location{
|
||||
{ID: 3, Mapping: mprof.Mapping[1], Address: address2 + 1},
|
||||
{ID: 4, Mapping: mprof.Mapping[1], Address: address2 + 2},
|
||||
}},
|
||||
{Value: []int64{1, 829411, 0, 0}, Location: []*profile.Location{
|
||||
{ID: 5, Mapping: mprof.Mapping[0], Address: address1 + 1},
|
||||
{ID: 6, Mapping: mprof.Mapping[0], Address: address1 + 2},
|
||||
{ID: 7, Mapping: mprof.Mapping[1], Address: address2 + 3},
|
||||
}},
|
||||
}
|
||||
|
||||
if p.Period != 512*1024 {
|
||||
t.Fatalf("Sampling periods do not match")
|
||||
}
|
||||
if !reflect.DeepEqual(p.PeriodType, expectedPeriodType) {
|
||||
t.Fatalf("Period types do not match")
|
||||
}
|
||||
if !reflect.DeepEqual(p.SampleType, expectedSampleType) {
|
||||
t.Fatalf("Sample types do not match")
|
||||
}
|
||||
if !reflect.DeepEqual(p.Sample, expectedSample) {
|
||||
t.Fatalf("Samples do not match: Expected: %v, Got:%v", getSampleAsString(expectedSample),
|
||||
getSampleAsString(p.Sample))
|
||||
}
|
||||
}
|
||||
|
||||
func testMemRecords(a1, a2 uint64) ([]runtime.MemProfileRecord, int64) {
|
||||
addr1, addr2 := uintptr(a1), uintptr(a2)
|
||||
rate := int64(512 * 1024)
|
||||
rec := []runtime.MemProfileRecord{
|
||||
{4096, 1024, 4, 1, [32]uintptr{addr1, addr2}},
|
||||
{512 * 1024, 0, 1, 0, [32]uintptr{addr2 + 1, addr2 + 2}},
|
||||
{512 * 1024, 512 * 1024, 1, 1, [32]uintptr{addr1 + 1, addr1 + 2, addr2 + 3}},
|
||||
}
|
||||
return rec, rate
|
||||
}
|
@ -485,15 +485,16 @@ func writeHeap(w io.Writer, debug int) error {
|
||||
// Profile grew; try again.
|
||||
}
|
||||
|
||||
if debug == 0 {
|
||||
pp := protopprof.EncodeMemProfile(p, int64(runtime.MemProfileRate), time.Now())
|
||||
return pp.Write(w)
|
||||
}
|
||||
|
||||
sort.Slice(p, func(i, j int) bool { return p[i].InUseBytes() > p[j].InUseBytes() })
|
||||
|
||||
b := bufio.NewWriter(w)
|
||||
var tw *tabwriter.Writer
|
||||
w = b
|
||||
if debug > 0 {
|
||||
tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
||||
tw := tabwriter.NewWriter(b, 1, 8, 1, '\t', 0)
|
||||
w = tw
|
||||
}
|
||||
|
||||
var total runtime.MemProfileRecord
|
||||
for i := range p {
|
||||
@ -521,10 +522,8 @@ func writeHeap(w io.Writer, debug int) error {
|
||||
fmt.Fprintf(w, " %#x", pc)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
if debug > 0 {
|
||||
printStackRecord(w, r.Stack(), false)
|
||||
}
|
||||
}
|
||||
|
||||
// Print memstats information too.
|
||||
// Pprof will ignore, but useful for people
|
||||
@ -557,9 +556,7 @@ func writeHeap(w io.Writer, debug int) error {
|
||||
fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC)
|
||||
fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC)
|
||||
|
||||
if tw != nil {
|
||||
tw.Flush()
|
||||
}
|
||||
return b.Flush()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user