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  }