Source file src/cmd/trace/trace_unix_test.go

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

View as plain text