Source file src/runtime/pprof/protomem.go

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pprof
     6  
     7  import (
     8  	"io"
     9  	"math"
    10  	"runtime"
    11  	"strings"
    12  )
    13  
    14  // writeHeapProto writes the current heap profile in protobuf format to w.
    15  func writeHeapProto(w io.Writer, p []runtime.MemProfileRecord, rate int64, defaultSampleType string) error {
    16  	b := newProfileBuilder(w)
    17  	b.pbValueType(tagProfile_PeriodType, "space", "bytes")
    18  	b.pb.int64Opt(tagProfile_Period, rate)
    19  	b.pbValueType(tagProfile_SampleType, "alloc_objects", "count")
    20  	b.pbValueType(tagProfile_SampleType, "alloc_space", "bytes")
    21  	b.pbValueType(tagProfile_SampleType, "inuse_objects", "count")
    22  	b.pbValueType(tagProfile_SampleType, "inuse_space", "bytes")
    23  	if defaultSampleType != "" {
    24  		b.pb.int64Opt(tagProfile_DefaultSampleType, b.stringIndex(defaultSampleType))
    25  	}
    26  
    27  	values := []int64{0, 0, 0, 0}
    28  	var locs []uint64
    29  	for _, r := range p {
    30  		hideRuntime := true
    31  		for tries := 0; tries < 2; tries++ {
    32  			stk := r.Stack()
    33  			// For heap profiles, all stack
    34  			// addresses are return PCs, which is
    35  			// what appendLocsForStack expects.
    36  			if hideRuntime {
    37  				for i, addr := range stk {
    38  					if f := runtime.FuncForPC(addr); f != nil && strings.HasPrefix(f.Name(), "runtime.") {
    39  						continue
    40  					}
    41  					// Found non-runtime. Show any runtime uses above it.
    42  					stk = stk[i:]
    43  					break
    44  				}
    45  			}
    46  			locs = b.appendLocsForStack(locs[:0], stk)
    47  			if len(locs) > 0 {
    48  				break
    49  			}
    50  			hideRuntime = false // try again, and show all frames next time.
    51  		}
    52  
    53  		values[0], values[1] = scaleHeapSample(r.AllocObjects, r.AllocBytes, rate)
    54  		values[2], values[3] = scaleHeapSample(r.InUseObjects(), r.InUseBytes(), rate)
    55  		var blockSize int64
    56  		if r.AllocObjects > 0 {
    57  			blockSize = r.AllocBytes / r.AllocObjects
    58  		}
    59  		b.pbSample(values, locs, func() {
    60  			if blockSize != 0 {
    61  				b.pbLabel(tagSample_Label, "bytes", "", blockSize)
    62  			}
    63  		})
    64  	}
    65  	b.build()
    66  	return nil
    67  }
    68  
    69  // scaleHeapSample adjusts the data from a heap Sample to
    70  // account for its probability of appearing in the collected
    71  // data. heap profiles are a sampling of the memory allocations
    72  // requests in a program. We estimate the unsampled value by dividing
    73  // each collected sample by its probability of appearing in the
    74  // profile. heap profiles rely on a poisson process to determine
    75  // which samples to collect, based on the desired average collection
    76  // rate R. The probability of a sample of size S to appear in that
    77  // profile is 1-exp(-S/R).
    78  func scaleHeapSample(count, size, rate int64) (int64, int64) {
    79  	if count == 0 || size == 0 {
    80  		return 0, 0
    81  	}
    82  
    83  	if rate <= 1 {
    84  		// if rate==1 all samples were collected so no adjustment is needed.
    85  		// if rate<1 treat as unknown and skip scaling.
    86  		return count, size
    87  	}
    88  
    89  	avgSize := float64(size) / float64(count)
    90  	scale := 1 / (1 - math.Exp(-avgSize/float64(rate)))
    91  
    92  	return int64(float64(count) * scale), int64(float64(size) * scale)
    93  }
    94  

View as plain text