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
5package pprof
6
7import (
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.
16func 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).
79func 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