lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/tracing/tracing_test.go (about) 1 // Copyright (C) 2017 Nexedi SA and Contributors. 2 // Kirill Smelkov <kirr@nexedi.com> 3 // 4 // This program is free software: you can Use, Study, Modify and Redistribute 5 // it under the terms of the GNU General Public License version 3, or (at your 6 // option) any later version, as published by the Free Software Foundation. 7 // 8 // You can also Link and Combine this program with other software covered by 9 // the terms of any of the Free Software licenses or any of the Open Source 10 // Initiative approved licenses and Convey the resulting work. Corresponding 11 // source of such a combination shall include the source code for all other 12 // software used. 13 // 14 // This program is distributed WITHOUT ANY WARRANTY; without even the implied 15 // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 // 17 // See COPYING file for full licensing terms. 18 // See https://www.nexedi.com/licensing for rationale and options. 19 20 package tracing 21 22 import ( 23 "reflect" 24 "runtime" 25 "testing" 26 "time" 27 "unsafe" 28 29 "github.com/kylelemons/godebug/pretty" 30 ) 31 32 func TestAttachDetach(t *testing.T) { 33 var traceX *Probe // list head of a tracing event 34 35 // check that traceX probe list has such and such content and also that .prev 36 // pointers in all elements are right 37 checkX := func(probev ...*Probe) { 38 t.Helper() 39 var pv []*Probe 40 41 pp := (*Probe)(unsafe.Pointer(&traceX)) 42 for p := traceX; p != nil; pp, p = p, p.next { 43 if p.prev != pp { 44 t.Fatalf("probe list: %#v: .prev is wrong", p) 45 } 46 pv = append(pv, p) 47 } 48 49 if !reflect.DeepEqual(pv, probev) { 50 t.Fatalf("probe list:\n%s\n", pretty.Compare(probev, pv)) 51 } 52 } 53 54 checkX() 55 56 // attach probe to traceX 57 attachX := func(probe *Probe) { 58 Lock() 59 AttachProbe(nil, &traceX, probe) 60 Unlock() 61 } 62 63 // detach probe 64 detach := func(probe *Probe) { 65 Lock() 66 probe.Detach() 67 Unlock() 68 } 69 70 p1 := &Probe{} 71 attachX(p1) 72 checkX(p1) 73 74 detach(p1) 75 checkX() 76 detach(p1) 77 checkX() 78 attachX(p1) 79 checkX(p1) 80 81 p2 := &Probe{} 82 attachX(p2) 83 checkX(p1, p2) 84 85 p3 := &Probe{} 86 attachX(p3) 87 checkX(p1, p2, p3) 88 89 detach(p2) 90 checkX(p1, p3) 91 92 detach(p1) 93 checkX(p3) 94 95 detach(p3) 96 checkX() 97 } 98 99 // Test use vs concurent detach. 100 // 101 // Detach works under tracing lock (= world stopped) - so changing a probe list 102 // should be ok, but since race detector does not know we stopped the world it 103 // could complain. 104 func TestUseDetach(t *testing.T) { 105 var traceX *Probe // list head of a tracing event 106 107 // attach probe to traceX 108 probe := Probe{} 109 Lock() 110 AttachProbe(nil, &traceX, &probe) 111 Unlock() 112 113 // simulate traceX signalling and so probe usage and concurrent probe detach 114 go func() { 115 // delay a bit so that main goroutine first spins some time 116 // with non-empty probe list 117 time.Sleep(1 * time.Millisecond) 118 119 Lock() 120 probe.Detach() 121 Unlock() 122 }() 123 124 loop: 125 for { 126 np := 0 127 for p := traceX; p != nil; p = p.Next() { 128 np++ 129 } 130 131 switch np { 132 case 1: 133 // ok - not yet detached 134 case 0: 135 // ok - detached 136 break loop 137 default: 138 t.Fatalf("probe seen %d times; must be either 1 or 0", np) 139 } 140 141 // XXX as of go19 tight loops are not preemptible (golang.org/issues/10958) 142 // and Lock does stop-the-world -> make this loop explicitly preemtible. 143 runtime.Gosched() 144 } 145 }