github.com/timandy/routine@v1.1.4/goid_test.go (about) 1 package routine 2 3 import ( 4 "bytes" 5 "fmt" 6 "reflect" 7 "runtime" 8 "strconv" 9 "testing" 10 "unsafe" 11 12 "github.com/stretchr/testify/assert" 13 ) 14 15 var goroutineSpace = []byte("goroutine ") 16 17 func TestG_Goid(t *testing.T) { 18 runTest(t, func() { 19 gp := getg() 20 runtime.GC() 21 assert.Equal(t, curGoroutineID(), gp.goid) 22 }) 23 } 24 25 func TestG_Paniconfault(t *testing.T) { 26 runTest(t, func() { 27 gp := getg() 28 runtime.GC() 29 //read-1 30 assert.False(t, setPanicOnFault(false)) 31 assert.False(t, gp.getPanicOnFault()) 32 //read-2 33 setPanicOnFault(true) 34 assert.True(t, gp.getPanicOnFault()) 35 //write-1 36 gp.setPanicOnFault(false) 37 assert.False(t, setPanicOnFault(false)) 38 //write-2 39 gp.setPanicOnFault(true) 40 assert.True(t, setPanicOnFault(true)) 41 //write-read-1 42 gp.setPanicOnFault(false) 43 assert.False(t, gp.getPanicOnFault()) 44 //write-read-2 45 gp.setPanicOnFault(true) 46 assert.True(t, gp.getPanicOnFault()) 47 //restore 48 gp.setPanicOnFault(false) 49 }) 50 } 51 52 func TestG_Gopc(t *testing.T) { 53 runTest(t, func() { 54 gp := getg() 55 runtime.GC() 56 assert.Greater(t, int64(*gp.gopc), int64(0)) 57 }) 58 } 59 60 func TestG_ProfLabel(t *testing.T) { 61 runTest(t, func() { 62 ptr := unsafe.Pointer(&struct{}{}) 63 null := unsafe.Pointer(nil) 64 assert.NotEqual(t, ptr, null) 65 // 66 gp := getg() 67 runtime.GC() 68 //read-1 69 assert.Equal(t, null, getProfLabel()) 70 assert.Equal(t, null, gp.getLabels()) 71 //read-2 72 setProfLabel(ptr) 73 assert.Equal(t, ptr, gp.getLabels()) 74 //write-1 75 gp.setLabels(nil) 76 assert.Equal(t, null, getProfLabel()) 77 //write-2 78 gp.setLabels(ptr) 79 assert.Equal(t, ptr, getProfLabel()) 80 //write-read-1 81 gp.setLabels(nil) 82 assert.Equal(t, null, gp.getLabels()) 83 //write-read-2 84 gp.setLabels(ptr) 85 assert.Equal(t, ptr, gp.getLabels()) 86 //restore 87 gp.setLabels(null) 88 }) 89 } 90 91 func TestOffset(t *testing.T) { 92 runTest(t, func() { 93 assert.Panics(t, func() { 94 gt := reflect.TypeOf(0) 95 offset(gt, "hello") 96 }) 97 assert.PanicsWithValue(t, "No such field 'hello' of struct 'runtime.g'.", func() { 98 gt := getgt() 99 offset(gt, "hello") 100 }) 101 }) 102 } 103 104 // curGoroutineID parse the current g's goid from caller stack. 105 func curGoroutineID() int64 { 106 b := make([]byte, 64) 107 b = b[:runtime.Stack(b, false)] 108 // Parse the 4707 out of "goroutine 4707 [" 109 b = bytes.TrimPrefix(b, goroutineSpace) 110 i := bytes.IndexByte(b, ' ') 111 if i < 0 { 112 panic(fmt.Sprintf("No space found in %q", b)) 113 } 114 b = b[:i] 115 n, err := strconv.ParseInt(string(b), 10, 64) 116 if err != nil { 117 panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err)) 118 } 119 return n 120 } 121 122 // setPanicOnFault controls the runtime's behavior when a program faults at an unexpected (non-nil) address. 123 // 124 //go:linkname setPanicOnFault runtime/debug.setPanicOnFault 125 func setPanicOnFault(new bool) (old bool) 126 127 // getProfLabel get current g's labels which will be inherited by new goroutine. 128 // 129 //go:linkname getProfLabel runtime/pprof.runtime_getProfLabel 130 func getProfLabel() unsafe.Pointer 131 132 // setProfLabel set current g's labels which will be inherited by new goroutine. 133 // 134 //go:linkname setProfLabel runtime/pprof.runtime_setProfLabel 135 func setProfLabel(labels unsafe.Pointer) 136 137 //=== 138 139 // BenchmarkGohack-8 258425366 4.808 ns/op 0 B/op 0 allocs/op 140 func BenchmarkGohack(b *testing.B) { 141 _ = getg() 142 b.ReportAllocs() 143 b.ResetTimer() 144 for i := 0; i < b.N; i++ { 145 gp := getg() 146 _ = gp.goid 147 _ = gp.gopc 148 _ = gp.getLabels() 149 _ = gp.getPanicOnFault() 150 gp.setLabels(nil) 151 gp.setPanicOnFault(false) 152 } 153 }