mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
build: move package sources from src/pkg to src
Preparation was in CL 134570043. This CL contains only the effect of 'hg mv src/pkg/* src'. For more about the move, see golang.org/s/go14nopkg.
This commit is contained in:
parent
220a6de47e
commit
c007ce824d
2097 changed files with 0 additions and 0 deletions
452
src/runtime/pprof/pprof_test.go
Normal file
452
src/runtime/pprof/pprof_test.go
Normal file
|
|
@ -0,0 +1,452 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !nacl
|
||||
|
||||
package pprof_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
. "runtime/pprof"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func cpuHogger(f func()) {
|
||||
// We only need to get one 100 Hz clock tick, so we've got
|
||||
// a 100x safety buffer.
|
||||
// But do at least 2000 iterations (which should take about 400ms),
|
||||
// otherwise TestCPUProfileMultithreaded can fail if only one
|
||||
// thread is scheduled during the 1 second period.
|
||||
t0 := time.Now()
|
||||
for i := 0; i < 2000 || time.Since(t0) < time.Second; i++ {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
salt1 = 0
|
||||
salt2 = 0
|
||||
)
|
||||
|
||||
// The actual CPU hogging function.
|
||||
// Must not call other functions nor access heap/globals in the loop,
|
||||
// otherwise under race detector the samples will be in the race runtime.
|
||||
func cpuHog1() {
|
||||
foo := salt1
|
||||
for i := 0; i < 1e5; i++ {
|
||||
if foo > 0 {
|
||||
foo *= foo
|
||||
} else {
|
||||
foo *= foo + 1
|
||||
}
|
||||
}
|
||||
salt1 = foo
|
||||
}
|
||||
|
||||
func cpuHog2() {
|
||||
foo := salt2
|
||||
for i := 0; i < 1e5; i++ {
|
||||
if foo > 0 {
|
||||
foo *= foo
|
||||
} else {
|
||||
foo *= foo + 2
|
||||
}
|
||||
}
|
||||
salt2 = foo
|
||||
}
|
||||
|
||||
func TestCPUProfile(t *testing.T) {
|
||||
testCPUProfile(t, []string{"runtime/pprof_test.cpuHog1"}, func() {
|
||||
cpuHogger(cpuHog1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCPUProfileMultithreaded(t *testing.T) {
|
||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
|
||||
testCPUProfile(t, []string{"runtime/pprof_test.cpuHog1", "runtime/pprof_test.cpuHog2"}, func() {
|
||||
c := make(chan int)
|
||||
go func() {
|
||||
cpuHogger(cpuHog1)
|
||||
c <- 1
|
||||
}()
|
||||
cpuHogger(cpuHog2)
|
||||
<-c
|
||||
})
|
||||
}
|
||||
|
||||
func parseProfile(t *testing.T, bytes []byte, f func(uintptr, []uintptr)) {
|
||||
// Convert []byte to []uintptr.
|
||||
l := len(bytes) / int(unsafe.Sizeof(uintptr(0)))
|
||||
val := *(*[]uintptr)(unsafe.Pointer(&bytes))
|
||||
val = val[:l]
|
||||
|
||||
// 5 for the header, 2 for the per-sample header on at least one sample, 3 for the trailer.
|
||||
if l < 5+2+3 {
|
||||
t.Logf("profile too short: %#x", val)
|
||||
if badOS[runtime.GOOS] {
|
||||
t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
hd, val, tl := val[:5], val[5:l-3], val[l-3:]
|
||||
if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 {
|
||||
t.Fatalf("unexpected header %#x", hd)
|
||||
}
|
||||
|
||||
if tl[0] != 0 || tl[1] != 1 || tl[2] != 0 {
|
||||
t.Fatalf("malformed end-of-data marker %#x", tl)
|
||||
}
|
||||
|
||||
for len(val) > 0 {
|
||||
if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] {
|
||||
t.Fatalf("malformed profile. leftover: %#x", val)
|
||||
}
|
||||
f(val[0], val[2:2+val[1]])
|
||||
val = val[2+val[1]:]
|
||||
}
|
||||
}
|
||||
|
||||
func testCPUProfile(t *testing.T, need []string, f func()) {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
out, err := exec.Command("uname", "-a").CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vers := string(out)
|
||||
t.Logf("uname -a: %v", vers)
|
||||
case "plan9":
|
||||
// unimplemented
|
||||
return
|
||||
}
|
||||
|
||||
var prof bytes.Buffer
|
||||
if err := StartCPUProfile(&prof); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f()
|
||||
StopCPUProfile()
|
||||
|
||||
// Check that profile is well formed and contains need.
|
||||
have := make([]uintptr, len(need))
|
||||
parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
|
||||
for _, pc := range stk {
|
||||
f := runtime.FuncForPC(pc)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
for i, name := range need {
|
||||
if strings.Contains(f.Name(), name) {
|
||||
have[i] += count
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if len(need) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var total uintptr
|
||||
for i, name := range need {
|
||||
total += have[i]
|
||||
t.Logf("%s: %d\n", name, have[i])
|
||||
}
|
||||
ok := true
|
||||
if total == 0 {
|
||||
t.Logf("no CPU profile samples collected")
|
||||
ok = false
|
||||
}
|
||||
// We'd like to check a reasonable minimum, like
|
||||
// total / len(have) / smallconstant, but this test is
|
||||
// pretty flaky (see bug 7095). So we'll just test to
|
||||
// make sure we got at least one sample.
|
||||
min := uintptr(1)
|
||||
for i, name := range need {
|
||||
if have[i] < min {
|
||||
t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have)))
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
if badOS[runtime.GOOS] {
|
||||
t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCPUProfileWithFork(t *testing.T) {
|
||||
// Fork can hang if preempted with signals frequently enough (see issue 5517).
|
||||
// Ensure that we do not do this.
|
||||
heap := 1 << 30
|
||||
if testing.Short() {
|
||||
heap = 100 << 20
|
||||
}
|
||||
// This makes fork slower.
|
||||
garbage := make([]byte, heap)
|
||||
// Need to touch the slice, otherwise it won't be paged in.
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
for i := range garbage {
|
||||
garbage[i] = 42
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
<-done
|
||||
|
||||
var prof bytes.Buffer
|
||||
if err := StartCPUProfile(&prof); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer StopCPUProfile()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
exec.Command("go").CombinedOutput()
|
||||
}
|
||||
}
|
||||
|
||||
// Test that profiler does not observe runtime.gogo as "user" goroutine execution.
|
||||
// If it did, it would see inconsistent state and would either record an incorrect stack
|
||||
// or crash because the stack was malformed.
|
||||
func TestGoroutineSwitch(t *testing.T) {
|
||||
// How much to try. These defaults take about 1 seconds
|
||||
// on a 2012 MacBook Pro. The ones in short mode take
|
||||
// about 0.1 seconds.
|
||||
tries := 10
|
||||
count := 1000000
|
||||
if testing.Short() {
|
||||
tries = 1
|
||||
}
|
||||
for try := 0; try < tries; try++ {
|
||||
var prof bytes.Buffer
|
||||
if err := StartCPUProfile(&prof); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < count; i++ {
|
||||
runtime.Gosched()
|
||||
}
|
||||
StopCPUProfile()
|
||||
|
||||
// Read profile to look for entries for runtime.gogo with an attempt at a traceback.
|
||||
// The special entry
|
||||
parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
|
||||
// An entry with two frames with 'System' in its top frame
|
||||
// exists to record a PC without a traceback. Those are okay.
|
||||
if len(stk) == 2 {
|
||||
f := runtime.FuncForPC(stk[1])
|
||||
if f != nil && (f.Name() == "System" || f.Name() == "ExternalCode") {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, should not see runtime.gogo.
|
||||
// The place we'd see it would be the inner most frame.
|
||||
f := runtime.FuncForPC(stk[0])
|
||||
if f != nil && f.Name() == "runtime.gogo" {
|
||||
var buf bytes.Buffer
|
||||
for _, pc := range stk {
|
||||
f := runtime.FuncForPC(pc)
|
||||
if f == nil {
|
||||
fmt.Fprintf(&buf, "%#x ?:0\n", pc)
|
||||
} else {
|
||||
file, line := f.FileLine(pc)
|
||||
fmt.Fprintf(&buf, "%#x %s:%d\n", pc, file, line)
|
||||
}
|
||||
}
|
||||
t.Fatalf("found profile entry for runtime.gogo:\n%s", buf.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test that profiling of division operations is okay, especially on ARM. See issue 6681.
|
||||
func TestMathBigDivide(t *testing.T) {
|
||||
testCPUProfile(t, nil, func() {
|
||||
t := time.After(5 * time.Second)
|
||||
pi := new(big.Int)
|
||||
for {
|
||||
for i := 0; i < 100; i++ {
|
||||
n := big.NewInt(2646693125139304345)
|
||||
d := big.NewInt(842468587426513207)
|
||||
pi.Div(n, d)
|
||||
}
|
||||
select {
|
||||
case <-t:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Operating systems that are expected to fail the tests. See issue 6047.
|
||||
var badOS = map[string]bool{
|
||||
"darwin": true,
|
||||
"netbsd": true,
|
||||
"plan9": true,
|
||||
}
|
||||
|
||||
func TestBlockProfile(t *testing.T) {
|
||||
type TestCase struct {
|
||||
name string
|
||||
f func()
|
||||
re string
|
||||
}
|
||||
tests := [...]TestCase{
|
||||
{"chan recv", blockChanRecv, `
|
||||
[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]+
|
||||
# 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/runtime/chan.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
`},
|
||||
{"chan send", blockChanSend, `
|
||||
[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]+
|
||||
# 0x[0-9,a-f]+ runtime\.chansend1\+0x[0-9,a-f]+ .*/src/runtime/chan.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
`},
|
||||
{"chan close", blockChanClose, `
|
||||
[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]+
|
||||
# 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/runtime/chan.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
`},
|
||||
{"select recv async", blockSelectRecvAsync, `
|
||||
[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]+
|
||||
# 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/runtime/select.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
`},
|
||||
{"select send sync", blockSelectSendSync, `
|
||||
[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]+
|
||||
# 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/runtime/select.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
`},
|
||||
{"mutex", blockMutex, `
|
||||
[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]+
|
||||
# 0x[0-9,a-f]+ sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+ .*/src/sync/mutex\.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
`},
|
||||
{"cond", blockCond, `
|
||||
[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]+
|
||||
# 0x[0-9,a-f]+ sync\.\(\*Cond\)\.Wait\+0x[0-9,a-f]+ .*/src/sync/cond\.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.blockCond\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
|
||||
`},
|
||||
}
|
||||
|
||||
runtime.SetBlockProfileRate(1)
|
||||
defer runtime.SetBlockProfileRate(0)
|
||||
for _, test := range tests {
|
||||
test.f()
|
||||
}
|
||||
var w bytes.Buffer
|
||||
Lookup("block").WriteTo(&w, 1)
|
||||
prof := w.String()
|
||||
|
||||
if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") {
|
||||
t.Fatalf("Bad profile header:\n%v", prof)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if !regexp.MustCompile(test.re).MatchString(prof) {
|
||||
t.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const blockDelay = 10 * time.Millisecond
|
||||
|
||||
func blockChanRecv() {
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
time.Sleep(blockDelay)
|
||||
c <- true
|
||||
}()
|
||||
<-c
|
||||
}
|
||||
|
||||
func blockChanSend() {
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
time.Sleep(blockDelay)
|
||||
<-c
|
||||
}()
|
||||
c <- true
|
||||
}
|
||||
|
||||
func blockChanClose() {
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
time.Sleep(blockDelay)
|
||||
close(c)
|
||||
}()
|
||||
<-c
|
||||
}
|
||||
|
||||
func blockSelectRecvAsync() {
|
||||
c := make(chan bool, 1)
|
||||
c2 := make(chan bool, 1)
|
||||
go func() {
|
||||
time.Sleep(blockDelay)
|
||||
c <- true
|
||||
}()
|
||||
select {
|
||||
case <-c:
|
||||
case <-c2:
|
||||
}
|
||||
}
|
||||
|
||||
func blockSelectSendSync() {
|
||||
c := make(chan bool)
|
||||
c2 := make(chan bool)
|
||||
go func() {
|
||||
time.Sleep(blockDelay)
|
||||
<-c
|
||||
}()
|
||||
select {
|
||||
case c <- true:
|
||||
case c2 <- true:
|
||||
}
|
||||
}
|
||||
|
||||
func blockMutex() {
|
||||
var mu sync.Mutex
|
||||
mu.Lock()
|
||||
go func() {
|
||||
time.Sleep(blockDelay)
|
||||
mu.Unlock()
|
||||
}()
|
||||
mu.Lock()
|
||||
}
|
||||
|
||||
func blockCond() {
|
||||
var mu sync.Mutex
|
||||
c := sync.NewCond(&mu)
|
||||
mu.Lock()
|
||||
go func() {
|
||||
time.Sleep(blockDelay)
|
||||
mu.Lock()
|
||||
c.Signal()
|
||||
mu.Unlock()
|
||||
}()
|
||||
c.Wait()
|
||||
mu.Unlock()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue