]> Cypherpunks.ru repositories - gostls13.git/blob - test/heapsampling.go
test: disable flaky heapsampling test for now
[gostls13.git] / test / heapsampling.go
1 // run
2
3 // Copyright 2009 The Go Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file.
6
7 // Test heap sampling logic.
8
9 package main
10
11 import (
12         "fmt"
13         "math"
14         "runtime"
15 )
16
17 var a16 *[16]byte
18 var a512 *[512]byte
19 var a256 *[256]byte
20 var a1k *[1024]byte
21 var a64k *[64 * 1024]byte
22
23 // This test checks that heap sampling produces reasonable
24 // results. Note that heap sampling uses randomization, so the results
25 // vary for run to run. This test only checks that the resulting
26 // values appear reasonable.
27 func main() {
28         return // TODO: fix this flaky test; golang.org/issue/13098
29
30         const countInterleaved = 10000
31         allocInterleaved(countInterleaved)
32         checkAllocations(getMemProfileRecords(), "main.allocInterleaved", countInterleaved, []int64{256 * 1024, 1024, 256 * 1024, 512, 256 * 1024, 256})
33
34         const count = 100000
35         alloc(count)
36         checkAllocations(getMemProfileRecords(), "main.alloc", count, []int64{1024, 512, 256})
37 }
38
39 // allocInterleaved stress-tests the heap sampling logic by
40 // interleaving large and small allocations.
41 func allocInterleaved(n int) {
42         for i := 0; i < n; i++ {
43                 // Test verification depends on these lines being contiguous.
44                 a64k = new([64 * 1024]byte)
45                 a1k = new([1024]byte)
46                 a64k = new([64 * 1024]byte)
47                 a512 = new([512]byte)
48                 a64k = new([64 * 1024]byte)
49                 a256 = new([256]byte)
50         }
51 }
52
53 // alloc performs only small allocations for sanity testing.
54 func alloc(n int) {
55         for i := 0; i < n; i++ {
56                 // Test verification depends on these lines being contiguous.
57                 a1k = new([1024]byte)
58                 a512 = new([512]byte)
59                 a256 = new([256]byte)
60         }
61 }
62
63 // checkAllocations validates that the profile records collected for
64 // the named function are consistent with count contiguous allocations
65 // of the specified sizes.
66 func checkAllocations(records []runtime.MemProfileRecord, fname string, count int64, size []int64) {
67         a := allocObjects(records, fname)
68         firstLine := 0
69         for ln := range a {
70                 if firstLine == 0 || firstLine > ln {
71                         firstLine = ln
72                 }
73         }
74         var totalcount int64
75         for i, w := range size {
76                 ln := firstLine + i
77                 s := a[ln]
78                 checkValue(fname, ln, "objects", count, s.objects)
79                 checkValue(fname, ln, "bytes", count*w, s.bytes)
80                 totalcount += s.objects
81         }
82         // Check the total number of allocations, to ensure some sampling occurred.
83         if totalwant := count * int64(len(size)); totalcount <= 0 || totalcount > totalwant*1024 {
84                 panic(fmt.Sprintf("%s want total count > 0 && <= %d, got %d", fname, totalwant*1024, totalcount))
85         }
86 }
87
88 // checkValue checks an unsampled value against a range.
89 func checkValue(fname string, ln int, name string, want, got int64) {
90         if got < 0 || got > 1024*want {
91                 panic(fmt.Sprintf("%s:%d want %s >= 0 && <= %d, got %d", fname, ln, name, 1024*want, got))
92         }
93 }
94
95 func getMemProfileRecords() []runtime.MemProfileRecord {
96         // Find out how many records there are (MemProfile(nil, true)),
97         // allocate that many records, and get the data.
98         // There's a race—more records might be added between
99         // the two calls—so allocate a few extra records for safety
100         // and also try again if we're very unlucky.
101         // The loop should only execute one iteration in the common case.
102         var p []runtime.MemProfileRecord
103         n, ok := runtime.MemProfile(nil, true)
104         for {
105                 // Allocate room for a slightly bigger profile,
106                 // in case a few more entries have been added
107                 // since the call to MemProfile.
108                 p = make([]runtime.MemProfileRecord, n+50)
109                 n, ok = runtime.MemProfile(p, true)
110                 if ok {
111                         p = p[0:n]
112                         break
113                 }
114                 // Profile grew; try again.
115         }
116         return p
117 }
118
119 type allocStat struct {
120         bytes, objects int64
121 }
122
123 // allocObjects examines the profile records for the named function
124 // and returns the allocation stats aggregated by source line number.
125 func allocObjects(records []runtime.MemProfileRecord, function string) map[int]allocStat {
126         a := make(map[int]allocStat)
127         for _, r := range records {
128                 for _, s := range r.Stack0 {
129                         if s == 0 {
130                                 break
131                         }
132                         if f := runtime.FuncForPC(s); f != nil {
133                                 name := f.Name()
134                                 _, line := f.FileLine(s)
135                                 if name == function {
136                                         allocStat := a[line]
137                                         allocStat.bytes += r.AllocBytes
138                                         allocStat.objects += r.AllocObjects
139                                         a[line] = allocStat
140                                 }
141                         }
142                 }
143         }
144         for line, stats := range a {
145                 objects, bytes := scaleHeapSample(stats.objects, stats.bytes, int64(runtime.MemProfileRate))
146                 a[line] = allocStat{bytes, objects}
147         }
148         return a
149 }
150
151 // scaleHeapSample unsamples heap allocations.
152 // Taken from src/cmd/pprof/internal/profile/legacy_profile.go
153 func scaleHeapSample(count, size, rate int64) (int64, int64) {
154         if count == 0 || size == 0 {
155                 return 0, 0
156         }
157
158         if rate <= 1 {
159                 // if rate==1 all samples were collected so no adjustment is needed.
160                 // if rate<1 treat as unknown and skip scaling.
161                 return count, size
162         }
163
164         avgSize := float64(size) / float64(count)
165         scale := 1 / (1 - math.Exp(-avgSize/float64(rate)))
166
167         return int64(float64(count) * scale), int64(float64(size) * scale)
168 }