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 }