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

View as plain text