github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/src/cmd/trace/trace_unix_test.go (about) 1 // Copyright 2017 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 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package main 8 9 import ( 10 "bytes" 11 "internal/trace" 12 "runtime" 13 rtrace "runtime/trace" 14 "sync" 15 "syscall" 16 "testing" 17 "time" 18 ) 19 20 // TestGoroutineInSyscall tests threads for timer goroutines 21 // that preexisted when the tracing started were not counted 22 // as threads in syscall. See golang.org/issues/22574. 23 func TestGoroutineInSyscall(t *testing.T) { 24 // Start one goroutine blocked in syscall. 25 // 26 // TODO: syscall.Pipe used to cause the goroutine to 27 // remain blocked in syscall is not portable. Replace 28 // it with a more portable way so this test can run 29 // on non-unix architecture e.g. Windows. 30 var p [2]int 31 if err := syscall.Pipe(p[:]); err != nil { 32 t.Fatalf("failed to create pipe: %v", err) 33 } 34 35 var wg sync.WaitGroup 36 defer func() { 37 syscall.Write(p[1], []byte("a")) 38 wg.Wait() 39 40 syscall.Close(p[0]) 41 syscall.Close(p[1]) 42 }() 43 wg.Add(1) 44 go func() { 45 var tmp [1]byte 46 syscall.Read(p[0], tmp[:]) 47 wg.Done() 48 }() 49 50 // Start multiple timer goroutines. 51 allTimers := make([]*time.Timer, 2*runtime.GOMAXPROCS(0)) 52 defer func() { 53 for _, timer := range allTimers { 54 timer.Stop() 55 } 56 }() 57 58 var timerSetup sync.WaitGroup 59 for i := range allTimers { 60 timerSetup.Add(1) 61 go func(i int) { 62 defer timerSetup.Done() 63 allTimers[i] = time.AfterFunc(time.Hour, nil) 64 }(i) 65 } 66 timerSetup.Wait() 67 68 // Collect and parse trace. 69 buf := new(bytes.Buffer) 70 if err := rtrace.Start(buf); err != nil { 71 t.Fatalf("failed to start tracing: %v", err) 72 } 73 rtrace.Stop() 74 75 res, err := trace.Parse(buf, "") 76 if err != nil { 77 t.Fatalf("failed to parse trace: %v", err) 78 } 79 80 // Check only one thread for the pipe read goroutine is 81 // considered in-syscall. 82 viewerData, err := generateTrace(&traceParams{ 83 parsed: res, 84 endTime: int64(1<<63 - 1), 85 }) 86 if err != nil { 87 t.Fatalf("failed to generate ViewerData: %v", err) 88 } 89 for _, ev := range viewerData.Events { 90 if ev.Name == "Threads" { 91 arg := ev.Arg.(*threadCountersArg) 92 if arg.InSyscall > 1 { 93 t.Errorf("%d threads in syscall at time %v; want less than 1 thread in syscall", arg.InSyscall, ev.Time) 94 } 95 } 96 } 97 }