Reverting merge from trunk
[official-gcc.git] / libgo / go / runtime / pprof / pprof_test.go
blobbdbbf42f028e7450061a7959e94eaf9100f81835
1 // Copyright 2011 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.
5 package pprof_test
7 import (
8 "bytes"
9 "fmt"
10 "hash/crc32"
11 "os/exec"
12 "regexp"
13 "runtime"
14 . "runtime/pprof"
15 "strings"
16 "sync"
17 "testing"
18 "time"
19 "unsafe"
22 func TestCPUProfile(t *testing.T) {
23 buf := make([]byte, 100000)
24 testCPUProfile(t, []string{"crc32.update"}, func() {
25 // This loop takes about a quarter second on a 2 GHz laptop.
26 // We only need to get one 100 Hz clock tick, so we've got
27 // a 25x safety buffer.
28 for i := 0; i < 1000; i++ {
29 crc32.ChecksumIEEE(buf)
34 func TestCPUProfileMultithreaded(t *testing.T) {
35 buf := make([]byte, 100000)
36 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
37 testCPUProfile(t, []string{"crc32.update"}, func() {
38 c := make(chan int)
39 go func() {
40 for i := 0; i < 2000; i++ {
41 crc32.Update(0, crc32.IEEETable, buf)
43 c <- 1
44 }()
45 // This loop takes about a quarter second on a 2 GHz laptop.
46 // We only need to get one 100 Hz clock tick, so we've got
47 // a 25x safety buffer.
48 for i := 0; i < 2000; i++ {
49 crc32.ChecksumIEEE(buf)
51 <-c
55 func parseProfile(t *testing.T, bytes []byte, f func(uintptr, []uintptr)) {
56 // Convert []byte to []uintptr.
57 l := len(bytes) / int(unsafe.Sizeof(uintptr(0)))
58 val := *(*[]uintptr)(unsafe.Pointer(&bytes))
59 val = val[:l]
61 // 5 for the header, 2 for the per-sample header on at least one sample, 3 for the trailer.
62 if l < 5+2+3 {
63 t.Logf("profile too short: %#x", val)
64 if badOS[runtime.GOOS] {
65 t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
66 return
68 t.FailNow()
71 hd, val, tl := val[:5], val[5:l-3], val[l-3:]
72 if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 {
73 t.Fatalf("unexpected header %#x", hd)
76 if tl[0] != 0 || tl[1] != 1 || tl[2] != 0 {
77 t.Fatalf("malformed end-of-data marker %#x", tl)
80 for len(val) > 0 {
81 if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] {
82 t.Fatalf("malformed profile. leftover: %#x", val)
84 f(val[0], val[2:2+val[1]])
85 val = val[2+val[1]:]
89 func testCPUProfile(t *testing.T, need []string, f func()) {
90 switch runtime.GOOS {
91 case "darwin":
92 out, err := exec.Command("uname", "-a").CombinedOutput()
93 if err != nil {
94 t.Fatal(err)
96 vers := string(out)
97 t.Logf("uname -a: %v", vers)
98 case "plan9":
99 // unimplemented
100 return
103 var prof bytes.Buffer
104 if err := StartCPUProfile(&prof); err != nil {
105 t.Fatal(err)
108 StopCPUProfile()
110 // Check that profile is well formed and contains ChecksumIEEE.
111 have := make([]uintptr, len(need))
112 parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
113 for _, pc := range stk {
114 f := runtime.FuncForPC(pc)
115 if f == nil {
116 continue
118 for i, name := range need {
119 if strings.Contains(f.Name(), name) {
120 have[i] += count
126 var total uintptr
127 for i, name := range need {
128 total += have[i]
129 t.Logf("%s: %d\n", name, have[i])
131 ok := true
132 if total == 0 {
133 t.Logf("no CPU profile samples collected")
134 ok = false
136 min := total / uintptr(len(have)) / 3
137 for i, name := range need {
138 if have[i] < min {
139 t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have)))
140 ok = false
144 if !ok {
145 if badOS[runtime.GOOS] {
146 t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
147 return
149 t.FailNow()
153 func TestCPUProfileWithFork(t *testing.T) {
154 // Fork can hang if preempted with signals frequently enough (see issue 5517).
155 // Ensure that we do not do this.
156 heap := 1 << 30
157 if testing.Short() {
158 heap = 100 << 20
160 // This makes fork slower.
161 garbage := make([]byte, heap)
162 // Need to touch the slice, otherwise it won't be paged in.
163 done := make(chan bool)
164 go func() {
165 for i := range garbage {
166 garbage[i] = 42
168 done <- true
170 <-done
172 var prof bytes.Buffer
173 if err := StartCPUProfile(&prof); err != nil {
174 t.Fatal(err)
176 defer StopCPUProfile()
178 for i := 0; i < 10; i++ {
179 exec.Command("go").CombinedOutput()
183 // Test that profiler does not observe runtime.gogo as "user" goroutine execution.
184 // If it did, it would see inconsistent state and would either record an incorrect stack
185 // or crash because the stack was malformed.
186 func TestGoroutineSwitch(t *testing.T) {
187 if runtime.GOOS == "windows" {
188 t.Skip("flaky test; see http://golang.org/issue/6417")
190 // How much to try. These defaults take about 1 seconds
191 // on a 2012 MacBook Pro. The ones in short mode take
192 // about 0.1 seconds.
193 tries := 10
194 count := 1000000
195 if testing.Short() {
196 tries = 1
198 for try := 0; try < tries; try++ {
199 var prof bytes.Buffer
200 if err := StartCPUProfile(&prof); err != nil {
201 t.Fatal(err)
203 for i := 0; i < count; i++ {
204 runtime.Gosched()
206 StopCPUProfile()
208 // Read profile to look for entries for runtime.gogo with an attempt at a traceback.
209 // The special entry
210 parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
211 // An entry with two frames with 'System' in its top frame
212 // exists to record a PC without a traceback. Those are okay.
213 if len(stk) == 2 {
214 f := runtime.FuncForPC(stk[1])
215 if f != nil && f.Name() == "System" {
216 return
220 // Otherwise, should not see runtime.gogo.
221 // The place we'd see it would be the inner most frame.
222 f := runtime.FuncForPC(stk[0])
223 if f != nil && f.Name() == "runtime.gogo" {
224 var buf bytes.Buffer
225 for _, pc := range stk {
226 f := runtime.FuncForPC(pc)
227 if f == nil {
228 fmt.Fprintf(&buf, "%#x ?:0\n", pc)
229 } else {
230 file, line := f.FileLine(pc)
231 fmt.Fprintf(&buf, "%#x %s:%d\n", pc, file, line)
234 t.Fatalf("found profile entry for runtime.gogo:\n%s", buf.String())
240 // Operating systems that are expected to fail the tests. See issue 6047.
241 var badOS = map[string]bool{
242 "darwin": true,
243 "netbsd": true,
244 "openbsd": true,
247 func TestBlockProfile(t *testing.T) {
248 t.Skip("lots of details are different for gccgo; FIXME")
249 type TestCase struct {
250 name string
251 f func()
252 re string
254 tests := [...]TestCase{
255 {"chan recv", blockChanRecv, `
256 [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
257 # 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+
258 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
259 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
261 {"chan send", blockChanSend, `
262 [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
263 # 0x[0-9,a-f]+ runtime\.chansend1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+
264 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
265 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
267 {"chan close", blockChanClose, `
268 [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
269 # 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+
270 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
271 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
273 {"select recv async", blockSelectRecvAsync, `
274 [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
275 # 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+
276 # 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
277 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
279 {"select send sync", blockSelectSendSync, `
280 [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
281 # 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+
282 # 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
283 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
285 {"mutex", blockMutex, `
286 [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
287 # 0x[0-9,a-f]+ sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+ .*/src/pkg/sync/mutex\.go:[0-9]+
288 # 0x[0-9,a-f]+ runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
289 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
293 runtime.SetBlockProfileRate(1)
294 defer runtime.SetBlockProfileRate(0)
295 for _, test := range tests {
296 test.f()
298 var w bytes.Buffer
299 Lookup("block").WriteTo(&w, 1)
300 prof := w.String()
302 if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") {
303 t.Fatalf("Bad profile header:\n%v", prof)
306 for _, test := range tests {
307 if !regexp.MustCompile(test.re).MatchString(prof) {
308 t.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof)
313 const blockDelay = 10 * time.Millisecond
315 func blockChanRecv() {
316 c := make(chan bool)
317 go func() {
318 time.Sleep(blockDelay)
319 c <- true
324 func blockChanSend() {
325 c := make(chan bool)
326 go func() {
327 time.Sleep(blockDelay)
330 c <- true
333 func blockChanClose() {
334 c := make(chan bool)
335 go func() {
336 time.Sleep(blockDelay)
337 close(c)
342 func blockSelectRecvAsync() {
343 c := make(chan bool, 1)
344 c2 := make(chan bool, 1)
345 go func() {
346 time.Sleep(blockDelay)
347 c <- true
349 select {
350 case <-c:
351 case <-c2:
355 func blockSelectSendSync() {
356 c := make(chan bool)
357 c2 := make(chan bool)
358 go func() {
359 time.Sleep(blockDelay)
362 select {
363 case c <- true:
364 case c2 <- true:
368 func blockMutex() {
369 var mu sync.Mutex
370 mu.Lock()
371 go func() {
372 time.Sleep(blockDelay)
373 mu.Unlock()
375 mu.Lock()