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  }