github.com/c9s/go@v0.0.0-20180120015821-984e81f64e0c/src/cmd/trace/trace_test.go (about)

     1  // Copyright 2016 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  package main
     6  
     7  import (
     8  	"internal/trace"
     9  	"strings"
    10  	"testing"
    11  )
    12  
    13  // stacks is a fake stack map populated for test.
    14  type stacks map[uint64][]*trace.Frame
    15  
    16  // add adds a stack with a single frame whose Fn field is
    17  // set to the provided fname and returns a unique stack id.
    18  func (s *stacks) add(fname string) uint64 {
    19  	if *s == nil {
    20  		*s = make(map[uint64][]*trace.Frame)
    21  	}
    22  
    23  	id := uint64(len(*s))
    24  	(*s)[id] = []*trace.Frame{{Fn: fname}}
    25  	return id
    26  }
    27  
    28  // TestGoroutineCount tests runnable/running goroutine counts computed by generateTrace
    29  // remain in the valid range.
    30  //   - the counts must not be negative. generateTrace will return an error.
    31  //   - the counts must not include goroutines blocked waiting on channels or in syscall.
    32  func TestGoroutineCount(t *testing.T) {
    33  	w := trace.NewWriter()
    34  	w.Emit(trace.EvBatch, 0, 0)  // start of per-P batch event [pid, timestamp]
    35  	w.Emit(trace.EvFrequency, 1) // [ticks per second]
    36  
    37  	var s stacks
    38  
    39  	// In this test, we assume a valid trace contains EvGoWaiting or EvGoInSyscall
    40  	// event for every blocked goroutine.
    41  
    42  	// goroutine 10: blocked
    43  	w.Emit(trace.EvGoCreate, 1, 10, s.add("pkg.f1"), s.add("main.f1")) // [timestamp, new goroutine id, new stack id, stack id]
    44  	w.Emit(trace.EvGoWaiting, 1, 10)                                   // [timestamp, goroutine id]
    45  
    46  	// goroutine 20: in syscall
    47  	w.Emit(trace.EvGoCreate, 1, 20, s.add("pkg.f2"), s.add("main.f2"))
    48  	w.Emit(trace.EvGoInSyscall, 1, 20) // [timestamp, goroutine id]
    49  
    50  	// goroutine 30: runnable
    51  	w.Emit(trace.EvGoCreate, 1, 30, s.add("pkg.f3"), s.add("main.f3"))
    52  
    53  	w.Emit(trace.EvProcStart, 2, 0) // [timestamp, thread id]
    54  
    55  	// goroutine 40: runnable->running->runnable
    56  	w.Emit(trace.EvGoCreate, 1, 40, s.add("pkg.f4"), s.add("main.f4"))
    57  	w.Emit(trace.EvGoStartLocal, 1, 40)          // [timestamp, goroutine id]
    58  	w.Emit(trace.EvGoSched, 1, s.add("main.f4")) // [timestamp, stack]
    59  
    60  	res, err := trace.Parse(w, "")
    61  	if err != nil {
    62  		t.Fatalf("failed to parse test trace: %v", err)
    63  	}
    64  	res.Stacks = s // use fake stacks.
    65  
    66  	params := &traceParams{
    67  		parsed:  res,
    68  		endTime: int64(1<<63 - 1),
    69  	}
    70  
    71  	// If the counts drop below 0, generateTrace will return an error.
    72  	viewerData, err := generateTrace(params)
    73  	if err != nil {
    74  		t.Fatalf("generateTrace failed: %v", err)
    75  	}
    76  	for _, ev := range viewerData.Events {
    77  		if ev.Name == "Goroutines" {
    78  			cnt := ev.Arg.(*goroutineCountersArg)
    79  			if cnt.Runnable+cnt.Running > 2 {
    80  				t.Errorf("goroutine count=%+v; want no more than 2 goroutines in runnable/running state", cnt)
    81  			}
    82  			t.Logf("read %+v %+v", ev, cnt)
    83  		}
    84  	}
    85  }
    86  
    87  func TestGoroutineFilter(t *testing.T) {
    88  	// Test that we handle state changes to selected goroutines
    89  	// caused by events on goroutines that are not selected.
    90  
    91  	var s stacks
    92  
    93  	w := trace.NewWriter()
    94  	w.Emit(trace.EvBatch, 0, 0)  // start of per-P batch event [pid, timestamp]
    95  	w.Emit(trace.EvFrequency, 1) // [ticks per second]
    96  
    97  	// goroutine 10: blocked
    98  	w.Emit(trace.EvGoCreate, 1, 10, s.add("pkg.f1"), s.add("main.f1")) // [timestamp, new goroutine id, new stack id, stack id]
    99  	w.Emit(trace.EvGoWaiting, 1, 10)                                   // [timestamp, goroutine id]
   100  
   101  	// goroutine 20: runnable->running->unblock 10
   102  	w.Emit(trace.EvGoCreate, 1, 20, s.add("pkg.f2"), s.add("main.f2"))
   103  	w.Emit(trace.EvGoStartLocal, 1, 20)                    // [timestamp, goroutine id]
   104  	w.Emit(trace.EvGoUnblockLocal, 1, 10, s.add("pkg.f2")) // [timestamp, goroutine id, stack]
   105  	w.Emit(trace.EvGoEnd, 1)                               // [timestamp]
   106  
   107  	// goroutine 10: runnable->running->block
   108  	w.Emit(trace.EvGoStartLocal, 1, 10)         // [timestamp, goroutine id]
   109  	w.Emit(trace.EvGoBlock, 1, s.add("pkg.f3")) // [timestamp, stack]
   110  
   111  	res, err := trace.Parse(w, "")
   112  	if err != nil {
   113  		t.Fatalf("failed to parse test trace: %v", err)
   114  	}
   115  	res.Stacks = s // use fake stacks
   116  
   117  	params := &traceParams{
   118  		parsed:  res,
   119  		endTime: int64(1<<63 - 1),
   120  		gs:      map[uint64]bool{10: true},
   121  	}
   122  
   123  	_, err = generateTrace(params)
   124  	if err != nil {
   125  		t.Fatalf("generateTrace failed: %v", err)
   126  	}
   127  }
   128  
   129  func TestPreemptedMarkAssist(t *testing.T) {
   130  	w := trace.NewWriter()
   131  	w.Emit(trace.EvBatch, 0, 0)  // start of per-P batch event [pid, timestamp]
   132  	w.Emit(trace.EvFrequency, 1) // [ticks per second]
   133  
   134  	var s stacks
   135  	// goroutine 9999: running -> mark assisting -> preempted -> assisting -> running -> block
   136  	w.Emit(trace.EvGoCreate, 1, 9999, s.add("pkg.f1"), s.add("main.f1")) // [timestamp, new goroutine id, new stack id, stack id]
   137  	w.Emit(trace.EvGoStartLocal, 1, 9999)                                // [timestamp, goroutine id]
   138  	w.Emit(trace.EvGCMarkAssistStart, 1, s.add("main.f1"))               // [timestamp, stack]
   139  	w.Emit(trace.EvGoPreempt, 1, s.add("main.f1"))                       // [timestamp, stack]
   140  	w.Emit(trace.EvGoStartLocal, 1, 9999)                                // [timestamp, goroutine id]
   141  	w.Emit(trace.EvGCMarkAssistDone, 1)                                  // [timestamp]
   142  	w.Emit(trace.EvGoBlock, 1, s.add("main.f2"))                         // [timestamp, stack]
   143  
   144  	res, err := trace.Parse(w, "")
   145  	if err != nil {
   146  		t.Fatalf("failed to parse test trace: %v", err)
   147  	}
   148  	res.Stacks = s // use fake stacks
   149  
   150  	params := &traceParams{
   151  		parsed:  res,
   152  		endTime: int64(1<<63 - 1),
   153  	}
   154  
   155  	viewerData, err := generateTrace(params)
   156  	if err != nil {
   157  		t.Fatalf("generateTrace failed: %v", err)
   158  	}
   159  
   160  	marks := 0
   161  	for _, ev := range viewerData.Events {
   162  		if strings.Contains(ev.Name, "MARK ASSIST") {
   163  			marks++
   164  		}
   165  	}
   166  	if marks != 2 {
   167  		t.Errorf("Got %v MARK ASSIST events, want %v", marks, 2)
   168  	}
   169  }